搜尋
首頁php教程PHP开发C++中的extern 'C”用法詳解

C++中的extern 'C”用法詳解

Dec 19, 2016 pm 02:47 PM
extern

簡單來說,extern 「C」是C++宣告或定義C語言符號的方法,是為了與C相容。說來容易,要理解起來還是得費些周折,首先我們要從C++和C的差別說起。

符號

大家都知道,從程式碼到執行程式需要經過編譯和連結兩個過程,其中編譯階段會做語法檢測,程式碼展開,另外它還會做一件事,就是將變數轉成符號,連結的時候其實是透過符號來定位的。編譯器在編譯C和C++程式碼時,將變數轉成符號的過程是不同的。本文所使用的編譯器為gcc4.4.7

我們先來看一段簡單的程式碼

/* hello.c */  
#include <stdio.h>  
  
const char* g_prefix = "hello ";  
  
void hello(const char* name)  
{  
    printf("%s%s", g_prefix, name);  
}

注意,這裡的檔名hello.c,我們執行編譯gcc -c hello.c得到目標檔hello.o,在Linux下用nm查看目標檔的符號表得到以下結果($符號代表shell命令提示字元)

$ nm hello.o  
0000000000000000 D g_prefix  
0000000000000000 T hello  
                 U printf

這是C程式碼編譯後的符號列表,其中第三列為編譯後的符號名,我們主要看自己定義的全域變數g_prefix和函數hello,它們的編譯後的符號名稱和程式碼裡的名字是一樣的。我們將hello.c重新命名為hello.cpp,重新編譯gcc -c hello.cpp得到hello.o,在用nm查看,結果如下

0000000000000000 T _Z5helloPKc  
                 U __gxx_personality_v0  
0000000000000000 D g_prefix  
                 U printf

這是C++程式碼編譯後的符號列表,gcc會自動根據文件後綴名來識別C和C++程式碼,這時我們發現g_prefix的符號沒變,但函數hello的符號變成了_Z5helloPKc,這就說明gcc在編譯C和C++程式碼時處理方式是不一樣的,對於C程式碼,變數的符號名稱就是變數本身(在早期編譯器會為C程式碼變數前加底線_,現在預設都不會了,在編譯時可以透過編譯選項-fno-leading-underscore和-fleading-underscore來明確設定),而對於C++程式碼,如果是資料變數且沒有嵌套,符號名也是本身,如果變數名稱有嵌套(在名稱空間或類別中)或是函數名,符號名就會依下列規則來處理


1、 符號以_Z開始
2、 如果有嵌套,後面緊跟N,然後是名稱空間、類別、函數的名字,名字前的數字是長度,以E結尾
3 、 如果沒嵌套,則直接是名字長度後面跟著名字
4、 最後是參數列表,類型和符號對應關係如下

    int    -> i  
    float  -> f  
    double -> d  
    char   -> c  
    void   -> v  
    const  -> K  
    *      -> P

這樣就很好理解為什麼C++程式碼裡的void hello(const char*)編譯之後符號為_Z5helloPKc(PKc翻譯成型別要從右到左翻譯為char const *,這是編譯器內部的表示方式,我們習慣的表示方式是const char*,兩者是一樣的),c++filt工具可以從符號反推名字,使用方法為c++filt _Z5helloPKc

這樣也很容易理解為什麼C++支援函數重載而C不支援了,因為C++將函數修飾為符號時把函數的參數型別加進去了,而C卻沒有,所以在C++下,即便函數名稱相同,只要參數不同,它們的符號名稱是不會衝突的。我們可以透過下面一個例子來驗證變數名和符號的這種關係。

/ * filename : test.cpp */  
#include <stdio.h>  
  
namespace myname  
{  
    int var = 42;  
}  
  
extern int _ZN6myname3varE;  
  
int main()  
{  
    printf("%d\n", _ZN6myname3varE);  
    return 0;  
}

這裡我們在名稱空間namespace定義了全域變數var,根據前面的內容,它會被修飾為符號_ZN6myname3varE,然後我們手動聲明了外部變數_ZN6myname3varE並將其列印出來。編譯並運行,它的值正好就是var的值

$ gcc test.cpp -o test -lstdc++  
$ ./test  
42

extern "C"

有了符號的概念我們再來看extern “C”的用法就很容易了

extern "C"  
{  
    int func(int);  
    int var;  
}

它的意思就是告訴編譯器將extern “C”後面的括號裡的程式碼當做C程式碼來處理,當然我們也可以用單條語句來聲明

extern "C" int func(int);  
extern "C" int var;

這樣就聲明了C類型的func和var。很多時候我們寫一個頭檔聲明了一些C語言的函數,而這些函數可能被C和C++程式碼調用,當我們提供給C++程式碼調用時,需要在頭檔裡加extern “C”,否則C++編譯的時候會找不到符號,而給C程式碼呼叫時又不能加extern “C”,因為C是不支援這樣的語法的,常見的處理方式是這樣的,我們以C的函式庫函數memset為例

#ifdef __cplusplus  
extern "C" {  
#endif  
  
void *memset(void*, int, size_t);  
  
#ifdef __cplusplus  
}  
#endif

其中__cplusplus是C++編譯器定義的一個宏,如果這份程式碼和C++一起編譯,那麼memset會在extern "C"裡被聲明,如果是和C程式碼一起編譯則直接聲明,由於__cplusplus沒有被定義,所以也不會有文法錯誤。這樣的技巧在系統頭檔裡常被用到。



更多C++中的extern 「C」用法詳解相關文章請關注PHP中文網!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具