有人可以向我解释以下代码输出:
void myprint(unsigned long a)
{
printf("Input is %lx\n", a);
}
int main()
{
myprint(1 << 31);
myprint(0x80000000);
}
输出gcc main.c
:
Input is ffffffff80000000
Input is 80000000
为什么(1 << 31)
被视为已签名且0x80000000
被视为未签名?
答案 0 :(得分:13)
在C中,表达式的结果取决于操作数(或某些操作数)的类型。特别是,1
是int
(已签名),因此1 << n
也是int
。
0x80000000
的类型(包括签名)由规则here决定,它取决于系统上int
和其他整数类型的大小,没有指明。选择一种类型,使0x80000000
(一个大的正数)在该类型的范围内。
如果您有任何误解:文字0x80000000
是一个大的正数。人们有时会错误地将其等同于负数,将值与表示混合在一起。
在您的问题中,您说“为什么0x80000000被视为无符号?”。但是,您的代码实际上并不依赖于0x80000000
的签名。你用它做的唯一事情是将它传递给带有unsigned long
参数的函数。因此,无论是签名还是未签名都无关紧要;传递给转换时,它会转换为具有相同值的unsigned long
。 (由于0x80000000
在unsigned long
的最小保证范围内,因此不可能超出范围。
所以,那是0x80000000
处理的。那么1 << 31
呢?如果您的系统具有32位int(或更窄),则由于带符号的算术溢出而导致undefined behaviour。 (Link to further reading)。如果您的系统具有较大的整数,那么这将产生与0x80000000
行相同的输出。
如果您改为使用1u << 31
,并且您有32位整数,则没有未定义的行为,并且您可以保证看到程序输出80000000
两次。
由于您的输出不是80000000
,因此我们可以断定您的系统具有32位(或更窄)的int,并且您的程序实际上会导致未定义的行为。如果0x80000000
为32位,则unsigned int
的类型为int
,否则为unsigned long
。
答案 1 :(得分:6)
为什么
(1 << 31)
被视为已签名且0x80000000
被视为未签名?
来自 6.5.7比特移位运算符的C11规范:
3对每个操作数执行整数提升。结果的类型 升级后的左操作数。 [...]
4 E1的结果&lt;&lt; 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十进制常数以非零数字开头,由a组成 十进制数字序列。八进制常量由前缀0组成 可选地后跟一个数字0到7的序列。一个 十六进制常量由前缀0x或0X后跟a组成 十进制数字的序列和字母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
为datetime
,
答案 2 :(得分:2)
您显然使用的是32位public class YASGP {
public static void main(String args[]) throws IOException {
ServerSocket server;
try{
server = new ServerSocket(5559);
System.out.println("Listening for connection on port 5559 ....");
while (true) {
Socket clientSocket = server.accept();
InputStreamReader isr = new InputStreamReader(clientSocket.getInputStream());
BufferedReader reader = new BufferedReader(isr);
String line = reader.readLine();
new Thread(new WorkerRunnable(clientSocket)).start();
while (!line.isEmpty()) {
System.out.println("----- " + line);
if (!line.contains("OPTIONS")) {
// System.out.println("Non c'è nulla!!!");
} else {
timeS = line.substring(line.indexOf("timeS=") + 6, line.indexOf("&url"));
url = line.substring(line.indexOf("url=") + 4, line.lastIndexOf("¶m"));
param = line.substring(line.indexOf("¶m=") + 7, line.indexOf("HTTP"));
}
line = reader.readLine();
}
}
}
}catch (IOException e) {
System.out.println("Could not listen on port: 4001");
}
}
private static class RequestHandlingClass {
public RequestHandlingClass(Socket clientSocket) {
}
}
}
和int
的系统。
unsigned int
适合1
,因此它是int
,signed int
则不是。对于十进制常量,将使用下一个较大的有符号类型,它可以保存该值,对于十六进制和八进制常量,首先使用相应的无符号类型(如果适合)。这是因为无论如何它们通常都是无符号的。有关完整的值/类型矩阵,请参阅C标准6.4.4.1p5。
对于有符号整数,通过更改符号的左移是未定义的行为。这意味着所有赌注都已关闭,因为您超出了语言规范。
说,以下是对结果的解释:
0x80000000
显然是您系统上的64位。long
将int
转移到符号位,就像您预期的那样。1
。int
转换为ints
,使得2的补码表示不需要任何操作(只需重新解释位模式)unsigned
时,该符号将扩展为unsigned long
参数的高位。如何避免它:
myprint
后缀附加到整数常量,此处为:U
或1U
)。0x1U
更小的类型时,请注意标准整数转换。int
固定宽度类型。请注意,标准整数类型没有定义的位宽。对于32位,请使用stdint.h
作为变量。对于常量,请使用宏:uint32_t
(不带后缀!)。答案 3 :(得分:1)
我的想法:第一次打电话给myprint()&#39;是一个表达式,因此必须在运行时计算。所以编译器需要将它(通过生成的指令)解释为带符号的 int 左移,产生负的签名 int ,这是然后签名扩展以填充长,然后重新解释为 unsigned long 。 (我认为这可能是编译错误?)
相比之下,第二次打电话给'myprint()&#39;是一个硬编码的整数常量表达式,传递给以 unsigned long 作为参数的例程;我认为编译器是为了从这个上下文中假设常量表达式已经 unsigned long ,因为没有公开冲突的类型信息。
答案 4 :(得分:0)
如果我错了,请纠正我。这就是我所理解的。
在我的机器上,如M.M所说,sizeof(int)= 4.(通过print sizeof(int)确认)
因此,1&lt;&lt; 31变为(带符号)0x80000000,因为1被签名。 但是,0x8000000变为无符号,因为它不适合signed int(因为它被视为正数,而int的最大正数可以是0x7fffffff)。
因此,当signed int转换为long时,将发生符号扩展(使用符号位进行扩展)。转换unsigned int时,使用0进行扩展。
这就是为什么在myprint(1&lt;&lt; 31)的情况下会有额外的1,并且在任何一种情况都不是这样的
1)myprint(1u <&lt; 31)
2)myprint(1&lt;&lt; 31),当int&gt; 32位,因为在这种情况下符号位不是1.