Я пытаюсь лучше понять, как работают стек и куча.Я столкнулся с проблемой при сравнении 32-битных и 64-битных скомпилированных версий одной и той же программы.В обоих случаях я использовал гостевую виртуальную машину Fedora 15 (32 и 64), gcc для компиляции, gdb для отладки и одно и то же оборудование хоста.Рассматриваемая программа очень проста и сразу ниже:
Программа C
void function(int a, int b, int c, int d){
int value;
char buffer[10];
value = 1234;
buffer[0] = 'A';
}
int main(){
function(1, 2, 3, 4);
}
В целях экономии места я опустил дамп сборки программы;однако, если кто-то думает, что это может помочь им ответить на мои вопросы, я был бы рад включить его.
32-битная скомпилированная программа:
Параметры 4 (0xbffff3e4), 3 (0xbffff3e0),2 (0xbffff3dc) и 1 (0xbffff3d8) сначала помещаются в стек.Затем расположение инструкции после вызова функции () - или адреса возврата - помещается в стек (0x080483d1).Затем значение базового указателя для предыдущего стека (0xbffff3e8) помещается в стек.
(gdb) x/16xw $esp
0xbffff3c0: 0x00000000 0x410759c3 0x4105d237 0x00000000
0xbffff3d0: 0xbffff3e8 0x080483d1 0x00000001 0x00000002//pointers
0xbffff3e0: 0x00000003 0x00000004 0x00000000 0x4105d413//followed by params
0xbffff3f0: 0x00000001 0xbffff484 0xbffff48c 0x41040fc4
64-разрядная скомпилированная программа:
Однако;здесь значения 4, 3, 2 и 1 нигде не видно.Все, что я вижу, независимо от того, как далеко я смотрю в стеке, это адрес возврата (0x4004ae) и базовый указатель предыдущего кадра стека (0x7fffffffe210).
(gdb) x/16xg $rsp
0x7fffffffe200: 0x00007fffffffe210 0x00000000004004ae //pointers
0x7fffffffe210: 0x0000000000000000 0x00000036d042139d
0x7fffffffe220: 0x0000000000000000 0x00007fffffffe2f8
0x7fffffffe230: 0x0000000100000000 0x0000000000400491
0x7fffffffe240: 0x0000000000000000 0x7ade47f577d82f75
0x7fffffffe250: 0x0000000000400390 0x00007fffffffe2f0
0x7fffffffe260: 0x0000000000000000 0x0000000000000000
0x7fffffffe270: 0x8521b80ab3982f75 0x7ab3e77151682f75
64-битная скомпилированная программа с оператором print:
Теперь, после добавления простого оператора печати:
printf("%d, %c\n", flag, buffer[0]);
в функции (), я могу видеть странные параметры (см. Ниже, 0x7fffffffe1e0-0x7fffffffe1ec).Я также вижу Базовый указатель из предыдущего стекового кадра, 0x7fffffffe210 (в 0x7fffffffe200) и обратный адрес 0x400520 (в 0x7fffffffe208).Я полагаю, что это изменилось из-за нового заявления печати. Почему 4, 3, 2 и 1 не видны без оператора печати в этом случае?Является ли 64-битная реализация компилятора gcc достаточно умной, чтобы не «тратить» память на параметры и локальные переменные, которые никогда не используются?
(gdb) x/16xg $rsp
0x7fffffffe1e0: 0x0000000300000004 0x0000000100000002 //parameters
0x7fffffffe1f0: 0x0000000000000000 0x00000000004003e0
0x7fffffffe200: 0x00007fffffffe210 0x0000000000400520 //pointers
0x7fffffffe210: 0x0000000000000000 0x00000036d042139d
0x7fffffffe220: 0x0000000000000000 0x00007fffffffe2f8
0x7fffffffe230: 0x0000000100000000 0x0000000000400503
0x7fffffffe240: 0x0000000000000000 0xd3c0c92559feaed9
0x7fffffffe250: 0x00000000004003e0 0x00007fffffffe2f0
Наконец, почему 32-битныйОС помещает параметры 4, 3, 2 и 1 выше в стек, чем это делало ранее упомянутые указатели.И почему 64-битная ОС вместо этого помещает параметры ниже в стек, чем указанные указатели? У меня сложилось впечатление, что переданные параметры всегда помещаются в стек первыми (и, следовательно, будут вадрес памяти большего значения, так как стек увеличивается в сторону меньших адресов).Затем следовали сохраненный базовый указатель и адрес возврата (чтобы базовый указатель мог быть сброшен до своего предыдущего значения, а вызывающая функция могла быть возвращена).Такое поведение я наблюдаю в 32-битном скомпилированном коде, но не в 64-битной версии.Что я недопонимаю?Я ценю любое понимание этого вопроса и прошу прощения, если мои вопросы неясны.Пожалуйста, дайте мне знать, как я могу быть более кратким (или если я на самом деле ошибаюсь в любой момент).
Заранее спасибо.