簡體   English   中英

Memory 分配和基本 C 程序中未使用的字節

[英]Memory allocation and unused byte in basic C program

我對基本C程序上的 memory 分配有疑問。

#include <stdio.h>

int main()
{
    int  iarray[3];
    char carray[3];
    
    printf("%p\n", &iarray); // b8
    printf("%p\n", &iarray+1); // c4
    printf("%p\n", &carray); // c5
    
    return 0;
}

鑒於上面的代碼,您可以看到&iarray+1&carray有一個字節的差異,我不確定它的用途或內容,為什么編譯器會在兩個 arrays 之間分配一個未使用的字節?

我想也許它用來知道數組大小,但我知道sizeof是一個編譯時 function 知道大小而不分配真正的 memory,所以沒有用存儲數組大小

注: output 可以在每個printf的評論中看到。

b8
c4
c5

游樂場: https://onlinegdb.com/cTdzccpDvI

謝謝。

編譯器可以以任何他們認為合適的方式自由排列 memory 中的變量。 通常,它們將放置在 memory 偏移處,其值是變量大小的倍數,例如,4 字節intint數組將從 4 的倍數的地址開始。

在這種情況下,您有一個int數組,起始地址是 4 的倍數,后跟一個未使用的字節,然后是一個大小為 3 的char數組。理論上,一個intlong可以緊跟 memory 中的char數組如果它被定義為下一個可用地址是 8 的倍數。

從您的 output 看來,這些局部變量的堆棧排列方式如下:

b8-bb: 1st integer of iarray
bc-bf: 2nd integer of iarray
c0-c3: 3rd integer of iarray
c4:    padding probably, only compiler knows
c5-c7: carray

現在,當您執行&iarray+1時,您將獲取數組int[3]的地址,並將該數組類型的 +1 添加到其中。 換句話說,您將獲得下一個int[3]數組的地址,該數組確實位於 c4 (但不是因為只有一個int[3] )。

這段代碼實際上是有效的。 您不能取消引用此指針,但因為它正好指向iarray之后的 +1,所以擁有指針並打印其值是合法的(換句話說,不是未定義的行為,就像&iarray+2那樣)。

如果你也打印這個:

printf("%p\n", iarray+1);

您應該得到結果bc ,因為現在您采用int類型的指針( iarray被視為指向int的指針),將其加 1 ,得到下一個int

此行為純粹是(編譯器)實現定義的。 可能發生的事情是這樣的:

當調用具有局部變量的 function (在這種情況下為main()時,這些變量的 memory 將在堆棧上分配。 在這種情況下,需要 15 個字節,但很有可能堆棧分配需要 4 個字節的 alignment,因此分配了 16 個字節。

int -array 也可能必須是 4 字節對齊的。 因此int數組的地址是 4 的倍數。 char數組沒有任何 alignment 要求,因此它可以放置在剩余 4 個字節中的任何位置。

所以簡而言之,額外的字節是未使用的,而是由於 alignment 分配的。

發生這種情況的原因是您使用的特定編譯器在這種特殊情況下將 memory 從高地址分配到低地址。

編譯器分析main例程,發現carray需要 3 個int ,而iarray需要 3 個char 無論出於何種原因,它決定先處理carray

編譯器從計划的堆棧幀開始,該堆棧幀需要在某些點具有 16 字節 alignment。 堆棧上需要額外的數據,結果編譯器開始放置局部變量的點位於 8 模 16 的地址(因此其十六進制表示以 8 結尾)。 也就是說,從 7FFC565E90A8 及以上的某個地址開始,memory 用於管理堆棧幀。 本地對象的第一個字節將位於 7FFC565E90A7、7FFC565E90A6、7FFC565E90A5、7FFC565E90A4 等處。

編譯器將該空間的前三個字節用於carray 回想一下,我們是從高地址到低地址工作的。 (由於歷史原因,這是堆棧增長的方向;分配一些高地址作為起點,新數據放入低地址。)因此將carray放入地址7FFC565E90A5。 它填充 7FFC565E90A5、7FFC565E90A6 和 7FFC565E90A7 處的字節。

然后編譯器需要為iarray分配 12 個字節。 下一個可用的 12 個字節是從 7FFC565E9099 到 7FFC565E90A4。 但是 iarray 中的int元素需要 4 字節的iarray ,所以不能從 7FFC565E9099 開始。 因此,編譯器進行調整以使它們從 7FFC565E9098 開始。 然后iarray填充從 7FFC565E9098 到 7FFC565E90A3 的字節,而 7FFC565E90A4 未使用。

請注意,在其他情況下,編譯器可能會以不同的方式排列本地對象。 當您有多個具有不同對齊方式的對象時,編譯器可能會選擇將所有具有相同 alignment 的對象聚集在一起,以減少需要插入填充的位置數量。 編譯器還可以選擇按名稱的字母順序為對象分配 memory。 或者它可以按照恰好將它們存儲在其 hash 表中的順序來執行此操作。 或者這些東西的某種組合,例如按照 alignment 要求對所有對象進行聚類,然后在每個聚類中按名稱排序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM