Возврат адреса локальной переменной
От: salvequick  
Дата: 25.04.12 17:24
Оценка:
Коллеги,

Казалось бы очевидная ошибка и понятно, что так делать нельзя. Это просто эксперимент.

В каком случае в value будет не равняться 5 в приведенном ниже примере?
Функция возвращает адрес локальной переменной и это ошибка. Значение value скорее всего будет 5, но гарантии нет.
Если подумать то получается, что в value то всегда будет 5.
Даже если это многопоточное приложение и другой поток прервет выполнение основного потока то стековая память то у каждого потока все равно разная.
Что же должно случиться, чтобы в value было не 5?
Я сам прокручивал самые разные версии вплоть до влияния ОС, процессорной архитектуры, типа компилятора и ключей оптимизации кода. Никаких результатов стабильно держится value=5 при всех комбинациях.
gcc, Visual Studio, отладоная сборка неотладочная , оптимизация включена/выключена.
Хотя программа конечно слишком простая в примере.

int *f(void)
{int a=5;
return a;
}

int main()
{
int value;
int *m=f();    
value=m;
}
Re: Возврат адреса локальной переменной
От: zaufi Земля  
Дата: 25.04.12 17:32
Оценка:
Здравствуйте, salvequick, Вы писали:

S>Что же должно случиться, чтобы в value было не 5?


примерно вот что:

#include <iostream>

int* foo()
{
    int a = 5;
    return &a;
}

int* bar()
{
    int a = 10;
    return &a;
}

int main()
{
    int value;
    int* m = foo();
    int* n = bar();
    value = *m;
    std::cout << value << std::endl;
    return 0;
}


zaufi@gentop /work/tests $ g++ -o fail fail.cc 
fail.cc: In function 'int* foo()':
fail.cc:5:9: warning: address of local variable 'a' returned [enabled by default]
fail.cc: In function 'int* bar()':
fail.cc:11:9: warning: address of local variable 'a' returned [enabled by default]
zaufi@gentop /work/tests $ ./fail 
10


вызов foo оставляет в стэке "след"... любой следующий вызов в данном случае перезатрет его...
Re: Возврат адреса локальной переменной
От: vsb Казахстан  
Дата: 25.04.12 17:32
Оценка:
Здравствуйте, salvequick, Вы писали:

S>Что же должно случиться, чтобы в value было не 5?


Между вызовом функции f() и чтением m вызвать другую функцию, которая поменяет стек. А может не поменяет, или поменяет, если по сети приходит слишком длинный пакет (два раза в сутки), мало ли какая логика там.

PS полагаю, что в примере опечатка и имелось в виду return &a;
Re: Возврат адреса локальной переменной
От: Vamp Россия  
Дата: 25.04.12 17:36
Оценка:
S>Я сам прокручивал самые разные версии вплоть до влияния ОС, процессорной архитектуры, типа компилятора и ключей оптимизации кода. Никаких результатов стабильно держится value=5 при всех комбинациях.
Исправив ошибки (в таком виде она просто не компилируется) и добавив всего одну строчку, можно увидеть чем чреват возврат адреса локальной переменной:

#include <iostream>

using namespace std;

int* f() {
    int a = 5;
    return &a;
}


int main() {

    int* pv = f();
    cout << "PV: " << *pv << endl;
}


Результат:

PV: 29356624

Да здравствует мыло душистое и веревка пушистая.
Re: Возврат адреса локальной переменной
От: Libsdebs  
Дата: 25.04.12 17:38
Оценка:
Здравствуйте, salvequick, Вы писали:

S>
S>int *f(void)
S>{int a=5;
S>return a;
S>}

S>int main()
S>{
S>int value;
S>int *m=f();    
S>value=m;
S>}

S>


1. разберитесь c & и * в этом примере
2. rtfm по стеку — http://en.wikipedia.org/wiki/Call_stack и т.п.
3. после вызова такой функции (с сохранением результата в указатель/сcылку), вызовете какую-нибудь другую функцию, а потом прорвете значение по сохранённому указателю/ссылке
Re[2]: Возврат адреса локальной переменной
От: salvequick  
Дата: 25.04.12 17:44
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Исправив ошибки (в таком виде она просто не компилируется) и добавив всего одну строчку, можно увидеть чем чреват возврат адреса локальной переменной:


Я рассматриваю чистый С.
Да возвращается там адрес, это отпечатка.
Re[2]: Возврат адреса локальной переменной
От: salvequick  
Дата: 25.04.12 17:48
Оценка:
Здравствуйте, vsb, Вы писали:


vsb>Между вызовом функции f() и чтением m вызвать другую функцию, которая поменяет стек. А может не поменяет, или поменяет, если по сети приходит слишком длинный пакет (два раза в сутки), мало ли какая логика там.


Условия уточняю:

между int *m=f();
и value=m;
никаких вызовов нет. ничего не вызывается ,никто не трогает стек.

Используется чистый С.
Что будет потом по этому адресу не важно. Важно только то что в строке value=m;

vsb>PS полагаю, что в примере опечатка и имелось в виду return &a;

