C的预处理指令、typedef
1 集成开发环境
keil-MDK是集成开发环境,集成了C编译器、宏汇编、链接器、库管理和仿真调试器等在内的完整开发方案;
1.1 预处理器:处理注释和预处理指令;生成.i文件;
1.2 编译器:对预处理器处理之后的文件进行语法分析编译,生成.s文件;
1.3 汇编器:对编译器处理之后的文件进行翻译重定位,生成.o二进制文件;
1.4 链接器:对汇编器处理之后的文件进行捆绑打包,生成.exe可执行文件;
1 预处理器指令
作用:宏名替换,文件包含;
特点:预处理器不会处理逻辑问题;预处理指令不是c指令,所以没有分号结尾;
1.1 /**/ 注释
预处理器会将每条注释用一个空格替代;
int/*想不到我是一个空格吧?*/num; int num; //我也是呢;
1.2 #define 宏
作用:在函数中对宏名进行替换;
#define TWO 2 #define FOUR TWO*TWO /*#define 宏 替换对象 *替换之后FOUR不是4,而是2*2;*/
1.2.1 类函数宏
#define Byte0(data) ((byte *)(&(data)))[0] /*宏名为Byte0,参数为data*/ #define Byte1(data) ((byte *)(&(data)))[1] #define Byte2(data) ((byte *)(&(data)))[2] #define Byte3(data) ((byte *)(&(data)))[3] /* 相当于addr[0],addr[1],addr[2],addr[3],将原先存储着int型的地址,强制转换为byte型来存储,所以要声明4个指向byte型的指针; */ /* 目的大概是为了防止数据丢失,然后在不同位数的单片机上移植;或者是为了特定目的传输数据;*/
1.2.2 类函数宏
/*预处理器不会处理逻辑计算*/ #define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB) #define SysTick_CLKSource_HCLK ((uint32_t)0x00000004) #define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \ ((SOURCE) == SysTick_CLKSource_HCLK_Div8))
1.3 #include文件包含
作用:告诉预处理器将被包含文件的所有内容输入到当前位置;
#include <stdio.h> /*告诉预处理器在标准目录(也就是库目录)下查找该文件;*/ #include "stdio.h" /*告诉预处理器先在当前.c文件所在目录下查找该文件,找不到再去库中查找;*/ /*程序大小是由编译器编译时生成的代码决定的,与是否包含大型头文件无关;*/
1.4 #undef
作用:取消已有的预处理器指令;感觉用不上;
#define SIZE 256 /*定义了一个宏SIZE,预处理器在文件中将其替换为256;*/ #undef SIZE /*取消了宏SIZE的定义,预处理器在文件中不会将其替换了;*/
1.5 #ifdef
作用:告诉编译器执行或忽略某些代码块;
特点:只判断宏是否被声明,不判断宏是否为真;
#define USE_STD_LIB 1 #ifdef USE_STD_LIB int spi_std_init(void){ return 0; } #else int api_reg_init(void){ return 0; } #endif
1.6 #ifndef
作用:防止多次包含同一个文件,相同的宏被重新定义;
/*假设有math.h文件如下:*/ #ifndef SIZE #define SIZE 512 #endif /*当前文件为file1.h*/ #define SIZE 100 #include "math.h" /*由于前面一行定义过了SIZE,所以在math.h中就不会再重新替换SIZE为100;*/ /*那如果我把SIZE 10 放到math.h后面,SIZE的值是会改变,还是会报错呢??目测报错;*/
1.6.1常见用法
#ifndef DRIVE_H_ #define DRIVE_H_ /*...假装好多预处理指令*/ #endif /***标准函数库使用下划线作为前缀,自定义库可以去掉_前缀作为区分; ***就约定自定义库用大写字母加下划线定义好了; ***预处理指令可以避免头文件的重复包含;***/
#define USE_STD_LIB 1 /***条件编译语句,大概预处理器只是进行了替换,实际判断还是编译器处理的;***/ #if USE_STD_LIB int gpio_std_init(void){ return 0; } #elif USE_HAL_LIB int gpio_hal_init(void){ return 0; } #else int gpio_reg_init(void){ return 0; } #endif
/***#ifndef可以防止预处理器多次处理相同的xx.h文件,不能防止xx.h中定义式声明的多次重复; ***大约预处理器只是对用户代码中的#include进行了文件替换包含,而实际编译处理还是编译器编译的; ***在xx.h中进行定义式声明,则编译器会多次声明定义造成报错; ***大约编译器只会编译一次xx.c,而xx.h中的函数声明只是一个函数接口链接; ***解决办法是要么使用static副本,要么定义式声明在xx.c文件内,要么使用extern引用式声明;***/
1.6.2 static副本
作用:解决xx.h头文件中的定义式重复声明
/*以下代码为xx.h*/ static const double PI = 3.1415 ; static const months = 12 ; /*如果去掉static,将导致每个包含xx.h的C文件都有一个相同的定义式声明,会让cpu不知所措; *定义式声明只能声明一次,引用式声明可以多次;*/ void sum(int a,int b); /*将函数原型,全局变量等放在头文件中,然后在其他多个文件中包含该头文件, *就相当于给每个文件都提供了一个接口沟通 *如果头文件中含有定义式声明,那么前面需要加上一个static,这相当于给每个调用的文件一个单独的数据副本;*/
1.6.3 extern引用式声明
作用:解决xx.h头文件中的定义式重复声明
/**xx.c文件中**/ int receive_buf[256]; /***xx.c对应的xx.h文件中引用式声明,这样该数组就可以提供给其他xx.h文件使用了;***/ extern int receive_buf[256];
2 编译器指令
2.1 typedef 声明
typedef unsigned char BYTE ; /* 将 unsigned char 重新命名为BYTE ;*/ typedef char * STRING ; /*将 char * 声明为STRING */
2.2 typedef 与 #define
typedef char * STRING; #define STRING char * /*1,typedef只能定义数据类型,而#define可以定义类对象宏、类函数宏;*/ /*2,typedef由编译器解释,而#define由预处理器解释;*/ /*3,如果定义在函数外则具有文件作用域,函数内则局部作用域*/
2.3 typedef 与 结构体
2.3.1 重新声明结构体
/* 声明结构体类型complex;将其重新声明为COMPLEX; */ typedef struct complex{ int real; float imag; } COMPLEX; /*声明一个没有名字的结构体类型,将其声明为COMPLEX类型; *typedef声明时,结构体类型可以省略原标识符,只保留struct标识符;*/ typedef struct{ int real; float imag; } COMPLEX;
2.3.2 typedef 与 指针
struct rt_thread { }; typedef struct rt_thread *rt_thread_t; //此时 rt_thread_t 是一个 rt_thread 类型的指针; //为什么我的关注点是放在 这个指针的符号*为什么加在rt_thread_t上,而不是单独写;这又是什么固定格式把。。。
2.4 #pragma
作用:可以放入代码中的编译器指令;可是用了预处理指令的#,迷惑,先放着吧;
2.5 sizeof()
这不是函数也不是编译器指令,而是C语言运算符,运算符优先级2级,由编译器编译执行;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?