Архивировано

Эта тема находится в архиве и закрыта для публикации сообщений.

DeathStalker

Нужна помощь, по с++ возврат массива из функции

Рекомендованные сообщения

Добрый вечер, понимаю что сильно туплю, но не могу понять как это сделать. маленькое ТЗ

Допустим у нас есть некий одномерный массив чегото ,не важно чего, мы его передаем в функцию по адрессу, обрабатываем и получаем новый массив, а потом хотелось бы получить его обратно.

ну допустим

int func(int i, int *mas)
{
int mas2[4];
for(int z=0;z<i;z++)mas2[z]=sqrt(mas[z]);
return 0;
}
int main()
{
int mas[]={1,2,3,4}
func(4,mas);
}
вернуть по адрессу нельзя, так как как только функция уйдет из видимости все будет уничтоженно - и это понятно
теретически можно производить действия над исходным, как никак передав по адрессу мы работаем с ним -же, но а если хочется его оставить не изменным

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Попробуй вернуть указатель на то что выделено внутри функцией например с помощью new.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

#include <iostream>

#include <math>

 

using namespace std;

 

int func(int i, double *mas)

{

double mas2[4];

for(int z=0; z<i; z++){

mas2[z]=sqrt(mas[z]);

cout<<mas2[z]<<"\n";

}

cout<<"\n\n";

for(int z=0; z<i; z++){

cout<<mas[z]<<"\n";

}

 

return 0;

}

int main()

{

double mas[]={1,2,3,4};

func(4,mas);

system("pause");

}

 

 

Так не подойдет? это сложно назвать возвpатом но пеpвоначальное значение он выведет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
#include <iostream>

#include <math>

 

using namespace std;

 

int func(int i, double *mas)

{

double mas2[4];

for(int z=0; z<i; z++){

mas2[z]=sqrt(mas[z]);

cout<<mas2[z]<<"\n";

}

cout<<"\n\n";

for(int z=0; z<i; z++){

cout<<mas[z]<<"\n";

}

 

return 0;

}

int main()

{

double mas[]={1,2,3,4};

func(4,mas);

system("pause");

}

 

 

Так не подойдет? это сложно назвать возвpатом но пеpвоначальное значение он выведет.

К сожалению нет, вся суть в том чтоб получить назад новый массив, а вывод в консоль это не интересно. Я конечно могу с помощью слотов и сигналов переслать весь массив, но это уже не с++

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А если использовать функцию типа:

int func(int i, int * mas_old, int * mas_new);

И работать с ней так:

int main()
{
 int mas1[]={1, 2, 3, 4};
 int mas2[4];
 func(4, mas1, mas2);
 return 0;
}

 

Можно еще так попробовать...

#include <stdio.h>
#include <math.h>

double * func(int i, double * mas)
{
 double * mas_new = new double[i];
 for(int z=0; z<i; z++)
mas_new[z] = sqrt(mas[z]);
 return mas_new;
}

