C的“外部”如何运作?

时间:2010-02-10 16:28:07

标签: c++ c

我有一个C / C ++程序,它是Firefox的插件。因为它是一个插件,它有非主要入口点。这些入口点需要在C中编译,因为否则会导致名称损坏。但是,其他函数是过载的,所以它们需要是C ++。解决方案是外部“C”。我已经弄明白了。

但是,如果我在.c文件周围的外部“C”,我会收到链接错误,因为C ++文件名称被破坏,但.c文件中的调用没有。至少我 THINK 这就是正在发生的事情。

解决方案是将外部“C”放在.h文件周围。这个SEEMS意味着.h文件中声明的函数的名称不会被破坏,即使它们是在(可能是损坏的).c文件中定义的。

然而,对我来说,为什么这会起作用并没有任何意义。这是一块垃圾吗?我是否为以后难以找到的bug做好准备?或者这是解决这个问题的正确方法吗?

7 个答案:

答案 0 :(得分:8)

最不容易混淆的事情(至少对我而言)是使标题中的声明与函数定义匹配。正如其他答案所提到的那样,最重要的是声明/原型 - 只要编译器在获得定义时已经“看到”原型,就可以从函数定义中省略extern "C"链接规范。 。但是,我个人觉得这样做更可取,并且声明和定义匹配的可能性更小,但这是其他程序员可能不会分享的偏好。

请注意,标题或实现文件中没有extern "C"所有内容 - extern "C"可以应用于单个名称或一组名称(通过将其应用于块) )。

所以,在你的标题中你可以有类似的东西:

// entrypoints.h

#ifdef __cplusplus
// this opens an extern "C" block, but only if being included for a C++ compile
//  if this is being included in a C compile, the extern "C" bits won't be seen
extern "C" {
#endif

int foo(void);
int bar( void*);


#ifdef __cplusplus
// close the extern "C" block, but only if we started it in the first place
}
#endif

你的实施:

// entrypoints.cpp

// Note: since this is a .cpp file, it's compiled as C++ so we
//  don't need the #ifdefs around the extern "C"

#include "entrypoints.h"

extern "C"
int foo(void)
{
    return 42;
}


extern "C"
int bar( void* p)
{
    return -42;
}

答案 1 :(得分:6)

Extern“C”应该适用于函数原型,所以如果你有单独的原型和实现,请在原型周围加上extern声明。如果原型可见,那么实现也将是外部的,而不会被破坏。这不是一个错误。

答案 2 :(得分:2)

这不是一个问题 - 这就是C / C ++编译器的工作方式。当编译器编译使用您的库的文件时,它会检查.h文件以获取函数和全局变量的声明。因此,您使用extern "C"包装.h文件以避免名称损坏。

答案 3 :(得分:1)

如果.c文件包含.h文件且.h文件中的函数原型位于extern“C”块中,则一切都应该没问题。声明为extern“C”的函数不会有错位名称,所有其他函数都会有。

答案 4 :(得分:1)

只有extern "C"您需要外部可见的实体时已经足够了。你可以制作一个标准的cc / cpp / cxx / ...文件,但可以选择C-names:

class Foo {};

extern "C" int someInterfaceFun () {
    Foo foo;
    return 0xFEED;
}

答案 5 :(得分:1)

只需要在extern "C"中包含函数声明。一旦声明了函数,编译器就知道它的名称不应该被修改。由于大多数情况下您的函数声明都在头文件中,因此将extern "C"包裹在标题周围或包含它的周围应该可以正常工作。

答案 6 :(得分:1)

你似乎过于沉迷于这种“兜售”/“包裹”的事情。在基础层面上,通常不应该将extern "C"“放在”任何东西周围。 extern "C"是一个声明说明符,一个链接说明符,它将C链接分配给特定名称。同样,它适用于特定个人姓名。在基础层面,您应该在头文件中将每个C ++到C接口函数声明为extern "C",如

extern "C" void foo(void);
extern "C" char bar(int);

稍后您可以为实现文件

中的函数定义指定相同的extern "C"
extern "C" void foo(void) {
  /* whatever */
}

extern "C" char bar(int) {
  /* whatever */
}

但严格地说,在定义中不必重复extern "C",因为如果你已经声明你的函数为extern "C",那么这个链接规范基本上得到了定义也“继承”(假设声明是在此翻译单元中的定义之前做出的)。换句话说,这些函数名称不会被“损坏”,因为编译器已经知道这些名称被声明为extern "C"名称。

这就是它的全部。现在,正如您所知,您还可以使用{}形式的extern "C",以便将文件的整个部分包装到extern "C"区域。好吧,这只是extern "C"的一个很好的侧面功能,可以节省一些打字。但通常情况下,你不应该只是不加区分地将整个文件放入extern "C"区域 - 这类似于用大炮射击麻雀。应该选择性地应用说明符,仅适用于真正需要C链接的名称。实际上,在许多情况下,您可能(并且将)拥有一个专用的C接口头文件,该文件仅包含应该具有C链接的声明。在这种情况下将extern "C"放在整个头文件周围是正常的做法,但一般情况下你应该更仔细地使用它,并确保你没有用毯子extern "C"说明符覆盖不应该的东西有C连接。