2017-02-02 66 views
1

我正在搞鏈接列表類型的數據結構以獲得更好的C指針和結構,我不明白這一點。爲什麼node * root = malloc(sizeof(int))分配16個字節的內存而不是4個?

我認爲malloc返回指針的大小爲sizeof的第一塊內存的地址。

在這種情況下,我node struct看起來是這樣的,是16個字節:

typedef struct node{ 
    int index; 
    struct node* next; 
}node; 

我希望,如果我嘗試這樣做:node* root = malloc(sizeof(int))

malloc將只分配4個字節的塊並將該塊的地址返回到指針node

不過,我仍然能夠分配一個值,指數和獲得root指向下一個節點,因爲這樣的:

root->index = 0; 
root->next = malloc(sizeof(node)); 

,而最古怪的是,如果我嘗試運行:printf("size of pointer root: %lu \n", sizeof(*root));

我得到size of pointer root: 16,當時我顯然預計會看到4

發生了什麼事?

編輯:我剛剛試過malloc(sizeof(char)),它仍然告訴我,*root是16個字節。

+0

計算中的下一位會發生什麼? –

+0

@EdHeal,對不起,我不確定你在問什麼。 –

+0

未定義行爲意味着任何事情都可能發生,包括出現工作。 – aschepler

回答

3

這裏有一些事情正在進行,再加上一個可能在這個例子中不是問題,但總的來說是一個問題。

1)int不能保證是4個字節,雖然在大多數C編譯器實現它們。我會仔細檢查sizeof(int)看看你得到什麼。

2)node* root = malloc(sizeof(int))可能會導致各種問題,因爲sizeof(struct node)是不一樣的int。只要您嘗試訪問root->next,您就有未定義的行爲。

3)sizeof(struct node)不只是int,它是intpointer。指針是(據我所知,有人引用標準,如果不是的話)整個程序的大小相同,取決​​於編譯的方式(例如32位與64位)。您可以使用sizeof(void*)輕鬆地在編譯器上進行檢查。它應該與sizeof(int*)sizeof(double*)或任何其他指針類型相同。

4)您的結構應該sizeof(int) + sizeof(node*),但不能保證是。例如,假設我有這樣的結構:

struct Example 
{ 
    char c; 
    int i; 
    double d; 
}; 

你會想到它的大小爲,這是1 + 4 + 8 =在我的編譯器13,但在實踐中也不會。編譯器可以在內部「對齊」成員以匹配底層的指令體系結構,這通常會增加結構體的大小。權衡是他們可以更快地訪問數據。這不是標準化的,因編譯器而異,甚至具有不同設置的相同編譯器的不同版本。 You can learn more about it here

5)您的行printf("size of pointer root: %lu \n", sizeof(*root))不是指向根的指針的大小,它是結構根的大小。這使我相信你正在編譯它爲64位代碼,所以sizeof(int)是4,而sizeof(void*)是8,並且它們被對齊以匹配系統字(8字節),雖然我不能看到沒有看到你的編譯器,系統和設置。如果您想知道指向root的指針的大小,則需要執行sizeof(node*)sizeof(root)。您取消引用您的版本的指針,所以說sizeof(node)

底線相當於是,您所遇到的怪事是不確定的行爲。你不會找到一個具體的答案,只是因爲你認爲你在行爲中發現了一個模式並不意味着你應該使用它(除非你希望以後不可能找到讓你悲慘的錯誤)。

+0

謝謝你這個好的答案。回覆:4)當你談論底層架構時,這是關於* word *大小,其中單詞是最快的高速緩存可以一次處理什麼或類似的東西?並重新:5)我檢查,我的指針大小爲8個字節,整數是4個字節。我打印sizeof(* root)的原因是爲了獲得結構本身的大小。我期待4個字節,但我猜想類型設置實際上是告訴C如何導航保留在該地址的內存塊,對吧?有點像int數組會導航4個字節4個字節。 –

+0

在4上,它與字的大小密切相關,但我不確定它是否會在x86到ARM之間變化。據我瞭解,它只是不標準化,每個編譯器可以定義它的方式。我的理解是它應該是字的大小,除非數據類型溢出字大小(在32位x86上8字節的「double」),在這種情況下,我不知道。 – Cody

+0

5,我的意思是澄清你的'printf'中的文本是不正確的,因爲這不是指針,它是數據類型(雖然你的評論似乎澄清這是你的意圖)。因爲你說你的int是4個字節,所以它的結構絕對大於4個字節,所以它需要更多的指針空間。我認爲在理解對齊意味着什麼時,你有正確的概念,如果你需要更多的解釋,你應該問一個單獨的問題或[瀏覽這些](http://stackoverflow.com/search?q=c+struct+alignment) 。 – Cody

1

你沒有提及什麼系統(M $或linux,32位或64位),但你對內存分配的假設是錯誤的。內存分配與某些指定的邊界對齊,以保證支持類型的所有分配都正確對齊 - 對於64位模式,通常爲16字節。

檢查這 - libc的手冊:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

通過的malloc或realloc在GNU系統返回的塊的地址是 總是八(或16在64位系統)的倍數。如果您需要 一個地址爲2的倍數的塊,它使用aligned_alloc或posix_memalign。 aligned_alloc和 posix_memalign在stdlib.h中聲明。

2

這裏發生了一些事情。首先,C沒有邊界檢查。 C不會跟蹤您分配給變量的內存量。您沒有爲節點分配足夠的內存,但C沒有檢查該內存。下面的「作品」,但它確實沒有。

node* root = malloc(sizeof(int)); 

root->index = 0; 
root->next = malloc(sizeof(node)); 

由於沒有足夠的內存分配給結構,其他人的內存已被覆蓋。你可以通過打印指針來看到這一點。

printf("sizeof(int): %zu\n", sizeof(int)); 
printf("root: %p\n", root); 
printf("&root->index: %p\n", &root->index); 
printf("&root->next: %p\n", &root->next); 

sizeof(int): 4 
root: 0x7fbde5601560 
&root->index: 0x7fbde5601560 
&root->next: 0x7fbde5601568 

我只配備了4個字節,所以我只能從0x7fbde5601560到0x7fbde5601564好。 root->index很好,但root->next正在寫給別人的記憶。它可能是未分配的,在這種情況下,它可能被分配給其他變量,然後你會看到奇怪的事情發生。或者它可能是某些現有變量的內存,在這種情況下,它會覆蓋該內存並導致調試內存問題非常困難。

但它並沒有走出那麼遠的界限,從而走出分配給整個過程的內存,所以它沒有觸發你的操作系統的memory protection。這通常是segfault

注意root->nextroot->index之後的8個字節,因爲這是一個64位的機器,所以elements of a struct align on 8 bytes。如果你要在索引後面加入另一個整數,下一個仍然是8個字節。

還有另一種可能性:即使您只請求sizeof(int)內存,malloc可能會分配更多。大多數內存分配器以大塊的方式工作。但是這是所有的實現定義,所以你的代碼仍然有undefined behavior


,而最古怪的是,如果我嘗試運行:輸出( 「指針根的大小:%lu個\ N」 的sizeof(*根));我得到的指針根的大小:16,當我清楚地期望看到4

root是一個指向一個結構,你會期望sizeof(root)是指針大小,8個字節的64位機器上地址64位的內存。

*root指針的解引用,sizeof(*root)是結構的實際大小。那是16個字節。 (4爲整數,4爲填充,8爲結構指針)。同樣,C沒有跟蹤你分配了多少內存,它只跟蹤變量的大小應該是什麼。

+0

謝謝,這澄清了很多事情。 –

相關問題