int main()
{
 double mas[] = {1.0, 2.0, 3.0, 4.0};
 double * mas_new;

 for(int z=0; z<4; z++)
printf("%f ", mas[z]);

 mas_new = func(4, mas);

 for(int z=0; z<4; z++)
printf("%f ", mas_new[z]);

 delete mas_new;
 return 0;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
А если использовать функцию типа:

int func(int i, int * mas_old, int * mas_new);

И работать с ней так:

int main()
{
 int mas1[]={1, 2, 3, 4};
 int mas2[4];
 func(4, mas1, mas2);
 return 0;
}

 

Можно еще так попробовать...

#include <stdio.h>
#include <math.h>

double * func(int i, double * mas)
{
 double * mas_new = new double[i];
 for(int z=0; z<i; z++)
mas_new[z] = sqrt(mas[z]);
 return mas_new;
}

int main()
{
 double mas[] = {1.0, 2.0, 3.0, 4.0};
 double * mas_new;

 for(int z=0; z<4; z++)
printf("%f ", mas[z]);

 mas_new = func(4, mas);

 for(int z=0; z<4; z++)
printf("%f ", mas_new[z]);

 delete mas_new;
 return 0;
}

Вот к тому что бы передовать все массивы и я сколоняюсь. А вот второй метод что вы предложили в корне не правильный. Как только функция уйдет из видимости, по правилам с++ память будет очищенна - а следовательно и данные

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ну, насколько я понимаю, оператор new резервирует необходимый блок памяти и возвращает указатель на начало этого блока. Оператор delete, в свою очередь, очищает блок памяти на который указывает указатель. Очищение блока памяти происходит только после использования delete (поэтому после каждого new необходимо освобождать память delete).

PS. Последний пример у меня работает. Компилировал на VC от MicroSoft.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Ну, насколько я понимаю, оператор new резервирует необходимый блок памяти и возвращает указатель на начало этого блока. Оператор delete, в свою очередь, очищает блок памяти на который указывает указатель. Очищение блока памяти происходит только после использования delete (поэтому после каждого new необходимо освобождать память delete).

PS. Последний пример у меня работает. Компилировал на VC от MicroSoft.

Да Вы правы, только этот блок принадлежит той функциии, попробуйте вызвать еще парочку функций, а потом обратится к этому блоку памяти. Просто вот этот вариант разбирается много где на просторах интернета как не верный, а вот верный я пока не нашел

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Ну, насколько я понимаю, оператор new резервирует необходимый блок памяти и возвращает указатель на начало этого блока. Оператор delete, в свою очередь, очищает блок памяти на который указывает указатель. Очищение блока памяти происходит только после использования delete (поэтому после каждого new необходимо освобождать память delete).

PS. Последний пример у меня работает. Компилировал на VC от MicroSoft.

Да Вы правы, только этот блок принадлежит той функциии, попробуйте вызвать еще парочку функций, а потом обратится к этому блоку памяти. Просто вот этот вариант разбирается много где на просторах интернета как не верный, а вот верный я пока не нашел

 

Выделенный блок будет существовать до конца работы программы/вызова delete.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Ну, насколько я понимаю, оператор new резервирует необходимый блок памяти и возвращает указатель на начало этого блока. Оператор delete, в свою очередь, очищает блок памяти на который указывает указатель. Очищение блока памяти происходит только после использования delete (поэтому после каждого new необходимо освобождать память delete).

PS. Последний пример у меня работает. Компилировал на VC от MicroSoft.

Да Вы правы, только этот блок принадлежит той функциии, попробуйте вызвать еще парочку функций, а потом обратится к этому блоку памяти. Просто вот этот вариант разбирается много где на просторах интернета как не верный, а вот верный я пока не нашел

 

Выделенный блок будет существовать до конца работы программы/вызова delete.

От реализации С++ не требуется освобождения выделенной с помощью new

памяти, если на нее больше не ссылается ни один указатель (иными словами,

не требуется автоматическая "сборка мусора").

- вот строка из страупа. которая свидетельствует что очистка произойдет автоматически

 

2.1.1 Область видимости

 

 

Описанием определяется область видимости имени. Это значит, что

имя может использоваться только в определенной части текста программы.

Если имя описано в функции (обычно его называют "локальным именем"), то

область видимости имени простирается от точки описания

до конца блока, в котором появилось это описание. Если имя не находится

в описании функции или класса (его обычно называют "глобальным именем"),

то область видимости простирается от точки описания до конца файла,

в котором появилось это описание.

Описание имени в блоке может скрывать описание в объемлющем блоке или

глобальное имя; т.е. имя может быть переопределено так, что оно будет

обозначать другой объект внутри блока. После выхода из блока прежнее

значение имени (если оно было) восстанавливается.

 

и вот

2.1.3 Время жизни объектов

 

 

Если только программист не вмешается явно, объект будет создан при

появлении его определения и уничтожен, когда исчезнет из

области видимости. Объекты с глобальными именами создаются,

инициализируются (причем только один раз) и существуют до конца

программы. Если локальные объекты описаны со служебным словом

static, то они также существуют до конца программы. Инициализация их

происходит, когда в первый раз управление "проходит через"

описание этих объектов, например:

 

int a = 1;

 

void f()

{

int b = 1; // инициализируется при каждом вызове f()

static int c = a; // инициализируется только один раз

cout << " a = " << a++

<< " b = " << b++

<< " c = " << c++ << '\n';

}

 

int main()

{

while (a < 4) f();

}

 

Здесь программа выдаст такой результат:

 

a = 1 b = 1 c = 1

a = 2 b = 1 c = 2

a = 3 b = 1 c = 3

 

''Из примеров этой главы для краткости изложения исключена

макрокоманда #include <iostream>. Она нужна лишь в тех из них, которые

выдают результат.

 

Операция "++" является инкрементом, т. е. a++ означает: добавить 1

к переменной a.

 

Глобальная переменная или локальная переменная static, которая не была

явно инициализирована, инициализируется неявно нулевым значением (#2.4.5).

Используя операции new и delete, программист может создавать

объекты, временем жизни которых он управляет сам

new - это объект который в данном случае будет находится только в области видимости данной функции

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
От реализации С++ не требуется освобождения выделенной с помощью new

памяти, если на нее больше не ссылается ни один указатель (иными словами,

не требуется автоматическая "сборка мусора").

 

Именно что, реализация плюсов не осуществляет сборку мусора, надо самим память освобождать.

 

 

Используя операции new и delete, программист может создавать

объекты, временем жизни которых он управляет сам

 

Вот именно, что сам. Объект существует, пока не будет вызван delete

 

А по поводу области видимости - так это здесь вообще говоря немного не при чём. Вы же не переменную описываете, а явно выделяете область памяти

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поскольку уже много лет являюсь программистом на C++, позволю себе бросить свои 5 копеек.

DeathStalker, насчет того, что "второй вариант не будет работать" - ты не прав. Работать он будет - на объекты, выделенные по new правила для времени жизни автоматических (стековых) объектов не распространяются (похоже, ты и сам это понял). Другое дело, что этот подход просто считается дурным тоном в программировании. Правила хорошего тона гласят:

Если функция не называется как-то вроде double* CreateMyFuckingArray(...), то она не имеет права выделять память (или другой ресурс), который затем надо где-то освобождать (как правило в вызвавшем эту функцию коде). Напротив, если функция называется как написано выше, то подразумевается, что она выделяет некий ресурс, который вызвавший код обязан будет освободить (когда этот ресурс будет уже не нужен). [это правило не распространяется на private функции-члены класса, там от этого правила иногда можно отступать, но по возможности даже там желательно этого правила придерживаться]

Т.е. в твоем случае функция f(...) должна освободить все ресурсы (память, файлы, порты), которые она захватывала для своей работы. Так гласят правила хорошего тона. Поэтому для твоей задачи я бы выбрал первый вариант (передача в функцию указателя на выходной массив в качестве второго параметра). При этом управление временем жизни выходного массива полностью берет на себя вызывающий код. Это массив может быть автоматическим (как в примере), статическим или динамическим (выделяемым по new и освобождаемом delete). Функция f на этот процесс никаких ограничений не накладывает. Единственное, что требует функция f - это что бы этот массив был достаточного размера.

Ну, и конечно не забываем про const. Поскольку ты хочешь, что бы исходный массив остался без изменений, то можно явно об этом сказать компилятору, и тогда он внимательно проследит, что бы ты нечаянно этот массив не испортил (при попытке записи в этот массив компилятор выдаст ошибку). Учитывая вышесказанное, твоя функция будет иметь сигнатуру

int f(int i, const double* in, doble* out);

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Поскольку уже много лет являюсь программистом на C++, позволю себе бросить свои 5 копеек.

DeathStalker, насчет того, что "второй вариант не будет работать" - ты не прав. Работать он будет - на объекты, выделенные по new правила для времени жизни автоматических (стековых) объектов не распространяются (похоже, ты и сам это понял). Другое дело, что этот подход просто считается дурным тоном в программировании. Правила хорошего тона гласят:

Если функция не называется как-то вроде double* CreateMyFuckingArray(...), то она не имеет права выделять память (или другой ресурс), который затем надо где-то освобождать (как правило в вызвавшем эту функцию коде). Напротив, если функция называется как написано выше, то подразумевается, что она выделяет некий ресурс, который вызвавший код обязан будет освободить (когда этот ресурс будет уже не нужен). [это правило не распространяется на private функции-члены класса, там от этого правила иногда можно отступать, но по возможности даже там желательно этого правила придерживаться]

Т.е. в твоем случае функция f(...) должна освободить все ресурсы (память, файлы, порты), которые она захватывала для своей работы. Так гласят правила хорошего тона. Поэтому для твоей задачи я бы выбрал первый вариант (передача в функцию указателя на выходной массив в качестве второго параметра). При этом управление временем жизни выходного массива полностью берет на себя вызывающий код. Это массив может быть автоматическим (как в примере), статическим или динамическим (выделяемым по new и освобождаемом delete). Функция f на этот процесс никаких ограничений не накладывает. Единственное, что требует функция f - это что бы этот массив был достаточного размера.

Ну, и конечно не забываем про const. Поскольку ты хочешь, что бы исходный массив остался без изменений, то можно явно об этом сказать компилятору, и тогда он внимательно проследит, что бы ты нечаянно этот массив не испортил (при попытке записи в этот массив компилятор выдаст ошибку). Учитывая вышесказанное, твоя функция будет иметь сигнатуру

int f(int i, const double* in, doble* out);

Большое спасибо, за разяснения Вы многое мне прояснили. Сумятицу вызвало не понимание понятия о времени жизни объекта. А с методом передачи я пришел к вами же предложенному решению(т есть создание массивов в теле программы и предаче их всех по указателю в функцию), как максимально оптимальному для моей задачи

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Хм, ну еще есть класс std::auto_ptr<T>, который как раз таки решает задачу ограничения временем жизни блока динамической памяти областью видимости переменной.

include <stdio.h>
include <memory.h>

int[] AllocateDynamicMemory(int count)
{
return new int[count];
}

void Main()
{
....
{
	std::auto_ptr<int> ptr(AllocateDynamicMemory(100));
	for (int i = 0; i < 100; i++)
		cout << ptr.get()[i];
} 
....
}

Использование классов, автоматически управляющих памятью - это не то что бы правило хорошего тона, а абсолютная необходимость при создании надежного приложения на C++. Оператор delete не должен появляться в коде приложения в месте, отличном от деструктора класса. В случае крайней необходимости, он может появляться в блоке __finally SEH-фрейма. Только нужно учитывать, что компилятор Microsoft не поддерживает смешивание явных SEH фреймов и автоматических деструкторов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

return a;						   // возвращаем указатель на 0-ой элемент массива a
}

int main()
{
 system("chcp 1251");


 for(int i = 0; i < 5; i++)   // цикл вывода массива на экран
 {
  cout << Элемент " << i << " массива: " << *(func()+i) << endl;	 // операция * - операция получения значения по адресу.
																									   // На каждой итерации к адресу добавляем i, получая следующий элемент массива
 }

 system("pause");
 return 0;
}

 

Есть вопросы - задавайте.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

return a;						   // возвращаем указатель на 0-ой элемент массива a
}

