#ifdef 标记用于区分 gcc 和 g++ 编译器?

3

gcc编译C程序时将其视为C语言,编译C++程序时将其视为C++语言,因此需要在C++中使用'extern "C"'声明。而g++将C程序作为C++程序进行编译,将C++程序作为C++程序进行编译,因此在C++中不应使用'extern "C"'声明。

检查标准标志而没有任何输入时,两者是一样的。

 g++ -E -dM - </dev/null 
 gcc -E -dM - </dev/null 

产生相同的结果。

__GNUG__       is equivalent to testing (__GNUC__ && __cplusplus)

这句话的意思是,在 C++ 的源代码中,无法区分 gcc 和 g++。

下一句话的意思是,“这是什么魔法?”

#ifndef __MAGIC_G++_INCLUDE_FLAG_BUT_NOT_GCC__ 

如何包含extern "C"{声明?谢谢。

编辑:我意识到这是一个相当深奥的问题。测试需要在编译器版本上进行,而不是在源代码版本上进行。在两种情况下,测试都是在C++源代码列表内执行的(这就是为什么我提到GNUG不足够)。为了澄清,在测试main.cpp时:

#include <iostream>
int 
main( int argc, char **argv )
{
#ifdef __cplusplus  <<<< FIX THIS ONE
  std::cout << "I was compiled using g++" << std::endl;
#else
  std::cout << "I was compiled using gcc" << std::endl;
#endif 
}

当使用以下任一编译时:

 g++ main.cpp

或者

gcc main.cpp -lstdc++

在这两种情况下,似乎都会错误地输出"I was compiled using g++"。因此,在这种情况下,cplusplus不是正确的标志。我错了吗?或者什么是正确的标志?
编辑2: 谢谢大家。这里有一个例子供您参考。大型遗留系统"Alpha"主要用C编写,少量使用C++,并使用大型遗留源包"Charlie",该源包用C++编写。它使用g++系统进行编译和链接,并要求Charlie的头文件中没有extern "C"定义,否则它将拒绝链接。大型遗留系统"Bravo"主要用C编写,少量使用C++,也使用相同的遗留源包Charlie,用C++编写。它使用gcc系统进行编译和链接,并要求Charlie的头文件必须有extern "C"定义,否则它将拒绝链接。新系统"Delta"、"Echo"和"Foxtrot"也希望重用来自Charlie的部分源代码,使用的编译器尚不确定。正确的答案是一劳永逸地修改Charlie的头文件。
#ifndef __MAGIC_G++_INCLUDE_FLAG_BUT_NOT_GCC__ 
extern "C" {
#endif
...
declarations of code
...
#ifndef __MAGIC_G++_INCLUDE_FLAG_BUT_NOT_GCC__ 
}
#endif

否则将始终存在链接问题,因此最好解决它。当然,在这种特殊情况下,可能会编译两套库:一套使用C约定,另一套使用C++约定,并强制使用这个特定库来进行特定的链接。或者我可以设计两套头文件。但是这些方法都不够优雅,而且在今后的开发中仍会出现问题,就像这个例子一样;还有其他情况也是如此。至于g++和gcc都使用同一编译器的评论,因此似乎并不重要,但可悲的是,它确实很重要。如果错误地包含或不包含'extern "C"'教条,整个软件包将无法链接。当然,还有其他解决这个一般性问题的方法,但这是个简单的问题。要么存在这样的标志,要么已知不存在,要么答案尚不确定。我自己也不知道。


你需要知道这个的原因吗?你的构建脚本/流程等是否不符合gcc/g++编译器的标准? - sedavidw
3
你应该使用gcc命令来编译C语言源代码,使用g++命令来编译C++语言源代码。你这么做有什么特别的原因吗? - Keith Thompson
__cplusplus 定义了C++,但未定义 C。无论如何,两个驱动程序(gccg++ 都是真正编译器的包装器)都可以用于编译 C 和 C++ 源代码,尽管它们默认使用不同的标志,最适合分别用于 C 和 C++。 - Deduplicator
2
无论您是调用gcc还是g++,如果gcc确定源文件是C ++,那么与g ++运行的编译器相同,即二进制cc1plus实际上是运行以编译您的源代码。如果您使用gcc进行编译和链接,那么在链接阶段有所区别-但在编译阶段没有区别。如果您能解释一下您正在尝试解决的实际问题,那么这可能会有所帮助。 - nos
1
你的编辑显示了问题为什么没有意义——因为文件名为main.cpp,无论你在命令行上调用g++还是gcc,都将使用g++进行编译(在后一种情况下,gcc只会为您调用g++),因此您始终会得到“使用g++编译”。如果您想强制使用gcc而不考虑名称,可以使用命令行参数-x c,但这样它将根本无法编译,因为它不是有效的C代码。 - Chris Dodd
显示剩余3条评论
1个回答

5

标准技术是检查预处理器符号__cplusplus是否定义:

#ifdef __cplusplus
extern "C" {
#endif

/* declarations here */

#ifdef __cplusplus
}
#endif

遗憾的是,这并没有解决所提出的问题,请参见编辑。谢谢。 - DragonLord
这是一种标准的一阶方式,用于在gcc下调整C代码,使其适合于C或C++。它似乎并没有解决我正在攻击的C++问题。 - DragonLord
当源代码为C而不是C++时,它确实可以正确区分两个编译器。也就是说,如果我正在处理main.c而不是main.cpp,这个标志将会区分gcc和g++之间的差异。然而,由于extern "C"指令只在C++中有用,因此不清楚它如何解决一般问题。 - DragonLord
作为一个失败案例,从C语言的子程序child.c开始,它将被主程序main.cpp调用。通过在定义周围插入这段代码修改其头文件child.h。 事实上,这对于gcc编译器/链接器确实有效。然而,当使用g ++时,编译器会悄悄地将子程序的对象表示更改为C ++风格。但是父程序仍然期望C样式,因为包含的头部指定了extern“C”。因此,在g ++下最终的链接失败。我认为在使用g ++编译器/链接器时应该省略extern“C”。 - DragonLord

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接