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\n", 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\n", GetColor(handle)); ReleaseInstance(&handle); assert(handle == -1); return 0; }
================== End