int main()
{
 system("chcp 1251");


 for(int i = 0; i < 5; i++)   // цикл вывода массива на экран
 {
  cout << Элемент " << i << " массива: " << *(func()+i) << endl;	 // операция * - операция получения значения по адресу.
																									   // На каждой итерации к адресу добавляем i, получая следующий элемент массива
 }

 system("pause");
 return 0;
}

 

Есть вопросы - задавайте.

Сударь, вы совершенно не читали топик. Этот вариант обсуждался. без new delete - он работать не будет

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

return a;						   // возвращаем указатель на 0-ой элемент массива a
}

int main()
{
 system("chcp 1251");


 for(int i = 0; i < 5; i++)   // цикл вывода массива на экран
 {
  cout << Элемент " << i << " массива: " << *(func()+i) << endl;	 // операция * - операция получения значения по адресу.
																									   // На каждой итерации к адресу добавляем i, получая следующий элемент массива
 }

 system("pause");
 return 0;
}

 

Есть вопросы - задавайте.

Сударь, вы совершенно не читали топик. Этот вариант обсуждался. без new delete - он работать не будет

 

Этот вариант работает. Проверьте сами. Синтаксис с++ не запрещает нам возвращать адрес.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

return a;						   // возвращаем указатель на 0-ой элемент массива a
}

