最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C/C++開發(fā)中extern的一些使用注意事項(xiàng)

 更新時(shí)間:2023年01月04日 09:30:21   作者:????????????  
這篇文章主要為大家介紹了C/C++開發(fā)中extern一些使用注意事項(xiàng)的事例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

前些日子,有友友問了我這樣的一道問題:

數(shù)組通過外部聲明為指針時(shí),數(shù)組和指針是不能互換使用的;那么請思考一下,在 A 文件中定義數(shù)組 char a[100];在 B 文件中聲明為指針:extern char *a;此時(shí)訪問 a[i],會發(fā)生什么;

先說結(jié)果,會引起 segmentation fault 報(bào)錯(cuò);

那接下來由博主來分析一番;

數(shù)組與指針的區(qū)別

在介紹 extern 之前,我們需要了解一下數(shù)組與指針有什么區(qū)別?

數(shù)組變量代表了存放該數(shù)組的那塊內(nèi)存,它是這塊內(nèi)存的首地址。這就說明了數(shù)組變量是一個(gè)地址,而且,還是一個(gè)不可修改的常量,具體來說,就是一個(gè)地址常量。

數(shù)組變量跟枚舉常量一樣,都屬于符號常量。數(shù)組變量這個(gè)符號,就代表了那塊內(nèi)存的首地址。注意,不是數(shù)組變量這個(gè)符號的值是那塊內(nèi)存的首地址,而是數(shù)組變量這個(gè)符號本身代表了首地址,它就是這個(gè)地址值。這就是數(shù)組變量屬于符號常量的意義所在。

由于數(shù)組變量是一種符號常量,它是一個(gè)右值,而指針,作為變量,卻是一個(gè)左值,一個(gè)右值永遠(yuǎn)都不是左值,那么,數(shù)組名永遠(yuǎn)都不會是指針!

舉個(gè)例子,char a[] 中的 a 是常量,是一個(gè)地址,char *a 中 a 是一個(gè)變量,一個(gè)可以存放地址的變量。

具體分析

了解了數(shù)組與指針的區(qū)別之后,讓我們來看看 extern 聲明全局變量的內(nèi)部實(shí)現(xiàn);

extern 是 C/C++ 語言中表明函數(shù)和全局變量作用范圍(可見性)的關(guān)鍵字,該關(guān)鍵字告訴編譯器,其聲明的函數(shù)和變量可以在本模塊或其它模塊中使用。

TIP :被 extern 修飾的全局變量不被分配空間,而是在鏈接的時(shí)候到別的文件中通過查找索引定位該全局變量的地址。

extern char a[];

這是一個(gè)外部變量的聲明,它聲明了一個(gè)名為 a 的字符數(shù)組,編譯器看到這個(gè)聲明就知道不必為這個(gè)變量分配空間,這個(gè) .cpp 文件中所有對數(shù)組 a 的引用都化為一個(gè)不包含類型的標(biāo)號,具體地址的定位留給鏈接器完成。編譯完成之后也得到一個(gè)中間文件,鏈接器遍歷這個(gè)文件,發(fā)現(xiàn)有未經(jīng)定位的標(biāo)號,于是它搜索其他中間文件,試圖尋找到一個(gè)匹配的空間地址,在此例中無疑鏈接器將成功地尋找到這個(gè)地址并將此中間文件中所有的這個(gè)標(biāo)號替換為鏈接器所尋找到的地址,最終生成的可執(zhí)行文件中,所有曾經(jīng)的標(biāo)號都應(yīng)當(dāng)已經(jīng)被替換為地址。這是一個(gè)正常工作過程,鏈接出來的可執(zhí)行文件至少在對于該數(shù)組的引用部分將工作得很好。

extern char * a; 

這是一個(gè)外部變量的聲明,它聲明了一個(gè)名為 a 的字符指針,中間過程與上同,經(jīng)過一番搜索,找到了一個(gè)分配過空間的名為 a 的地方(也就是我們先定義的那個(gè)字符數(shù)組),鏈接器并不知道它們的類型,僅僅是發(fā)現(xiàn)它們的名字一樣,就認(rèn)為應(yīng)該把 extern 聲明的標(biāo)號鏈接到數(shù)組 a 的首地址上,因此鏈接器把指針 a 對應(yīng)的標(biāo)號替換為數(shù)組 a 的首地址。這里問題就出現(xiàn)了:由于在這個(gè)文件中聲明的 a 是一個(gè)指針變量而不是數(shù)組,鏈接器的行為實(shí)際上是把指針 a 自身的地址定位到了另一個(gè) .c 文件中定義的數(shù)組首地址上,而不是我們所希望的把數(shù)組的首地址賦予指針 a(這很容易理解:指針變量也需要占用空間,如果說把數(shù)組的首地址賦給了指針 a,那么指針 a 本身在哪里存放呢?)。這就是癥結(jié)所在了。所以此例中指針 a 的內(nèi)容實(shí)際上變成了數(shù)組 a 首地址開始的 4 字節(jié)表示的地址(如果在 16 位機(jī)上,就是 2 字節(jié))。

