1.结构体类型的声明
1.1.1 结构体的常见声明
struct name
{
element list
}member list;
1.1.2 结构体的特殊声明
除了这种常规的声明,还有一种匿名声明,它也叫做匿名结构体.
struct
{
element list;
}member list;
1.2 结构体的初始化和访问
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
int main()
{
//按照成员的顺序进行初始化
struct Stu s1 = { "张三",12,"男","1101" };
//按照指定的顺序初始化
struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex ="女" };
printf("学生1的姓名为:%s\n", s1.name);
printf("学生1的年龄为:%d\n", s1.age);
printf("学生1的性别为:%s\n", s1.sex);
printf("学生1的学号为:%s\n", s1.id);
printf("学生2的姓名为:%s\n", s2.name);
printf("学生2的年龄为:%d\n", s2.age);
printf("学生2的性别为:%s\n", s2.sex);
printf("学生2的学号为:%s\n", s2.id);
return 0;
}
struct
{
int a;
float b;
char c;
}* p;
struct
{
int a;
float b;
char c;
}x;
p = &x;//非法语句
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名结构体类型如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。使用的方式也是一样的。
1.3 结构体的自引用
首先来看这段代码
struct ListNode
{
int vaL;
struct ListNode next;
};
我们想自引用结构体这样做是否合理?答案是否定的。这样会导致结构体的大小会无限放大。正确的写法应该是存放结构体类型的指针,来指向该结构体。
struct ListNode
{
int vaL;
struct ListNode* next;
};
typedef关键字加入该怎么办
typedef struct ListNode
{
int vaL;
Node* next;
}Node;
首先这样的写法是错误的,在重命名ListNode为Node之前的结构体成员内部就已经使用了Node,这样会报错,正确的写法应该是,
typedef struct ListNode
{
int vaL;
struct ListNode* next;
}Node;
2 .1结构体内存的对齐
2.1.1结构体的对齐规则:
1.结构体第一个成员的起始位置和结构体的地址偏移量为0,两者的位置是一样的。
2.结构体的其他成员变量的位置是对齐数的整数倍的地址处
(对齐数 = min(编译器默认对齐数,该变量大小) )
VS里面默认的对齐数为8:Linux 系统 gcc环境下默认为结构体成员变量大小
3.结构体类型的大小为各个成员变量的对齐数中最大的那个对齐数的整数倍。
4.要是出现结构体嵌套的情况,那该结构体的大小也是根据各个变量的对齐数的最大值的整数倍,嵌套的那个结构体的对齐数也是按照第二点的方法来计算,取它各个变量的最大对齐数。
struct S1
{
char c1; //对齐数:min(8,1) = 1;
int i; //对齐数:min(8,4) = 4;
char c2; //对齐数:min(8,1) = 1;
};
//最大对齐数: 4
int main()
{
printf("%d\n", sizeof(struct S1));//答案为12
}
2.1.2如何改变编译器的默认对齐数
#pragma pack(2) //设置默认对齐数为2
#pragma pack() //还原默认对齐数
struct S1
{
char c1;
int i;
char c2;
};
int main()
{
printf("%d\n", sizeof(struct S1));
}
2.2 为什么存在内存对齐?
大部分的参考资料都是这样说的:
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在⾃然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对齐是拿空间来换取时间的做法