int main()
{
 system("chcp 1251");


 for(int i = 0; i < 5; i++)   // цикл вывода массива на экран
 {
  cout << Элемент " << i << " массива: " << *(func()+i) << endl;	 // операция * - операция получения значения по адресу.
																									   // На каждой итерации к адресу добавляем i, получая следующий элемент массива
 }

 system("pause");
 return 0;
}

 

Есть вопросы - задавайте.

Сударь, вы совершенно не читали топик. Этот вариант обсуждался. без new delete - он работать не будет

 

Этот вариант работает. Проверьте сами. Синтаксис с++ не запрещает нам возвращать адрес.

без new временем жизни объекта управляет компилятор. В вашем случае как только функция уйдет из зоны видимости она автаматически очистится

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
без new временем жизни объекта управляет компилятор. В вашем случае как только функция уйдет из зоны видимости она автаматически очистится

 

Хорошо. Тогда можно поступить иначе. Т.к. указатели позволяют максимально эффективно решить проблему возврата нескольких значений из функции, а также возврата больших объектов, копирование которых нежелательно, в случае, если требуется вернуть несколько значений, можно воспользоваться следующим синтаксисом:

 

void func(int *a, int *b, int *c)
{
 *a  = …;

 *b = …;

 *c = …;

}

 

И работать непосредственно с теми объектами, адреса которых передали. И не надо получать массив обратно, как хотели Вы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
без new временем жизни объекта управляет компилятор. В вашем случае как только функция уйдет из зоны видимости она автаматически очистится

 

