我继承了一个用C和C ++编写的相当大而复杂的软件包,它使用gcc在Mac OSX Lion上成功构建:
$> gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
我的任务是使用命令行工具附带的默认Mac编译器在OSX Mavericks上构建相同的软件包。
$> gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix
OSX Lion构建完成但OSX Mavericks构建失败并出现以下错误:
Undefined symbols for architecture x86_64:
"_MRIsetVoxVal", referenced from ...
我已使用以下代码块跟踪错误源到头文件:
#ifdef __cplusplus
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
#else
inline float MRIgetVoxVal(const MRI *mri, int c, int r, int s, int f);
inline int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
inline void MRIdbl2ptr(double v, void *pmric, int mritype);
inline double MRIptr2dbl(void *pmric, int mritype);
#endif
如果我通过简单地删除if else
和inline
语句来修改上面的代码块,使其看起来如下所示,那么构建在两个平台上完成:
float MRIgetVoxVal( const MRI *mri, int c, int r, int s, int f);
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval);
void MRIdbl2ptr(double v, void *pmric, int mritype);
double MRIptr2dbl(void *pmric, int mritype);
因此,在OSX Lion上,似乎会触发ifdef __cplusplus
语句,从而产生所需的行为。在小牛队上,else
语句被触发,最终导致错误。
请原谅我这是一个非常基本的问题,但C和C ++不属于我的专业领域。这里发生了什么? #ifdef __cplusplus
甚至意味着什么,为什么一个版本的gcc被它触发而另一个版本没有?
答案 0 :(得分:1)
在编译器正确执行之前,preprocessor执行许多任务。以哈希('#')开头的行是预处理程序指令,包括文件包含(#include <stdio.h>
),宏变量定义(#define DEBUG 1
),宏函数定义(#define LENGTH(array) (sizeof(array) / sizeof(0[array]))
)等任务和条件编译(#if ... #endif
),如代码片段中所示。
宏允许在编译时通过将宏名称替换为其值来重写源代码。它们是实现许多任务的早期方法,但基本上不尊重C / C ++语法,因此已经通过(例如)inline
说明符,模板,{ {3}}类和trait,更好地尊重语法&amp;语义。
在头文件中定义了一些宏(有些例子,请参见“limits.h”,用numeric_limits
代替C ++)。其他由预处理器定义; __cplusplus
是其中之一,应该在编译器处理C ++文件时定义(由文件扩展名或可能的命令行参数确定,例如GCC的'-x')。您可以使用'-dD'或'-dM'选项(如gcc nullptr
页面和man中所述)将gcc定向到列出宏,同时使用'-E'选项停止经过预处理。 Nadeau Software Consulting发布了有关如何为各种编译器列出online docs的提示。根据它,clang应该接受与gcc相同的论点,所以在Mavericks上尝试以下内容:
gcc -dM -E -x c++ /dev/null | less
C ++标准(C ++ 11中的第16.8节)要求 __cplusplus
。 Lion和Mavericks gcc之间的一个重要区别是后者使用clang前端,而前者使用GCC。对“clang __cplusplus”的网络搜索表明clang应该支持它,所以如果没有定义__cplusplus
,那么clang可能会将文件编译为C.你可以尝试使用'-x c ++'选项来强制C ++。您也可以尝试使用'-std = gnu ++ 11'选项,但我怀疑它不会产生任何影响,因为它旨在支持非标准predefined macros,而不是提供完整的GCC兼容性。