我编写了一个C ++函数,我需要从C程序调用它。为了使其可以从C调用,我在函数声明上指定了extern "C"
。然后我编译了C ++代码,但编译器(Dignus Systems / C ++)为该函数生成了mangled name。所以,它显然不尊重extern "C"
。
要解决此问题,我在功能定义中添加了extern "C"
。在此之后,编译器生成了一个可从C调用的函数名称。
从技术上讲,只需要在函数声明中指定extern "C"
。这是正确的吗? (C++ FAQ Lite就是一个很好的例子。)你是否也应该在函数定义中指定它?
这是一个证明这一点的例子:
/* ---------- */
/* "foo.h" */
/* ---------- */
#ifdef __cplusplus
extern "C" {
#endif
/* Function declaration */
void foo(int);
#ifdef __cplusplus
}
#endif
/* ---------- */
/* "foo.cpp" */
/* ---------- */
#include "foo.h"
/* Function definition */
extern "C" // <---- Is this needed?
void foo(int i) {
// do something...
}
我的问题可能是错误编码的结果,或者我可能发现了编译器错误。在任何情况下,我都想咨询stackoverflow以确保我知道哪种技术上是“正确”的方式。
答案 0 :(得分:33)
只要声明具有并且已经在定义的编译中看到,就不应该在函数定义上要求'extern "C"
'。该标准明确规定(7.5 / 5链接规范):
在看到明确的链接规范之后,可以在没有链接规范的情况下声明函数;前面声明中明确指定的链接不受此类函数声明的影响。
但是,我通常也将'extern "C"
'放在定义上,因为它实际上是一个带有extern“C”链接的函数。很多人讨厌不必要的,冗余的东西在声明上(比如把virtual
放在方法覆盖上),但我不是其中之一。
答案 1 :(得分:2)
修改:
好像我误解了这个问题。
无论如何,我试过了:
// foo.cpp
/* Function definition */
#include "foo.h"
void foo(int i) {
//do stuff
}
void test(int i)
{
// do stuff
}
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
/* Function declaration */
void foo(int);
#ifdef __cplusplus
}
#endif
void test(int);
使用命令nm
查看编译文件中的符号:
linuxuser$ nm foo.o
00000006 T _Z4testi
U __gxx_personality_v0
00000000 T foo
这清楚地表明声明为extern“C”的函数的名称没有被破坏,并且在定义时不需要extern“C”关键字。
如果它需要在没有extern“C”的情况下编写的每个C库代码都无法在C ++程序中使用。
答案 2 :(得分:1)
定义周围的extern "C"
不是必需的。你可以把它放在声明周围。你的例子中有一个注释......
#ifdef __cplusplus
extern "C" {
#endif
/* Function declaration */
void foo(int);
#ifdef __cplusplus
}
#endif
您的代码正在寻找预处理器宏“__cplusplus
”。
虽然它通常是实现的,但根据您的编译器,这可能会也可能不会被定义。在您的示例中,您还在声明周围使用 extern "C"
,但是您不检查“__cplusplus
”宏,这就是为什么我怀疑它一旦你做到了。
请参阅下面的注释 - 标准C ++要求预处理器定义__cplusplus
宏。
答案 3 :(得分:1)
刚遇到这种情况......不是一次愉快的经历。
以下是我的一个c
文件中声明的内容:
void unused_isr(void) {}
void ADC_IRQHandler(void) __attribute__ ((weak, alias("unused_isr")));
在我定义的cpp
文件中的某个地方:
void ADC_IRQHandler(void) {
...
}
我忘了将前方声明改为:
void ADC_IRQHandler(void);
我花了一段时间才发现我在AD转换方面做的一切正确,但我没有在定义中添加“extern C”!
extern "C" void ADC_IRQHandler(void) {
...
}
我的两分钱为什么在某些情况下习惯将其添加到定义中可能会有用。
答案 4 :(得分:1)
我认为这需要在这里澄清一下,因为我遇到了类似的问题,我花了一些时间才明白这一点,只有布鲁克斯摩西才能正确地触及这一点,我认为需要更多说明显然......
总之,标题可能会让你失望,编译器看到的所有内容都是cpp文件,如果标题不包含在extern中,那么C&#34;回到你的cpp(我常见),然后是extern&#34; C&#34;将需要在某个地方的cpp文件中(在定义或其他声明中),以便CXX编译器可以知道使用C链接,编译器不关心标头,只关心链接器。
答案 5 :(得分:0)
应该是两者兼而有之。编译调试调用站点时,编译器需要知道使用C符号名称和调用约定(可能只看到声明),编译器还需要知道生成C符号名称并在编译时使用C调用约定。函数定义本身(可能看不到任何其他声明)。
现在,如果你有一个从定义存在的翻译单元可见的extern-C声明,你可以从定义中删除extern-C,但我不知道那肯定是。