Sak*_*ain 10 c unsigned language-lawyer
有人可以向我解释以下代码输出:
void myprint(unsigned long a)
{
printf("Input is %lx\n", a);
}
int main()
{
myprint(1 << 31);
myprint(0x80000000);
}
Run Code Online (Sandbox Code Playgroud)
输出gcc main.c:
Input is ffffffff80000000
Input is 80000000
Run Code Online (Sandbox Code Playgroud)
为什么被(1 << 31)视为签名并被0x80000000视为未签名?
M.M*_*M.M 13
在C中,表达式的结果取决于操作数(或某些操作数)的类型.特别1是,int(签名),因此1 << n也是int.
类型(包括signed-ness)0x80000000由此处的规则确定,它取决于系统的大小int和其他未指定的整数类型.选择一种类型,使得0x80000000(大的正数)在该类型的范围内.
如果您有任何误解:文字0x80000000是一个大的正数.人们有时会错误地将其等同于负数,将值与表示混合在一起.
在你的问题中,你说"为什么0x80000000被视为无符号?".但是,您的代码实际上并不依赖于签名0x80000000.你唯一能做的就是将它传递给带unsigned long参数的函数.因此,无论是签名还是未签名都无关紧要; 当传递给转换时,它将转换为unsigned long具有相同值的a.(因为0x80000000它在最小保证范围内,unsigned long因此不可能超出范围).
所以,这是0x80000000处理.怎么样1 << 31?如果您的系统具有32位int(或更窄),则由于带符号的算术溢出而导致未定义的行为.(链接到进一步阅读).如果您的系统具有较大的整数,那么这将产生与该0x80000000行相同的输出.
如果你使用1u << 31而且你有32位整数,那么就没有未定义的行为,你可以保证看到程序输出80000000两次.
由于您的输出不是80000000那么我们可以断定您的系统具有32位(或更窄)的int,并且您的程序实际上导致未定义的行为.的类型的0x80000000将是unsigned int,如果int是32位的,或unsigned long以其他方式.
为什么被
(1 << 31)视为签名并被0x80000000视为未签名?
从C.7规格中的6.5.7 Bitise移位运算符:
3对每个操作数执行整数提升.结果的类型是提升的左操作数的类型.[...]
4 E1 << E2的结果是E1左移E2位位置; 腾出的位用零填充.如果E1具有无符号类型,则结果的值为E1×2 E2,比结果类型中可表示的最大值减少一个模数.如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; 否则,行为未定义
因此,因为1是int(来自下一段中提到的6.4.4.1节),1 << 31所以int在int小于或等于32位的系统上没有很好地定义该值.(甚至可能陷阱)
从6.4.4.1整数常量
3十进制常数以非零数字开头,由一系列十进制数字组成.八进制常量由前缀0组成,可选地后跟一个数字0到7的序列.十六进制常量由前缀0x或0X后跟一个十进制数字序列和字母a(或A)到f(或F)分别由值10到15组成.
和
5整数常量的类型是相应列表中可以表示其值的第一个.
Suffix | decimal Constant | Hex Constant ---------+------------------------------------+--------------------------- none | int | int | int | unsigned int | | long int | long int | unsigned long int | | long long int | long long int | unsigned long long int ---------+------------------------------------+--------------------------- u or U | unsigned int | unsigned int [...] | [...] | [...]
所以,0x80000000与在系统上32的位或更少的位int和32位或更大unsigned int是unsigned int,
| 归档时间: |
|
| 查看次数: |
1816 次 |
| 最近记录: |