C语言进阶:预处理命令(二)

三、预定义宏

预定义宏是指不需要用户使用#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已定义,预处理器会跳过整个块,避免重复定义。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
平安贷款庞小梅的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容