Почему так много места в стеке используется для каждой рекурсии? - PullRequest
5 голосов
/ 22 марта 2010

У меня есть простая рекурсивная функция RCompare (), которая вызывает более сложную функцию Compare (), которая возвращается перед рекурсивным вызовом. Каждый уровень рекурсии использует 248 байтов стекового пространства, что кажется намного больше, чем следовало бы. Вот рекурсивная функция:

void CMList::RCompare(MP n1) // RECURSIVE and Looping compare function
{
  auto MP ne=n1->mf;
  while(StkAvl() && Compare(n1=ne->mb))
    RCompare(n1); // Recursive call !
}

StkAvl () - это простая функция проверки пространства стека, которая сравнивает адрес автоматической переменной со значением адреса в конце стека, хранящегося в статической переменной.

Мне кажется, что единственное, что добавляется в стек в каждой рекурсии, это две переменные-указатели (MP - указатель на структуру) и вещи, которые хранит один вызов функции, несколько сохраненных регистров, базовый указатель, адрес возврата и т. д., все 32-битные (4 байта) значения. Неужели нет 248 байтов?

Я не знаю, как реально осмыслить стек в Visual Studio 2008.

Спасибо


Добавлена ​​разборка:

CMList::RCompare:
0043E000  push        ebp  
0043E001  mov         ebp,esp 
0043E003  sub         esp,0E4h 
0043E009  push        ebx  
0043E00A  push        esi  
0043E00B  push        edi  
0043E00C  push        ecx  
0043E00D  lea         edi,[ebp-0E4h] 
0043E013  mov         ecx,39h 
0043E018  mov         eax,0CCCCCCCCh 
0043E01D  rep stos    dword ptr es:[edi] 
0043E01F  pop         ecx  
0043E020  mov         dword ptr [ebp-8],edx 
0043E023  mov         dword ptr [ebp-14h],ecx 
0043E026  mov         eax,dword ptr [n1] 
0043E029  mov         ecx,dword ptr [eax+20h] 
0043E02C  mov         dword ptr [ne],ecx 
0043E02F  mov         ecx,dword ptr [this] 
0043E032  call        CMList::StkAvl (41D46Fh) 
0043E037  test        eax,eax 
0043E039  je          CMList::RCompare+63h (43E063h) 
0043E03B  mov         eax,dword ptr [ne] 
0043E03E  mov         ecx,dword ptr [eax+1Ch] 
0043E041  mov         dword ptr [n1],ecx 
0043E044  mov         edx,dword ptr [n1] 
0043E047  mov         ecx,dword ptr [this] 
0043E04A  call        CMList::Compare (41DA05h) 
0043E04F  movzx       edx,al 
0043E052  test        edx,edx 
0043E054  je          CMList::RCompare+63h (43E063h) 
0043E056  mov         edx,dword ptr [n1] 
0043E059  mov         ecx,dword ptr [this] 
0043E05C  call        CMList::RCompare (41EC9Dh) 
0043E061  jmp         CMList::RCompare+2Fh (43E02Fh) 
0043E063  pop         edi  
0043E064  pop         esi  
0043E065  pop         ebx  
0043E066  add         esp,0E4h 
0043E06C  cmp         ebp,esp 
0043E06E  call        @ILT+5295(__RTC_CheckEsp) (41E4B4h) 
0043E073  mov         esp,ebp 
0043E075  pop         ebp  
0043E076  ret              

Почему 0E4h?


Подробнее:

class mch // match node structure
{
public:
    T_FSZ c1,c2;   // file indexes
    T_MSZ sz;      // match size
    enum ntyp typ; // type of node
    mch *mb,*mf;   // pointers to next and previous match nodes
};

typedef mch * MP; // for use in casting (MP) x

Должен ли быть простой старый указатель, верно? Те же самые указатели находятся в самой структуре, и они являются обычными 4-байтовыми указателями.


Редактировать: Добавлено:

#pragma check_stack(off)
void CMList::RCompare(MP n1) // RECURSIVE and Looping compare function
{
  auto MP ne=n1->mf;
  while(StkAvl() && Compare(n1=ne->mb))
    RCompare(n1); // Recursive call !
} // end RCompare()
#pragma check_stack()

Но это ничего не изменило. (

Что теперь?

Ответы [ 4 ]

4 голосов
/ 22 марта 2010

Обратите внимание, что в режиме отладки компилятор связывает много байтов из стека, для каждой функции
для обнаружения ошибок переполнения буфера.

0043E003  sub         esp, 0E4h ; < -- bound 228 bytes
...
0043E00D  lea         edi,[ebp-0E4h] 
0043E013  mov         ecx, 39h 
0043E018  mov         eax, 0CCCCCCCCh ; <-- sentinel
0043E01D  rep stos    dword ptr es:[edi] ; <-- write sentinels

Редактировать: OP Harveyнашел прагму, которая включает / выключает стеки зондов.

check_stack

Указывает компилятору отключать датчики стека, если задано значение (или -),
или toвключите стековые пробники, если указано (или +).

#pragma check_stack([ {on | off}] )
#pragma check_stack{+ | –}

Обновление : ну, пробники - это другая история, как кажется.
Попробуйте это: / GZ (включить проверку ошибок времени выполнения стека)

0 голосов
/ 22 марта 2010

Я предполагаю, что для обработки исключений должно быть выделено некоторое пространство. Вы смотрели на разборку?

0 голосов
/ 22 марта 2010

В Visual Studio вы можете посмотреть регистр «esp», указатель стека, в окнах наблюдения (или регистрации). Установите точку останова в вашей функции между одним вызовом и следующим, чтобы увидеть, сколько стека вы потребляете.

Для функции боли в режиме отладки в Visual Studio 2008 это 16 байтов на вызов функции.

0 голосов
/ 22 марта 2010

Это также зависит от компилятора и архитектуры, которую вы используете - например, это может быть выравнивание до 256 байтов для более быстрого выполнения, так что каждый уровень использует 8 байтов переменной + заполнение 248.

...