简体   繁体   English

C的“外部”如何运作?

[英]How does C's “extern” work?

I have a C/C++ program that's a plugin to Firefox. 我有一个C / C ++程序,它是Firefox的插件。 Because it's a plugin, it has non-main entry points. 因为它是一个插件,它有非主要入口点。 Those entrypoints need to be compiled in C because otherwise they get name mangled.However, other functions are over-loaded, so they need to be C++. 这些入口点需要在C中编译,因为否则会导致名称损坏。但是,其他函数是过载的,所以它们需要是C ++。 The solution is extern "C". 解决方案是外部“C”。 That much I've already figured out. 我已经想通了。

However, if I extern "C" around the .c file, I get link errors because the C++ files got name mangled, but the calls in the .c file didn't. 但是,如果我在.c文件周围的外部“C”,我会收到链接错误,因为C ++文件名称被破坏,但.c文件中的调用没有。 At least I THINK that's what's happening. 至少我认为这是正在发生的事情。

The solution APPEARS to be to put the extern "C" around the .h file. 解决方案是将外部“C”放在.h文件周围。 This SEEMS to mean that the names of the functions declared in the .h file aren't mangled, even when they're defined in the (presumably mangled) .c file. 这个SEEMS意味着.h文件中声明的函数的名称不会被破坏,即使它们是在(可能是损坏的).c文件中定义的。

However, it doesn't really make any sense to me why this would work. 然而,对我来说,为什么这会起作用并没有任何意义。 Is this a kludge? 这是一块垃圾吗? Am I setting myself up for a hard to find bug later? 我是否为以后难以找到的bug做好准备? or is this the right way to solve this? 或者这是解决这个问题的正确方法吗?

The least confusing thing (at least for me) to do is to make the declarations in the header match the function definitions. 最令人困惑的事情(至少对我而言)是使标题中的声明与函数定义匹配。 As other answers have mentioned, it's the declaration/prototype that matters most - the extern "C" linkage specification can be omitted from the function definition as long as the prototype has been 'seen' by the compiler already when it gets to the definition. 正如其他答案所提到的那样,它是最重要的声明/原型 - 只要原型在到达定义时已被编译器“看到”,就可以从函数定义中省略extern "C"链接规范。 However, personally I find it preferable and less potentially confusing to have the declarations and the definitions match, but that's a preference that other programmers might not share. 但是,就我个人而言,我认为使声明和定义匹配更为可取,而且可能更容易混淆,但这是其他程序员可能不会分享的偏好。

Note that you don't have to extern "C" everything in a header or implementation file - the extern "C" can be applied to a single name or to a group of names (by applying it to a block). 请注意,您不必extern "C"在页眉或实现文件一切- extern "C"可以(通过将它应用于块)被应用到单个名称,一组名称。

So, in your header you can have something like: 所以,在你的标题中你可以有类似的东西:

// 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

And your implementation of them: 你的实施:

// 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;
}

Extern "C" should apply to function prototype, so if you have separate prototype and implementation, put extern declaration around prototypes. 外部“C”应该适用于函数原型,所以如果你有单独的原型和实现,请在原型周围加上extern声明。 Implementation, provided prototype is visible, will be extern as well and not mangled. 如果原型可见,那么实现也将是外部的,而不会被破坏。 It is not a bug. 这不是一个bug。

This is not a kludge - this is how C/C++ compilers work. 这不是一个问题 - 这就是C / C ++编译器的工作方式。 When the compiler compiles a file that uses your library, it checks the .h files for declarations of functions and globals. 当编译器编译使用您的库的文件时,它会检查.h文件以获取函数和全局变量的声明。 Hence, you wrap the .h files with extern "C" to avoid name mangling. 因此,您使用extern "C"包装.h文件以避免名称损坏。

if the .c file includes the .h file and the function prototypes in the .h file are in an extern "C" block, everything should be ok. 如果.c文件包含.h文件且.h文件中的函数原型位于extern“C”块中,则一切都应该没问题。 The functions declared as extern "C" won't have mangled names, all the other functions will. 声明为extern“C”的函数不会有错位名称,所有其他函数都会。

