C 常量32768和0x8000之间的类型差异是否会产生差异?

C 常量32768和0x8000之间的类型差异是否会产生差异?,c,language-lawyer,C,Language Lawyer,该标准规定,像0x8000这样的十六进制常量(大于有符号整数的大小)是无符号的(就像八进制常量一样),而像32768这样的十进制常量是有符号长的。(精确类型假定为16位整数和32位长。)但是,在常规C环境中,这两种类型都具有相同的表示形式,即二进制1000 0000。 这种差异真的会产生不同的结果吗?换句话说,在这种情况下,这种差异是否会产生影响?是的,它可能会产生影响。如果处理器具有16位int和32位long类型,则32768的类型为long(因为32767是有符号16位int中拟合的最大正

该标准规定,像0x8000这样的十六进制常量(大于有符号整数的大小)是无符号的(就像八进制常量一样),而像32768这样的十进制常量是有符号长的。(精确类型假定为16位整数和32位长。)但是,在常规C环境中,这两种类型都具有相同的表示形式,即二进制
1000 0000

这种差异真的会产生不同的结果吗?换句话说,在这种情况下,这种差异是否会产生影响?

是的,它可能会产生影响。如果处理器具有16位
int
和32位
long
类型,则32768的类型为
long
(因为32767是有符号16位
int
中拟合的最大正值),而0x8000(因为它也被认为适用于
无符号int
)仍然适用于16位
无符号int

现在考虑以下程序:

int main(int argc, char *argv[])
{
  volatile long long_dec = ((long)~32768);
  volatile long long_hex = ((long)~0x8000);

  return 0;
}
当32768被视为长时,求反将反转32位, 导致类型为
long
的表示0xFFFF7FFF;演员是 多余的。 当0x8000被认为是无符号整数时,求反将反转 16位,表示0x7FFF,类型为
无符号int
; 然后,强制转换将零扩展到
long
值0x00007FFF。 参见H&S5,第24ff页第2.7.1节


最好用
U
增加常数,
UL
L
视情况而定。

不同之处在于,如果尝试向16位int添加一个值,它将无法执行此操作,因为它将超出变量的边界,而如果使用32位长度,则可以向其添加任何小于2^16的数字

在具有64位
的32位平台上,以下代码中的
a
b
将具有不同的值:

int x = 2;
long a = x * 0x80000000; /* multiplication done in unsigned -> 0           */
long b = x * 2147483648; /* multiplication done in long     -> 0x100000000 */

另一个尚未给出的检查:将(大于或小于运算符)-1与32768和0x8000进行比较。或者,就这一点而言,试着用一个等于-32768的“int”变量来比较它们的相等性。

假设
int
是16位,
long
是32位(这在当今是相当不寻常的;
int
通常是32位):


在大多数上下文中,数值表达式将隐式转换为上下文确定的适当类型。(但这并不总是您想要的类型。)这不适用于可变函数的非固定参数,例如
*printf()之一的任何参数
函数遵循格式字符串。

我可以想象这样一种情况:您试图从
0x8000
中减去一个更大的数字,但由于它没有签名,因此无法正常工作。但这不太可能发生。@user786653:是的,就在那里,在下一页的表格中有两列区分十进制常数和十六进制和八进制常数,其中十六进制和八进制常数(不带后缀)也有无符号变体,与十进制常数相反。(评论已删除;请参阅第55f页。)@JohanBezem:是的,很抱歉,当我意识到自己是个白痴时,我本应该编辑我的评论,而不是删除它。
void main
?你一定是在开玩笑。@Fanael:对不起,是的,你是对的。谢谢你的更正+1但在嵌入式系统中,我们经常使用
void
,因为没有环境可以将
int
返回到。@OliCharlesworth:对于托管实现,
void main
只有在实现明确支持并记录它时才有效;否则会使程序的行为未定义。对于独立实现,入口点是实现定义的;同样,由实现决定
void main
是否有效。但是
int-main(void)
对于托管的实现总是有效的,并且没有合理的理由不使用它或
int-main(int-argc,char*argv)
或等效物。@JohanBezem:D'oh!但我实际上是想写
char*argv[]
(作为一个参数,它相当于
char**argv
)。@OliCharlesworth:说真的,我的建议是克服你的恼怒
void main
是一个非常常见的错误——是的,在大多数情况下,这是一个错误。它往往出现在新手问题中,新手通常使用托管实现。指出它是一件好事。(这通常表明程序员从一本本本该更了解的作者写的烂书中学到了东西。)如今,有几十种具有16位int的嵌入式处理器正在积极开发中使用。也就是说,您的第一个
printf
使用一个长整型常量作为长十进制值打印,没有问题(如您所示);IMHO第二个
printf
也没有问题,因为定义了从
无符号int
有符号long
的转换并保存值。所以在我看来,在第二个表达式中没有“未定义的行为”。@JohanBezem:关于嵌入式处理器的观点很好。变量参数(即与函数声明中的
,…
相对应的参数)不会转换,默认参数升级除外,因为编译器不知道需要
长整型。对于
printf
调用,如果提升的参数类型与格式指定的类型不匹配,则行为未定义;C99 7.19.6.1p9明确说明了这一点。请注意,
-1
是一个带一元负号的常量“1”。通常的一元和二元转换是应用的,因此在任何情况下,您都不会看到任何奇怪的行为。@JohanBezem:这是一个带有一元减号的常量,但一元减号的结果将是int类型(有符号的)。将-1与32768进行比较将在长值之间产生有符号的比较
printf("%ld\n", 32768);  // prints "32768"
printf("%ld\n", 0x8000); // has undefined behavior