当前位置: 首页 > news >正文

C语言的复合类型、内存管理、综合案例

day05:复合类型、内存管理、综合案例

文章目录

  • day05:复合类型、内存管理、综合案例
    • 一、复合类型(自定义类型)
      • 1.1 共用体(联合体)
      • 1.2 枚举
      • 1.3 typedef
    • 二、内存管理
      • 2.1 C代码编译过程(了解)
      • 2.2 进程的内存分布
      • 2.3 堆区内存的使用
      • 2.4 内存分布代码分析
        • 2.4.1 返回栈区地址
          • 2.4.1.1 返回data区地址
          • 2.4.1.1 普通和静态局部变量区别
        • 2.4.2 返回堆区地址
    • 三、学生信息管理系统

一、复合类型(自定义类型)

1.1 共用体(联合体)

共用体和结构体区别

特性结构体 (struct)共用体 (union)
存储方式各成员顺序存储,拥有独立的内存空间。所有成员共享同一块起始内存空间。
内存占用所有成员大小之和(需考虑内存对齐)。最大成员的大小。
成员访问所有成员同时有效,可随时访问,互不影响。同一时间只有一个成员有效,对一个成员赋值会覆盖其他成员。
#include <stdio.h>
#include<stdint.h>// 定义一个联合体(共同体)Text,它包含三个不同类型的成员
// 联合体的特点是所有成员共享同一块内存空间,其大小等于最大成员的大小
union Text{uint8_t a;  // 1字节uint16_t b; // 2字节uint32_t c; // 4字节// 因此,这个联合体的大小为4字节(即uint32_t的大小)
};int main() {union Text text; // 声明一个联合体变量text// 打印各个成员的地址,可以看到它们的地址是相同的// 这是因为联合体的所有成员都共享同一块内存空间printf("%p, %p, %p \n",&text.a,&text.b,&text.c);//地址// 打印联合体变量的大小,结果为4字节(最大成员uint32_t的大小)printf("%d\n",sizeof(text));//最大的数据类型就是共用体的内存// 给成员c赋值,由于联合体共享内存,这个值会同时影响其他成员text.c = 0x44332211;//赋值// 打印各个成员的值:// text.a 只能访问最低的1字节,即0x11// text.b 只能访问最低的2字节,即0x2211// text.c 访问全部4字节,即0x44332211printf("%#x %#x %#x\n",text.a,text.b,text.c);return 0;
}

1.2 枚举

  • 枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内

  • 语法格式:

    enum  枚举名 { 枚举值表 };
    
  • 在枚举值表中应列出所有可用值,也称为枚举元素

    • 枚举值是常量,不能在程序中用赋值语句再对它赋值
    • 枚举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …
#include <stdio.h>// 定义一个枚举类型color,用于表示颜色
// 枚举类型是一种用户定义的数据类型,它包含一组命名的整型常量
// 默认情况下,第一个枚举值被赋值为0,后续的枚举值依次递增1
enum color{// red 被赋值为 0red,// whild 被赋值为 1whild,// green 被赋值为 2green,// blue 被赋值为 3blue,// yellow 被赋值为 4yellow
};int main() {// 打印red的值,应该是0printf("red  = %d\n",red);// 使用switch语句根据枚举值进行分支处理// 这里使用blue作为switch的条件,blue的值为3switch (blue){// 当值为red(0)时,打印"红色"case red:printf("红色\n");break;// 当值为green(2)时,打印"绿色"case green:printf("绿色\n");break;// 当值为blue(3)时,打印"蓝色"case blue:printf("蓝色\n");break;// 默认情况,当没有匹配的case时执行default:break;}return 0;
}

1.3 typedef

  • typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。
#include <stdio.h>// 使用typedef为unsigned char类型起一个别名u8
// 这样在后续代码中可以直接使用u8来声明变量,提高代码可读性
typedef unsigned char u8;// 使用typedef为枚举类型起别名
// 定义一个枚举类型color,包含几种颜色
// 然后使用typedef将enum color命名为color,这样可以直接使用color来声明变量
typedef enum color{// 默认从0开始计数red,      // red = 0whild,    // whild = 1green,    // green = 2blue,     // blue = 3yellow    // yellow = 4
}color; // 为enum color起别名color// 使用typedef为结构体类型起别名
// 定义一个Student结构体,包含姓名、年龄和性别
// 然后使用typedef将struct Student命名为Student,并将struct Student*命名为PStudent
typedef struct Student{char name[20];  // 姓名,字符数组int age ;       // 年龄,整型char sex ;      // 性别,字符型
}Student,*PStudent;  // Student是struct Student的别名,PStudent是struct Student*的别名int main() {// 使用u8别名声明变量au8 a;// 使用Student别名声明结构体变量sStudent s;// 使用PStudent别名声明结构体指针变量p,并指向sPStudent p = &s;// 通过指针p修改结构体s的age成员p->age = 12;// 打印结构体s的age成员,验证修改成功printf("%d\n",s.age);// 使用color别名声明枚举变量ccolor c;return 0;
}

二、内存管理

