可能重复:
Confused about C macro expansion and integer arithmetic
A riddle (in C)
以下 C 程序的预期输出是打印数组中的元素。但实际运行时,它并没有这样做。
#include<stdio.h>
#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;
}
答案 0 :(得分:6)
因为sizeof
为您提供了未签名的值,如果您提高警告级别,例如将-Wall -Wextra
与gcc
一起使用,您可能会注意到该值(a)中:
xyzzy.c: In function 'main':
xyzzy.c:8: warning: comparison between signed and unsigned
如果你强制签名,它可以正常工作:
#define TOTAL_ELEMENTS (int)((sizeof(array) / sizeof(array[0])))
可以从ISO标准中收集详细信息。在不同类型之间的比较中,执行促销以使类型兼容。选择的兼容类型取决于几个因素,例如符号兼容性,精度和等级,但在这种情况下,认为无符号类型size_t
是兼容类型,因此d
已升级到该类型。
不幸的是,将-1
转换为无符号类型(至少是两个补码,这几乎肯定是你正在使用的那个)会产生相当大的正数。
从5
获得的(TOTAL_ELEMENTS-2)
肯定会更大。换句话说,您的for语句实际上变为:
for (d = some big honking number way greater than five;
d <= 5;
d++
) {
// fat chance of getting in here !!
}
(a)使用extra
的这一要求仍然是gcc
开发人员与我之间争论的焦点。他们显然正在使用我之前未发现的“所有”一词的 new 定义(道格拉斯亚当斯道歉)。
答案 1 :(得分:0)
TOTAL_ELEMENTS
的类型为size_t
,减去2是在编译时完成的,因此它是5UL
(强调无符号后缀)。然后,与 signed 整数d
的比较始终为false。尝试
for(d=-1;d <= (ssize_t)(TOTAL_ELEMENTS-2);d++)
FTW,当您尝试编译代码时,intel编译器会对此发出警告。
答案 2 :(得分:0)
澄清出现了什么问题:sizeof()转换为size_t
的结果类型,它只是一个无符号整数,大于或等于unsigned int
。
因此(sizeof(array) / sizeof(array[0]))
的结果是两个size_t类型的操作数的结果。对这些操作数执行除法:size_t / size_t
。两个操作数都是相同的类型,因此它工作正常。除法的结果是size_t
类型,这是TOTAL_ELEMENTS导致的类型。
因此,表达式(TOTAL_ELEMENTS-2)
具有类型size_t - int
,因为整数文字2
的类型为int。
这里我们有两种不同的类型。然后会发生什么叫做 balance (形式上是“通常的算术转换”),当编译器发现两种不同的类型时会发生这种情况。平衡规则规定如果一个操作数是有符号的,另一个是无符号的,那么带符号的操作数将被静默,隐式转换为无符号类型。
这是此代码中发生的情况。 size_t - int
转换为size_t - size_t
,然后执行减法,结果为size_t
。然后int <= size_t
转换为size_t <= size_t
。变量d
变为无符号,如果变量为负值,则代码变得混乱。