2011-06-02 39 views
4

考虑这个C代码:为什么C不运行unsigned int与负值的比较?

#include "stdio.h" 

int main(void) { 

    int count = 5; 
    unsigned int i; 

    for (i = count; i > -1; i--) { 
     printf("%d\n", i); 
    } 
    return 0; 
} 

我的观察/问题:循环永远不会被执行。但是,如果我将数据类型从unsigned int更改为int,则所有内容都按预期工作。

我一直在考虑未签名的整数作为值,“环绕”,当你试图保持减去他们。所以,当我是零,我减1,它会绕到UINT_MAX。而且由于它的价值从不消极,所以这将是一个无限循环。 (而这正是当我将比较从i> -1改变为i> = 0时发生的情况。)

我的逻辑中某处出现故障,因为如果我是无符号的,循环永远不会执行,而且我将它与-1进行比较。编译器会以某种方式优化它,或者运行时值的行为与我期望的不同。

为什么循环不能运行?

+0

您是否尝试过直接转换字面值-1? – 0xC0000022L 2011-06-02 17:17:11

+0

你是完全正确的。但请记住,5> UINT_MAX是错误的。 – sidyll 2011-06-02 17:18:29

+0

http://stackoverflow.com/questions/2084949/arithmetic-operations-on-unsigned-and-signed-integers http://stackoverflow.com/questions/24466857/why-sizeofint-is-not-greater-than- 1 http://stackoverflow.com/questions/3100365/why-is-%E2%88%921-sizeofint – 2015-03-24 11:59:15

回答

18

i > -1中,-1被转换为unsigned int,导致值为UINT_MAXi从不大于该值,所以循环体从不执行。

您可能会发现,您可以说服您的编译器警告您:在条件上下文中使用always-true或always-false表达式。但是,如果您编写了i > -2,那么仍然无法帮助您,因此您可能还会发现可以为所有混合符号比较启用警告。

请注意,在C中,算术是总是与相同类型的操作数执行。这包括比较,但IIRC不是轮班操作员。如果操作数是不同类型的,就像在这种情况下那样,至少其中一个操作数被转换为相同类型。处理目标类型的规则在6.3.1.1/2和6.3.1.8/1中。

+0

新手问题:你提到的章节号是什么规格? – Jaanus 2011-06-02 17:29:53

+2

@Jaanus C99的http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf – Wiz 2011-06-02 17:34:00

+1

@Jaanus:C99(准确地说是n1256)。在C89中是3.2.1.1和3.2.1.5。 – 2011-06-02 17:36:08

3

当您混合签署并在“类型对称”二进制运算相同宽度的无符号的操作数(在你的例子一样+*>),无符号的类型“胜”,并且操作中的无符号域中被评估。即带符号的操作数被转换为无符号类型。

在您的示例中,整数常量的类型为signed int,而i的类型为unsigned int。操作数具有相同的宽度,因此在您的示例中,i > -1被解释为i > (unsigned) -1,相当于i > UINT_MAX。这就是为什么你的循环从不执行。

+0

一般来说,这是不正确的,无符号类型获胜。试试,例如'unsigned char'和'int'。 – vitaut 2011-06-02 17:23:36

+0

@vitaut:是的,你说得对,但实际上这不是一个很好的例子。在表达式中使用'unsigned char'时,通常的算术转换首先应用,'unsigned char'很早就变成'signed int'。它发生在我在我的回答中提到的“二元”规则有一个机会踢。因此,形象地说,你只是“不能”在表达式中使用'unsigned char' - 它不会坚持。在任何其他事情都有机会发生之前,它将捕捉到“int”。 – AnT 2011-06-02 17:31:17

+0

为了更好的例子,'unsigned int'失去了'signed long long',假设(long是常见的)long long可以表示所有'unsigned int'的值。所以'(unsigned int)5> -1LL'在大多数实现中都是正确的,但是如果'int'和'long long'具有相同的宽度,则为false。这是一个非常好的理由,不要在'unsigned int'和'long'之间进行混合比较:你将在LP64机器上得到一个有符号的比较,并且在LLP64和(可能)32位上得到一个无符号的比较。 – 2011-06-02 17:46:30

0

-1在无符号比较中变成UINT_MAX。由于没有数字大于此值,因此循环条件从不为真,并且循环从不输入。

如果将其更改为i >= 0,则应该按预期工作。其实你可能不应该在这种情况下使用unsigned :-)

+1

如果你把它改成'i> = 0',那么它总是真的,因此是无用的。 – 2011-06-02 17:47:36

0

无论你是处理无符号还是带符号的数字,-1总是被编译为0xffffffff。处理器具有带符号和无符号比较标志。将该数字与5进行比较时,签名标志会将其视为-1并表示它较少,但无符号标志会将其视为一个较大的数字并表示它较大。由于该数字也与UINT_MAX相同,因此所有无符号数字的比较结果都将为false。

+0

“始终编译为0xffffffff”(4,294,967,295) - 这不是真的,该标准不能保证'UINT_MAX'的值。实际上,它所说的全部是“它们的实现定义的值应等于或大于65535(2^16-1)的幅度(绝对值)” – Wiz 2011-06-02 17:44:30