所以,在schooled by James Kanze and Loki Astari about C linkage期间,我对此感到疑惑:
extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }
在我上学之后,我认为必须是foo1
只接受带有C ++链接的函数指针,而foo2
只接受带有C链接的函数指针。我的理解是否正确? C ++标准中是否有特定的参考资料来解释我上面例子中的差异?
修改:让每个人都可以更轻松地跟进here's a pastebin with the relevant part from the C++ 11 draft standard。
答案 0 :(得分:9)
foo1获取指向C函数的指针,如[dcl.link] 7.5p4
所示在 linkage-specification 中,指定的语言链接适用于 所有函数声明符的函数类型,函数名称 外部链接和声明了外部链接的变量名称 在链接规范内。 [实施例:
extern "C" void f1(void(*pf)(int));
//名称f1及其功能类型具有C语言
//连接; pf是指向C函数的指针
该示例直接适用于foo1
,并且增加的重点突出了我认为的原因。函数的参数列表包含参数的函数声明符,并且所有函数声明符都受链接规范的影响。这适用于支撑和非支撑连杆规范。
不使用大括号时的一些差异是名称自动为extern
,并且禁止明确使用存储说明符。
extern "C" int i; // not a definition
int main() {
i = 1; // error, no definition
}
extern "C" static void g(); // error
作为这种差异的重要示例,请考虑包含以下内容的标题:
extern "C" int a;
extern "C" double b;
extern "C" char c;
有人可能会想要将其更改为:
extern "C" {
int a;
double b;
char c;
}
但这是不正确的,因为它将声明转换为定义。相反,使用extern "C" {}
的正确代码是:
extern "C" {
extern int a;
extern double b;
extern char c;
}
答案 1 :(得分:1)
当您有许多声明和定义时,将使用大括号。通常,您可以在标头文件中看到开头和结尾,以便在C
C++
代码
#ifdef __cplusplus
extern "C" {
#endif
// C stuff here to be available for C++ code
#ifdef __cplusplus
}
#endif
我可以推荐阅读“名称修改”http://en.wikipedia.org/wiki/Name_mangling extern "C"
是回退到C
链接名称约定的关键。
答案 2 :(得分:1)
extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }
那些是一样的。使用大括号的主要原因是你有多个函数,例如:
extern "C" int foo1 (void (*)());
extern "C" int foo2 (void (*)());
extern "C" int foo3 (void (*)());
extern "C" int foo4 (void (*)());
可以更简单地写成:
extern "C" {
int foo1 (void (*)());
int foo2 (void (*)());
int foo3 (void (*)());
int foo4 (void (*)());
}
此外,如果您尝试制作一个适用于C和C ++的头文件,您可能希望将其写为:
#ifdef __cplusplus
extern "C" {
#endif
int foo1 (void (*)());
int foo2 (void (*)());
int foo3 (void (*)());
int foo4 (void (*)());
#ifdef __cplusplus
}
#endif
P.S。我不知道任何编译器的功能指针的“C ++链接”或“C链接”之间存在差异。当我们谈论C或C ++链接时,我们正在讨论编译器如何破坏名称。对于函数指针,您传递指针,因此名称无关紧要。重要的是调用约定是相同的,但它通常与C和C ++相同,因为人们可以自由地混合这些语言。