前言
本章内容总结了结构体、联合、枚举,对于结构体总结了结构体指针用法,参数传递,内存对齐等较为重要的知识点。
C语言 | 快速了解C的发展史🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 指针、数组 一文透彻~~~🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 结构体、联合、枚举🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 声明🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 作用域 在也不用担心分不清变量的作用域拉!!!🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 编译步骤 会用C还不知道C如何编译???🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 数据类型总结🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 位及进制的用法🧡💛💚💙
文章目录
一、认识结构化数据形式
在日常的开发过程中,数据总是以组的形式存在。例如:一个学生,他具有相应的成绩、学号、班级等属性。很显然,这种数据存储在数组上是极其不方便的,而且数组存储的仅为同一类型的数据。所以就有了结构化的类型,能够将不同数据类型进行存储,满足了日常开发的需求。
1.1 基础结构
在C种提供了两种
聚合数据类型
供开发者使用。
- 数组;
- 结构;
聚合数据类型:
该类型能够同时存储超过一个的单独数据。
数组:
数组是存储相同数据类型的元素的集合。
结构:
结构中能够容纳不同的数据类型,甚至结构中能够包含结构。其中的值称之为成员。
- 数组中数组名为数组的首地址,而结构名并不是结构的地址;
二、进一步了解结构
2.1 结构体
2.1.1 结构体声明
// 声明结构体布局
struct 结构标签(可选){
int i,
成员....
}变量定义;
// 创建结构体变量
struct 变量定义/结构标签 变量名;
- 该声明并未创建实际数据对象,只是事先描述了该结构提由哪些成员组成;
- 结构标签是可选的,那什么是需要的呢?声明结构体时由定义结构体变量则不用,若需要多次使用则需要。
注意
在日常使用中,我们普遍喜欢将
typedef
运用于struct,但这样似乎不大利于后期源码的解读。如果可以,则还原使用struct来声明,更加直观,一眼即可知道是结构体。
2.1.2 结构体4种初始化
struct stu{
int sno,
char sname[20];
};
方式一:
struct stu stu1 = {21, "Jxiepc"};
需注意对应的类型,顺序。
方式二:
struct stu stu2;
stu2.sno = 21;
stu2.sname = "Jxiepc";
若该变量为指针类型,则使用
->
方式三:
struct stu stu3 = {
.sno = 21;
.sname = "Jxiepc";
}
方式四:
struct stu stu3 = {
sno : 21;
sname : "Jxiepc";
}
2.1.3 结构体指针
指向结构体的指针比数组更易操控;
- 常应用于
函数中传递
;- 结构体的
自引用
;
定义结构体指针:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stuInfo2
{
char *name;
float *scores;
};
int main()
{
struct stuInfo2 *s = (struct stuInfo2 *)malloc(sizeof(struct stuInfo2)*3);
for (int i=0; i<3; i++)
{
s[i].name = (char *)malloc(sizeof(char)*15);
s[i].scores = (float *)malloc(sizeof(float)*2);
strcpy(s[i].name, "小米");
s[i].scores[0] = 20.2;
s[i].scores[1] = 22.2;
}
for(int i=0; i<3; i++)
{
printf("%s, %.2f, %.2f\n", s[i].name, s[i].scores[0], s[i].scores[1]);
free(s[i].name);
free(s[i].scores);
}
//注意:先先释放内部的,在释放结构体
free(s);
return 0;
}
结构体的自引用
struct stu{
int num;
struct * stu;
}
常在
链表
中出现;
结构体成员为char *
类型
- 需要使用
strcpy
赋值。
- 如果使用
直接赋值
,是浅拷贝
修改数组会对结构体产生影响
使用
strcmp
是深拷贝
,把内容搬动进去。
// 结构体
struct stuInfo
{
char *name;
int Num;
}stu_;
int main()
{
// 创建堆空间结构体
// 创建结构体指针
struct stuInfo *s = &stu_;
// 申请空间
s->name = (char *)malloc(sizeof(char)*15);
strcpy(s->name, "虾米");
s->Num = 001;
printf("%s, %d", s->name, s->Num);
// 释放空间
free(s->name);
return 0;
}
2.1.4 结构体在函数中的特性
结构体传参
结构内的数据较多,则占用存储空间相对于数组较大,使用
传值
方式比较浪费空间
,则效率低
;一般是传地址
。
- 结构体不像数组,他的结构名不是数组名,若要传递其地址需要加上
&
;
结构体指针的双向特性
在函数中可作为
参数传入
,且又可以作为返回值传出
。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stuInfo3
{
char name[15];
int Num;
};
传参方式一:传入结构体,值传递不会改变实参的值
// *传入结构体:值传递不会改变实参的值。
void funCom(struct stuInfo3 s)
{
strcpy(s.name, "小米");
s.Num = 20;
printf("%s, %d\n", s.name, s.Num);
}
传参方式二:传入结构体指针,传递地址会改变值
// *传入结构体指针:传递地址会改变值
void funPointer(struct stuInfo3 *s)
{
strcpy(s->name, "小栏");
s->Num = 90;
}
传参方式三:返回结构体
// *返回结构体
struct stuInfo3 returnS()
{
struct stuInfo3 s = {"里斯", 29};
return s;
};
传参方式四:返回结构体指针
// *返回结构体指针
struct stuInfo3 *returnPointerS()
{
struct stuInfo3 s;
strcpy(s.name, "小");
s.Num = 01;
return &s; // 返回地址后,前面的值在栈区被销毁
};
2.1.5 嵌套结构体
使用
.
一层一层往里调用即可。
// 内层
struct inner
{
int inNum;
char inName[15];
};
// 外层
struct external
{
int exAge;
struct inner sIn;
};
int main()
{
struct external s;
s.exAge = 20;
strcpy(s.sIn.inName, "小米");
s.sIn.inNum = 2;
printf("%d, %s, %d", s.exAge, s.sIn.inName, s.sIn.inNum);
return 0;
}
案例:
#include<stdio.h>
/***********定义结构体**********/
struct stu2{
char name[15];
float score;
};
/***********冒泡排序**********/
void sortStruct(stu2 *s)
{
for(int i=0; i<3; i++)
{
for(int j=0; j<4-i-1; j++)
{
if(s[j].score < s[j+1].score)
{
stu2 temp = s[j];
s[j] = s[j+1];
s[j+1] = temp;
}
}
}
}
int main()
{
struct stu2 s[4]; // 创建结构体
// 循环写入多条数据
for(int i=0; i<4; i++)
{
printf("请输入姓名和成绩:\n");
scanf("%s%f", s[i].name, &s[i].score);
}
// 冒泡排序
sortStruct(s);
// 循环取出每一条数据
for(int i=0; i<4; i++)
{
printf("请输入姓名:%s,成绩:%.2f\n", s[i].name, s[i].score);
}
}
2.1.6 内存对齐
- 结构体中以一个
最大的数据类型
为单位;- 按照排序将每一个都填充满,若有剩余,则下一个刚才满足该空间的,即可在同一个空间,若不满足,则需要下一个空间;
- 可使用
#pragma pack(1)
保证结构体对齐。
2.1.7 复合字面量
C99允许字面量的特性用于
结构
和数组
中。
struct 标签名 {成员值};
2.1.8 伸缩型数组成员
C99中使用伸缩型数组特性:
- 该数组不会立即存在,即程序运行时,
不会立即分配内存
;- 使用其
创建指定个数的数组
;
struct stu{
int num;
float score[];
}
此时该结构体一般被
声明为指针
;
struct stu *stu1;
stu1 = (struct stu)malloc(sizeof(struct stu) + 3*sizeof(float));
// 即分配了一个num、一个3个score的空间
满足条件:
- 伸缩型数组成员必须要是该结构的
最后一个成员
;- 结构中
至少有一个成员
;- 声明类似于数组,但
[]
中间是空的;
注意:
- 若通过指针
赋值
的方式进行拷贝,则无法将伸缩型数组拷贝
。若要拷贝,则使用mencpy
;不能以值传递的方式传递给函数
,原因是无法将伸缩型数组拷贝;- 不要使用带伸缩型数组成员的结构作为
数组成员
或另一个结构中的成员
。
2.2 联合体
联合于结构类似,联合的所有成员都是处于内存中相同的位置。
- 联合的内存数量取决于它的最长成员的长度;
- 一次只存储一个值;
union {
int a;
double b;
float c;
char k;
}
2.2.1 联合体的初始化
联合变量可以被初始化,但初始值必须是
第一个成员
的类型;
- 初始化于结构体相似;
2.3 枚举
是声明符号来标识整型常量;
- 提高程序的
可读性
;- 常用于
switch
;- 只能在内部使用;
enum {
low,
medium,
tiger
}test;
2.3.1 初始化联合体
- 若枚举内的成员没有被初始化,则默认从
0开始依次赋值
;- 若其中有成员被赋值,则后续将依次
加1递增
;