C语言预处理指令

  • 0. 一览
  • 1. 简介
  • 2. 指令
  • 2.1 #include
  • 2.2 #define
  • 2.3 #undef
  • 2.4 #ifdef, #ifndef, #endif
  • 2.5 #if, #elif, #else和#endif
  • 2.6 #,##
  • 2.7 #pragma
  • 2.7.1 message
  • 2.7.2 code_seg
  • 2.7.3 once
  • 2.7.4 hdrstop
  • 2.7.5 resource
  • 2.7.6 warning
  • 2.7.7 comment
  • 2.7.8 disable
  • 2.7.9 data_seg
  • 2.7.10 region
  • 3. 引用

0. 一览

指令

说明

#include

包含一个文件

#define

定义宏

#undef

取消已定义的宏

#ifdef

如果宏已经定义,则编译下面代码

#ifndef

如果宏没有定义,则编译下面代码

#endif

结束一个#if……#else条件编译块

#if

如果给定条件为真,则编译下面代码

#elif

如果前面的#if给定条件不为真,当前条件为真,则编译下面代码

#else

配合#if使用

#

Stringize operator,就是把宏参数字符串化

##

Token pasting

#pragma

最复杂了,这里没法详述

#error

报错并停止预编译

#warning

警告并继续预编译

1. 简介

C语言预处理指令是一个声明(文本)代换表达式,它以#开头,且其必须是第一个字符,#后是指令关键字,在关键字和#之间允许存在任意个数的空白字符,整行语句构成了一条与处理指令,该指令在编译器进行编译之前对源码进行某些转换。
C预处理指令不是C编译器的一部分,它和标准的C有着语法上的一些不同,例如:

  • 它以#开头。
  • 它以行为单位的,每一声明单独一行,然而标准的C并不强制。
  • 它以新行为结尾,但是标准C要以;结尾。

2. 指令

2.1 #include

用于引入另一个文件,主要是头文件。有两种方式

  • #include <file.h>:从C的标准库中查找头文件
  • #include "path_to_header_file":从当前目录中查找,然后再去C标准库中查找,主要是自定义的头文件

2.2 #define

宏定义,给值/常量/表达式定义一个名字,格式
#define MACRO_NAME macro_body 其中MACRO_NAME是C标识符,而macro_body是字符串/值/表达式。
举例:

#include <stdio.h>
// MACRO definition
#define COUNTRY "CHINA"		// 字符串常量
#define TRUE	1							// 整型常量
#define FALSE 0							// 整型常量
#define SUM(x,y) ((x)+(y))			// 宏定义
int main(void)
{
	printf("COUNTRY: %s\n", COUNTRY);
	printf("TRUE: %d\n", TRUE);
	printf("FALSE: %d\n", FALSE);
	printf("SUM(10+20): %d\n", SUM(10, 20));
	return 0;
}

结果:

COUNTRY: CHINA
TRUE: 1
FALSE: 0
SUM(10+20): 30

2.3 #undef

#define正号相反,用于移除宏定义,如果已经定义了,则移除,如果没有定义,则不用管。格式:#undef MACRO_NAME 举例:

#include <stdio.h>
// MARCO definition
#define TRUE 1
#define FALSE 0
int main(void)
{
	printf("TRUE: %d\n", TRUE);
    printf("FALSE: %d\n", FALSE);

    // Undefine a previously defined macro
    #undef TRUE
    #undef FALSE

    // Re-define macro values
    #define TRUE 0
    #define FALSE 1

    printf("\nMacro values are redefinition\n");
    printf("TRUE: %d\n", TRUE);
    printf("FALSE: %d\n", FALSE);

    return 0;
}

结果

TRUE: 1
FALSE: 0

Macro values are redefinition
TRUE: 0
FALSE: 1

如果在#undef之后使用则会报错:

ex1.c: In function 'main':
ex1.c:14:26: error: 'TRUE' undeclared (first use in this function)
     printf("TRUE: %d\n", TRUE);
                          ^~~~
ex1.c:14:26: note: each undeclared identifier is reported only once for each function it appears in
ex1.c:15:27: error: 'FALSE' undeclared (first use in this function); did you mean 'FILE'?
     printf("FALSE: %d\n", FALSE);
                           ^~~~~
                           FILE

