三、预定义宏
预定义宏是指不需要用户使用#define定义就可以使用的特殊宏名称,通常是由预处理器提前定义好的。预定义宏名称是由 _ _ 开头和结尾,中间是大写字符。例如ANSI C标准中的五个预定义宏:
__LINE__:当前编译代码的行号。__FILE__:当前源程序文件的名称。__DATE__:当前源程序创建日期。__TIME__:当前源程序创建时间。__STDC__:判断当前编译器是否是ANSI C。如果是返回 1 。
这些定义在实际开发中,结合条件编译可以实现跨平台的效果。比如,如果当前编译器不是ANSI C,只支持一部分预定义的宏名称等等。
四、文件包含
在C语言编程中,#include是一个预处理指令,用于将其他源文件或头文件的内容包含到当前源文件中。最常见的用法是包含C标准库提供的头文件,例如 stdio.h(标准输入输出)、string.h(字符串处理)和 math.h(数学函数)等。除此之外,开发者也可以根据需要包含自己定义的头文件(通常以 .h为扩展名),以便在多个源文件中共享代码和声明。
在实际的程序开发中,通常会将以下内容集中存放在头文件(.h 文件)中,以便通过 #include指令进行包含和复用:
宏定义(使用 #define定义的常量或宏函数)结构体、联合体与枚举的声明使用 typedef进行的类型重定义函数的声明(尤其是那些在多个源文件中使用的函数)全局变量的声明(注意:这些变量不应使用 static修饰,否则其作用域仅限于当前文件)
通过合理地使用头文件和 #include指令,可以提高代码的模块化程度,增强可维护性与复用性。
通常情况下,使用 #include 指令,被包含的头文件有以下两种写法:
#include "stdio.h"
#include <stdio.h>
这两种写法的区别如下所示:
尖括号<>:这是标准方式,系统直接去存放C库函数的目录下寻找要包含的头文件。双引号””:这种写法会使系统先在用户当前目录下查找要包含的文件,没找到就将搜索目标转向库函数所在目录。
因此,在实际开发中,为提升搜索效率,使用标准库函数时使用<>,而包含自主开发的头文件时使用双引号(双引号内可以使用绝对路径和相对路径)。
注意点:
一个 #include 指令只能包含一个文件文件包含可以嵌套使用,即被包含的文件中可以包含其他文件
五、条件编译
在 C 语言中,还提供了条件编译的功能,也就是说当满足某个条件时,编译器才会编译某个代码块。如下所示:
#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7
if (unlikely(Py_SIZE(x) < 0)) {
goto raise_neg_overflow;
}
#else
{
int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
if (unlikely(result < 0))
return (int) -1;
if (unlikely(result == 1))
goto raise_neg_overflow;
}
#endif
这段代码的含义如下:
如果代码 正在 CPython 中编译(CYTHON_COMPILING_IN_CPYTHON)并且 Python 版本低于 3.12(PY_VERSION_HEX < 0x030C00A7),那么采用第一种(旧)方式来检测某个值是否为负数;否则(即:要么不是在 CPython 中编译,要么是 Python 3.12 或更高版本),就采用第二种(新)方式来判断。
可见,使用这样的技巧,可以实现跨版本,跨平台的效果。
5.1、#if #elif #else #endif 指令
#if —— 条件为真,则编译下面代码。
#if 常量表达式
// 如果表达式为真(非零),则编译这部分代码
#endif
“常量表达式” 必须是 能在预处理阶段求值的常量表达式,通常用到的是宏(比如 defined()或自定义的 #define宏)。不能使用变量,只能使用宏和字面量。
例如:
#define DEBUG 1
#if DEBUG
printf("已开启DEBUG
");
#endif
#if #elif #elif…….多条件分支
#if 表达式1
// 如果表达式1为真
#elif 表达式2
// 如果表达式1为假,表达式2为真
#elif 表达式3
// ...
#else
// 所有表达式都为假
#endif
例如:
#define VERSION 3
#if VERSION == 2
printf("第二版
");
#elif VERSION == 3
printf("第三版
");
#else
printf("不知道
");
#endif
注意点:
#else是当前面的所有条件都不成立时执行#endif是结束标志,必须要有,否则预处理器会报错
5.2、条件编译指令集合
| 指令 | 说明 | 示例 |
| #if 常量表达式 | 如果常量表达式为真(非零),则编译后续代码 | #if DEBUG == 1 |
| #elif 常量表达式 | else if,用于多条件判断 | #elif PLATFORM_LINUX |
| #else | 如果前面所有条件均不成立,则编译此部分代码 | #else |
| #endif | 结束一个 #if…条件编译块 | #endif |
| #ifdef 宏名 | 如果 宏已定义(不管值是什么),则编译代码 | #ifdef DEBUG |
| #ifndef 宏名 | 如果 宏未定义,则编译代码 | #ifndef HEADER_H |
| #define 宏名 [值] | 定义一个宏(可用于条件判断) | #define DEBUG 1 |
| #undef 宏名 | 取消定义一个宏 | #undef DEBUG |
| #defined(宏名) | 用在 #if中判断某个宏是否已定义 | #if defined(WIN32) |
使用他们编译跨平台应用:
#ifdef _WIN32
// Windows 专用代码
#include <windows.h>
#elif __linux__
// Linux 专用代码
#include <unistd.h>
#else
// 其他平台
#endif
_WIN32是 Windows 系统下几乎总是定义的宏。__linux__是 Linux 系统下定义的宏。
进行头文件保护
#ifndef MYLIB_H
#define MYLIB_H
// 声明函数、结构体等
#endif // MYLIB_H
第一次 #include “mylib.h”时,MYLIB_H未定义,所以会定义它并包含内容;第二次再 #include时,MYLIB_H已定义,预处理器会跳过整个块,避免重复定义。
















暂无评论内容