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

嵌入式学习---在 Linux 下的 C 语言学习 Day10

Day10

共用体(Union)

也称为联合体,和结构体类似,也是由多个相同或不同类型的数据成员构成的集合。但和结构体不同的是它的所有数据成员都是共享同一块内存空间,并没有独立的内存空间

共用体的用法和结构体是一样的,只是使用的是 union 关键字

共用体的总大小就是其数据成员

一定要弄清楚共用体的内存布局(即内存存储形态)

#include <stdio.h>// 声明共用体类型 demo,类型名是 demo
union demo
{int i;char c;short s;long l;char a[5];};typedef union
{float f;int i;short s;} money;int main()
{// 使用上面声明的共用体类型定义变量或常量union demo d1;money m1;printf("%lu\n",sizeof(d1));printf("%lu\n",sizeof(m1));d1.i = 6513249;printf("%c\n",d1.c); // aprintf("%hd\n",d1.s); // 25185printf("%s\n",d1.a); // abcd1.a[1] = 0;printf("%c\n",d1.c); // aprintf("%hd\n",d1.s); // 97printf("%s\n",d1.a); // aprintf("%d\n",d1.i); // 6488161d1.l = 0x10064636261;printf("%c\n",d1.c); // aprintf("%hd\n",d1.s); // 25185printf("%s\n",d1.a); // abcdprintf("%x\n",d1.i); // 64636261union demo* p1 = &d1;printf("%hd\n",p1->s);return 0;
}

在这里插入图片描述

枚举(Enum)

用来表示取值个数有限的数据,比如性别、学历等。

枚举类型其实就是整数类型,只不过使用一些更有意义的名字来代表整数值

枚举也是一种自定义数据类型,需要先声明再使用,使用 enum 关键字

枚举元素就是一个整数常量,在所有枚举类型中都不能同名

如果不显示指定枚举元素的具体值,第一个枚举元素的值就是 0,后面枚举元素的值就是它前面的枚举元素的值加一后的值,也支持我们显式指定枚举

enum sex_t
{// 枚举元素female,male,unknow
};typedef enum
{// 枚举元素dazhuan,benke,yanjiusheng,boshi
} degree;int main()
{// 使用上面声明的枚举类型定义变量或常量enum sex_t s1 = male;s1 = female;s1 = unknow;degree d1 = boshi;d1 = benke;if(s1 == male){printf("男\n");}else if(s1 ==female){printf("女\n");}else{printf("未知\n");}printf("%lu\n",sizeof(s1));printf("%lu\n",sizeof(d1));printf("%d\n",s1);printf("%d\n",d1);printf("%d\n",male);printf("%d\n",boshi);
}

内存管理(Memory Management)

每个进程都拥有自己独立的私有地址空间,每个进程的地址空间都分为下面的区域:

  • 栈区(Stack):可读可写,存放形参变量,非静态局部变量,函数返回地址等,系统自动管理分配和释放,我们无法干预。容量很小(通常只有若干 MB),不适合处理大量数据。
  • 静态存储区:可读可写,存放静态局部变量、全局变量等,容量大,系统自动管理分配和释放,我们无法干预。
  • 常量区:只读,存放常量数据,容量大,系统自动管理分配和释放,我们无法干预。
  • 堆区:可读可写,容量大,由我们自己管理分配和释放,很灵活,可以实现动态内存管理。
  • 代码段:只读,存放程序指令(即代码),系统自动管理,我们无法干预。

进程(Process):正在运行的程序

程序(Program):可执行文件,存放在硬盘、U 盘面等外存设备上

动态内存管理相关的标准 C 库函数:

  • malloc:在堆区申请一块指定大小的内存空间,未初始化
  • calloc:在堆区申请一块指定大小的内存空间,全部清零初始化
  • realloc:调整一块已分配的堆区空间的大小(会保留原来的数据,调大时会尽可能进行原地扩充内存,如果尾部没有足够的空闲空间,就会直接申请一块新的空间,将旧空间的所有数据都拷贝过去,然后释放掉旧空间)
  • free:释放一块已分配的堆区空间(PS:释放后就不要再访问那块堆区空间了)

malloc 函数如果执行成功,返回值为分配的堆区空间的首地址,否则返回 NULL。

calloc 函数内部首先调用 malloc 函数申请指定大小的空间

在各个进程频繁进行申请和释放后,堆区肯定会产生很多内存碎片

关于 main 函数返回值的意义,规范的做法是程序执行成功(正常结束)返回 0,执行失败(异常结束)返回非零值,操作系统或其他程序可以通过获取我们的程序的返回值判断是否执行成功

进程结束后,系统会释放它占用的所有资源,包括未释放的堆区空间等

#include <stdio.h>
#include <stdlib.h> // standard library
#include <string.h>struct book
{int isbn;char name[51];float price;
};int main()
{/*// 栈溢出(Stack Overflow)崩溃double nums[10000000] = {1,2,3,4};;nums[1000] = 3.14;printf("%g\n",nums[1000]);*/// 在堆区中申请空间存放10000000个浮点数double* nums= malloc(10000000 * sizeof(double));if(nums == NULL){printf("malloc fail\n");return 1;}int* i = malloc(sizeof(int));if(i == NULL){printf("malloc fail\n");return 1;}*i = 3;(*i)++;printf("%d\n",*i);free(i);nums[1000] = 3.14;printf("%g\n",nums[1000]);free(nums); // 释放 nums 指向的堆区空间/*// 堆区释放了就不要再访问了nums[3] = 5.8;printf("%g\n",nums[3]);*/// 单个堆区结构体struct book* b1 = malloc(sizeof(struct book));if(b1 == NULL){printf("malloc fail\n");return 1;}b1->isbn = 10001;strcpy(b1->name,"操作系统");b1->price = 35.5;//(*b1).price = 35.5;                  printf("%d %s %g\n",b1->isbn,b1->name,b1->price);free(b1);// 堆区结构体数组struct book* bs = malloc(1000 * sizeof(struct book));if(bs == NULL){printf("malloc fail\n");return 1;}bs[3].isbn = 10001;strcpy(bs[3].name,"操作系统");(bs + 3)->price = 35.5;printf("%d %s %g\n",bs[3].isbn,bs[3].name,(bs + 3)->price);free(bs);// calloc 的用法// calloc 函数内部实现/*int* datas = malloc(100 * sizeof(int));// 全部清零char* p = (char*)datas;for(int i = 0;i < 100 * sizeof(int);i++){p[i] = 0;}*/int* datas = calloc(100,sizeof(int));if(datas == NULL){;}printf("%d\n",datas[90]);datas[3] = 100;printf("%d\n",datas[3]);free(datas);// realloc 用法char* s = malloc(5);if(s == NULL){;}//s = "qzp"; // 会导致内存泄漏strcpy(s,"qzp");s = realloc(s, 10);printf("%s\n", s);strcat(s,"666");printf("%s\n",s);free(s); // 段错误return 0;
}

常用内存操作相关的标准 C 库函数(所有内存区域都可以使用):

  • memset:memory set,将一块内存空间中的每个字节都设置为指定的值
  • memcpy:memory copy,内存空间拷贝,源内存空间和目标内存空间不能存在重叠
  • memmove:memory move,内存空间拷贝,源内存空间和目标内存空间可以存在重叠
  • memcmp:memory compare,比较两块内存空间中的数据是否相同
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int i;memset(&i, 0, sizeof(int));printf("%d\n",i);memset(&i, 1, sizeof(int));printf("%d\n",i);memset(&i, 0xFF, sizeof(int));printf("%d\n",i);// 栈区数组int nums1[100] = {5,4,3,1,2};//memset(nums1, 0, sizeof(nums1)); // 数组清零// 堆区数组int* nums2 = malloc(100 * sizeof(int));memcpy(nums2,nums1,100 * sizeof(int));for(int i = 0;i < 5;i++){printf("%d ",nums2[i]);}printf("\n");free(nums2);char s[] = "hello,qzp";memmove(s,s + 2,7); // llo,qzpzp
//      memmove(s + 2,s,7); // hehello,qprintf("%s\n",s);return 0;
}
http://www.xdnf.cn/news/17362.html

相关文章:

  • 《C语言》指针练习题--2
  • Redisson中的分布式锁
  • uni-app vue3 小程序接入 aliyun-rtc-wx-sdk
  • Vscode Data Wrangler 数据查看和处理工具
  • 如何为WordPress启用LiteSpeed缓存
  • Linux 限制 root 登录 IP 地址的方法
  • Activiti 中各种 startProcessInstance 接口之间的区别
  • Java——详解形参实参方法的重载
  • .NET PDF处理组件IronPDF:如何通过 AI 简化开发人员处理 PDF的方式
  • platform总线简介和使用场景说明
  • 设计模式-装饰模式 Java
  • Web开发-JS应用WebPack构建打包Mode映射DevTool源码泄漏识别还原
  • [激光原理与应用-169]:测量仪器 - 能量型 - 光功率计(功率稳定性监测)
  • RepoCoder:仓库级代码补全的迭代检索生成框架解析与应用前沿
  • 基于Python+Vue+Mysql实现(物联网)智能大棚
  • 【tips】css模仿矢量图透明背景
  • Vue 3 入门教程 9 - 表单处理
  • change和watch
  • 酉矩阵(Unitary Matrix)和随机矩阵
  • 拥抱云原生:从传统架构到云原生架构的演进与实践
  • 慢SQL优化实战:从一例线上慢SQL探究执行引擎工作过程
  • 如何快速掌握Excel公式?14天轻松通关
  • 大疆前端笔试题目详解
  • Kafka数据生产和发送
  • 如何将Dubbo从Zookeeper平滑地迁移到Nacos?
  • OpenCV图像处理入门实战指南
  • 【办公自动化】使用Python来自动化处理Excel表格有哪些方法?
  • 急危重症专科智能体”构建新一代急诊、手术与重症中心的AI医疗方向探析
  • 图像认知与OpenCV——图像预处理4
  • Java并发与数据库锁机制:悲观锁、乐观锁、隐式锁与显式锁