да опечатка.
Re: Возврат адреса локальной переменной
От: watch-maker  
Дата: 25.04.12 17:54
Оценка:
Здравствуйте, salvequick, Вы писали:

S>Даже если это многопоточное приложение и другой поток прервет выполнение основного потока то стековая память то у каждого потока все равно разная.

Ещё есть системные прерывания и сигналы.
Если пришел сигнал, то его обработчик может и затереть соответствующую память. Впрочем, если ОС гарантирует наличие Red Zone, то может и не затереть.
Re: Возврат адреса локальной переменной
От: pepelac Россия  
Дата: 25.04.12 17:55
Оценка:
Здравствуйте, salvequick, Вы писали:

Если значение по возвращенному адресу вытащить сразу после вызова функции, как у вас, а не как тут предлагают с cout)) то есть без использования стека, то действительно очень сложно представить ситуацию, когда бы значение испортилось. Можно подумать в сторону того, что некоторые компиляторы могут вернуть значение в регистре(для АРМ вполне вероятно), а все разыменования заменить на простое присваивание из регистра выкинув указатель m, тогда достаточно будет:
int main()
{
  int value;
  int *m=f();
  int n = 9; // n может храниться в том же регистре
  value=*m;
}

В общем от компилятора все зависит, ибо UB.
Re[2]: Возврат адреса локальной переменной
От: salvequick  
Дата: 25.04.12 18:37
Оценка:
Здравствуйте, watch-maker, Вы писали:

WM>Если пришел сигнал, то его обработчик может и затереть соответствующую память. Впрочем, если ОС гарантирует наличие Red Zone, то может и не затереть.


У такого обработчика дожен быть совершенно другой контекст по идее.
И следовательно другой стек.
Re: Возврат адреса локальной переменной
От: rg45 СССР  
Дата: 25.04.12 19:25
Оценка:
Здравствуйте, salvequick, Вы писали:

S>Коллеги,


S>Казалось бы очевидная ошибка и понятно, что так делать нельзя. Это просто эксперимент.


S>В каком случае в value будет не равняться 5 в приведенном ниже примере?

S>Функция возвращает адрес локальной переменной и это ошибка. Значение value скорее всего будет 5, но гарантии нет.
S>Если подумать то получается, что в value то всегда будет 5.
S>Даже если это многопоточное приложение и другой поток прервет выполнение основного потока то стековая память то у каждого потока все равно разная.
S>Что же должно случиться, чтобы в value было не 5?


В отладочной конфигурации компилятор вполне может помечать освобожденную стековую память каким-нибудь определенным значением — для облегчения диагностики подобных ошибок.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Возврат адреса локальной переменной
От: MasterZiv СССР  
Дата: 25.04.12 20:27
Оценка: +2
> Я сам прокручивал самые разные версии вплоть до влияния ОС, процессорной
> архитектуры, типа компилятора и ключей оптимизации кода. Никаких результатов
> стабильно держится value=5 при всех комбинациях.
> gcc, Visual Studio, отладоная сборка неотладочная , оптимизация включена/выключена.

Зачем об этом рассуждать, если так делать всё равно нельзя?
Вот гляди, вот человек допустим берёт в руки нож, и со всей дури
втыкает себе в живот. Что будет ? Я думаю, что во всех без исключения
случаях человеку будет плохо. Я даже представить себе не могу другой
исход. Ну разве что он промахнётся. Ну и что ? Нам полезно это знание?
Да нет, не будет он себя ножём в живот тыкать. Просто не будет.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Возврат адреса локальной переменной
От: watch-maker  
Дата: 25.04.12 20:34
Оценка:
Здравствуйте, salvequick, Вы писали:

S>У такого обработчика дожен быть совершенно другой контекст по идее.

По какой ещё идее? Обработчик же может принадлежать программе.

S>И следовательно другой стек.

Создавать новый полноценный стек на каждый приход сигнала. Шутишь?

Когда происходит прерывание или приходит сигнал, то этим обычно занимается ядро, у него конечно же есть свой стек. Но сама-то обработка происходит уже в процессе.
И разумеется фрейм под обработчик выделяется в стеке процесса, вот пример — http://ideone.com/9RTQo — никакого нового стека.
Всё что находится ниже указателя указателя на вершину стека при этом затирается, включая и память под локальные переменных уже завершенных функций. Не затирается только Red Zone, но её наличие и размеры зависят от ОС и архитектуры процессора (например, в Windows её нет, во многих x86 — тоже, а вот в *nix с amd64 она есть).



Да, тут ещё можно вспомнить всякие исторические штуки, вроде обработки прерываний в DOS, где стек процесса использовался как минимум для сохранения регистров IP и FLAGS. Но, наверно, DOS уже слишком устаревший пример.
Re[2]: Возврат адреса локальной переменной
От: Vamp Россия  
Дата: 25.04.12 20:50
Оценка:
MZ>Зачем об этом рассуждать, если так делать всё равно нельзя?
Не согласен, кстати. По двум причинам:
1. Человек разумный хочет понимать, почему нельзя. В чем причина запрета? В каких случаях все-таки можно? Классический пример — старые контейнеры в стандарте 98. Как известно, в них запрещено класть авто_птр. А почему? Потому, что сортировка нормально работать не будет. Если не понимать, почему нельзя, то придется обходиться без автопойнтеров в контейнере — а они бывают очень полезные. А если понимать, то можно класть, но не сортировать стандартной сортировкой.
2. Надо знать, к каким последствиям приводит нарушение запрета, чтобы по симптомам уметь вычислять причину и исправлять ее.