2.1 C代码编译过程(了解)

  • 预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法

  • 编译:检查语法,将预处理后文件编译生成汇编文件

  • 汇编:将汇编文件生成目标文件(二进制文件)

  • 链接:将目标文件链接为可执行程序

2.2 进程的内存分布

  • 程序运行起来(没有结束前)就是一个进程

    • windows打开任务管理器:ctrl+shfit+esc
  • 程序内存区域划分

    内存区域存储内容生存周期管理方式主要特点
    代码区 (Text)程序的可执行代码整个程序运行期间系统只读
    数据区 (Data)已初始化的全局/静态变量、常量整个程序运行期间系统程序结束时释放
    BSS区 (BSS)未初始化的全局/静态变量整个程序运行期间系统程序启动时清零
    栈区 (Stack)函数参数、局部变量、返回值函数调用期间编译器自动管理先进后出 (FILO),空间有限
    堆区 (Heap)动态分配的内存 (malloc)从分配到释放程序员手动管理空间较大,需手动释放,易产生碎片

2.3 堆区内存的使用

  • 手动管理
  • 不手动释放,如果程序没有结束,堆区内存一直在

2.4 内存分布代码分析

2.4.1 返回栈区地址
#include <stdio.h>int *func() {int a = 10;return &a; // 函数调用完毕,因为a是局部变量,a释放
}int main() {int *p = NULL;p = func();*p = 100; // 操作野指针指向的内存,errprintf("11111111111111111\n"); // 这句话可能执行不到,因为上一句话报错return 0;
}
2.4.1.1 返回data区地址
  • 在函数内部使用static修饰的变量称为静态局部变量
  • 它在程序运行期间只被初始化一次,并且在函数调用结束后也不会被销毁
#include <stdio.h>int *func() {// 静态局部变量,只会初始化一次static int a = 10;return &a; // 函数调用完毕,a不释放
}int main() {int *p = NULL;p = func();*p = 100; // okprintf("*p = %d\n", *p);return 0;
}
2.4.1.1 普通和静态局部变量区别
特性普通局部变量静态局部变量
存储位置栈区 (Stack)静态数据区 (Data/BSS)
生命周期函数调用开始 到 函数返回结束整个程序运行期间
作用域仅限于声明所在的 函数内部仅限于声明所在的 函数内部
初始化时机每次 进入函数时都初始化仅在第一次 进入函数时初始化一次
默认初始值不确定值(随机值)0 或 NULL(由系统自动初始化)
函数调用间的值每次调用都是新的,不保留 上次的值会保留 上次调用结束时的值
#include <stdio.h>void normal_func() {int i = 0;i++;printf("局部变量 i = %d\n", i);
}void static_func() {static int j = 0;j++;printf("static局部变量 j = %d\n", j);
}int main() {// 调用3次normal_func()normal_func();normal_func();normal_func();// 调用3次static_func()static_func();static_func();static_func();return 0;
}
2.4.2 返回堆区地址
#include <stdio.h>
#include <stdlib.h>int *func() {int *tmp = NULL;// 堆区申请空间tmp = (int *)malloc(sizeof(int));*tmp = 100;return tmp; // 返回堆区地址,函数调用完毕,不释放
}int main() {int *p = NULL;p = func();printf("*p = %d\n", *p); // ok// 堆区空间,使用完毕,手动释放if (p != NULL) {free(p);p = NULL;}return 0;
}

三、学生信息管理系统

  • 函数版学生信息管理系统
