C 的空类为什么占一个字节

情景分析

#include <stdio.h>


class A {};

int main(int argc, char *argv[])

{

    printf("%ld\n", sizeof(A));

    return 0;

}

 

Linux端

 

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 <main>:
   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 <main+0x1b>
  1b:	b8 00 00 00 00       	mov    $0x0,%eax
  20:	e8 00 00 00 00       	callq  25 <main+0x25>
  25:	b8 00 00 00 00       	mov    $0x0,%eax
  2a:	c9                   	leaveq 
  2b:	c3                   	retq   

g++ 得到可执行文件(动态链接)后,通过objdump -ds查看

000000000000078a <main>:
 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 <printf@plt>
 7af:	b8 00 00 00 00       	mov    $0x0,%eax
 7b4:	c9                   	leaveq 
 7b5:	c3                   	retq   

可知关于sizeof(A)处的代码,很早的时候就替换成常量0x1。

 

Windows

 

#include <stdio.h>

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中内容的占用内存大小。

在这个过程中,编译器有如下规则。

  1. 类型本身不占用内存,而是实例化后的对象占用内存。所以sizeof中的内容不论是什么,都要理解成是个实例
  2. 实例的内存大小至少为其非静态成员的大小总和。
  3. 编译器什么时候会额外分配内存给对象呢?
    1. 虚函数指针。
    2. 内存对齐。以CPU字长的整数倍读取数据会更快。
    3. 空的stuct、union、class的实例都至少占用1字节。C/C++标准都如此。

扩展

 

占用空间为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个字节内存,刚好也和本文探讨的内容对应,是不是很神奇呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值