[英]Casting uint64_t on bitfield
我找到了位域用於網絡消息的代碼。 我想知道什么鑄造bitfield_struct data = *(bitfield_struct *)&tmp;
exaclty 和它的語法是如何工作的。 它不會違反嚴格的別名規則嗎? 這是代碼的一部分:
typedef struct
{
unsigned var1 : 1;
unsigned var2 : 13;
unsigned var3 : 8;
unsigned var4 : 10;
unsigned var5 : 7;
unsigned var6 : 12;
unsigned var7 : 7;
unsigned var8 : 6;
} bitfield_struct;
void print_data(u_int64_t * raw, FILE * f, int no_object)
{
uint64_t tmp = ntohll(*raw);
bitfield_struct data = *(bitfield_struct *)&tmp;
...
}
它不會違反嚴格的別名規則嗎?
是的,它會,所以代碼調用未定義的行為。 它也是高度不可移植的:
我們不知道給定系統使用的稱為“可尋址存儲單元”的抽象項的大小。 它不一定是 64 位,因此理論上可能有填充和其他令人討厭的東西隱藏在位字段中。 64 位unsigned
是可疑的。
我們也不知道位域是否使用與uint64_t
相同的位順序。 我們也不知道它們是否使用相同的字節序。
如果需要訪問uint64_t
單個位(字段),我建議使用按位移位這樣做,因為即使在不同的字節序架構之間,這也使代碼完全可移植。 那么您也不需要非便攜式ntohll
調用。
它所做的(或嘗試做的)非常簡單。
uint64_t tmp = ntohll(*raw);
該行采用指針 raw 中的值,反轉字節順序並將其復制到 temp 中。
bitfield_struct data = *(bitfield_struct *)&tmp;
此行將 temp(是 uint64)中的數據重新解釋為bitfield_struct
類型並將其復制到數據中。 這基本上相當於:
/* Create a bitfield_struct pointer that points to tmp */
bitfield_struct *p = (bitfield_struct *)&tmp;
/* Copy the value in tmp to data */
bitfield_struct data = *p;
這是因為通常bitfield_struct
和uint64
是不兼容的類型,您不能僅使用bitfield_struct data = tmp;
將一個分配給另一個bitfield_struct data = tmp;
代碼大概會繼續通過data
訪問位域中的字段,例如data.var1
。
現在,正如人們指出的那樣,有幾個問題使此代碼不可靠且不可移植。
位域在很大程度上依賴於實現。 解決方案? 閱讀手冊並弄清楚您的特定編譯器變體如何處理位域。 或者根本不使用位域。
不能保證 uint64_t 和 bitfield_struct 具有相同的對齊方式。 這意味着可能有填充可以完全抵消您的期望並讓您最終得到錯誤的數據。 一種解決方案是使用memcpy
來復制而不是指針,這可能會讓您遇到此特定問題。 或者使用編譯器提供的機制指定壓縮對齊。
當應用嚴格的別名規則時,代碼會調用 UB。 解決方案? 大多數編譯器都有一個可以啟用的no-strict-aliasing
標志,但會降低性能。 或者更好的是,使用bitfield_struct
和uint64_t
創建聯合類型,並使用它來重新解釋一個和另一個。 即使有嚴格的別名規則,這也是允許的。 使用memcpy
也是合法的,因為它將數據視為字符數組。
但是,最好的辦法是根本不要使用這段代碼。 您可能已經注意到,它過於依賴編譯器和特定於平台的東西。 相反,嘗試使用位掩碼和移位來完成同樣的事情。 這擺脫了上面提到的所有三個問題,不需要特殊的編譯器標志,也不必面對任何真正的可移植性問題。 最重要的是,它可以避免其他開發人員閱讀您的代碼,以后不必擔心此類事情。
右到左:
&tmp
取&tmp
地址
(bitfield_struct *)&tmp
地址是 bitfield_struct 類型的數據的地址
*(bitfield_struct *)&tmp
從*(bitfield_struct *)&tmp
提取值,假設它是 bitfield_struct 數據
bitfield_struct data = *(bitfield_struct *)&tmp;
將tmp存入數據,假設tmp是bitfield_struct
所以它只是使用額外的指針進行復制以避免編譯錯誤/不兼容類型的警告。
您可能不明白的是結構的位尋址。
unsigned var1 : 1; unsigned var2 : 13;
在這里你會找到更多關於它的信息: https : //www.tutorialspoint.com/cprogramming/c_bit_fields.htm
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.