2011-09-26 382 views
5

我们发现了一些奇怪的值正在生成,下面是一个小测试用例。 这会打印“FFFFFFFFF9A64C2A”。这意味着无符号的long long似乎已经被延长了。 但是为什么? 以下所有类型都是无符号的,所以什么是符号扩展?预期的输出 将是“F9A64C2A”。使用无符号long long的符号扩展

#include <stdio.h> 

int main(int argc,char *argv[]) 
{ 
    unsigned char a[] = {42,76,166,249}; 

    unsigned long long ts; 
    ts = a[0] | a[1] << 8U | a[2] << 16U | a[3] << 24U; 

    printf("%llX\n",ts); 


    return 0; 

} 

回答

5

在表达a[3] << 24U,该a[1]具有类型unsigned char。现在, “整数促销” 将其转换为int,因为:

以下可以在表达式中用于任何一个或int可以unsigned int 可以使用:

[...]

如果int可以表示原始类型的所有值,则将该值转换为 和int; 否则,它将转换为unsigned int

(draft) ISO/IEC 9899:1999,6.3.1.1 2)

请还注意,移位运算符(比大多数其它运营商其他)做做“通常的算术转换”这两个操作数转换成普通类型。但是

结果的类型是提升的左操作数的类型。

(6.5.7 3)

在32位的平台,249 << 24 = 4177526784解释为int具有其符号位组。

只是更改为

ts = a[0] | a[1] << 8 | a[2] << 16 | (unsigned)a[3] << 24; 

修复该问题(后缀为U常量没有任何影响)。

+0

小修正:'a [1]'具有'unsigned char'类型。 –

+0

@ user964970:再次阅读。 'x << y'的类型与'y'的类型无关。 –

+0

@Dietrich Epp:谢谢。 –

1
 
ts = ((unsigned long long)a[0]) | 
    ((unsigned long long)a[1] << 8U) | 
    ((unsigned long long)a[2] << 16U) | 
    ((unsigned long long)a[3] << 24U); 

铸造防止转换中间结果来默认的int类型。

+1

但*为什么*有一个中间整型结果,当涉及的所有类型都是无符号类型?罪魁祸首似乎只是第一个'a [0]',用'(无符号)a [0]代替'一切都很好。但为什么。 – user964970

1

当从unsigned char自动转换为int时,某些移位的a [i]产生符号扩展值。

这符合第6.3.1节算术操作数,第6.3.1.1节C标准草案N1570的布尔,字符和整数,其部分读取“2.以下内容可用于表达式无论使用int还是unsigned int:... - 具有整型(不包括int或unsigned int)的对象或表达式 其整型转换等级小于或等于int和unsigned int的等级。 ..如果一个int可以表示原始类型的所有值...,则该值被转换为一个int;否则,它被转换为一个无符号整数,这些被称为整型促销... 3.整型促销保留包括符号在内的价值“

例如见www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf

你可以使用如下代码,其中工程确定:

 int i; 
     for (i=3, ts=0; i>=0; --i) ts = (ts<<8) | a[i]; 
+0

由于常量上的U前缀,在示例代码中所有被移位的[i]的右边都是无符号的。 (例如“8U”),意思是例如根据这些规则,表达式a [1] << 8U应该具有无符号类型。 – user964970

+0

@ user964970:罪魁祸首不是'a [0]'。但是,将'a [0]'强制转换为'unsigned'会强制执行按位或无符号的结果,从而截断出现在“a [3] << 24”中的符号扩展名,这是真正的罪魁祸首。 –