An int that is 4 bytes in native, and 12 bytes in .NET? Huh?


B

BogusException

Posted this to comp.lang.c++, where I was informed this was an
implementation issue/concern/problem.

Why is it that VS 2005, VC++.NET needs 12 bytes to store an int, when
it can be done natively with 4? Mind you, even on 64-bit systems the
int is still supposed to be 4 bytes. An yes, the _pointer_ will be 8
bytes on a 64 bit system versus 4 bytes on a 32 bit one.

So what gives?

Consider:

The two listings below came from compiling the following console
application with VS6.0 and VS2005 on a/the same 32 bit system:

1. void main(){
2. int x=2;
3. int y=5;
4. int z=0;
5. z=x+y;
6. }

Looking below, the first listing shows each int being stored at 4 byte
intervals on the stack. Whereas the second listing shows x being
stored 8 bytes into the stack, y 12 bytes further in, and z another 12
bytes further.

But as you can see from the immediate data in the instructions and the
use of the 32 bit registers that receive the data when fetched, only 4
bytes is being used for the data. So it appears that the second
program is wasting 4 bytes for the storage of x and 8 bytes each for y
and z.

Source code, generated offsets, machine code, and assembly language
generated by VS6.0:

1: void main(){
00401010 55 push ebp
00401011 8B EC mov ebp,esp
00401013 83 EC 4C sub esp,4Ch
00401016 53 push ebx
00401017 56 push esi
00401018 57 push edi
00401019 8D 7D B4 lea edi,[ebp-4Ch]
0040101C B9 13 00 00 00 mov ecx,13h
00401021 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401026 F3 AB rep stos dword ptr [edi]
2: int x=2;
00401028 C7 45 FC 02 00 00 00 mov dword ptr [ebp-4],2
3: int y=5;
0040102F C7 45 F8 05 00 00 00 mov dword ptr [ebp-8],5
4: int z=0;
00401036 C7 45 F4 00 00 00 00 mov dword ptr [ebp-0Ch],0
5: z=x+y;
0040103D 8B 45 FC mov eax,dword ptr [ebp-4]
00401040 03 45 F8 add eax,dword ptr [ebp-8]
00401043 89 45 F4 mov dword ptr [ebp-0Ch],eax
6: }
00401046 5F pop edi
00401047 5E pop esi
00401048 5B pop ebx
00401049 8B E5 mov esp,ebp
0040104B 5D pop ebp
0040104C C3 ret

Source code, generated offsets, machine code, and assembly language
generated by VS2005:

1: void main(){

00411360 55 push ebp
00411361 8B EC mov ebp,esp
00411363 81 EC E4 00 00 00 sub esp,0E4h
00411369 53 push ebx
0041136A 56 push esi
0041136B 57 push edi
0041136C 8D BD 1C FF FF FF lea edi,[ebp+FFFFFF1Ch]
00411372 B9 39 00 00 00 mov ecx,39h
00411377 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041137C F3 AB rep stos dword ptr es:[edi]
2: int x=2;
0041137E C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
3: int y=5;
00411385 C7 45 EC 05 00 00 00 mov dword ptr [ebp-14h],5
4: int z=0;
0041138C C7 45 E0 00 00 00 00 mov dword ptr [ebp-20h],0
5: z=x+y;
00411393 8B 45 F8 mov eax,dword ptr [ebp-8]
00411396 03 45 EC add eax,dword ptr [ebp-14h]
00411399 89 45 E0 mov dword ptr [ebp-20h],eax
6: }
0041139C 33 C0 xor eax,eax
0041139E 5F pop edi
0041139F 5E pop esi
004113A0 5B pop ebx
004113A1 8B E5 mov esp,ebp
004113A3 5D pop ebp
004113A4 C3 ret

WTF???

BogusException
 
Ad

Advertisements

D

Doug Harrison [MVP]

Posted this to comp.lang.c++, where I was informed this was an
implementation issue/concern/problem.

Why is it that VS 2005, VC++.NET needs 12 bytes to store an int, when
it can be done natively with 4? Mind you, even on 64-bit systems the
int is still supposed to be 4 bytes. An yes, the _pointer_ will be 8
bytes on a 64 bit system versus 4 bytes on a 32 bit one.

So what gives?

Consider:

The two listings below came from compiling the following console
application with VS6.0 and VS2005 on a/the same 32 bit system:

1. void main(){
2. int x=2;
3. int y=5;
4. int z=0;
5. z=x+y;
6. }

Looking below, the first listing shows each int being stored at 4 byte
intervals on the stack. Whereas the second listing shows x being
stored 8 bytes into the stack, y 12 bytes further in, and z another 12
bytes further.

But as you can see from the immediate data in the instructions and the
use of the 32 bit registers that receive the data when fetched, only 4
bytes is being used for the data. So it appears that the second
program is wasting 4 bytes for the storage of x and 8 bytes each for y
and z.

