结构体对齐及联合体(大小端)--C语言


在掌握了结构体的基本使用后。
现在我们深入讨论一个问题: 计算结构体的大小

计算结构体大小

如何计算结构体的大小:
首先得掌握结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处对齐数 = 编译器默认的一个对齐数 与 该成员大小两者的较小值。
    VS中默认的值为8 ,Linux中的默认值为4
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是 所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

计算结构体大小示例:

//练习1 
struct S1
 {    
 	char c1;    
 	int i;    
 	char c2; 
 }; 
printf("%d\n", sizeof(struct S1));
// 12
//练习2 
struct S2 
{
    char c1;    
    char c2;    
    int i; 
};
printf("%d\n", sizeof(struct S2));
// 8
//练习3 
struct S3 
{    
	double d;    
	char c;    
	int i; 
}; 
printf("%d\n", sizeof(struct S3));
// 16
//练习4-结构体嵌套问题 
struct S4 
{    
	char c1;    
	struct S3 s3;    
	double d; 
}; 
printf("%d\n", sizeof(struct S4))
// 8, 16, 8 === 32    

为什么存在内存对齐?

大部分的参考资料都是如是说的:

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址 处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理 器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:
结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起

//例如: 
struct S1 
{    
	char c1;
    int i;    
    char c2; 
}; 
struct S2 
{    
	char c1;    
	char c2;    
	int i; 
};

S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别

修改默认对齐数

#include <stdio.h> 
#pragma pack(8)
//设置默认对齐数为8 
struct S1 
{    
	char c1;    
	int i;   
	char c2; 
};
#pragma pack()
//取消设置的默认对齐数,还原为默认
 
#pragma pack(1)
//设置默认对齐数为8 
struct S2 
{    
	char c1;    
	int i;    
	char c2; 
}; 
#pragma pack()
//取消设置的默认对齐数,还原为默认 
int main() 
{
    //输出的结果是什么?    
    printf("%d\n", sizeof(struct S1));   // 12 
    printf("%d\n", sizeof(struct S2));   // 6
    return 0; 
}

如何知道结构体中某个成员相对于结构体起始位置的偏移量
offsetof(StructName, MunberName);// offsetof是个宏

联合体

“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构体变量的总长度大于等于各成员长度之和。而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内,而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。如下面介绍的“单位”变量,如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符型(教研室)。要么赋予整型值,要么赋予字符型,不能把两者同时赋予它。<百度百科>

这段文字基本能解释什么是联合体

//联合类型的声明 
union Un 
{    
	char c;    
	int i; 
};
 
//联合变量的定义 
union Un un; 
//计算连个变量的大小 
printf("%d\n", sizeof(un)); // 4

联合体的特点: 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得 有能力保存最大的那个成员)。

什么是大端和小端

  1. Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
  2. Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    举一个例子,比如数字0x12 34 56 78在内存中的表示形式为:
  1. 大端模式:

低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78

  1. 小端模式:

低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
可见,大端模式和字符串的存储模式类似。
例如:

union Un 
{    
	int i;    
	char c; 
}; 
union Un un;
 
// 下面输出的结果是一样的吗? 
printf("%d\n", &(un.i)); 
printf("%d\n", &(un.c));
// 相同

//下面输出的结果是什么? 
un.i = 0x11223344; 
un.c = 0x55;
printf("%x\n", un.i); 
// 11223355

大端小段的应用场景

主要运用在网络协议中

联合体大小计算

  1. 联合的大小至少是最大成员的大小。
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
union Un1 
	{ 
		char c[5];    
		int i; 
	}; 
	union Un2 
	{ 
		short c[7];    
		int i; 
	}; //下面输出的结果是什么? 
	short c;
	printf("%d\n", sizeof(union Un1)); // 8
	printf("%d\n", sizeof(union Un2)); // 16

完.

发布了53 篇原创文章 · 获赞 46 · 访问量 7227

猜你喜欢

转载自blog.csdn.net/new_bee_01/article/details/102781659