標籤:

共用體只能同時儲存一個值嗎?

據說共用體任何時刻內只能儲存一個值,於是做了如下試驗:

#include&
#include&
using namespace std;

union one4all
{
int int_val;
long long_val;
double double_val;
};

int main(void)
{
union one4all num;
num.double_val = 4503599627370496;
cout &<&< num.double_val &<&< endl; num.int_val = 3; cout &<&< num.int_val &<&< endl; num.long_val = 2147483647; cout &<&< num.long_val &<&< endl; cout &<&< num.int_val &<&< endl; cout &<&< num.double_val &<&< endl; num.double_val = 2.35; cout &<&< num.double_val &<&< endl; cout &<&< num.long_val &<&< endl; cout &<&< num.int_val &<&< endl; cin.get(); return 0; }

其實做過幾種試驗,目前得出的一些結論是:

同一個union中,先賦值int再賦值long,無論long多大,都會覆蓋掉int原先的值。

先賦值浮點型再賦值整形,浮點型不會被整形所覆蓋,即使浮點型已經取到了最大值(設若union被分配的內存空間只有其所包含類型的最大空間,也就是double那麼大的話,那麼先賦值double的理論最大值,再賦值整形,double無論如何都應該被覆蓋了)

先賦值整形再賦值浮點型,無論浮點型多小,整形都會由於被覆蓋過,而導致輸出的結果不正確。

初步的揣測是,如果後輸入的數據結構比之前的一種複雜,則會將之前的輸入覆蓋掉。而如果後輸入的數據結構比之前的簡單(比如先輸入double,再輸入int),則union會同時保存兩種數據結構。

有更具體一點的存儲規則嗎?


union 在標準 C 和標準 C++ 中行為不同。

C 的 union 中,若 union 在生存期內,則所有成員都同時處於生存期內,但可能有非法對象表示。這允許類型雙關,即將一個位模式以多個類型解釋。

C++ 的 union 中,同一時刻只有一個成員在生存期內,使用不同的成員讀寫一般是未定義行為。例外是若公共起始序列存在,且在生存期中的成員擁有公共起始序列,則可通過其他成員的公共起始序列讀取。這實際上不允許類型雙關。

聯合體聲明 - cppreference.com?

zh.cppreference.com

聯合體聲明 - cppreference.com?

zh.cppreference.com


嚴格來說題主的代碼存在 UB ,不過考慮到不少編譯器默認有允許類型雙關的擴展,有時也可認為有確定結果並作出解釋。

一般實現是 union 的每個成員對象存儲都始於 union 存儲的首位元組。現在常見的 int 占 4 位元組, double 占 8 位元組。先寫入 double 成員再寫入 int 成員時,原先的 double 成員對象表示的高 4 位元組未被覆蓋( C 標準中是未指定的,實際上覆蓋也得到允許)。而被覆蓋的低位位元組,在實現中正好對應 double 的低位尾數。

由於輸出方式的設置,某些時候低位位元組的改變恰好會被掩蓋。這造成了你的 one4all 能同時存儲 double 和 int 的錯覺。

(參見 std::basic_ostream::operator&<&< - cppreference.com 、 std::num_put::put, std::num_put::do_put 、 std::basic_ios::init - cppreference.com 、std::printf, std::fprintf, std::sprintf, std::snprintf )

另外也有 NaN-boxing 這種技術能在浮點 NaN 值里放別的東西,不過這就和 union 沒什麼關係了。


直接回答你的問題,不是。

你修改int或者long之後再列印double沒有看出變化的根本原因是你列印的精度太低了,你可以包含下iomanip頭文件,然後把兩次列印double的語句改為

cout &<&< setprecision(16) &<&< nun.double_val &<&< endl;

這樣就可以看出差別了。


看書,書上的解釋是對的。

union one4all
{
int int_val;
long long_val;
double double_val;
};

相當於:

union one4all
{
byte[4];
byte[8];
byte[8];
};


cpp類型雙關不是安全的 新標準有tagged union強化這個傾向


C的union和「同時存儲幾個值」沒關係,只和內存布局有關係。

union這種東西是為了方便一些packed的數據結構存在的,比如ip/tcp/udp的header,gdt和idt的描述符,頁表項這種。所以說即使是packed也盡量不要用union,其他情況下也基本更沒有用union的必要。

在C++里不要用union。


推薦閱讀:

和團隊做物理引擎,做到商業程度還需要什麼?
c++不滿足於小黑框控制台,下一步還應該學什麼呢?
如何學好編程語言以及進行軟體開發?
C/C++ 中 bool 相比於 char 有什麼優勢?為什麼要設立 bool 類型?
c/c++中簡單加法出現奇怪的錯誤?

TAG:CC |