QT C 中extern C 的作用

2021-06-21 13:00:22 字數 3628 閱讀 6111

前些天,程式設計序是用到了很久以前寫的c程式,想把裡面的函式利用起來,連線發現出現了找不到具體函式的錯誤: 

以下是假設舊的c程式庫

c的標頭檔案 

[cpp]view plain

copy

/*-----------c.h--------------*/

#ifndef _c_h_ 

#define _c_h_ 

extern

intadd(

intx, 

inty);   

#endif 

c的原始檔   

/*-----------c.c--------------*/

intadd(

intx, 

inty)   

c++的呼叫   

/*-----------cpp.cpp--------------*/

#include "c.h" 

void

main()   

這樣編譯會產生錯誤cpp.obj : error lnk2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@yahhh@z),原因是找不到add的目標模組 

這才令我想起c++過載的函式命名方式和c函式的命名方式,讓我們回顧一下:c中函式編譯後命名會在函式名前加以"_",比如add函式編譯成obj檔案時的實際命名為_add,而c++命名則不同,為了實現函式過載同樣的函式名add因引數的不同會被編譯成不同的名字 

例如 [cpp]view plain

copy

intadd(

int, 

int)==>add@@yahhh@z,   

float

add(

float

, float

)==>add@@yammm@z,   

以上是vc6的命名方式,不同的編譯器會不同,總之不同的引數同樣的函式名將編譯成不同目標名,以便於函式過載是呼叫具體的函式. 

編譯cpp.cpp中編譯器在cpp檔案中發現add(1, 0);的呼叫而函式宣告為extern int add(int x, int y);編譯器就決定去找add@@yahhh@z,可惜他找不到,因為c的原始檔把extern int add(int x, int y);編譯成_add了; 

為了解決這個問題c++採用了extern "c",這就是我們的主題,想要利用以前的c程式庫,那麼你就要學會它,我們可以看以下標準標頭檔案你會發現,很多標頭檔案都有以下的結構 

[cpp]view plain

copy

#ifndef __h 

#define __h 

#ifdef __cplusplus 

extern

"c"   

#endif 

#endif /*__h*/ 

如果我們仿製該標頭檔案可以得到 

[cpp]view plain

copy

#ifndef _c_h_ 

#define _c_h_ 

#ifdef __cplusplus 

extern

"c"   

#endif 

#endif /* _c_h_ */ 

這樣編譯 

[cpp]view plain

copy

/*-----------c.c--------------*/

intadd(

intx, 

inty)   

這時原始檔為*.c,__cplusplus沒有被定義,extern "c" {}這時沒有生效對於c他看到只是extern int add(int, int); 

add函式編譯成_add(int, int); 

而編譯c++原始檔 

[cpp]view plain

copy

/*-----------cpp.cpp--------------*/

#include "c.h" 

void

main()   

這時原始檔為*.cpp,__cplusplus被定義,對於c++他看到的是extern "c" 編譯器就會知道 add(1, 0);呼叫的c風格的函式,就會知道去c.obj中找_add(int, int)而不是add@@yahhh@z; 

這也就為什麼dll中常看見extern "c" {},windows是採用c語言編制他首先要考慮到c可以正確呼叫這些dll,而使用者可能會使用c++而extern "c" {}就會發生作用 

一、修飾名(decorated name) 

c/c++程式中的函式在內部是通過修飾名來標識的.修飾名是在函式定義或原型編譯階段由編譯器建立字串.當你在link等工具中要指定乙個函式名時,會用到修飾名. 

1、使用修飾名: 

大多數情況下,你不必知道函式的修飾名是什麼.聯結器等工具通常都能處理函式未修飾的名字.然而,在有些情況下,你可能需要指定函式的修飾名.對於c++過載函式和特定的成員函式(如:建構函式和析構函式),你必須指定這些函式的修飾名,以便聯結器等工具能夠匹配名字.同時,你也必須在那些引用c或c++函式名的彙編原始檔中使用修飾名. 

2、檢視修飾名: 

如果你編譯了乙個原始檔,該原始檔中包含了函式定義或原型,你可以獲得函式的修飾名形式. 

(1)用編譯器列表(compiler listing)來檢視: 

(i)通過將列表檔案型別編譯器選項(/fa[c|s]) 設定為下面中的一種,來產生列表檔案:assembly with machine code (/fac); assembly with source code (/fas); assembly, machine code, and source (/facs). 

(ii)在產生的列表檔案中,找到包含未經修飾的函式定義的行. 

(iii)查詢前面一行.proc near 命令標籤前就是函式名經過修飾後的形式. 

(2)使用dumpbin工具來檢視: 

在.obj或.lib上執行 dumpbin,使用/symbols選項.在輸出中查詢未經修飾的函式定義.後面跟著的就是經過修飾的函式名,用圓括號括起來的. 

二、替代連線說明: 

如果在c++中編寫乙個程式需要用到c的庫,那該如何?如果這樣宣告乙個c函式: 

void f(int a,char b); 

c++編譯器就會將這個名字變成相應的修飾名,比如:?f@@yaxhd@z. 

然而,c編譯器編譯的庫的內部函式名(聯結器使用)是完全不同的.這樣,當c++聯結器連線c的函式庫時,將會產生內部使用函式不匹配. 

故,c++中提供了乙個替代連線說明(alternate linkage specification),它是通過過載extern關鍵字來實現的. 

extern後跟乙個字串來指定想宣告的函式的連線型別,後面是函式宣告,比如: 

extern "c" void f(int a,char b); 

這樣,就是告訴編譯器是c連線,這樣就不會轉換函式名了.此例中,編譯後的內部函式名是_f.

QT C 中extern C 的作用

前些天,程式設計序是用到了很久以前寫的c程式,想把裡面的函式利用起來,連線發現出現了找不到具體函式的錯誤 以下是假設舊的c程式庫 c的標頭檔案 c.h ifndef c h define c h extern int add int x,int y endif c的原始檔 c.c int add i...

C C 中extern C 的作用分析

我們經常會在c c svjahslg程式中見到extern c 這是乙個很重要的概念。本文就來以例項形式講述c c 中extern c 的作用。分享給大家供大家參考之用。具體分析如下 作用 實現c和c 混合程式設計。原理 c和c 編譯器編譯之後,函式名會編譯成不同的名字,鏈結階段名字查詢會找不到目標...

extern「c」宣告的作用

extern c 簡介extern c 包含雙重含義,從字面上即可得到 首先,被它修飾的目標是 extern 的 其次,被它修飾的目標是 c 的。讓我們來詳細解讀這兩重含義。含義 1 被extern c 限定的函式或變數是extern型別的 extern是c c 語言中表明函式和全域性變數作用範圍 ...