C语言 struct结构体超详细讲解
一、本章重点
- 创建结构体
- typedef与结构体的渊源
- 匿名结构体
- 结构体大小
- 结构体指针
- 其他
二、创建结构体
先来个简单的结构体创建
这就是一个比较标准的结构体
struct people { int age; int id; char address[10]; char sex[5]; };//不要少了分号。
需要注意的是不要少了分号。
那么这样创建结构体呢?
struct phone { char brand[10];//品牌 int price;//价格 }; struct people { int age; int id; char address[10]; char sex[5]; struct phone; };
很显然,一个结构体是能够嵌套另一个结构体的。
没有这样的设计,这样做也行
struct people { int age; int id; char address[10]; char sex[5]; char phone_brand[10]; int phone_price; };
但结构体中成员太多了是不利于我们后期的维护的,试问:假设有1000个成员,你能快速的找出你需要的成员吗?当有了分块的结构体,我们是能够迅速的定位和查看的。
💡结构体能够嵌套另一结构体,那么结构体能否嵌套自己呢?
struct phone { char brand[10]; int price; struct phone; };
这样做之后编译器会给你一个报错
原因是什么呢?
因为这个结构体的大小是未定义的,你能算出这个结构体的大小吗?这是不可能的!
既然大小不能确定,那么当你用这个结构体去创建变量,编译器该为这个变量开辟多大的空间呢?所有编译器在设计之初便杜绝了这种可能。
在提一个问题,结构体是否可以嵌套自己的结构体指针呢?
struct people { int age; int id; char address[10]; char sex[5]; struct people* son[2]; };
答案是:可以
这里并不存在空间该分配多少的问题,因为struct people*是指针类型,它的大小是确定的,在32位机器下是4字节,64为机器是8字节。
三、typedef与结构体的渊源
先上一段代码
struct people { int age; int id; }a;//a代表什么? int main() { a.age = 20; printf("%d\n", a.age); return 0; }
提问:a代表什么?
其实我们可以这样去看这个问题
struct people{int age;int id;} a; int main() { a.age = 20; printf("%d\n", a.age); return 0; }
对比int b呢?
int b; struct people{int age;int id;} a; int main() { a.age = 20; printf("%d\n", a.age); return 0; }
显然,struct people{int age;int id;}代表的是结构体类型,就像整形类型一样去创建变量。
那么这里的a就是结构体创建的变量。
这里也能明白结构体创建的最后为什么要保留分号。
那我们再看一段代码:
typedef struct people { int age; int id; }a; int main() { a.age = 20; printf("%d\n", a.age); return 0; }
此时加上typedef,a还能当结构体创建的变量吗?
显然不行,此时编译器会报错。
理解方式以上述一致。
typedef struct people{int age;int id;} a;
类似于
typedef struct people{int age;int id;} a; typedef int b;
此时的a就是struct people{int age;int id;}
typedef的作用是把struct people{int age;int id;}这一类型重命名为a。
不知道你有没有见过这样的代码
typedef struct people { int age; int id; }b,a,*c; int main() { a a1; b b1; c c1 = &a1; a1.age = 5; b1.age = 6; c1->age = 10; printf("%d %d %d\n", a1.age, b1.age, c1->age); return 0; }
你知道运行结果吗?
这里的b、a、c是什么呢?
这里我就不啰嗦了,a和b都是struct people{int age;int id;}结构体类型,c是struct people{int age;int id;}* 结构体指针类型。
运行结果:
那么再次提升一下
typedef struct people { int age; int id; }b, a, c[20]; 这里的b、a、c代表什么呢?
期待着你动手解决这一问题。
四、匿名结构体
这就是一个匿名的结构体
struct { int age; int id; }; int main() { struct p1; p1.age = 10; printf("%d\n", p1.age); return 0; }
匿名结构体能这样创建结构体变量吗?
此时编译器会报错
这样的匿名结构体只能在创建结构体的时候定义好变量。
比如这样
struct { int age; int id; }p1; int main() { p1.age = 10; printf("%d\n", p1.age); return 0; }
接下来我们看下这段代码
typedef struct { int age; int id; }people; int main() { people p1; p1.age = 10; printf("%d\n", p1.age); return 0; }
这里我们重命名这个匿名结构体,即把这个结构体类型重命名为people。
那么我们自然可以用people类型来创建p1。也可创建p2、p3等等。
运行结果:
以下代码合法吗?
//匿名结构体类型 struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20], *p; int main() { //在上面代码的基础上,下面的代码合法吗? p = &x; return 0; }
警告: 编译器会把上面的两个声明当成完全不同的两个类型。 所以是非法的。
五、结构体大小
如何求结构体类型的大小?
这需要了解结构体成员在内存是怎么存储的。
你知道下面这段代码的运行结果吗?
struct people { char a; int b; char c; }; int main() { struct people p1; printf("%d\n", sizeof(p1));//大小是6吗? return 0; }
char是一字节大小
int是四字节大小
char是一字节大小
直接相加等于6
那么这个结构体的大小是6吗?
但我们发现答案是12
这是为什么呢?
简单来说编译器为了读取内存时提升效率和避免读取出错,它做了内存对齐的操作。
什么是内存对齐?
就是让数据安排在合适的位置上所进行的对齐操作。
为什么要内存对齐?
- 一、移植原因
1.不是所有的硬件平台都能访问任意地址上的任意数据的;
2.某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
- 二、性能原因:
为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
对齐规则:
Windows中默认对齐数为8,Linux中默认对齐数为4
- 一:第一个数据成员从偏移地址为0的地方开始存放。
- 二:对齐数:等于默认对齐数与与该成员大小的较小值。
- 三:从第二成员开始,从它对齐数的整数倍的偏移地址开始存放。
- 四:最后结构体的大小需要调整为最大对齐数的整数倍。
最大对齐数:即所有成员对齐数中最大的对齐数。
struct people { char a; int b; char c; };
解析:
第一个为char,直接放在偏移地址为0的位置处。
第二个为int,它的自身大小为4,比默认对齐数小,所以它的对齐数是4。
4是4的整数倍,所以在偏移地址为4处开始放数据。
第三个为char,自身大小为1,比默认对齐数小,它的对齐数是1。
8是1的整数倍,从偏移地址为8的位置开始放。
总大小为9,不是最大对齐数的整数倍
要浪费3个空间,调整后为12。
我们再看看下面这段代码:
将char 和 int成员变量交换位置后,这结构体的大小还是12吗?
struct people { char a; char c; int b; }; int main() { struct people p1; printf("%d\n", sizeof(p1));//大小是多少呢? return 0; }
解析:
第一个:直接从0处开始放
第二个是char:对齐数是1,1是1的整数倍,从偏移地址为1的位置开始放。
第二个是int:对齐数是4,4是4的整数倍,从偏移地址为4的位置开始放。
放好各个成员变量后,总大小是8,是最大对齐数(4)的整数倍,不需要调整。
因此这个结构体的大小是8.
再来看看下面这个如何?
struct people { char a; int b; short c[2]; char d; }; int main() { struct people p1; printf("%d\n", sizeof(p1));//大小是6吗? return 0; }
解析:
第一个成员是char,直接放再0地址处。
第二个成员是int,对齐数是4,从偏移地址为4的位置处开始存放。
第三个成员是short[2],关于数组,它的对齐数是首元素的大小与默认对齐数的较小值,这里它的对齐数是2,然后从偏移地址为8处开始存放4个字节。
第四个成员是char,对齐数是1,直接放开12位置处。
总大小是13,最大对齐数是4,不是最大对齐数的整数倍,需要对齐到最大对齐数的整数倍,浪费3字节大小,对齐到16.
所以这个结构体的大小是16.
六、结构体指针
先创建一个结构体
struct people { char a; int b; };
然后用该结构体创建变量,再用结构体指针指向该变量。
int main() { struct people p1; struct people* p = &p1; return 0; }
所谓结构体指针,即指向结构体的指针。
正如整形指针,即指向整形的指针。
访问变量方式1:
int main() { struct people p1; struct people* p = &p1; p1.a = 'a'; p1.b = 10; printf("%c %d\n", p1.a, p1.b); return 0; }
访问变量方式2:
int main() { struct people p1; struct people* p = &p1; p->a = 'a'; p->b = 10; printf("%c %d\n", p->a, p->b); return 0; }
访问变量方式3:
int main() { struct people p1; struct people* p = &p1; (*p).a = 'a'; (*p).b = 10; printf("%c %d\n", (*p).a, (*p).b); return 0; }
七、其他
结构体中还有两个常见知识点:
一、位端
二、柔性数组
由于篇幅原因,下期会细细讲解这两个知识点
到此这篇关于C语言 struct关键字超详细讲解的文章就介绍到这了,更多相关C语言 struct内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
C/C++ Qt TreeWidget 单层树形组件应用小结
TreeWidget 目录树组件,该组件适用于创建和管理目录树结构,在开发中我们经常会把它当作一个升级版的ListView组件使用,本文将通过TreeWidget实现多字段显示,并增加一个自定义菜单,通过在指定记录上右键可弹出该菜单并对指定记录进行操作2021-11-11
最新评论