2.4 #ifdef, #ifndef, #endif

这是带有条件的预处理指令,用于检查是否定义了宏。其中#ifdef#ifndef是互补的。

  • #ifdef:如果已经定义了宏,则编译下面的代码,格式:#ifdef MACRO_NAME
  • #ifndef:如果没有定义宏,则编译下面的代码,格式:#ifndef MACRO_NAME
  • #endif:结束上面的代码,配套使用,一个对应一个,必须要有
    举例:
#include <stdio.h>
// MACRO definition
#define COUNTRY "CHINA"

int main(void)
{
	#ifdef COUNTRY
		printf("Country is defined\n");
	#endif
	
	#ifndef PROVINCE
		printf("PROVINCE is not defined. Defining PROVINCE... \n");
		#define PROVINCE "HN"
	#endif
	printf("Province is: %s\n", PROVINCE);
		
	return 0;
}

结果

Country is defined
PROVINCE is not defined. Defining PROVINCE...
Province is: HN

这些个指令主要用途是防止一个头文件重复加载多次。例如有一个自定义的头文件myfile.h,在多个文件中*.c文件中使用,那么为了防止多次加载,可以如下定义这个文件

#ifndef _MY_FILE_H
	#define _MY_FILE_H
	// 声明其他宏
	// 声明变量和方法
#endif

在第一次加载的时候,C预处理器会检查发现没有定义_MY_FILE_H,就会执行定义,并声明其它宏、变量以及方法,第二次加载引入的时候,已经定义了_MY_FILE_H,就不会重新声明了。

2.5 #if, #elif, #else和#endif

这些个指令类似C语言中的if...else...语句,用于条件编译。格式:

#if expression
	// If condition is true
#elif expression
	// If else if condition is true
#else
	// If no condition is true
#endif

举例:

#include <stdio.h>

#define IND 1
#define USA 2
#define UK  3

#define COUNTRY IND

int main()
{
	#if COUNTRY == IND
		printf("Selected country code is: %d\n", COUNTRY);
        // Do some task if country is India
	#elif COUNTRY == USA
		printf("Selected country code is: %d\n", COUNTRY);
        // Do some task if country is USA
	#else
		printf("Selected country code is: %d\n", COUNTRY);
        // Do some task if country is UK
	#endif
	
	return 0;
}

2.6 #,##

这是两个特殊的C预处理指令,是用来处理字符串的。

  • #,用于把宏参数转化为字符串,使得不用再显示使用双引号来括起来,格式:#define MACRO_NAME(param) #param,举例:
#include <stdio.h>

#define PRINT(msg) #msg
int main(void)
{
	printf(PRINT(I love programming));
	return 0;
}

结果:

I love programming
  • ##,用于连接两个数据,把他们作为字符串进行拼接。格式:```#define MACTRO_NAME(param1, param2) param1##param2,举例:
#include <stdio.h>
#define CONCAT(a,b) a##b

int main(void)
{
	printf("CONCAT(10, 20) = %d\n", CONCAT(10, 20));
	return 0;
}

结果:

CONCAT(10, 20) = 1020

2.7 #pragma

在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
格式:#pragma Para

2.7.1 message

message 参数能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。格式:#pragma message("Text")。当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。举例:

#ifdef _X86
#pragma message("_X86 macro activated!")
#endif

2.7.2 code_seg

格式:#pragma code_seg(["section-name"[, "section-class"]]) 它能够设置程序中函数代码存放的代码段,开发驱动程序的时候会使用到。

2.7.3 once

这个比较常用,主要是用在头文件最开始加入这条指令保证头文件只被引入编译一次。和#ifndef那个类似,但是这个有兼容性问题,有些编译器不支持,有些已经支持的也在考虑是否继续支持。
剩下的,等我用到了,再补充具体解释吧。也可以参考百度百科了。

2.7.4 hdrstop

2.7.5 resource

2.7.6 warning

2.7.7 comment

2.7.8 disable

2.7.9 data_seg

2.7.10 region

3. 引用

  1. C preprocessor directives – #include, #define, #undef and conditional directives
  2. C语言预处理指令总结
  3. Stringize (#) and token pasting (##) operator in C language
  4. C Language: Preprocessor Directives
  5. #pragma