[英]Obj-C: Is it really safe to compare BOOL variables?
我以前認為在64位Obj-C運行時BOOL實際上是_Bool而且它是一個真正的類型所以這樣編寫是安全的:
BOOL a = YES;
BOOL b = NO;
if (a != b) {...}
它一直在工作似乎很好,但今天我發現一個問題,當我使用像這樣的位字段結構:
typedef struct
{
BOOL flag1 : 1;
} FlagsType;
FlagsType f;
f.flag1 = YES;
BOOL b = YES;
if (f.flag1 != b)
{
// DOES GET HERE!!!
}
似乎從位字段返回的BOOL等於-1而常規BOOL為1,它們不相等!!!
請注意,我知道當一個任意整數被轉換為BOOL時的情況,因此變成一個“奇怪的”BOOL,這是不安全的比較。
但是在這種情況下,flag1字段和b都被聲明為BOOL並且從不進行轉換。 問題是什么? 這是編譯器錯誤嗎?
更大的問題是,比較BOOL是否真的安全,還是應該編寫XORing輔助函數? (這將是一件苦差事,因為布爾比較無處不在...)
我不重復使用C布爾類型解決了BOOL
可能遇到的問題。 這是真的 - 特別是在這里,你可以在下面看到 - ,但大多數問題是由於錯誤存儲到布爾(C)對象造成的。 但在這種情況下, _Bool
或unsigned
( int
)似乎是唯一可行的解決方案。 (除了帶有額外代碼的解決方案。)有一個原因:
我找不到關於Objective-C中BOOL
的新行為的精確文檔,但是你發現的行為是壞的和錯誤的。 我預計最新的行為類似於_Bool
。 在你的情況下,這不是真的。 (感謝您找到了!)也許這是為了向后兼容。 講述全文:
在C中, int
類型的對象是signed int
。 (這與char
有所不同。對於此類型,signedess是實現定義的。)
- int,signed或signed int
ISO / IEC 9899:TC3,6.7.2-2
每個逗號分隔的集合指定相同的類型,[...]
ISO / IEC 9899:TC3,6.7.2-5
但是由於歷史原因,有一個奇怪的例外:
如果int
對象是位字段,則它是實現定義的,無論是signed int
還是unsigned int
。 (可能這是因為過去的某些CPU無法自動擴展部分字節整數的符號。因此,使用無符號整數更容易,因為將頂部位置零就足夠了。)
在clang上,默認值為signed int
。 因此根據全寬度整數int
總是表示有符號整數,即使它只有一位。 一個int member : 1
只能存儲0
和-1
! (因此,使用int
不是解決方案。)
每個逗號分隔的集都指定相同的類型,除了對於位字段,它是實現定義的,指定符int是指定與signed int相同的類型還是與unsigned int相同的類型。
ISO / IEC 9899:TC3,6.7.2-5
C標准表示布爾位字段是整數類型,因此參與了位字段的奇怪整數簽名規則:
位字段被解釋為由指定位數組成的有符號或無符號整數類型。
ISO / IEC 9899:TC3,6.7.2.1-9
這是您找到的行為。 因為這對於1位布爾類型沒有意義,所以C標准明確表示將1存儲到布爾位域必須在每種情況下比較等於1:
如果將值0或1存儲到_Bool類型的非零寬度位字段中,則位字段的值應等於存儲的值。
ISO / IEC 9899:TC3,6.7.2.1-9
這導致了一種奇怪的情況,即一個實現可以將寬度為1的布爾值實現為{0,-1},但必須滿足1 == -1。 大。
所以,簡短的故事: BOOL
表現得像一個整數位字段(符合標准),但不參與_Bool
的額外要求。
我認為這是因為遺留代碼。 (人們可以期待過去的-1。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.