1. 程式人生 > >關於union的記憶體對齊問題,從二進位制數分析

關於union的記憶體對齊問題,從二進位制數分析

今天遇到一個感覺很奇葩的問題,一般都說union是公用記憶體,然後有下面幾個需要注意的點:

1.union是共用儲存空間的
2.它分配的空間大小為資料型別的最大位元組的最小倍數
3.在union中,所有的共用體成員共用一個空間,並且同一時間只能儲存其中一個成員變數的值。(這裡要注意)

下面看一段程式碼:

#include <iostream>
using namespace std;
union lia
{
    int s;
    double b;
}li;
int main()
{
    li.b = 45;
    li.s = 3072;
    cout << li.s << endl << li.b << endl;  
}

輸出結果為:

3072
45

不知道你們會不會有奇怪呢?在前面的第三個知識點說"同一時間只能儲存其中一個成員變數的值",那這個為什麼輸出的兩個值都正確呢?

在來看一下這個:

如果把上面的第10行和第11行換一下位置,結果會是什麼樣的呢?

#include <iostream>
using namespace std;
union lia
{
    int s;
    double b;
}li;
int main()
{
    li.s = 3072;
    li.b = 45;
    cout << li.s << endl << li.b << endl;
}

輸出結果:

0
45

這個結果到還是和上面的知識點說的是一樣的,那到底是為什麼呢?

首先請在注意三個東西:

1. union記憶體地址對齊是從低地址對齊開始的;

2. IEEE規定單精度浮點數(float) 的格式是:   1位符號位+8位指數位 +23位尾數位(總共四位元組,32位),而且採用隱藏位為1

3. IEEE規定雙精度浮點數(double) 的格式是:   1位符號位+11位指數位 +52位尾數位(總共八位元組,64位),而且採用隱藏位為1

我們就來看看3072這個雙進度浮點數的記憶體分佈:

3072 = 1.5 * 2^11

那麼:  符號位 : 0(正數)

           指數位: 11+10 = 21 ,所以為 :000 000 101 01 (21)

           尾數位: 1000```0(51個0),這裡表示十進位制的0.5

而45(10) = 101101(2) 

低地址對齊,這樣45就會覆蓋掉尾數位的後六位000000為101101,但是這個浮點數的精度太低了,以至於編譯器會省略掉,所以,輸出來的結果還是原來的3072;

可是,如果是第二種情況,那麼3072的最後六位就會全部變為0,這樣,原本45就會變為0 了

如果大家覺得有問題,這裡可以在做一個測試:

#include <iostream>
using namespace std;

union lia
{
    int s;
    double b;
}li;
int main()
{
    li.b = 45;
    double bb = li.b;
    li.s = 3245;
    cout << li.s << endl;
    cout << li.b << endl;
    cout << bb << endl;
    if(li.b == bb)
        cout << "equal" << endl
    else
        cout << "not equal" << endl;//事實證明他們是不相等的,也就是說li.s賦值的時候改變了,原來的值

    /*//這裡是用來證明 == 會進行二進位制層面的比較,即:比較每一個二進位制位,因為你可能會說浮點數直接的比較是不能用==直接進行,但是用==表示它會進行所有二進位制位的比較
    double b,c;
    b = 4.0;
    c = 4.0;
    cout << (b == c) << endl;//輸出為1,表示它們是相等的
    */
}

建議:有時候,我們如果要驗證一個東西,就需要從他們的本質上來驗證,而不是別人所謂的經驗

另外:

  整型資料在記憶體中的存放形式

如果定義了一個整型變數i:

int i;

i=10; 

0

0

0

0

0

0

0

0

0

0

0

0

1

0

1

0

數值是以補碼錶示的:

n         正數的補碼和原碼相同;

n         負數的補碼:將該數的絕對值的二進位制形式按位取反再加1。

例如:

求-10的補碼:

10的原碼:

0

0

0

0

0

0

0

0

0

0

0

0

1

0

1

0

      取反:

1

1

1

1

1

1

1

1

1

1

1

1

0

1

0

1

再加1,得-10的補碼:

1

1

1

1

1

1

1

1

1

1

1

1

0

1

1

0

由此可知,左面的第一位是表示符號的。