Obj-C:比较BOOL变量真的安全吗?

时间:2015-06-30 15:25:26

标签: objective-c

我曾经认为在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辅助函数? (这将是一件苦差事,因为布尔比较无处不在......)

1 个答案:

答案 0 :(得分:3)

我不重复使用C布尔类型解决了BOOL可能遇到的问题。这是真的 - 特别是在这里,你可以在下面看到 - ,但大多数问题是由于错误存储到布尔(C)对象造成的。但在这种情况下,_Boolunsignedint)似乎是唯一可行的解​​决方案。 (除了带有额外代码的解决方案。)有一个原因:

我找不到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。)