#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#1evil#2evil#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/ 

posted @ 2021-04-18 15:12  逃之夭夭uu  阅读(513)  评论(0编辑  收藏  举报