Linux入门DAY12
#嵌入式开发基础##Linux##C语言#
前言
本篇笔记梳理Linux下gcc四阶段编译流程、C预处理全套语法(宏、头文件、条件编译)与指针核心知识点,是嵌入式Linux C开发的基础重点。预处理负责文本替换,指针直接操作内存地址,二者结合是函数传地址、数组操作、底层开发的核心。
一、gcc编译四阶段(Day12 开篇)
完整流程: main.c → 预处理 → 编译 → 汇编 → 链接 → a.out
1. 预处理(-E)
作用:展开 # 开头代码,处理头文件、宏、条件编译
命令: gcc -E main.c -o main.i
处理内容: #include 、 #define 、 #if/#endif 条件编译
2. 编译(-S)
作用:C代码转为汇编指令
命令: gcc -S main.i -o main.s
3. 汇编(-c)
作用:汇编指令转为二进制目标文件
命令: gcc -c main.s -o main.o
4. 链接(无参数)
作用:链接系统库函数、整合目标文件,生成可执行程序
命令: gcc main.o -o main
二、预处理命令
1. 宏定义 #define
(1)无参宏(常量)
语法: #define 标识符 替换文本
⚠ 核心注意点:
1. 预处理阶段纯文本替换,不做运算;
2. 宏标识符推荐大写,定义末尾不能加分号;
3. 表达式宏建议整体加括号,避免优先级错误;
4. 宏vs函数:宏是文本替换(无调用开销,代码膨胀);函数运行时调用,保存/恢复栈帧;
5. 大量使用宏会增大程序体积。
(2)带参宏(宏函数)
语法: #define 标识符(参数1,参数2) 实现 。
规则:传参只写变量名,不能加数据类型。
2. 头文件包含 #include
1. <头文件> :系统头文件,编译器去 /usr/include 查找;
2. "头文件" :自定义头文件,从当前工程目录查找;
自定义头文件可存放内容
- 嵌套包含其他头文件
- 自定义数据类型(struct/enum)
- 宏定义
- 函数、全局变量声明
3. 条件编译(两大用途)
用途1:防止头文件重复包含(头文件保护宏)
#ifndef 宏名
#define 宏名
// 头文件全部内容
#endif
逻辑:宏未定义则编译内部代码,已定义直接跳过。
用途2:代码分段编译、屏蔽代码
1. #if 逻辑表达式 :表达式为真才编译中间代码, #if 0 可永久屏蔽代码;
2. #ifdef 宏名 :宏已定义则编译代码1,搭配 #else 实现二选一编译;
#ifdef PI
//代码1
#else
//代码2
#endif
三、指针全套知识点
1. 内存、地址、指针基础概念
- 内存:程序运行可访问的存储空间,以字节为最小单位;
- 地址:内存每个字节的编号;
- 指针 = 地址,代表一块内存空间的编号;
- 指针变量:专门存放地址的变量,占用独立内存。
2. 指针变量定义
语法: 基类型 * 变量名;
- 基类型:指针指向内存存储的数据类型;
- * :标识该变量是指针;
- 大小:64位系统占8字节,32位系统占4字节,与指向类型无关。
3. 指针初始化与野指针
1. 野指针:未初始化、指向已释放内存的指针,直接访问会崩溃;
2. 规避方案:- 初始化赋值 NULL (数值0);
直接赋值已有普通变量的地址。
4. 两大核心运算符
(1)取地址运算符 &
作用:获取变量首字节地址
规则:
- 只能作用于变量,不能给常量、表达式取地址;
- &变量 拿到变量起始内存地址。
(2)解引用运算符 *
作用:访问指针指向内存里的数据
规则:
1. 仅放在指针变量左侧使用;
2. *指针 等价于指针指向的普通变量;
3. 读写字节数由指针基类型决定( int* 读写4字节)。
5. 指针算数运算
1. 无意义运算: * / % 不能用于指针;
2. 指针±整数: p+1 向后偏移 sizeof(基类型) 字节, p-1 向前偏移;
3. 指针-指针:仅同类型指针可相减,结果为两个地址间元素个数 (地址差 / sizeof(基类型)) 。
6. 指针作为函数参数(传地址核心考点)
1. 修改主函数变量:传递变量地址,函数内通过 * 修改原值;
2. 只读使用变量:传值/传地址均可;
3. 多返回值:传入外部变量地址,函数内部写入结果。
7. 指针与一维数组
1. 数组名是首元素地址常量,不能赋值修改;
2. 下标等价写法:
- a[0] ↔ *a
- a[1] ↔ *(a+1)
- a[n] ↔ *(a+n)
3. 数组做函数参数时,底层会退化为对应类型指针。
小结
1. 预处理是编译第一阶段,核心完成文本替换,宏、头文件、条件编译是开发高频工具,头文件保护宏是工程必备规范;
2. gcc四阶段编译命令需熟记,可单独生成.i/.s/.o中间文件用于调试底层代码;
3. 指针本质是内存地址, & 取地址、 * 解引用是基础操作,算数偏移遵循基类型字节大小;
4. 传地址调用是指针核心应用:实现函数修改变量、多返回值、数组批量操作,是测井嵌入式、Linux C课程设计的高频考点;
5. 数组名等价首元素指针,下标与指针写法可以互相转换,是遍历数组的两种实现方式。
