C 神秘溢出
C Mysterious Overflow
为什么这段代码输出 -32768 而不是 32768?看起来像溢出,但我不知道在哪里。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *buffer = (char*)malloc(sizeof(char)*2);
buffer[0] = 0x80;
buffer[1] = 0x00;
int address = (buffer[0]<<8) | (buffer[1]);
printf("%d\n", address); //outputs -32768
return 0;
}
在您的编译器上 char
已签名。
在您的编译器上,0x80
被转换为 -0x80
以适应带符号的 char
。
所以 buffer[0]
为 -128,((-128)<<8) | (0)
计算为 -32768。
此代码可以通过多种方式执行。
char
在您的编译器上未签名。然后表达式将被评估为 0x80<<8 | 0x00
,从而给出 0x8000
。如果它适合您系统上的 int
,则结果将为 32768。否则它将以某种实现定义的方式转换为签名格式。在二进制补码计算机上,您可能会得到结果 -32768.
或 char
已在您的编译器上签名。然后 0x80
无法放入其中,但会以某种实现定义的方式转换为负数。在二进制补码计算机上,它可能会得到值 -128。然后左移这个负值 - 这会调用 未定义的行为(来源:C11 6.5.7/4)。这反过来可能会导致任何事情发生:您的程序可能会崩溃或打印无意义的内容,或者您的编译器可能会出现一些特定的非标准行为,例如将结果视为 -32768.
这里的关键是您不应该编写这样的代码,它依赖于多种形式的指定不当的行为。这是不好的做法。你最终得到这个的原因是因为你使用的是 C 的原生原始数据类型,如 char
和 int
,它们没有明确指定,因此用于位操作是危险的。
你的代码应该被固定到安全的地方,无论系统或编译器如何,它都会给出确定的结果:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main()
{
uint8_t *buffer = malloc( sizeof(uint8_t[2]) );
buffer[0] = 0x80;
buffer[1] = 0x00;
uint16_t address = ((uint16_t)buffer[0]<<8) | (buffer[1]);
printf("%" PRIu16 "\n", address);
free(buffer);
return 0;
}
为什么这段代码输出 -32768 而不是 32768?看起来像溢出,但我不知道在哪里。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *buffer = (char*)malloc(sizeof(char)*2);
buffer[0] = 0x80;
buffer[1] = 0x00;
int address = (buffer[0]<<8) | (buffer[1]);
printf("%d\n", address); //outputs -32768
return 0;
}
在您的编译器上 char
已签名。
在您的编译器上,0x80
被转换为 -0x80
以适应带符号的 char
。
所以 buffer[0]
为 -128,((-128)<<8) | (0)
计算为 -32768。
此代码可以通过多种方式执行。
char
在您的编译器上未签名。然后表达式将被评估为0x80<<8 | 0x00
,从而给出0x8000
。如果它适合您系统上的int
,则结果将为 32768。否则它将以某种实现定义的方式转换为签名格式。在二进制补码计算机上,您可能会得到结果 -32768.或
char
已在您的编译器上签名。然后0x80
无法放入其中,但会以某种实现定义的方式转换为负数。在二进制补码计算机上,它可能会得到值 -128。然后左移这个负值 - 这会调用 未定义的行为(来源:C11 6.5.7/4)。这反过来可能会导致任何事情发生:您的程序可能会崩溃或打印无意义的内容,或者您的编译器可能会出现一些特定的非标准行为,例如将结果视为 -32768.
这里的关键是您不应该编写这样的代码,它依赖于多种形式的指定不当的行为。这是不好的做法。你最终得到这个的原因是因为你使用的是 C 的原生原始数据类型,如 char
和 int
,它们没有明确指定,因此用于位操作是危险的。
你的代码应该被固定到安全的地方,无论系统或编译器如何,它都会给出确定的结果:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main()
{
uint8_t *buffer = malloc( sizeof(uint8_t[2]) );
buffer[0] = 0x80;
buffer[1] = 0x00;
uint16_t address = ((uint16_t)buffer[0]<<8) | (buffer[1]);
printf("%" PRIu16 "\n", address);
free(buffer);
return 0;
}