2009-04-11 79 views
34

C/C++中union的大小是多少?它是它裏面最大的數據類型的大小嗎?如果是這樣,如果union的較小數據類型之一是活動的,編譯器如何計算如何移動堆棧指針?C/C++中union的大小

回答

25

該標準回答了C++標準第9.5節或C99標準第6.5.2.3節第5段中的所有問題(或C11標準的第6段):

在一個聯合中,最多隻有一個數據成員可以在任何時候處於活動狀態,也就是說,至多一個數據成員的值可以存儲在任何時候都可以參加工會。 [注意:爲了簡化聯合的使用,做了一個特殊的保證:如果一個POD-union包含多個共享一個公共初始序列(9.2)的POD-structs,並且這個POD-union類型的一個對象包含POD結構體,允許檢查任何POD結構體成員的共同初始序列;見9.2。 ]聯合的大小足以包含其中最大的數據成員。每個數據成員都被分配好像它是結構的唯一成員。

這意味着每個成員共享相同的存儲器區域。有至多有一個成員活躍,但你不知道哪一個。您將不得不在其他地方存儲有關當前活動成員的信息。除聯合之外存儲這樣一個標誌(例如,將一個帶有整數的結構作爲類型標誌並將聯合作爲數據存儲)將會給你一個所謂的「區別聯合」:一個聯合知道什麼類型它目前是「積極的一員」。

一個常見的用途是在詞法分析器,在那裏你可以有不同的標記,但根據令牌,你有不同的信息存儲(把line到每個結構表現出共同的初始序列是什麼):

struct tokeni { 
    int token; /* type tag */ 
    union { 
     struct { int line; } noVal; 
     struct { int line; int val; } intVal; 
     struct { int line; struct string val; } stringVal; 
    } data; 
}; 

該標準允許您訪問每個成員的line,因爲這是每個成員的共同初始序列。

存在編譯器擴展,允許訪問所有成員,而不管當前哪個存儲其值。這允許在每個成員之間有效地重新解釋具有不同類型的存儲位。例如,以下可以被用於將浮點變量解剖成2個無符號短褲:

union float_cast { unsigned short s[2]; float f; }; 

編寫低級代碼時可以來非常方便。如果編譯器不支持該擴展,但無論如何你都要這樣做,你可以編寫未定義結果的代碼。所以如果你使用這個技巧,請確保你的編譯器支持它。

50

A union總是佔用儘可能多的空間作爲最大的成員。目前使用什麼並不重要。

union { 
    short x; 
    int y; 
    long long z; 
} 

上述union的實例將始終以至少一個long long用於存儲。

旁註:由於Stefano,實際空間的任何類型(unionstructclass)將採取不依賴於其他問題,如編譯器對準指出。我之所以沒有簡單介紹這一點,是因爲我只想告訴工會將最大的項目考慮在內。 重要的是要知道實際尺寸確實取決於對齊

+0

sizeof可能會返回更大的東西的一個地方是使用long double時。雙倍長度是10個字節,但英特爾建議以16個字節對齊。 – dreamlax 2009-04-15 23:14:52

+0

長雙是...好,取決於編譯器。我*認爲* PowerPC編譯器使用128bit長雙打 – 2009-04-15 23:15:49

+0

是的哎呀,我打算說在x86上長雙。 – dreamlax 2009-04-16 01:08:47

10

聯合的活動數​​據類型沒有概念。你可以自由閱讀和編寫工會的任何「成員」:這取決於你解釋你得到的。

因此,聯合的大小始終是其最大數據類型的大小。

+2

你當然是錯的......標準的語言明確指向活動數據類型。但是,sizeof是一個編譯時操作,所以當然不依賴於活動數據類型。 – 2014-05-02 10:53:32

+1

@JimBalter - 你對標準是正確的。我的意思是在C中你不能查詢一個關於它的_active datatype_的聯合。沒有什麼能阻止編碼器寫一個float並讀取一個int(並且獲取垃圾)。 – mouviciel 2014-05-02 16:04:57

+2

你說過「沒有關於工會的活動數據類型的概念」。你錯了;擁有它。它不會聲稱你的意思與你爲了避免錯誤而寫的東西截然不同。 「沒有什麼能阻止編碼人員編寫一個float並讀取一個int(並且獲取垃圾)。」 - 當然沒有什麼可以阻止它...... C標準不會阻止*任何事情;它只會告訴你該行爲是否被定義 - 事實並非如此。正如人們一再指出的那樣,UB包括任何東西,甚至包括核武器爆炸。對於一些人來說,這會阻止他們編碼UB。 – 2014-05-02 20:14:24

3

大小將至少是最大的構成類型的大小。沒有「主動」類型的概念。

2

您應該真正將聯合看作是其中最大數據類型的容器,並將其與轉換的快捷鍵結合使用。當你使用其中一個較小的成員時,未使用的空間仍然存在,但它仍然未被使用。

