首頁 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脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)