结构体
结构体类型的声明
结构体是一种用户自定义的数据类型,用于存储不同类型的数据,可以包含多个不同的成员变量。
1 2 3 4 5
| struct 结构体名 { 类型 成员变量1; 类型 成员变量2; };
|
注意:
1、{ }内部是结构体的成员变量
2、结构体结尾时,最后的分号不能丢
特殊声明
匿名结构体声明
可以在定义一个结构体类型的同时,直接定义变量,可以不给结构体起名字
使用场景:一次性使用的结构体类型
1 2 3 4 5 6 7 8 9 10
| struct { int age; char name[20]; } person; struct { int age; char name[20]; } person[20];
|
结构体成员也是结构体类型
结构体可以作为另一个结构体的成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <stdio.h> struct Date { int year; int month; int day; }; struct Person { char name[20]; struct Date birthday; };
int main() { struct Person p[3] = { {"Ethaniel", 2000, 10, 1},{"李四", 2001,5,1},{"王五",{2023, 1, 1}} }; printf("%s, %d-%d-%d\n", p[1].name, p[1].birthday.year, p[1].birthday.month, p[1].birthday.day);
printf("%lu\n", sizeof(struct Person)); printf("%lu\n", sizeof(p[0]));
return 0; }
|
结构体变量的定义、初始化和成员访问
结构体的变量是普通变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h> #include <string.h>
struct Student { char s_name[20]; char s_id[20]; int score[3]; }; int main() { struct Student s1; strcpy(s1.s_id,"s001"); strcpy(s1.s_name,"Ethaniel"); s1.score[0] = 90; s1.score[1] = 95; s1.score[2] = 60;
printf("姓名:%s\n", s1.s_name); printf("学号:%s\n", s1.s_id); printf("语文成绩:%d,数学成绩:%d,英语成绩:%d\n", s1.score[0],s1.score[1],s1.score[2]);
struct Student s2 = {"李四","s002", 99, 88, 77}; printf("姓名:%s\n", s2.s_name); printf("学号:%s\n", s2.s_id); printf("语文成绩:%d,数学成绩:%d,英语成绩:%d\n", s2.score[0],s2.score[1],s2.score[2]);
strcpy(s2.s_name, "王五"); printf("姓名:%s\n", s2.s_name); return 0; }
|
结构体变量是指针变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <string.h>
struct Student { char name[20]; int age; }; int main() { struct Student s; struct Student *ptr = &s; strcpy(ptr->name,"王五"); ptr->age=18; (*ptr).age = 19; printf("姓名:%s\n", ptr->name); printf("年龄:%d\n", (*ptr).age);
strcpy(ptr->name,"如花"); (*ptr).age = 23;
return 0; }
|
注意事项:
1、使用正确的运算符:普通变量用点运算符(.),指针变量用箭头运算符(->)来访问结构体的成员2、正确的成员名:结构体成员的名称是区分大小写的,需要确保大小写正确。
其他初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<stdio.h> #include<string.h> struct Person { char name[20]; int age; }; int main() { struct Person p1 = {}; strcpy(p1.name, "Tom"); p1.age = 20; printf("姓名:%s,年龄:%d\n",p1.name,p1.age);
struct Person p2; strcpy(p2.name, "Jerry"); p2.age = 25; printf("姓名:%s,年龄:%d\n",p2.name,p2.age); struct Person p3=p2; printf("姓名:%s,年龄:%d\n",p3.name,p3.age); struct Person p4={"lisi",18}; printf("姓名:%s,年龄:%d\n",p4.name,p4.age); struct Person p5={ .name ="wangwu", .age = 19 }; printf("姓名:%s,年龄:%d\n",p5.name,p5.age); return 0; }
|
结构体传参
值传递
将整个结构体变量的值复制一份,传递给函数。在函数内部对结构体的修改不会影响原始的结构体类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> struct Point { int x; int y; }; void printPoint(struct Point p) { p.x=30; p.y=50; printf("函数中:x: %d, y: %d\n", p.x, p.y); } int main() { struct Point p = {3, 5}; printPoint(p); printf("main函数中:x: %d, y: %d\n", p.x, p.y); return 0; }
|
注意事项:
1、优点:保护原始数据不被修改。
2、缺点:若结构体大,可能消耗大量内存。
指针传递
将结构体的指针传递给函数,通过指针可以直接修改原始的结构体变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> struct Point { int x; int y; }; void modifyPoint(struct Point* p) { p->x += 10; p->y += 10; printf("函数中:x: %d, y: %d\n", p->x, p->y); } int main() { struct Point p = {3, 5}; modifyPoint(&p); printf("main函数中:x: %d, y: %d\n", p.x, p.y); return 0; }
|
注意事项:
1、优点:内存使用高效,大型结构体传递快速。
2、缺点:可能导致原始数据不安全,因为可以通过指针直接修改结构体内的数据。
typedef用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include<stdio.h> #include<string.h>
typedef struct st_student//st_student结构体名称 { char name[20]; int age; }Stu;
int main() { Stu student = {}; strcpy(student.name,"ww"); student.age=16; printf("姓名:%s\n", student.name); return 0; }
|
简单链表
结构体的自引用就是指在结构体内部,包含指向自身类型结构体的指针。
链表是由一个个节点通过指针域链接而成的表,所以,我们只需要定义一个节点,就可以定义整个表
1 2 3 4 5
| typedef struct Node { int data; struct Node* next; }*linklist;
|
首元结点:链表中有有效(非头节点)的第一个节点称为:”首元结点”
头节点:”首元结点”前额外增设的节点,特点:数据域内一般不放数据,也可以放链表长度等信息。
头指针:指向链表的第一个节点的指针;作用,他用来识别链表的起始位置,并通过它可以遍历整个链表
空表:头指针或者头节点的指针域为null的链表

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <stdio.h> #include <stdlib.h>
struct Node { int data; struct Node* next; }; int main() {
struct Node *head = NULL; for (int i = 0; i<100; i++) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = i; newNode->next = head; head = newNode; } struct Node* p = head; while(NULL!= p) { printf("%d ", p->data); p = p -> next; } printf("\n"); return 0; }
|
两种不合法案例
1 2 3 4 5 6
| struct Node { int data; struct Node next; };
|
1 2 3 4 5 6
| typedef struct { int data; Node* next; }Node;
|
注意事项:结构体自引用,成员定义只能是指针。
结构体内存对齐
是指编译器在分配结构体变量的内存空间时,按照一定的规则将结构体成员按照一定的字节对齐方式进行排列。
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h> struct Test { int a; char b; int c; }; int main() { struct Test test = {1,'a',3}; printf("%lu\n", sizeof(test)); return 0; }
|
注:以下都是在32位操作系统上讨论
寄存器只能从整除以4的地址开始读取数据
内存对齐的目的:提高内存访问效率和处理器的数据访问速度
注意:
结构体的内存对齐是由编译器根据一定的规则来进行的,可以提高内存访问的效率和速度。对于特定的应用场景,可以根据需要进行适当的对齐优化。
枚举
枚举是一种用户自定义的数据类型,用于定义一组具体的常量。枚举常量被称为枚举值,它们可以是整数、字符或字符串类型。
枚举类型的定义及使用
枚举定义:
enum 枚举类型名
{
枚举值1,
枚举值2,
…
};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h>
enum Weekday { MONDAY, TUESDAY, }; int main() { enum Weekday today = TUESDAY; printf("%d\n", today); switch(today) { case MONDAY: printf("今天是星期一\n"); break; case TUESDAY: printf("今天是星期二\n"); break; default: printf("无效的枚举值\n"); } return 0; }
|
枚举的使用场景
| 场景 |
说明 |
案例 |
| 状态/类型 |
当程序中有一组固定的状态或类型需要使用时 |
enum season { spring, Summer, }; |
| 错误码的表示 |
枚举来表示不同的错误码,提高代码的可读性和可维护性 |
enum ErrorCode { SUCCESS, NOT_FOUND, }; |
| 选项的表示 |
当某个变量有一组固定的选项时 |
enum Gender { MAN, UNMAN, }; |
| 常量集合的定义 |
当程序需要定义一组常量时 |
enum Color { MAN, UNMAN, }; |
注意:
1、枚举类型变量的值其实就是整数值
2、枚举类型的变量赋值尽可能使用枚举变量,而不是整数值
共用体(联合)
共用体(union)是一种特殊的数据类型,它允许不同的数据类型共享同一块内存空间。只能同时存储其中一个成员的值。
共用体类型的定义
定义格式(声明):
union 联合名称
{
类型 成员变量1;
类型 成员变量2;
//…
};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include<stdio.h> union MyUnion { int a; char b; int c; }; int main() { union MyUnion u; u.a = 1; u.b = 'a'; u.c = 3; printf("%lu\n",sizeof(u)); return 0; }
|
共用体的特点
由于共用体的成员共享同一块内存空间,因此对一个成员的赋值会影响到其他成员的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| union MyUnion { int num; char c; }; int main() { union MyUnion u = {}; u.num = 97; printf("%d\n", u.num); printf("%c\n", u.c); u.c = 'A'; printf("%d\n", u.num); printf("%c\n", u.c); return 0; }
|
注意事项:
联合体的使用需要谨慎,因为它对数据的存储和访问方式较为复杂,容易引发错误。