预定义宏

为了让我们写的软件能够在各种平台运行,我们经常需要写一些跨平台的 C/C++ 代码来处理不同处理器架构、不同操作系统、不同语言版本、不同编译器之间的差异。它们之间的差异可谓千差万别:类似的功能 API 接口不同,一些系统不提供某些需要的 API,16 位、32 位、64 位系统字长不一样,以及为了兼容古老的语言版本而必须在代码中手动添加特性,甚至是大小端无关代码。

比如,很多时候我们需要一个通用指针类型,void *,但是在 ANSI C 之前没有提供这种的 void 指针,所以我们需要自己定义。

#if defined(__STDC__) || defined(__cplusplus) || defined(_MSC_EXTENSIONS)
typedef void * t_pointer;
#else
typedef char * t_pointer;
#endif

又比如说 VS C++ 4.2 版本添加了编译指令可以让每个包含文件只包含一次来提高编译速度。

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

与操作系统相关的宏最常见的应该是 __linux__ _WIN32 __APPLE__ __FreeBSD__ 等标识是那个操作系统的宏。与语言相关的宏是 __STDC____cplusplus

上面提到的所有的宏都是预定义在编译器中的,这些宏指示目标处理器、操作系统、语言特性和编译器版本。跨平台代码需要使用 #if/#endif 来处理 OS 特定、编译器特定、处理器特定的代码。预定义的宏分为三大类:

  • 标准预定义宏:这些宏由语言标准定义,在任何支持该语言标准的编译器实现都有定义。具体参见:Standard Predefined Macros
  • 通用预定义宏:就是编译器的扩展预定义宏,在特定编译器的任何平台实现上都有定义。Common Predefined Macros
  • 系统特定预定义宏:标识系统和处理器的宏,在所有平台都不一样。

在 GCC 中,所有的预定的宏都以 __ 开头或者 _E(单下划线和大写字母),所以应用程序不应该以这种方式定义自己的宏。我们可以通过调用 gcc -dM -E -x c /dev/null 或者 g++ -dM -E -x c++ /dev/null 来了解 GNU 编译器在当前平台中的所有预定义宏,此方法对于 clang clang++ cc aCC 是一样的, 提供不同的参数 -m64 -mwin32 可以得到不同目标平台下的预定义宏。文件 defines script 还在此基础上提供了类型大小信息。

VS C++ 的预定义宏在 MSDN 页面 Predefined Macros 可以找到完整的列表。

一些国外同学收集了大量的编译预定义宏,包括 标准预定义宏通用预定义宏标准 C 库宏系统特定预定义宏 以及 处理器架构预定义宏

通过以上信息,我们在阅读和写跨平台代码时就更加精准。 (完)