目录
1. 结构体
C语言中, 数组 是一个存储 相同类型 数据项的变量;而 结构体 可以存储 不同类型 的数据项。
(a) 定义
struct 结构体名 {
类型名1 成员名1;
类型名2 成员名2;
类型名3 成员名3;
...
}结构体变量;
struct 结构体名 {
类型名1 成员名1;
类型名2 成员名2;
类型名3 成员名3;
...
};
struct 结构体名 结构体变量
struct {
类型名1 成员名1;
类型名2 成员名2;
类型名3 成员名3;
...
}结构体变量;
-
struct 是关键字,是结构体类型的标志
-
结构体名 ,相当于 int , 只是表明一个类型
-
结构体变量 ,相当于真正的变量
(b) 注意点
-
不允许对结构体本身递归定义:line3
1 struct Student { 2 int age; 3 struct Student stu; 4 };
-
结构体内可含其他结构体
1 struct Date { 2 int year; 3 int month; 4 int day; 5 }; 6 7 struct Student { 8 char *name; 9 struct Date birthday; 10 };
-
定义结构体类型只说明了该类型的组成情况,并没有给它分配存储空间(就和系统不为int类型本身分配空间一样),只有当定义结构体变量时,才会跟配存储空间
1 struct Student { 2 char *name; 3 int age; 4 }; 5 6 struct Student stu;
line1-4没有给分配存储空间;line6定义 结构体变量 stu 时才分配了存储空间。
-
结构体变量占用的内存空间是其成员所占内存之和,且个成员在内存中按定义的顺序依次排列
1 struct Student { 2 char *name; // 姓名 3 int age; // 年龄 4 float height; // 身高 5 };
在16位的环境下,一个 s t u d e n t student student 变量共占用内存: 2 + 2 + 4 = 8 2+2+4=8 2+2+4=8 字节
© 结构体初始化
将个成员的初始化值,按顺序放在一对花括号内,并用逗号分隔,一一对应赋值:
1 struct Student {
2 char *name;
3 int age;
4 };
5
6 struct Student stu = {"MJ", 27};
只能在定义变量的同时进行初始化,初始化和定义变量不可以分开。
(d) 结构体的使用
-
一般以成员为单位进行,格式:结构体变量名 . 成员名。line9对结构体的成员 a g e age age 进行了赋值。
1 struct Student { 2 char *name; 3 int age; 4 }; 5 6 struct Student stu; 7 8 // 访问stu的age成员 9 stu.age = 27;
"."称为成员运算符,它在所有运算符中优先级最高。
-
如果结构体里的成员也是结构体变量,那么可以连续使用成员运算符“.” 访问最低一级的成员。
1 struct Date { 2 int year; 3 int month; 4 int day; 5 }; 6 7 struct Student { 8 char *name; 9 struct Date birthday; 10 }; 11 12 struct Student stu; 13 14 stu.birthday.year = 1986; 15 stu.birthday.month = 9; 16 stu.birthday.day = 10;
-
相同类型的结构体变量之间可以整体赋值
1 struct Student { 2 char *name; 3 int age; 4 }; 5 6 struct Student stu1 = {"MJ", 27}; 7 8 // 将stu1直接赋值给stu2 9 struct Student stu2 = stu1; 10 11 printf("age is %d", stu2.age); 输出:age is 27
(e) 结构体数组
-
定义:定义一个变量名为stu的结构体数组,数组元素个数为5
struct Student { char *name; int age; }; struct Student stu[5]; //定义1
struct Student { char *name; int age; } stu[5]; //定义2
typedef struct Student { char *name; int age; } Node; //定义2 Node stu[5];
struct { char *name; int age; } stu[5]; //定义3
-
初始化
struct { char *name; int age; } stu[2] = { {"MJ", 27}, {"JJ", 30} };
也可以用数组下标访问每一个结构体元素,跟普通数组的用法是一样的
(f) 结构体作为函数参数
结构体变量作为函数参数进行传递时,传递的是全部成员的值,即将实参中成员的值赋给对应的形参成员,所以形参的改变不会影响实参。
1 #include <stdio.h>
2
3 // 定义一个结构体
4 struct Student {
5 int age;
6 };
7
8 void test(struct Student stu) {
9 printf("修改前的形参:%d \n", stu.age);
10 // 修改实参中的age
11 stu.age = 10;
13 printf("修改后的形参:%d \n", stu.age);
14 }
15
16 int main(int argc, const char * argv[]) {
18 struct Student stu = {30};
19 printf("修改前的实参:%d \n", stu.age);
21 // 调用test函数
22 test(stu);
25 printf("修改后的实参:%d \n", stu.age);
26 return 0;
27 }
(g) 指向结构体的指针
- 因为每个结构体变量都有自己的存储空间和地址,所以指针也可以指向结构体变量
- 结构体指针变量的形式:struct 结构体名称 *指针变量名
- 访问结构体成员的方式:
- 结构体变量名. 成员名
- (*指针变量名) . 成员名
- 指针变量名 -> 成员名
1 #include <stdio.h>
2
3 int main(int argc, const char * argv[]) {
4 // 定义一个结构体类型
5 struct Student {
6 char *name;
7 int age;
8 };
9
10 // 定义一个结构体变量
11 struct Student stu = {"MJ", 27};
12
13 // 定义一个指向结构体的指针变量
14 struct Student *p;
15
16 // 指向结构体变量stu
17 p = &stu;
18
19 /*
20 这时候可以用3种方式访问结构体的成员
21 */
22 // 方式1:结构体变量名.成员名
23 printf("name=%s, age = %d \n", stu.name, stu.age);
24
25 // 方式2:(*指针变量名).成员名
26 printf("name=%s, age = %d \n", (*p).name, (*p).age);
27
28 // 方式3:指针变量名->成员名
29 printf("name=%s, age = %d \n", p->name, p->age);
30
31 return 0;
32 }
2. 链表
-
链表是一种常见的数据结构, 结构体指针 在这里得到了充分的利用。
-
链表可以动态的进行存储分配,即,链表是一个功能强大的 数组 ,它可以在节点中定义多个数据类型;也可根据需要添加、删除、插入节点。
-
链表有一个 头指针 ,一般用 head 表示,用于存放地址。
-
链表中的节点分为 两类 :
- 链表中的节点有两个部分:数据域+指针域
- 头节点:没有数据域
- 一般节点:指针域+数据域
-
链表像一条锁链:head指向第一个元素 → \to → 第一个元素指向第二个元素 → \to → …… → \to → 最后一个元素结束,称为 表尾 ,其指针域存放 “NULL” 表示时空地址。
-
应用:链表的创建,修改,删除,插入,输出,排序,反序,清空链表的元素,求链表的长度等等。
(a) 创建链表
typedef struct student{
int score;
struct student *next;
} LinkList;(注意分号)
(b) 初始化链表
LinkList *creat(int n){
LinkList *head, *node, *end;//定义头节点,普通节点,尾部节点;
head = (LinkList*)malloc(sizeof(LinkList));//分配地址
end = head; //若是空链表则头尾节点一样
for (int i = 0; i < n; i++) {
node = (LinkList*)malloc(sizeof(LinkList));
scanf("%d", &node->score);
end->next = node;
end = node;
}
end->next = NULL;//结束创建
return head;
}
分析一下上面的过程吼:
-
先定义三个结构体指针: h e a d head head 作为头节点; n o d e node node 作为一般节点; e n d end end 作为尾节点。
-
先给头节点 h e a d head head 分配地址空间,大小为结构体的大小;
-
首先让尾节点 e n d end end 指向头节点 h e a d head head ,此时链表为空链表(此时 h e a d head head 和 e n d end end 是指向的同一个结构体);
-
其次通过遍历,得到 n n n 个普通节点:首先给 n o d e node node 分配空间,将输入的值赋给 n o d e node node 的 s c o r e score score ,然后将 e n d end end (此时为头节点 h e a d head head )中的 n e x t next next 结构体赋为前面定义好的 n o d e node node ;最后让 e n d end end 指向 n o d e node node 结构体,重复。【本质上就是 e n d end end 的滑动,指来指去】
-
最后将 e n d end end 中的结构体指向 NULL ,结束链表的创建。
© 修改链表节点值
void change(LinkList *list,int n) {//n为第n个节点
LinkList *t = list;
int i = 0;
while (i < n && t != NULL) {
t = t->next;
i++;
}
if (t != NULL) {
puts("输入要修改的值");
scanf("%d", &t->score);
}
else {
puts("节点不存在");
}
}
分析一下上面的过程吼:
- 先定义一个结构体指针 t t t ,指向结构体指针 l i s t list list 。
- 然后通过while先遍历到需要改的节点(t != NULL 为了防止要改的节点超出范围/尾节点)
(d) 删除链表节点
删除节点就是将 前一个节点的指针域 要越过删除的节点,指向下一个节点。即:p->next = q->next,释放节点q的空间,free(q)
void delet(LinkList *list, int n) {//删除节点n
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL) {//遍历到要删除的点前一个节点
in = t;//要删除的前一个节点
t = t->next;//要删除的节点
i++;
}
if (t != NULL) {
in->next = t->next;
//让指向前一个节点的next结构体的in,指向下一个节点的next结构体
free(t);//释放要删除的节点的空间
}
else {
puts("节点不存在");
}
}
(e) 插入链表节点
插入节点就是用 插入前节点 的 指针域 链接上插入节点的 数据域 ,再把 插入节点 的 指针域 链接上 插入后节点 的 数据域 ,此时这个数据域存在前节点的指针域中。根据图,插入节点也就是:e->next = head->next ; head->next = e。
void insert(LinkList *list, int n) {
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL) {
t = t->next;
i++;
}
if (t != NULL) {
in = (LinkList*)malloc(sizeof(LinkList));
puts("输入要插入的值");
scanf("%d", &in->score);
in->next = t->next;//填充in节点的指针域,也就是说把in的指针域指向t的下一个节点
t->next = in;//填充t节点的指针域,把t的指针域重新指向in
}
else {
puts("节点不存在");
}
}
(f) 输出链表
遍历输出:
while (h->next != NULL) {
h = h->next;
printf("%d ", h->score);
}
Reference
- https://www.runoob.com/cprogramming/c-structures.html
- https://www.cnblogs.com/mjios/archive/2013/03/24/2977910.html
- https://www.cnblogs.com/ariel-dreamland/p/10469696.html