预处理
C语言的编译步骤
- 预处理
- 编译
- 汇编
- 链接
什么是预处理
预处理就是在源文件(.c文件)编译之前,所进行的一部分备份操作,这部分是由预处理器(预处理程序)
自动完成。当源文件在编译时,编译器会自动调用预处理程序来完成预处理执行的操作,预处理执行解析完成才能进入下一步的编译过程。
查看预处理的结果:
gcc 源文件 -E -o 程序名
预处理功能
宏定义
不带参数的定义
语法:
#define 宏名称 宏值(替换文本)
**预处理机制:**此时的预处理只是数据替换,不做类型检查
**注意:**宏定义不会占用内存空间,因为编译前已经将宏名称替换成宏值
**宏展开:**在预处理段将宏名称替换成宏名称替换成宏值的过程称之为“宏展开”。
案例:
/************************************************************************* > File Name: demo01.c > Author: 小刘 > Description: > Created Time: 2025年05月29日 星期四 09时22分59秒 ************************************************************************/ #include <stdio.h> #define PI 3.1415926 int main(int argc,char *argv[]) { float l,s,r,v; printf("请输入圆的半径:"); scanf("%f",&r); l = 2 * PI * r; s = r * r * PI; printf("l=%0.4f\ns=%0.4f\n",l,s); return 0; }
带参数的定义
语法:
#definr 宏名(参数列表) 替换表达式
面试题:
#define multi(a,b) (a) * (b) #define multi(a,b) a * b
实现
/*************************************************************************
> File Name: demo02.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月29日 星期四 10时24分27秒
************************************************************************/
#include <stdio.h>
// 带参数的宏定义,宏名一般小写
#define mult_1(a,b) (a)*(b)
#define mult_2(a,b) a * b
int main(int argc,char *argv[])
{
int result1 = mult_1(7+2,3);
printf("%d\n",result1);
int result2 = mult_2(7+2,3);
printf("%d\n",result2);
return 0;
}
宏定义的作用域
#define
命令出现程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。可以用
#undef
命名终止宏定义的作用域。案例:
/************************************************************************* > File Name: demo03.c > Author: 刘孟丹 > Description: > Created Time: 2025年05月29日 星期四 10时32分45秒 ************************************************************************/ #include <stdio.h> #define PI 3.14 //PI的作用域 10行~19行 #define DAY 29 void func1() { float r = 4 ; float s = PI * r * r; // 预处理后 int day = DAY; // } #undef PI //终止了PI的范围 #define PI 3.1415926 void func2() { float r = 4 ; float s = PI * r * r; int day = DAY; } int main(int argc,char *argv[]) { return 0; }
宏定义中引用已定义的宏明
案例
/************************************************************************* > File Name: demo04.c > Author: 小刘 > Description: > Created Time: 2025年05月29日 星期四 10时39分52秒 ************************************************************************/ #include <stdio.h> #define R 3.0 //半径 #define PI 3.14 #define L 2*PI*R // 周长 在宏定义的时候,引入已定义的宏名 #define S PI*R*R //面积 #define P_WIDTH = 800 #define P_HeIGTH = 480 #define SIZE = p_HEIGHT * P_WIDTH int main(int argc,char *argv[]) { printf("L = %f\n s=%f\n",L,S) return 0; }
预处理结果:
条件编译
**定义:**根据设定的条件选择待编译的语句代码。
预处理机制:将满足条件的语句进行保留将不满足的条件语句进行删除,交个下一步编译,
语法:
语法1:
根据是否找到标记,来决定是否参与编译(标记存在为真,不存在为假)
#ifdef 标记
... 语句代码1
#else
... 语句代码2
#endif
语法2
根据是否找到标记,来决定是否参与编译(标记不存在为真,存在为假)
#ifndef 标记 ... 语句代码1 #else ... 语句代码2 #endif
语法3
根据表达式的结果,来决定是否参与编译(表达式成立为真,不成立为假)
------ 单分支 #if 表达式 ... 语句代码 #endif -----双分支结构---- #ifdef 标记 ... 语句代码1 #else ... 语句代码2 #endif ----- 多分支 ----- #if 表达式1 ... 语句代码1 #elif 表达式n ... 语句代码n #else ... 语句代码n+1 #endif
案例:
/*************************************************************************
> File Name: demo05.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月29日 星期四 11时30分59秒
************************************************************************/
#include <stdio.h>
// 定义一个条件编译的标记
#define LETTER 0 // 默认是大小
int main(int argc,char *argv[])
{
// 测试用字母字符串
char str[20] = "C language";
char c;
int i = 0;
while((c = str[i]) != '\0')
{
#if LENTTER
if(c >= 'a' && c <= 'z')
{
c -= 32;
}
#else
if(c >= 'A' && c <= 'Z')
{
c += 32;
}
#endif
printf("%c",c);
i++;
}
printf("\n");
return 0;
}
概念
所谓“文件包含”处理是指一个源文件可以将另一个源文件的全部内容包含进来。这适合于多文件开发。通常,一个常规的C语言程序会包含多个源文件( *.c
),当某些公共资源需要在各个源文件中使用时,为了避免多次编写相同的代码,我们一般会进行代码的抽取( *.h
),然后在各个源文件中直接包含即可。
注意:*.h
中的函数声明必须在*.c
中有对应的函数定义,否则没有意义。(函数一旦声
明,就一定要定义)
头文件(.h)的内容
头文件中所存放的内容,就是各个源文件彼此可见的公共资源包括:
- 全局变量声明
- 普通函数的声明
- 静态函数的定义
- 宏定义
- 结构体、共用体的定义
- 枚举常量列表
- 其他头文件包含
示例代码
myhead.h
extern int global;
extern void fun1();
static void fun2();
{
....
};
#define max(a,b) ((a) > (b) ? (a) : (b)) // 宏定义
struct node // 结构体定义
{
..
};
union attr // 共用体定义
{
..
};
enum SEX // 枚举常量列表定义
{
..
};
#include <stdio.h> // 引入系统头文件
#include "myhead2.h" // 引入自定义头文件
特别说明
- 全局变量、普通函数的定义一般出现在某个源文件(
*.c
)中其他源文件都需要进行声明。因此一般放在文件中更方便。 - 静态函数、宏定义、结构体、共用体、枚举的定义都只能在其他所在文件可见,因此如果多个源文件都需要的话,放到头文件中定义时最方便的选择。
预处理机制:将文件中的内容替换文件包含指令
包含方式
#include <XXXXX.h>
:系统会到标准库文件目录(Linux下/usr/include
)查找包含文件建议对于系统库访问采用这种写法。#include "xxxx.h"
:在当前工程路径下(Linux下 ./ )查找包含的文件,如果未找到,就去标准库文件目录下查找。建议对于自定义库采用这种写法。
案例
myhead.h
/************************************************************************* > File Name: myhead.h > Author: 小刘 > Description: > Created Time: 2025年06月01日 星期日 19时24分29秒 ************************************************************************/ #ifndef _MYHEAD_H #define _MYHEAD_H /** * 数组的累加和运算 * @param int *int数组 * @param int 数组大小 */ extern int sum(const int*,int); #endif //_MYHEAD_H
myhead.c
/************************************************************************* > File Name: myhead.c > Author: 小刘 > Description: > Created Time: 2025年06月01日 星期日 19时34分24秒 ************************************************************************/ #include <stdio.h> int sum(const int *arr,int len) { const int *p = arr; int sum = 0; for(;p < arr + len; p++) { sum += *p; } return sum; }
app.c
/************************************************************************* > File Name: app.c > Author: 小刘 > Description: > Created Time: 2025年06月01日 星期日 19时41分43秒 ************************************************************************/ #include <stdio.h> #include "myhead.h" int main(int argc,char *argv[]) { int arr[] = {11,12,13,14,15}; int result = sum(arr,sizeof(arr)/ sizeof(arr[0])); printf("数组累加和的结果是%d\n",result); return 0; }
多条件编译命令
gcc app.c myhead.c -o app
避免头文件重复包含的方法
其实就是头文件去重复。
由于头文件包含指令#include的本质是复制粘贴,并且一个头文件中可以嵌套包含其他头文件,因此很容易出现头文件被重复包含的情况。此时就需要我们进行去重,去重需要用到预处理提供的去重相关指令。
语法:
#ifndef XXXX_H // 一般为 头文件名大写+下换线+H
#define XXXX_H
..
#endif
案例:
/*************************************************************************
> File Name: myhead.h
> Author: 小刘
> Description:
> Created Time: 2025年06月01日 星期日 19时24分29秒
************************************************************************/
#ifndef _MYHEAD_H
#define _MYHEAD_H
/**
* 数组的累加和运算
* @param int *int数组
* @param int 数组大小
*/
extern int sum(const int*,int);
#endif //_MYHEAD_H