一,结构体
概念:是一种新的自定义类型,由一组成员的不同数据组成的,每个成员都可以具有不同的数据类型,通常来表示类型不同但是又有 相关的若干数据。
例如:一个表示学生的结构体中有:char类型的学生名字,int类型的年龄等等。
struct Student
{
char name[20];
char gender[3];
int age;
double hegiht;
};
printf("%d\n", sizeof(struct Student));
//在使用是必须加上struct
struct Student//表示结构体类型名
{
char name[20];
char gender[3];
int age;
double hegiht;
}S1;
printf("%d\n", sizeof(S1));
//S1表示结构体变量名
typedef struct Student
{
char name[20];
char gender[3];
int age;
double hegiht;
}Student,*PStudent;
//Student是struct Student的别名
//PStudent是struct Student*的别名
结构体对齐:
对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
(对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的值为8)
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
struct S1
{
double d;//8
char c;//1+3,需要对齐所以是4
int i;//4
};
printf("%d\n", sizeof(struct S1));//8+4+4=16
struct S2
{
char c1;//1+7,
struct S1 s1;//16
double d;//8
};
printf("%d\n", sizeof(struct S2));8+16+8=32
内存对齐的原因:
1,平台原因:因为不是所有平台都可以访问任意地址上的数据。
2,性能:如果内存对齐,在访问时效率会更高。如果没有对齐,会出现为了访问未对齐的内存,处理器再一次访问内存。
二,联合体
定义:联合体也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用一块空间
union Un
{
int i;
char c;
};
union Un un;
联合体大小的计算:
1,联合的大小至少是最大成员的大小
2,当最大成员的大小不是最大对齐树的整倍时,就要对齐到最大对齐树的整数倍。
union Un1
{
char c[5];//5
int i;
};
union Un2
{
short c[7];//14
int i;
};
printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16,
联合体与结构体区别:
1,结构和联合都是由多个不同的数据类型成员组成,但在任一时刻,联合中只存放一个成员,而结构的所有成员都存在。
2,对于联合的不同成员赋值,将会对其成员重写,原来成员的值不存在了,而对于结构的不同成员赋值是互不影响的。
三,位段:
与结构体的对齐类似,但是成员必须是整形,而且段位成员后面必须要有一个冒号和数字
struct A
{
int _a:2;//_a是2个比特位
int _b:5;//_b占5个比特位
int _c:10;//_c占10个比特位
int _d:30;//_d占30个比特位
};
//_a+_b+_c对齐共用4个字节
//_d用4个字节,struct A的大小是8个字节
注:1,如果位段中的类型是有符号的,位段的高位表示的就是符号位
2,前后类型相同,比特位能共用则共用,否则重新开辟空间。
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
struct S s = { 0 };
s.a = 10;// 0000 1010 -> 010 ->2
s.b = 12;// 0000 1100 -> 1100 ->-4 (因为只取了4个比特位,首位是符号位所以s.b是-4)
s.c = 3;// 0000 0011 -> 0 0011 ->3
s.d = 4;// 0000 0100 -> 0100 ->4
printf("s=%d\n", sizeof(struct S));//3
printf("%d %d %d %d \n", s.a, s.b, s.c, s.d);//2 -4 3 4
大小端问题:
小端:是指数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中
大端:是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
如何判断大小端:
1,利用union来检测:
因为联合体union的存放顺序是所有成员都从低地址开始存放这一特性进行检测
2,通过将多字节数据强制类型转换成单字节数据,再通过判断起始存储位置是数据高字节还是低字节进行检测
#include <stdio.h>
int IsBigEndian_1()
{
union Un
{
char c;
int i;
}un;
un.i = 1;
return un.c;
}
int IsBigEndian_2()
{
int a = 1;
return *(char*)&a;
//一个整形的指针每次偏移的是4个字节,而一个字符类型的指针每次偏移的是一个字节
}
int main()
{
int ret=IsBigEndian_1();
if (1 == ret1)
{
printf("当前模式为小端存储\n");
}
else
{
printf("当前模式为大端存储\n");
}
return 0;
}
为什么区分大小端;大小端是根据cpu架构而决定的,跟操作系统无关。在网络编程是需要考虑双方机器是否大小端一致,在代码移植时需要考虑到硬件的情况,否则无法得到正确的数据。而且一部分PC机是小端,网络序是大端,在写代码的时候必须考虑到大小端转换的问题,否则网卡或者网络设备会解析错误。