Я знаю, что это не будет работать, потому что переменная x уничтожается, когда функция возвращается:
int* myFunction()
{
int x = 4; return &x;
}
таким образом, как я правильно возвращаю указатель на что-то, что я создаю в функции, и с чем я должен заботиться? Как я избегаю утечек памяти?
Я также использовал malloc:
int* myFunction2()
{
int* x = (int*)malloc(sizeof int); *x = 4; return x;
}
Как Вы правильно делаете это - в C и C++?
Для C ++ вы можете использовать интеллектуальный указатель для принудительной передачи права собственности. auto_ptr
или boost :: shared_ptr
- хорошие варианты.
Есть другой подход - объявить x
статическим. В этом случае он будет расположен в сегменте данных, а не в стеке, поэтому он доступен (и сохраняется) во время выполнения программы.
int *myFunction(void)
{
static int x = 4;
return &x;
}
Обратите внимание, что присвоение x = 4
будет выполнено только при первом вызове myFunction
:
int *foo = myFunction(); // foo is 4
*foo = 10; // foo is 10
*foo = myFunction(); // foo is 10
NB! Использование статических переменных в области функций не является безопасным методом.
Как правило, лучше использовать указатели Boost или TR1. Это позволяет избежать накладных расходов на копирование и обеспечивает полуавтоматическое удаление. Итак, ваша функция должна выглядеть так:
boost::shared_ptr<int> myFunction2()
{
boost::shared_ptr<int> x = new int;
*x = 4;
return x;
}
Другой вариант - просто разрешить копирование. Это не так уж плохо, если объект небольшой (как этот) или вы можете организовать создание объекта в операторе return. Компилятор обычно оптимизирует копию, если объект создается в операторе return.
В C ++ вы должны использовать new
:
int *myFunction() { int blah = 4; return new int(blah); }
И чтобы избавиться от него, используйте delete:
int main(void) { int *myInt = myFunction(); // do stuff delete myInt; }
Обратите внимание, что я вызываю конструктор копирования для int при использовании new
, так что значение «4» копируется в динамическую память. Единственный способ надежно получить указатель на что-то в стеке - скопировать его в кучу, правильно вызвав new
.
РЕДАКТИРОВАТЬ: Как отмечалось в другом ответе, вам также необходимо задокументировать, что указатель должен быть освобожден вызывающим абонентом позже. Иначе может произойти утечка памяти.
Он определяет «sram» как указатель на память, начинающийся с нуля. Вы можете получить доступ к памяти через указатель, например, sram [0] - это адрес ноль, sram [1] - это материал по адресу один и т.д.
В частности, это отсчет 0, чтобы быть указателем на массив неподписанных символов и проходить через него косвенно (оставляя массив неподписанных символов).
Подобный результат может быть получен с помощью
#define sram ((unsigned char*)0)
Он также полностью не определен в стандарте С, но это не мешает людям использовать его и иметь ангелы вылетают из пупков.
-121--3329716-Ваш второй подход верен. Просто нужно четко документировать, что вызывающий «владеет» указателем результата, и несет ответственность за его освобождение.
Из-за этой дополнительной сложности это редко делается для «малых» типов, таких как int, хотя я предполагаю, что вы просто использовали int здесь ради примера.
Некоторые пользователи также предпочитают использовать указатель на уже назначенный объект в качестве параметра, а не назначать его внутри. Это делает более ясным, что вызывающий абонент несет ответственность за освобождение объекта (так как он был выделен в первую очередь), но делает сайт вызова немного более подробным, поэтому это компромисс.
-121--3250383-Для C++ во многих случаях просто возвращается по значению. Даже в случае больших объектов RVO часто позволяет избежать ненужного копирования.
Одна из возможностей - передать функции указатель:
void computeFoo(int *dest) {
*dest = 4;
}
Это хорошо, потому что вы можете использовать такую функцию с автоматической переменной:
int foo;
computeFoo(&foo);
С этим подход, вы также сохраняете управление памятью в той же части кода, т.е. вы не можете пропустить malloc только потому, что это происходит где-то внутри функции:
// Compare this:
int *foo = malloc(…);
computeFoo(foo);
free(foo);
// With the following:
int *foo = computeFoo();
free(foo);
Во втором случае легче забыть о свободном, поскольку вы не видите malloc. Это часто, по крайней мере, частично решается соглашением, например: «Если имя функции начинается с XY, это означает, что вы владеете данными, которые она возвращает»
. Интересным угловым случаем возврата указателя на переменную «функция» является объявление variable static:
int* computeFoo() {
static int foo = 4;
return &foo;
}
Конечно, это плохо для нормального программирования, но когда-нибудь может пригодиться.
Ваш второй подход верен. Вам просто нужно четко задокументировать, что вызывающая сторона «владеет» указателем результата и несет ответственность за его освобождение.
Из-за этой дополнительной сложности это редко делается для «маленьких» типов, таких как int, хотя я предполагаю, что вы просто использовали здесь int в качестве примера.
Некоторые люди также предпочтут использовать указатель на уже выделенный объект в качестве параметра, а не выделять объект внутри. Это проясняет, что вызывающая сторона несет ответственность за освобождение объекта (поскольку они выделили его в первую очередь), но делает сайт вызова более подробным, так что это компромисс.
Подход C++ для предотвращения утечек памяти. (по крайней мере, когда вы игнорируете вывод функции)
std::auto_ptr<int> myFunction() {
std::auto_ptr<int> result(new int(4));
return result;
}
Тогда назовите его:
std::auto_ptr<int> myFunctionResult = myFunction();
EDIT: Как указал Джоэл. std::auto_ptr имеет свои собственные недостатки и в целом его следует избегать. Вместо std::auto_ptr вы можете использовать boost::shared_ptr (std::tr1::shared_ptr).
boost::shared_ptr<int> myFunction() {
boost::shared_ptr<int> result(new int(5));
return result;
}
или при использовании компилятора, соответствующего C++0x, можно использовать std::unique_ptr.
std::tr1::unique_ptr<int> myFunction() {
std::tr1::unique_ptr<int> result(new int(5));
return result;
}
Основное отличие заключается в том, что:
shared_ptr позволяет нескольким экземплярам shared_ptr указывать на один и тот же RAW-указатель. Он использует механизм подсчета ссылок, чтобы гарантировать, что память не будет освобождена до тех пор, пока существует хотя бы один экземпляр shared_ptr.
unique_ptr позволяет только одному экземпляру держать указатель, но имеет семантику истинного перемещения, в отличие от auto_ptr.
Второй фрагмент кода правильный.
Чтобы избежать утечек памяти, я позволил соглашению по кодированию помочь мне.
xxxCreate () выделит память для xxx и инициализирует ее. xxxDelete () уничтожит / повредит xxx и освободит его.
xxxInit () инициализирует xxx (никогда не выделяет) xxxDestroy () уничтожает / повреждает xxx (никогда не освобождает)
Кроме того, я пытаюсь добавить код для удаления / уничтожения / освобождения, как только Добавляю код для создания / init / malloc. Он не идеален, но я считаю, что он помогает мне различать предметы, которые нужно освободить, и те, которые не нужны, а также снижает вероятность того, что я забуду освободить что-то позже.
Я бы попробовал что-то вроде этого:
int myFunction2b( int * px )
{
if( px )
{
*px = 4;
return 1;
}
// Choice 1: Assert or Report Error
// Choice 2: Allocate memory for x. Caller has to be written accordingly.
// My choice is 1
assert( 0 && "Argument is NULL pointer" );
return 0;
}
Вы спрашиваете, как правильно вернуть указатель. Это неправильный вопрос, потому что вам следует использовать умные указатели, а не необработанные указатели. scoped_ptr и shared_ptr (доступные в boost и tr1) - хорошие указатели, на которые стоит обратить внимание (например, здесь и здесь )
Если вам нужен необработанный указатель для что-то (например, переход к функции C), метод get () предоставит это.
Если необходимо создать необработанные указатели, например для домашнего задания вы можете использовать malloc () (как и вы) или new внутри функции и надеяться, что вы не забудете освободить память (через free ( ) и delete соответственно) Или, используя идиому с меньшей вероятностью утечки, вы можете создать указатель с помощью new , передать его функции и освободить место с помощью удалить , когда вы закончите с этим. Опять же, используйте умные указатели.