一、什么是结构体
C语言中存在int、char、long、double、float等内置类型,但是仅存在内置类型是不够的。比如当我们需要描述一个复杂对象的时候,就不能只用一个数据来描述。描述一个人需要身高、体重、年龄等数据;描述一本书需要书名、价格等数据。为了解决这个问题,C语言就增加了结构体这种⾃定义的数据类型,让程序员可以自己创造适合的类型。
二、结构体的声明
struct是结构体关键字;tag是标签,可以自己定义;member-list是成员列表,成员可以是各个类型的变量;variable-list是变量列表。
例如:描述一个学生
//创建一个结构体类型,类型名叫struct Stu
struct Stu
{
//成员变量
char name[20];
int age;
char id[20];
};
三、结构体变量的定义、初始化以及结构体成员的访问
(一)、结构体变量的定义
struct Stu
{
char name[20];
int age;
char id[20];
}p1; //声明类型的同时定义变量p1
struct Stu p2; //定义结构体变量p2
(二)、结构体变量的初始化
struct Stu s1 = { "张三",20,"123456" };
//按顺序初始化
struct Stu s2 = {.age = 20,.id = "123456",.name = "张三"};
//指定顺序初始化
(三)、结构体成员的访问
结构体成员的访问可以通过(.)操作符来完成,使⽤⽅式:结构体变量.成员名。
#include<stdio.h>
struct Stu
{
char name[20];
int age;
char id[20];
};
int main()
{
struct Stu s1 = { "张三",20,"123456" };
printf("%s\n", s1.name);
printf("%d\n", s1.age);
printf("%s\n", s1.id);
return 0;
}
有时候我们得到的不是⼀个结构体变量,⽽是得到了⼀个指向结构体的指针,可以通过 结构体指针->成员名 的方式来完成。
#include<stdio.h>
struct Stu
{
char name[20];
int age;
char id[20];
};
int main()
{
struct Stu s1 = { "张三",20,"123456" };
struct Stu* ps = &s1;
printf("%s\n", ps->name);
return 0;
}
四、结构体内存对齐
结构体内存对齐存在以下几个规则:
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
【注】偏移量:第一个字节相对于起始位置的偏移量是0,第二个字节相对于起始位置的偏移量是1,第三个字节相对于起始位置的偏移量是2……
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。
VS中默认的对齐数为8,Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。
3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,最大对齐数是所有对齐数中最大的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
如:求下面结构体变量的大小
输出的结果是12。
嵌套结构体的情况:
如:求变量s4的大小
#include<stdio.h>
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
struct S4 s4 = { 0 };
printf("%zd\n", sizeof(s4));
return 0;
}
结构体在对⻬⽅式不合适的时候,我们可以使用#pragma这个预处理指令来修改默认的对齐数。
如:
#pragma pack(1)//设置默认对齐数为1 这样struct S这个类型就按照默认对齐数为1来算
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对齐数,恢复为默认对齐数
int main()
{
printf("%d", sizeof(struct S));
return 0;
}
为什么会存在内存对齐?
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
总的来说,结构体的内存对⻬是拿空间来换取时间的做法。
以上是与结构体有关的一些内容,如有不对,欢迎批评指正!