目录
[1. 结构体的定义](#1. 结构体的定义)
[2. 结构体变量的声明](#2. 结构体变量的声明)
[3. 结构体成员的访问](#3. 结构体成员的访问)
[使用 . 运算符(结构体变量):](#使用 . 运算符(结构体变量):)
[使用 -> 运算符(结构体指针):](#使用 -> 运算符(结构体指针):)
[4. 结构体的初始化](#4. 结构体的初始化)
[1-4 编程示例](#1-4 编程示例)
[5. 结构体的大小](#5. 结构体的大小)
[6. 结构体数组](#6. 结构体数组)
[7. 结构体作为函数参数](#7. 结构体作为函数参数)
[8. 动态内存分配](#8. 动态内存分配)
[1-8 编程示例](#1-8 编程示例)
[1. 联合体的定义](#1. 联合体的定义)
[2. 联合体变量的声明](#2. 联合体变量的声明)
[3. 联合体的内存分配](#3. 联合体的内存分配)
[4. 联合体的访问](#4. 联合体的访问)
[5. 联合体的初始化](#5. 联合体的初始化)
[6. 联合体 vs 结构体](#6. 联合体 vs 结构体)
[7. 注意事项](#7. 注意事项)
[1. 枚举的定义](#1. 枚举的定义)
[2. 枚举的特性](#2. 枚举的特性)
[(1) 显式赋值](#(1) 显式赋值)
[(2) 隐式递增](#(2) 隐式递增)
[(3) 类型本质](#(3) 类型本质)
[3. 枚举变量的使用](#3. 枚举变量的使用)
[结合 switch 语句](#结合 switch 语句)
[4. 枚举 vs 宏定义](#4. 枚举 vs 宏定义)
[typedef 用法](#typedef 用法)
结构体
C语言中的结构体(struct
)是一种用户自定义的数据类型,允许将多个不同类型的变量组合成一个单一的逻辑单元。
例如:整型数,浮点型数,字符串是分散的数据表示 有时候我们需要用很多类型的数据来表示一个整体,比如学生信息:

类比与数组:++数组是元素类型一样的数据集合 如果是元素类型不同的数据集合,就要用到结构体了++
1. 结构体的定义
结构体通过 struct
关键字定义,语法如下:
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例:定义一个学生结构体
cpp
struct Student {
char name[50];
int age;
float score;
};

它算是一个模板,一般不给赋具体的值,每一项在实际应用中并不是都要使用
2. 结构体变量的声明
- 直接声明:
cpp
struct Student s1; // 声明一个Student类型的变量s1
- 定义时声明:
cpp
struct Student {
// ...
} s1, s2; // 同时声明s1和s2
- 使用
typedef
简化:
cpp
typedef struct {
char name[50];
int age;
} Student; // 重命名为Student
Student s1; // 直接使用,无需写struct
3. 结构体成员的访问
使用 .
运算符(结构体变量):
**功能:**直接访问结构体变量的成员。
cpp
s1.age = 20;
printf("年龄: %d\n", s1.age);
使用 ->
运算符(结构体指针):
**功能:**通过指针间接访问结构体的成员(等价于先解引用指针,再用点运算符)。
cpp
struct Student *ptr = &s1;
ptr->age = 22;
printf("年龄: %d\n", ptr->age);
4. 结构体的初始化
cpp
#include <stdio.h>
#include <string.h>
struct Student
{
int num;
char name[28];
char sex[5];
int age;
};
int main()
{
struct Student stu;
struct Student stu1 = {2,"李四","男",18}; // 初始化方式一
// 初始化方式二
stu.num = 1;
strcpy(stu.name,"张三");
strcpy(stu.sex,"男");
stu.age = 19;
printf("学号:%d,姓名:%s,性别:%s,年龄:%d\n",stu.num,stu.name,stu.sex,stu.age);
printf("学号:%d,姓名:%s,性别:%s,年龄:%d\n",stu1.num,stu1.name,stu1.sex,stu1.age);
return 0;
}
1-4 编程示例
例题:有两个学生的名字,学号,成绩数据。输出成绩高的学生的信息
cpp
#include <stdio.h>
#include <string.h>
struct Student
{
int num;
char name[28];
char sex[5];
int age;
int score;
};
int main()
{
struct Student stu;
struct Student stu1 = {2,"李四","男",18,98};
struct Student max;
max = stu;//假设stu的成绩大
stu.num = 1;
strcpy(stu.name,"张三");
strcpy(stu.sex,"男");
stu.age = 19;
stu.score = 96;
if( max.score < stu1.score )
{
max = stu1;
}
printf("学号:%d,姓名:%s,性别:%s,年龄:%d,成绩:%d\n",max.num,max.name,max.sex,max.age,max.score);
return 0;
}

5. 结构体的大小
使用 sizeof
获取结构体占用的内存大小:
cpp
printf("结构体大小: %zu 字节\n", sizeof(struct Student));
注意:结构体的实际大小可能因内存对齐规则而大于成员大小的总和。
6. 结构体数组
cpp
struct Student class[3] = {
{"Alice", 20, 85.5},
{"Bob", 21, 92.0},
{"Charlie", 19, 78.5}
};
编程示例
结构体数组中有三个学生的信息,请打印三个学生的信息
cpp
#include <stdio.h>
struct Student
{
int stuNum;
char name[32];
char sex[4];
int age;
int score;
};
int main()
{
int i;
int length;
struct Student stu[3] = {
{1,"张三","男",19,86},
{2,"李四","男",21,98},
{3,"小五","女",23,100}
};
//计算结构体数组各数
length = sizeof(stu) / sizeof(stu[0]);
for( i = 0; i < length; i++ )
{
printf("学号:%d,姓名:%s,性别:%s,年龄:%d,成绩:%d\n",
stu[i].stuNum, stu[i].name, stu[i].sex, stu[i].age, stu[i].score);
}
return 0;
}

应用练习:选票系统
cpp
#include <stdio.h>
#include <string.h>
struct XuanMin
{
int num;//编号
char name[32];
int vote;//票数
};
int main()
{
struct XuanMin xm[3];
struct XuanMin max;
int i;
int j;
int length; // 获取结构体各数
int vote_num = 5; // 投票人数
char tempName[32]; // 临时变量,存储候选人姓名
int mark; // 标志位
int invalid_tickets = 0; //废票
// 获取结构体数组各数
length = sizeof(xm) / sizeof(xm[0]);
//初始化候选人信息
for( i = 0; i < length; i++ )
{
xm[i].vote = 0;
printf("请输入第%d位参选者编号及姓名:\n",i + 1);
scanf("%d%s",&xm[i].num,xm[i].name);
}
//唱票环节
for( i = 0; i < vote_num; i++ )
{
mark = 0;
printf("请第%d位投票人,为参选者投票\n", i + 1);
scanf("%s",tempName);
for( j = 0; j < length; j++ )
{
if( strcmp(tempName,xm[j].name) == 0 )
{
xm[j].vote++;
mark = 1;
}
}
if( mark == 0 )
{
invalid_tickets++;
}
}
//宣布结果
for( i = 0; i < length; i++ )
{
printf("姓名:%s,票数:%d\n", xm[i].name, xm[i].vote);
}
max = xm[0]; // 让第一个结构体元素为最大值
for( i = 0; i < length; i++ )
{
if( max.vote < xm[i].vote )
{
max = xm[i];
}
}
printf("恭喜%s,以%d票当选,弃票:%d\n", max.name, max.vote, invalid_tickets);
return 0;
}

7. 结构体作为函数参数
-
传值(拷贝整个结构体):
cppvoid printStudent(struct Student s) { printf("姓名: %s, 年龄: %d\n", s.name, s.age); }
-
传指针(高效,避免拷贝):
cppvoid modifyStudent(struct Student *s) { s->age += 1; }
8. 动态内存分配
cpp
struct Student *s = (struct Student*)malloc(sizeof(struct Student));
strcpy(s->name, "David");
s->age = 25;
free(s); // 释放内存
1-8 编程示例
选票系统2.0
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct XuanMin
{
char name[32];
int numberVotes;
};
// 初始化选民信息(候选人姓名,候选人人数)
void initXuanMin(struct XuanMin **p, int *pn)
{
printf("请输入有多少名候选人:\n");
scanf("%d",pn);
*p = malloc( ( *pn ) * sizeof( struct XuanMin ) );
// 判断是否开辟内存成功
if( *p == NULL )
{
printf("内存开辟失败!\n");
exit(-1);
}
for ( int i = 0; i < *pn; i++ )
{
printf("请输入第%d位候选人名称:\n", i + 1);
scanf("%s", (*p)->name);
// 初始化每一个候选人的票数为0
(*p)->numberVotes = 0;
// 地址偏移,为下一个候选人初始化做准备
(*p)++;
}
*p = *p - *pn; // 将指针 *p 向前移动 *pn 个 struct XuanMin 的位置。
}
// 打印函数
void printfXuanMin(struct XuanMin *p, int numberPeople)
{
for ( int i = 0; i < numberPeople; i++ )
{
printf("姓名:%s,票数:%d\n", p->name, p->numberVotes);
p++;
}
}
// 唱票环节
int Call_Out_Votes(struct XuanMin *p, int numberPeople)
{
int i;
int j;
int mark = 0; // 标志位
int voter = 0; // 投票人数
int invalidated_ticket = 0; // 废票
char tempName[32] = {'\0'};
struct XuanMin *Pbak = p;
printf("请输入为候选人投票人数:\n");
scanf("%d", &voter);
// 请输入每位投票人,想为候选人投票的姓名
for( i = 0; i < voter; i++ )
{
printf("有请,第%d位投票人,投票!\n", i + 1);
scanf("%s", tempName);
// 比对每一个候选人的姓名
for( j = 0; j < numberPeople; j++ )
{
// 如果对比结果正确,相对应的候选人票数+1
if( strcmp(tempName, p->name) == 0 )
{
p->numberVotes++;
mark = 1;
}
p++; // 偏移到下一个候选人,直至最后一个
}
p = Pbak; //使p回到起始地址,为下一轮比较做准备
// 如果mark == 0,那么表示没有匹配到有效候选人姓名,为废票
if( mark == 0 )
{
invalidated_ticket++;
}
mark = 0; //恢复初值,为下一次投票是否为废票,做标记
}
return invalidated_ticket;
}
// 获取票数最多者
struct XuanMin* getTheMostVotes(struct XuanMin *p, int numberPeople)
{
int i;
struct XuanMin *getMaxVotes = NULL;
getMaxVotes = p; //将第一个候选人的结构体地址复制给getMaxVotes
for( i = 0; i < numberPeople; i++ )
{
if( getMaxVotes->numberVotes < p->numberVotes ) //各候选人比对票数
{
getMaxVotes = p;
}
p++;
}
return getMaxVotes;
}
int main()
{
int numberPeople = 0; // 候选人人数
int invalidated_ticket = 0; // 废票
struct XuanMin *xm = NULL;
struct XuanMin *getMaxVotes = NULL;
// 初始化候选人信息
initXuanMin(&xm, &numberPeople);
printf("候选人信息初始化完毕!\n");
printfXuanMin(xm, numberPeople);
// 唱票环节,并且返回废票数
invalidated_ticket = Call_Out_Votes(xm, numberPeople);
printf("投票完成!\n");
printfXuanMin(xm, numberPeople);
// 获取票数最多者
getMaxVotes = getTheMostVotes(xm, numberPeople);
printf("恭喜,候选人:%s 以%d票当选!废票:%d\n", getMaxVotes->name, getMaxVotes->numberVotes, invalidated_ticket);
return 0;
}

9.应用场景
-
数据封装:将逻辑相关的数据组合在一起(如坐标点、学生信息)。
-
文件操作:将结构体数据写入文件或从文件读取。
-
链表/树:通过结构体和指针实现动态数据结构。
联合体/共用体
C语言中的联合体(Union) ,也称为共用体 ,是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。联合体的核心特点是:所有成员共享同一块内存空间,同一时间只能使用其中一个成员。
1. 联合体的定义
联合体通过 union
关键字定义,语法类似结构体:
cpp
union 联合体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例:定义一个存储整数、浮点数或字符串的联合体
cpp
union Data {
int i;
float f;
char str[20];
};
2. 联合体变量的声明
- 直接声明:
cpp
union Data data;
- 定义时声明:
cpp
union Data {
int i;
float f;
} data1, data2;
- 使用
typedef
简化:
cpp
typedef union {
int i;
float f;
} Data;
Data d1;
3. 联合体的内存分配
-
所有成员共享同一块内存空间。
-
联合体的大小等于其最大成员的大小。
cpp
union Data {
int i; // 4字节(假设int为32位)
float f; // 4字节
char str[20]; // 20字节
};
printf("联合体大小: %zu\n", sizeof(union Data)); // 输出20
4. 联合体的访问
使用 .
运算符访问成员(类似结构体):
cpp
union Data data;
data.i = 10;
printf("i = %d\n", data.i);
data.f = 3.14;
printf("f = %f\n", data.f); // 此时i的值已被覆盖!
注意:同一时间只能有一个成员有效,修改一个成员会覆盖其他成员的值。
5. 联合体的初始化
先声明后赋值
联合体变量声明后,可以单独对某个成员赋值:
cpp
union Data data;
data.i = 42; // 使用整形成员i
data.f = 2.5; // 此时i的值被覆盖!
6. 联合体 vs 结构体

7. 注意事项
-
成员覆盖:修改一个成员会覆盖其他成员的值。
-
字节序问题:类型转换时需注意平台的字节序(大端/小端)。
-
未定义行为:访问未初始化的成员可能导致未定义结果。
编程案例

cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct People
{
char name[32]; // 姓名
int number; // 号码
char sex[3]; // 性别
char occupation; // 职业
union Msg
{
char class[7]; // 班级
char job[3]; // 职务
}msg;
};
// 初始化人员数据
void initPeopleMsg(struct People **p, int *pn)
{
printf("请输入人数:\n");
scanf("%d", pn);
scanf("%*c"); // 吸收回车符
// 开辟 *pn 个 struct People 类型的大小内存空间
*p = ( struct People* )malloc( ( *pn ) * sizeof( struct People ) );
// 判断开辟的内存空间是否正常
if( *p == NULL )
{
printf("地址空间开辟失败\n");
exit(-1);
}
// 给每个人员填写数据
for( int i = 0; i < *pn; i++ )
{
printf("请输入你的职业:s 学生、t 教师\n");
scanf("%c",&(*p)->occupation);
getchar(); // 吸收回车符
// 判断是否为t(表示教师)
if( (*p)->occupation == 't' )
{
printf("请输入您的姓名:\n");
scanf( "%s", (*p)->name );
printf("请输入您的号码:\n");
scanf( "%d", &(*p)->number );
printf("请输入您的性别:\n");
scanf( "%s", (*p)->sex );
printf("请输入您的职务:\n");
scanf( "%s", (*p)->msg.job );
}
else //不是,则为s(表示学生)
{
printf("请输入您的姓名:\n");
scanf( "%s", (*p)->name );
printf("请输入您的号码:\n");
scanf( "%d", &(*p)->number );
printf("请输入您的性别:\n");
scanf( "%s", (*p)->sex );
printf("请输入您的班级:\n");
scanf( "%s", (*p)->msg.class );
}
getchar(); // 吸收回车符
(*p)++; //偏移地址
}
*p = *p - *pn; // 使*p回到起始地址
}
// 打印人员数据
void printfMsg(struct People *p, int peopleNum)
{
for (int i = 0; i < peopleNum; i++)
{
// 判断是否为occupation == t,因为联合体会覆盖数据,printf原样输出打印结果不对,所以需要分开打印
if( p->occupation == 't' )
{
printf("职业:%c,姓名:%s,号码:%d,性别:%s,职务:%s\n",p->occupation,p->name,p->number,p->sex,p->msg.job);
}
else
{
printf("职业:%c,姓名:%s,号码:%d,性别:%s,班级:%s\n",p->occupation,p->name,p->number,p->sex,p->msg.class);
}
p++; //偏移地址
}
}
int main()
{
struct People *p = NULL;
int peopleNum = 0; // 人数
// 初始化人员数据
initPeopleMsg(&p, &peopleNum);
// 打印人员数据
printfMsg(p, peopleNum);
return 0;
}

枚举类型
枚举类型用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。 ++定义:是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。++
比如一年有十二个月,一个礼拜有七天,这是毫无疑问的,就可以将这些月份天数用常量来代替。枚举类型和宏定义是差不多的,只有细微区别,++宏运行是在预处理阶段完成的,枚举类型是在与编译阶段完成的。++
1. 枚举的定义
基本语法
cpp
enum 枚举名 {
标识符1, // 默认值为0
标识符2, // 默认值为前一个值+1
// ...
};
**注意:**列表中的名字,可以自己定义,无需像变量一样去申请 。C编译器把它当成常量处理,也称枚举常量
示例:定义一个表示星期的枚举
cpp
enum Weekday {
MON, // 0
TUE, // 1
WED, // 2
THU, // 3
FRI, // 4
SAT, // 5
SUN // 6
};
枚举值默认从 0 开始,往后逐个加 1(递增);也就是说,Weekday中的 MON, TUE, WED, THU, FRI, SAT, SUN 分对应的值为 0、1、2、3、4、5、6。
2. 枚举的特性
(1) 显式赋值
可以手动为枚举成员指定值:
cpp
enum Color {
RED = 1, // 1
GREEN = 2, // 2
BLUE = 4 // 4
};
(2) 隐式递增
未显式赋值的成员自动递增:
cpp
enum State {
IDLE = 1, // 1
RUNNING, // 2
STOPPED, // 3
ERROR // 4
};
(3) 类型本质
-
枚举常量本质是整型(
int
),可以直接参与整数运算。 -
枚举变量的大小与编译器实现相关,通常为
sizeof(int)
。
3. 枚举变量的使用
枚举类型对变量声明的方式
(1)枚举类型的定义和变量的声明分开:
cpp
#include <stdio.h>
enum WeekDay{ sun, mon, tus, wed, thi, fri, sat};
int main()
{
enum WeekDay w;
w = sun;
for (int i = 0; i < 7; i++)
{
printf("w = %d\n",w + i);
}
return 0;
}
(2)类型定义与变量声明同时进行:
cpp
#include <stdio.h>
//跟第一个定义不同的是,此处的标号WeekDay省略,这是允许的。
enum { sun, mon, tus, wed, thi, fri, sat}w1 w2; //变量w1、w2的类型为枚举型enum WeekDay
int main()
{
w1 = mon;
w2 = fri;
printf("w1 = %d\n",w1);
printf("w2 = %d\n",w2);
return 0;
}
结合 switch
语句
cpp
switch (w1) {
case MON:
printf("周一\n");
break;
case TUE:
printf("周二\n");
break;
// ...
}
4. 枚举 vs 宏定义

编程示例
cpp
#include <stdio.h>
enum Colour { reb, blue, green, yello};
enum { sun, mon, tus, wed, thi, fri, sat}today;
int main()
{
enum Colour rgb = reb;
today = sun;
for (int i = 0; i < 4; i++)
{
printf("rgb = %d\n",rgb + i);
}
for (int i = 0; i < 7; i++)
{
printf("WeekDay = %d\n",today + i);
}
return 0;
}

typedef关键字
typedef
(Type Define)++用于为已有的数据类型定义一个新的名称(别名)++。它不创建新类型,而是提供一种更简洁、更具可读性的方式引用现有类型。
基本语法
cpp
typedef 原类型 新类型名;
-
原类型 :可以是基本类型(如
int
)、结构体、联合体、枚举或函数指针等。 -
新类型名:自定义的别名,遵循标识符命名规则。
typedef 用法
方式一:
cpp
#include <stdio.h>
struct Test {
int idata;
char cdata;
};
int main() {
typedef struct Test t; // 为 struct Test 重新定义别名为 t
t t1; // 使用别名 t 声明一个结构体变量 t1
t1.idata = 10; // 为结构体成员 idata 赋值
printf("%d\n", t1.idata); // 输出 idata 的值
return 0;
}
方式二:
cpp
#include <stdio.h>
typedef struct Test {
char cdata;
} t; // 直接在定义结构体时添加别名
int main() {
t t1; // 使用别名 t ,定义一个结构体变量为t1
t1.cdata = 'c';// 初始化成员变量
printf("%c\n", t1.cdata);
return 0;
}
方式三:
cpp
#include <stdio.h>
typedef struct { // 与方式二 不同之处Test可以省略
char cdata;
} t; // 直接在定义结构体时添加别名
int main() {
t t1; // 使用别名 t ,定义一个结构体变量为t1
t1.cdata = 'c';// 初始化成员变量
printf("%c\n", t1.cdata);
return 0;
}
方式四(指针):
cpp
#include <stdio.h>
typedef struct people
{
int num;
char name[32];
int age;
}People, *pPeople; // People 是 struct people 结构体类型
// pPeople 是 struct people* 结构体指针类型
void printfStruct(pPeople p)
{
printf("编号:%d,姓名:%s,年龄:%d\n", p->num, p->name, p->age);
}
int main()
{
People p1 = {39,"小薇",21};
People p2 = {23,"诗诗",20};
pPeople p3 = &p1; // pPeople 等价于 struct people* pPeople;
pPeople p4 = &p2;
printf("结构体指针变量\n");
printfStruct2(p3);
printfStruct2(p4);
return 0;
}
