stack - Does Visual C++ Push Eight Bytes for DWORDs? (If so, why?) -


without knowing it, @ first, wrote code involved long long variable. mistakenly treating long, passed printf, printed value contained (as high bits zeroes).

eventually, found out using long long, , got me interested in why printf seems resilient against kind of error had made. wrote this:

int main() {     long = 1;     long long b = 2;     long c = 3;     long d = 4;     long long e = 5;     long f = 6;     long g = 7;     long long h = 8;     long = 9;     long long j = 10;      printf("%d %i64d %d %d %i64d %d %d %i64d %d %i64d\n", a, b, c, d, e, f, g, h, i, j);     printf("%i64d %i64d %i64d %i64d %i64d %i64d %i64d %i64d %i64d %i64d\n", a, b, c, d, e, f, g, h, i, j);     printf("%d %d %d %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g, h, i, j);      getchar();     return 0; } 

the first of printfs associates right format specifier type of each subsequent argument after format string. second applies specifier long long, , third specifies plain old long (well, int, precise, right?).

here's output:

1 2 3 4 5 6 7 8 9 10 1 2 3 -3689348818177884156 5 -3689348818177884154 -3689348818177884153 8 -3689348818177884151 10 1 2 3 4 5 6 7 8 9 10 

now kind of puzzled me, because expected compiled code call printf pushed 4 bytes onto stack arguments longs, , 8 bytes long longs. however, if case, printf code have been looking in wrong places after processing first incorrect format specifier. yet, output shows, printf never got lost, associating matching specifier argument in each case. second line fails print right values, that's reason turns out make perfect sense, leads question.

to find out going on, had compiler (vs2015's vc++, x64) produce assembly listing. here's first call printf:

; 20   :    printf("%d %i64d %d %d %i64d %d %d %i64d %d %i64d\n", a, b, c, d, e, f, g, h, i, j);      mov rax, qword ptr j$[rbp]     mov qword ptr [rsp+80], rax     mov eax, dword ptr i$[rbp]     mov dword ptr [rsp+72], eax     mov rax, qword ptr h$[rbp]     mov qword ptr [rsp+64], rax     mov eax, dword ptr g$[rbp]     mov dword ptr [rsp+56], eax     mov eax, dword ptr f$[rbp]     mov dword ptr [rsp+48], eax     mov rax, qword ptr e$[rbp]     mov qword ptr [rsp+40], rax     mov eax, dword ptr d$[rbp]     mov dword ptr [rsp+32], eax     mov r9d, dword ptr c$[rbp]     mov r8, qword ptr b$[rbp]     mov edx, dword ptr a$[rbp]     lea rcx, offset flat:??_c@_0cl@ghcekcad@?$cfd?5?$cfi64d?5?$cfd?5?$cfd?5?$cfi64d?5?$cfd?5?$cfd?5?$cfi64d@     call    printf 

now, has been long time since did assembly programming, but, if read correctly, appears each argument right left being put memory locations relative rsp register are, every 1 of them, 8 bytes apart each other, regardless of whether or not value requires 8 bytes representation. (i note statement not apply values a, b, , c, kept in registers, not memory; optimization short argument lists, perhaps?)

thus, makes sense: printf never gets lost, because knows (or coder wrote knew) each argument found 8 bytes away neighbor, regardless of argument's size. (the few values printed incorrectly can, think, explained fact compiled code stored four-byte dwords values, , there may have been non-zero bytes in high 4 bytes of qword printf expected find in locations.)

so, seems me compiler maintains stack (is "stack" right word here?) eight-byte entries in it, regardless of whether or not 8 needed.

why?

update:

the question i've asked (and hans passant has answered) doesn't rely on printf or variadic argument lists. it's inherent in how fifth , higher arguments functions handled x64 architecture.

for example, calling function:

void sub(long a, long b, long c, long d, long e, long f) {  } 

gets assembler code:

; 28   :    sub(a, c, d, f, g, i);      mov eax, dword ptr i$[rbp]     mov dword ptr [rsp+40], eax     mov eax, dword ptr g$[rbp]     mov dword ptr [rsp+32], eax     mov r9d, dword ptr f$[rbp]     mov r8d, dword ptr d$[rbp]     mov edx, dword ptr c$[rbp]     mov ecx, dword ptr a$[rbp]     call    ?sub@@yaxjjjjjj@z           ; sub 

where, again, see arguments longs passed function in memory locations 8 bytes apart.


Comments