结构体
一 . 结构体类型简述。
当我们深度学习C语言后发现,储存整型可以用 “ int ” ,浮点型可以用 “ float ” “ double ”,字符型可以用 “ char ”,可每一次只能存放一个信息在变量里,且不能存放类型不同的数据。
那如果我们要存放不同种类的信息到一个内容里,且我们可以随时得到所储存的信息,又该怎么办呢?
这就不得不提到我们的一个老朋友兼新朋友——结构体类型。
我们设想一个场景,对于一个学生的信息有多种,例如姓名,年龄,身高,学号。
这时我们就可以使用结构体变量来一次性存储。(这里声明结构体类型的关键字是 “ struct ”)
因结构体类型的初始化有多种方式,所以尽可能的展现给大家。
方式1:先定义结构体,再创建结构体变量。
struct 类型名
{
成员;
};
struct 类型名 变量名;
方式2:定义结构体类型时同时创建结构体变量。
struct 类型名
{
成员;
}变量名;
方式3:通过定义匿名结构体类型再创建结构体变量。
struct
{
成员;
}变量名;
这里需注意,如果在定义结构体类型时未输入结构体类型名,而直接创建结构体变量,那么这一个结构体类型就称为匿名结构体,匿名结构体类型在使用时只能使用一次,如果再次创建,则会发生错误。
二 . 结构体类型的初始化。
在我们了解了结构体类型如何定义后,就到了初始化的环节。
例如我们创建了一个结构体变量 Stu (代表一个学生的信息),对其中的名字,年龄,身高(cm)进行信息的初始化。
struct Stu
{
char name[20];
int age;
int high;
};
当我们想初始化时,可以有多种方式。
方式1:按结构体成员顺序进行初始化。
struct Stu a = {"Frank" , 50 , 175 };
方式2:按指定成员的方式进行初始化。
struct Stu b = {.age=28 , .high = 175 , .name="Lip"};
方式3:创建一个结构体指针和一个结构体变量,通过指针对结构体成员进行初始化。
struct Stu c = {0};
struct Stu * p = &c;
p->high = 170;
p->age = 32;
三 . 结构体成员的访问。
对于结构体成员,我们在C语言中有两种访问方式,第一种是通过 " . "来进行访问,第二种是通过 “ -> ” 进行访问,这也是 “ -> ” 在C语言中的唯一作用了。
而他们两个有什么区别呢?
“ . ” 是直接对结构体变量成员进行访问,它的操作对象是结构体变量。
“ -> ” 是对结构体指针进行操作,通过结构体指针来访问结构体的成员。
struct Stu
{
char name[20];
int age;
int high;
}a = {"Frank" , 50 , 175 },*p;
printf("%s %d %d\n",a.name,a.age,a.high);//通过点来访问
p = &a;
printf("%s %d %d\n",p->name,p->age,p->high);//通过指针访问
四 . 结构体的内存对齐。
在了解了结构体如何定义,如何创建和初始化后,我们来思考一个问题,那就是一个结构体的大小到底是多少,每个结构体的成员都不尽相同,那它的大小又是如何计算的呢?
让我们先看一个例子。
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
我们在不了解结构体的大小分配方式的时候,可能就直接莽着去计算了。
哦~,char 类型一个字节,int 类型四个字节,一看大小就是6!
让我们真实打印一下看看。
怎么会是12?
这就不得不了解一下结构体内存的对齐方式了,也就是如何存放的。
4.1. 结构体的对齐规则
结构体的存放规则有四个,如下:
1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。(VS 中默认的值为 8 ,Linux 中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩)
3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
4.2. 嵌套结构体的大小计算。
在上述规则的第四条,如果嵌套了结构体的情况,那又该如何计算结构体的大小?
struct S1
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S1));
struct S2
{
char c1;
struct S2 s;
double d;
};
printf("%d\n", sizeof(struct S2));
这里我们创建了一个结构体S3和一个结构体S4,通过应用上面提到的规则,我们可以轻松的计算出结构体类型S1的大小为16个字节,而对于结构体类型S2里嵌套了一个结构体S1,而对齐规则4中提到嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍,通过计算可以得到,S2的大小为32。
而事实也正是如此。
五. 结构体实现位段。
5.1. 什么是位段。
1.位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
例如:
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
5.2. 位段的功能。
位段被发明出来必然有它的作用,主要应用于根据需求来对结构体成员存放的内存进行指定大小(单位为二进制位,故称位段),主要目的是节省空间,提高效率。
5.3. 位段的内存分配。
1. 位段的成员可以是 int , unsigned int , signed int 或者是 char 等类型。
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
4. 在VS环境下,默认存放为从右到左存放,且如果剩余位置不够存放下一个变量,则开辟另一个需求量大小的空间继续存放。
例如:
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
当具体存放的时候,a的值为10,二进制表示为1010,因只给a了三个比特位空间,所以会发生截断,只能存放进去010。
b的值为12,二进制表示为1100,且存放空间足够,故储存1100,又因对结构体s最初赋值为0,故未存放位置数值就为0。
c的值为3,二进制表示为0000 0011,且存放空间足够,故存进去00011。
d的值为4,二进制表示为0100,且存放空间足够,故存放0100。
故存放后内存中的数据应该为上图所示。
当我们访问内存查看真实数据的时候,也会看到上图的内容(VS小端存储)。
这样的存储方式真是行云流水 令人拍案!
六 . 结语 。
十分感谢您观看我的原创文章。
本文主要用于个人学习和知识分享,学习路漫漫,如有错误,感谢指正。
如需引用,注明地址。