c語言和c++的相互調用


1.c與c++編譯方式

(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

//add.h
#ifndef __ADD_H__
#define __ADD_H__
int add(int, int);
#endif /* __ADD_H__ */

//add.c
#include "add.h"

int add(int a, int b)
{
return a + b;
}

sub.h和sub.c

//sub.h
#ifndef __SUB_H__
#define __SUB_H__
int sub(int, int);
#endif /* __SUB_H__ */

//sub.c
#include <stdio.h>
#include "sub.h"
int sub(int a, int b)
{
printf("%d - %d = %d\n", a, b, a - b);
return 0;
}

 

將各自編譯成.o文件,並打包成靜態庫

$ gcc -c add.c
$ gcc -c sub.c
$ ar cqs libadd.a *.o

 

這樣就生成了libadd.a靜態庫。
若要編譯成動態庫,則

$ gcc -shared -fPIC *.o -o libadd.so

 

main.cpp是c++寫的,用g++編譯。

//main.cpp
#include "add.h"
#include "sub.h"
#include <stdio.h>

int main(void)
{
int c = add(1, 6);
sub(8, 2);
printf("c = %d\n", c);
return 0;
}

 

編譯:

 


報錯未定義的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 "add.h"
#include "sub.h"
}
#include <stdio.h>
int main(void)
{
int c = add(1, 6);
printf("c = %d\n", c);
return 0;
}

編譯運行 

 


特別注意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 */

//用c方式編譯的代碼

#ifdef __cplusplus
}
#endif /* __cplusplus */


這樣,對於一份.c文件,采用gcc編譯時候沒有定義__cplusplus,宏判斷不起作用,且自是用c語言的方式編譯,采用g++編譯定義了_cplusplus,經過上面宏判斷,所以還是會以c語言的方式編譯。注意,extern “C”是g++才具有的關鍵字,gcc並沒有,所以如果用gcc編譯而不加以宏判斷直接使用extern “C”那么就會出現語法錯誤。
用c方式去編譯c++文件,還要注意重載函數。c方式編譯的c++文件決定不能出現重載函數。嘗試extern “C”中出現重載函數:

#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */
int func(int a, int b){
return a + b;
}

void func(const char* str){
printf("str = %s\n", str);
}
#ifdef __cplusplus
}
#endif /* __cplusplus */

int main(void)
{
func("hello");
return 0;
}

 


 


提示找不到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
//add.h
#ifndef _ADD_H_
#define _ADD_H_
int add(int, int);
#endif /* _ADD_H_ */
//add.cpp
#include <iostream>
extern "C" { //以c的方式去編譯
int add(int a, int b)
{
std::cout<<"a + b = "<< a + b << std::endl;
return 0;
}
}


注意不能在聲明add函數的add.h中指定以c的方式去編譯,因為add.h是要被main.c文件包含的,c方式編譯時並不能認得extern “C”關鍵字。 

main.c

//main.c
#include <stdio.h>
#include "add.h"
int main(void)
{
add(4, 9);
return 0;
}


編譯運行:

 

因為add.cpp用到標准c++庫,所以要手動鏈接該庫。
(2)c調用c++的重載函數
前面已經知道,在extern “C”中是不允許出現重載函數的,因為c方式下的程序並不支持重載函數,所以需要對重載函數進行接口封裝。使用extern “C”是要告訴編譯器按照c的方式來編譯封裝接口,接口里面的函數還是按照c++的語法和c++方式編譯。 

add.h和add.cpp

//add.h
#ifndef _ADD_H_
#define _ADD_H_
int add_ii(int, int);
int add_c(char);
#endif /* _ADD_H_ */

 


上面兩個函數是供c程序調用的,自然需要用c方式編譯。而這兩個函數的實現體可以去調用c++的重載函數,這就完美契合了。

//add.cpp
#include <iostream>
int add(int a, int b)
{
std::cout<< "a + b = " << a + b << std::endl;
return 0;
}

int add(char c)
{
std::cout << "c = " << c << std::endl;
}

extern "C" int add_ii(int a, int b) //供c程序調用,用c方式編譯
{
add(a, b);
}

extern "C" int add_c(char c)
{
add(c);
}

 


main.cpp

//main.cpp
#include <stdio.h>
#include "add.h"

int main(void)
{
add_ii(4, 9);
add_c('d');
return 0;
}

 


編譯運行:

 


---------------------
作者:echo_bright_
來源:CSDN
原文:https://blog.csdn.net/qq_29344757/article/details/73332501
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM