Возвращает локальную переменную из функции в C

#include <stdio.h>

int foo1(void)
{
    int p;
    p = 99;
    return p;
}

char *foo2(void)
{
    char buffer[] = "test_123";
    return buffer;
}

int *foo3(void)
{
    int t[3] = {1,2,3};
    return t;
}

int main(void)
{
    int *p;
    char *s;

    printf("foo1: %d\n", foo1());
    printf("foo2: %s\n", foo2());
    printf("foo3: %d, %d, %d\n", p[0], p[1], p[2]);
    return 0;
}

Когда я компилирую это с помощью gcc -ansi -pedantic -W -Wall, компилятор выдает предупреждающие сообщения для foo2() и foo3():

warning: function returns address of local variable

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

Может кто-нибудь пролить свет на эту проблему? Спасибо заранее!

Ответ 1

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

Ответ 2

Для foo1() вы возвращаете копию локальной переменной, а не локальную переменную.

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

Ответ 3

Любая переменная имеет некоторое пространство в памяти. Указатель ссылается на это пространство. Пространство, которое занимают локальные переменные, освобождается при возврате вызова функции, что означает, что он может и будет использоваться повторно для других целей. Как следствие, ссылки на это пространство собираются привести к чему-то совершенно не связанному. Массивы в C реализуются как указатели, поэтому это приводит к их применению. И постоянные массивы, объявленные в функции, также считаются локальными.

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

Ответ 4

В дополнение к тому, что было сказано, вот мое мнение о возвращении указателей от функций:

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

Указатель, возвращаемый функцией, может указывать на следующие виды переменных:

  • Локальные переменные. Этот случай всегда является ошибкой в ​​программе.

  • Те же данные, что и один из параметров функции. Это плохое программирование, так как один и тот же параметр будет использоваться дважды в функции, занимая ненужное пространство стека при вызове функции.

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

  • Глобальные переменные. Это ошибка в многопроцессорной среде, так как это может сделать функцию небезопасной для нескольких процессов.

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

Кроме того, для глобальных и статических переменных рассмотрим эту ситуацию:

static uint8 static_str[6];

uint8* func(const uint8 str[6])
{
  uint8 i;

  for(i=0; i<6; i++)
  {
    static_str[i] = str[i];
  }

  return static_str;
}

int main()
{
  print_strings(func("hello"), func("world"));
}

Вывод функции, печатающей две строки, будет либо "hello hello", либо "world world" (в зависимости от порядка оценки параметров функции).

Ответ 5

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