2013-05-15 62 views
6

爲了我的好奇心,我編寫了一個程序來顯示我的結構的每個字節。下面是代碼:在64位機器上的內存中的結構表示

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdint.h> 
#include <limits.h> 

#define MAX_INT 2147483647 
#define MAX_LONG 9223372036854775807 

typedef struct _serialize_test{ 
    char a; 
    unsigned int b; 
    char ab; 
    unsigned long long int c; 
}serialize_test_t; 


int main(int argc, char**argv){ 
    serialize_test_t *t; 
    t = malloc(sizeof(serialize_test_t)); 
    t->a = 'A'; 
    t->ab = 'N'; 
    t->b = MAX_INT; 
    t->c = MAX_LONG; 

    printf("%x %x %x %x %d %d\n", t->a, t->b, t->ab, t->c, sizeof(serialize_test_t), sizeof(unsigned long long int)); 

    char *ptr = (char *)t; 

    int i; 
    for (i=0; i < sizeof(serialize_test_t) - 1; i++){ 
     printf("%x = %x\n", ptr + i, *(ptr + i)); 
    } 

    return 0; 
} 

,這裏是輸出:

41 7fffffff 4e ffffffff 24 8 
26b2010 = 41 
26b2011 = 0 
26b2012 = 0 
26b2013 = 0 
26b2014 = ffffffff 
26b2015 = ffffffff 
26b2016 = ffffffff 
26b2017 = 7f 
26b2018 = 4e 
26b2019 = 0 
26b201a = 0 
26b201b = 0 
26b201c = 0 
26b201d = 0 
26b201e = 0 
26b201f = 0 
26b2020 = ffffffff 
26b2021 = ffffffff 
26b2022 = ffffffff 
26b2023 = ffffffff 
26b2024 = ffffffff 
26b2025 = ffffffff 
26b2026 = ffffffff 

,這裏是一個問題: 如果sizeof(long long int) is 8,那麼爲什麼sizeof(serialize_test_t) is 24,而不是32 - 我一直以爲結構的該尺寸舍入爲最大類型並乘以字段數,例如:8(字節)* 4(字段)= 32(字節) - 缺省情況下,沒有編譯指示包指令?

此外,當我將該結構轉換爲char *時,我可以從輸出中看到內存中值之間的偏移量不是8個字節。你能給我一個線索嗎?或者,這可能只是一些編譯器優化?

+3

你的假設是錯誤的。除了可能存在的標準之外,標準沒有對包裝或填充做任何說明。 –

+0

填充要求不是C或者甚至是依賴於曲線的,但是依賴於ABI,您將很可能獲得特定於x86_64 ABI的答案,兩個主要的答案win64和System V(其他所有內容)將非常相似。 –

+0

該標準說明了關於填充在結構中的一件事情:在結構的第一個元素之前不會有任何東西。除了第一個元素是由編譯器決定之前,是否有填充和其發生的位置。但對於任何結構類型'struct X x;','x'的地址也是'x'的第一個元素的地址(儘管地址類型不同)。 –

回答

4

在像SPARC或Intel [86] 86這樣的現代32位機器或68020上的任何摩托羅拉芯片上,每個數據通常必須是「自對齊」的,從地址開始它的類型大小的倍數。因此,32位類型必須在32位邊界上開始,在16位邊界上開始16位類型,8位類型可以在任何地方開始,struct/array/union類型具有其限制性最強的成員的對齊。

結構的總規模將取決於packing.In的情況下它會爲8字節所以最終的結構將類似於

typedef struct _serialize_test{ 

    char a;//size 1 byte 

    padding for 3 Byte; 

    unsigned int b;//size 4 Byte 

    char ab;//size 1 Byte again 

    padding of 7 byte; 

    unsigned long long int c;//size 8 byte 

}serialize_test_t; 

以這種方式前兩個和最後兩個是正確和總對準大小達到24個。

+1

您確定M68000,M68008,M68010與M68020有任何不同嗎? –

+0