Source code, generated offsets, machine code, and assembly language
generated by VS6.0:

1: void main(){
00401010 55 push ebp
00401011 8B EC mov ebp,esp
00401013 83 EC 4C sub esp,4Ch
00401016 53 push ebx
00401017 56 push esi
00401018 57 push edi
00401019 8D 7D B4 lea edi,[ebp-4Ch]
0040101C B9 13 00 00 00 mov ecx,13h
00401021 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401026 F3 AB rep stos dword ptr [edi]
2: int x=2;
00401028 C7 45 FC 02 00 00 00 mov dword ptr [ebp-4],2
3: int y=5;
0040102F C7 45 F8 05 00 00 00 mov dword ptr [ebp-8],5
4: int z=0;
00401036 C7 45 F4 00 00 00 00 mov dword ptr [ebp-0Ch],0
5: z=x+y;
0040103D 8B 45 FC mov eax,dword ptr [ebp-4]
00401040 03 45 F8 add eax,dword ptr [ebp-8]
00401043 89 45 F4 mov dword ptr [ebp-0Ch],eax
6: }
00401046 5F pop edi
00401047 5E pop esi
00401048 5B pop ebx
00401049 8B E5 mov esp,ebp
0040104B 5D pop ebp
0040104C C3 ret

Source code, generated offsets, machine code, and assembly language
generated by VS2005:

1: void main(){

00411360 55 push ebp
00411361 8B EC mov ebp,esp
00411363 81 EC E4 00 00 00 sub esp,0E4h
00411369 53 push ebx
0041136A 56 push esi
0041136B 57 push edi
0041136C 8D BD 1C FF FF FF lea edi,[ebp+FFFFFF1Ch]
00411372 B9 39 00 00 00 mov ecx,39h
00411377 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041137C F3 AB rep stos dword ptr es:[edi]
2: int x=2;
0041137E C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
3: int y=5;
00411385 C7 45 EC 05 00 00 00 mov dword ptr [ebp-14h],5
4: int z=0;
0041138C C7 45 E0 00 00 00 00 mov dword ptr [ebp-20h],0
5: z=x+y;
00411393 8B 45 F8 mov eax,dword ptr [ebp-8]
00411396 03 45 EC add eax,dword ptr [ebp-14h]
00411399 89 45 E0 mov dword ptr [ebp-20h],eax
6: }
0041139C 33 C0 xor eax,eax
0041139E 5F pop edi
0041139F 5E pop esi
004113A0 5B pop ebx
004113A1 8B E5 mov esp,ebp
004113A3 5D pop ebp
004113A4 C3 ret

WTF???

BogusException

What compiler options did you use?
 
B

BogusException

Doug,

Thanks for writing. Do the defaults create this output? Are there
compiler options that are more efficient in this regard?

Thanks!

BogusException
 
N

Nathan Mates

Thanks for writing. Do the defaults create this output? Are there
compiler options that are more efficient in this regard?

You really do need to provide more information. Is this a debug
or optimized build? (Debug errs on the side of safety and information,
versus size/speed. Release tends to do the opposite).

Nathan Mates
 
D

Doug Harrison [MVP]

Doug,

Thanks for writing. Do the defaults create this output? Are there
compiler options that are more efficient in this regard?

Thanks!

BogusException

Apparently, you were compiling for a debug build using the /RTC1 and /ZI
options. At least that combination produces for me the code you posted. You
should look up those options to determine what they do. Then compile for
release mode and verify the code doesn't contain all the debug mode,
runtime checking stuff.
 
B

BogusException

Nathan and Doug,

Thank you both very much for writing. Thanks to you I was able to
figure out a way to get the code trimmed down. See below...

Apparently, you were compiling for a debug build using the /RTC1 and /ZI
options. At least that combination produces for me the code you posted. You
should look up those options to determine what they do. Then compile for
release mode and verify the code doesn't contain all the debug mode,
runtime checking stuff.

It wasn't the /Zi or /RTC1 switch. It was simply the /Os optimization
switch which favors small code. Check it out:

1: void main(){
00411235 55 push ebp
00411236 8B EC mov ebp,esp
00411238 83 EC 4C sub esp,4Ch
0041123B 53 push ebx
0041123C 56 push esi
0041123D 57 push edi
2: int x=2;
0041123E C7 45 FC 02 00 00 00 mov dword ptr [ebp-4],2
3: int y=5;
00411245 C7 45 F8 05 00 00 00 mov dword ptr [ebp-8],5
4: int z=0;
0041124C 83 65 F4 00 and dword ptr [ebp-0Ch],0
5: z=x+y;
00411250 8B 45 FC mov eax,dword ptr [ebp-4]
00411253 03 45 F8 add eax,dword ptr [ebp-8]
00411256 89 45 F4 mov dword ptr [ebp-0Ch],eax
6: }
00411259 33 C0 xor eax,eax
0041125B 5F pop edi
0041125C 5E pop esi
0041125D 5B pop ebx
0041125E C9 leave
0041125F C3 ret

