嵌入式 Day 13:预处理命令与指针基础详解
define 标识符 替换内容注意:宏是纯文本替换,不参与计算,末尾不能加分号,否则会一并被替换,引发语法错误。eg: 加上括号保证优先级#define 宏名(形参表) 宏内容(a) : (b))括号加得越多越安全,避免运算符优先级造成错误。宏展开是直接替换文本,不需要保护现场,所以执行效率高但代码体积变大。
主要内容包括预处理命令、宏定义、条件编译、标准头文件写法以及C语言中指针的概念与使用。
一、C语言的编译过程
C语言的完整编译过程分为四个阶段:
-
预处理:处理
#include、#define、条件编译等指令,进行文本替换。 -
编译: 将预处理后的代码编译成汇编代码。
-
汇编: 将汇编代码转化为目标文件(.o 或 .obj)。
-
链接: 将多个目标文件链接为可执行程序。
其中,预处理的本质就是文本替换,这对后续编译影响很大。
二、宏定义
1. 不带参数的宏定义
语法:#define 标识符 替换内容
例如:
#define PI 3.14
使用时:
float area = PI * r * r;
注意:宏是纯文本替换,不参与计算,末尾不能加分号,否则会一并被替换,引发语法错
误。
eg: 加上括号保证优先级

2. 带参数的宏定义
语法:#define 宏名(形参表) 宏内容
例如:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
使用示例:
int x = 5, y = 10; int max = MAX(x, y);
括号加得越多越安全,避免运算符优先级造成错误。宏展开是直接替换文本,不需要保护现场,所以执行效率高但代码体积变大。
宏与函数的本质区别:
宏是直接进行文本替换,不进行类型检查,也不参与函数调用机制。它在预处理阶段就被展开,不会产生函数调用开销,也不会保护现场和恢复现场。
函数则是在编译阶段编译生成机器代码,具有参数检查、类型检查等机制,并需要执行时进行调用、压栈、返回等操作。
只是做文本替换,不计算,输出结果为15

//3>4+5加号优先级高,所以3>9,输出结果为0

例题:给定三角形三边求三角形面积(用宏)


三、文件包含
语法:
#include <stdio.h> // 系统路径查找
#include "func.h" // 当前目录查找
实质也是文本替换,不参与编译。头文件用于声明函数或变量,不能包含函数定义。
四、条件编译
用法1:
#if 0 // 这段代码会被替换为空
#endif
用法2:
#ifdef DEBUG // 如果宏 DEBUG 被定义,编译这段
#else // 否则编译这一段
#endif
用法3:
#ifndef FUNC_H
#define FUNC_H // 头文件内容
......
#endif
常用于防止头文件重复包含。
五、指针基础
1. 指针概念
指针是一种用来存储地址的变量。地址即数据在内存中的编号。
int i = 10;
int *p = &i; // p 是指向 int 型的指针,保存变量 i 的地址(此处*声明p是个存地址的变量)
*p = 100; // 通过指针 p 修改 i 的值(此处*是指针运算符,说明指向 i 的内容)
2. 指针语法
类型 *指针名;
例如:
int *p; // 指向 int 类型的指针
char *cp; // 指向 char 类型的指针
*在定义时表示“指针类型”;在使用时表示“间接访问”。
3. 间接访问的三大步骤:
-
根据指针变量中的值去内存中定位;
-
从定位处开始向后偏移
sizeof(基类型)个字节; -
将偏移后的那部分内存空间当作一个基类型变量来看。
4. 指针作为函数参数
值传递:
void change(int a)
{
a = 100;
}
int main()
{
int x = 10;
change(x); // x 仍为 10
}
地址传递(指针传递):
void change(int *p)
{
*p = 100;
}
int main()
{
int x = 10;
change(&x); // x 被修改为 100
}
要修改谁,就传它的地址。指针参数本质上就是“地址传递”,可以在被调函数中修改主调函数中的数据。所有指针在 64 位系统下占 8 个字节,在 32 位系统下占 4 个字节,不随基类型变化而改变。
指针百分之八十的用途:在函数内部可实现在被调函数中修改主调函数
eg:
int *p;
*p = 100;
*p为野指针,编译不报错,运行程序崩溃(断错误)
注:被调函数中,一定要有*p运算,间接访问操作;
指针变量(野指针)与指针变量t指向的变量应都是确定的。
六、标准头文件防止重复包含
写头文件时,通常加如下模板:
#ifndef FUNC_H
#define FUNC_H
void myFunction();
#endif
这可以避免多个文件重复包含头文件,导致重复定义错误。
七、练习
用指针方式求3个数的和

求3个数最大,最小值

交换两数(注意不要将t写为*t,因为*a,*b已经指向i和j了,已经是整型数字了)

输出类型选择

前3个表达式输出的都是i的地址(*与&可抵消);
后2个表达式第一个输出i本身,第二个编译报错。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)