Хорошо. Тогда можно поступить иначе. Т.к. указатели позволяют максимально эффективно решить проблему возврата нескольких значений из функции, а также возврата больших объектов, копирование которых нежелательно, в случае, если требуется вернуть несколько значений, можно воспользоваться следующим синтаксисом:

 

void func(int *a, int *b, int *c)
{
 *a  = …;

 *b = …;

 *c = …;

}

 

И работать непосредственно с теми объектами, адреса которых передали. И не надо получать массив обратно, как хотели Вы.

К этому то мы и пришли

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
  int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

  return a;						   // возвращаем указатель на 0-ой элемент массива a
}

 

Есть вопросы - задавайте.

 

 

Сударь, вы совершенно не читали топик
У меня более жесткий вопрос - где Вы учились программированию? Это чтобы знакомым сообщить, что идти учиться туда не надо. Потому что любой нормальный преподаватель за такой код сразу двойку Вам поставит и отправит на пересдачу. Да и любой нормальный компилятор на такую писанину должен ворнинг выдавать

warning C4172: returning address of local variable or temporary

Ах да, настоящие хакеры ворнингов не читают (и мануалы тоже).

А ведь по стандарту твое *(func()+i) - это undefined behaviour (в программистской среде именуемое UB), частным случаем которого может быть и правильная (т.е. ожидаемая тобой) работа программы (обычно в DOS-среде). В виндах такой код скорее всего свалится к чертям (потому как там более жесткий контроль за работой с памятью). Но даже если и не свалится - это еще не повод утверждать, что такой код правильный. В общем читайте стандарт и учитесь программировать, а не направляйте людей по ложному пути.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Пример возврата массива из функции:

 

