c++的 union類型平時用的真心少 從來沒有認真研究過他
今天看了幾道c++的筆試題 有幾道 關於union的 題目 現在總結下
第一題:
union V {
struct X {
unsigned char s1:2;
unsigned char s2:3;
unsigned char s3:3;
} x;
unsigned char c;
} v;
v.c = 100;
printf("%d", v.x.s3);
答案爲 3 (s2 爲 1 , s1爲0)
網上的解釋
v是聯合體(共用體)變量,共有兩個元素x和c,都需要一個字節,它們分配於同一個地址。
而x是結構體變量,共有三個元素s1、s2、s3,分別佔2位、3位、3位。分配內存時低位在前,最位在後。
當有v.c=100(其二進制爲01100100)時,各變量的關係及內存存儲情況見圖所示。
其中x的成員s3爲二進制的011,即十進制的3,所以輸出結果爲3。
同理的第二題:
#i nclude <stdio.h>
union
{
int i;
char x[2];
}a;
void main()
{
a.x[0] = 10;
a.x[1] = 1;
printf("%d",a.i);
}
int 爲 .. ... 0000 0001 0000 0010 答案:266 (低位低地址,高位高地址,內存佔用情況是Ox010A)
學習中 看了些文章 有所收穫 下面轉兩篇文章
一篇是 union和struct的 大小問題
1,對於union,對齊的大小是最大的基本元素的對齊大小;對象的大小必須是該基本元素大小的整數倍;
2,對於struct,對齊的大小也是最大的基本元素的對齊大小,對象的大小需要考慮元素的對齊,並且需要是最大基本元素的整數倍;同時有#pragma pack修飾的情況,關於struct請詳細參考另外一個帖子。
3,這裏所說的struct和union的對齊,是指其作爲其他複雜對象中的元素的時候要求的對齊,對於本身大小的計算並沒有關係。本身的大小隻和其所包含的基本元素的對齊有關係。
(copy 一段網上的對齊規則 1,數據成員對齊規則:結構的數據成員,第一個數據成員放在offset爲0的地方,以後每個數據成員存儲的起始位置要從該成員大小的整數倍開始(比如int在32位機爲4字節,則要從4的整數倍地址開始存儲) 2,,結構體作爲成員:如果一個結構體裏有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲(struct a裏存有 stuct b, b裏有char,int,double等元素,那麼b應該從8的整數倍開始存儲) 3,收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。)
例子一:
union U1
{
char a[9]; //對齊大小是1,大小是9個字節
int b; //對齊大小是4,大小是4個字節
};
所以該union的對齊大小是4個字節;大小爲大於等於max(9,4)=9併爲4的整數倍,所以是12字節。
例子二:
union U1_Another
{
char a[9]; //對齊大小是1,大小是9個字節
double b; //對齊大小是8,大小是8個字節
};
所以該union的對齊大小是8個字節,大小是大於等於9併爲8的整數倍,即爲16字節;
例子三:
union U2
{
U1 a; //對齊大小是4個字節,大小是12字節
double b; //對齊大小是8個字節,大小是8字節
};
所以該union的對齊大小是8個字節,大小是16;
例子四:
struct S1
{
char a[13]; //對齊大小是1字節,位置是[0,12]
double c; //對齊大小是8字節,位置是[16,23]
}
所以該結構體是8字節對齊,大小爲24個字節;
一.union數據類型
union顧名思義,聯合體,float,int,char等等類型數據共用一塊內存空間。其大小爲佔據內存空間最大的那個變量的空間大小。
eg:
union
{
float a;
char b[2];
}u_test;
那麼union所佔的內存空間大小爲float類型變量a所佔的內存大小,在常見32位計算機上爲4字節。
二.大小端對union的影響
一般x86體系的計算機採用的是小端模式:高高低低,高字節數據存放在內存中的高地址處,低字節數據存放在內存中的低地址處。
反之,大端模式就是高低高低了,高字節數據存放在內存中的低地址處,低字節數據存放在內存中的高地址處。
16bit寬的數0x1234在Little-endian模式(以及Big-endian模式)CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址 |
小端模式存放內容 |
大端模式存放內容 |
0x4000 |
0x34 |
0x12 |
0x4001 |
0x12 |
0x34 |
說明:內存中最小的單位爲1字節(8bit),上表中的0x4000正是這樣一個字節內存,顯然相對於0x4001而言,0x4000是低地址。低字節數據0x34就存放於內存的低地址0x4000處,而高字節數據0x12就存放於內存的高地址0x4001處。
32bit寬的數0x12345678在Little-endian模式以及Big-endian模式)CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址 |
小端模式存放內容 |
大端模式存放內容 |
0x4000 |
0x78 |
0x12 |
0x4001 |
0x56 |
0x34 |
0x4002 |
0x34 |
0x56 |
0x4003 |
0x12 |
0x78 |
三.測試大小端對union的影響,假設union分配的內存地址首地址爲0,
eg:
union u_test
{
int a;
char b[2];
}mm;
寫一個測試函數:
void test_union(void)
{
mm.a = 65*256 + 97;
printf("union's b[0] is: %c,b[1] is: %c\n",mm.b[0],mm.b[1]);
}
這個結構體的大小爲int a的大小4字節。假設在內存中是從地址0開始的連續4個單位。顯然這4個字節的內存塊的存放着a的值。則按照高高低低的原則,低內存地址0應該存放的是低字節數據97,依此往下存放,即內存地址爲1的內存單元存放着十進制數爲65的數據,內存地址爲2的內存單元存放着0,內存地址爲3的內存單元存放着0。
內存地址 |
小端模式存放內容 |
0 |
存放着十進制97 |
1 |
存放着十進制65 |
2 |
0 |
3 |
0 |
所以上面的函數test_union執行的結果將會是打印小寫字母a(97)和大寫字母A(65).
那如果是大端模式將會是怎樣的結果呢?如下:
內存地址 |
大端模式存放內容 |
0 |
0 |
1 |
0 |
2 |
十進制65 |
3 |
十進制97 |
四.測試CPU是大端還是小端模式?
當然對於一般的CPU,我們已經瞭解到:
Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式。
如果不知平臺到底運行於何種模式,也根據上面的這些特性可以自己測試出來?
- /*
- ** 函數名:checkCPU
- ** 輸 入:none
- ** 輸 出:0-- 大端模式
- ** 1-- 小端模式
- ** 功 能:檢測CPU的大小端模式
- */
- int checkCPU(void)
- {
- union check
- {
- int i;
- char j;
- }c;
- c.i = 1;
- return (c.j == 1);
- }
注: union 爲低地址對齊 如果是 little 則int 爲 01 00 00 00 然後 j 對齊低地址 爲01 所以c.j==1
如果爲big爲 00 00 00 01 然後j對齊低地址 爲 00 所以c.j==0
或者:
bool IsBigendian()
{
unsigned short usData = 0x1122;
unsigned char *pucData = (unsigned char*)&usData;
return (*pucData == 0x11);
}
注: 轉換 也是從地地址開始處理 若爲 little usData 存爲 22 11 pucData從低地址開始獲取 則爲 22
若爲 big usData存爲 11 22 pucData從低地址開始獲取 則爲 11