Что делает fflush() с оборванными указателями?

Я наткнулся на эту страницу, на которой показаны распространенные способы создаются.

Код ниже используется для иллюстрации висячих указателей путем возврата адреса локальной переменной:

// The pointer pointing to local variable becomes  
// dangling when local variable is static.
#include<stdio.h>

int *fun()
{
    // x is local variable and goes out of scope 
    // after an execution of fun() is over.
    int x = 5;

    return &x;
}

// Driver Code
int main()
{
    int *p = fun();
    fflush(stdout);

    // p points to something which is not valid anymore
    printf("%d", *p);
    return 0;
}

При запуске это предупреждение компилятора, которое я получаю (как и ожидалось):

 In function 'fun':
12:2: warning: function returns address of local variable [-Wreturn-local-addr]
  return &x;
  ^

И это результат, который я получаю (пока хорошо):

32743

Однако, когда я комментирую строку fflush(stdout), я получаю следующий вывод (с тем же предупреждением компилятора):

5

В чем причина такого поведения? Как именно присутствие/отсутствие команды fflush вызывает такое изменение поведения?


person Arpith    schedule 23.04.2017    source источник
comment
stackoverflow.com/a/6445794/12711   -  person Michael Burr    schedule 23.04.2017
comment
В чем причина такого поведения? Это неопределенное поведение. Использование вами fflush не имеет значения. (fflush очищает содержимое stdout, но, поскольку у вас ничего не буферизовано, это не служит законной цели). Вы можете получить 5, вы можете получить Segmentation Fault, это не определено.   -  person David C. Rankin    schedule 23.04.2017


Ответы (2)


Как вы упомянули, возвращать указатель на объект в стеке плохо. Причина, по которой вы видите проблему только с вашим вызовом fflush(), заключается в том, что стек не изменяется, если его там нет. То есть 5 все еще на месте, поэтому разыменование указателя по-прежнему дает вам 5. Если вы вызываете функцию (вероятно, почти любую функцию) между fun и printf, она почти наверняка перезапишет это место в стеке, в результате чего последующее разыменование вернет весь мусор, который эта функция там оставила.

person Carl Norum    schedule 23.04.2017
comment
Не будет ли printf() также помещена в стек? Что может привести к тому, что адрес, указанный указателем, будет перезаписан чем-то другим (возможно)? - person Arpith; 23.04.2017
comment
Все возможно. Хотя в вашем случае этого явно не происходит. - person Carl Norum; 23.04.2017
comment
Я понимаю. Попробовал простой сон (1), и это помогло. Спасибо за помощь @CarlNorum - person Arpith; 23.04.2017

Это связано с тем, что вызов fflush(stdout) записывает в стек, где был x.

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

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

Затем эти вещи извлекаются из стека одна за другой, просто меняя место, где, по мнению ЦП, находится вершина стека. Это означает, что данные все еще существуют, но их существование не гарантируется.

Вызов другой функции после fun() перезаписывает предыдущие значения над вершиной стека, в данном случае со значением stdout, и поэтому значение, на которое ссылается указатель, изменяется.

Без вызова другой функции данные остаются там и по-прежнему действительны при разыменовании указателя.

person Community    schedule 23.04.2017
comment
Однажды я кодировал систему, в которой стек не использовался для локальных переменных и аргументов функций. - person M.M; 23.04.2017
comment
Если OP программирует в системе x86 или x86-64 (большинство персональных компьютеров), то для локальных переменных и параметров функций используется стек. Если они программируют встроенную систему, то OP в идеале должен знать об особенностях выбранной ими системы и действовать соответственно. Возврат указателя на локальную переменную в любом случае является поведением undefined. - person ; 23.04.2017