如何获取预处理器定义了哪些宏?为什么预处理条件编译不能解析sizeof表达式?

时间:2024-11-13 11:38:12

预处理

预处理本质不是编译,编译型语言为了提前做文本替换、条件编译。更高级编程语言一般不需要预处理,而是引入形如using/import代表依赖的模块,编译器会自动检查代码调用的接口与引入模块的匹配。

预处理的位置

  • 预处理可以在代码的任意位置,只要不在字符串””以内。

头文件包含

  • 典型代表是C/C++ #include, 仅仅是文件内容插入,ObjC用#import改进#include, 可自动判断是否有重复包含。
    • 更多参考:头文件包含

  • 汇编语言就有macro的概念。和头文件包含相同之处是文本替换,不过就是文本替换。
  • Pascal用BEGIN和END作为函数的开始和结束,C语言可以把BEGIN和END分别#define成{和},看起来就像是Pascal代码。
  • 内核代码一般会大量使用宏,为支援高效、可适配的代码。

条件编译

  • C语言虽然说平台无关,但不同平台架构差异需要用不同条件编译。
  • ifdef在20世纪70年代就存在,#if defined是80年代C标准化过程中加进来,后者可支持超过一个宏定义判断,*度更高。早期的C语言是不支持嵌套包含同一个头文件的,后来引入了#ifndef/#define/#endif做去重处理。
  • C#确实很调皮,即使定位是跨平台中间件编程语言,依然可以用宏。但有很大约束,#define只能放在文件最开头,没有#ifdef, 只有#if.
  • 宏和函数可以同名,为了避免误用到宏,#undef可以去掉宏定义。

预处理器

  • C语言预处理器被称为cpp.

如何获取预处理器定义了哪些宏?

  • GCC/G++ 提供了-dM选项可抓取预处理器预定义了哪些宏,可方便程序员了解差异。
    对于C代码,用gcc -dM -E -x c /dev/null
    对于C++, 用g++ -dM -E -x c++ /dev/null
    - 可以看到形如#define __amd64__ 1之类的定义,可判断是64位还是32位。
例如:
......
#define __x86_64 1
#define __INT_FAST64_TYPE__ long int
#define __FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64
#define __DBL_MIN__ ((double)2.22507385850720138309023271733240406e-308L)
#define __FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128
#define __FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x
#define __SIZEOF_POINTER__ 8
#define __SIZE_TYPE__ long unsigned int
#define __LP64__ 1
......
  • Clang类似GCC,但不能用/dev/null作为参数。
    clang -dM -E -x c xxx.c
  • MSVC 提供/PD /Zc:preprocessor参数选项可以输出所有预定义的宏。
    cl /PD /Zc:preprocessor xxxxx.c
    例如:
    #define _MSC_EXTENSIONS 1
    #define _MSC_BUILD 0
    #define _IS_ASSIGNABLE_NOCHECK_SUPPORTED 1
    #define _M_IX86_FP 2
    #define _MT 1
    #define _MSVC_WARNING_LEVEL 1L
    #define STDC_HOSTED 1
    #define _MSC_FULL_VER 193030706
    #define _MSC_VER 1930
    #define _MSVC_TRADITIONAL 0
    #define _M_IX86 600
    #define TEST_STDIO 11
    #define _MSVC_EXECUTION_CHARACTER_SET 936
    #define _INTEGRAL_MAX_BITS 64
    #define _WIN32 1

为什么预处理条件编译不能解析sizeof表达式?

虽然sizeof表达式宣称是编译期常量,依然不能被条件编译处理,因为预处理根本无法解析稍微复杂的表达式,包括sizeof表达式。有人会说,既然sizeof都是常量,为何不让预处理也能使用呢?

  • 根源在于sizeof表达式并非很简单,可以是sizeof(int), 也可以是sizeof(a + 1.2), 预处理器被设计的比较简单,即使只加入简单的C语言类型sizeof处理,也没办法处理后者这种复杂的表达式,如果需要,也会让预处理器设计更复杂。总结就是:sizeof表达式可能涉及到复杂的类型推导和计算,无法在预处理阶段计算出结果。
  • PS: sizeof既是一个关键字,也是一个运算符。


若文章对您有帮助,欢迎关注。助您在编程路上越走越好!

微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是程序员小迷(致力于C、C++、Java、Kotlin、Android、iOS、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。