【问题标题】:Why does my test of sign always report "negative"?为什么我的符号测试总是报告“阴性”?
【发布时间】:2017-07-10 19:05:05
【问题描述】:

下面的程序应该打印数组元素的和是正数还是负数:

#include <stdio.h>

#define ARR_SIZE 5

int main()
{
   int array[ARR_SIZE] = {1,-2,3,4,-5};
   unsigned sum;
   int i;

   for(i=0, sum=0; i < ARR_SIZE; i++)
   {
         sum += array[i];
         printf("sum %d\n ", sum);
   }


         printf("%d\n",sum);
   if(sum>-1) printf("non negative\n");
   else printf("negative\n");
   return 0;
 }

程序没有做它应该做的;无论收到什么数组值,它都会打印“负数”。

例如,上面程序中写的数组的和为1,因此我期望得到以下输出:

sum 1
sum -1
sum 2
sum 6
sum 1
1
non negative

虽然输出是:

sum 1
sum -1
sum 2
sum 6
sum 1
1
negative

为什么我会得到这个输出?

【问题讨论】:

  • 您将sum 声明为unsigned。想一想……
  • sum 不能是unsigned
  • if(sum&gt;-1) 应该给出编译器警告。打开编译器警告!
  • 家庭作业:解释当sumunsigned 时会发生什么。
  • @squeamish ossifrage 谢谢。我实际上已经考虑过这个问题,但问题是,如果 sum 是无符号的,那么这意味着它始终是正数....那么输出怎么会是“负数”呢?我认为它可能会一直打印“非负数”。

标签: c integer sum comparison-operators


【解决方案1】:

在表达式sum &gt; -1 中,通常的算术转换(“平衡”)适用。由于一个操作数是unsigned int,另一个是int,因此操作数-1(即int)会隐式转换为unsigned int。比较操作对unsigned int类型进行。

此外,无符号变量一开始就永远不能保存负值,因此即使没有隐式转换,表达式也没有任何意义。

【讨论】:

  • 谢谢。你能更详细地解释一下吗?将操作数 -1 转换为无符号整数实际上意味着什么?当 sum 赋值为负值时会发生什么?我有点困惑,因为当我写这个时: int main() { unsigned int x; x = -1; printf("%d\n",sum); return 0 } 输出为-1。我读到了这个,据我了解,这是因为 printf。
  • @Tree 简单地说,C 将获取-1 的二进制表示并将其转换为无符号等价物。如果是 32 位整数 2 补码,您最终会得到 0xFFFFFFFF。而且sum 永远不能大于0xFFFFFFFF
  • @Lunding 谢谢。那么 if(sum>-1) 语句真的是 if(sum>0) 吗?我还是不明白:如果 -1 被转换为 unsigned int,并且 sum 也是 unsigned int 类型,为什么输出总是“负”?它不应该是“非负面的”吗?虽然 sum 被隐式转换为 unsigned int,但我仍然不知道它的值,那么当我执行这个程序时,实际进行的比较是什么?
  • @Tree 什么是“UMAX+1”?你的意思是UINT_MAX?有符号到无符号的转换是按照这样的规则进行的:“如果新类型是无符号的,则在新类型可以表示的最大值的基础上反复加减一,直到值在范围内。新型”。因此对于这种情况,编译器将在内部执行-1 + UINT_MAX+1 = UINT_MAX,它在unsigned int 的范围内。在 32 位二进制补码系统上,UINT_MAX 恰好是 0xFFFFFFFF
  • @Tree 因为对于unsigned char,“新类型可以表示的最大值”是UCHAR_MAX,255。也就是说,最大值是根据我们要转换的新类型确定的。
【解决方案2】:

隐式类型转换:当您将变量定义为无符号并将其与负数进行比较时,该负数是隐式类型转换的。正如我们所知,负数存储在二进制补码中,所以 -1 实际上变成了一个非常大的正数。现在,无论您提供什么大数字,它总是小于那个大数字,这就是为什么您总是得到否定的答案。

可能的解决方案:使用三元运算符来处理 + 和 - 数字。 谢谢

【讨论】:

  • 正确的说法是隐式转换。类型转换仅指程序员通过写(type)x显式更改类型的情况。
  • 是一样的。点击链接link
  • @Tree 你现在能接受我的回答吗?这将是一个很大的帮助。
【解决方案3】:

启用一组合理的警告后,好的编译器会突出问题:

gcc -std=c11 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds      42362568.c    -o 42362568
42362568.c: In function ‘main’:
42362568.c:19:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    if(sum>-1) printf("non negative\n");
          ^

尽可能多地启用警告总是好的建议;由于历史原因,大多数编译器很少给出警告,除非你记得要求它们。

请注意,如果您将比较更改为 if (sum &gt;= 0),那么您将收到不同的(也许更有用的)消息,因为文字 0 可以被视为 0u

42362568.c:19:10: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
    if(sum>=0) printf("non negative\n");
          ^~

【讨论】:

    【解决方案4】:

    当我们试图比较两个不同(相同数据类型)符号的变量时,编译器会隐式(内部)将这两个变量视为更高的有符号类型(无符号类型)。

    听到 if(sum &gt; -1) 是您的代码。您正在尝试比较 unsigned sum , signed constant 所以编译器在内部转换 -1 as unsigned 。 如果我们将整数大小视为 2 个字节,则 sum = 1(as your code)-1 will be converted to unsigned value 65535 (do 2's compliment for -1) 现在你的代码将被简化为if(sum &gt; 65535),所以条件是FALSE,这就是为什么你总是得到意想不到的结果。如果你想避免这个问题,总是比较相同的有符号类型变量(如果两个变量相同数据类型,否则不同的有符号类型也没有问题。

    【讨论】:

      【解决方案5】:

      two's compliment of 1 ( assume int size is 2 Bytes )

      0000 0000 0000 0001 (1 binary value)

      1111 1111 1111 1110 (one's compliment)

      1111 1111 1111 1111 (two's compliment)(这个值为65535)

      这个值在你的代码中是replacing with -1

      【讨论】:

      • 谢谢!有点迷茫,我在看题目,以为-1会得到UMAX+1的值,为什么不是呢?
      • 一旦读到关于二人的赞美。然后你会得到你的问题的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      • 2019-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多