2013-04-29 48 views
20

我的应用程序中有代码,如下所示。我收到了一些关于错误的反馈,当我惊恐时,我将一个调试器放在它上面,发现-5和0之间的MAX是-5!目标C中的MAX/MIN函数避免了投射问题

NSString *test = @"short"; 
int calFailed = MAX(test.length - 10, 0);      // returns -5 

查看MAX宏后,我看到它需要两个参数是相同的类型。在我的情况下,“test.length”是一个无符号整数,0是一个有符号整数。所以简单的强制转换(对于任一参数)可以解决这个问题。

NSString *test = @"short"; 
int calExpected = MAX((int)test.length - 10, 0);     // returns 0 

这看起来像是这个宏的令人讨厌和意想不到的副作用。有没有另一个内置的方法来执行MIN/MAX的iOS编译器会警告不匹配的类型?看起来像这样应该是一个编译时问题,并不需要调试器弄清楚。我可以随时写我自己的,但想看看是否有其他人有类似的问题。

+0

你的意思是像'fmax'? – 2013-04-29 02:53:56

+0

谢谢。它帮助了我。 – Raja 2017-05-22 11:51:13

回答

12

您可能没有打开足够的编译器警告。如果你打开-Wsign-compare(可以用-Wextra开启),你会产生类似以下

warning: signed and unsigned type in conditional expression [-Wsign-compare] 

这使您可以放置​​在铸件如有必要,正确的地方,你不应该需要一个警告重写最大或最小宏

+3

是的,这是诀窍。当然,现在我的项目有107个新问题。我想知道有多少像这样的错误。谢谢你的提示。 – raider33 2013-04-29 01:42:40

+1

@ raider33如果您还没有它,我还建议添加'-Wall'来打开更多的编译器警告。 – FDinoff 2013-04-29 02:47:15

+1

由于我们正在讨论ObjC,因此我们可能会使用'clang'而不是'gcc'。所以你可以比['-Wall']做得更好(http://assets.diylol.com/hfs/b1e/58b/811/resized/sad-all-the-things-meme-generator-turn-on-all -the-warnings-6e57ad.jpg):打开['-Weverything'](http://assets.diylol.com/hfs/3a0/292/5f5/resized/all-the-things-meme-generator-turn一切警告-5a92fa.jpg)*所有警告*。然后尝试'-Werror'获取Hard Mode成就。 – rickster 2013-04-29 18:45:31

29

启用-Wsign-compare,正如FDinoff的回答所建议的,这是一个好主意,但我认为可能需要更详细地解释背后的原因,因为这是一个很常见的错误。

问题不在于MAX特别是宏,而是与a)以导致溢出的方式从无符号整数中减去,以及b)(如警告所示)与​​编译器如何处理一般的有符号和无符号值的比较。

第一个问题很容易解释:当从无符号整数中减去并且结果为负数时,结果“溢出”为非常大的正值,因为无符号整数不能表示负值。因此[@"short" length] - 10将评估为4294967291

什么可能是更令人惊讶的是,即使没有减,像MAX([@"short" length], -10)不会产生正确的结果(这将评估为-10,即使[@"short" length]5,这显然是更大)。这与宏无关,类似if ([@"short" length] > -10) { ... }会导致相同的问题(if-block中的代码将执行而不是)。

所以一般的问题是:当你比较一个无符号整数和一个有符号整数(为什么首先有一个警告)?编译器会根据某些可能导致令人惊讶的结果的规则将这两个值转换为常见类型。

Understand integer conversion rules [cert.org]引用:

  • 如果具有符号整型操作数的类型,可以表示所有与无符号整数类型的操作数的类型的值的,与无符号整数类型的操作数是转换为带有符号整数类型的操作数的类型。
  • 否则,两个操作数都转换为与带符号整数类型的操作数的类型相对应的无符号整数类型。

(重点煤矿)

考虑这个例子:

int s = -1; 
unsigned int u = 1; 
NSLog(@"%i", s < u); 
// -> 0 

其结果将是0(假的),即使s-1)显然小于u1)。发生这种情况是因为两个值都转换为unsigned int,因为int无法表示可包含在unsigned int中的所有值。

如果将s的类型更改为long,则会变得更加混乱。然后,你会在32位平台(iOS)上得到相同(不正确)的结果,但在64位Mac应用程序中,它会工作得很好! (说明:long是一个64位类型那里,所以它可以代表所有32位unsigned int值。)

所以,长话短说:不要比较无符号和符号整数,特别是如果有符号值潜在负面。

+1

非常好的解释。 – 2013-04-29 05:29:05