2016-07-12 18 views
-6

Возвращает указатель, объявленный внутри функции, считающейся плохой практикой? Пример:Указатель возвращает и область действия

int* foo(void) 
{ 
    int * ret_val = 1234; 

    return ret_val; 
} 

Я считаю, что вы предполагается использовать:

static int * ret_val = 1234; 

Но опять же, не все еще считается плохой практикой вернуть память за пределами вашей сферы?

+1

Вы не выделили какую-либо память для возврата! Так что это плохая практика. Однако, если бы вы использовали 'malloc()' для распределения памяти, тогда, поскольку память malloc может использоваться между вызовами функций, тогда это не было бы плохой практикой. – Haris

+0

'* ret_val = 1234;' плохой сам по себе. Что касается вопроса, если вы не используете 'new', то это обман http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its- scope – NathanOliver

+1

Да, для возвращения динамически выделенного объекта является плохой практикой, так как сложнее отслеживать возможные утечки памяти. – meJustAndrew

ответ

2

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

Проблема с вышеуказанным кодом заключается в том, что вы разыскиваете указатель, не присваивая ему ничего. Это: *ret_val = 1234; присваивает значение 1234 по адресу ret_val указывает на, но ret_val ничего не назначен.

Если с другой стороны, вы сделали это:

int* foo(void) 
{ 
    int * ret_val; 

    ret_val = malloc(sizeof(int)); 
    *ret_val = 1234; 

    return ret_val; 
} 

Это нормально, так как вы возвращающая указатель на динамически выделенную память.

+1

«Память, которая все еще в сфере видимости»: что это значит? –

+0

@ScottHunter Добавлено более подробно. – dbush

2

Первичный преступник

*ret_val = 1234; 

применяется с неинициализированного указателем.

Я считаю, что вы предполагается использовать:

static int* ret_val; 

Нет, чтобы исправить, что вы могли бы использовать

int* foo() { 
    static int_val = 1234; 
    return &int_val; 
} 

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

+2

Зачем вам это делать? Не научите новичков злоупотреблять «статикой», это ужасная практика, если вы точно не знаете, что делаете. – interjay

+0

@interjay Я не решался закрыть это как обман для [Можно ли получить доступ к локальной папке за пределами ее возможностей?] (Http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be- accessed-outside-its-scope), потому что вопрос не совсем соответствует ситуации. –

+0

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

0

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

int* foo(void) 
{ 
    int a = 1337; 
    return &a; //Don't do this! 
} 

Но иногда это может быть полезно:

int* copy_array(int *array, int size) 
{ 
    int *v = (int*) malloc (size * sizeof (int)); 
    //..copy the array.. 
    return v; 
} 

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

+0

Я бы подумал о возврате необработанного указателя на динамически выделенную память, а в C++ – Slava

+0

Да, вместо этого он должен использовать контейнеры STL (вместо необработанных массивов) или другие решения. –

+0

Ну, в его ответе есть тег 'c', поэтому я использовал его. Также эта функция типична в C, на C++ вы должны использовать STL. Во всяком случае, я не имею никаких оснований отрицать этот ответ из-за этого. –

0

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

  • Не возвращает указатель на локальную переменную, как это будет уничтожен, когда функция выйдет и вызовет неопределенное поведение при разыменовании
  • Если функция выделяет память, функция вызывающего абонента отвечает за освобождение указателя, когда он больше не нужен, чтобы избежать утечек памяти.
  • Если функция обрабатывает массив в fo п.м. указателя, он должен знать размер массива, чтобы избежать неопределенное поведение, как правило, в качестве дополнительного size параметра или глобальной константы
0

Вы должны рассмотреть:

int *foo() { 
     int *ret_val = new int(1234); 
     return ret_val; 
} 

вместо (плохой код):

int* foo(void) 
{ 
    int * ret_val; 

    *ret_val = 1234; 

    return ret_val; // dangling pointer! 
} 

иначе вы будете иметь оборванных указатель (второй пример). Очевидно, не забудьте выпустить память. Вы должны также рассмотреть возможность использования смарт-указатели: https://en.wikipedia.org/wiki/Smart_pointer

Как вы определили как C++ и C языки выше код в C++ (оператор новый), пожалуйста, обратитесь к (например) @Mattia F. ответ на ANSI C.

+0

@Amadeus Очевидно нет. Для ANSI C необходимо учитывать malloc. В вопросе есть тег C++, поэтому не должно быть проблемой. – nosbor

+0

@Amadeus Должно быть хорошо сейчас – nosbor

0

Указатель как ярлык или гиперссылка. Создание только ярлыка не устанавливает программу. Создание гиперссылки не волшебным образом настраивает весь сайт.

То же, что и указатель: a int* - это указатель на целое число. Это не целое число. Таким образом, вы должны убедиться, что указатель указывает на что-то значимое, прежде чем пытаться следовать указателю.

Один из способов сделать так, чтобы создать целое и наборы указатель, чтобы указать на него:

int* ret_val = new int; 

Это создаст целое число (new оператора), а затем устанавливает ret_val указатель на точку к нему.

Объекты, созданные через new, существуют до тех пор, пока вы явно не уничтожите их через delete или когда ваше приложение закончится. По этой причине, если у вас есть функция new int, она безопасна для ее доступа, даже если функция завершается.

Что бы dangereous, и, вероятно, вызвано ваше беспокойство, если вы бы установить указатель ret_val не новое целое, но к существующей локальной переменной, т.е.

int myInteger; 
int* ret_val = &myInteger; 

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


Таким образом, функция формы:

int* foo(void) 
{ 
    int* ret_val = new int; 
    *ret_val = 1234; 
    return ret_val; 
} 

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

Смежные вопросы