C调用C++(C++封装以及C对其调用)
来源 https://blog.csdn.net/wonengguwozai/article/details/89854781
相关知识提点:很经典的extern C解释
编译器:
gcc和g++编译器区别
gcc是c语言编译器(也可处理c++);g++是c++编译器
g++对.c和.cpp文件都当c++处理;gcc对.c文件当作c处理,对.cpp当做c++处理
g++编译器在使用时其实调用的是gcc编译器
gcc不能自动链接库文件(.so等),一般用g++来自动链接库文件,要一定使用gcc则需要加上-lstdc++参数(使用libstdc++.so库)
gcc编译器和g++编译器在编译函数时,在相同调用方式下(如都是用_stdcall),对函数名的修饰方式不一样
gcc和g++搜索库文件的原则
头文件如果放在/usr/include/下,库文件放在/lib或/usr/lib或usr/local/lib下编译器会自动发现对应的库
如果头文件和库文件不在上述位置存放则在编译器编译时需要指定对应依赖的头文件和库文件的位置,否则编译器找不到库文件。指定方式:
使用-I(大写的i)指定头文件位置
使用-L指定库文件位置
使用 -l库名 指定链接的库名
例子(链接mytest/lib下的libgtd.so库,注意使用-lgtd来指定):g++ test.cpp -o test.txt -L /mytest/lib -lgtd -I /mytest/include
函数名的修饰:
含义:函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。LINK程序或其他工具有时需要指定函数的名字修饰来定位函数的正确位置。多数情况下程序员并不需要知道函数的名字修饰,LINK程序或其他工具会自动区分他们。当然,在某些情况下需要指定函数的名字修饰,例如在C++程序中, 为了让LINK程序或其他工具能够匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。
几种函数调用约定(__cdecl, __stdcall,__fastcall)
用法:调用约定说明符放在函数名前,如int __cdecal add(int a, int b);调用约定一定在函数的声明和定义中都指定且需保持一致
几种调用约定的区别
函数被调用时参数(尤其形参)的入栈顺序不同
函数出栈是由调用函数还是由被调函数弹出栈的区别
函数名的修饰方式不同。函数名的修饰时编译器在编译函数时对函数名进行名称修饰的操作。这对c语言和c++由很大不同,因为c++有同名函数的重载,类的构造和析构函数。
由于前两种区别由编译器决定,编程者无从干预,所以不再深究。那么对于第三种(函数名的修饰)区别下面主要说明。
不同编译环境(c和c++环境)下,各调用约定下对函数名修饰原则
如函数为:int functionname(int a, int b)
c环境下
__cdecl对函数名的修饰:__functionname
__stdcall对函数名的修饰:_functionname@number(number为形参的字节数)
__fastcall对函数的修饰:@functionname@number(number为形参的字节数)
c++环境下(为了重载、继承特性)
以“?”标识函数名的开始,后跟函数名
如果是__cdecal调用约定,函数名后面接“@@YA”标识参数表的开始;如果是__stdcall调用约定,函数名后面接“@@YG”标识参数表的开始;如果是__fastcall调用约定,函数名后面接“@@YI”标识参数表的开始。
后面再跟参数表,参数表以代号表示(各代号的含义后面说明),参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
参数表代号说明
X--void , D--char, E--unsigned char, F--short, H--int, I--unsigned int, J--long, K--unsigned long, M--float, N--double, _N--bool, PA--指针,如果PA表示的是类对象的指针,则PA后接“V+类名+@@”
那么上面的functionname函数在__cdecal调用约定下编译出的函数名修饰为?add@@YAHHH@Z其余不再列举。
note: VS中编译C++程序时,编译器自动定义了一个预处理名字__cplusplus,而编译标准C时,自动定义名字__STDC__
Linux下C调用C++库(首先要对C++封装成C)实例:
实例链接
实例说明:
生成的libadd.so、mylib.so、main.bin缺一不可(因为时动态库),其中libadd.so是原始c++的实现库,而mylib.so是c对c++封装后的库,main.bin是最终的可执行程序。
勘误:mylib.so的编译命令中-la 应为-ladd,即链接上libadd.so库。
如果没有按照例子中将libadd.so放到/usr/lib/下,则在编译mylib.so库时应该先指定动态库环境变量LD_LIBRARY_PATH的值(libadd.so的位置)。
C语言封装 C++的类
来源 https://blog.csdn.net/caspiansea/article/details/9676153
本文给出了一种方法。基本思想是,写一个 wrapper文件,把 C++类封装起来,对外只提供C语言的接口,和 C++i相关的都在 wrapper的实现文件里实现。
1. apple.h
#ifndef __APPLE_H__ #define __APPLE_H__ class Apple { public: Apple(); int GetColor(void); void SetColor(int color); private: int m_nColor; }; #endif
apple.cpp
#include "apple.h" Apple::Apple() : m_nColor(0) { } void Apple::SetColor(int color) { m_nColor = color; } int Apple::GetColor(void) { return m_nColor; }
2. AppleWrapper.h
#ifndef _APPLE_WRAPPER_H__ #define _APPLE_WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif struct tagApple; // Warning: 不能使用 extern 修饰 extern struct tagApple *GetInstance(void); extern void ReleaseInstance(struct tagApple **ppInstance); extern void SetColor(struct tagApple *pApple, int color); extern int GetColor(struct tagApple *pApple); #ifdef __cplusplus }; #endif #endif
AppleWrapper.cpp
#include "AppleWrapper.h" #include "apple.h" #ifdef __cplusplus extern "C" { #endif struct tagApple { Apple apple; }; struct tagApple *GetInstance(void) { return new struct tagApple; } void ReleaseInstance(struct tagApple **ppInstance) { delete *ppInstance; *ppInstance = 0; } void SetColor(struct tagApple *pApple, int color) { pApple->apple.SetColor(color); } int GetColor(struct tagApple *pApple) { return pApple->apple.GetColor(); } #ifdef __cplusplus }; #endif
3. test.c
#include "AppleWrapper.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> int main(void) { struct tagApple * pApple; pApple = GetInstance(); SetColor(pApple, 1); printf("color = %d ", GetColor(pApple)); ReleaseInstance(&pApple); assert(pApple == 0); return 0; }
可以用 GCC编译:
g++ -g -c apple.cpp AppleWrapper.cpp gcc -g test.c -o test AppleWrapper.o apple.o -lstdc++
其实, wrapper里的 struct 完全可以不要,定义一个 handle更好:
AppleWrapper.h
#ifndef _APPLE_WRAPPER_H__ #define _APPLE_WRAPPER_H_ #ifdef __cplusplus extern "C" { #endif extern int GetInstance(int * handle); extern void ReleaseInstance(int *handle); extern void SetColor(int handle, int color); extern int GetColor(int handle); #ifdef __cplusplus }; #endif #endif
AppleWrapper.cpp
#include "AppleWrapper.h" #include "apple.h" #include <vector> #ifdef __cplusplus extern "C" { #endif static std::vector<Apple *> g_appleVector; int GetInstance(int * handle) { g_appleVector[0] = new Apple; // Error: Segmentation fault (core dumped) *handle = 0; return 1; } void ReleaseInstance(int *handle) { delete g_appleVector[*handle]; *handle = -1; } void SetColor(int handle, int color) { g_appleVector[handle]->SetColor(color); } int GetColor(int handle) { return g_appleVector[handle]->GetColor(); } #ifdef __cplusplus }; #endif
test.c
#include "AppleWrapper.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> int main(void) { int handle = -1; GetInstance(&handle); SetColor(handle, 1); printf("color = %d ", GetColor(handle)); ReleaseInstance(&handle); assert(handle == -1); return 0; }
================== End