C 无符号整数减法、宏和类型转换
Posted
技术标签:
【中文标题】C 无符号整数减法、宏和类型转换【英文标题】:C unsigned integer subtraction, macros, and typecasting 【发布时间】:2013-02-06 02:51:39 【问题描述】:为示例代码的 Objective-C 特性道歉,但我很确定我的问题的答案在 C 标准库和/或 Apple clang
编译器中。
我有一个NSArray
,其中包含可变数量的项目。我想使用项目计数来创建一个介于 1 和 3 之间的值。我正在使用 C MAX
宏,但它有一些奇怪的行为:
NSLog( @"%d %d %d %d", 1, [tasks count], 3 - [tasks count], MAX( 1, 3 - [tasks count] ) );
当增加tasks
中的项目数时,此日志语句的输出是这样的:
1 0 3 3
1 1 2 2
1 2 1 1
1 3 0 1
1 4 -1 -1
我稍微研究了一下文档,发现count
函数返回了NSUInteger
。我的困境的解决方案只是将返回值类型转换为NSInteger
:
NSLog( @"%d %d %d %d", 1, (NSInteger)[tasks count], 3 - (NSInteger)[tasks count], MAX( 1, 3 - (NSInteger)[tasks count] ) );
1 0 3 3
1 1 2 2
1 2 1 1
1 3 0 1
1 4 -1 1
(如果你不熟悉 Objective-C,在 32 位架构上,NSInteger
的类型定义为 int
,NSUInteger
是 unsigned int
。)
我很难理解在我的原始代码中隐式发生的类型转换,这导致了我不直观的结果。有人能点亮吗?
【问题讨论】:
【参考方案1】:打开编译器警告。正如你所说,Objective C 位无关紧要,所以我将从这里开始引用 C。
假设您的 MAX()
宏是这样定义的(使用 GCC 扩展):
#define MAX(x, y) ( typeof (x) _x = (x); \
typeof (y) _y = (y); \
_x > _y ? _x : _y )
(MAX()
宏是非标准的
当您计算 MAX(1, 3 - [tasks count])
时,您会得到:
int x = 1;
unsigned y = 3 - [tasks count];
x > y ? x : y;
现在,可以在 C 中正确比较 int
和 unsigned
,但这不是您在 C 中使用 x > y
时得到的行为。相反,两个操作数都转换为 unsigned
(由于到“通常的算术转换”)。
所以您的比较是(unsigned) 1 > (unsigned) -1
,这是错误的,因为(unsigned) -1
是最大可能的unsigned
,在大多数系统(32 位和64 位)上是0xffffffff
。
哪里出错了?
NSLog(@"%d", [tasks count]);
这在技术上是错误的,您将unsigned
传递给NSLog()
,但使用了%d
格式说明符,它用于int
。请改用%u
,或先将您的值转换为int
。
编译器警告
编译器会发出两个警告:
它可能告诉您在计算 MAX(1, 3 - [tasks count])
时正在比较有符号和无符号。
当格式需要 int
时,它会告诉您您正在将 unsigned
传递给 NSLog()
。
【讨论】:
Xcode 的默认编译器配置没有给我这些警告。如果有的话,我就不会浪费两个小时来调试它了。我必须打开“隐式签名转换”,然后给出 150 个我不关心的其他警告。 @jab:这也归结为风格问题。如果我负责您的代码,我会保留警告并修复代码,直到所有警告都消失。我的猜测是,在生成警告的代码中的这 150 个位置中的一些或许多位置中存在一些意外行为。这些警告默认情况下没有打开的事实反映了,IMO,大多数代码都是草率的,如果诊断消息太多,大多数程序员会忽略诊断消息。我通常用-Werror
编译,如果有any 警告,它会阻止代码编译。
这很容易演变成关于强类型语言与动态语言的争论……我只想说,大多数时候,我对无符号整数和有符号整数之间的区别不感兴趣。有时它会咬我,很明显,但我最终打赌我会浪费更多时间修复编译器警告,而不是节省调试这些罕见情况的时间。懒惰是一种美德。
@jab:好吧,只要您可以编写带有这些警告检测到的缺陷类型的代码。我曾多次被签名/未签名比较所困扰,多年来,此类错误已导致各种产品中出现许多安全漏洞。它并不比缓冲区溢出更安全,如果你已经确定了自己的位置,我所说的也不会让你关心缓冲区溢出。
这也不是关于静态与动态类型的真正区别。大多数动态语言使这种错误不可能发生。具体来说,这实际上更像是算术在 C 中的工作方式的一个怪癖。有人将此称为“强”和“弱”类型系统之间的区别。【参考方案2】:
3 - [tasks count] 使用无符号类型完成,因此减法回绕,变为 0xffffffff。所以你会得到无符号类型的 MAX(1, 0xffffffff)。
但是,您将其打印为有符号整数,因为您的 NSLog 格式化字符串是“%d”,它将使 NSLog 将参数的位视为有符号整数,并且位模式 0xfffffff 为 -1 .
【讨论】:
是的。当然,使用错误的格式说明符无论如何都会调用 UB。 @H2CO3:在这种情况下它会调用 UB,但使用错误的说明符并不总是会导致 UB(您可以将%u
和 %d
互换为 int
范围内的正值) .以上是关于C 无符号整数减法、宏和类型转换的主要内容,如果未能解决你的问题,请参考以下文章
从有符号/无符号字符到无符号/有符号整数类型转换的 IA32 汇编代码