【问题标题】:Why does the 'for' loop condition fail? [duplicate]为什么'for'循环条件失败? [复制]
【发布时间】:2016-12-26 18:29:56
【问题描述】:

在下面显示的代码中,没有打印任何内容,这意味着for 循环中的条件失败。可能是什么原因?

我想知道,因为当我单独打印TOTAL_ELEMENTS时,它给出了5,所以自然这一定是5-2=3 => -1<=3,所以它应该打印一些东西。

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

int array[] = { 23, 34, 12, 17, 204, 99, 16 };
int main()
{
    int d;

    for (d = -1; d <= (TOTAL_ELEMENTS - 2); d++) {
        printf("%d\n", array[d + 1]);
    }

    return 0;
}

谁能解释一下这段代码?

【问题讨论】:

  • “因为当我单独打印 TOTAL_ELEMENTS 时,结果为 5” - 不,不是。你有什么理由让它这么复杂?
  • 我不知道您使用的是哪个编译器,但 GCC 会给出正确的提示:“警告:有符号整数和无符号整数之间的比较”。当然,如果您已打开所有警告。谁给了这 8 个(用词:8 个!)赞成票?
  • @deamentiaemundi 只有-Wextra 才会发出警告。不是每个人都使用它,但每个人都应该使用它。
  • 什么是for循环从-1开始,然后数组索引加1的原因?

标签: c type-conversion c-preprocessor implicit-conversion


【解决方案1】:

正如其他答案已经解释的那样,原因是通常的算术转换,使用sizeof 获得的类型 size_t 导致 int 转换为对应于类型 size_t 的无符号类型。

我想补充一点,行为是实现定义的1。可以采取或不采取循环。这取决于 size_t 类型的定义。

C 标准允许 size_t 类型的等级低于 int 类型。在这种情况下,整数提升将 size_t 类型提升为 int 类型。此时双方都具有相同的 int 类型,因此转换停止。比较 d &lt;= (TOTAL_ELEMENTS - 2) 则结果为真,并执行循环。


1 因此,该程序并不严格遵守,因为输出依赖于实现定义的行为。

【讨论】:

    【解决方案2】:

    你知道#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) 返回一个无符号数,-1 可能成为最大数,因为它转换为一个无符号数。

    你可以测试表达式 "printf("%d\n", -1

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

    而且我不认为使d 变量依赖是一个好方法,因为dfor 循环中的一个变量。

    【讨论】:

      【解决方案3】:

      这是“通常的算术转换”的结果。

      来自C standard 的第 6.3.1.8 节:

      如果两个操作数的类型相同,则不再进行转换 需要。

      否则,如果两个操作数都具有有符号整数类型或都具有 无符号整数类型,具有较小类型的操作数 整数转换等级转换为操作数的类型 排名更高。

      否则,如果具有无符号整数类型的操作数具有 等级大于或等于其他类型的等级 操作数,则带符号整数类型的操作数为 转换为无符号整数的操作数类型 输入。

      否则,如果带符号整数类型的操作数的类型可以 用无符号表示操作数类型的所有值 整数类型,则无符号整数类型的操作数为 转换为带符号整数类型的操作数的类型。

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

      sizeof 运算符返回一个size_t,它是一个无符号值。所以(sizeof(array) / sizeof(array[0])) - 2 也是无符号的。

      因为您正在比较有符号和无符号值,所以将有符号值转换为无符号值。将 -1 转换为无符号会产生最大的无符号值,这会导致比较结果为假。

      如果您将右侧转换为int,它将按预期工作。

      for(d=-1;d <= (int)(TOTAL_ELEMENTS-2);d++)
      

      输出:

      23
      34
      12
      17
      204
      99
      16
      

      或者您可以通过规范化数组的索引方式来避免该问题:

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

      【讨论】:

        【解决方案4】:
        #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
        

        计算结果为unsigned 类型。但是,在您的循环中,d 是一个 signed 值。在有符号和无符号值参与的表达式中,有符号值被转换为无符号值。但是d 是-1,它不能放入无符号数中,因此它“环绕”到机器上的最高无符号值(在 2 的补码上)。

        【讨论】:

          【解决方案5】:

          当我尝试像这样打印TOTAL_ELEMENTS - 2 时:

          printf("total %d\n", TOTAL_ELEMENTS - 2);
          

          我收到一条警告(使用 gcc 4.8):

          test.c:8:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
            printf("total %d\n", TOTAL_ELEMENTS - 2);
            ^
          

          警告意味着TOTAL_ELEMENTS - 2long unsigned。现在,当您将signed intunsigned int 进行比较时,有符号的int 被视为无符号的。所以在d &lt;= (TOTAL_ELEMENTS-2) 中,d 变成了一个非常高值的正数(假设使用了 2 的补数系统)。

          您可以将结果转换为 int 以解决问题。

          d &lt;= (int)(TOTAL_ELEMENTS-2)

          或者,如果您在很多地方都使用宏,那么您可以这样更改:

          #define TOTAL_ELEMENTS (int)(sizeof(array) / sizeof(array[0]))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-12-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多