- 结构体
- 概述
- 结构体变量的定义和初始化
- 结构体数组
- 结构体排序
- 开辟堆空间存储结构体
- 结构体嵌套结构体
- 结构体赋值
- 结构体和指针
- 结构体和函数
- const修饰结构体指针
- 联合体
- 枚举
- typedef关键字
- 数组:描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算。
- 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐,数据不便于管理。
- C语言中给出了另一种构造数据类型——结构体。
定义结构体变量的方式:
- 先声明结构体类型再定义变量名
- 在声明类型的同时定义变量
- 直接定义结构体类型变量(无类型名)
示例:
方法一:
#include//struct 结构体名 //{ // //结构体成员列表 // 姓名 // 年龄 //}; struct student { char name[21]; int age; int score; char addr[51]; }; int main() { //创建及结构体变量 //方法1:比较慢 //struct student stu;//结构体类型 结构体变量 //strcpy(stu.name,"张三");//结构体对字符串数组进行赋值时,采用拷贝赋值。//数组名为常量,所以对其赋值的时候不能使用=,得使用strcpy //stu.age = 18; //stu.score = 100; //strcpy(stu.addr, "山西省太原市"); //方法2 struct student stu = { "张三",18,100,"山西省太原市" }; printf("姓名:%sn", stu.name); printf("年龄:%dn", stu.age); printf("成绩:%dn", stu.score); printf("地址:%sn", stu.addr); return 0; }
方法二:
#includestruct student { char name[21]; int age; int score; char addr[51]; }stu= { "张三",18,100,"山西省太原市" }; int main0202() { printf("姓名:%sn", stu.name); printf("年龄:%dn", stu.age); printf("成绩:%dn", stu.score); printf("地址:%sn", stu.addr); return 0; } int main(void) { struct student stu; //键盘获取值 scanf("%s%d%d%s", stu.name, &stu.age, &stu.score, stu.addr);//数组名为首地址,所以不用加& printf("姓名:%sn", stu.name); printf("年龄:%dn", stu.age); printf("成绩:%dn", stu.score); printf("地址:%sn", stu.addr); return 0; }
结果:
姓名:张三 年龄:18 成绩:100 地址:山西省太原市结构体数组
示例:
头文件.h——struct.h
#pragma once
struct student
{
char name[21];
int age;
char sex;
int score[3];
char addr[51];
};
主函数调用头文件
#include#include "struct.h"//头文件调用 int main(void) { struct student stu[3]= { {"马笑笑",19,'M',90,100,98,"河南郑州"}, {"丁心",18,'F',97,96,99,"重庆"}, {"刘光头",15,'M',97,96,98,"重庆"} }; printf("结构体大小:%dn", sizeof(stu)); printf("结构体元素大小:%dn", sizeof(stu[0]));结构体成员需要偏移对齐 printf("结构体元素个数:%dn", sizeof(stu) / sizeof(stu[0])); for (int i = 0; i < 3; i++) { printf("姓名:%sn", stu[i].name); printf("年龄:%dn", stu[i].age); printf("性别:%sn", stu[i].sex == 'M' ? "男" : "女"); printf("成绩1:%dn", stu[i].score[0]); printf("成绩2:%dn", stu[i].score[1]); printf("成绩3:%dn", stu[i].score[2]); printf("地址:%sn", stu[i].addr); printf("n"); } return 0; }
结果:
结构体大小:288 结构体元素大小:96 结构体元素个数:3 姓名:马笑笑 年龄:19 性别:男 成绩1:90 成绩2:100 成绩3:98 地址:河南郑州 姓名:丁心 年龄:18 性别:女 成绩1:97 成绩2:96 成绩3:99 地址:重庆 姓名:刘光头 年龄:15 性别:男 成绩1:97 成绩2:96 成绩3:98 地址:重庆结构体排序
#include#include "struct.h"//头文件调用 //结构体排序 int main(void) { struct student stu[3] = { {"马笑笑",19,'M',90,100,98,"河南郑州"}, {"丁心",18,'F',97,96,99,"重庆"}, {"刘光头",15,'M',97,96,98,"重庆"} }; for (int i = 0; i < 3 - 1; i++) { for (int j = 0; j < 3 - i - 1; j++) { if (stu[j].age > stu[j + 1].age) { struct student temp=stu[j]; stu[j] = stu[j + 1]; stu[j + 1] = temp; } } } for (int i = 0; i < 3; i++) { printf("姓名:%sn", stu[i].name); printf("年龄:%dn", stu[i].age); printf("性别:%sn", stu[i].sex == 'M' ? "男" : "女"); printf("成绩1:%dn", stu[i].score[0]); printf("成绩2:%dn", stu[i].score[1]); printf("成绩3:%dn", stu[i].score[2]); printf("地址:%sn", stu[i].addr); printf("n"); } return 0; }
结果:
姓名:刘光头 年龄:15 性别:男 成绩1:97 成绩2:96 成绩3:98 地址:重庆 姓名:丁心 年龄:18 性别:女 成绩1:97 成绩2:96 成绩3:99 地址:重庆 姓名:马笑笑 年龄:19 性别:男 成绩1:90 成绩2:100 成绩3:98 地址:河南郑州开辟堆空间存储结构体
示例:
#define _CRT_SECURE_NO_WARNINGS #include#include "struct.h" typedef struct student ss;//起个别名 int main(void) { //printf("%d", sizeof(struct student));//结构体元素大小 ss* p = (ss*)malloc(sizeof(ss) * 3);//开辟堆空间 for (int i = 0; i < 3; i++) { scanf("%s%d,%c%d%d%d%s",p[i].name,&p[i].age,&p[i].sex, &p[i].score[0],&p[i].score[1],&p[i].score[2],p[i].addr);//因为有字符"%d%c",所以加逗号区分前面的int类型和字符类型 } for (int i = 0; i < 3; i++) { printf("姓名:%sn", p[i].name); printf("年龄:%dn", p[i].age); printf("性别:%sn", p[i].sex == 'M' ? "男" : "女"); printf("成绩1:%dn", p[i].score[0]); printf("成绩2:%dn", p[i].score[1]); printf("成绩3:%dn", p[i].score[2]); printf("地址:%sn", p[i].addr); printf("n"); } free(p); return 0; }
结果:
哈哈 18,M 89 98 99 山西 嘻嘻 19,M 99 80 98 陕西 嘿嘿 17,M 78 85 86 山东 姓名:哈哈 年龄:18 性别:男 成绩1:89 成绩2:98 成绩3:99 地址:山西 姓名:嘻嘻 年龄:19 性别:男 成绩1:99 成绩2:80 成绩3:98 地址:陕西 姓名:嘿嘿 年龄:17 性别:男 成绩1:78 成绩2:85 成绩3:86 地址:山东结构体嵌套结构体
示例:
建立头文件.h文件
struct1.h
#pragma once
struct scores
{
int cl;//c语言
int cpp;//c++
int cs;//c#
};
struct student
{
char name[21];
int age;
struct scores ss;
char addr[51];
};
主程序调用
#include#include "struct1.h" int main(void) { struct student stu = { "哈喽",20,98,99,100,"陕西宝鸡" }; printf("%sn%dn%dn%dn%dn%s", stu.name, stu.age, stu.ss.cl, stu.ss.cpp, stu.ss.cs, stu.addr); return 0; }
结果:
哈喽 20 98 99 100 陕西宝鸡结构体赋值
示例:
#include#include"struct.h" int main() { struct student stu = { "呼呼",19,89,98,100,"太原" }; struct student s1 = stu; //数组名为常量,所以对其赋值的时候不能使用=,得使用strcpy strcpy(s1.name, "呵呵");//对s1的修改不影响stu 虽然复制 但两者为独立的空间 printf("%s", stu.name); return 0; }
结果:
呼呼结构体和指针
结构体成员为指针类型
示例:
#includestruct student//结构体成员为指针类型 { char* name; int age; int* scores; char* addr; }; int main() { struct student stu; //开辟堆空间 stu.name = (char*)malloc(sizeof(char) * 21);//为指针时,需要开辟堆空间再进行赋值,否则值为常量将不能被改变 stu.scores = (int*)malloc(sizeof(int) * 3); stu.addr = (char*)malloc(sizeof(char) * 51); //结构体赋值 strcpy(stu.name, "李四"); stu.age = 18; stu.scores[0] = 89; stu.scores[1] = 89; stu.scores[2] = 89; strcpy(stu.addr, "重庆"); //打印 printf("%sn", stu.name); printf("%dn", stu.age); printf("%dn", stu.scores[0]); printf("%dn", stu.scores[1]); printf("%dn", stu.scores[2]); printf("%sn", stu.addr); //释放堆空间 free(stu.name); free(stu.scores); free(stu.addr); return 0; }
结果:
李四 18 89 89 89 重庆
结构体指针
#includeint main() { struct student1 ss = { "王五",17,84,85,86,"四川" }; struct student1* p = &ss; //== //结构体指针->成员 //结构体变量.成员 printf("%sn", p->name); printf("%Dn", p->scores[0]); return 0; }
结果:
王五 84
结构体整合(类似于开辟二级指针开辟的堆空间)
#define _CRT_SECURE_NO_WARNINGS #includetypedef struct student ss; struct student { char* name; int age; int* scores; char* addr; }; int main() { ss* p = (ss*)malloc(sizeof(ss) * 3); for (int i = 0; i < 3; i++) { p[i].name= (char*)malloc(sizeof(char) * 21); p[i].scores = (int*)malloc(sizeof(int) * 3); p[i].addr = (char*)malloc(sizeof(char) * 51); } for (int i = 0; i < 3; i++) { scanf("%s%d%d%d%d%s", p[i].name, &p[i].age, &p[i].scores[0], &p[i].scores[1], &p[i].scores[2], p[i].addr); } for (int i = 0; i < 3; i++) { printf("%s %d %d %d %d %sn", p[i].name, p[i].age, (p+i)->scores[0], (p+i)->scores[1], p[i].scores[2], p[i].addr); } //释放 for (int i = 0; i < 3; i++) { free(p[i].name); free(p[i].scores); free(p[i].addr); } free(p); return 0; }
结果:
哈哈 18 98 99 96 北京 呵呵 17 95 96 97 上海 嘻嘻 16 89 86 87 广州 哈哈 18 98 99 96 北京 呵呵 17 95 96 97 上海 嘻嘻 16 89 86 87 广州结构体和函数
示例:
# include#include "struct.h" typedef struct studennt ss; void fun01(struct student stu1) { strcpy(stu1.name, "嘿嘿");//改变形参 printf("%sn", stu1.name); } //若结构体成员为指针,则需要再主函数中开辟堆空间,若其他函数中的形参没开辟堆空间 //则,对形参的改变将影响实参。 int main0901() { struct student stu = { "哈哈",18,89,79,99,"天津" }; fun01(stu); printf("%s", stu.name);//形参不会改变实参 return 0; } void fun02(struct student* p) { strcpy(p->name, "你好");//地址传递,可以改变实参 printf("%sn", p->name); } int main0902() //结构体指针作为函数参数 { struct student stu = { "嘻嘻",19,98,79,97,"南京" }; fun02(&stu); printf("%s", stu.name); return 0; } //数组作为函数参数退化为指针 丢失元素精度,需要传递个数 void BUbbleSort(struct student* stu, int len) { for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - i - 1; j++) { if (stu[j].age > stu[j + 1].age) { struct student temp = stu[j]; stu[j] = stu[j + 1]; stu[j + 1] = temp; } } printf("n"); } } int main() { //结构体数组作为函数参数 struct student stu[3] = { {"张三",20,89,89,89,"宝鸡"}, {"李四",18,87,87,87,"汉中"}, {"王五",21,85,85,85,"渭南"} }; BUbbleSort(stu, 3); for (int i = 0; i < 3; i++) { printf("姓名:%sn", stu[i].name); printf("年龄:%dn", stu[i].age); printf("成绩1:%dn", stu[i].score[0]); printf("成绩2:%dn", stu[i].score[1]); printf("成绩3:%dn", stu[i].score[2]); printf("地址:%sn", stu[i].addr); printf("n"); } return 0; }
结果0901:
嘿嘿 哈哈
结果0902:
你好 你好
结果0903:
姓名:李四 年龄:18 成绩1:87 成绩2:87 成绩3:8949408 地址: 姓名:张三 年龄:20 成绩1:89 成绩2:89 成绩3:8949400 地址: 姓名:王五 年龄:21 成绩1:85 成绩2:85 成绩3:8949416 地址:const修饰结构体指针
示例:
#include联合体typedef struct student ss; struct student { char name[21]; int age; char sex; int score[3]; char addr[51]; }; int main1001() { ss stu1 = { "陈一",22,99,99,99,"陈仓" }; ss stu2 = { "郭二",24,98,98,98,"天水" }; //const修饰结构体指针类型,可修改变量 不能修改指针指向内存空间的值 const ss* p = &stu1; //p = &stu2;//ok //(*p).age = 28;//err //p->age = 30;//err return 0; } int main1002() { ss stu1 = { "陈一",22,99,99,99,"陈仓" }; ss stu2 = { "郭二",24,98,98,98,"天水" }; //const 修饰结构体指针变量 ss* const p = &stu1; //strcpy(p->name, "孙叁");//ok //p = &stu2;//err //(*p).age = 28;//ok //p->age = 30;//ok return 0; } //const修饰结构体指针类型,const 修饰结构体指针变量 int main1001() { ss stu1 = { "陈一",22,99,99,99,"陈仓" }; ss stu2 = { "郭二",24,98,98,98,"天水" }; //const 修饰结构体指针变量 const ss* const p = &stu1; //p = &stu2;//err //(*p).age = 28;//err ss** pp = &p; //(*pp)->age = 45;//ok 指针指向内容用-> //*pp = &stu2;//ok //(**pp).age = 50;//ok 变量.向内容用. return 0 }
- 联合union是一个能在同一个存储空间存储不同类型数据的类型;
- 联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
- 共用体变量的地址和它的各成员的地址都是同一地址。
示例:
#includeunion Var //联合体里面类型的地址都是一样的 { int a; float b; double c; char d; short f; }; int main() { union Var var; var.a = 10; var.b = 3.14; printf("%dn",var.a); printf("%fn", var.b);//每一瞬时只有一种起作用,起作用的成员是最后一次存放的成员 printf("%dn", sizeof(var));//大小是按内存最大的类型来计算的 return 0; }
结果:
1078523331 3.140000 8枚举
- 枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
- 枚举用于流程控制比较多
- 枚举类型定义:
示例:
#define _CRT_SECURE_NO_WARNINGS #include//枚举用于流程控制比较多 enum TYPE { run,attack,skill,dance=10,showUI,frozen=20,dizz,dath,moti }type; //enum 交易 //{ // 插卡,读卡,锁卡,输入密码,查询,取款,退卡,取卡 //}; int main() { int value; while (1)//死循环 { scanf("%d", &value); switch (value)//switch括号中写type是展现出case类型,写value是对各个类型进行输出 { case run: printf("英雄正在移动中.....n"); break; case attack: printf("英雄正在攻击中.....n"); break; case skill: printf("英雄正在释放技能中.....n"); break; case dance: printf("英雄正在跳舞中.....n"); break; case showUI: printf("英雄正在显示徽章.....n"); break; case frozen: printf("英雄被冰冻中.....n"); break; case dizz: printf("英雄被眩晕中.....n"); break; case dath: printf("英雄死亡.....n"); return 0;//外层为死循环,break跳不出来,可以加goto break; case moti: printf("英雄等待释放命令.....n"); break; default: break; } } return 0; }
结果:
0 英雄正在移动中..... 1 英雄正在攻击中..... 10 英雄正在跳舞中.....typedef关键字
- 作用:起别名 定义函数指针
- typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。
- 与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值。
- #define发生在预处理,typedef发生在编译阶段。



