可以说一直被各类结构体大小问题所困扰,花了大半天时间查了一下资料,现在整理汇总如下。
sizeof:C语言中判断数据类型或者表达式长度符;不是一个函数,字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来。
基本数据类型的大小很好计算,我们主要看一下构造数据类型的大小,包括数组,结构体和共用体。
1、数组类型,计算单个元素的大小,整个数组大小就是单个元素大小乘以元素个数。
char a[] = "abc";
char b[100] = "abc";
......
sizeof(a); // =4 , 字符数组末尾自动添加 '\0'
sizeof(b); // =100,内存中预分配的大小为100 ×1
2、结构体类型,大小不是每个结构体成员的简单相加,需要考虑到系统存储时结构体变量地址对齐的一系列问题。
1)结构体的大小等于结构体内最大成员大小的整数倍
2)结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数。
3)为了满足规则1和2编译器会在结构体成员之后进行字节填充!
struct{
int a;
static int b;
}Q;
sizeof(Q); // Q=4, 静态变量是存放在全局数据区的,而sizeof计算栈中分配的大小,是不会计算在内的。
tips: 32位系统,int 大小为4,char大小为1,float大小为4,double大小为8。
3、结构体中包含结构体的情况
一个完整的测试程序如下:
struct test_st1{
char a;
long b;
};
struct test_st2{
char c;
struct test_st1 st1;
long long d;
};
int main() {
struct test_st2 t;
int x1 = (unsigned int)(void*)&t.c - (unsigned int)(void*)&t;
int x2 = (unsigned int)(void*)&t.st1.a - (unsigned int)(void*)&t;
int x3 = (unsigned int)(void*)&t.st1.b - (unsigned int)(void*)&t;
int x4 = (unsigned int)(void*)&t.d - (unsigned int)(void*)&t;
printf("a=0x%p,b=0x%p,c=0x%p,d=0x%p", x1, x2, x3, x4);
system("pause");
}
主要来看一下结构体test_st2这个结构体,c是本身是一个字节,所以对齐方式是1,而st1是一个结构,那么这个结构本身在其他结构体中,对齐的方式是什么呢,是以结构体的大小和给定的对齐方式做比较吗?不对,它的对齐方式是它成员变量中最大的那个成员变量所占的内存空间和给定的值进行比较,继而,st1的对齐方式是4,它的起始地址是可以整除4的地方开始。 对于d,因为它占用8个字节的内存,所以它的对齐方式是8,c和st1用去了12个字节,所以d从内存地址可以整除8的地方开始存放,所以这个test_st2结构体的大小是24。测试结果如图:
4、几个常见面试题
a). sizeof在计算变量所占空间大小时采取的机制;
结构体的大小等于结构体内最大成员大小的整数倍;结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数。
b).如果是自己为一个类写一个sizeof函数,应该考虑哪些问题
方式一:
#define SIZEOF(X) (int)((X)+1) - (int)(X)
char a[2]; //定义一个同类型的数组,两个元素的地址差就是该类型数据所占字节数
printf("%d", SIZEOF(a)); //这里的可以求任意数据类型的所占的字节数。
方式二:
#define my_sizeof(L_Value) (char* )(&L_Value + 1) - (char* ) (&L_Value)
int main(void)
{
char a;
printf("%d", my_sizeof(a));
system("pause");
}
c).虚函数和虚继承对于一个类求sizeof的影响有什么差别
class A {
public:
virtual void funa();
virtual void funb();
void func();
static void fund();
static int si;
private:
int i;
char c;
};
问:sizeof(A) = ?
解答:
关于类占用的内存空间,有以下几点需要注意:
(1)如果类中含有虚函数,则编译器需要为类构建虚函数表,类中需要存储一个指针指向这个虚函数表的首地址,注意不管有几个虚函数,都只建立一张表,所有的虚函数地址都存在这张表里,类中只需要一个指针指向虚函数表首地址即可。
(2)类中的静态成员是被类所有实例所共享的,它不计入sizeof计算的空间
(3)类中的普通函数或静态普通函数都存储在栈中,不计入sizeof计算的空间
(4)类成员采用字节对齐的方式分配空间
答案:12(32位系统)或16(64位系统)