今天团队成员做了一个PID算法,是用C语言实现的。而我的图像处理算法依赖于opencv库,使用的C++,要在C++中嵌入C语言,一般的函数g++是兼容的,但是队友用了信号的方式进行进程控制,按照一般方式编译总是会出现如下的错误:
error: invalid conversion from 'void (*)()' to '__sighandler_t {aka void (*)(int)}' [-fpermissive]
从错误的字面意思判断,初始化信号的时候,安装处理函数时,不能正常初始化,格式上是不被容许的。
把PID处理的C程序编译一遍,没有错。使用g++进行编译会一直出现这样的错误~网上google一下,有人说是缺少“time.h”头文件,我查找了下并没有缺少。突然想到,我使用的是C与C++混编,是不是编译器处理的时候造成格式不同的原因,才会造成这个错误!后面确认确实是这个错误!下面我将处理的过程描述一遍。
说到C与C++混编,不得不提一下extern “C”,这个是最常用的,它有什么作用呢?其作用有以下几点:
1. extern "C"的真实目的是实现类C和C++的混合编程。extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。extern “C”后面的函数不使用的C++的名字修饰,而是用C。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数。
2.C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
3.被extern "C"限定的函数或变量是extern类型的;extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。
4.与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
下面看一道腾讯的面试题,解释下面的代码: #ifndef BAT_INCLUDE
#define BAT_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*.................................
* do something here
*.................................
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BAT_INCLUDED */
首先,我们来理解下
#ifndef #endif
的作用?这个表达的是条件编译。为了解释一个问题,我假设这些内容包含于bat.h中,
bat.h可能在项目中被多个源文件包含(#include "bat.h"),而对于一个大型项目来说,这些冗余可能导致错误,因为一个头文件包含类定义或inline函数,在一个源文件中bat.h可能会被#include两次(如,alibaba.h头文件包含了bat.h,而在baidu.c文件中#include alibaba.h和bat.h)——这就会出错(在同一个源文件中一个结构体、类等被定义了两次)。
从逻辑观点和减少编译时间上,都要求去除这些冗余。然而让程序员去分析和去掉这些冗余,不仅枯燥且不太实际,最重要的是有时候又需要这种冗余来保证各个模块的独立。
为了解决这个问题,上面代码中的
#ifndef BAT_INCLUDED
#define BAT_INCLUDED
/*……………………………*/
#endif /* BAT_INCLUDED */
就起作用了。如果定义了BAT_INCLUDED,#ifndef #endif之间的内容就被忽略掉。因此,编译时第一次看到BAT.h头文件,它的内容会被读取且给定BAT_INCLUDED一个值。之后再次看到bat.h头文件时,BAT_INCLUDED就已经定义了,BAT.h的内容就不会再次被读取了。
下面解释:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
_cplusplus标示符用来判断程序是用c还是c++编译程序编译的。当编译c++程序时,这个标示符会被定义,编译c程序时,不会定义。于C++文档的头文件中,上面代码的意思是:如果是C++文件(*.cpp)后缀,则使用extern “C”,在C++项目中经常见到。
接下来处理今天遇到的问题,C++中如何调用C程序。看下面一个简单的例子:
1、hello.h中输入:
#ifndef HELLO_H
#define HELLO_H
#include
extern void print(char* );
#endif
2、hello.c中输入:
#include "hello.h"
void print(char* data)
{
printf("%s\n", data);
}
3、main.cpp中输入:
extern "C"{
#include "hello.h"
}
int main(void)
{
print(“hello,world\n”);
return 0;
}
编写makefile文件:
main:
gcc -c hello.c
g++ main.c hello.o -o main
按照上面的思路对我的程序进行修改后,但是又出现一大堆这样的错误:
multiple definition of XXX,好多变量重定义。我有两个文件PID.c smart_cat.cpp中含有PID.h文件,确实是重定义了,怎么解决呢?找到几个方案是,全局变量不能在.h中声明,也就是在PID.h中声明的全局变量是不对的,解决方案是在PID.c只中哦你声明,然后在smart_car.cpp中使用关键字extern把所有的变量声明下,告诉编译器这些变量已经在别的文件中定义的。
拓展一下,下面看看C中如何调用C++,看下面的代码进行理解:
1、cpp头文件:animal.h:
#ifndef __ANIMAL_H__ //防止被重复包含
#define __ANIMAL_H__
class ANIMAL{
public:
ANIMAL(char* );
~ANIMAL();
char* getname(void);
private:
char* name;
};
#endif // __ANIMAL_H__
2、cpp文件:animal.cpp:
#include "animal.h"
#include
#include
using namespace std;
ANIMAL::ANIMAL(char* data)//构造函数
{ name = new char[64];
strcpy(name, data);
}
ANIMAL::~ANIMAL() //析构函数
{
if(name)
{
delete[] name;
name = NULL;
}
}
char* ANIMAL::getname(void)
{
return name;
}
3、中间层cpp头文件:middle.h:
#ifndef __MIDDLE_H__
#define __MIDDLE_H__
#ifdef __cplusplus
extern "C"
{
#endif
void print(void);
#ifdef __cplusplus
}
#endif
#endif
4、中间层cpp文件:middle.cpp
#include "middle.h"
#include "animal.h"
#include
using namespace std;
void print(void) //对外接口
{
ANIMAL animal("dog");
char* animal_name = animal.getname();
cout << "animal name is :" << animal_name << endl;
}
5.main.c:
#include "middle.h"
int main(int argc, char *argv[])
{
print();
return 0;
}
makefile文件:
main: test.o animal.o middle.o
gcc -o main middle.o animal.o main.o -lstdc++
animal.o:animal.h
g++ -c animal.cpp
middle.o:middle.h
g++ -c middle.cpp
clean:
rm -f middle.o animal.o main