C的“外部”如何运作?

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

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

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

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

外部“C”应该适用于函数原型,因此如果您有单独的原型和实现,请在原型周围添加外部声明。 如果原型可见,那么实现也将是外部的,而不会被破坏。 这不是一个bug。

最令人困惑的事情(至少对我而言)是使标题中的声明与函数定义匹配。 正如其他答案所提到的那样,它是最重要的声明/原型 – 只要原型在到达定义时已被编译器“看到”,就可以从函数定义中省略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; } 

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

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

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

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

只有函数的声明需要包含在extern "C" 。 一旦声明了函数,编译器就知道它的名字不应该被破坏。 由于大多数时候你的函数声明都在你的头文件中,所以将extern "C"包裹在你的标题周围或者它的包含周围应该可以正常工作。

你似乎过于沉迷于这种“推杆”/“包裹”的东西。 在基本层面上,你通常不应该把“ extern "C" ”外围的“ 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" ,那么这个链接规范基本上也会被定义“inheritance”(假设声明)在此翻译单元的定义之前制作)。 换句话说,这些函数名称不会被“损坏”,因为编译器已经知道这些名称被声明为extern "C"名称。

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