#include "iostream.h"


int* func()						   // наша функция, возвращающая указатель на элемент
{
  int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов

  return a;						   // возвращаем указатель на 0-ой элемент массива a
}

 

Есть вопросы - задавайте.

 

 

Сударь, вы совершенно не читали топик
У меня более жесткий вопрос - где Вы учились программированию? Это чтобы знакомым сообщить, что идти учиться туда не надо. Потому что любой нормальный преподаватель за такой код сразу двойку Вам поставит и отправит на пересдачу. Да и любой нормальный компилятор на такую писанину должен ворнинг выдавать

warning C4172: returning address of local variable or temporary

Ах да, настоящие хакеры ворнингов не читают (и мануалы тоже).

А ведь по стандарту твое *(func()+i) - это undefined behaviour (в программистской среде именуемое UB), частным случаем которого может быть и правильная (т.е. ожидаемая тобой) работа программы (обычно в DOS-среде). В виндах такой код скорее всего свалится к чертям (потому как там более жесткий контроль за работой с памятью). Но даже если и не свалится - это еще не повод утверждать, что такой код правильный. В общем читайте стандарт и учитесь программировать, а не направляйте людей по ложному пути.

 

Не надо обсуждать тут где я учился программированию! Про Вас я тоже смогу многое чего сказать неприятного, если пообщаюсь с вами подольше!!!

Не знаю каким компилятором Вы пользуетесь, но мой компилятор все устраивает. И никакой ошибки в коде я не вижу. Интересно кто запретил прибавлять к адресу единичку? И вообще, результат, зависит от реализации компилятора.

Скажите, что

int i = 5;

i = ++i + ++i;

тоже есть неопределённое поведение???

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Не надо обсуждать тут где я учился программированию! Про Вас я тоже смогу многое чего сказать неприятного, если пообщаюсь с вами подольше!!!
Умерьте пыл, молодой человек. Лучше слушайте, что говорят опытные люди.
Не знаю каким компилятором Вы пользуетесь,
Microsoft Visual Studio 6, 8 и 9
но мой компилятор все устраивает.
Ну отключить ворнинги (или просто не обращать на них внимания) много ума не надо. Позиция "раз .exe собирается - значит мой компилятор все устраивает" тоже имеет право на существование.
И никакой ошибки в коде я не вижу.
Плохо. Это говорит лишь об Вашей низкой квалификации (либо о ее отсутствии)
Интересно кто запретил прибавлять к адресу единичку?
Никто. А вот разыменовывать указатель на локальный объект уже отработавшей функции запрещает стандарт. Точнее говорит, что в этом случае будет неопределенное поведение
И вообще, результат, зависит от реализации компилятора.
ИМЕННО!!! Когда результат работы программы зависит от компилятора (или даже от настроек оптимизации компилятора) - ЭТО И ЕСТЬ СЛУЧАЙ НЕОПРЕДЕЛЕННОГО ПОВЕДЕНИЯ! Результат работы корректно написанного кода не должен зависеть от компилятора. Главное, что бы этот компилятор соответствовал стандарту.
Скажите, что

int i = 5;

i = ++i + ++i;

тоже есть неопределённое поведение???

Здесь неопределенного поведения нет, здесь все четко. Тем не менее за злоупотребление даже таким кодом я бы бил по рукам. Ибо код должен быть легко читаем и по возможности легко понимаем.

 

Ну и в качестве иллюстрации ошибочности Вашего кода. Если Вашу программу Вы компилировали и исполняли (и она не падала и даже печатала правильные результаты), то должен заработать и следующий пример (слегка модифицируем Вашу программу):

