область локальных переменных функции в C - PullRequest
4 голосов
/ 01 декабря 2009

Я слышал о следующем сценарии, когда начинал программировать на C.

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

Но мой приведенный ниже пример кода выводит значение 50. Я компилирую код с помощью новейшего компилятора GCC.

#include <stdio.h>

int * left();

int main()
{
      int *p=left();
      printf("%d\n",*p);
      return 0;
}

int * left()
{
        int i=50;
        return &i;
}

Просветите меня по этому вопросу.

Могу ли я узнать поведение в C ++ ?? Это похоже на c ..

Ответы [ 8 ]

12 голосов
/ 01 декабря 2009

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

7 голосов
/ 01 декабря 2009

Измените его, чтобы добавить второй вызов к printf, и вы увидите значение, отличное от первого раза. Скомпилируйте его с включенными оптимизациями, и вы увидите другой набор значений. Сделайте что-нибудь со значением, и вы войдете в неопределенную территорию, что означает, что компилятор может вызывать демонов через носовые проходы.

В моей системе я вижу 50, а затем 0; с оптимизацией я вижу 0 и затем 32767.

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

Когда функция возвращается, локальное хранилище, которое она использовала в стеке, теперь считается программой "неиспользуемой", поскольку стек больше не поднимается так высоко. Однако, как правило, значения все еще там, так как нет необходимости срочно их очищать. Память также все еще принадлежит программе, поскольку нет смысла возвращать память операционной системе по несколько байт за раз. Так что для вашего конкретного примера, при обстоятельствах, в которых вы его скомпилировали, указанная память все еще содержит значение 50. Официально, однако, значение *p равно неопределенно , и попытки использовать его приводят к поведению undefined .

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

6 голосов
/ 01 декабря 2009

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

Это работает по счастливой случайности / случайности и больше ничего. Не полагайтесь на это поведение, потому что оно вернется, чтобы укусить вас.

3 голосов
/ 01 декабря 2009

Посмотрите на этот модифицированный пример, где он четко показывает, находится ли что-то среднее между ними:

int *p=left();
// right here
printf("%d\n",*p);

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

int main()
{
    int *p=left();
    right(); // Look here!  
    printf("%d\n",*p); // prints -50 instead of 50
    return 0;
}
...
int * right()
{
    int i=-50;
    return &i;
}
2 голосов
/ 01 декабря 2009

Что касается стандарта C, значение не определено.

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

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

2 голосов
/ 01 декабря 2009

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

2 голосов
/ 01 декабря 2009

Работает ДТП . Расположение в памяти, обозначенное p, все еще содержит целое значение 50, когда вызывается printf(). Но вызов любой функции внутри main() между left() и printf() перезапишет ее.

Например, я не знаю, что произойдет в вашей реализации, если вы измените свой printf() вызов на:

printf("abs(%d) = %d ???\n", *p, abs(*p));

Не делай этого!

1 голос
/ 01 декабря 2009

Неопределенное поведение. i уничтожается при выходе left(). У вас есть адрес для мусора в p. Компилятор еще не уничтожил i по счастливой случайности.

...