C语言中,用结构体来将多种不同的数据类型整合在一起,形成某种意义上的自定义数据类型,结构体本质上就是一个自定义数据类型
结构体就是多个不同数据类型组合在一起,形成一个自定义的数据类型
所以结构体是一个组合数据类型,把他叫做结构体数据类型
标签名就是用来区分对应不同的东西的,函数标签名就是函数名加上参数列表,也就是区分函数类型
1.结构体声明
用结构体之前一定要先结构体声明
第一次用结构体数据类型记得展开就行,其余一模一样和普通变量,展开的东西就是结构体声明的东西
结构体声明就是第一次使用结构体数据类型的格式,后面第二次写数据类型省略里面内容
结构体声明就是结构体的数据类型,只不过是第一次且必须,后面第二次写数据类型省略里面内容
如果还要申请其他结构体,也要先写结构体声明后面一样
struct 结构体名(标签名)
{
结构体成员 1 (数据类型的声明);
结构体成员 2 (数据类型的声明);
结构体成员 3 (数据类型的声明);
结构体成员 n (数据类型的声明);
}结构体变量名或者不写;// 以分号结尾
struct是关键字,结构体成员1就是类似int a,这里的数据类型甚至可以是数组,结构体
结构体声明的作用就是声明这个结构体数据类型里面长啥样
所以第一次声明后,我就不用再写这个数据类型里面长啥样了,省去花括号内容
第一次用结构体数据类型记得展开就行,其余一模一样和普通变量
定义一个结构体先写结构体声明再写结构体变量名
2.声明结构体变量有两种方式
1.在结构体声明的时候顺便在花括号后面写上变量名
在结构体声明花括号接变量名叫做直接定义结构体变量名
在结构体外面定义的结构体变量是间接定义结构体变量名
struct student
{
int age;
int score;
char name[5];
}a,b; //意思是申明了两个结构体数据类型的变量 相当于int a,b
2.函数声明过后,用数据类型加变量名的方式定义
struct student a //相当于int a
3.结构体指针变量
struct student *a //相当于int *a
4.结构体数组变量
struct student a[2] //相当于int a[5]存放五个结构体类型的数据,但是结构体又是组合数据类型
这里就有大坑小坑,实际是一个二维数组
结构体数组的中括号是二维中一维数组的个数,是结构体的个数
struct student a[2]={{20,80,"zhangsan"},{40,90,"lisi"}
结构体数组也可以用数组下标,p[0]就是第一个结构体
既然结构体可以写成数组,那数组名也有含义,数组名是数组首元素地址
那么结构体数组的数组名是第一个结构体的地址,结构体数组也可以偏移
a+1就是跳过一整个结构体到达下一个结构体就是p[1]
3.结构体变量的初始化
结构体变量的初始化要根据结构体变量名出发
结构体变量用结构体类型初始化要初始化多个用花括号
结构体变量点运算符初始化是初始化单个不用花括号
1.结构体变量名在结构体声明初始化
struct student
{
int age;
int score;
char name[5];
}a={20,80,"zhangsan"}
2.结构体变量名用结构体数据类型初始化
struct student
{
int age;
int score;
char name[5];
}a,b; //这里写不写都一样,只不过代表这个和下面的可以共存,这样也是可以的,就把结构体声明看成数据类型就行了,只是这里未初始化
1.
用结构体数据类型的变量名不用写在函数声明上
struct student a={13,60,"lisi"} //按着结构体成员顺序初始化 //就是一数据类型而已,变量名随便取
struct student a.age=20 //这样写是错的
不用管编译器知不知道,因为第一次使用结构体类型已经声明了
2.
struct student a={.score=80, .age=23, .name="liubei"} //不按着结构体成员顺序初始化,需要用到 . 运算符
struct student a={ a.score=80 }; // ❌语法错误
在 {} 初始化里面,成员前面不能加变量名,只能 .成员名
3.
struct student a={.score=80, .age=23} //部分初始化
结构体的未初始化该是什么数据分为两种
1.如果结构体是带{ }初始过一次其他的成语,但没有初始未初始化的,无论结构体本身放在全局还是局部,未初始化的都是对应类型的0
2.如果结构体是没有{ },没有初始化任何东西,此时就按照本来处于什么空间赋值对应值就行
struct student a = stuct studnet b //结构体数据类型也是数据类型,同种数据类型也可以赋值,但这里必须是同种结构体,不同变量名而已
3.结构体变量名用点运算符初始化
点运算符服务对象在单独的变量名和数组下标还有花括号里面,单独的变量名是没有数据类型的变量名
用点运算符如果是单独变量名要在函数声明写上变量名
struct student
{
int age;
int score;
char name[6];
}a,b;
结构体用点运算符初始化成员格式
结构体变量名 . 成员名=初始化内容
a . age=20 ;
b . name="hello"
c.score[1]=20 //这样也是可以的,score[1]只是代表score数组第一个元素
4.结构体数组的初始化
结构体数组不是二维数组,只是很多数据类型合在一起才算一个元素
数组成立的条件,一片连续的内存,每个元素字节相同,每个元素数据类型相同
其他东西只要满足连续内存和数据类型相同就可以用数组下标,如果所处空间能够定义变量就可以用花括号初始化,堆空间不能定 义变量
结构体数组的初始化有两种(只要是数组的初始化都可以用定义赋值和数组下标)
1.定义数组变量的时候赋值
struct student a[2]={{20,80,"zhangsan"},{30,60,"lisi"}
这里花括号里面不能写点运算符,因为数组元素没有变量名
2.用strcpy函数之间覆盖拷贝
结构体的 “的”
. 作用:通过结构体变量名数组下标访问内部成员
只要用点运算符点前面只能跟变量名
只要用点运算必须前面有这个结构体变量名
格式:
a.age=20 //此时这个变量名是age,数据类型是int ,.只是运算符不修改数据类型
用这个a之前必须前面有a这个变量,甚至不给a赋值,就定义变量都行
node_t a;
a.date=10;
a.next=NULL;
- > 作用:通过结构体指针访问内部成员 是“的”意思,,还可以运用于函数调用地址传递
只要用->前面只能跟结构体指针名,只要是这个结构体指针的地址相同,都可以->去访问里面的东西
不论多少个结构体指针名只要这些指针里面的地址相同都可以去访问里的元素
总结:
只要用->前面只能跟结构体指针名
->前面指针名只要地址一样就能访问
->前面只管地址
格式:
struct student *a;
a ->age=20 //变量名还是age,数据类型还是int, -> 也不修改数据类型
. 用于值传递,形参是结构体时候,结构体访问成员用 .
- > 用于地址传递,形参是结构体指针时候,结构体访问成员用->
struct student a[2]={{20,80,"zhangsan"},{30,60,"lisi"}
strcpy((a+1) ->name , "liubei") //a+1就是第二个结构体的指针,(a+1) ->name实际就是数组名name
strcpy((a[0] . name,"wangwu") //a[0]就是第一个结构体,(a[0] . name)实际上就是数组名name
说白了这里a[0]就是一次结构体声明,a[0]是变量名
注意:
结构体数组就是一个一维数组,一维数组里面只有一个元素,只是这个元素是很多数据类型合起来的
{20,80,"zhangsan"}整体数据类型就是结构体数据类型,所以整个数组数据类型和字节相同
3.数组下标
a[0].name="cuihua"
a[0].age=20 //结构体的数组下标只能针对于一维数组,这样才可以满足数据类型相同,字节是整个结构体
5.结构体与传参
结构体如果遇到传参,结构体声明必须写在函数声明前面
因为编译器认识不了结构体这个东西,必须现在函数声明前写结构体声明
而其他数据类型遇见传参不用把数据类型写在函数声明前
6.结构体与typedef
结构体与typedef分为匿名结构体typedef和有标签名typedef
1.匿名结构体typedef
匿名结构体是什么
匿名结构体就是结构体声明没有标签名
如
struct
{
int age;
char name[];
int score;
}a,b; //此时a,b是结构体数据类型名,a,b不是变量名
此时a,b替代了struct 标签名
匿名结构体无法单独间接定义新的结构体数据类型变量
struct stu a //就不行,因为此时压根没有标签名,无法定义新的结构体数据类型
所以只能直接定义新的结构体数据类型,直接定义结构体数据类型的方式就是在结构体声明花括号后面跟变量名
小扩展
两个匿名结构体,成员一模一样,编译器视为两种不同类型
struct {int a;} x;
struct {int a;} y;
x = y; // 编译报错!类型不匹配
如果匿名结构体后续还想间接定义新的结构体数据类型怎么做?
用typedef关键字
匿名结构体配合typedef
匿名结构体取别名:
tyepdef struct
{
int a;
char name[10];
}stu; //此时stu是类型名不是变量名
匿名结构体typedef花括号后面的东西是类型名,用来替代结构体数据类型名的,其余用到结构体数据类型的指针,数组也是由类型
名替代
匿名结构体有了typedef就可以间接定义结构体数据类型变量
此时要写间接定义结构体类型变为了 stu a ,变为花括号后面加变量名
等价于创建一个匿名结构体类型的类型名
2.有标签名typedef
有标签结构体取别名
此时如果这个结构体花括号后面没有变量名,定义间接结构体数据类型变量只能按照原本格式,struct 标签名 变量名
但是如果这个结构体花括号后面有变量名,定义间接结构体数据类型可以按照原本格式也可以按照匿名结构体定义的格式
typedef struct student
{
int a;
char name[10];
}
现在typedef有标签名结构体花括号后面没有变量名,此时定义间接数据类型只能是struct student a,其余用到结构体数据类型的
指针数组还是不变
有标签名结构体的typedef后面花括号没有变量名,这个typedef没有起作用,带不带typedef都一样
typedef struct student
{
int a;
char name[10];
}stu;
现在typedef有标签名结构体花括号后面有变量名,此时定义间接数据类型有两种:struct student a
stu a
其余用到结构体数据类型的指针,数组可以用类型名替代也可以不用
像数组:struct student arr[10] 就是 stu arr[10];
甚至是sizeof,malloc里面的数据类型也随便替换或者不替换
所以我的理解是对的,类型名就是替换数据类型名了
struct student *node = malloc(sizeof(struct student));
stu *node = malloc(sizeof(stu));
有标签名结构体的typedef后面花括号有变量名,这个typedef才有作用,此时后面花括号变量名就算
注意:
结构体声明用了typedef就不能在结构体声明初始化了
typedef struct student
{
int a;
char name[10];
}stu={3,"abc"} //这样是错的