#include "iostream"
// Вывод результатов оформим в виде отдельной функции
void PrintRes(const int* InputArray, size_t ArraySize)
{
 int LocalArray[50];
 for (int i = 0; i < 50; i++)
 {
 LocalArray[i] = i*100;
 }
 // Выводим входной массив.
 for (size_t j = 0; j < ArraySize; j++)
 {
 std::cout << *(InputArray + j) << std::endl;
 }
 // Выводим первые 5 элементов массива LocalArray
 std::cout << "Local Array" << std::endl;
 for (size_t k = 0; k < 5; k++)
 {
 std::cout << LocalArray[k] << std::endl;
 }
}
// Далее идет Ваша функция func
int* func()						   // наша функция, возвращающая указатель на элемент
{
  int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов
  return a;						   // возвращаем указатель на 0-ой элемент массива a
}
// пытаемся все это дело применить
int main()
{
 system("chcp 1251");
 // Убедимся, что функция PrintRes работает
 int Array1[5] = {10,9,8,7,6};
 PrintRes(Array1 , 5);  // Вывели то, что и ожидали

 // Отделяем один вывод от другого
 std::cout << "=========================" << std::endl;

 // Теперь получим массив из функции func  
 int* ReturnedArray = func();
 // И пытаемся напечатать массив, который вернула функция func
 PrintRes(ReturnedArray, 5);
 system("pause");
 // Смотрим на экран и очень удивляемся.
 return 0;
}

Контрольные вопросы:

Что должна напечатать программа?

Что она печатает на самом деле?

Объяснить результаты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Скажите, что

int i = 5;

i = ++i + ++i;

тоже есть неопределённое поведение???

Здесь неопределенного поведения нет, здесь все четко. Тем не менее за злоупотребление даже таким кодом я бы бил по рукам. Ибо код должен быть легко читаем и по возможности легко понимаем.

 

А вот и нет)) Как раз это один из ярких примеров неопределенного поведения. При его выполнении переменная i может принять значения 13 или 14 для C/C++. Неопределенность в языке C/C++ связана с тем, что побочные эффекты (т.е. инкремент в данном случае) могут быть применены в любой удобный для компилятора момент между двумя точками следования.

 

Ну отключить ворнинги (или просто не обращать на них внимания) много ума не надо. Позиция "раз .exe собирается - значит мой компилятор все устраивает" тоже имеет право на существование.

 

Ворнинги я не отключал. Даже включил отображение ВСЕХ ворнингов в настройках компилятора. При этом борлондавский компилятор не выдал ни одного ворнинга.

 

Ну и в качестве иллюстрации ошибочности Вашего кода. Если Вашу программу Вы компилировали и исполняли (и она не падала и даже печатала правильные результаты), то должен заработать и следующий пример (слегка модифицируем Вашу программу):

#include "iostream"
// Вывод результатов оформим в виде отдельной функции
void PrintRes(const int* InputArray, size_t ArraySize)
{
 int LocalArray[50];
 for (int i = 0; i < 50; i++)
 {
 LocalArray[i] = i*100;
 }
 // Выводим входной массив.
 for (size_t j = 0; j < ArraySize; j++)
 {
 std::cout << *(InputArray + j) << std::endl;
 }
 // Выводим первые 5 элементов массива LocalArray
 std::cout << "Local Array" << std::endl;
 for (size_t k = 0; k < 5; k++)
 {
 std::cout << LocalArray[k] << std::endl;
 }
}
// Далее идет Ваша функция func
int* func()						   // наша функция, возвращающая указатель на элемент
{
  int a[5] = {1, 5, 6, 7, 8};	// наш массив из 5-ти элементов
  return a;						   // возвращаем указатель на 0-ой элемент массива a
}
// пытаемся все это дело применить
int main()
{
 system("chcp 1251");
 // Убедимся, что функция PrintRes работает
 int Array1[5] = {10,9,8,7,6};
 PrintRes(Array1 , 5);  // Вывели то, что и ожидали

 // Отделяем один вывод от другого
 std::cout << "=========================" << std::endl;

 // Теперь получим массив из функции func  
 int* ReturnedArray = func();
 // И пытаемся напечатать массив, который вернула функция func
 PrintRes(ReturnedArray, 5);
 system("pause");
 // Смотрим на экран и очень удивляемся.
 return 0;
}

