深入理解數(shù)組和結(jié)構(gòu)體

數(shù)組概述

Array Allocation

對(duì)于數(shù)據(jù)類(lèi)型 T 和 整型常數(shù) N,聲明如下 T A[N],這個(gè)聲明有如下效果:

  1. 首先,計(jì)算機(jī)在內(nèi)存中分配一個(gè) L * N 字節(jié)的連續(xù)區(qū)域,這里 L 指的是數(shù)據(jù)類(lèi)型 T 的大小。
  2. 數(shù)組引入標(biāo)識(shí) A, 可以用 A 來(lái)作為指向數(shù)組開(kāi)頭的指針,這個(gè)指針的值表示為 Xa,可以用 0 ~ N-1 的整數(shù)索引來(lái)訪問(wèn)該數(shù)組元素,第 i 個(gè)數(shù)組元素會(huì)被存放在地址為 Xa + L*i 的地方。

訪問(wèn)數(shù)組

Array Access

C 語(yǔ)言允許對(duì)指針進(jìn)行運(yùn)算,而計(jì)算出來(lái)的值會(huì)根據(jù)該指針引用的數(shù)據(jù)類(lèi)型的大小進(jìn)行伸縮。也就是說(shuō),如果 p 是一個(gè)指向類(lèi)型為 T 的數(shù)據(jù)的指針,p 的值為 xp, 那么表達(dá)式 p+i 的值為 xp + L*i,這里 L 是數(shù)據(jù)類(lèi)型 T 的大小。操作符 & 可以產(chǎn)生指針,操作符 * 可以產(chǎn)生間接引用指針。也就是說(shuō),對(duì)于一個(gè)表示某個(gè)對(duì)象的表達(dá)式 Expr, &Expr 是給出該對(duì)象地址的一個(gè)指針。對(duì)于一個(gè)表示地址的表達(dá)式 AExpr, *AExp 是給出該地址處的值。因此,表達(dá)式 Expr 和 *&Expr 是等價(jià)的。可以對(duì)數(shù)組和指針應(yīng)用數(shù)組下標(biāo)操作,數(shù)組引用 A[ i ] 等價(jià)于表達(dá)式 *(A+i),它計(jì)算第 i 個(gè)數(shù)組元素的地址,然后訪問(wèn)這個(gè)內(nèi)存地址。

嵌套數(shù)組

Nested Array

當(dāng)我們創(chuàng)建數(shù)組類(lèi)型的數(shù)組時(shí),數(shù)組分配和引用的一般原則也是成立的。創(chuàng)建了一個(gè) T 數(shù)據(jù)類(lèi)型的嵌套數(shù)組 A [R][C],表示數(shù)組有 R 行,C 列,一個(gè) T 數(shù)據(jù)類(lèi)型需要 k 個(gè)字節(jié)存儲(chǔ)。 整個(gè)數(shù)組 A 的大小就是 R * C * k 字節(jié)。要訪問(wèn)嵌套數(shù)組的元素,編譯器會(huì)以數(shù)組起始為基地址,偏移量為索引,計(jì)算出期望的元素的偏移量。對(duì)于 A[R][C] 來(lái)說(shuō), &A[i][j] 的內(nèi)存地址為 &A[R][C] = x + k * ( C * i + j ),其中 x 為數(shù)組 A 的起始地址, k是數(shù)據(jù)類(lèi)型 T 以字節(jié)為單位的大小。

數(shù)據(jù)內(nèi)存排列順序

二維數(shù)組在內(nèi)存中的排列順序是以行為主,如上圖所示,int A[R][C] 中 R 的值先從 0 開(kāi)始,接著 C 的值從 0 ~ (c-1), 之后 R 的值到 1, C 的值再?gòu)?0 ~ (c-1), 直到 R 的值為 R-1。

多維數(shù)組

回憶一下,上面提到的嵌套數(shù)組。

Nested Array Example

那么接下來(lái)講講多維數(shù)組,這兩個(gè)概念可是不一樣的哦!

Multi-Level Array Example