It is already enough when you only extern "C" the entities you need to be externally visible. 只有extern "C"表示您需要外部可见的实体,这已经足够了。 You can make a standard cc/cpp/cxx/... file, but cherry pick C-names: 您可以制作标准的cc / cpp / cxx / ...文件,但是可以选择C-names:

class Foo {};

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

Only the declaration of the functions needs to be wrapped in extern "C" . 只有函数的声明需要包含在extern "C" Once the function has been declared, the compiler knows that its name shouldn't be mangled. 一旦声明了函数,编译器就知道它的名称不应该被修改。 Since most of the time your function declarations are in your header file, wrapping the extern "C" around your header or around the include of it should work fine. 由于大多数时候你的函数声明都在你的头文件中,所以将extern "C"包裹在你的标题周围或者它的包含周围应该可以正常工作。

You seem to be overly obsessed with this "putting around"/"wrapping" thing. 你似乎过于沉迷于这种“推杆”/“包裹”的事情。 At the fundamental level you are not normally supposed to put extern "C" "around" anything. 在基本层面上,你通常不应该把“ extern "C" “外围”放在任何东西上。 extern "C" is a declaration specifier, a linkage specifier, which assigns C-linkage to a specific name. extern "C"是一个声明说明符,一个链接说明符,它将C链接分配给特定名称。 Again, it applies to specific individual names . 同样,它适用于特定的个人名称 At the fundamental level you are supposed to declare each of your C++-to-C interface functions in your header file as extern "C" , as in 在基础层面,您应该在头文件中将每个C ++到C接口函数声明为extern "C" ,如

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

Later you can specify the same extern "C" to the function definitions in the implementation file 稍后您可以为实现文件中的函数定义指定相同的extern "C"

extern "C" void foo(void) {
  /* whatever */
}

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

But strictly speaking, it is not really necessary to repeat extern "C" in the definition, since if you have already declared your function as extern "C" , this linkage specification basically gets "inherited" by the definition as well (assuming the declaration is made before the definition in this translation unit). 但严格地说,在定义中没有必要重复extern "C" ,因为如果你已经你的函数声明extern "C" ,那么这个链接规范基本上也会被定义“继承”(假设声明)在此翻译单元的定义之前制作)。 In other words, these function names will not get "mangled", since the compiler knows already, that these names are declared as extern "C" names. 换句话说,这些函数名称不会被“损坏”,因为编译器已经知道这些名称被声明为extern "C"名称。

That's all there's to it. 这就是它的全部内容。 Now, as you already know, you can also use a {} form of extern "C" that lets you wrap the entire section of a file into the extern "C" region. 现在,正如您已经知道的那样,您还可以使用extern "C"{}形式,它允许您将文件的整个部分包装到extern "C"区域中。 Well, that's just a nice side-feature of extern "C" that can save you some typing. 嗯,这只是extern "C"一个很好的侧面功能,可以节省一些打字。 But normally, you should not just indiscriminately enclose entire files into the extern "C" region - this is akin to shooting sparrows with a cannon. 但通常情况下,你不应该只是不加区分地将整个文件放入extern "C"区域 - 这类似于用大炮射击麻雀。 The specifier is supposed to be applied selectively, only to the names that really need C-linkage. 应该选择性地应用说明符,仅适用于真正需要C链接的名称。 Actually, in many cases you might (and will) have a dedicated C-interface header file that contains only declarations that are supposed to have C-linkage. 实际上,在许多情况下,您可能(并且将)拥有一个专用的C接口头文件,该文件仅包含应该具有C链接的声明。 Putting the extern "C" around the entire header file in this case is normal practice, but in general case you should use it more carefully and make sure you are not covering with a blanket extern "C" specifier something that is not supposed to have C-linkage. 在这种情况下将extern "C"放在整个头文件周围是正常的做法,但一般情况下你应该更仔细地使用它,并确保你没有用毯子extern "C"说明符覆盖一些不应该有的东西C-联动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM