前些天,程式設計序是用到了很久以前寫的c程式,想把裡面的函式利用起來,連線發現出現了找不到具體函式的錯誤:
以下是假設舊的c程式庫
c的標頭檔案
/*-----------c.h--------------*/
#ifndef _c_h_
#define _c_h_
extern int add(int x, int y);
#endif
c的原始檔
/*-----------c.c--------------*/
int add(int x, int y)
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因引數的不同會被編譯成不同的名字
例如
int add(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程式庫,那麼你就要學會它,我們可以看以下標準標頭檔案你會發現,很多標頭檔案都有以下的結構
#ifndef __h
#define __h
#ifdef __cplusplus
extern "c"
#endif
#endif /*__h*/
如果我們仿製該標頭檔案可以得到
#ifndef _c_h_
#define _c_h_
#ifdef __cplusplus
extern "c"
#endif
#endif /* _c_h_ */
這樣編譯
/*-----------c.c--------------*/
int add(int x, int y)
這時原始檔為*.c,__cplusplus沒有被定義,extern "c" {}這時沒有生效對於c他看到只是extern int add(int, int);
add函式編譯成_add(int, int);
而編譯c++原始檔
/*-----------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的標頭檔案 cpp view plain copy c.h ifndef c h define c h extern intadd intx,inty endif c的...
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 語言中表明函式和全域性變數作用範圍 ...