美文网首页
预处理阶段能做什么:宏定义和条件编译

预处理阶段能做什么:宏定义和条件编译

作者: Drew_MyINTYRE | 来源:发表于2021-06-19 15:08 被阅读0次

    预处理指令只有十来个,实在是少得可怜。而且,常用的也就是 #include、#define、#if,所以很容易掌握。预处理指令都以符号“#”开头,这个你应该很熟悉了。但同时你也应该意识到,虽然都在一个源文件里,但它不属于 C++ 语言,它走的是预处理器,不受 C++ 语法规则的约束。

    你在写预处理代码的时候,可以参考这个格式。

    #                              // 预处理空行
    #if __linux__                  // 预处理检查宏是否存在
    #   define HAS_LINUX    1      // 宏定义,有缩进
    #endif                         // 预处理条件语句结束
    #                              // 预处理空行
    

    在写头文件的时候,为了防止代码被重复包含,通常要加上“Include Guard”,也就是用“#ifndef/#define/#endif”来保护整个头文件,建议你在所有头文件里强制使用,像下面这样:

    #ifndef _XXX_H_INCLUDED_
    #define _XXX_H_INCLUDED_
    
    ...    // 头文件内容
    
    #endif // _XXX_H_INCLUDED_
    

    宏定义(#define/#undef)

    宏的展开、替换发生在预处理阶段,不涉及函数调用、参数传递、指针寻址,没有任何运行期的效率损失,所以对于一些调用频繁的小代码片段来说,用宏来封装的效果比 inline 关键字要更好,因为它真的是源码级别的无条件内联。

    #define ngx_tolower(c)      ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
    #define ngx_toupper(c)      ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
    
    #define ngx_memzero(buf, n)       (void) memset(buf, 0, n)
    

    宏是没有作用域概念的,永远是全局生效。对于一些用来简化代码、起临时作用的宏,最好是用完后尽快用“#undef”取消定义,避免冲突的风险。

    #define CUBE(a) (a) * (a) * (a)  // 定义一个简单的求立方的宏
    
    cout << CUBE(10) << endl;        // 使用宏简化代码
    cout << CUBE(15) << endl;        // 使用宏简化代码
    
    #undef CUBE                      // 使用完毕后立即取消定义
    

    另一种做法是宏定义前先检查,如果之前有定义就先 undef,然后再重新定义:

    #ifdef AUTH_PWD                  // 检查是否已经有宏定义
    #  undef AUTH_PWD                // 取消宏定义
    #endif                           // 宏定义检查结束
    #define AUTH_PWD "xxx"           // 重新宏定义
    

    虽然不少人认为,定义常量更应该使用 enum 或者 const,但我觉得宏定义毕竟用法简单,也是源码级的真正常量,而且还是从 C 继承下来的传统,用在头文件里还是有些优势的。

    #define MAX_BUF_LEN    65535
    #define VERSION        "1.0.18"
    

    条件编译(#if/#else/#endif)

    你必须知道的一个宏是“__cplusplus”,它标记了 C++ 语言的版本号,使用它能够判断当前是 C 还是 C++,是 C++98 还是 C++11。你可以看下面这个例子

    #ifdef __cplusplus                      // 定义了这个宏就是在用C++编译
        extern "C" {                        // 函数按照C的方式去处理
    #endif
        void a_c_function(int a);
    #ifdef __cplusplus                      // 检查是否是C++编译
        }                                   // extern "C" 结束
    #endif
    
    #if __cplusplus >= 201402                // 检查C++标准的版本号
        cout << "c++14 or later" << endl;    // 201402就是C++14
    #elif __cplusplus >= 201103              // 检查C++标准的版本号
        cout << "c++11 or before" << endl;   // 201103是C++11
    #else   // __cplusplus < 201103          // 199711是C++98
    #   error "c++ is too old"               // 太低则预处理报错
    #endif  // __cplusplus >= 201402         // 预处理语句结束
    

    条件编译还有一个特殊的用法,那就是,使用“#if 1”“#if 0”来显式启用或者禁用大段代码,要比“/* … */”的注释方式安全得多,也清楚得多,这也是我的一个“不传之秘”。

    #if 0          // 0即禁用下面的代码,1则是启用
      ...          // 任意的代码
    #endif         // 预处理结束
    
    #if 1          // 1启用代码,用来强调下面代码的必要性
      ...          // 任意的代码
    #endif         // 预处理结束
    

    相关文章

      网友评论

          本文标题:预处理阶段能做什么:宏定义和条件编译

          本文链接:https://www.haomeiwen.com/subject/bjzryltx.html