上圖中,首先定義了三個(gè) zip_dig 類(lèi)型的數(shù)組 cmu,uw,ucb。然后定義了一個(gè) univ 的指針數(shù)組,該指針數(shù)組保存了 cmu,uw,ucb 的值。注意一下,univ 保存的是指針。

那么怎么訪問(wèn)嵌套數(shù)組呢?又怎么訪問(wèn)多維數(shù)組呢?

Array Element Accesses

對(duì)于嵌套數(shù)組來(lái)說(shuō),數(shù)組在內(nèi)存的排列是連續(xù)的。所以數(shù)組元素的內(nèi)存地址的計(jì)算方式是 Mem [ sea + 20 * index + 4 *dig ] ,其中 sea 是數(shù)組的起始地址,4 是數(shù)組中每個(gè)元素的字節(jié)大小, 20 是嵌套數(shù)組每行的內(nèi)存容量由 4 * 5 得來(lái),Mem表示內(nèi)存數(shù)組。

嵌套數(shù)組

對(duì)于多維數(shù)組來(lái)說(shuō),數(shù)組在內(nèi)存的排列則是非連續(xù)性的,依賴于指針的值。數(shù)組元素的內(nèi)存地址的計(jì)算方式需要經(jīng)過(guò) 2 個(gè)步驟,第一個(gè)步驟是計(jì)算出一維數(shù)組的指針地址 Mem[univ + 4 * index],第二個(gè)步驟才是計(jì)算出數(shù)組元素的地址 Mem[ Mem[univ + 4 * index] + 4* dig]。其中 univ 是多維數(shù)組的起始位置, 4 是數(shù)組中每個(gè)元素的字節(jié)大小,Mem表示內(nèi)存數(shù)組。

多維數(shù)組

結(jié)構(gòu)體

Structures

C 語(yǔ)言的 struct 聲明創(chuàng)建一個(gè)數(shù)據(jù)類(lèi)型,將可以能不同類(lèi)型的對(duì)象聚合到一個(gè)對(duì)象中,用名字來(lái)引用結(jié)構(gòu)中的各個(gè)組成部分。類(lèi)似于數(shù)組的實(shí)現(xiàn),結(jié)構(gòu)中的所有組成部分都存放在內(nèi)存中一段連續(xù)的區(qū)域內(nèi),而指向結(jié)構(gòu)的指針就是結(jié)構(gòu)第一個(gè)字節(jié)的地址,結(jié)構(gòu)體中的變量的偏移地址是由編譯器在編譯時(shí)期確定的。

Structures

對(duì)于 struct 的實(shí)例來(lái)說(shuō),我們可以使用點(diǎn)(.)操作符來(lái)引用 struct 的變量

struct rec r1; 
r1.i = val; 

也可以使用指針來(lái)表示 struct,如 struct rec *r = &r1;
使用指針和點(diǎn)(.)操作符來(lái)訪問(wèn) struct 變量

struct rec r1; 
(*r1).i = val; 

使用箭頭(->)操作符來(lái)訪問(wèn) struct 變量

r->i = val;

結(jié)構(gòu)體對(duì)齊

Structures & Alignment

數(shù)據(jù)對(duì)齊的實(shí)現(xiàn)是依賴于機(jī)器的,IA32 Linux,x86-64 Linux 和 window 都是有差異的。

Unaligned Data

如上圖所示,結(jié)構(gòu)體 S1 的內(nèi)存排列順序在未進(jìn)行對(duì)齊的情況下是這樣的。按結(jié)構(gòu)體的變量聲明順序排列,每個(gè)變量的容量大小按變量類(lèi)型的字節(jié)大小分配。

Aligned Data

在數(shù)據(jù)對(duì)齊中,有 2 個(gè)重要的點(diǎn),第

  1. 假設(shè)變量的數(shù)據(jù)類(lèi)型的字節(jié)大小 k 。
  2. 對(duì)于數(shù)據(jù)類(lèi)型的字節(jié)大小為 k 的變量,這個(gè)變量的地址必須是 k 的倍數(shù)。

