#ifdef __cplusplus extern "C" { #endif的作用
经常可以在C的头文件中看到如下代码:
1 #ifdef __cplusplus 2 extern "C" { 3 #endif 4 /* head file contents */ 5 #ifdef __cplusplus 6 } 7 #endif
那么这一段代码的作用又是什么呢?
查询的时候发现一个非常好用的FAQ网站,原文链接如下: https://isocpp.org/wiki/faq/mixing-c-and-cpp#include-c-hdrs-nonsystem
FAQ:如何在C++代码中调用C函数?
只需要使用extern "C"声明C函数(在你的C++代码中),然后调用它(在你的C或C++代码中)。举个例子:
1 // C++ code 2 extern "C" void f(int); // one way 3 extern "C" { // another way 4 int g(double); 5 double h(); 6 }; 7 void code(int i, double d) 8 { 9 f(i); 10 int ii = g(d); 11 double dd = h(); 12 // ... 13 }
函数的定义可能是下面这样:
1 /* C code: */ 2 void f(int i) 3 { 4 /* ... */ 5 } 6 int g(double d) 7 { 8 /* ... */ 9 } 10 double h() 11 { 12 /* ... */ 13 }
注意使用的是C++类型规则,而不是C的。所以你不能通过错误数量的参数,调用使用extern "C"声明的函数。举个例子:
1 // C++ code 2 void more_code(int i, double d) 3 { 4 double dd = h(i,d); // error: unexpected arguments 5 // ... 6 }
FAQ:如何在C代码中调用C++函数?
只需要使用extern "C"声明C++函数(在你的C++代码中),然后调用它(在你的C或C++代码中)。举个例子:
1 // C++ code: 2 extern "C" void f(int); 3 void f(int i) 4 { 5 // ... 6 }
接下来f()函数可以被这样调用:
1 /* C code: */ 2 void f(int); 3 void cc(int i) 4 { 5 f(i); 6 /* ... */ 7 }
当然,这只适用于非成员函数。如果要在C调用成员函数(包括虚拟函数),则需要提供一个简单的包装器。例如:
1 // C++ code: 2 class C { 3 // ... 4 virtual double f(int); 5 }; 6 extern "C" double call_C_f(C* p, int i) // wrapper function 7 { 8 return p->f(i); 9 }
现在C::f()可以被这样调用:
1 /* C code: */ 2 double call_C_f(struct C* p, int i); 3 void ccc(struct C* p, int i) 4 { 5 double d = call_C_f(p,i); 6 /* ... */ 7 }
如果你想要在C代码中调用重载函数,你必须具有不同名称的包装器供C代码调用。举个例子:
1 // C++ code: 2 void f(int); 3 void f(double); 4 extern "C" void f_i(int i) { f(i); } 5 extern "C" void f_d(double d) { f(d); }
现在f()函数可以被这样调用:
1 /* C code: */ 2 void f_i(int); 3 void f_d(double); 4 void cccc(int i,double d) 5 { 6 f_i(i); 7 f_d(d); 8 /* ... */ 9 }
注意,即使你不能(或不想)修改C++头,这些技术也可以用来从C代码中调用C++库。
FAQ:怎么在C++代码中包含C标准头文件?
要使用#include包含标准头文件(如<cstdio>),您不必做任何不寻常的事情。比如:
1 // This is C++ code 2 #include <cstdio> // Nothing unusual in #include line 3 int main() 4 { 5 std::printf("Hello world\n"); // Nothing unusual in the call either 6 // ... 7 }
如果你认为std::printf()调用的std::很不习惯,最好的办法就是克服它。换句话说,这是在标准库中使用名称的标准方法,所以您现在不妨开始习惯它。
然而如果你正在使用C++编译器编译C代码,并且你不想把所有的printf()转换成std::printf()。幸运的是,在这种情况下,C代码会使用旧样式的头文件<stdio.h>而不是新样式的<cstdio.h>,并且命名空间的神奇之处将解决其他问题。
1 /* This is C code that I'm compiling using a C++ compiler */ 2 #include <stdio.h> /* Nothing unusual in #include line */ 3 int main() 4 { 5 printf("Hello world\n"); /* Nothing unusual in the call either */ 6 // ... 7 }
最后一点建议:如果你的C头文件不是C标准库的一部分,我们有两种不同的指导建议。分别是你 can’t change the header,或你can change the header。
FAQ:如何在C++代码中引用非系统C头文件?
如果你要引用非系统提供的C头文件,你需要把#include包装在extern "C" { /*...*/ }代码块中。这将会告诉C++编译器,声明在这个头文件中的函数是C函数。
1 // This is C++ code 2 extern "C" { 3 // Get declaration for f(int i, char c, float x) 4 #include "my-C-code.h" 5 } 6 int main() 7 { 8 f(7, 'x', 3.14); // Note: nothing unusual in the call 9 // ... 10 }
FAQ:怎么修改C头文件,让C++代码更容易通过#include引用?
如果你包含了系统中没有提供的C头文件,如果你能改变C头文件,你应该强烈考虑在头中添加外部extern "C" {...}逻辑,以便C++用户更容易将其包含到C++代码中。由于C编译器不理解extern“C”,因此必须将extern“C”{和}包装在一个#ifdef中,这样普通的C编译器就看不到它们。
Step #1:在C头文件的最顶部放置下面几行(注:符号__cplusplus当且仅当编译器是C++编译器时才会被定义):
1 #ifdef __cplusplus 2 extern "C" { 3 #endif
Step #2:在C头文件的最底部放置下面几行
1 #ifdef __cplusplus 2 } 3 #endif
现在你可以在C++代码中#include你的C头文件,而不需要extern "C"
1 // This is C++ code 2 // Get declaration for f(int i, char c, float x) 3 #include "my-C-code.h" // Note: nothing unusual in #include line 4 int main() 5 { 6 f(7, 'x', 3.14); // Note: nothing unusual in the call 7 // ... 8 }
Note: #define
macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. But they’re still useful sometimes. Just wash your hands after using them.(关于这一点,暂无研究,读者感兴趣可自行查看)
FAQ:怎么在C++代码中调用非系统C函数f(int,char,float)?
如果你有一个你想调用的C函数,并且由于某种原因,你不希望或者不想#include一个声明了该函数的C头文件,你可以使用extern "C"语法在C++代码中声明单个C函数。
当然,您需要使用完整函数原型:
1 extern "C" void f(int i, char c, float x);
几个C函数块可以通过大括号组合:
1 extern "C" { 2 void f(int i, char c, float x); 3 int g(char* s, const char* s2); 4 double sqrtOfSumOfSquares(double a, double b); 5 }
然后你可以向调用C++函数一样调用这个函数:
1 int main() 2 { 3 f(7, 'x', 3.14); // Note: nothing unusual in the call 4 // ... 5 }
FAQ:如何构造一个能被C代码调用的C++函数f(int,char,float)
C++编译器必须知道f(int,char,float)能够通过extern "C"
被C编译器调用。
1 // This is C++ code 2 // Declare f(int,char,float) using extern "C": 3 extern "C" void f(int i, char c, float x); 4 // ... 5 // Define f(int,char,float) in some C++ module: 6 void f(int i, char c, float x) 7 { 8 // ... 9 }
extern“C”告诉编译器,发送到链接器的外部信息应该使用C调用约定和名称改写(例如,前面有一个下划线)。由于C不支持名称重载,因此不能使多个重载函数同时可由C程序调用。
另外附上另外一篇文件,从原理上较为清楚的说明了extern C的作用及原因:https://www.geeksforgeeks.org/extern-c-in-c/