宝玛科技网
您的当前位置:首页C语言预处理知识

C语言预处理知识

来源:宝玛科技网


一、预处理的定义

在C语言学习中,我们通过预处理这节课了解到;我们用C语言直接写出来的代码是不能被计算机进行识别的,需要被翻译为二进制语言才能被计算机收到并执行;这个过程就叫做翻译。

翻译过程一般有以下几步:

预处理:头文件展开,去注释,宏替换,条件编译等。

编译:将C语言翻译成汇编语言

汇编:将汇编语言转化为可重定向目标文件(可被链接,已经是二进制,但不是可执行文件)

链接:自身程序+库文件进行关联,形成可执行程序

二、头文件展开

在编写C程序时,我们常常需要包括许多头文件;为了达到这个目的,我们常常会使用预处理指令#include来实现。在程序进行预处理时,就会将头文件的内容复制到我们的代码文件中,以便进行函数及变量的调用。

常见的头文件预处理代码:

#include<stdio.h>

#include<string.h>

#include<math.h>

对于#include的使用,有两种实现形式:

1.使用尖括号<>,编译器会从系统路径寻找头文件

三、去注释

在写C语言程序中我们常常会因为便于代码理解而增加一点注释,这些注释在预处理阶段会进行替换;编译器会将这些注释全部更改为空格。

注:在C语言中,我们有两种写注释的写法//和/**/

四、宏替换

4.1什么是宏

在C语言中,一般使用#define来定义宏,这个命令允许把一个名称指定成为任何所需的文本,比如替换为一个常量值。定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会将其用定义时指定的文本替换掉。

代码示例:

#include <stdio.h>

#define PI 3.14159 #define SQUARE(x) ((x) * (x)) #define CIRCLE_AREA(r) (PI * SQUARE(r))

int main() { float radius = 5.0; printf("The area of a circle with radius %f is %f\n", radius, CIRCLE_AREA(radius)); return 0; }

在这个例子中,我们定义了三个宏:

PI 是一个代表圆周率的宏。

SQUARE(x) 是一个计算平方的宏。

CIRCLE_AREA(r) 是一个计算圆面积的宏,它使用了 PI 和 SQUARE(x) 宏。

在 main 函数中,我们使用CIRCLE_AREA 宏来计算半径为 5.0 的圆的面积,并打印结果。



4.2 宏的作用范围

源文件的任何地方都可以定义宏,与是否在函数内外无关,我们习惯将其写在最上方。宏的作用范围是从定义处往后都是有效的,之前无效。#undef可以提前结束宏的作用范围。例:

#include <stdio.h>

#define PI 3.14159 #define SQUARE(x) ((x) * (x)) #define CIRCLE_AREA(r) (PI * SQUARE(r))

int main() { float radius = 5.0; printf("The area of a circle with radius %f is %f\n", radius, CIRCLE_AREA(radius));



#undef PI
    // 在这里,PI 宏已经被取消定义
​
    // 尝试再次使用 PI 宏,将会导致编译错误
    // printf("PI is now: %f\n", PI); // 这行代码会导致编译错误
​
    return 0;
}

4.3.#和##

#和##是C语言中的预处理指令,它们只能在宏定义中使用。功能如下:

#:当在宏定义中出现使用#参数的形式,就是将参数的字面值转换为字符串。如#define STR(s) #s ,这个宏作用就是把s的字面值转为字符串常量。

##:如果出现aa##bb##cc这种使用双井号定义的宏,作用就是形成一个新的符号aabbcc,注意是符号而不是字符串。

代码示例:

#include <stdio.h>

#define PI 3.14159 #define SQUARE(x) ((x) * (x)) #define CIRCLE_AREA(r) (PI * SQUARE(r)) #define SUM_OF_SQUARES(x, y) (SQUARE(x) + SQUARE(y)) //SUM_OF_SQUARES宏使用#运算符来计算两个数的平方和 #define CONCAT(x, y) x##y //而CONCAT宏使用##运算符来拼接两个变量名。

int main() { float radius = 5.0; printf("The area of a circle with radius %f is %f\n", radius, CIRCLE_AREA(radius));

int a = 3, b = 4;
printf("The sum of squares of %d and %d is %d\n", a, b, SUM_OF_SQUARES(a, b));
​
int ab = CONCAT(a, b); // 定义了一个名为ab的整数变量,其值为34
printf("The value of ab is %d\n", ab);
​
return 0;
}



4.4宏替换与去注释

4.4宏替换与去注释

代码示例:

#include<stdio.h>
#define ABS // 
 int main() 
 { 
    ABS printf("hello");
 }

在这段代码中: 我们定义了一个ABS宏为//,并在主函数中使用了这个宏。我们不妨来猜测以下程序是否会输出hello字符串?

如果宏替换先于去注释,则会先将ABS替换为//,然后将printf语句注释掉,最后将注释变为空格,程序不会输出。 如果去注释先于宏替换,则会先将//后本行的内容作为注释改为空格,最后ABS宏相当于一个空格,宏替换时将ABS宏文本替换为空格,程序输出字符串hello。

运行结果:

结果输出为hello,说明去注释优先于宏替换进行。

五、条件编译

5.1条件编译的定义

一般情况下,源程序中所有的非注释行都需要参加编译。但是有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”。使用条件编译后,在预处理阶段,如果使用条件编译的代码块满足条件,则保留;如果不满足条件,则预处理器就会将代码裁剪,使其不会在后续阶段被编译。

C语言中常见的条件编译指令有#ifdef,#ifndef,#if,#else,#elif,#if defined(),#endif

5.2条件编译的使用

1.使用#ifdef#ifndef来检查宏是否已定义:

#ifdef DEBUG printf("Debug mode is enabled.\n"); 
#endif

#ifndef RELEASE printf("This is not a release build.\n");
 #endif

2.使用#if#elif#endif来根据常量表达式的值包含或排除代码块:

#define OPTION 1

#if OPTION == 1 printf("Option 1 is selected.\n"); 
#elif 
OPTION == 2 printf("Option 2 is selected.\n");
 #else printf("Invalid option.\n"); 
#endif

3.使用defined运算符来检查宏是否已定义:

#if defined(DEBUG) && defined(RELEASE) 
printf("Both debug and release modes are enabled. This is not allowed.\n");
 #elif 
defined(DEBUG) printf("Debug mode is enabled.\n");
 #elif defined(RELEASE)
 printf("Release mode is enabled.\n");
 #else printf("No special mode is enabled.\n");
 #endif

4.使用#undef来取消宏的定义:

#define DEBUG

#ifdef DEBUG printf("Debug mode is enabled.\n"); #undef DEBUG #endif

#ifdef DEBUG printf("This line will not be printed because DEBUG is undefined now.\n"); #endif

5.使用条件编译来包含不同平台的代码:

#ifdef _WIN32 
#include <windows.h>
 printf("This is a Windows system.\n");
 #elif APPLE #include <TargetConditionals.h>
 #if TARGET_OS_MAC printf("This is a Mac system.\n"); 
#endif 
#elif linux
 #include <unistd.h> 
printf("This is a Linux system.\n"); 
#else printf("Unknown operating system.\n"); 
#endif

5.3条件编译的作用

一个C工程可能有多个.c文件,而.c文件可能又包含多个.h文件,难免会出现头文件重复包含的现象。这将会导致大量重复的代码被拷贝至我们的文件中,后期编译时会大大降低效率。因此我们可以使用条件编译来防止头文件重复包含。

因篇幅问题不能全部显示,请点此查看更多更全内容