C语言学习笔记(12)
1. 结构体声明
学习C#/Java的我们,结构体相信都非常熟悉了。简单先来介绍下语法,声明一个结构体:
struct { char *name; int age; }person1,person2; int main (void) { person1.name="kym"; person1.age=21; printf("%s",person1.name); return 0; }
当然,我们也可以在声明时直接初始化:
struct { char *name; int age; }person1={"kym",21}, person2={"test",30}; int main (void) { printf("%s",person2.name); return 0; }
但是我们在这里可以发现一点,声明的结构体是不能重用的,也就是说,必须要在结构体后一次性地把所有需要用到的结构体变量全部初始化,于是,我们更多的是这么做。
struct person { char *name; int age; }; int main (void) { struct person person1={"kym",21}; printf("%s",person1.name); return 0; }
如果你写习惯了Person person1这样的代码而对在结构体名字前跟着struct而感到不爽,那你也可以这样来写:
typedef struct { char *name; int age; }Person; int main (void) { Person person1={"kym",21}; printf("%s",person1.name); return 0; }
2. 结构体琐碎
首先,无需我多言,C语言的结构体复制时,两份结构体分别有着自己的地址,因此,当我对其赋值时,两个结构体没什么关系。说不明白了…….看下代码吧
typedef struct { char *name; int age; }Person; int main (void) { Person person1={"kym",21}; Person person2=person1; printf("person2:%s\n",person2.name); person1.name="huangxin"; printf("person2:%s",person2.name); return 0; }
我们可以看到,对person1的改变并没有引起person2的随之改变。
再来看一个例子:
typedef struct { char *name; int age; }Person; void ChangePerson(Person); int main (void) { Person person1={"kym",21}; printf("person1:%s\n",person1.name); ChangePerson(person1); printf("person1:%s",person1.name); return 0; } void ChangePerson(Person person1) { person1.name="huangxin"; }
我们可以看到 ,person1的值并没有发生任何改变,由此说明,在传参时是传递了一份新的拷贝,因此当结构体比较大时,生成结构所有成员的副本,这样给系统增加了一定的开销,因此我们可以利用指针来传递结构体。
typedef struct { char *name; int age; }Person; void ChangePerson(Person *); int main (void) { Person person1={"kym",21}; printf("person1:%s\n",person1.name); ChangePerson(&person1); printf("person1:%s",person1.name); return 0; } void ChangePerson(Person *person1) { (*person1).name="huangxin"; }
typedef struct { double name; int age; }Person; int main (void) { printf("int:%d\n",sizeof(int)); printf("double:%d\n",sizeof(double)); printf("Person:%d",sizeof(Person)); return 0; }
为什么会出现这样的情况呢?那是因为有些计算机要求特定数据项的地址比如是某个字节数(可能是2,4或者是8)的倍数,于是为了满足这个要求,编译器会在临近的成员,或者成员后添加一些内存空洞,用来使成员对齐。
3. 联合体
曾经学联合体时,我一直不知道他的应用场景,这次终于搞明白了。
先来看一下联合体的特征,就是编译器只为联合中最大的成员分配内存空间。看段代码吧:
typedef union { double name; int age; }Person; int main (void) { printf("int:%d\n",sizeof(int)); printf("double:%d\n",sizeof(double)); printf("Person:%d",sizeof(Person)); return 0; }
那么他应用于什么应用场景呢?我们来考虑一次考试的情况。英语不好,就汉语编程了……
typedef struct { char *name; int totalScore; ///begin Math int 选择题分数; int 填空题分数; int 解答题分数; int 证明题分数; ///end Math ///begin Chinese int 选择题分数; int 阅读题分数; int 作文分数; ///end Chinese }Exam;
这样本身没什么问题,但是如果说,每次我只想访问某一学科,这样的结构体就比较浪费空间,那么我们就可以用联合体来解决这个问题:
typedef struct { char *name; int totalScore; union { struct { int 选择题分数; int 填空题分数; int 解答题分数; int 证明题分数; }Math; struct { int 选择题分数; int 阅读题分数; int 作文分数; }Chinese; }; ///end Chinese }Exam; int main (void) { Exam exam={"kym",100}; exam.Math.解答题分数 = 30; exam.Math.填空题分数 = 20; exam.Math.选择题分数= 20; exam.Math.证明题分数 = 30; }
当然,我们在这里要注意,我们只能为Math和Chinese的一者复制,因为两者是公用一块内存空间,如果同时赋值,会出现覆盖的情况。
4. 用联合体来构造混合类型数组
我们来巧用一下联合体,我们知道,数组必须要是同一类型的元素。但是我们可以用联合体来构造混合类型数组。
typedef union { int i; double d; }Number; int main (void) { Number numberArray[2]; numberArray[0].i=10; numberArray[1].d=3.4; return 0; }
也许看到这里,有人会说,如果这样也算的话,那我用结构体也可以实现啊,可是别忘了,结构体要比联合体耗内存的多,尤其是我们想做个类似于Object类型数组的时候。
接下来我们继续想,当我们要获得这个混合类型的某个元素时怎么办,你怎么能知道这个元素的类型呢?那么这个时候,我们就需要一个标志位。
#define NUMBER_INT 0 #define NUMBER_DOUBLE 1 typedef struct { int flag; union { int i; double d; }Temp; }Number; int main (void) { int i ; Number numberArray[2]; numberArray[0].flag=NUMBER_INT; numberArray[0].Temp.i=10; numberArray[1].flag=NUMBER_DOUBLE; numberArray[1].Temp.d=3.14; for(i=0;i<sizeof(numberArray)/sizeof(Number);i++) { if(numberArray[i].flag==NUMBER_INT) { printf("%d:%d\n",i,numberArray[i].Temp.i); } else if (numberArray[i].flag==NUMBER_DOUBLE) { printf("%d:%f\n",i,numberArray[i].Temp.d); } } }
这样我们就完成了一个混合类型数组的编写。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
2009-04-15 一步一步学WF系列(三)
2009-04-15 重温设计模式(六)—— 阶段总结一