目录
- C 预处理
- 1 #define
- 1)基本语法
- 2)常量宏
- 3)表达式宏
- 4)语句宏
- 5)代码块宏
- 6)总结
- 2 #include
- 1)基本语法
- 2)头文件的作用
- 3)头文件的保护
- 4)示例
- 5)头文件设计原则
- 3 #if、#ifdef、ifndef、#elif、#else、#endif
- 1)`#if` 指令
- 2)`#ifdef` 和 `#ifndef` 指令
- 3)`#elif` 指令
- 4)`#else` 指令
- 5)`#endif` 指令
C 预处理
预处理是C语言编译过程的第一个阶段,它主要由预处理器(Preprocessor)完成。
预处理指令以#
符号开头,不是C语句,是在编译之前由预处理器处理的命令。
常用预处理指令:#define, #include, #if…#elif…#else…#endif等
1 #define
#define
是C语言预处理器提供的指令,用于定义宏。
宏是一种简单的文本替换机制,在编译前将源代码中的宏名替换为对应的宏值或代码片段。
1)基本语法
#define 宏名 替换文本
宏名: 通常是一个标识符,命名规则与变量相同。
替换文本: 可以是常量、表达式、语句或代码块,用于替换宏名。
2)常量宏
#define PI 3.14159
这是最简单的宏定义,将 PI
定义为一个常量,所有出现 PI
的地方都会被替换为 3.14159
。
3)表达式宏
#define SQUARE(x) ((x) * (x))int result = SQUARE(5); // 编译时会被替换为 int result = ((5) * (5));
这个宏用于计算传入参数的平方,是一个带有表达式的宏。
4)语句宏
#define PRINT_MESSAGE() printf("Hello, World!\n")PRINT_MESSAGE(); // 编译时会被替换为 printf("Hello, World!\n");
这个宏定义了一个打印消息的语句,可以在代码中使用 PRINT_MESSAGE()
来替代实际的 printf
语句。
5)代码块宏
#define MAX(a, b) \({ \typeof(a) _a = (a); \typeof(b) _b = (b); \_a > _b ? _a : _b; \})int max_value = MAX(10, 20); // 编译时会被替换为 int max_value = ({ typeof(10) _a = (10); typeof(20) _b = (20); _a > _b ? _a : _b; });
这是一个用于获取两个数中较大值的宏,使用了代码块,其中包含了局部变量的定义和比较操作。
6)总结
宏虽然有其用处,但在实际编程中应慎重使用,确保宏的使用不会导致代码可读性下降或产生难以维护的问题。
在实际编程中,尽量使用函数代替宏,以提高代码的可读性和维护性。
2 #include
#include
是C语言预处理器指令之一,用于包含外部文件的内容。
1)基本语法
#include <header_file.h>
#include "header_file.h"
-
尖括号
<>
: 用于包含系统提供的头文件,通常是标准C库的头文件。 -
双引号
""
: 用于包含用户自定义的头文件,通常是项目内部的头文件。 -
当使用尖括号
<>
时,编译器通常会在系统的标准头文件目录中搜索头文件。 -
当使用双引号
""
时,编译器会先在当前源代码文件所在的目录搜索,然后在系统的标准头文件目录中搜索。
2)头文件的作用
头文件是C语言中一种用于组织代码的文件。
头文件中可以包含变量、函数、结构体、宏等的声明和定义。
这样,在其他源文件中,只需要包含头文件就可以使用这些声明和定义,而无需重新编写。
// sample.h// 函数声明
void printMessage();// 宏定义
#define PI 3.14159// 结构体声明
struct Point {int x;int y;
};
3)头文件的保护
为了防止头文件被多次包含,常常在头文件的开头和结尾使用预处理器指令进行保护。
// sample.h
#ifndef SAMPLE_H
#define SAMPLE_H// 头文件内容#endif // SAMPLE_H
这样,即使同一个头文件被多次包含,也只会在第一次包含时展开。
#ifndef
和#endif
之间的部分是头文件的实际内容。#ifndef
是条件编译指令,用于判断是否定义了宏SAMPLE_H
,如果未定义,则表示这是第一次包含,执行其中的内容。如果已定义该宏,说明头文件已包含过,不再执行后面内容。#define
用于定义宏SAMPLE_H
。#endif
表示条件编译结束。
4)示例
// sample.h#ifndef SAMPLE_H
#define SAMPLE_H// 函数声明
void printMessage();// 宏定义
#define PI 3.14159// 结构体声明
struct Point {int x;int y;
};#endif // SAMPLE_H
// main.c#include <stdio.h>
#include "sample.h"int main() {printMessage();printf("Value of PI: %f\n", PI);struct Point p;p.x = 10;p.y = 20;return 0;
}
上述示例展示了一个简单的头文件,其中包含了函数声明、宏定义和结构体声明。在主程序中使用 #include
引入了这个头文件,使得主程序可以使用头文件中定义的内容。
5)头文件设计原则
包含必要内容: 头文件应该只包含其他源文件需要的声明、定义和结构体等内容,避免将不必要的信息暴露给其他文件。
保持简洁: 头文件应该保持简洁,避免包含过多的内容。过大的头文件可能会导致编译时间的增加。
使用预处理器保护: 使用条件编译指令(#ifndef
, #define
, #endif
)来防止头文件被多次包含。
3 #if、#ifdef、ifndef、#elif、#else、#endif
#if
、#ifdef
、#ifndef
、#elif
、#else
和 #endif
是C语言中的预处理指令,用于条件编译。
汇总:
#if
用于基于条件,来判断是否编译特定的代码块。#ifdef
和#ifndef
分别用于检查一个宏是否已经被定义或尚未被定义。#elif
允许在前面的条件不满足时,指定一个新的条件。#else
用于在前面的条件不满足时执行另一段代码。#endif
用于结束条件编译块,必须与之前的#if
、#ifdef
、#ifndef
、#elif
或#else
配对使用。
1)#if
指令
#if
指令用于基于条件判断是否编译特定的代码块。条件为一个常量表达式,如果该表达式为真(非零),则执行后面的代码块。
示例:
#define DEBUG 1#if DEBUGprintf("Debug mode is enabled\n");
#endif
在这个例子中,如果 DEBUG
宏被定义为非零值,就会编译 printf
语句。
2)#ifdef
和 #ifndef
指令
#ifdef
(if defined)指令用于检查一个宏是否已经被定义。
#ifndef
(if not defined)指令用于检查一个宏是否尚未被定义。
#ifdef MACRO// 如果 MACRO 宏已经被定义,执行这部分代码
#endif#ifndef MACRO// 如果 MACRO 宏尚未被定义,执行这部分代码
#endif
示例:
#define DEBUG#ifdef DEBUGprintf("Debug mode is enabled\n");
#endif
在这个例子中,如果 DEBUG
宏被定义,就会编译 printf
语句。
3)#elif
指令
#elif
指令是 #else
和 #if
的组合,用于指定一个新的条件,如果前面的 #if
或 #elif
条件不满足,且当前条件为真,则执行后面的代码块。
示例:
#define DEBUG_LEVEL 2#if DEBUG_LEVEL == 1printf("Debug level is 1\n");
#elif DEBUG_LEVEL == 2printf("Debug level is 2\n");
#elseprintf("Unknown debug level\n");
#endif
在这个例子中,根据 DEBUG_LEVEL
的值不同,选择性地编译不同的 printf
语句。
4)#else
指令
#else
指令用于在前面的条件均不满足时,执行另一段代码。
示例:
#define DEBUG#ifdef DEBUGprintf("Debug mode is enabled\n");
#elseprintf("Debug mode is disabled\n");
#endif
在这个例子中,如果 DEBUG
宏被定义,就会编译第一个 printf
语句,否则编译第二个 printf
语句。
5)#endif
指令
#endif
指令用于结束条件编译块,必须与之前的 #if
、#ifdef
、#ifndef
、#elif
或 #else
配对使用。
示例:
#define DEBUG#ifdef DEBUGprintf("Debug mode is enabled\n");
#elseprintf("Debug mode is disabled\n");
#endif
在这个例子中,#endif
结束了条件编译块。