在實際專案開發中,c和c++**的相互呼叫是常見的,c++能夠相容c語言的編譯方式,但是c++編譯器g++缺省會以c++的方式編譯程式,而c程式編譯器gcc會預設以c的方式編譯它,所以c和c++的相互呼叫存在一定的技巧。
1.c方式編譯和c++方式編譯
一般.cpp檔案是採用g++去編譯,.c檔案是採用gcc編譯,然而這不是絕對的。
(1)gcc和g++都可以編譯.c檔案,也都可以編譯.cpp檔案。g++和gcc是通過字尾名來辨別是c程式還是c++程式的(這一點與linux辨別檔案的方式不同,linux是通過檔案資訊頭辨別檔案的)。
(2)在gcc看來,.c檔案會以c方式去編譯,.cpp檔案則是以c++的方式去編譯,注意,gcc不會主動去鏈結c++用到庫stdc++,所以用gcc編譯cpp檔案時需要手動指定鏈結選項-lstdc++。而對於g++,不管是.c還是.cpp檔案,都是以c++方式去編譯。
(3)還需要注意,並不是說__cpluscplus 是g++編譯器才會定義的巨集,確切的說,是只有以c++編譯的方式去編譯檔案的巨集才會定義的巨集,這樣說來,gcc編譯.cpp檔案、g++編譯.c、.cpp檔案,這個 __cplusplus都會被編譯器定義。
2.c++呼叫c程式
假設c程式是之前寫好的具有價值的靜態庫,該庫是由add.o和sub.o編譯而成,而add.c和sub.c是由c語言寫的:
add.h和add.c
#ifndef __add_h__
#define __add_h__
int add(int, int);
#endif /* __add_h__ */
#include "add.h"
int add(int a, int b)
12345678910111213
sub.h和sub.c
#ifndef __sub_h__
#define __sub_h__
int sub(int, int);
#endif /* __sub_h__ */
#include
#include "sub.h"
int sub(int a, int b)
1234567891011121314
將各自編譯成.o檔案,並打包成靜態庫
$ gcc -c add.c
$ gcc -c sub.c
$ ar cqs libadd.a *.o123
這樣就生成了libadd.a靜態庫。
若要編譯成動態庫,則
$ gcc -shared -fpic *.o -o libadd.so1
main.cpp是c++寫的,用g++編譯。
#include "add.h"
#include "sub.h"
#include
int main(void)
123456789101112
編譯:報錯未定義的add和sub函式。
通過nm確實是可以看到add和sub標號的存在的:
原因在於,main.cpp是c++檔案,用g++編譯器編譯時(gcc也是一樣的結果),會優先選擇cpp的編譯方式,也就是會用cpp的編譯方式去編譯add()、sub()函式。然而,它們是.c檔案,用的是c語言的方式去編譯的,所以出現如上問題。注意,cpp編譯器是相容c語言的編譯方式的,所以在編譯cpp檔案的時候,呼叫到.c檔案的函式的地方時,需要用extern 「c」指定用c語言的方式去編譯它:
//使得add.h、sub.h裡面的**用c語言的方式去編譯
extern "c"
#include
int main(void)
123456789101112
編譯執行
特別注意extern 「c」是c++方式編譯才認識的關鍵字。
3. c語言呼叫c++程式
c語言用c方式編譯,c++程式用c++方式,要使得c語言能呼叫c++程式無非有2種方法(c++呼叫c也是一樣)
(1) c語言程式用c++方式編譯
既然是c語言呼叫c++程式,肯定是要採取c++方式編譯,所以覺得這個沒什麼意義。操作很簡單,用g++方式編譯c程式即可,注意,g++會對語法、型別等更為嚴格的檢查。
(2) c++程式用c語言方式編譯
a. c方式編譯和c++方式編譯,其差異就在於符號表識別符號。同乙個函式名,在c方式編譯的其函式名跟編譯前的函式一致,c++方式編譯的則是以函式名結合引數作為編譯後的函式名。要確保檔案以.c方式編譯,可以利用__cplusplus,這個巨集在c++編譯的方式才會定義的巨集,結合之前的extern c用法如下:
#ifdef __cplusplus
extern "c"
#endif /* __cplusplus */123456789
這樣,對於乙份.c檔案,採用gcc編譯時候沒有定義__cplusplus,巨集判斷不起作用,且自是用c語言的方式編譯,採用g++編譯定義了_cplusplus,經過上面巨集判斷,所以還是會以c語言的方式編譯。注意,extern 「c」是g++才具有的關鍵字,gcc並沒有,所以如果用gcc編譯而不加以巨集判斷直接使用extern 「c」那麼就會出現語法錯誤。
用c方式去編譯c++檔案,還要注意過載函式。c方式編譯的c++檔案決定不能出現過載函式。嘗試extern 「c」**現過載函式:
#ifdef __cplusplus
extern "c"
void func(const char* str)
#ifdef __cplusplus
}#endif /* __cplusplus */
int main(void)
12345678910111213141516171819
提示找不到func函式。在c語言中肯定不能出現同名函式,不然編譯器怎麼知道它要呼叫的是哪乙個。去除extern 「c」關鍵字,也就是使其採用c++方式編譯,編譯結果:
編譯通過。通過nm命令可以檢視可執行程式的符號表:
可見c++編譯方式會將過載函式名結合引數形成唯一的函式名。但是c方式編譯的可執行檔案卻並非如此,函式名經過編譯後的符號還是之前的函式名,所以出現找不到呼叫函式的現象。
b. c程式要呼叫c++寫的程式,涉及到的可能有:c程式呼叫c++的普通函式,c程式呼叫c++的過載函式,c程式呼叫c++的成員函式(包括虛函式)。c程式自然是採用c的方式去編譯的,即是採用gcc編譯器,然而c++是採用c++方式編譯,所以要強制將c++**以c的方式去編譯。
(1) c程式呼叫c++的普通函式
add.h和add.cpp
#ifndef _add_h_
#define _add_h_
int add(int, int);
#endif /* _add_h_ */
pp#include
extern "c"
}123456789101112131415
注意不能在宣告add函式的add.h中指定以c的方式去編譯,因為add.h是要被main.c檔案包含的,c方式編譯時並不能認得extern 「c」關鍵字。
main.c
#include
#include "add.h"
int main(void)
12345678
編譯執行:
因為add.cpp用到標準c++庫,所以要手動鏈結該庫。
(2)c呼叫c++的過載函式
前面已經知道,在extern 「c」中是不允許出現過載函式的,因為c方式下的程式並不支援過載函式,所以需要對過載函式進行介面封裝。使用extern 「c」是要告訴編譯器按照c的方式來編譯封裝介面,介面裡面的函式還是按照c++的語法和c++方式編譯。
add.h和add.cpp
#ifndef _add_h_
#define _add_h_
int add_ii(int, int);
int add_c(char);
#endif /* _add_h_ */123456
上面兩個函式是供c程式呼叫的,自然需要用c方式編譯。而這兩個函式的實現體可以去呼叫c++的過載函式,這就完美契合了。
pp#include
int add(int a, int b)
int add(char c)
extern "c" int add_ii(int a, int b) //供c程式呼叫,用c方式編譯
extern "c" int add_c(char c)
12345678910111213141516171819202122
main.cpp
#include
#include "add.h"
int main(void)
12345678910
C語言和ARM組合語言的相互呼叫
1.匯程式設計序訪問c語言全域性變數 全域性變數只能通過位址間接呼叫,為了訪問c語言中全域性變數,首先要通過extern偽指令引入全域性變數,然後將其位址裝入暫存器中。對於unsigned char型別,使用ldrb strb訪問 對於unsigned short型別,使用ldrh strh訪問 對...
C語言與C 語言相互呼叫
1 c 呼叫c中的函式 1.1 c 呼叫c中的函式 正確使用 1 案例原始檔組成 圖12 math模組包含檔案 1 原始檔math.c 圖22 標頭檔案math.h 圖33 主模組包含檔案 1 原始檔main.cpp 圖42 標頭檔案module.h 圖54 編譯math模組 圖65 編譯主模組 圖...
ARM彙編 C語言 C 相互呼叫
1.匯程式設計序訪問c語言全域性變數 全域性變數只能通過位址間接呼叫,為了訪問c語言中全域性變數,首先要通過extern偽指令引入全域性變數,然後將其位址裝入暫存器中。對於unsigned char型別,使用ldrb strb訪問 對於unsigned short型別,使用ldrh strh訪問 對...