#include <stdio.h>
#include<string.h>// 定义学生数组的最大容量
#define MAX 50// 定义学生结构体,包含姓名、年龄和性别
// 使用typedef为struct {...}起别名为Student
typedef struct {char name[20];  // 学生姓名,最多19个字符+1个结束符int age ;       // 学生年龄char sex ;      // 学生性别,'m'表示男性,'f'表示女性
}Student;// 初始化学生数组,包含4个学生的信息
Student stu [MAX]={{"feifei",18,'m'},{"mamafei",18,'f'},{"koko",18,'f'},{"yoyo",20,'f'},
};// 当前学生数组中的实际学生数量
int len = 4 ;// 根据姓名查找学生在数组中的索引
// 参数:p - 要查找的学生姓名
// 返回值:找到则返回索引,未找到返回-1
int find_by_name(char * p){for (int i = 0; i < len; i++){// 使用strcmp比较字符串,相等时返回0if(strcmp(stu[i].name,p)==0){return i ; }}return -1;
}// 显示帮助菜单
void help_menu() {printf("\n");printf("     欢迎使用本学生信息管理系统\n");printf("* ================================ *\n");printf("* 1. 添加                          *\n");printf("* 2. 显示                          *\n");printf("* 3. 查询                          *\n");printf("* 4. 修改                          *\n");printf("* 5. 删除                          *\n");printf("* 6. 退出                          *\n");printf("* ================================ *\n");
}// 添加学生信息
void add(){printf("增加第%d的学生的信息\n",len+1);printf("请输入新的学生的名字\n");scanf("%s",stu[len].name);  // 读取学生姓名printf("请输入新的学生的年龄\n");scanf("%d",&stu[len].age);  // 读取学生年龄printf("请输入新的学生的性别(m,f)\n");scanf(" %c",&stu[len].sex); // 读取学生性别,注意前面有空格用于跳过换行符len++;  // 学生数量增加printf("添加成功\n");}// 显示所有学生信息
void show(){printf("姓名\t年龄\t性别\n");for (int i = 0; i < len; i++){printf("%s\t%d\t%c\n",stu[i].name,stu[i].age,stu[i].sex);}// 检查是否达到最大容量if (len >MAX){printf("不能在存储数据了\n");}}// 查询单个学生信息
void show_one(){char tamp [20];  // 临时存储输入的姓名printf("请输入姓名\n");scanf("%s",tamp);int sop = find_by_name(tamp);  // 查找学生if (sop != -1){// 如果找到学生,显示其信息printf("%s\t%d\t%c\n",stu[sop].name,stu[sop].age,stu[sop].sex);}else{printf("请重新输入名字,%s不存在\n",tamp);}}// 修改学生信息
void updata(){char tamp [20];  // 临时存储输入的姓名printf("请输入姓名\n");scanf("%s",tamp);int sop = find_by_name(tamp);  // 查找学生if (sop != -1){//printf("%s\t%d\t%c\n",stu[sop].name,stu[sop].age,stu[sop].sex);//=======================修改printf("请输入需要修改学生的名字\n");scanf("%s",stu[sop].name);  // 修改姓名printf("请输入学生修改后的年龄\n");scanf("%d",&stu[sop].age);  // 修改年龄printf("请输入新的学生修改后的性别(m,f)\n");scanf(" %c",&stu[sop].sex); // 修改性别printf("修改成功\n");}else{printf("请重新输入名字,%s不存在\n",tamp);}}// 删除学生信息
void delete(){char tamp [20];  // 临时存储输入的姓名printf("请输入姓名\n");scanf("%s",tamp);int sop = find_by_name(tamp);  // 查找学生if (sop != -1){//=======================删除// 将数组最后一个元素复制到要删除的位置,然后减少数组长度// 这是一种简单的删除方法,避免了数组元素的移动stu[sop] = stu[len-1];len--;printf("%s删除成功\n",tamp);}else{printf("请重新输入名字,%s不存在\n",tamp);}}// 主函数,程序入口
int main() {// 无限循环,直到用户选择退出while (1){help_menu();  // 显示菜单int a ;printf("请输入一个数字 ");scanf("%d",&a);  // 读取用户选择// 根据用户输入执行相应操作if (a==1){printf("添加\n");add();  // 添加学生}else if (a==2){printf("显示\n");show();  // 显示所有学生}else if (a==3){printf("查询\n");show_one();  // 查询单个学生}else if (a==4){printf("修改\n");updata();  // 修改学生信息}else if (a==5){printf("删除\n");delete();  // 删除学生信息}else if (a==6){printf("退出\n");break;  // 退出循环,结束程序}else{printf("请重新输入数字\n");  // 输入无效时提示}
} return 0;
}
http://www.dtcms.com/a/309559.html

相关文章:

  • 【AI 加持下的 Python 编程实战 2_12】第九章:繁琐任务的自动化(上)——自动清理电子邮件文本
  • PendingIntent相关流程解析
  • MySQL——事务详解
  • React Refs:直接操作DOM的终极指南
  • RAGFlow Agent 知识检索节点源码解析:从粗排到精排的完整流程
  • Java学习第九十六部分——Eureka
  • Elasticsearch IK 中文分词器指南:从安装、配置到自定义词典
  • IPAM如何帮助企业解决IP冲突、识别未经授权设备并管理子网混乱
  • MAC 升级 Ruby 到 3.2.0 或更高版本
  • ARM Cortex-M 处理器的应用
  • Smart Launcher:安卓设备上的智能启动器
  • ElasticSearch Linux 下安装及 Head 插件 | 详情
  • 设计Mock CUDA库的流程与实现
  • 【秋招笔试】07.27文远知行-第一题
  • Git 实现原理剖析
  • Boost.Asio学习(5):c++的协程
  • Python Flask框架Web应用开发完全教程
  • 后台管理系统权限管理:前端实现详解
  • 关于WIKI的一些使用技巧
  • windows系统安装文生图大模型Stable diffusion V3.5 large(完整详细可用教程)
  • 20250801在Ubuntu24.04.2LTS下编译firefly_itx_3588j的Android12时解决boot.img过大的问题
  • 李宏毅深度学习教程 第4-5章 CNN卷积神经网络+RNN循环神经网络
  • 基于SpringBoot+MyBatis+MySQL+VUE实现的经方药食两用服务平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)
  • 【科普】进程与线程的区别
  • 电商前端Nginx访问日志收集分析实战
  • 机器学习【三】SVM
  • 无人机避让路径规划模块运行方式
  • uniapp无线(WIFI)运行调试APP(真机)
  • C++继承中虚函数调用时机问题及解决方案
  • 无人机模式的切换