结构体

1. 结构体类型

struct stu {
	char name[20];
	char tele[12];
	char sex[10];
	int age;
} s4, s5, s6;

这是声明一个结构体类型,stu是结构体标签,name,tele,sex,age;是成员变量,s4,s5,s6是成员列表,表示在声明结构体时创建出来的变量。

结构体变量初始化

//创建结构体-结构体变量初始化
struct stu s1 = { "zhangshan","111222333","nan",22 };
struct stu s2;
printf("%s,%s,%s,%d\n", s1.name, s1.tele, s1.sex, s1.age);

机器学习中的对齐是什么意思 对齐法则_嵌套


匿名结构体:

struct {
	int a;
	char c;
	float b;
}va;

匿名结构体只能通过名va来创建,声明中的va也是全局变量。

结构体的自引用

struct ListNode {
	int data;
	struct ListNode* next;
};

2. 结构体大小计算

结构体对齐原则:

  1. 第一个成员在结构体变量偏移量为0的地址处
  2. 其他成员变量对齐到某个数字(对齐数)的整数倍的地址处
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
  5. 对齐数=编译器默认的一个对齐数与该成员大小的较小值。vs中默认的值为8
struct d1 {
	char a;
	char b;
	int c;
};
struct d2 {
	char a;
	int b;
	char c;
};
int main()
{
    struct d1 s3 = { 0 };
	struct d2 s4 = { 0 };
	printf("%d\n", sizeof(s3));
    printf("%d\n", sizeof(s4));
    return 0;
}

机器学习中的对齐是什么意思 对齐法则_c语言_02

为什么两个结构体类型的大小不一样?

解析:

d1结构体类型:假设s3的地址为0,那么第一个成员变量a的地址也是0,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,首先对齐数是8和1中的较小值1,因此b的对齐数就是1,而每一个地址都是1的整数倍,因此b的地址就是1,对于c的对齐数就是8和4中较小的4,所以c的地址必须是4的倍数,因此要跳过4个:0123,再加上int型的4个字节。最后内存中存储的是:01_ _4567。结构体的大小是最大对齐数的整数倍,也就是4的整数倍,所以最后存储的是:01_ _4567。8的字节

d2结构体类型:假设s4的地址为0,那么第一个成员变量a的地址也是0,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,对于b的对齐数就是8和4中较小的4,所以b的地址必须是4的倍数,因此要跳过4个:0123,在加上int型的4个字节。在4处开始存储,因此b存储完就是0_ _ _4567。而c的对齐数就是1,地址为8。并且结构体的总大小要是4的倍数,所以最后存储的是:01_ _45678_ _ _。12的字节

结构体嵌套:

struct d3 {
	double a;
	char b;
	int c;
};
struct d4 {
	char a;
	struct d3 b;
	double c;
};
int main()
{
	struct d3 s5 = { 0 };
	struct d4 s6 = { 0 };
	printf("%d\n", sizeof(s5));
	printf("%d\n", sizeof(s6));
        return 0;
}

解析:

可以求出来s5的大小为16,最大对其数为8。所以对于s6来说:a的地址是0,b是结构体类型,他自己的最大对齐数是8(不包括默认值8),而不是他的大小16,所以要对齐到8处,所以b的地址是在8处开始,0_ _ _ _ _ _ _891011121314151617181920212223,c的对齐数是8,在24处开始,0_ _ _ _ _ _ _8910111213141516171819202122232425262728293031

最大对齐数就是8,总大小是8的整数倍,所以一共32个字节。

机器学习中的对齐是什么意思 对齐法则_成员变量_03


重点:为什么有结构体对齐原则:

  1. 不是所有的硬件平台都可以访问任意地址上的任意数据,某些平台只能在某些地址处取某些特定类型的数据,否则抛出异常
  2. 数据结构尤其是栈应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问只需要一次。
  3. 如果内存连续:char a;int b; 地址为01234—,对于32为的电脑,有32根地址线和32根数据线,每次可以访问4个字节,所以要读取b的话,需要从0开始读取123,再从4开始读取到4,才能读取完整的b,所以是两次。如果内存对齐:0—1234,只需一次就可以读取b。
  4. 结构体的内存对齐是拿空间来换取时间

结构体中类型的顺序也影响内存的大小,既要满足对齐,也要节省空间。同一类型的数据尽量挨在一起。

修改默认对齐数:

#pragma pack(4)--修改为4,中间放要修改的结构体类型声明
#pragma pack()--取消设置

查看结构体中的元素类型的偏移量:

offsetof(),是一个宏,头文件:#include <stddef.h>

size_t offsetof(structName, memberName)

printf("%d\n", offsetof(struct d1, a));
printf("%d\n", offsetof(struct d1, b));
printf("%d\n", offsetof(struct d1, c));

