Причина, по которой адрес j
никогда не изменяется, заключается в том, что компилятор выделяет память для j
в стеке, когда функция вводится в противоположность тому, когда j
входит в объем.
Как всегда, просмотр кода сборки может помочь объяснить концепцию. Возьмем следующую функцию: -
int foo(void)
{
int i=3;
i++;
{
int j=2;
i=j;
}
return i;
}
НКУ преобразует это в следующий код x86 сборки: -
foo:
pushl %ebp ; save stack base pointer
movl %esp, %ebp ; set base pointer to old top of stack
subl $8, %esp ; allocate memory for local variables
movl $3, -4(%ebp) ; initialize i
leal -4(%ebp), %eax ; move address of i into eax
incl (%eax) ; increment i by 1
movl $2, -8(%ebp) ; initialize j
movl -8(%ebp), %eax ; move j into accumulator
movl %eax, -4(%ebp) ; set i to j
movl -4(%ebp), %eax ; set the value of i as the function return value
leave ; restore stack pointers
ret ; return to caller
Давайте пройдем через этот ассемблере. Первая строка сохраняет текущий указатель стека, чтобы он мог быть восстановлен, когда функция завершена, вторая строка устанавливает текущую вершину стека как новый указатель стека для этой функции.
Третья строка - это та, которая выделяет память в стеке для всех локальных переменных. Инструкция subl $8, %esp
вычитает 8 из текущей вершины указателя стека, регистр esp
. Стеки растут в памяти, поэтому эта строка кода фактически увеличивает память в стеке на 8 байтов. У нас есть две целые функции: i
и j
, каждая из которых требует 4 байта, поэтому она выделяет 8 байтов.
Строка 4 инициализирует i
по 3 путем прямой записи на адрес в стеке. Строки 5 и 6 затем загружают и увеличивают i
. Строка 7 инициализирует j
, записав значение 2 в память, выделенную для j
в стеке. Обратите внимание, что когда j
попал в область видимости в строке 7, ассемблерный код не настроил стек, чтобы выделить для него память, о которой уже позаботились раньше.
Я уверен, что это очевидно, но причина, по которой компилятор выделяет память для всех локальных переменных в начале функции, заключается в том, что это более эффективно. Настройка стека каждый раз, когда локальная переменная включалась или выходила из области видимости, приводила бы к множеству ненужных манипуляций с указателем стека без усиления.
Я уверен, что вы можете решить, что делает остальная часть кода сборки, если не оставить комментарий, и я проведу вас через него.
Я отправил неправильный вопрос по ошибке .. Мой плохой .. извините ..Я обновил вопрос –
@ Gardener: он будет компилироваться отлично. 'void *' будет автоматически изменяться. –
Теперь вы изменили вопрос. Пробовали ли вы его проверить, верно ли ваше первоначальное утверждение? Это даже не правильный код, поэтому ответ на это почти наверняка нет. – Clifford