c语言成长之路14

129 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 13 月更文挑战」的第14天,点击查看活动详情

C 预处理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:

指令描述
#define定义宏
#include包含一个源代码文件
#undef取消已定义的宏
#ifdef如果宏已经定义,则返回真
#ifndef如果宏没有定义,则返回真
#if如果给定条件为真,则编译下面代码
#else#if 的替代方案
#elif如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个 #if……#else 条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中

预处理器实例

分析下面的实例来理解不同的指令。

#define MAX_ARRAY_LENGTH 20

取消宏定义和宏的重定义 
·使用#undef命令,语法: 
#undef 宏名 
·避免宏的重定义,使用#ifndef或#ifdef命令,例 
#ifndef MYHEADER_H 
#define MYHEADER_H 2000 
#endif 

宏展开的优先级错误 
·宏的操作完全是标记的文本替换,只有在宏展开结束后,才会把宏体解析为声明,表达式或语句,例: 
#define SQUARE(x) x
SQUARE(a)=>a
a  //ok 
SQUARE(a+b)=>a+ba+b //??? 
·避免这种事件发生,可以使用括号 
#define SQUARE(x) (x)
(x) 
SQUARE(a+b)=>(a+b)*(a+b) //ok 

把标记转换为字符串 
·在标记序列中使用#,例 
#define TEST(a,b) printf( #a "<" #b "=%d\n", (a)<(b) ) 
TEST(0,0xFFFF)=>printf("0<0xFFFF=%d\n", (0)<(0xFFFF)) 

宏展开中的标记合并 
·在标中使用##,例 
#define NAME(i) name ## i 
NAME(1)=>name1 

宏的可变参数列表 
·使用省略号来表示宏参数中的可变参数列表,语法: 
#define 宏名(参数列表,...) 标记序列 
#define 宏名(...) 标记序列 
·在标记序列中使用__VA__ARGS__来对应参数列表中的...,例 
#define MAKE_EM_A_STRING(...) #VA__ARGS 
MAKE_EM_A_STRING(a,b,c,d)=>"a,b,c,d" 

文件包含 
·使用#include命令,形式如下: 
#include <字符序列>
#include "字符序列" 
#include 标记序列 
·使用<>和""的区别在于编译器查找包含文件的方式 
<>:根据编译器的定义规则和路径进行查找 
"":从源文件的当前路径开始查找,如果不存在,则以<>形式来处理 

条件编译 
·使用#if,#else,#endif命令,形式: 
#if 常量表达式 
.... 
#else 
... 
#endif 
·使用#elif命令,形式 
#if 常量表达式1 
... 
#elif 常量表达式2 
... 
#elif 常量达式n 
... 
#else 
... 
#endif 
·使用#ifdef和#ifndef命令,形式 
#ifdef 宏名 
表示如果该宏名已被定义,则相当于#if 1 
如果该宏名没有定义,则相当于#if 0 
·defined操作符 
·defined操作符可以在#if和#elif表达式中使用,不能用于别处,形式 
defined name 
defined (name) 
例: 
#if defined(VAX) 代替 
#ifdef VAX   

显示的行号 
·使用#line命令,形式: 
#line n "filename" 
#line n 
表示源代码的下一行来自用户所编写的filename的文件的第n行,n必须是十进制数字 

pragma指定 
·以添加新的预处理器功能或者向编译器提供因实现而异的信息 
·标准pragma命令 
#pragma [FP_CONTRACT|FENV_ACCESS|CX_LIMITED_RANGE] [ON|OFF|DEFAULT] 
·_Pragma操作符,形式: 
_Pragma("字符串序列") 
例: 
_Pragma("STDC FENV_ACCESS ON") 等价于 
#pragma STDC FENV_ACCESS ON 

错误指令 
·使用#error命令,形式 
#error 预处理器标记 
·如果信息中包含有宏,而不想将宏进行展开,可以使用字符串形式 
#define SIZE 1024 
#if (SIZE%256 !=0 ) 
#error "SIZE must be a multiple of 256" 
#endif 

关于一些旧编译器空白字符的预处理 
例: 
#define INC ++  
#define TAB internal_table 
#define INCTAB table_of _increments 

  #define CONC(x,y) x/**/y 
对于CONC(INC,TAB)进行展开,标准与非标准编译器处理方式: 
标准                          非标准 
CONC(INC,TAB)             CONC(INC,TAB) 
INC TAB                   INCTAB   这里出现了不一样的结果 
++ internal_table        table_of_increments  导致结果也不一样了