机器学习中的对齐是什么意思 对齐法则_c语言_04

位端

位端也是一种结构体类型,但和结构体有点区别,位端内的成员变量一般都是一个类型。并且成员变量后面都跟一个变量所占的比特位数。

struct s {
	int _a : 2;//_a占2个比特位
	int _b : 5;//_b占5个比特位
	int _c : 10;//_c占10个比特位
	int _d : 30;//_d占30个比特位
};
int main()
{
	struct s s1;
	printf("%d\n", sizeof(s1));
}

机器学习中的对齐是什么意思 对齐法则_嵌套_05


解析:

s类型实际上占47个比特位,按理来说6个字节就可以。

实际上,在电脑中整型一次开辟4个字节,放入_a,_b,_c,还剩15个字节。不够放_d的,直接舍弃掉。这时再开辟4个字节,放入_d,还剩2个字节,也舍弃掉。这样最终就是8个字节。

位端就是为了节省空间,不用位端要使用16个字节。位端不适合跨平台使用。

内存中存储原理:

struct c {
	char _a : 3;
	char _b : 4;
	char _c : 5;
	char _d : 6;
};
int main()
{
	struct c c1 = { 0 };
	c1._a = 10;
	c1._b = 20;
	c1._c = 3;
	c1._d = 4;
}

解析:

  1. 先开辟一个字节8个比特位:0000 0000;a是10,二进制是1010,但是a只有3个比特位,所以只能放进去10。0000 0010。
  2. b是20,二进制是10100,但是b有4个比特位,只能存储0100,并且在a后面。0010 0010;还剩一个字节舍弃。
  3. c是3,二进制是011,c有5个比特位,不全的补0,00011。此时再开辟8个比特位,放入后为:0000 0011
  4. d是4,二进制为100,d有6个比特位,000100。此时再开辟8个比特位,放入后为:0000 0100
  5. 所以最后存储的是:0010 0010;0000 0011;0000 0100----2 2 0 3 0 4

机器学习中的对齐是什么意思 对齐法则_枚举类型_06

枚举类型

//枚举类型
enum Sex {
	//枚举的可能取值
	MALE,
	FEMALE,
	SECRET
};
enum he {
	//枚举的可能取值
	MALE1 = 2,
	FEMALE1 = 3,
	SECRET1 = 5
};
int main()
{
	//枚举类型使用
	enum Sex s = MALE;
	printf("%d,%d,%d\n", MALE, FEMALE, SECRET);
	printf("%d,%d,%d\n", MALE1, FEMALE1, SECRET1);
	return 0;
}

机器学习中的对齐是什么意思 对齐法则_成员变量_07


解析:

定义枚举变量的值不能取其他值,已经规定了可能取得值,只能等于声明中的值。枚举类型内部已经定义了变量的值,第一个可能取值是0,以此类推。但是在类型声明的时候可以定义值。

联合体-共用体

在C语言中,变量的定义是分配存储空间的过程。一般的,每个变量都具有其独有的存储空间,那么可不可以在同一个内存空间中存储不同的数据类型呢?

答案是可以的,使用联合体就可以达到这样的目的。在C语言中定义联合体的关键字是union。

union Un {
	int i;
	char a[5];
	
};
int main()
{
	union Un u;
	printf("%d\n", sizeof(u));
	printf("%p\n", &u);
	printf("%p\n", &(u.a));
	printf("%p\n", &(u.i));
}

机器学习中的对齐是什么意思 对齐法则_成员变量_08


可以看出来u的地址和u中a的地址以及u中i的地址全是一样的。那为什么它的大小是8字节?

a和i共用一块空间,联合体中的a和i不能同时使用,一时只能使用一个,联合体的大小至少是最大成员的大小,当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。所以要大于5,并且对齐到8。

使用联合体检测内存大小端存储

什么是大小端存储?

int a = 0x11223344

低地址------------------高地址

…[][][][11][22][33][44][][][][][][]…大端字节序存储模式

…[][][][44][33][22][11][][][][][][]…小端字节序存储模式

不使用联合体判断:

int a = 1;
if (1 == *(char*)&a)
{
        printf("小端\n");
}
else
{
        printf("大端\n");
}

解析:
取a的地址变为char*类型,里面的四个字节只可以取第一个了,然后解引用,如果是1就是小端

使用联合体判断:

int system_check()
{
	union un {
		char a;
		int b;
	}u;
	u.b = 1;
	return u.a;
}
int main()
{
	int ret = system_check();
	if (1 == ret)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
}

机器学习中的对齐是什么意思 对齐法则_c语言_09

解析:

a和b共用4个字节,a占第一个字节,所以令b的值等于1,如果小端那么a占用的字节存储为1(途中绿色的情况),反之为0(图中紫色的情况)。

机器学习中的对齐是什么意思 对齐法则_嵌套_10