上述加粗部分的可以理解為,鏈接器認(rèn)為 a 變量本身的內(nèi)存位置是數(shù)組的首地址,但其實(shí) a 的位置是其他位置,其內(nèi)容才是數(shù)組首地址。

舉個(gè)例子,定義 char a[] = "abcd",則外部變量 extern char a[] 的地址是 0x12345678 (數(shù)組的起始地址),而 extern char *a 是重新定義了一個(gè)指針變量 a,其地址可能是 0x87654321,因此直接使用 extern char *a 是錯(cuò)誤的。

通過上述分析,我們得到的最重要的結(jié)論是:使用 extern 修飾的變量在鏈接的時(shí)候只找尋同名的標(biāo)號,不檢查類型,所以才會導(dǎo)致編譯通過,運(yùn)行時(shí)出錯(cuò)。

extern "C"

extern "C" 包含雙重含義,從字面上即可得到:

  • 首先,被它修飾的目標(biāo)是 extern 的;
  • 其次,被它修飾的目標(biāo)是 C 的。

1、 被 extern "C" 限定的函數(shù)或變量是 extern 類型的;

extern int a;

僅僅是一個(gè)變量的聲明,其并不是在定義變量 a,并未為 a 分配內(nèi)存空間。變量 a 在所有模塊中作為一種全局變量只能被定義一次,否則會出現(xiàn)連接錯(cuò)誤。

通常,在模塊的頭文件中對本模塊提供給其它模塊引用的函數(shù)和全局變量以關(guān)鍵字 extern 聲明。例如,如果模塊 B 欲引用該模塊 A 中定義的全局變量和函數(shù)時(shí)只需包含模塊 A 的頭文件即可。這樣,模塊 B 中調(diào)用模塊 A 中的函數(shù)時(shí),在編譯階段,模塊 B 雖然找不到該函數(shù),但是并不會報(bào)錯(cuò),它會在連接階段中從模塊 A 編譯生成的目標(biāo)代碼中找到此函數(shù)。

extern 對應(yīng)的關(guān)鍵字是 static,被它修飾的全局變量和函數(shù)只能在本模塊中使用。因此,一個(gè)函數(shù)或變量只可能被本模塊使用時(shí),其不可能被 extern "C" 修飾。

2、被 extern "C" 修飾的變量和函數(shù)是按照 C 語言方式編譯和連接的;

未加 extern "C" 聲明時(shí)的編譯方式

作為一種面向?qū)ο蟮恼Z言,C++ 支持函數(shù)重載,而過程式語言 C 則不支持。函數(shù)被 C++ 編譯后在符號庫中的名字與 C 語言的不同。例如,假設(shè)某個(gè)函數(shù)的原型為:

void foo( int x, int y );

該函數(shù)被 C 編譯器編譯后在符號庫中的名字為 _foo,而 C++ 編譯器則會產(chǎn)生像 _foo_int_int 之類的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機(jī)制,生成的新名字稱為 “mangled name”)。

_foo_int_int 這樣的名字包含了函數(shù)名、函數(shù)參數(shù)數(shù)量及類型信息,C++ 就是靠這種機(jī)制來實(shí)現(xiàn)函數(shù)重載的。例如,在 C++ 中,函數(shù) void foo(int x, int y)void foo(int x, float y) 編譯生成的符號是不相同的,后者為 _foo_int_float。

同樣地,C++ 中的變量除支持局部變量外,還支持類成員變量和全局變量。用戶所編寫程序的類成員變量可能與全局變量同名,我們以 . 來區(qū)分。而本質(zhì)上,編譯器在進(jìn)行編譯時(shí),與函數(shù)的處理相似,也為類中的變量取了一個(gè)獨(dú)一無二的名字,這個(gè)名字與用戶程序中同名的全局變量名字不同。

未加 extern "C" 聲明時(shí)的連接方式

假設(shè)在 C++ 中,模塊 A 的頭文件如下:

// 模塊A頭文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo(int x, int y);
#endif

在模塊 B 中引用該函數(shù):

// 模塊B實(shí)現(xiàn)文件 moduleB.cpp
#include "moduleA.h"
foo(2, 3);

實(shí)際上,在連接階段,連接器會從模塊 A 生成的目標(biāo)文件 moduleA.obj 中尋找 _foo_int_int 這樣的符號;

extern "C" 聲明后的編譯和連接方式

extern "C" 聲明后,模塊 A 的頭文件變?yōu)椋?/p>

// 模塊A頭文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo(int x, int y);
#endif

在模塊 B 的實(shí)現(xiàn)文件中仍然調(diào)用 foo(2, 3),其結(jié)果是:

  • 模塊 A 編譯生成 foo 的目標(biāo)代碼時(shí),沒有對其名字進(jìn)行特殊處理,采用了 C 語言的方式;
  • 連接器在為模塊 B 的目標(biāo)代碼尋找 foo(2, 3) 調(diào)用時(shí),尋找的是未經(jīng)修改的符號名 _foo;