@JonathanLeffler - > 68010改進68010以上,具有32位算術邏輯單元(ALU),以前的68000和68010處理器只能訪問字(16位)和長字(32位)數據,如果它是字對齊的。 68020對數據訪問沒有對齊限制。 –

+0

但是對於這個特定情況,68000編譯器是否仍然按照OP的文章中的方式對齊結構成員?因爲在這個特定的例子中沒有16位訪問。 – Lundin

0

的襯墊通常加入使得結構是字大小的倍數(在此情況下8)

所以第2個字段是在一個8字節塊。第三個字段位於另一個8字節的塊中,最後一個位於一個8字節的塊中。總共24個字節。

char 
padding 
padding 
padding 
unsigned int 
unsigned int 
unsigned int 
unsigned int 
char       // Word Boundary 
padding 
padding 
padding 
padding 
padding 
padding 
padding 
unsigned long long int   // Word Boundary 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
unsigned long long int 
0

與對齊有關。

結構的大小未被四捨五入爲最大類型並乘以字段。 http://en.wikipedia.org/wiki/Data_structure_alignment#Architectures

對齊工作在該類型必須出現在那就是它的大小的倍數的存儲器地址,所以::

字符爲1個字節對齊,因此它的字節由各自類型的對準每個可以出現在內存中任何1的倍數(任何地方)的任何地方。

將unsigned int是需要在這是4

多的字符可以在任何地方的地址開始。

,然後將長長的需求是在8

的倍數。如果你看一看地址,這樣的話。

2

取決於編譯器選擇的對齊方式。但是,您可以合理預期以下缺省值:

typedef struct _serialize_test{ 
    char a;      // Requires 1-byte alignment 
    unsigned int b;    // Requires 4-byte alignment 
    char ab;      // Requires 1-byte alignment 
    unsigned long long int c;  // Requires 4- or 8-byte alignment, depending on native register size 
}serialize_test_t; 

鑑於上述要求,第一場將在抵消爲零。

字段b將從偏移量4開始(在3個字節的填充之後)。

下一個字段從偏移量8開始(不需要填充)。

下一個字段從偏移量12(32位)或16(64位)開始(在另一個3或7個字節的填充之後)。

根據您平臺上的long long的對齊要求,這會給您總共20或24的大小。

GCC有offsetof功能,你可以用它來識別任何特定成員的偏移,或者你可以自己定義一個:

// modulo errors in parentheses... 
#define offsetof(TYPE,MEMBER) (int)((char *)&((TYPE *)0)->MEMBER - (char *)((TYPE *)0)) 

這基本上計算使用地址的不同使用虛構的底邊偏移聚合類型的地址。

0

編譯器只關心結構成員逐個對齊。它沒有考慮整個結構。因爲在二進制級別結構不存在,只是在特定地址偏移處分配一大塊單個變量。沒有像「struct round-up」這樣的事情,只要所有結構成員都正確對齊,編譯器就不會在意struct的大小。

C標準沒有提到填充方式,除了不允許編譯器在結構的最開始處添加填充字節。除此之外,編譯器可以自由地在結構中的任何位置添加任意數量的填充字節。它可以填充999個字節,並且仍然符合標準。

所以編譯器經過結構並看到:這是一個字符,它需要對齊。在這種情況下,CPU可能可以處理32位訪問,即4字節對齊。因爲它只增加了3個填充字節。

接下來它找到一個32位int,不需要對齊,它保持原樣。然後是另一個char,3個填充字節,然後是64位int,不需要對齊。

+0

有一個地方,編譯器認爲'結構作爲一個整體',這是確保結構將被正確對齊,當它們的數組被分配。假設'unsigned long long'後面還有另一個'char d'字段;該結構將是8字節長的倍數(假設'unsigned long long'需要8字節對齊),所以在假設的'd'之後會有7個填充字節。 –

+0

@JonathanLeffler這是真的,但是這種情況與數組的性質有關,而不是結構。出於對齊的原因,一組字符理論上也可以在成員之間獲得相同的填充。我不記得C標準中的某些東西是否不允許數組內的這種填充。 – Lundin

相關問題