дизассемблерный фрагмент
Выделение памяти на стеке осуществляется путем "приподнимания" регистра-указателя стека на некоторую величину, что можно сделать командой "SUB ESP, n", где n – количество выделяемых байт. Поскольку, компилятор адресует локальные переменные через регистр EBP, то изменение ESP не нарушит работы функции и все будет ОК, но… так будет продолжаться недолго. При выходе из функции она попытается восстановить регистры, сохраненные на входе (в данном случае — это регистр ESI), но на вершине перемещенного стека их не окажется! В регистр ESI попадет мусор и материнская функция рухнет.
Существует по меньшей мере два решения проблемы: либо вручную опускаем регистр ESP при выходе из функции (если, конечно, не забудем это сделать), либо копируем на вершину выделенного блока порядка 20h байт памяти с макушки старого стека (обычного этого более чем достаточно, даже если функция сохраняет все регистры общего назначения ей требуется всего лишь 1Сh байт). В этом случае о ручном освобождении выделенной памяти можно не заботиться. Это сделает машинная команда MOV ESP, EBP, помещенная компилятором в эпилог функции.
Ниже приведена пара макросов для динамического выделения стековой памяти по первому сценарию:
#define stack_alloc(p,n,total) { __asm{sub esp,n};\
__asm{mov dword ptr ds:[p],esp};\
total += n;}
#define stack_free(total) {__asm{add esp,total};}