为啥'for'循环条件失败? [复制]

Posted

技术标签:

【中文标题】为啥\'for\'循环条件失败? [复制]【英文标题】: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;

谁能解释一下这段代码?

【问题讨论】:

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

【讨论】:

以上是关于为啥'for'循环条件失败? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥在下面的 for 循环中没有任何条件检查?

尽管条件仍然成立,为啥这个 for 循环似乎没有执行?

这个“for(;;)”循环的条件是啥? [复制]

为啥在 for 循环的条件下调用函数是不好的?

为啥我的 for 循环在“继续”之后没有重新评估条件?

为啥在 C for 循环的条件中使用表达式而不是常量?