C语言详解


【全文大纲】 : https://blog.csdn.net/Engineer_LU/article/details/135149485


1 . 概要

  • 深入浅出C语言,以下内容简洁,适合入门与加深理解

2 . C语言语法

autobreakcasecharconstcontinuedefaultdo
doubleelseenumexternfloatforgotoif
intlongregisterreturnshortsignedsizeofstatic
structswitchtypedefunionunsignedvoidvolatilewhile

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语言运算符优先级

C语言运算符优先级

4 . 本质理解

4.1 内存的本质:数字世界的生命与轮回

  • 在C语言的世界里,学习语法之前,我们首先要理解一个最基础也最重要的概念——内存。
  • 想象我们身处的自然界:地球上有70多亿人,每个人都是独特的生命单元。我们从自然中诞生——吸收阳光、空气、水,从自然获取物质构成身体;我们最终又回归自然——生命结束后,身体分解,元素重新进入大自然的循环。
  • 每一块内存单元都像是数字宇宙中的基本粒子
  • 当我们声明一个变量时,我们是在向操作系统申请一小块“自然空间”——这是内存的诞生
  • 当我们为变量赋值时,我们是在用数据为这块空间赋予“生命的意义”
  • 当变量超出作用域或被显式释放时,内存回归系统的“自然状态”——这是内存的回归

4.2 语法的本质:掌控数字宇宙的至高功法

  • 理解了内存的本质,我们站在了数字宇宙的边缘。但仅凭领悟无法成为真正的主宰者——就像理解海洋的广阔,不等于能够驾驭惊涛骇浪。认知与掌控之间,隔着一条名为“功法”的鸿沟。
  • 只有理解规律,才能驾驭万物。只有掌握方法,才能塑造世界。于是,他们倾尽智慧,创出了这部数字世界的至高功法——《C语言》。这不仅仅是一门编程语言,而是一整套掌控内存、塑造现实的道法术器。

5 . 语法应用

5.1 简单示例

int main(int argc,char* argv[]) {
	unsigned char value = 0x01;
	if (value == 0x01) {
		 ;
	}
	return 0;
}

语法极其简洁,可能有读者不太了解 “ int argc,char* argv[] ”:这里的内容意义,由于C语言是面向过程,系统级,当系统调用一个程序时,可以执行初期给定输入数据,其中前者是数据数量,后者是具体数据

5.2 指针:时空操控的灵魂之术

5.2.1 跨越维度的力量

在C语言的修行之路上,指针是让无数初学者望而生畏,却又让大师们如鱼得水的核心功法。它不是阻碍,而是通往内存主宰之境的必经之门。当我们谈论指针时,我们谈论的是一种跨越时空的操控能力。

5.2.2 为何需要指针?时空效率的双重突破

在数字宇宙中,每一次变量操作都是在时空中留下痕迹。直接操作变量如同每次都要亲临现场:

// 传统方式:亲力亲为
int data = 100;      // 创造
data = data * 2;     // 修改
int backup = data;   // 复制(消耗额外时空)

这种方式的局限显而易见:

  • 时间消耗:大量数据复制需要CPU周期
  • 空间消耗:每个副本占用额外内存
  • 效率瓶颈:难以跨越函数边界传递大型数据

指针的出现,是时空操控的一次革命性突破。

5.2.3 指针的本质:坐标与存在

int reality = 42;              // 在某个坐标创造存在
printf("存在本身: %d\n", reality);        // 42
printf("存在坐标: %p\n", &reality);       // 0x7ffeebd39a9c
// &reality 不是值,而是"这里"的坐标
// 就像"北京市朝阳区建国门外大街1号"不是建筑本身,而是位置
#### 坐标系统:&运算符
每个变量在数字宇宙中都有一个绝对坐标——内存地址。&运算符就是获取这个坐标的钥匙:

5.2.4 时空穿梭:*运算符

拥有坐标后,我们可以跨越时空直接操控。*运算符就是时空穿梭之门:

int* observer = &reality;      // 获得坐标,成为观察者
printf("坐标指向的存在: %d\n", *observer);  // 42

*observer = 2024;              // 无需亲自到场,直接修改
printf("修改后的存在: %d\n", reality);      // 2024

5.2.5 灵魂出窍:隔空取物的高阶功法

让我们通过一个精妙的示例,体会指针带来的效率革命:

int main(int argc, char* argv[]) {
    u8 *consciousness = NULL;  // 意识体,暂时不依附任何存在
    u8 body = 1;               // 肉身,初始状态为1
    
    consciousness = &body;     // 意识出窍,附身于肉身
    *consciousness = 2;        // 隔空操控,直接修改肉身状态
    
    // 传统方式需要这样检查
    if (body == 1) {           // 此时body已经是2,无需进入
        body = 2;              // 这段代码永远不会执行
    }
    
    return 0;
}
灵魂出窍的哲学意义

