我在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)。
答案 0 :(得分:16)
很明显,
thread_proxy
函数是私有内部的,我不认为它会被破坏为“thread_proxy”,因为我实际上根本不需要它。
无论如何,它仍然会受到损害。 (如果不是extern "C"
)这就是编译器的工作原理。我同意可以想象一个编译器可以说“这不一定需要被修复”,但标准上没有说明任何内容。也就是说,在这里没有发挥作用,因为我们没有尝试链接到该功能。
事实上,在我编写的所有代码中,我在许多平台上运行,但我从未使用过
extern "C"
,这与普通函数一样。
在不同平台上书写与extern "C"
无关。我希望所有标准C ++代码都能在所有具有标准C ++兼容编译器的平台上运行。
extern "C"
与C接口有关,pthread是C的库。它不仅不会破坏名称,还确保它可以使用C调用约定进行调用。这是需要保证的调用约定,因为我们不能假设我们在某个编译器,平台或体系结构上运行,尝试这样做的最好方法是使用给我们的功能:{{1} }。
我的问题是
extern "C"
函数污染了全局命名空间,并且它们实际上并没有像作者所期望的那样被隐藏。
上述代码没有任何污染。它位于未命名的命名空间中,无法在翻译单元外访问。
答案 1 :(得分:6)
extern "C"
链接并不一定意味着仅抑制名称修改。实际上,可能有一个编译器将extern "C"
视为不同的调用约定。
标准将此完全打开为实现定义的语义。
答案 2 :(得分:5)
问题是有效的 - 虽然函数被传递给C库,但C库根本没有链接到C ++代码。它仅被赋予函数的地址,因此它对函数的名称根本没有兴趣。
关键是extern "C"
是最接近跨平台的方式告诉编译器使函数在该平台上使用标准C调用约定(即参数和返回值应该如何应该是最接近的)被传递到堆栈上。)
不幸的是,它还具有在全局级别创建外部链接器符号的副作用。但是,可以通过使用boost_detail_thread_proxy
之类的名称来减轻这种情况。
答案 3 :(得分:1)
它用于使函数使用编译器通过C调用约定理解的任何内容,同时避免编译器特定的关键字,如__cdecl
。
这就是它的全部。它与名称修改,命名空间或任何其他奇怪的答案完全无关(正如你在问的时候已经知道的那样)。
答案 4 :(得分:0)
可能是因为你正在连接普通的C库 - pthreads。
答案 5 :(得分:0)
由于不保证C和C ++具有相同的调用约定,因此您需要将回调函数声明为extern "C"
,以便将其传递给pthread_create
C函数。
上面的thread_proxy
函数具有外部链接(即在其翻译单元之外可见),因为名称空间对extern "C"
函数没有影响 - 甚至是匿名名称空间。相反,要赋予thread_proxy
函数内部链接,您需要将其声明为静态:
namespace boost {
namespace {
extern "C" {
static void *thread_proxy(void *f)
{
....
}
} // extern "C"
} // anonymous
...
} // boost
[编辑]请注意,boost已包含此更改。请参阅https://svn.boost.org/trac/boost/ticket/5170。