问题描述:假设在Ubuntu的一个用户目录下有2个文件,main.c, VectorAdd.cpp,其中 VectorAdd.cpp有vectorAdd函数,main.c提供程序的入口main函数。现在为了在main.c中实现两个向量相加的操作,就需要调用 VectorAdd.cpp中的vectorAdd函数
首先列出两个文件中的内容
//VectorAdd.cpp
extern "C" void VectAdd(int *a, int *b, int *c, int length);
void VectAdd(int *a, int *b, int *c, int length)
{
int i;
for(i = 0; i < length; ++i)
c[i] = a[i] + b[i];
}
//main.c文件
#include <stdio.h>
#include <malloc.h>
int main()
{
int *a, *b, *c;
int length = 32;
int i;
a = (int*)malloc(sizeof(int) * length);
b = (int*)malloc(sizeof(int) * length);
c = (int*)malloc(sizeof(int) * length);
for(i = 0; i < length; ++i)
{
a[i] = i;
b[i] = i;
}
VectAdd(a,b,c,length);
for(i = 0; i < length; i++)
{
printf("%d ",c[i]);
}
printf("\n");
return 0;
}
上述问题的实质就是如何在.c文件中调用.cpp或是.cu中的函数,为方便讨论,假设在.c文件中调用.cpp中的函数,为.cpp文件使用g++编译,对.c文件使用gcc编译,具体的编译命令如在makefile文件所示:
default: libcpp run
C = gcc
CPP = g++
SOURCE = main.c
DEST = main
libcpp:
$(CPP) -c VectorAdd.cpp -o VectorAddCpp
ar cr libcpp.a VectorAddCpp.o
run:
$(C) $(SOURCE) -lstdc++ -o $(DEST) libcpp.a
如果去掉"-lstdc++"就会出现如下的错误:
undefined reference to `__gxx_personality_v0'
这是因为我们在.c的文件中需要链接由g++编译的库,因此我们就需要告诉编译器去链接C++的库,如果不告诉编译器的话,编译器肯定就会找不到,故会出错。
另外在VectorAdd.cpp文件的开头有这样一句话
extern "C" void VectAdd(int *a, int *b, int *c, int length);
这是因为使用extern "C'是为了告诉编译器以C的方式来编译封装接口,至于接口中函数里面的c++语法还是使用C++来编译。
c编译器编译函数时是不带参数的类型信息的,只包含函数的符号名字,例如VectAdd会被编译器编译成类似_VectAdd的符号,当使用gcc链接的时候只要找到_VectAdd时,就认为链接成功了。
但C++编译器为了实现函数重载,在编译函数的时候回带上函数的参数信息,上面的函数可能就会编译成_VectAdd_int_int_int_int这样的符号
如果不使用extern "C", 则在main.c中需要_VectAdd,但VectorAdd.cpp所提供的是_VectAdd_int_int_int_int,这样肯定就会提示找不到_VectAdd。
当使用extern "C"时,编译器就会将VectAdd编译成类似_VectAdd的符号,这样在main.c中就可以找到_VectAdd,就可以连接成功
还有另外一种方法来解决这个问题,可以对main.c直接只用g++编译,这样main.c就会以c++的方式来编译,由于VectAdd.cpp是c++文件,所以肯定是以C++的方式来编译的,这样编译器就可以自动链接了,不用收到指定需要连接c++库。另外由于main.c和VectAdd.cpp都是以c++的方式来编译的,所以在VectorAdd.cpp文件的开头的这行代码
extern "C" void VectAdd(int *a, int *b, int *c, int length);
就是不需要的,需要注释掉,同时还需要定义一个头文件VectAddCpp.h用来存放对VectAdd函数的声明,VectAddCpp.h的内容如下:
void VectAdd(int *a, int *b, int *c, int length);
之后将VectAddCpp.h添加到main.c中,如果不添加会出现如下的错误:
main.c:21: 错误:‘VectAdd’ 在此作用域中尚未声明
既然c文件可以用gcc和g++编译,那么直接使用g++编译不就行了吗,这样还不用手动指定去链接c++库,那么使用gcc编译是不是就没有需要了呢?
答案肯定是否定的。这是因为c和c++的语法是有区别的,使用gcc和g++来编译同一个.文件时,当使用gcc可以通过时,使用g++就可能通不过,这主要是c++的语法规则更严谨一些,就拿上面的例子来说,g++编译时就需要添加头文件,而gcc编译时就不用