c語言和c 的相互呼叫

2021-09-10 10:07:54 字數 4255 閱讀 9226

在實際專案開發中,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訪問 對...