文章目录
一、C 预处理器
C预处理器(C Preprocessor)是C语言编译器的一个组成部分,它负责在编译过程之前处理源代码。预处理器的主要任务是扫描源代码,对其中的预处理指令进行解释和替换,然后输出一个修改后的源代码文件,这个文件随后会被C编译器处理。
C预处理器指令都以#
符号开始,并且必须在每行的开头(前面不能有任何空格或制表符)。下面是一些常见的C预处理器指令:
#include
:包含另一个文件的内容。有两种形式:
#include <filename>
:从标准库路径中搜索filename
文件。#include "filename"
:首先在当前目录或指定的目录中搜索filename
文件,如果找不到,再从标准库路径中搜索。
#define
:定义一个宏。例如,#define PI 3.14159
会将源代码中所有的PI
替换为3.14159
。#undef
:取消之前由#define
定义的宏。#if
、#elif
、#else
、#endif
:条件编译指令,允许你根据条件包含或排除代码段。#ifdef
、#ifndef
:检查某个宏是否已经被定义。#line
:改变编译器的行号和文件名,这在创建报告错误和警告的宏时很有用。#error
和#warning
:生成编译时错误或警告。#pragma
:这是预处理器的扩展指令,它允许向编译器发送特定的指令,这些指令可能因编译器而异。
预处理器指令在源代码被编译之前执行,并且通常不会生成目标代码。它们主要用于处理代码中的常量和宏定义,以及控制代码中的条件编译。预处理器指令在大型项目中特别有用,因为它们可以帮助组织和管理代码,减少冗余,并允许在不同的编译配置之间切换。
二、C 预处理器指令
C预处理器指令在C语言编程中非常有用,它们允许你在编译前对源代码进行各种处理。以下是一些C预处理器指令的详细代码案例:
1. #include
// 使用尖括号包含标准库头文件
#include <stdio.h>
// 使用双引号包含用户定义的头文件
#include "myheader.h"
int main() {
printf("Hello, World!\n"); // 假设myheader.h中没有任何与此冲突的声明
return 0;
}
2. #define
和 #undef
#define PI 3.14159
int main() {
double circle_area = PI * 5 * 5; // 使用PI宏替换为3.14159
printf("Circle area is %.2f\n", circle_area);
#undef PI // 取消PI的宏定义
// 下面的代码会导致编译错误,因为PI已经被undef了
// double another_area = PI * 3 * 3;
return 0;
}
3. #if
、#elif
、#else
和 #endif
#define DEBUG 1
int main() {
#if DEBUG
printf("Debugging is on\n");
#elif defined(TEST)
printf("Testing is on\n");
#else
printf("Neither debugging nor testing is on\n");
#endif
return 0;
}
在这个例子中,DEBUG
被定义为1,所以程序会输出"Debugging is on"。如果你注释掉#define DEBUG 1
并取消注释#define TEST 1
,程序会输出"Testing is on"。
4. #ifdef
和 #ifndef
// 假设没有定义MY_FEATURE
int main() {
#ifdef MY_FEATURE
printf("My feature is enabled\n");
#else
printf("My feature is disabled\n");
#endif
#ifndef MY_FEATURE
printf("Again, my feature is disabled\n");
#endif
return 0;
}
在这个例子中,因为MY_FEATURE
没有被定义,所以两个printf
都会执行,分别输出"My feature is disabled"和"Again, my feature is disabled"。
5. #line
这个指令通常用于调试目的,允许你改变编译器的行号和文件名。在实际编程中较少直接使用。
6. #error
和 #warning
#if !(defined(WIN32) || defined(__unix__) || defined(__APPLE__))
#error Unsupported platform!
#endif
int main() {
// ...
return 0;
}
在这个例子中,如果代码不是在Windows、Unix或Apple平台上编译的,预处理器会生成一个错误消息"Unsupported platform!"。
7. #pragma
#pragma
指令是特定于编译器的,用于提供编译器的特定指令。例如,在某些编译器中,你可以使用#pragma once
来确保头文件只被包含一次。
// myheader.h
#pragma once
// ... 其余的头文件内容
注意:#pragma once
并不是C语言标准的一部分,但它在许多现代编译器中都被支持。在编写跨平台代码时,你应该避免依赖非标准的预处理器指令。
三、C 预定义宏
C语言预处理器提供了一些预定义的宏,这些宏在编译时总是可用的,而无需显式地定义它们。这些预定义宏通常用于获取关于编译器和编译环境的信息。下面是一些常见的C预定义宏及其详细代码案例:
1. __LINE__
这个宏被预处理器替换为当前源代码行号(十进制整数)。
#include <stdio.h>
int main() {
printf("This is line %d\n", __LINE__);
printf("This is line %d\n", __LINE__ + 1); // 下一行
return 0;
}
2. __FILE__
这个宏被预处理器替换为当前源代码文件的名称(带路径或不带路径,取决于编译器)。
#include <stdio.h>
int main() {
printf("This file is %s\n", __FILE__);
return 0;
}
3. __DATE__
这个宏被预处理器替换为编译时的日期,格式为 “Day Mon DD HH:MM:SS YYYY\n”。
#include <stdio.h>
int main() {
printf("Compilation date: %s", __DATE__);
return 0;
}
4. __TIME__
这个宏被预处理器替换为编译时的时间,格式为 “HH:MM:SS”。
#include <stdio.h>
int main() {
printf("Compilation time: %s", __TIME__);
return 0;
}
5. __STDC__
如果编译器遵循C语言标准,此宏被设置为1。否则,它未定义。
#include <stdio.h>
int main() {
#if __STDC__
printf("This is a C Standard compliant compiler\n");
#else
printf("This is not a C Standard compliant compiler\n");
#endif
return 0;
}
6. __STDC_VERSION__
如果编译器遵循C99或更新的标准,此宏将被设置为一个表示C标准版本的整数。例如,对于C99,它是199901L。如果不支持C99或更新的标准,此宏可能未定义。
#include <stdio.h>
int main() {
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
printf("This compiler supports C99 or newer\n");
#else
printf("This compiler does not support C99 or newer\n");
#endif
return 0;
}
7. __cplusplus
当代码在C++编译器中编译时,此宏被设置为1。在C编译器中,它未定义。
注意:这个宏主要用于区分C和C++代码,而不是在C语言代码中使用。
8. 编译器特定的预定义宏
除了上述标准的预定义宏之外,不同的编译器还可能提供自己的预定义宏。例如,GCC提供了__GNUC__
、__GNUC_MINOR__
、__GNUC_PATCHLEVEL__
等宏来表示GCC的版本信息。
四、相关链接
- Visual Studio Code下载地址
- Sublime Text下载地址
- 「C系列」C 简介
- 「C系列」C 基本语法
- 「C系列」C 数据类型
- 「C系列」C 变量及常见问题梳理
- 「C系列」C 常量
- 「C系列」C 存储类
- 「C系列」C 运算符
- 「C系列」C 判断/循环
- 「C系列」C 函数
- 「C系列」C 作用域规则
- 「C系列」C 数组
- 「C系列」C enum(枚举)
- 「C系列」C 指针及其应用案例