40

我编写了一个需要从 C 程序调用的 C++ 函数。为了使它可以从 C 中调用,我extern "C"在函数声明中指定了。然后我编译了 C++ 代码,但是编译器 (Dignus Systems/C++) 为函数生成了一个错误的名称。所以,它显然没有兑现extern "C".

为了解决这个问题,我添加extern "C"了函数定义。在此之后,编译器生成了一个可从 C 调用的函数名。

从技术上讲,extern "C"唯一需要在函数声明中指定。这是正确的吗?(C++ FAQ有一个很好的例子。)你是否也应该在函数定义中指定它?

下面是一个例子来证明这一点:

/* ---------- */
/* "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 以确保我知道在技术上哪个是“正确”的方式。

4

6 回答 6

40

extern "C"只要声明有它并且已经在定义的编译中看到,函数定义中就不需要“ ”。该标准明确规定(7.5/5 联动规范):

在看到显式链接规范后,可以在没有链接规范的情况下声明函数;先前声明中明确指定的链接不受此类函数声明的影响。

但是,我通常也会将 ' extern "C"' 放在定义上,因为它实际上是一个带有外部“C”链接的函数。很多人讨厌在声明中出现不必要的、多余的东西(比如virtual覆盖方法),但我不是其中之一。

于 2009-09-04T18:48:59.310 回答
1

编辑:
好像我误解了这个问题。无论如何,我试过:


// 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++ 程序中将无法使用。

于 2009-09-04T19:08:53.543 回答
1

extern "C"不需要围绕定义。你可以把它放在声明周围。您的示例中的一个注释...

#ifdef __cplusplus
extern "C" {
#endif

/* Function declaration */
void foo(int);

#ifdef __cplusplus
}
#endif

您的代码正在寻找预处理器宏“ __cplusplus”。

虽然它通常被实现,但取决于您的编译器,这可能会或可能不会被定义。在您的示例中,您还使用extern "C"了声明,但是您没有检查“ __cplusplus”宏,这就是为什么我怀疑它在您这样做后起作用的原因。

请参阅下面的注释 — 标准 C++ 要求__cplusplus由预处理器定义宏。

于 2009-09-04T19:54:22.657 回答
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) {                                                                                  
    ...
}

只是我的两分钱,为什么在某些情况下习惯将它也添加到定义中可能很有用。

于 2014-11-07T09:21:42.007 回答
1

我认为这需要在这里澄清,因为我刚刚遇到了类似的问题,我花了一段时间才弄清楚这一点,只有布鲁克斯摩西正确地谈到了这一点,我认为需要更清楚地说明.. .

总之,头文件可能会让你失望,编译器看到的只是cpp文件,如果头文件不包含在extern“C”中回到你的cpp(我经常看到),那么extern“C”将需要位于 cpp 文件的某个位置(在定义中或另一个声明中),因此 CXX 编译器可以知道使用 C 链接进行操作,编译器不关心标头,只关心链接器。

于 2015-01-21T04:04:03.273 回答
0

它应该在两者周围。编译器在编译调用站点时需要知道使用 C 符号名称和调用约定(可能只看到一个声明),编译器还需要知道在编译时生成 C 符号名称和使用 C 调用约定函数定义本身(可能看不到任何其他声明)。

现在,如果您有一个可从定义所在的翻译单元中看到的 extern-C 声明,您也许可以不使用定义中的 extern-C,但我不确定.

于 2009-09-04T18:32:30.560 回答