external “C”,gcc和g++编译器的区别

gcc和g++编译器的区别   

对于.c文件gcc当做c语言处理,g++当做c++处理;对于.cpp文件gcc和g++均当做c++处理;
    g++编译时实际上是调用gcc进行编译;
    gcc不能自动链接库文件,一般用g++来链接库文件,非要用gcc的话,一般使用gcc -lstdc++命令;
    extern “c”对于gcc和g++没有区别;
    实际使用时只需安装gcc和g++中的一个就行了,如果使用gcc,编译直接用gcc就行了,链接要加上-lstdc++参数;如果使用g++,编译时实际还是调用gcc,链接直接使用g++即可;

关于包含库文件头文件的说明:以gtd库文件为例,库文件一般是libgtd.so格式的文件,如果是libgtd-***.so或者其他格式,可以通过软链接命令转化成libgtd.so格式,ln -s libgtd-***.so libgtd.so。一般库文件如果是放在/lib,/usr/lib,/usr/local/lib目录下,则无需额外处理,编译链接时-lgtd会自动找到对应文件不会报错,但是如果不在对应目录,则需要加上-L /所在目录 -lgtd才能找到对应的库文件。头文件一般是采用源文件中用#include命令包含,而不是采用-include参数包含,头文件如果放在/usr/include目录下则会自动找到不会报错,否则编译链接时需要使用-I /所在目录才能正确找到。
编译链接命令示例:
g++ test.cpp -o test.txt -L /mytest/lib -lgtd -I /mytest/include

 

extern "C"

从字面上看:extern "C"有两部分内容:extern和“C”,下面分别说明:

(1)extern

学过C或C++的人都知道,extern是编程语言中的一种属性,它表征了变量、函数等类型的作用域(可见性)属性,是编程语言中的关键字。当进行编译时,该关键字告诉编译器它所声明的函数和变量等可以在本模块或者文件以及其他模块或文件中使用。通常,程序员都只是在“*.h”(头文件)中使用该关键字以限定变量或函数等类型的属性,然后在其他模块或本模块中使用,如:

file1.h

1

extern int i;

file2.c /*其他文件调用该变量*/

1

2

3

4

5

void welcome(void)

{

    if(i > 0)

        printf("Hello World!\n");

}

(2)“C”

编程语言种类繁多各有优劣,因此在做软件工程时,往往会出现不同语言嵌套或互相调用的情况,例如在C和C++之间就经常出现互相调用。虽然C++是C的超集,但是,它们之间的编译器是不同的,这就产生了各自的编译器在对C和C++进行编译时要依据哪一个作为大家都认可的规范或者约定的问题。很幸运的是,通过一些基础特征我们制定了这样的一个规约,这就是“C”的出处,这些基础特征就是:

1)这种调用编译是一种“超”链接;

2)这种调用编译不能影响现有的编译规范;

3)C++是C的拓展,是C的升华。

根据1),extern关键字可以表达这种“超”链接;根据2)、3)用“C”来规约在C++中对C的编译。

因此,extern "C"表明了一种编译规约,其中extern是关键字属性,“C”表征了编译器链接规范。对于extern "C"可以理解成在C++/C中的混合编程的编译指令。

明白了这层意思,下面的代码就不难解释了:

1

2

3

4

5

6

7

#ifdef __cplusplus /* 如果采用了C++,如下代码使用C编译器 */

    extern "C" /* 如果没有采用C++,顺序预编译 */

#endif

/* 采用C编译器编译的C语言代码段 */

#ifdef __cplusplus /* 结束使用C编译器 */

    }

#endif

external "C"主要用于c和c++之间的互相调用的兼容而设计的。

声明了external "C"后编译或者链接的方法会按照c的规范去做。

c++中调用c的函数

总结来说, 加external "C"的声明或者#include的代码块会使用C编译器的规范去编译或者链接这些变量或者方法时,使用C的规范去链接。如:一个使用gcc编译好的动态库,如果在c++文件中调用动态库中的方法,那么链接的时候,首先要去找c++中使用的函数在动态库中的实现,那么在c++中对动态库中的函数声明需要使用extern "C"声明,否则因g++编译器会使用c++规范去找动态库中的实现,就找不到了,因为符号(代表某个函数的一个描述符,即函数签名吧,)对不上,因为g++和gcc为同一函数生成的描述符是不一样的。

c中调用c++的函数

在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,只需要在C源文件中将C++中定义的extern "C"函数声明为extern类型。实例代码如下:

//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif
//C++实现文件 cppExample.cpp
#include "cppExample.h"
int add( int x, int y )
{
return x + y;
}
// C实现文件 cFile.c
/* 这样会编译出错:#include "cppExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}

也就是说在c调用c++时,只需c++那边处理,而c这边只需像调用普通外部函数一样使用external声明一下就好了。而c++需要将对应函数声明使用c的规范编译和链接。那么在c++中使用了external "C"声明的函数是否还可以重载,那么就不知道了。

 

 

未加extern “C”声明时的编译方式

首先看看C++中对类似C的函数是怎样编译的。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

1

void foo(int x, int y);

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

 

为什么使用external "C"(c++调用c库文件的代码示例)

为什么需要使用extern "C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

//f1.c

extern "C"

{

void f1()

{

return;

}

}

编译命令是:gcc -c f1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数:

// test.cxx

//这个extern表示f1函数在别的地方定义,这样可以通过编译,

//但是链接的时候还是需要链接上原来的库文件.

extern void f1();

int main()

{

f1();

return 0;

}

通过gcc -c test.cxx -o test.o 产生一个叫test.o的文件。然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是:

test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'

也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。

因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

extern "C"

{

#include "f.h"

}

回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

extern "C"

{

extern void f1();

}

int main()

{

f1();

return 0;

}

重新编译并且链接就可以过去了.

总结

C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

你可能感兴趣的:(c,c++)