1. 程式人生 > >混合編譯:c++檔案呼叫c檔案變數和函式、c檔案呼叫c++檔案變數和函式

混合編譯:c++檔案呼叫c檔案變數和函式、c檔案呼叫c++檔案變數和函式

1.c++檔案呼叫c檔案變數和函式

前言:以前見到extern "C"這樣的語句,只是簡單地知道跟外部連結有關,但是沒有深刻理解它的意思。

首先,為什麼要使用extern "C"修飾符?

C++呼叫其它語言的函式,由於編譯器生成函式的機制不一樣,所以需要經過特殊處理,才可以呼叫。呼叫C語言的函式,需要在函式宣告的地方語句extern "C"。如果不使用該語句,在連結的時候,編譯器就會報以下這種錯誤。

Test.obj : error LNK2019: 無法解析的外部符號 "void __cdecl DeleteStack(struct _Node *)" ([email protected]

@[email protected]@@Z),該符號在函式 _main 中被引用。

然後是如何使用?

應該怎麼使用該語句呢?

剛開始,我簡單地在C++原始檔的前面使用該語句宣告,但是還是出錯,而且是在編譯階段就報錯。

  1. error C2732: 連結規範與“DeleteStack”的早期規範衝突。  

為什麼會出現這個錯誤呢?因為C++原始檔已經引入了C的標頭檔案,在標頭檔案裡,宣告該函式時沒有extern修飾,而這裡有extern修飾,所以衝突了。解決的辦法有兩個。

一。在C標頭檔案中加上extern修飾符。

直接加,也不行。因為C原始檔也包含了這個標頭檔案,當編譯C原始檔時,就會出現錯誤。所以,需要一種機制來區分是編譯C還是C++檔案。方法如下:

  1. #ifdef __cplusplus
  2. extern"C"
  3. #endif
  4.  void DeleteStack(Stack stack);  

因為在編譯C++檔案時,自動定義前處理器名字__cplusplus,而編譯C時,沒有該處理器名字。所以只有編譯C++時,才有符號extern “C”。

此外,連結指示extern "C"有單個和複合兩種形式。以上為單個形式,複合形式可以同時將幾個函式宣告為extern "C"

  1. extern"C" {  
  2. void DeleteStack(Stack stack);  
  3. void PrintStack(Stack stack);  
  4. void Pop(Stack stack);  
  5. }  

加上前處理器名字如下:
  1. #ifdef __cplusplus
  2. extern"C" {  
  3. #endif
  4. void DeleteStack(Stack stack);  
  5. void PrintStack(Stack stack);  
  6. void Pop(Stack stack);  
  7. #ifdef __cplusplus
  8. }  
  9. #endif

二。編寫一個C++風格的標頭檔案,在這裡新增extern修飾符。

