文章目录
- 1 . 概要
- 2 . C语言语法
- 2.1 关键字解释、
- 3 . C语言运算符优先级
- 4 . 本质理解
- 4.1 内存的本质:数字世界的生命与轮回
- 4.2 语法的本质:掌控数字宇宙的至高功法
- 5 . 语法应用
- 5.1 简单示例
- 5.2 指针:时空操控的灵魂之术
- 5.2.1 跨越维度的力量
- 5.2.2 为何需要指针?时空效率的双重突破
- 5.2.3 指针的本质:坐标与存在
- 5.2.4 时空穿梭:*运算符
- 5.2.5 灵魂出窍:隔空取物的高阶功法
- 灵魂出窍的哲学意义
- 5.2.6 指针功法
- 指针不是C语言的难点,而是精髓所在。初学者看到的可能是语法复杂性,但修行者看到的是时空操控的自由度。当你掌握了指针,你就掌握了:
- 5.3 C语言结构体:数据封装与内存管理的艺术
- 引言:为什么需要结构体?
- 5.3.1 结构体与数组的本质区别
- 内存布局对比
- 关键差异:
- 5.3.2 结构体的专业应用
- 1. 数据封装与抽象
- 2. 内存映射与硬件寄存器访问
- 3. 灵活的内存操作
- 5.3.3结构体的高级特性
- 1. 内存对齐与优化
- 2. 位域:紧凑存储布尔标志
- 3. 结构体嵌套与数据层次
- 5.3.4 工程实践建议
- 1. 命名规范
- 2. 封装与访问控制
- 3. 错误处理模式
- 5.3.5 结论
- 6 . 编译过程
- 6.1 预处理
- 6.2 编译
- 6.3 汇编
- 6.4 链接
- 7 . 小结
【全文大纲】 : https://blog.csdn.net/Engineer_LU/article/details/127486388
1 . 概要
- 深入浅出C语言,以下内容简洁,适合入门与加深理解
2 . C语言语法
| auto | break | case | char | const | continue | default | do |
|---|---|---|---|---|---|---|---|
| double | else | enum | extern | float | for | goto | if |
| int | long | register | return | short | signed | sizeof | static |
| struct | switch | typedef | union | unsigned | void | volatile | while |
2.1 关键字解释、
- auto :声明自动变量
- break:跳出当前循环
- case:开关语句分支
- char :声明字符型变量或函数返回值类型
- const :声明只读变量
- continue:结束当前循环,开始下一轮循环
- default:开关语句中的“默认”分支
- do :循环语句的循环体
- double :声明双精度浮点型变量或函数返回值类型
- else :条件语句否定分支(与 if 连用)
- enum :声明枚举类型
- extern:声明变量或函数是在其它文件或本文件的其他位置定义
- float:声明浮点型变量或函数返回值类型
- for:一种循环语句
- goto:无条件跳转语句
- if: 条件语句
- int: 声明整型变量或函数
- long :声明长整型变量或函数返回值类型
- register:声明寄存器变量
- return :子程序返回语句(可以带参数,也可不带参数)
- short :声明短整型变量或函数
- signed:声明有符号类型变量或函数
- sizeof:计算数据类型或变量长度(即所占字节数)
- stati` :声明静态变量
- struct:声明结构体类型
- switch :用于开关语句
- typedef:用以给数据类型取别名
- unsigned:声明无符号类型变量或函数
- union:声明共用体类型
- void :声明函数无返回值或无参数,声明无类型指针
- volatile:说明变量在程序执行中可被隐含地改变
- while :循环语句的循环条件
3 . C语言运算符优先级
4 . 本质理解
4.1 内存的本质:数字世界的生命与轮回
- 在C语言的世界里,学习语法之前,我们首先要理解一个最基础也最重要的概念——内存。
- 想象我们身处的自然界:地球上有70多亿人,每个人都是独特的生命单元。我们从自然中诞生——吸收阳光、空气、水,从自然获取物质构成身体;我们最终又回归自然——生命结束后,身体分解,元素重新进入大自然的循环。
- 每一块内存单元都像是数字宇宙中的基本粒子
- 当我们声明一个变量时,我们是在向操作系统申请一小块“自然空间”——这是内存的诞生
- 当我们为变量赋值时,我们是在用数据为这块空间赋予“生命的意义”
- 当变量超出作用域或被显式释放时,内存回归系统的“自然状态”——这是内存的回归
4.2 语法的本质:掌控数字宇宙的至高功法
- 理解了内存的本质,我们站在了数字宇宙的边缘。但仅凭领悟无法成为真正的主宰者——就像理解海洋的广阔,不等于能够驾驭惊涛骇浪。认知与掌控之间,隔着一条名为“功法”的鸿沟。
- 只有理解规律,才能驾驭万物。只有掌握方法,才能塑造世界。于是,他们倾尽智慧,创出了这部数字世界的至高功法——《C语言》。这不仅仅是一门编程语言,而是一整套掌控内存、塑造现实的道法术器。
5 . 语法应用
5.1 简单示例
intmain(intargc,char*argv[]){unsignedcharvalue=0x01;if(value==0x01){;}return0;}语法极其简洁,可能有读者不太了解 “ int argc,char* argv[] ”:这里的内容意义,由于C语言是面向过程,系统级,当系统调用一个程序时,可以执行初期给定输入数据,其中前者是数据数量,后者是具体数据
5.2 指针:时空操控的灵魂之术
5.2.1 跨越维度的力量
在C语言的修行之路上,指针是让无数初学者望而生畏,却又让大师们如鱼得水的核心功法。它不是阻碍,而是通往内存主宰之境的必经之门。当我们谈论指针时,我们谈论的是一种跨越时空的操控能力。
5.2.2 为何需要指针?时空效率的双重突破
在数字宇宙中,每一次变量操作都是在时空中留下痕迹。直接操作变量如同每次都要亲临现场:
// 传统方式:亲力亲为intdata=100;// 创造data=data*2;// 修改intbackup=data;// 复制(消耗额外时空)这种方式的局限显而易见:
- 时间消耗:大量数据复制需要CPU周期
- 空间消耗:每个副本占用额外内存
- 效率瓶颈:难以跨越函数边界传递大型数据
指针的出现,是时空操控的一次革命性突破。
5.2.3 指针的本质:坐标与存在
intreality=42;// 在某个坐标创造存在printf("存在本身: %d\n",reality);// 42printf("存在坐标: %p\n",&reality);// 0x7ffeebd39a9c// &reality 不是值,而是"这里"的坐标// 就像"北京市朝阳区建国门外大街1号"不是建筑本身,而是位置#### 坐标系统:&运算符 每个变量在数字宇宙中都有一个绝对坐标——内存地址。&运算符就是获取这个坐标的钥匙:5.2.4 时空穿梭:*运算符
拥有坐标后,我们可以跨越时空直接操控。*运算符就是时空穿梭之门:
int*observer=&reality;// 获得坐标,成为观察者printf("坐标指向的存在: %d\n",*observer);// 42*observer=2024;// 无需亲自到场,直接修改printf("修改后的存在: %d\n",reality);// 20245.2.5 灵魂出窍:隔空取物的高阶功法
让我们通过一个精妙的示例,体会指针带来的效率革命:
intmain(intargc,char*argv[]){u8*consciousness=NULL;// 意识体,暂时不依附任何存在u8 body=1;// 肉身,初始状态为1consciousness=&body;// 意识出窍,附身于肉身*consciousness=2;// 隔空操控,直接修改肉身状态// 传统方式需要这样检查if(body==1){// 此时body已经是2,无需进入body=2;// 这段代码永远不会执行}return0;}灵魂出窍的哲学意义
想象一下,你有一个强大的意识体(指针):
- 没有指针:你需要走到每个人面前,亲口告诉他们该做什么
- 拥有指针:你的意识可以瞬间附身任何人,直接操控他们的行动
这种效率差异是指数级的!
5.2.6 指针功法
- 一取地址得坐标,二解引用触本质。
- 三传指针免复制,四动内存创天地。
- 五防悬空避崩溃,六置NULL归虚无。
- 七层递进掌万物,八面玲珑控时空。
结语:从畏惧到精通
指针不是C语言的难点,而是精髓所在。初学者看到的可能是语法复杂性,但修行者看到的是时空操控的自由度。当你掌握了指针,你就掌握了:
跨越函数边界的操控能力
动态创造和销毁世界的力量
避免不必要数据复制的智慧
构建复杂数据结构的基石
指针,是连接意识与存在的桥梁,是数字宇宙中最高效的沟通方式。 不要畏惧它,而要渴望掌握它。因为当你真正理解指针的那一刻,你就不再是内存的访客,而是它的主宰者。
5.3 C语言结构体:数据封装与内存管理的艺术
引言:为什么需要结构体?
在C语言编程中,当我们需要处理一组逻辑上相关但类型可能不同的数据时,简单的数组显得力不从心。数组要求所有元素类型相同,而现实世界中的数据往往是多元化的。结构体(struct)正是为了解决这一问题而设计的,它允许我们将不同类型的数据组合成一个单一的逻辑单元。
5.3.1 结构体与数组的本质区别
内存布局对比
// 数组:相同类型元素的连续集合u8 array[10];// 10个连续的u8类型内存单元// 结构体:不同类型成员的组合typedefstruct{u8 id;// 1字节u16 value;// 2字节(注意对齐问题)u32 count;// 4字节}SensorData;// 总共可能需要7字节(考虑对齐后可能是8字节)关键差异:
- 数组提供同质化数据存储,支持随机访问和迭代
- 结构体提供异质化数据封装,强调数据之间的逻辑关联性
5.3.2 结构体的专业应用
1. 数据封装与抽象
// 定义学生信息结构体typedefstruct{u32 student_id;// 学号charname[50];// 姓名u8 age;// 年龄floatgpa;// 平均绩点u8 course_count;// 选修课程数}Student;2. 内存映射与硬件寄存器访问
在嵌入式系统和操作系统开发中,结构体常用于精确映射硬件寄存器:
// 映射串口控制寄存器typedefstruct{volatileu32 DATA;// 数据寄存器volatileu32 STATUS;// 状态寄存器volatileu32 CONTROL;// 控制寄存器volatileu32 BAUD;// 波特率寄存器}UART_Registers;// 定义UART1的寄存器映射#defineUART1_BASE0x40001000UART_Registers*uart1=(UART_Registers*)UART1_BASE;// 直接操作寄存器uart1->BAUD=9600;// 设置波特率uart1->CONTROL|=0x01;// 使能UART3. 灵活的内存操作
结构体支持指针访问,但需注意类型安全和内存对齐:
typedefstruct{u8 status;// 状态标志u32 data;// 数据值u16 checksum;// 校验和}Packet;Packet packet;intmain(){u8*raw_ptr=NULL;// 获取结构体起始地址raw_ptr=(u8*)&packet;// 通过字节指针操作结构体内容// 注意:这种操作需要了解结构体内存布局和对齐*raw_ptr=0x01;// 设置status字段// 更安全的访问方式是通过结构体成员packet.status=0x01;packet.data=0x12345678;packet.checksum=0xABCD;return0;}5.3.3结构体的高级特性
1. 内存对齐与优化
编译器会对结构体进行内存对齐以提高访问效率:
// 编译器可能会在成员之间插入填充字节typedefstruct{u8 a;// 1字节// 编译器可能插入3字节填充(在32位系统上)u32 b;// 4字节u16 c;// 2字节// 编译器可能插入2字节填充}UnoptimizedStruct;// 总大小可能为12字节// 优化版本:按大小降序排列减少填充typedefstruct{u32 b;// 4字节u16 c;// 2字节u8 a;// 1字节// 只插入1字节填充}OptimizedStruct;// 总大小可能为8字节2. 位域:紧凑存储布尔标志
// 使用位域优化存储空间typedefstruct{u8 is_active:1;// 1位:是否激活u8 mode:2;// 2位:模式选择(0-3)u8 priority:3;// 3位:优先级(0-7)u8 reserved:2;// 2位:保留位}DeviceStatus;// 总共1字节3. 结构体嵌套与数据层次
// 日期结构体typedefstruct{u16 year;u8 month;u8 day;}Date;// 员工信息结构体(嵌套使用)typedefstruct{u32 emp_id;charname[50];Date hire_date;// 嵌套结构体floatsalary;struct{// 匿名结构体:联系信息charphone[15];charemail[50];}contact;}Employee;5.3.4 工程实践建议
1. 命名规范
- 使用有意义的名称,避免缩写
- 结构体类型名使用sPascalCase
- 结构体变量使用PascalCase或蛇形命名随个人意愿
2. 封装与访问控制
// 头文件声明(public_interface.h)typedefstructDataHandleDataHandle;// 不完全类型,隐藏实现细节// 公共APIDataHandle*create_data_handle(u32 size);voidprocess_data(DataHandle*handle);voiddestroy_data_handle(DataHandle*handle);// 实现文件(private_implementation.c)structDataHandle{// 具体实现对外隐藏u8*buffer;u32 size;u32 capacity;u8 checksum;};3. 错误处理模式
typedefstruct{interror_code;charerror_msg[100];void*data;size_tdata_size;}OperationResult;OperationResultread_file(constchar*filename){OperationResult result={0};// 执行操作FILE*file=fopen(filename,"rb");if(!file){result.error_code=ERR_FILE_NOT_FOUND;snprintf(result.error_msg,sizeof(result.error_msg),"无法打开文件: %s",filename);returnresult;}// ... 读取文件内容returnresult;}5.3.5 结论
结构体是C语言中实现数据封装和抽象的核心机制。它不仅解决了异质数据集合的管理问题,还通过内存对齐、位域等特性提供了精细的内存控制。在大型工程中,合理使用结构体能够:
- 提高代码可读性:通过有意义的成员名称代替晦涩的数组索引
- 增强类型安全:编译器能够检查结构体成员的类型正确性
- 简化维护:相关数据的修改被限制在结构体定义范围内
- 优化内存访问:合理对齐可以显著提升CPU缓存效率
- 支持复杂数据结构:为链表、树、图等数据结构奠定基础
正如生物体由不同类型的细胞有序组成,良好的软件结构也需要将相关数据高内聚地组织在一起。结构体正是实现这一目标的关键工具,它让C语言在保持底层控制能力的同时,具备了构建复杂系统的表达能力。
6 . 编译过程
6.1 预处理
#definemacro0x01voidfunc(){unsignedcharx=macro;}预处理阶段时把宏macro替换为0x01
6.2 编译
unsignedcharx=macro;MOVS r0, #0x01编译阶段把C语言转换成汇编内容, 因为这里在局部变量因此被分配到缓存寄存器R0
6.3 汇编
MOVS R0, #0x010x2001 汇编阶段把汇编指令根据当前平台转换成机器码
6.4 链接
程序中N条汇编机器码需要整合在一起,此时把这些机器码链接在一块输出最终可执行文件
编译过程大致如此,【Q】最终可执行文件为什么可以执行?【A】计算机最终执行权是CPU,CPU按照Flash或RAM区域地址逐步执行,而当可执行文件存到Flash或RAM区域,CPU就会根据可执行文件的内容逐步执行,这里讲解一个例子,例如当我在桌面GUI右击一下鼠标,为什么桌面会弹窗出来,步骤是这样,鼠标机械动作触发了电信号,经过USB协议与电脑主板通信,电脑主板把收到的数据反馈给CPU,由于加载了系统,系统层面会接收到鼠标这种HID设备事件,系统事件循环触发系统GUI已定逻辑,显存加载弹窗的数据,系统界面弹窗,这一切都是因为电脑系统已经集成了逻辑,这个系统逻辑就是用C语言逐步写出来的,也是经过了上述编译四个过程得出的程序
7 . 小结
学习C语言平时用C free,VS等软件验证程序,若中途学习感到困惑有阻碍,不必忧心,多看多写,自然就明白了其中的逻辑,另不必刻意背运算优先级,写着自然就记住了,印象模糊时来回顾一遍运算优先级,看到这里,相信大家已经入门。