想象一下,你有一个强大的意识体(指针):

  • 没有指针:你需要走到每个人面前,亲口告诉他们该做什么
  • 拥有指针:你的意识可以瞬间附身任何人,直接操控他们的行动

这种效率差异是指数级的!

5.2.6 指针功法

  • 一取地址得坐标,二解引用触本质。
  • 三传指针免复制,四动内存创天地。
  • 五防悬空避崩溃,六置NULL归虚无。
  • 七层递进掌万物,八面玲珑控时空。
    结语:从畏惧到精通
指针不是C语言的难点,而是精髓所在。初学者看到的可能是语法复杂性,但修行者看到的是时空操控的自由度。当你掌握了指针,你就掌握了:
  • 跨越函数边界的操控能力

  • 动态创造和销毁世界的力量

  • 避免不必要数据复制的智慧

  • 构建复杂数据结构的基石

指针,是连接意识与存在的桥梁,是数字宇宙中最高效的沟通方式。 不要畏惧它,而要渴望掌握它。因为当你真正理解指针的那一刻,你就不再是内存的访客,而是它的主宰者。


5.3 C语言结构体:数据封装与内存管理的艺术

引言:为什么需要结构体?

在C语言编程中,当我们需要处理一组逻辑上相关但类型可能不同的数据时,简单的数组显得力不从心。数组要求所有元素类型相同,而现实世界中的数据往往是多元化的。结构体(struct)正是为了解决这一问题而设计的,它允许我们将不同类型的数据组合成一个单一的逻辑单元。

5.3.1 结构体与数组的本质区别

内存布局对比
// 数组:相同类型元素的连续集合
u8 array[10];  // 10个连续的u8类型内存单元

// 结构体:不同类型成员的组合
typedef struct {
    u8 id;      // 1字节
    u16 value;  // 2字节(注意对齐问题)
    u32 count;  // 4字节
} SensorData;   // 总共可能需要7字节(考虑对齐后可能是8字节)
关键差异:
  • 数组提供同质化数据存储,支持随机访问和迭代
  • 结构体提供异质化数据封装,强调数据之间的逻辑关联性

5.3.2 结构体的专业应用

1. 数据封装与抽象
// 定义学生信息结构体
typedef struct {
    u32 student_id;     // 学号
    char name[50];      // 姓名
    u8 age;             // 年龄
    float gpa;          // 平均绩点
    u8 course_count;    // 选修课程数
} Student;
2. 内存映射与硬件寄存器访问

在嵌入式系统和操作系统开发中,结构体常用于精确映射硬件寄存器:

// 映射串口控制寄存器
typedef struct {
    volatile u32 DATA;    // 数据寄存器
    volatile u32 STATUS;  // 状态寄存器
    volatile u32 CONTROL; // 控制寄存器
    volatile u32 BAUD;    // 波特率寄存器
} UART_Registers;

// 定义UART1的寄存器映射
#define UART1_BASE  0x40001000
UART_Registers* uart1 = (UART_Registers*)UART1_BASE;

// 直接操作寄存器
uart1->BAUD = 9600;      // 设置波特率
uart1->CONTROL |= 0x01;  // 使能UART
3. 灵活的内存操作

结构体支持指针访问,但需注意类型安全和内存对齐:

typedef struct {
    u8 status;      // 状态标志
    u32 data;       // 数据值
    u16 checksum;   // 校验和
} Packet;

Packet packet;

int main() {
    u8* raw_ptr = NULL;
    
    // 获取结构体起始地址
    raw_ptr = (u8*)&packet;
    
    // 通过字节指针操作结构体内容
    // 注意:这种操作需要了解结构体内存布局和对齐
    *raw_ptr = 0x01;  // 设置status字段
    
    // 更安全的访问方式是通过结构体成员
    packet.status = 0x01;
    packet.data = 0x12345678;
    packet.checksum = 0xABCD;
    
    return 0;
}

5.3.3结构体的高级特性

1. 内存对齐与优化

编译器会对结构体进行内存对齐以提高访问效率:

// 编译器可能会在成员之间插入填充字节
typedef struct {
    u8 a;      // 1字节
    // 编译器可能插入3字节填充(在32位系统上)
    u32 b;     // 4字节
    u16 c;     // 2字节
    // 编译器可能插入2字节填充
} UnoptimizedStruct;  // 总大小可能为12字节

// 优化版本:按大小降序排列减少填充
typedef struct {
    u32 b;     // 4字节
    u16 c;     // 2字节
    u8 a;      // 1字节
    // 只插入1字节填充
} OptimizedStruct;    // 总大小可能为8字节
2. 位域:紧凑存储布尔标志
// 使用位域优化存储空间
typedef struct {
    u8 is_active   : 1;  // 1位:是否激活
    u8 mode        : 2;  // 2位:模式选择(0-3)
    u8 priority    : 3;  // 3位:优先级(0-7)
    u8 reserved    : 2;  // 2位:保留位
} DeviceStatus;          // 总共1字节
3. 结构体嵌套与数据层次
// 日期结构体
typedef struct {
    u16 year;
    u8 month;
    u8 day;
} Date;

// 员工信息结构体(嵌套使用)
typedef struct {
    u32 emp_id;
    char name[50];
    Date hire_date;     // 嵌套结构体
    float salary;
    struct {            // 匿名结构体:联系信息
        char phone[15];
        char email[50];
    } contact;
} Employee;

5.3.4 工程实践建议

1. 命名规范
  • 使用有意义的名称,避免缩写
  • 结构体类型名使用sPascalCase
  • 结构体变量使用PascalCase或蛇形命名随个人意愿
2. 封装与访问控制
// 头文件声明(public_interface.h)
typedef struct DataHandle DataHandle;  // 不完全类型,隐藏实现细节

// 公共API
DataHandle* create_data_handle(u32 size);
void process_data(DataHandle* handle);
void destroy_data_handle(DataHandle* handle);

// 实现文件(private_implementation.c)
struct DataHandle {  // 具体实现对外隐藏
    u8* buffer;
    u32 size;
    u32 capacity;
    u8 checksum;
};

3. 错误处理模式
typedef struct {
    int error_code;
    char error_msg[100];
    void* data;
    size_t data_size;
} OperationResult;

OperationResult read_file(const char* 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);
        return result;
    }
    
    // ... 读取文件内容
    
    return result;
}

5.3.5 结论

结构体是C语言中实现数据封装和抽象的核心机制。它不仅解决了异质数据集合的管理问题,还通过内存对齐、位域等特性提供了精细的内存控制。在大型工程中,合理使用结构体能够:

  • 提高代码可读性:通过有意义的成员名称代替晦涩的数组索引
  • 增强类型安全:编译器能够检查结构体成员的类型正确性
  • 简化维护:相关数据的修改被限制在结构体定义范围内
  • 优化内存访问:合理对齐可以显著提升CPU缓存效率
  • 支持复杂数据结构:为链表、树、图等数据结构奠定基础

正如生物体由不同类型的细胞有序组成,良好的软件结构也需要将相关数据高内聚地组织在一起。结构体正是实现这一目标的关键工具,它让C语言在保持底层控制能力的同时,具备了构建复杂系统的表达能力。


6 . 编译过程

6.1 预处理

    #define macro 0x01
    void func() {
    	unsigned char x = macro;
    }	

预处理阶段时把宏macro替换为0x01

6.2 编译

unsigned char x = macro;
MOVS r0, #0x01

编译阶段把C语言转换成汇编内容, 因为这里在局部变量因此被分配到缓存寄存器R0

6.3 汇编

MOVS R0, #0x01

0x2001 汇编阶段把汇编指令根据当前平台转换成机器码

6.4 链接

程序中N条汇编机器码需要整合在一起,此时把这些机器码链接在一块输出最终可执行文件


编译过程大致如此,【Q】最终可执行文件为什么可以执行?【A】计算机最终执行权是CPU,CPU按照Flash或RAM区域地址逐步执行,而当可执行文件存到Flash或RAM区域,CPU就会根据可执行文件的内容逐步执行,这里讲解一个例子,例如当我在桌面GUI右击一下鼠标,为什么桌面会弹窗出来,步骤是这样,鼠标机械动作触发了电信号,经过USB协议与电脑主板通信,电脑主板把收到的数据反馈给CPU,由于加载了系统,系统层面会接收到鼠标这种HID设备事件,系统事件循环触发系统GUI已定逻辑,显存加载弹窗的数据,系统界面弹窗,这一切都是因为电脑系统已经集成了逻辑,这个系统逻辑就是用C语言逐步写出来的,也是经过了上述编译四个过程得出的程序

7 . 小结

学习C语言平时用C free,VS等软件验证程序,若中途学习感到困惑有阻碍,不必忧心,多看多写,自然就明白了其中的逻辑,另不必刻意背运算优先级,写着自然就记住了,印象模糊时来回顾一遍运算优先级,看到这里,相信大家已经入门。

技术交流QQ群 : 745662457
群内专注问题答疑,技术交流,项目研究

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客 - L U

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值