您經常會在Unix中看到與ioctl()調用結合使用,所有ioctl()調用都會傳遞相同的結構,其中包含所有可能的響應的聯合。例如。 這個例子來自/usr/include/linux/if.h,這個結構用於ioctl()的配置/查詢以太網接口的狀態,請求參數定義了union的哪一部分實際上正在使用:

struct ifreq 
{ 
#define IFHWADDRLEN 6 
    union 
    { 
     char ifrn_name[IFNAMSIZ];  /* if name, e.g. "en0" */ 
    } ifr_ifrn; 

    union { 
     struct sockaddr ifru_addr; 
     struct sockaddr ifru_dstaddr; 
     struct sockaddr ifru_broadaddr; 
     struct sockaddr ifru_netmask; 
     struct sockaddr ifru_hwaddr; 
     short ifru_flags; 
     int ifru_ivalue; 
     int ifru_mtu; 
     struct ifmap ifru_map; 
     char ifru_slave[IFNAMSIZ]; /* Just fits the size */ 
     char ifru_newname[IFNAMSIZ]; 
     void * ifru_data; 
     struct if_settings ifru_settings; 
    } ifr_ifru; 
}; 
0
  1. 最大元件的尺寸。

  2. 這就是爲什麼工會通常在一個結構中有意義,該結構有一個標誌,指示哪個是「活動」成員。

實施例:

struct ONE_OF_MANY { 
    enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag; 
    union { short x; int y; long long z; }; 
}; 
15

這取決於編譯器,和上的選項。

int main() { 
    union { 
    char all[13]; 
    int foo; 
    } record; 

printf("%d\n",sizeof(record.all)); 
printf("%d\n",sizeof(record.foo)); 
printf("%d\n",sizeof(record)); 

} 

此輸出:

如果我沒有記錯,這取決於編譯器付諸分配的空間排列。因此,除非您使用某些特殊選項,否則編譯器會將填充放入聯合空間。

編輯:用gcc,你需要使用編譯指令

int main() { 
#pragma pack(push, 1) 
     union { 
      char all[13]; 
      int foo; 
     } record; 
#pragma pack(pop) 

     printf("%d\n",sizeof(record.all)); 
     printf("%d\n",sizeof(record.foo)); 
     printf("%d\n",sizeof(record)); 

} 

這個輸出

你也可以看到它從拆機(去掉了一些的printf,爲清晰起見)

0x00001fd2 <main+0>: push %ebp    | 0x00001fd2 <main+0>: push %ebp 
    0x00001fd3 <main+1>: mov %esp,%ebp  | 0x00001fd3 <main+1>: mov %esp,%ebp 
    0x00001fd5 <main+3>: push %ebx    | 0x00001fd5 <main+3>: push %ebx 
    0x00001fd6 <main+4>: sub $0x24,%esp  | 0x00001fd6 <main+4>: sub $0x24,%esp 
    0x00001fd9 <main+7>: call 0x1fde <main+12> | 0x00001fd9 <main+7>: call 0x1fde <main+12> 
    0x00001fde <main+12>: pop %ebx    | 0x00001fde <main+12>: pop %ebx 
    0x00001fdf <main+13>: movl $0xd,0x4(%esp) | 0x00001fdf <main+13>: movl $0x10,0x4(%esp)           
    0x00001fe7 <main+21>: lea 0x1d(%ebx),%eax | 0x00001fe7 <main+21>: lea 0x1d(%ebx),%eax 
    0x00001fed <main+27>: mov %eax,(%esp)  | 0x00001fed <main+27>: mov %eax,(%esp) 
    0x00001ff0 <main+30>: call 0x3005 <printf> | 0x00001ff0 <main+30>: call 0x3005 <printf> 
    0x00001ff5 <main+35>: add $0x24,%esp  | 0x00001ff5 <main+35>: add $0x24,%esp 
    0x00001ff8 <main+38>: pop %ebx    | 0x00001ff8 <main+38>: pop %ebx 
    0x00001ff9 <main+39>: leave     | 0x00001ff9 <main+39>: leave 
    0x00001ffa <main+40>: ret      | 0x00001ffa <main+40>: ret  

其中唯一的區別是在主+13版中,編譯器在棧上分配0xd而不是0x10

0

什麼是C/C++ sizeof運算工會?它裏面最大的 數據類型的大小是多少?

,工會的大小是其最大成員的大小。

例如:

#include<stdio.h> 

union un 
{ 
    char c; 
    int i; 
    float f; 
    double d; 
}; 

int main() 
{ 
    union un u1; 
    printf("sizeof union u1 : %ld\n",sizeof(u1)); 
    return 0; 
} 

輸出:

sizeof union u1 : 8 
sizeof double d : 8 

這裏最大構件double。兩者的尺寸均爲8。所以,正如sizeof正確地告訴你,工會的規模確實是8

如果union的較小數據類型的一個 是活動的,編譯器如何計算如何移動堆棧指針?

它由編譯器內部處理。假設我們正在訪問union的一個數據成員,那麼我們不能訪問其他數據成員,因爲我們可以訪問union的單個數據成員,因爲每個數據成員共享相同的內存。通過使用聯盟,我們可以節省很多寶貴的空間。