✨✨欢迎大家来到Celia的博客✨✨
🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉
所属专栏:C语言
目录
前言
在C语言中,分为内置类型和自定义类型,内置类型譬如int,char等,自定义类型分为结构体,联合体和枚举类型。本篇文章将会详细介绍自定义类型中的结构体类型。
一、结构体的意义
我们想要描述一个事物单一的特点时,可以用int变量(比如价格),char类型变量(比如人名)等,但是如果我们想要详细的描述一个物品、人的特点的时候,用单一的类型变量会显得很单薄,并不能把事物全面的描述出来,这时,我们可以声明自定义类型:结构体。
二、结构体的声明
2.1 普通声明
struct str { int a; char b; short c; char d; long long e;//这里是结构体成员,可以拥有不同类型的变量 };//一定要有分号
struct等同于告诉编译器,要声明一个结构体变量,str是结构体的类型名,struct stu加在一起,就是一个新的结构体类型,与int,char是一个级别的存在。
2.2 特殊声明
我们在声明结构体类型变量时,可以省略自定义的结构体类型名,如下所示:
//匿名结构体类型 struct { int a; char b; short c; char d; long long e; }x;
匿名结构体类型变量如果没有进行重命名的话(typedef)通常情况下只能使用一次,且不可以进行取地址赋值给指针的操作。
三、结构体类型的初始化和成员访问
在介绍之前,我们需要明确如何访问结构体的成员:
- 结构体访问操作符:. (点)
- 结构体指针访问操作符:-> (箭头)
3.1 初始化
#include<stdio.h> struct str { int a; char b; short c; char d; long long e; }; int main() { struct str s = { 1,'a',12 ,'m',11223344};//这里在主函数里声明一个结构体类型变量s return 0; }
struct str { int a; char b; short c; char d; long long e; }s = { 1,'x',12,'m',11223344 };
以上是两种初始化的方式
3.2 赋值
struct str { int a; char b; short c; char d; long long e; }s;//这里直接在声明的时候,创建了一个结构体变量s int main() { s.a = 1;//使用结构体访问操作符 . 来对结构体中的变量进行赋值 s.b = 'x'; s.c = 12; s.d = 'm'; s.e = 11223344; return 0; }
struct str { int a; char b; short c; char d; long long e; }s;//这里直接在声明的时候,创建了一个结构体变量s int main() { struct str* p = &s;//创建一个结构体指针,指向结构体变量s p->a = 1; p->b = 'x'; p->c = 12; p->d = 'm'; p->e = 11223344;//使用结构体指针访问操作符 -> 对结构体成员进行赋值 return 0; }
以上是两种访问结构体成员的方式
四、结构体内存对齐
在我们对结构体有了初步的认识后,我们可能会想一个问题,结构体包含那么多不同类型的变量,它的大小是如何计算的呢,是把结构体成员的大小直接加和吗?我们来看下面的代码:
#include<stdio.h> struct str { int a;//4个字节 char b;//1个字节 short c;//2个字节 char d;//1个字节 long long e;//8个字节 }; int main() { struct str s = { 1,'a',12 ,'m',11223344 }; printf("%zd", sizeof(s));//输出结果是什么呢,是简单的4+1+2+1+8吗??? return 0; }
答案是24,这是为什么呢?
4.1 结构体的对齐规则
- 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认对齐数 和 该成员 变量大小的较小值
- VS默认对其数为8
- Linux中gcc没有默认对齐数,对齐数就是成员变量的大小- 结构体总大小为最大对齐数(结构体每个成员都有对齐数,所有对齐数中最大的)的整数倍
- 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,该结构体的大小为自身的最大对齐数,整个结构体的大小就是结构体最大对齐数(包括嵌套的结构体)的整数倍
我们来单独看一下这个结构体:
struct str
{
int a;//4个字节
char b;//1个字节
short c;//2个字节
char d;//1个字节
long long e;//8个字节
};
- a为第一个成员,对齐到偏移量为0的地址处存储
- b的类型为char,对齐数为默认对齐数(8)与变量大小(1)的较小值,为1,4是1的整数倍,所以b对齐到偏移量为4的地址处存储
- c的类型为short,对齐数为2,5不是2的倍数,向下寻找,6为2的倍数,所以c对齐到偏移量为6的地址处存储
- d的类型为char,对齐到偏移量为8的地址处存储
- e的类型为longlong,对齐数为8,向下寻找至16为8的倍数,所以e对齐到偏移量为16的地址处存储
- 这时整个结构体的大小为24字节,是最大对齐数(longlong - 8)的整数倍,整个结构体的大小就为24字节
4.2 修改默认对齐数
#pragma pack(1)
我们可以使用这个预处理指令来修改默认对齐数的值,把想要修改的值填入pack后的括号内就可以了。
五、结构体实现位段
struct str { char a : 1;//位段 char b : 2; char c : 3; char d; }s;
struct str { int a : 1;//位段 int b : 2; int c : 3; int d; }s;
- 位段的成员可以是int,unsigned int,signed int,或者是char等类型。
- 位段在空间是是按照需要以4个字节(int)或者1个字节(char)来开辟的,VS是从右向左进行空间分配的。
- 位段涉及很多不确定因素,是不跨平台的。
- 这种创建结构体成员的方法可以限制每个结构体成员的大小(bit位),比如a的大小为1个bit,b的大小为2个bit,c的大小为3个bit,d是一个普通字符变量,大小为8bit。
具体存储方式(以char类型为例):
#include<stdio.h> struct str { char a : 1; char b : 2; char c : 3; char d; }s; int main() { printf("%zd", sizeof(s)); return 0; }
大小果然为2个字节