Я думаю, у меня та же проблема.
Это происходит с малыми типами возвращаемых значений и . Порядок включения заголовков имеет значение. Во избежание этого не используйте прямое объявление типа возвращаемого значения или , включая заголовки в том же порядке .
Для возможного объяснения посмотрите на это:
func.h
struct Foo;
Foo func();
func.cpp
#include "func.h"
#include "foo.h"
Foo func()
{
return Foo();
}
foo.h
struct Foo
{
int a;
};
Обратите внимание, что весь Foo помещается в один регистр процессора.
func.asm (MSVS 2005)
$T2549 = -4 ; size = 4
___$ReturnUdt$ = 8 ; size = 4
?func@@YA?AUFoo@@XZ PROC ; func
; 5 : return Foo();
xor eax, eax
mov DWORD PTR $T2549[ebp], eax
mov ecx, DWORD PTR ___$ReturnUdt$[ebp]
mov edx, DWORD PTR $T2549[ebp]
mov DWORD PTR [ecx], edx
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
Когда объявляется func (), размер Foo неизвестен. Он не знает, как можно вернуть Фу. Поэтому func () ожидает, что указатель вернет значение хранилища в качестве параметра. Здесь это _ $ ReturnUdt $. Значение Foo () копируется туда.
Если мы изменим порядок заголовков в func.cpp, мы получим:
func.asm
$T2548 = -4 ; size = 4
?func@@YA?AUFoo@@XZ PROC ; func
; 5 : return Foo();
xor eax, eax
mov DWORD PTR $T2548[ebp], eax
mov eax, DWORD PTR $T2548[ebp]
Теперь компилятор знает, что Foo достаточно маленький, поэтому он возвращается через регистр и никаких дополнительных параметров не требуется.
main.cpp
#include "foo.h"
#include "func.h"
int main()
{
func();
return 0;
}
Обратите внимание, что здесь размер Foo известен при объявлении func ().
main.asm
; 5 : func();
call ?func@@YA?AUFoo@@XZ ; func
mov DWORD PTR $T2548[ebp], eax
; 6 : return 0;
Таким образом, компилятор предполагает, что func () вернет значение через регистр. Он не передает указатель на временное местоположение для хранения возвращаемого значения.
Но если func () ожидает указатель, он записывает в память, повреждая стек.
Давайте изменим порядок заголовков, чтобы func.h шел первым.
* * Main.asm тысяча сорок-девять
; 5 : func();
lea eax, DWORD PTR $T2548[ebp]
push eax
call ?func@@YA?AUFoo@@XZ ; func
add esp, 4
; 6 : return 0;
Компилятор передает указатель, ожидаемый функцией func (), поэтому не приводит к повреждению стека.
Если бы размер Foo был больше 2 целых, компилятор всегда передавал бы указатель.