возвратите указатель на данные, объявленные в функции

Я знаю, что это не будет работать, потому что переменная x уничтожается, когда функция возвращается:

int* myFunction()
{
    int x = 4; return &x;
}

таким образом, как я правильно возвращаю указатель на что-то, что я создаю в функции, и с чем я должен заботиться? Как я избегаю утечек памяти?

Я также использовал malloc:

int* myFunction2()
{
    int* x = (int*)malloc(sizeof int); *x = 4; return x;
}

Как Вы правильно делаете это - в C и C++?

9
задан sp. 23 February 2010 в 18:05
поделиться

11 ответов

Для C ++ вы можете использовать интеллектуальный указатель для принудительной передачи права собственности. auto_ptr или boost :: shared_ptr - хорошие варианты.

7
ответ дан 4 December 2019 в 08:33
поделиться

Есть другой подход - объявить 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! Использование статических переменных в области функций не является безопасным методом.

1
ответ дан 4 December 2019 в 08:33
поделиться

Как правило, лучше использовать указатели Boost или TR1. Это позволяет избежать накладных расходов на копирование и обеспечивает полуавтоматическое удаление. Итак, ваша функция должна выглядеть так:

boost::shared_ptr<int> myFunction2()
{
    boost::shared_ptr<int> x = new int; 

    *x = 4; 
    return x;
}

Другой вариант - просто разрешить копирование. Это не так уж плохо, если объект небольшой (как этот) или вы можете организовать создание объекта в операторе return. Компилятор обычно оптимизирует копию, если объект создается в операторе return.

0
ответ дан 4 December 2019 в 08:33
поделиться

В 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 .

РЕДАКТИРОВАТЬ: Как отмечалось в другом ответе, вам также необходимо задокументировать, что указатель должен быть освобожден вызывающим абонентом позже. Иначе может произойти утечка памяти.

1
ответ дан 4 December 2019 в 08:33
поделиться

Он определяет «sram» как указатель на память, начинающийся с нуля. Вы можете получить доступ к памяти через указатель, например, sram [0] - это адрес ноль, sram [1] - это материал по адресу один и т.д.

В частности, это отсчет 0, чтобы быть указателем на массив неподписанных символов и проходить через него косвенно (оставляя массив неподписанных символов).

Подобный результат может быть получен с помощью

#define sram ((unsigned char*)0)

Он также полностью не определен в стандарте С, но это не мешает людям использовать его и иметь ангелы вылетают из пупков.

-121--3329716-

Ваш второй подход верен. Просто нужно четко документировать, что вызывающий «владеет» указателем результата, и несет ответственность за его освобождение.

Из-за этой дополнительной сложности это редко делается для «малых» типов, таких как int, хотя я предполагаю, что вы просто использовали int здесь ради примера.

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

-121--3250383-

Для C++ во многих случаях просто возвращается по значению. Даже в случае больших объектов RVO часто позволяет избежать ненужного копирования.

5
ответ дан 4 December 2019 в 08:33
поделиться

Одна из возможностей - передать функции указатель:

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;
}

Конечно, это плохо для нормального программирования, но когда-нибудь может пригодиться.

3
ответ дан 4 December 2019 в 08:33
поделиться

Ваш второй подход верен. Вам просто нужно четко задокументировать, что вызывающая сторона «владеет» указателем результата и несет ответственность за его освобождение.

Из-за этой дополнительной сложности это редко делается для «маленьких» типов, таких как int, хотя я предполагаю, что вы просто использовали здесь int в качестве примера.

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

6
ответ дан 4 December 2019 в 08:33
поделиться

Подход 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.

2
ответ дан 4 December 2019 в 08:33
поделиться

Второй фрагмент кода правильный.

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

xxxCreate () выделит память для xxx и инициализирует ее. xxxDelete () уничтожит / повредит xxx и освободит его.

xxxInit () инициализирует xxx (никогда не выделяет) xxxDestroy () уничтожает / повреждает xxx (никогда не освобождает)

Кроме того, я пытаюсь добавить код для удаления / уничтожения / освобождения, как только Добавляю код для создания / init / malloc. Он не идеален, но я считаю, что он помогает мне различать предметы, которые нужно освободить, и те, которые не нужны, а также снижает вероятность того, что я забуду освободить что-то позже.

1
ответ дан 4 December 2019 в 08:33
поделиться

Я бы попробовал что-то вроде этого:

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;

}
0
ответ дан 4 December 2019 в 08:33
поделиться

Вы спрашиваете, как правильно вернуть указатель. Это неправильный вопрос, потому что вам следует использовать умные указатели, а не необработанные указатели. scoped_ptr и shared_ptr (доступные в boost и tr1) - хорошие указатели, на которые стоит обратить внимание (например, здесь и здесь )

Если вам нужен необработанный указатель для что-то (например, переход к функции C), метод get () предоставит это.

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

-1
ответ дан 4 December 2019 в 08:33
поделиться