2009-02-26 72 views
22

静态成员函数和extern“C”链接函数有什么区别?例如,当在C++中使用“makecontext”时,我需要传递一个指向函数的指针。 Google建议使用extern“C”链接,因为“makecontext”是C.但是我发现使用静态作品也是如此。我只是幸运还是...static vs extern“C”/“C++”

class X { 
    public: 
    static void proxy(int i) {} 
} 
makecontext(..., (void (*)(void)) X::proxy, ...); 

VS

extern "C" void proxy(int i) {} 
makecontext(..., (void (*)(void)) proxy, ...); 

编辑:你能告诉编译器或体系结构,其中的静态成员的版本不工作(和它不是在编译器中的错误) ?

+0

`我很抱歉,但我仍然不相信......什么?标准比一些编译器的巧合实现定义的行为更具授权的事实? – 2017-03-12 17:23:20

+0

这是一个旧帖子(8年前今天)我的观点是当时像恕我直言,如果每个现有的实施不同于标准,那么也许你应该问是否是标准是错误的问题。我正在寻找无法运行的平台示例。 – 2017-03-14 06:36:40

+0

够公平的。对于C或C++标准来说,包含所有现有编译器忽略的意想不到的结果当然是前所未有的。这是我目前最喜欢的:http://stackoverflow.com/a/42335543/2757035但是在这种情况下,我认为标准很清楚地说明它的含义,并且实现可能随时改变它们的行为,如果有的话有一些优势。 – 2017-03-14 09:46:17

回答

33

是的,你只是幸运:) extern“C”是C语言的一种语言链接,每个C++编译器都必须支持,除了默认的extern“C++”。编译器可能支持其他语言链接。例如GCC支持extern“Java”,它允许与java代码连接(尽管这很麻烦)。

extern“C”告诉编译器你的函数可以被C代码调用。这可以(但不是必须)包括适当的调用约定和适当的C语言名称(有时称为“装饰”),这取决于实现。如果您有一个静态成员函数,那么它的调用约定就是您的C++编译器之一。它们通常与该平台的C编译器相同 - 所以我说你只是幸运。如果你有一个C API,你传递一个函数指针,更好地总是把一个与外部的“C”声明的函数一样

extern "C" void foo() { ... } 

即使函数指针类型不包含链接规范,而是看起来像

void(*)(void) 

的关联是类的一个组成部分 - 你就不能没有一个typedef直接表达出来:

extern "C" typedef void(*extern_c_funptr_t)(); 

的科莫C++编译器,在严格模式,例如,如果您尝试将上面的extern“C”函数的地址分配给(void(*)()),则会发出错误,因为这是指向具有C++链接的函数的指针。

4

extern "C"禁用C++编译器的名称修改(这是重载所必需的)。

如果你在A.cpp中声明一个函数为static,那么它不能被B.cpp找到(它是从C中剩余的,并且它具有将函数放入匿名名称空间中的相同效果)。

+0

这并不回答我的问题 – 2009-02-26 20:09:11

+0

它也可以改变调用约定。 – 2012-02-12 06:06:18

5

请注意,extern C推荐的方式的C/C++互操作性。 Here是大师们在谈论它。要添加到eduffy的答案:请注意,全局名称空间中的静态函数和变量已弃用。至少使用匿名命名空间。

返回到extern C:如果您不使用extern C,您将必须知道确切的错位名称并使用它。这更加痛苦。

2

extern "C"所做的大部分工作主要依赖于编译器。许多平台都根据声明更改了名称调用和调用约定,但是没有一个是由标准指定的。标准要求的唯一要求是块中的代码可以从C函数中调用。至于你的具体问题,标准说:

Two function types with different language linkages are distinct types even if they are otherwise identical.

这意味着extern "C" void proxy(int i) {}/*extern "C++"*/void proxy(int i) {}有不同的类型,并因此指向这些功能将有不同的类型,以及。编译器不会失败你的代码出于同样的原因,将不会失败很大一块类似工作:

int *foo = (int*)50; 
makecontext(..., (void (*)(void)) foo, ...); 

此代码可能会在某些平台上工作,但这并不意味着它会在另一个工作平台(即使编译器完全符合标准)。你正在利用你的特定平台如何工作,如果你不关心编写可移植代码,这可能是好的。

至于静态成员函数,它们不需要有一个this指针,因此编译器可以自由地将它们视为非成员函数。再次,这里的行为是特定于平台的。

2

一般来说

存储类:

存储类用于指示变量或标识符的持续时间和范围。

时间:

时间指示变量的寿命。

范围:

范围表示变量的可见性。

静态存储类:

静态存储类用于声明的标识符是一个局部变量或者函数或一个文件,并存在并且控制传递之后,从它那里保留其值声明。此存储类具有永久性的持续时间。这个类声明的变量从函数的一个调用到下一个调用保留其值。范围是本地的。一个变量只能由它在内部声明的函数来知道,或者如果在一个文件中被全局声明的话,它只能被该文件中的函数知道或看到。这个存储类保证变量的声明也将变量初始化为零或全部关闭。

的extern存储类:

器extern存储类用于声明将是已知的功能在一个文件中并能够被已知的所有功能在一个程序中的全局变量。此存储类具有永久性的持续时间。此类的任何变量都会保留其值,直到被另一个赋值更改为止。范围是全球性的。变量可以被程序中的所有函数知道或看到。