情景分析
#include
class A {};
int main(int argc, char *argv[])
{
printf("%ld\n", sizeof(A));
return 0;
}
g++ -S下得到的汇编(部分代码)
.file "emptyclass.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.LC0:
.string "%ld\n"
.text
.globl main
.type main, @function
main:
.LFB1493:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $1, %esi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
g++ -c 得到的目标文件,通过objdump -ds查看
0000000000000000 :
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 48 89 75 f0 mov %rsi,-0x10(%rbp)
f: be 01 00 00 00 mov $0x1,%esi
14: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 1b
1b: b8 00 00 00 00 mov $0x0,%eax
20: e8 00 00 00 00 callq 25
25: b8 00 00 00 00 mov $0x0,%eax
2a: c9 leaveq
2b: c3 retq
g++ 得到可执行文件(动态链接)后,通过objdump -ds查看
000000000000078a :
78a: 55 push %rbp
78b: 48 89 e5 mov %rsp,%rbp
78e: 48 83 ec 10 sub $0x10,%rsp
792: 89 7d fc mov %edi,-0x4(%rbp)
795: 48 89 75 f0 mov %rsi,-0x10(%rbp)
799: be 01 00 00 00 mov $0x1,%esi
79e: 48 8d 3d 00 01 00 00 lea 0x100(%rip),%rdi # 8a5 <_ZStL19piecewise_construct+0x1>
7a5: b8 00 00 00 00 mov $0x0,%eax
7aa: e8 91 fe ff ff callq 640
7af: b8 00 00 00 00 mov $0x0,%eax
7b4: c9 leaveq
7b5: c3 retq
可知关于sizeof(A)处的代码,很早的时候就替换成常量0x1。
#include
class A {};
int main(int argc, char *argv[])
{
00AD13C0 push ebp
00AD13C1 mov ebp,esp
00AD13C3 sub esp,0C0h
00AD13C9 push ebx
00AD13CA push esi
00AD13CB push edi
00AD13CC lea edi,[ebp-0C0h]
00AD13D2 mov ecx,30h
00AD13D7 mov eax,0CCCCCCCCh
00AD13DC rep stos dword ptr es:[edi]
printf("%ld\n", sizeof(A));
00AD13DE mov esi,esp
00AD13E0 push 1
00AD13E2 push 0AD5858h
00AD13E7 call dword ptr ds:[0AD9114h]
00AD13ED add esp,8
00AD13F0 cmp esi,esp
00AD13F2 call __RTC_CheckEsp (0AD1136h)
return 0;
00AD13F7 xor eax,eax
}
00AD13F9 pop edi
00AD13FA pop esi
00AD13FB pop ebx
00AD13FC add esp,0C0h
00AD1402 cmp ebp,esp
00AD1404 call __RTC_CheckEsp (0AD1136h)
00AD1409 mov esp,ebp
00AD140B pop ebp
00AD140C ret
sizeof只是个运算符,它在汇编时就会被编译器替换成值。
也就是说,编译器决定sizeof中内容的占用内存大小。
在这个过程中,编译器有如下规则。
占用空间为0的对象,其意义和malloc(0)相对应。
能不能支持大小为0的对象,就看操作系统对malloc(0)的反应:有的会返回NULL,表示申请失败;有的返回一个貌似正常的指针,但是这个指针所致内存并不有效。和编译器一样,大多数运行库为了保证广泛的可移植性,会对malloc做一层包装,以Python为例
#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
: malloc(((n) != 0) ? (n) : 1))
#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
: realloc((p), ((n) != 0) ? (n) : 1))
#define PyMem_FREE free
它保证了对malloc调用至少会申请1个字节内存,刚好也和本文探讨的内容对应,是不是很神奇呢?