在.c文件中调用c++定义的函数

问题描述:假设在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编译时就不用


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页