對(duì)于結(jié)構(gòu)體 S1, char c 分配 1 個(gè)字節(jié), int i [2] 分配 8 個(gè)字節(jié), double v 分配 8 個(gè)字節(jié)。

  1. 從結(jié)構(gòu)體對(duì)齊的角度來(lái)說(shuō),結(jié)構(gòu)體 S1 的第一個(gè)變量 c 的地址必須是結(jié)構(gòu)體 S1 的最大數(shù)據(jù)類(lèi)型 double 的字節(jié)大小的倍數(shù)。
  2. 結(jié)構(gòu)體 S1 的第二個(gè)變量是 int 類(lèi)型的 i[0],根據(jù) “對(duì)于數(shù)據(jù)類(lèi)型的字節(jié)大小為 k 的變量,這個(gè)變量的地址必須是 k 的倍數(shù)” 的原則, i[0] 的地址必須是 4 的倍數(shù),所以需要在 c 之后增加 3 個(gè) 字節(jié)的位置之后再放置 i[0]。
  3. 結(jié)構(gòu)體 S1 的第二個(gè)變量是 int 類(lèi)型的 i[1],由于 i[0] 的地址是 4 的倍數(shù),而且 i[0] 占用 4 個(gè)位置,所以 i[0] 之后不需要增加額外的位置,可以直接放置 i[1]。
  4. 結(jié)構(gòu)體 S1 的第三個(gè)變量 v 是 double 類(lèi)型,所以 v 的地址需要是 8 的倍數(shù),i[1] 的地址是 8 的倍數(shù),而 i[1] 的容量是 4,所以需要再增加額外的 4 個(gè)字節(jié)之后再放置 v。
  5. 所以結(jié)構(gòu)體 S1 的內(nèi)存對(duì)齊格式如上圖所示。

總結(jié)

數(shù)組和結(jié)構(gòu)體是 C 語(yǔ)言編程過(guò)程中經(jīng)常使用到的數(shù)據(jù)類(lèi)型,對(duì)它們的理解越多越深,更能避免使用過(guò)程中的各種錯(cuò)誤。

參考

本文是華盛頓大學(xué)的公開(kāi)課 《 The Hardware / Software Interface 》的課程筆記,該課程的參考書(shū)籍是大名鼎鼎的 CSAPP 也就是《 深入理解計(jì)算機(jī)系統(tǒng) 》這書(shū)。文章截圖來(lái)源于課程,文章的內(nèi)容也參考了 CSAPP 的書(shū)本內(nèi)容。

  1. https://courses.cs.washington.edu/courses/cse351/17wi/videos.html
  2. https://book.douban.com/subject/26912767/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評(píng)論 3 425
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,635評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,539評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,255評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,646評(píng)論 1 326
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,838評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,146評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,338評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,565評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,983評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,257評(píng)論 1 292
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,059評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,296評(píng)論 2 376

推薦閱讀更多精彩內(nèi)容

  • 指針是C語(yǔ)言中廣泛使用的一種數(shù)據(jù)類(lèi)型。 運(yùn)用指針編程是C語(yǔ)言最主要的風(fēng)格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,464評(píng)論 3 44
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,731評(píng)論 18 399
  • 版權(quán)聲明:本文為 gfson 原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處。注:作者水平有限,文中如有不恰當(dāng)之處,請(qǐng)予以指正,萬(wàn)分感謝...
    gfson閱讀 3,130評(píng)論 0 6
  • 最近在看動(dòng)畫(huà)片《銀魂》,感覺(jué)有些詞特別好,就摘錄出來(lái)。以前就知道《銀魂》,但一直沒(méi)有看,因?yàn)閷?shí)在太長(zhǎng)了。現(xiàn)在人坑之...
    無(wú)霜之夜閱讀 497評(píng)論 0 0
  • 文/圖 南楚人 太陽(yáng)像個(gè)巨大的橙子在山脊上彈跳,一下蹦到山那邊去了。 猛踩一腳油門(mén),小車(chē)就著下坡的勁沖上坡,一直轟...
    老媒子閱讀 1,144評(píng)論 0 4