Чтобы понять, ПОЧЕМУ вам не следует пытаться вернуть указатель на локальную переменную, вам необходимо визуализировать, как локальные переменные размещаются в первую очередь.
Локальные переменные размещаются в STACK.Стек является зарезервированной областью памяти, имеющей основное назначение, оставляя «крошечный» след адресов памяти, по которому ЦП должен переходить после завершения выполнения подпрограммы.
Перед вводом подпрограммы (обычно с помощью инструкции CALL
на машинном языке в архитектурах x86) ЦПУ помещает в стек адрес инструкции сразу после ВЫЗОВА.
ret_address_N
. . . . . . .
ret_address_3
ret_address_2
ret_address_1
Когда подпрограмма заканчивается, инструкция RET
urn заставляет процессор извлекать самый последний адрес из стека и перенаправляет выполнение путем перехода к нему, эффективно возобновляя выполнение подпрограммы или функции, которая инициировала вызов.
Такое расположение стека очень мощное, поскольку оно позволяет вам вкладывать большое количество независимых вызовов подпрограмм (позволяя создавать универсальные библиотеки многократного использования), а также позволяет рекурсивные вызовы, когда функция может вызывать себя (напрямую или косвенно)., вложенной подпрограммой).
Кроме того, ничто не мешает помещать пользовательские данные в стек (для этого есть специальные инструкции процессора) КАК ДОЛГО СОСТОЯНИЕ СТЕКОВОГО СОСТОЯНИЯВОССТАНОВЛЕНО ПЕРЕД ВОЗВРАТОМ ИЗ СУБРУТИНЫ , прочееВ случае, когда инструкции RET выдвигают ожидаемый адрес возврата, он извлекает мусор и пытается перейти к нему, скорее всего, произойдет сбой.(Кстати, это также то, сколько вредоносных программ работает, перезаписывая стек с действительным адресом и заставляя ЦПУ переходить к вредоносному коду при выполнении инструкции RET)
Может использоваться эта функция стека,например, чтобы сохранить исходное состояние регистров ЦП, которые были изменены внутри подпрограммы - что позволяет коду восстанавливать их значения до выхода из подпрограммы, чтобы подпрограмма вызывающей стороны могла видеть регистры в том же состоянии, в котором они находились, ДО выполнения подпрограммыCALL.
Такие языки, как C, также используют эту функцию для распределения локальных переменных путем установки стекового фрейма .Компилятор в основном суммирует, сколько байтов требуется для учета каждой локальной переменной в определенной подпрограмме, и будет выдавать инструкции ЦП, которые сместят вершину стека на эту вычисленную сумму в байтах при вызове подпрограммы.Теперь каждая локальная переменная может быть доступна как относительно смещения к текущему состоянию стека.
-------------
------------- local variables for subroutine N
-------------
ret_address_N
------------- local variables for subroutine 3
ret_address_3
------------- local variables for subroutine 2
-------------
ret_address_2
-------------
------------- local variables for subroutine 1
-------------
-------------
ret_address_1
Помимо выдачи инструкций по настройке кадра стека (эффективное выделение локальных переменных в стеке и сохранение текущих значений регистров), компилятор C будет выдавать инструкции, которые будут восстанавливать состояние стека до его исходного состояния до того, каквызов функции, поэтому инструкция RET может найти в верхней части стека правильный адрес памяти, когда в ней появляется значение, к которому следует перейти.
Теперь вы можете понять, почему вы не можете не должен возвращать указатель на локальную переменную.Тем самым вы возвращаете адрес значению, которое хранилось временно в стеке.Вы можете разыменовать указатель и MIGHT посмотреть, что выглядит как действительные данные, когда вы немедленно возвращаетесь из подпрограммы, возвращающей указатель на локальную переменную, но эти данные, безусловно, будут перезаписаны, вероятно, в самом ближайшем будущем, так каквыполнение программы продолжает вызывать подпрограммы.