为什么C ++函数的C ++回调需要“extern C”?

我在Boost代码中找到了这样的例子。

namespace boost { namespace { extern "C" void *thread_proxy(void *f) { .... } } // anonymous void thread::thread_start(...) { ... pthread_create(something,0,&thread_proxy,something_else); ... } } // boost 

为什么你真的需要这个extern "C"

很明显, thread_proxy函数是私有内部的,我不认为它会被破坏为“thread_proxy”,因为我实际上根本不需要它。

事实上,在我编写的所有代码中,我在许多平台上运行,我从未使用过extern "C" ,这与普通函数一样。

为什么添加了extern "C"


我的问题是extern "C"函数污染了全局命名空间,它们实际上并没有像作者所期望的那样被隐藏。

这不是重复的! 我不是在谈论破坏和外部联系。 在这段代码中很明显,外部链接是不需要的!

答: C和C ++函数的调用约定不一定相同,因此您需要使用C调用约定创建一个。 参见C ++标准的7.5(p4)。

很明显, thread_proxy函数是私有内部的,我不认为它会被破坏为“thread_proxy”,因为我实际上根本不需要它。

无论如何,它仍然会受到损害。 (如果不是extern "C" )这就是编译器的工作原理。 我同意可以想象一个编译器可以说“这不一定需要被修复”,但标准上没有说明任何内容。 也就是说,在这里没有发挥作用,因为我们没有尝试链接到该function。

事实上,在我编写的所有代码中,我在许多平台上运行,我从未使用过extern "C" ,这与普通函数一样。

在不同平台上书写与extern "C"无关。 我希望所有标准C ++代码都适用于所有具有标准C ++兼容编译器的平台。

extern "C"与C接口有关,pthread是一个库。 它不仅不会破坏名称,还确保它可以使用C调用约定进行调用。 这是需要保证的调用约定,因为我们不能假设我们在某个编译器,平台或体系结构上运行,尝试这样做的最好方法是使用给我们的function: extern "C"

我的问题是extern "C"函数污染了全局命名空间,它们实际上并没有像作者所期望的那样被隐藏。

上面的代码没有任何污染。 它位于未命名的命名空间中,无法在翻译单元外部访问。

extern "C"链接并不一定意味着仅抑制名称修改。 实际上,可能有一个编译器将extern "C"视为不同的调用约定。

该标准将此完全打开为实现定义的语义。

这个问题是有效的 – 虽然函数被传递给C库,但C库根本没有链接到C ++代码。 它仅被赋予函数的地址,因此它对函数的名称根本没有兴趣。

关键是extern "C"是最接近于跨平台的方式告诉编译器使函数在该平台上使用标准C调用约定(即确切地如何传递参数和返回值)堆)。

不幸的是,它还具有在全局级别创建外部链接器符号的副作用。 但是,可以通过使用类似boost_detail_thread_proxy的名称来减轻这种情况。

它被用来使函数使用编译器通过C调用约定理解的任何内容,同时避免编译器特定的关键字,如__cdecl


这里的所有都是它的。 它与名称修改,名称空间或任何其他奇怪的答案完全无关(正如你在问的时候已经知道的那样)。

可能是因为你正在连接一个普通的C库 – pthreads。

由于不保证C和C ++具有相同的调用约定,因此需要将回调函数声明为extern "C" ,以便将其传递给pthread_create C函数。

上面的thread_proxy函数具有外部链接(即在其翻译单元之外可见),因为名称空间对extern "C"函数没有影响 – 甚至是匿名名称空间。 相反,要给thread_proxy函数内部链接,您需要将其声明为static:

 namespace boost { namespace { extern "C" { static void *thread_proxy(void *f) { .... } } // extern "C" } // anonymous ... } // boost 

[编辑]请注意,boost已包含此更改。 请参阅https://svn.boost.org/trac/boost/ticket/5170 。