如果在模塊 A 中函數(shù)聲明了 fooextern "C" 類型,而模塊 B 中包含的是 extern int foo(int x, int y),則模塊 B 找不到模塊 A 中的函數(shù);反之亦然。

所以,可以用一句話概括 extern "C" 這個(gè)聲明的真實(shí)目的:實(shí)現(xiàn) C++ 與 C 及其它語言的混合編程。

以上就是C/C++開發(fā)中extern的一些使用注意事項(xiàng)的詳細(xì)內(nèi)容,更多關(guān)于C/C++開發(fā)extern使用事項(xiàng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言如何正確的終止正在運(yùn)行的子線程

    C語言如何正確的終止正在運(yùn)行的子線程

    這篇文章主要介紹了C語言如何正確的終止正在運(yùn)行的子線程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • C++最優(yōu)二叉樹哈夫曼樹算法解析

    C++最優(yōu)二叉樹哈夫曼樹算法解析

    這篇文章主要介紹了C++最優(yōu)二叉樹哈夫曼樹算法解析,哈夫曼樹又稱最優(yōu)二叉樹,是一種帶權(quán)路徑長度最短的二叉樹,所謂樹的帶權(quán)路徑長度,就是樹中所有的葉結(jié)點(diǎn)的權(quán)值乘上其到根結(jié)點(diǎn)的路徑長度,需要的朋友可以參考下
    2023-08-08
  • 適合初學(xué)者的C語言常量類型的講解

    適合初學(xué)者的C語言常量類型的講解

    常量是固定值,在程序執(zhí)行期間不會改變。這些固定的值,又叫做字面量。常量可以是任何的基本數(shù)據(jù)類型,比如整數(shù)常量、浮點(diǎn)常量、字符常量,或字符串字面值,也有枚舉常量。常量就像是常規(guī)的變量,只不過常量的值在定義后不能進(jìn)行修改
    2022-04-04
  • c++制作的時(shí)間函數(shù)類

    c++制作的時(shí)間函數(shù)類

    本文給大家分享的是一個(gè)個(gè)人使用C++編寫的時(shí)間函數(shù)類,主要是實(shí)現(xiàn)了類的定義和調(diào)用,相比較來說還算比較復(fù)雜的時(shí)間類了,推薦給小伙伴們,有需要的朋友可以參考下。
    2015-03-03
  • OpenCV使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤的方法詳解

    OpenCV使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤的方法詳解

    這篇文章主要為大家詳細(xì)介紹了OpenCV如何使用稀疏光流實(shí)現(xiàn)視頻對象跟蹤功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2023-02-02
  • C++ Boost Parameter超詳細(xì)講解

    C++ Boost Parameter超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-11-11
  • C語言內(nèi)嵌匯編API內(nèi)存搜索引擎實(shí)例

    C語言內(nèi)嵌匯編API內(nèi)存搜索引擎實(shí)例

    這篇文章主要介紹了C語言內(nèi)嵌匯編API內(nèi)存搜索引擎實(shí)例,涉及匯編語言與內(nèi)存相關(guān)操作,需要的朋友可以參考下
    2014-10-10
  • 利用Matlab制作三子棋游戲的示例代碼

    利用Matlab制作三子棋游戲的示例代碼

    三子棋是一種民間傳統(tǒng)游戲,又叫九宮棋、圈圈叉叉、一條龍、井字棋等。將正方形對角線連起來,相對兩邊依次擺上三個(gè)雙方棋子,只要將自己的三個(gè)棋子走成一條線,對方就算輸了。本文將用Matlab制作這一經(jīng)典游戲,感興趣的可以試一試
    2022-03-03
  • 全面了解結(jié)構(gòu)體、聯(lián)合體和枚舉類型

    全面了解結(jié)構(gòu)體、聯(lián)合體和枚舉類型

    下面小編就為大家?guī)硪黄媪私饨Y(jié)構(gòu)體、聯(lián)合體和枚舉類型。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-07-07
  • C++實(shí)現(xiàn)推箱子功能附加回撤示例

    C++實(shí)現(xiàn)推箱子功能附加回撤示例

    本文主要介紹了C++實(shí)現(xiàn)推箱子功能附加回撤示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評論

文水县| 浦东新区| 兴宁市| 上杭县| 通许县| 中阳县| 霞浦县| 汤阴县| 泽普县| 江山市| 富裕县| 金门县| 长泰县| 陈巴尔虎旗| 定边县| 绵竹市| 理塘县| 洛浦县| 长春市| 镇江市| 平远县| 福鼎市| 任丘市| 杭锦后旗| 扎兰屯市| 满洲里市| 张北县| 姚安县| 临泉县| 互助| 洛隆县| 会泽县| 兴安县| 兴山县| 兖州市| 宜阳县| 壶关县| 乐山市| 宁河县| 玉环县| 会东县|