今天团队成员做了一个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 <stdio.h> extern void print(char* ); #endif2、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 <iostream> #include <string.h> 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 #endif4、中间层cpp文件:middle.cpp
#include "middle.h" #include "animal.h" #include <iostream> 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