使用方法一,很簡單。但是如果該標頭檔案是別人寫好,你無法修改。這個時候就要使用其它方法了。方法是定義C++自己的標頭檔案,檔名為"CStack.h"

  1. // CStack.h
  2. extern"C" {  
  3. #include "Stack.h";

2.c檔案呼叫c++檔案變數和函式

//xxx.cpp

#ifdef __cplusplus  
extern "C" {
#endif

int ignore_raw_data;

#ifdef __cplusplus  
}  
#endif

//xxx.c

extern int ignore_raw_data;

3.綜合使用
一般我們都將函式宣告放在標頭檔案,當我們的函式有可能被C或C++使用時,但我們無法確定函式是在c還是cpp裡面被定義(cxx都算,如蘋果是mm,google是cc),也就無法確定是否要將函式宣告在extern "C"裡,所以,我們應該新增
xxx.h

[cpp] view plain copy print?
  1. #ifdef __cplusplus
  2. extern"C"
  3. {  
  4. #endif
  5. //函式宣告
  6. #ifdef __cplusplus
  7. }  
  8. #endif

如果我們注意到,很多標頭檔案都有這樣的用法,比如string.h,等等。

[cpp] view plain copy print?
  1. //test.h
  2. #ifdef __cplusplus
  3. #include <iostream>
  4. usingnamespace std;  
  5. extern"C"
  6. {  
  7. #endif
  8. void mytest();  
  9. #ifdef __cplusplus
  10. }  
  11. #endif

這樣,可以將mytest()的實現放在.c或者.cpp檔案中,可以在.c或者.cpp檔案中include "test.h"後使用標頭檔案裡面的函式,而不會出現編譯錯誤。一般情況下,只需要把_cpluscplus和extern "C"的關鍵字放到h檔案就行了,c檔案和cpp檔案包含這個標頭檔案,而不需要在加入這兩個關鍵字,以下是特殊情況,因為cout不能再c裡面用。

[cpp] view plain copy print?
  1. //test.c或者test.cpp
  2. #include "test.h"
  3. void mytest()  
  4. {  
  5. #ifdef __cplusplus
  6. cout << "cout mytest extern ok " << endl;  
  7. #else
  8. printf("printf mytest extern ok n");  
  9. #endif
  10. }  
[cpp] view plain copy print?
  1. //main.cpp
  2. #include "test.h"
  3. int main()  
  4. {  
  5. mytest();  
  6. return 0;  
  7. }  


總之,以後再見到這樣的用法就不要大驚小怪了。


參考:

http://blog.csdn.net/u012234115/article/details/43272441

http://bbs.csdn.net/topics/380061545

http://www.cnblogs.com/rollenholt/archive/2012/03/20/2409046.html

相關推薦

混合編譯c++檔案呼叫c檔案變數函式c檔案呼叫c++檔案變數函式

1.c++檔案呼叫c檔案變數和函式 前言:以前見到extern "C"這樣的語句,只是簡單地知道跟外部連結有關,但是沒有深刻理解它的意思。 首先,為什麼要使用extern "C"修飾符? C++呼叫其它語言的函式,由於編譯器生成函式的機制不一樣,所以需要經過特殊處理,才可以

MATLABC++編譯器混合編譯Xcode 8的不相容錯誤

最近在看tracking方面的論文和程式碼,發現大多數都是用matlab和c++混合編譯的。對於linux和windows這兩個系統,暫時還沒有碰到編譯器不相容的問題,但是mac上面卻有問題,在命令列裡輸入: mex -setup 會有下面的錯誤提示:

C語言基礎 浮點數轉化整數(向上取整向下取整)| 求絕對值函式

Objective-C拓展了C,自然很多用法是和C一致的。比如浮點數轉化成整數,就有以下四種情況。  1.簡單粗暴,直接轉化 float f = 1.5; int a;a = (int)f; NSLog("a = %d",a); 輸出結果是1。(int)是強制型別轉化,丟棄浮點數的小數部分。

C++的拷貝建構函式operator=運算子過載,深拷貝淺拷貝explicit關鍵字

1、在C++編碼過程中,類的建立十分頻繁。 簡單的功能,當然不用考慮太多,但是從進一步深刻理解C++的內涵,類的結構和用法,編寫更好的程式碼的角度去考慮,我們就需要用到標題所提到的這些內容。 最近,在看單例模式,覺得十分有趣,然而如果想要掌握單例模式,就必須掌握這些內容。下

手把手教你用 Spring Boot搭建一個線上檔案預覽系統!支援pptdoc等多種型別檔案預覽

> 昨晚搭建環境都花了好一會時間,主要在浪費在了安裝 openoffice 這個依賴環境上(_Mac 需要手動安裝_)。 > > 然後,又一步一步功能演示,記錄,除錯專案,並且簡單研究了一下核心程式碼之後才把這篇文章寫完。 > > 另外,這篇文章我還會簡單分析一下專案核心程式碼。

Python函式的定義匿名函式函式的引數函式呼叫引數傳遞變數作用域遞迴呼叫

Python函式: 函式是組織好的,可重複使用的,用來實現單一,或相關聯功能的程式碼段。 Python提供了許多內建函式,比如print()。你也可以自己建立函式,這被叫做使用者自定義函式。 定義函式: 在Python中,定義一個函式要使用def語句,依次寫出函式名、

(23)物件呼叫非靜態方法執行過程靜態方法呼叫記憶體執行過程

public class Person{ private String name; private int age; private static String country="cn

機器學習練習記錄(1)偽逆法函式基於二次準則的H-K函式感知器法

勢函式的構造是人工勢場方法中的關鍵問題,典型的勢函式構造方法:P(θ)=f{d(θ,θ0),[dR(θ),O],dT}(1),式中 θ,θ0——機器人當前位姿與目標位姿向量;d(θ,θ0)——θ與θ0間的某種廣義距離函式;dR(θ),O——當前位姿下機器人與障礙物間的最小距離;dT——給定的門限值;P(θ)

執行緒順序控制四個執行緒ABCD向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B……

四個執行緒A、B、C、D向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B…… 最終達到的效果: A.txt內容為: A    B     C    D  &nbs

C語言__預處理(巨集定義檔案包含條件編譯

C語言__預處理(巨集定義、檔案包含、條件編譯) 預處理簡單理解     1.C語言在對源程式進行編譯之前,會先對一些特殊的預處理指令作解釋(比如之前使用的#include檔案包含指令),產生一個新的源程式(這個過程稱為編譯預處理),之後再進行通常的編譯

C++使用static的錯誤無法解析的外部符號“static”不應在檔案範圍內定義的成員函式上使用 .

   當將類的某個資料成員宣告為static時,該靜態資料成員只能被定義一次,而且要被同類的所有物件共享。各個物件都擁有類中每一個普通資料成員的副本,但靜態資料成員只有一個例項存在,與定義了多少類物件無關。靜態方法就是與該類相關的,是類的一種行為,而不是與該類的例項物件相關。     靜態資料成員的用途之一

c++11 條款22當使用Pimpl(指向實現的指標)時,在實現檔案裡定義特定的成員函式

條款22:當使用Pimpl(指向實現的指標)時,在實現檔案裡定義特定的成員函式         假如你曾經和過多的編譯構建時間抗爭過,你應該熟悉Pimpl(指向實現的指標)這個術語。這項技術是你可以把類的資料成員替換成一個指向實現類(結構)的指標,把原來在主類中的資料成員

16bit C & ASM 如何混合編譯

.exe 資料 bit urb 價值 nmake 開發 include 生成 起源: 今天在看以前沒看完的一本書《圖形程序開發人員指南》,在做裏面的例子。 第一章就出問題了,一個例子“L1_2.c, L1_3.asm" ,這是C程序和ASM匯編程序的混合編譯

Java編譯(二) Java前端編譯Java原始碼編譯成Class檔案的過程

Java編譯(二)Java前端編譯: Java原始碼編譯成Class檔案的過程               在上篇文章《Java三種編譯方式:前端編

Windows遍歷全盤所搜檔案,並通過多執行緒顯示搜尋進度(C++/C

程式設計軟體,系統 軟體:VS2017 工程屬性:MFC對話方塊(其他工程屬性稍作修改也適用) 系統:windows10家庭版 主要思想 1、利用CFileFind實現查詢檔案 2、利用遞迴實現全盤搜尋 3、多執行緒實現父對話方塊搜尋檔案時,子對話方塊顯示搜尋進度 軟體執行效

c++與matlab混合編譯———讀取mxArray內容

            mxArray作為一種特殊且複雜的資料結構,需要通過matrix.h提供的相關介面去訪問。在混合編譯過程中,matlab變數以mxArray形式儲存,通常用mxArray* 型別來指定matlab工作空間變數的地

CPP檔案C檔案混編將sqlite3加入自己的c++工程

今天嘗試將使用sqlite3資料庫,直接使用sqlite3的原始碼,得到sqlite3.c和sqlite3.h。 我想將他們加入到我的cpp工程裡面 所以我新建了一個mysqlite3.cpp檔案,在裡面呼叫了sqlite3的函式。 下面來說明我遇到的問題及解決方法 一共有兩種編譯方

C++函式的過載inline函式C/C++相互呼叫

一、過載(overloading) :函式名字相同,而引數不同。返回型別可以相同也可以不同。      若有題目為:實現兩個數相加,一般我們都會想到如下程式碼: int Sum(int a,int b) { return a

C# 將圖片匯出Excel(包括 建立Excel 檔案壓縮遞迴刪除檔案及資料夾)

新增引用 using ICSharpCode.SharpZipLib.Zip;   public void CreateDirectory(string DirectoryPath) { if (!Directory.Exist

C# 用Linq的方式實現對Xml檔案的基本操作(建立xml檔案增刪改查xml檔案節點資訊)

1 private static void GetXmlNodeInforOld( string xmlPath) 2 { 3 try 4 { 5