C/C++中union的大小是多少?它是它裏面最大的數據類型的大小嗎?如果是這樣,如果union的較小數據類型之一是活動的,編譯器如何計算如何移動堆棧指針?C/C++中union的大小
回答
該標準回答了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; };
編寫低級代碼時可以來非常方便。如果編譯器不支持該擴展,但無論如何你都要這樣做,你可以編寫未定義結果的代碼。所以如果你使用這個技巧,請確保你的編譯器支持它。
A union
總是佔用儘可能多的空間作爲最大的成員。目前使用什麼並不重要。
union {
short x;
int y;
long long z;
}
上述union
的實例將始終以至少一個long long
用於存儲。
旁註:由於Stefano,實際空間的任何類型(union
,struct
,class
)將採取不依賴於其他問題,如編譯器對準指出。我之所以沒有簡單介紹這一點,是因爲我只想告訴工會將最大的項目考慮在內。 重要的是要知道實際尺寸確實取決於對齊。
聯合的活動數據類型沒有概念。你可以自由閱讀和編寫工會的任何「成員」:這取決於你解釋你得到的。
因此,聯合的大小始終是其最大數據類型的大小。
你當然是錯的......標準的語言明確指向活動數據類型。但是,sizeof是一個編譯時操作,所以當然不依賴於活動數據類型。 – 2014-05-02 10:53:32
@JimBalter - 你對標準是正確的。我的意思是在C中你不能查詢一個關於它的_active datatype_的聯合。沒有什麼能阻止編碼器寫一個float並讀取一個int(並且獲取垃圾)。 – mouviciel 2014-05-02 16:04:57
你說過「沒有關於工會的活動數據類型的概念」。你錯了;擁有它。它不會聲稱你的意思與你爲了避免錯誤而寫的東西截然不同。 「沒有什麼能阻止編碼人員編寫一個float並讀取一個int(並且獲取垃圾)。」 - 當然沒有什麼可以阻止它...... C標準不會阻止*任何事情;它只會告訴你該行爲是否被定義 - 事實並非如此。正如人們一再指出的那樣,UB包括任何東西,甚至包括核武器爆炸。對於一些人來說,這會阻止他們編碼UB。 – 2014-05-02 20:14:24
大小將至少是最大的構成類型的大小。沒有「主動」類型的概念。
您應該真正將聯合看作是其中最大數據類型的容器,並將其與轉換的快捷鍵結合使用。當你使用其中一個較小的成員時,未使用的空間仍然存在,但它仍然未被使用。
您經常會在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;
};
最大元件的尺寸。
這就是爲什麼工會通常在一個結構中有意義,該結構有一個標誌,指示哪個是「活動」成員。
實施例:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};
這取決於編譯器,和上的選項。
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
什麼是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的單個數據成員,因爲每個數據成員共享相同的內存。通過使用聯盟,我們可以節省很多寶貴的空間。
sizeof可能會返回更大的東西的一個地方是使用long double時。雙倍長度是10個字節,但英特爾建議以16個字節對齊。 – dreamlax 2009-04-15 23:14:52
長雙是...好,取決於編譯器。我*認爲* PowerPC編譯器使用128bit長雙打 – 2009-04-15 23:15:49
是的哎呀,我打算說在x86上長雙。 – dreamlax 2009-04-16 01:08:47