Контрольные вопросы:

Что должна напечатать программа?

Что она печатает на самом деле?

Объяснить результаты.

 

Да, действительно печатает не то, что я ожидал... Логично.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вмешаюсь и я. При всем уважение к компании Borland, Их с++ компилятор это нечто, как я матерился когда при проходе высокоточных расчетов, я не знал что получу исключение в связи с переполнением буфера переменной или верный результат и все это на одном и том же примере, который точно известно не вызывает переполнений.

Поэтому обсуждать результаты работы этого компилятора я вообще бы не пытался

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Если под Window, то кроме MS VS навряд ли есть смысл что-то использовать.

Под Linux если сравнивать, то gcc откровенно сакс для сипэпэшных прог в сравнении по скорости компиляции.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Под Linux если сравнивать, то gcc откровенно сакс для сипэпэшных прог в сравнении по скорости компиляции.
Я не знаток этих ваших православных линуксов, но ИМХО в любом случае критерием хорошести компилятора являются в первую очередь соответствие стандарту и качество генерируемого кода, а скорость компиляции стоит далеко не на первом месте. Я ошибаюсь?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Под Linux если сравнивать, то gcc откровенно сакс для сипэпэшных прог в сравнении по скорости компиляции.
Я не знаток этих ваших православных линуксов, но ИМХО в любом случае критерием хорошести компилятора являются в первую очередь соответствие стандарту и качество генерируемого кода, а скорость компиляции стоит далеко не на первом месте. Я ошибаюсь?

Я думаю Вы правы. А со скоростью там такая фигня, с провославными линухами. компиляций бьется на 3 этапа.1 и 2 обязательный. И так 1 - это конфигурация, происходит создания MakeFile (инструкция по сборке компилятору) и в этот момент вся система шерстится на наличие библиотек и их необходимости и всех нужных файлов. 2 этап, если все прошло гладко на первом, - это уже непосредственно компиляция и сборка. 3 -этап распихивание файликов по системе. Так вот все это гарантирует что программа верно собирается, с нужными библиотеками, с библиотеками нужных версий и запустится после сборки. Такая проверка несомненно занимает кучу времени

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Под Linux если сравнивать, то gcc откровенно сакс для сипэпэшных прог в сравнении по скорости компиляции.
Я не знаток этих ваших православных линуксов, но ИМХО в любом случае критерием хорошести компилятора являются в первую очередь соответствие стандарту и качество генерируемого кода, а скорость компиляции стоит далеко не на первом месте. Я ошибаюсь?

Нет, вы в целом правы. И по этим критериям виндовый компилятор тоже впереди. Но про скорость - это тоже немаловажно. Компиляция программы мегов на 40-50 занимает в Linux скажем от балды час(зависит от железа, конечно).

Я сталкиваюсь с этим постоянно как пользователь Gentoo и обновление системы занимает кучу времени, так как все программы компилируются на моём компе. Это в качестве примера :huh:

Для разработчика это важно, так как его время дорого и ждать какждый раз компилляции очень муторно.

Вам то это хорошо известно. Но согласен, что соответствие стандартам и качество ни в коем случае не должны приноситься в жертву скорости.

 

 

 

И так 1 - это конфигурация, происходит создания MakeFile (инструкция по сборке компилятору) и в этот момент вся система шерстится на наличие библиотек и их необходимости и всех нужных файлов.

Это занимает максимум минуты даже для сложной проги.

3 -этап распихивание файликов по системе.

Установка это называется, а не компиляция :P И распихивает сама система, но например при компиляция часто необходимо патчить код, чтобы было известно куда пихать в данной системе, это отнимает время при создании пакетов для разных дистров. Например Gentoo разбивает процесс на этапы, налагает патчи в конфигах есл инужно, и компилирует всё во временных каталогах после чего перекидывает по нужным каталогам в соответствии с инструкциями в ebuild. Процесс создания софта тут, естественно, дольше даже без учёта гуя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах