Предположим, что у вас есть следующий стек вызовов в вашем приложении:
- Основная процедура
- Локальные переменные Function1
- Локальные переменные Function2 <- STACK POINTER </li>
В этом случае main вызывает function1, а function1 вызывает function2.
Теперь предположим, что function2 вызывает function3, а возвращаемое значение function3 возвращается в стеке:
- Основная процедура
- Локальные переменные Function1
- Локальные переменные Function2
- Локальные переменные Function3, включая возвращаемое значение <- STACK POINTER </li>
Функция 3 сохраняет возвращаемое значение в стеке, а затем возвращает. Возвращение означает, снова уменьшая указатель стека, поэтому стек становится таким:
- Основная процедура
- Локальные переменные Function1
- Локальные переменные Function2 <- STACK POINTER </li>
Видите ли, стекового фрейма функции 3 здесь больше нет.
Ну, на самом деле я немного соврал. Кадр стека все еще там:
- Основная процедура
- Локальные переменные Function1
- Локальные переменные Function2 <- STACK POINTER </li>
- Локальные переменные Function3, включая возвращаемое значение
Так что, кажется, безопасно получить доступ к стеку, чтобы получить возвращаемое значение.
Но, если есть прерывание, ПОСЛЕ возврата функции 3, но ДО того, как функция 2 получит возвращаемое значение из стека, мы получим следующее:
- Основная процедура
- Локальные переменные Function1
- Локальные переменные Function2
- Локальные переменные функции прерывания <- STACK POINTER </li>
И теперь кадр стека действительно перезаписан, и возвращаемое значение, в котором мы отчаянно нуждались, ушло.
Поэтому возвращать возвращаемое значение в стеке небезопасно.
Проблема похожа на проблему, показанную в этом простом фрагменте кода C:
char *buf = (char *)malloc(100*sizeof(char *));
strcpy (buf, "Hello World");
free (buf);
printf ("Buffer is %s\n",buf);
В большинстве случаев память, которая использовалась для buf, все еще будет содержать содержимое «Hello World», но она может пойти ужасно неправильно, если кто-то сможет выделить память после вызова free, но до вызова printf. Один из таких примеров - в многопоточных приложениях (и мы уже столкнулись с этой проблемой внутри), как показано здесь:
THREAD 1: THREAD 2:
--------- ---------
char *buf = (char *)malloc(100);
strcpy (buf, "Hello World");
free (buf);
char *mybuf = (char *)malloc(100);
strcpy (mybuf, "This is my string");
printf ("Buffer is %s\n",buf);
printf is Thread 1 теперь может печатать «Hello World» или «This my string». Все может случиться.