MZ>Вот гляди, вот человек допустим берёт в руки нож, и со всей дури

MZ>втыкает себе в живот. Что будет ? Я думаю, что во всех без исключения
MZ>случаях человеку будет плохо. Я даже представить себе не могу другой
MZ>исход.
Никогда не видал йогинов?
Да здравствует мыло душистое и веревка пушистая.
Re[3]: Возврат адреса локальной переменной
От: MasterZiv СССР  
Дата: 25.04.12 21:06
Оценка:
On 04/26/2012 12:50 AM, Vamp wrote:

> 1. Человек разумный хочет понимать, почему нельзя. В чем причина запрета? В


Больно будет как минимум! Что тут не понятного ?

> каких случаях все-таки можно? Классический пример — старые контейнеры в

> стандарте 98. Как известно, в них запрещено класть авто_птр. А почему? Потому,
> что сортировка нормально работать не будет.

Нет, не поэтому. а ПО СТАНДАРТУ!

Если не понимать, почему нельзя, то
> придется обходиться без автопойнтеров в контейнере — а они бывают очень
> полезные. А если понимать, то можно класть, но не сортировать стандартной
> сортировкой.

Нельзя даже класть.

> 2. Надо знать, к каким последствиям приводит нарушение запрета, чтобы по

> симптомам уметь вычислять причину и исправлять ее.

Это UB, симптомы могут быт ЛЮБЫЕ.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Возврат адреса локальной переменной
От: Erop Россия  
Дата: 26.04.12 00:23
Оценка:
Здравствуйте, MasterZiv, Вы писали:

>> 2. Надо знать, к каким последствиям приводит нарушение запрета, чтобы по

>> симптомам уметь вычислять причину и исправлять ее.

MZ>Это UB, симптомы могут быт ЛЮБЫЕ.


Это в теории могут. А в конкретных реализациях не такие уж и любые
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Возврат адреса локальной переменной
От: Erop Россия  
Дата: 26.04.12 00:30
Оценка:
Здравствуйте, salvequick, Вы писали:

S>Я рассматриваю чистый С.

Это не важно.
Оптимизирующий компилятор С тоже умеет переставлять местами функции.
Давай немного изменим твой пример?
int* makeUB( int i ) { return &i; }

int main()
{
    int* p1 = makeUB( 5 );
    int result = *p1;
    int* p2 = makeUB( 10 );
    printf( "*p2 = %d; *p1 = %d\n", *p2, result );
    return result;
}

Оптимизирующий компилятор, по идее, может переставить местами
    int result = *p1;
    int* p2 = makeUB( 10 );
в зависимости от не пойми чего.

Кроме того, бывают однопоточные платформы, на которых можно словить прерывание...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Возврат адреса локальной переменной
От: Erop Россия  
Дата: 26.04.12 03:51
Оценка: 11 (2)
Здравствуйте, MasterZiv, Вы писали:


MZ>Зачем об этом рассуждать, если так делать всё равно нельзя?

MZ>Вот гляди, вот человек допустим берёт в руки нож, и со всей дури
MZ>втыкает себе в живот. Что будет ?
От человека зависит. Например, история советской медицины знает случай удаления апендицита самому себе...

Ясен пень, что от хорошей жизни так не делают, но кто обещал, что жизнь всегда будет хорошей?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Возврат адреса локальной переменной
От: c-smile Канада http://terrainformatica.com
Дата: 26.04.12 04:13
Оценка:
Здравствуйте, MasterZiv, Вы писали:


>> Я сам прокручивал самые разные версии вплоть до влияния ОС, процессорной

>> архитектуры, типа компилятора и ключей оптимизации кода. Никаких результатов
>> стабильно держится value=5 при всех комбинациях.
>> gcc, Visual Studio, отладоная сборка неотладочная , оптимизация включена/выключена.

MZ>Зачем об этом рассуждать, если так делать всё равно нельзя?


Это еще почему?

MZ>Вот гляди, вот человек допустим берёт в руки нож, и со всей дури

MZ>втыкает себе в живот. Что будет ? Я думаю, что во всех без исключения

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

MZ>случаях человеку будет плохо. Я даже представить себе не могу другой

MZ>исход. Ну разве что он промахнётся. Ну и что ? Нам полезно это знание?

Знание полезно. Хотя бы за для понять как устроен стек.

Взятия адреса внутренней переменной функции используется например в консервативных GC для определения
текущей глубины стека. Да мало ли...
Re: Возврат адреса локальной переменной
От: dilmah США  
Дата: 26.04.12 04:44
Оценка:
S>Казалось бы очевидная ошибка и понятно, что так делать нельзя. Это просто эксперимент.
S>В каком случае в value будет не равняться 5 в приведенном ниже примере?

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

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