C++语法之C和C++混编

本文首发于个人博客

extern "C"

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言(而不是C++)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

extern "C" 可以修饰单个函数,也可以修饰多个函数。

例如下面的三个函数都会用 C语音的方式编译

1
2
3
4
5
6
7
8
9
10
11
12
13
extern "C" void test(int a){
cout << a << endl;
}

extern "C"{
void test1(int a,int b = 10){
cout << a << endl;
}

void test2(){
cout << "test2" << endl;
}
}

声明必须修饰,实现可修饰可不修饰

  • 如果函数同时有声明和实现,要让函数声明被extern “C”修饰,函数实现可以不修饰

  • 函数的声明都被extern “C”修饰是可以编译通过的

1
2
3
4
5
6
//可以编译通过
extern "C" void test(int a);

extern "C" void test(int a){
cout << a << endl;
}
  • 函数的声明被extern “C”修饰,实现没有被extern “C”修饰,是可以编译通过的
1
2
3
4
5
6
//可以编译通过
extern "C" void test(int a);

void test(int a){
cout << a << endl;
}
  • 函数的声明没有被extern “C”修饰,实现被extern “C”修饰,不能编译通过
1
2
3
4
5
6
//不能编译通过
void test(int a);

extern "C" void test(int a){
cout << a << endl;
}

__cplusplus

有时候我们的函数,不仅要在C语言中调用,还要再C++中调用。怎么做呢?

如果我们加了extern "C" 那么只能是C语言的环境编译,C++环境编译就会有问题了。

如果我们不加extern "C" 那么只能是C++语言的环境编译,C环境编译就会有问题了。

如果能够判断是C语言环境还是C++环境,这样在C++语言的环境下按照C++编译方式,在C语言的环境下按照C编译方式,岂不是都可以了。

__cplusplus就可以达成这样的效果。

例如

1
2
3
#ifdef __cplusplus
void test(int a);
#endif

这样就能做到C++语言的环境下按照C++编译方式,在C语言的环境下按照C编译方式。

ifndef

我们知道,当引用其他文件的时候,我们要用到 #include 例如

1
#include "iostream"

如果我们不小心写了很多次

1
2
3
4
#include "iostream"
#include "iostream"
#include "iostream"
#include "iostream"

那就相当于把引用的文件的内容拷贝过来很多次,编译器编译的时候,要执行多次,浪费性能。这时候我们只需要在被引用文件里面加上如下代码即可

1
2
3
4
5
6
#ifndef _LIBCPP_IOSTREAM
#define _LIBCPP_IOSTREAM

//。。。很多代码

#endif

这样就算引用多次,也没关系。因为第二次引用的时候,判断引用过了,直接跳过。其中_LIBCPP_IOSTREAM这个为了避免重复,一般采用和文件同名的大写字母来命名。

pragma once

  • 我们经常使用#ifndef、#define、#endif来防止头文件的内容被重复包含
  • #pragma once可以防止整个文件的内容被重复包含

区别

  • #ifndef#define#endifC\C++标准的支持,不受编译器的任何限制
  • 有些编译器不支持#pragma once(较老编译器不支持,如GCC 3.4版本之前),兼容性不够好
  • #ifndef#define#endif可以针对一个文件中的部分代码,而#pragma once只能针对整个文件