【问题标题】:C's comparison's wrong with hierarchy promotionC 的比较错误与层次提升
【发布时间】:2015-06-19 23:00:41
【问题描述】:
#include <stdio.h>

int main()
{
    unsigned int x=1;
    char y=-1;
    if (x>y)
    {
        printf("x>y");
    }
    else if(x==y)
        printf("x=y");
    else
        printf("x<y");
    return 0;
}

我在上面运行代码的时候,会执行最后一个else的printf,这真的很尴尬,因为x是1,y是-1。

我认为比较“x>y”与分层提升有一些关系,因为当我将 x 的类型更改为“int”而不是“unsigned int”时,它确实是正确的。 这个问题真的很有趣..欢迎任何答案/思考/建议。

【问题讨论】:

  • 编译该程序时你会收到警告......注意警告
  • 提示:您如何将 -1 解释为 unsigned 值?
  • 抱歉,在 Xcode 和 Ubuntu 的 gcc 中,它没有收到任何警告信息。您到底在说什么警告信息?
  • 不,这是未定义的行为
  • 试试-Wconversion (gcc)

标签: c


【解决方案1】:

根据标准,实际上是正确的。

首先,定义charsigned还是unsigned是实现。

如果charunsigned,则初始化将使用模运算,因此初始化为-1 将初始化为unsigned char 的最大值——保证大于1。比较将转换在进行比较之前将 charunsigned(不会更改值)。

如果charsigned,则比较会将具有-1 值的char 转换为unsigned 类型(因为x 是unsigned 类型)。除了unsigned 类型之外,该转换再次使用模运算(因此-1 将转换为unsigned 可以表示的最大值)。这会导致值超过 1。

实际上,在编译器上调高警告级别会触发此类事情的警告。这在实践中是一个好主意,因为可以说代码的行为方式不太直观。

【讨论】:

  • 我还有一件事要问你.. 当我把这个说出来时,“printf("%d\n",(unsigned int)y);"它打印出-1。编译器没有打印出 unsigned int 的最大值有什么原因吗?
  • %d 将打印一个签名的int,您应该使用%u 打印一个unsigned int
  • @beta:从技术上讲,行为是不确定的,因为参数的类型 (unsigned int) 与转换说明符所期望的 (int) 不匹配。 %d 将输出格式化为有符号十进制整数,并将其对应的参数解释为有符号整数。
【解决方案2】:

为了比较,y提升 从类型 char 到类型 unsigned int。但是,无符号类型不能表示负值;相反,-1 被解释为UINT_MAX,这绝对小于1

【讨论】:

    【解决方案3】:

    这叫Type Promotions

    那么规则(您也可以在 K&R2 的第 44 页或较新的 ANSI/ISO C 标准的第 6.2.1 节中找到)大致如下:

    1,首先,在大多数情况下,charshort int 类型的值会立即转换为 int

    2、如果一个操作涉及两个操作数,其中一个是long double类型,另一个转换为long double

    3、如果一个操作涉及两个操作数,其中一个是double类型,另一个转换为double

    4、如果一个操作涉及两个操作数,其中一个是float类型,另一个转换为float

    5、如果一个操作涉及两个操作数,其中一个是long int类型,另一个转换为long int

    6、如果一个运算同时涉及有符号和无符号整数,情况就复杂一些。如果无符号操作数较小(也许我们正在对unsigned intlong int 进行操作),使得较大的有符号类型可以表示较小的无符号类型的所有值,那么无符号值将转换为较大的值,有符号类型,结果具有更大的有符号类型。否则(即,如果有符号类型不能表示无符号类型的所有值),则将两个值都转换为通用无符号类型,并且结果具有该无符号类型。

    7、最后,当使用赋值运算符为变量赋值时,如果 (a) 值和变量都具有算术类型(即整数或浮点数),则它会自动转换为变量的类型点),或者(b)值和变量都是指针,其中一个或另一个是void *类型。

    【讨论】:

      【解决方案4】:

      根据 C 标准(6.5.8 关系运算符)

      3 如果两个操作数都有算术类型,通常的算术 执行转换。

      进一步(6.3.1.1 布尔、字符和整数,#2)

      如果一个 int 可以表示原始类型的所有值(受限制 通过宽度,对于一个位域),该值被转换为一个 int; 否则,它将转换为无符号整数。这些被称为 整数促销

      最后(6.3.1.8 常用算术转换)

      否则,对两个操作数都执行整数提升。然后 以下规则适用于提升的操作数

      ...

      否则,两个操作数都转换为无符号整数类型 对应带符号整数类型的操作数的类型。

      因此在表达式中

      x > y
      

      字符 y 被提升为类型 int。由于unsigned int(对应于x)和int具有相同的排名,因此根据最后的报价y被解释为unsigned int。它的所有位都已设置,并且对应于可以存储在 unsigned int 类型中的最大值。 这样你就有了

      UINT_MAX > 1
      ^^^^^^^^  ^^^
         y       x
      

      【讨论】:

        【解决方案5】:

        运行这个:

        int main()
        {
            unsigned int x=1;
            char y=-1;
            printf("x : %#010x\n", x);
            printf("y : %#010x\n", y);
            return 0;
        }
        

        这将输出变量的十六进制值:

        x : 0x00000001
        y : 0xffffffff
        

        我还需要继续吗...?

        【讨论】:

          【解决方案6】:

          问题是将有符号类型与无符号类型进行比较。 有符号变量,例如char y,通常使用一位作为符号存储,在负数时使用值的 2 位补码。 因此,char y = -1; 为您提供 y 的一般表示:

           vvvvvvv value  : 1111111
          11111111
          ^ sign : negative
          
          2-bit complement: invert all bits and add one = (0000000 + 1) = 1
          

          意思是你的比较确实if (binary 1 &gt; binary 11111111)

          【讨论】:

            【解决方案7】:

            C++ 语言编译器尝试提升如果没有精确匹配的类型(如在我们的示例中:显然 char 不是 unsigned int)。 不幸的是,这种提升的方向是从不太精确的类型转向更精确的类型,反之亦然。这意味着,任何 char 都可以提升为 int,但不能将 int 提升为 char。

            预计编译器会通知您它无法找到最佳候选者并且编译将失败。

            负数用管理所谓的补码数的规则来表示。 要表示char = -1,将所有位反转并加一:

            0000 0001 
            1111 1110 
            +       1 
            1111 1111
            

            现在,当提升发生时,char 隐式提升为 4 个字节。它与尝试用 4 字节 int 表示 -1 的过程相同:

            0000 0000 0000 0001 
            1111 1111 1111 1110 
            +                 1 
            1111 1111 1111 1111
            

            该值现在被视为无符号,如下面的代码所示:

            int main(){
                int y = -1;
                cout << y << endl;             //if x is an int: x > y
                cout << unsigned(y) << endl;   //if x is an unsigned int y is now treated as UINT_MAX: x < y 
                return 0;
            }
            

            哪个打印:

            -1
            4294967295
            

            因此,x &lt; y 的评估将为真。

            这里有一些进一步的细节:

            【讨论】:

              猜你喜欢
              • 2011-03-07
              • 2015-02-15
              • 2021-08-15
              • 2015-04-07
              • 1970-01-01
              • 2017-01-24
              • 2016-05-11
              • 1970-01-01
              • 2016-07-14
              相关资源
              最近更新 更多