为什么' for'循环条件失败?

时间:2016-08-19 14:34:28

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

在下面显示的代码中,没有任何内容被打印,这意味着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;
}

有人可以解释一下这段代码吗?

5 个答案:

答案 0 :(得分:33)

这是&#34;通常的算术转换&#34;。

的结果

来自C standard

的第6.3.1.8节
  

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

     

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

     

否则,如果具有无符号整数类型的操作数具有   等级大于或等于另一个类型的等级   操作数,那么带有符号整数类型的操作数是   转换为带无符号整数的操作数类型   类型。

     

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

     

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

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]);
}

答案 1 :(得分:9)

当我尝试像这样打印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进行比较时,该signed int将被视为无符号。因此,在d <= (TOTAL_ELEMENTS-2)中,d成为一个非常高值的正数(假设使用2的补数系统)。

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

d <= (int)(TOTAL_ELEMENTS-2)

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

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

答案 2 :(得分:5)

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

评估为unsigned类型。但是,在您的循环中,dsigned值。在有符号和无符号值参与的表达式中,有符号值将转换为无符号值。但是d是-1,它不适合无符号,因此它包裹着&#34;到机器上最高的无符号值(在2&#39;补码)。

答案 3 :(得分:3)

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

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

C标准允许类型size_t的排名低于int类型。在这种情况下,整数提升会将类型size_t提升为int类型。此时双方都具有相同的int类型,因此转换将停止。比较d <= (TOTAL_ELEMENTS - 2)然后产生真,并且循环被采用。

1 因此,程序不严格符合,因为输出依赖于实现定义的行为。

答案 4 :(得分:0)

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

你可以测试表达式“printf(”%d \ n“,-1&lt; TOTAL_ELEMENTS);”;它打印0.所以我们可以通过在(TOTAL_ELEMENTS - 2)之前添加(int)或改变循环来解决:

const

我不认为让for (int d = 0; d < TOTAL_ELEMENTS; d++) { printf("%d\n", array[d]); } 变量依赖是一种好方法,因为dd循环中的变量。