Thanks again or the insight!

Bogus Exception
 
Ad

Advertisements

D

Doug Harrison [MVP]

Nathan and Doug,

Thank you both very much for writing. Thanks to you I was able to
figure out a way to get the code trimmed down. See below...

Apparently, you were compiling for a debug build using the /RTC1 and /ZI
options. At least that combination produces for me the code you posted. You
should look up those options to determine what they do. Then compile for
release mode and verify the code doesn't contain all the debug mode,
runtime checking stuff.

It wasn't the /Zi or /RTC1 switch. It was simply the /Os optimization
switch which favors small code. Check it out:

1: void main(){
00411235 55 push ebp
00411236 8B EC mov ebp,esp
00411238 83 EC 4C sub esp,4Ch
0041123B 53 push ebx
0041123C 56 push esi
0041123D 57 push edi
2: int x=2;
0041123E C7 45 FC 02 00 00 00 mov dword ptr [ebp-4],2
3: int y=5;
00411245 C7 45 F8 05 00 00 00 mov dword ptr [ebp-8],5
4: int z=0;
0041124C 83 65 F4 00 and dword ptr [ebp-0Ch],0
5: z=x+y;
00411250 8B 45 FC mov eax,dword ptr [ebp-4]
00411253 03 45 F8 add eax,dword ptr [ebp-8]
00411256 89 45 F4 mov dword ptr [ebp-0Ch],eax
6: }
00411259 33 C0 xor eax,eax
0041125B 5F pop edi
0041125C 5E pop esi
0041125D 5B pop ebx
0041125E C9 leave
0041125F C3 ret

Thanks again or the insight!

Bogus Exception

You seem to have concluded that using /Os gives you the desired result.
This is not the case, because you'll get the same output WRT the stack
offsets if you use no compiler options at all. It was the use of /ZI and
/RTC1 together that caused the crazy stack offsets. HTH.
 
L

Lucvdv

You seem to have concluded that using /Os gives you the desired result.
This is not the case, because you'll get the same output WRT the stack
offsets if you use no compiler options at all. It was the use of /ZI and
/RTC1 together that caused the crazy stack offsets. HTH.

I didn't try to find out what exact switch[es] is/are responsible, but in a
default debug build they're spaced at 12 bytes, so I suppose that's what
the OP was talking about.


I used this code:

void DoIt(int a, int b)
{
int x, y, z;
x = a;
y = b;
z = a + b;
printf("%d + %d = %d\n", x, y, z);
}

int _tmain(int argc, _TCHAR* argv[])
{
DoIt(1, 2);
return 0;
}


In a debug build, the offsets are declared to be:

_TEXT SEGMENT
_z$ = -32 ; size = 4
_y$ = -20 ; size = 4
_x$ = -8 ; size = 4
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4


In a release build everything is optimized away, only the printf call
remains with the results pre-computed and pushed onto the stack as
constants.
To my surprise, it even optimizes away the call to DoIt() !

; 17 : DoIt(1, 2);

00000 6a 03 push 3
00002 6a 02 push 2
00004 6a 01 push 1
00006 68 00 00 00 00 push OFFSET
[email protected][email protected]@[email protected]
0000b ff 15 00 00 00
00 call DWORD PTR __imp__printf
00011 83 c4 10 add esp, 16 ; 00000010H

; 18 : return 0;

00014 33 c0 xor eax, eax

; 19 : }
 
Ad

Advertisements

B

BogusException

You seem to have concluded that using /Os gives you the desired result.
This is not the case, because you'll get the same output WRT the stack
offsets if you use no compiler options at all. It was the use of /ZI and
/RTC1 together that caused the crazy stack offsets. HTH.

Doug,

I stand corrected. It wasn't the /Os switch.

In playing with switch combinations, I inadvertently changed /ZI to /
Zi and ended up with /Zi, /RTC1, and /Os. I didn't even notice the
case difference. Both switches, /ZI and /Zi, give the debugger a
database to retrieve program source code info from such as variable
names but /ZI gives the debugger an edit and continue capability as
well.

It turns out that /Zi together with /RTC1 without /Os does the trick.
The run time check switch /RTC1 is equivalent to using both /RTCs and /
RTCu which is for stack frame and uninitialized variable run time
checks.

I was able to get the 4 byte storage with the follow combinations of
debugger and run time check switches: (/Zi, /RTC1), (/ZI, /RTCu), (/
Zd, /RTC1), and (/Z7, /RTC1). It looks like it's the combo of /ZI
with /RTCs that's causing the extra memory.

Thanks!

Bogus Exception
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top