2016-11-21 55 views
0

为什么此代码输出-32768而不是32768?看起来像一个溢出,但我不知道在哪里。C神秘的溢出

#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; 
} 
+3

由于缓冲区[0]为-128 – immibis

+0

哇哦,应该是无符号的不应该是 – danielmhanover

+0

的“int”是一个有符号的数字。如果高位被设置为有符号整数,则表示它为负。也许你应该谷歌“二补”。 – TonyB

回答

1

对您的编译器char进行签名。

在您的编译器上,0x80转换为-0x80以适合签署的char

因此buffer[0]保持-128,并且((-128)<<8) | (0)评估为-32768。

+0

'( - 128)<< 8'评估为未定义的行为。 – Lundin

0

该代码可以执行的几种可能的方式。

  • char在你的编译器上没有签名。然后该表达式将被评估为0x80<<8 | 0x00,其给出0x8000。如果这适合您系统中的int,则结果将为32768.否则,它将以某种实现定义的方式转换为带符号格式。在二进制补码计算机上,您可能会得到结果-32768。

  • char在您的编译器上签名。然后0x80不适合它,但在某些实现定义的方式中转换为负数。在二进制补码计算机上,它可能会得到-128的值。然后,您左移这个负值 - 这会调用未定义的行为(来源:C11 6.5.7/4)。这反过来可能导致任何事情发生:你的程序可能会崩溃或打印废话,或者编译器可能存在一些特定的非标准行为,如将结果视为-32768。

这里的关键是,你不应该写这样的代码,它依赖于许多形式不明确的行为。这是不好的做法。之所以最终这样做,是因为您使用的是原始的C原始数据类型,如charint,这些数据类型指定不准确,因此很难用于位操作。

你的代码应该被固定到安全的东西,这将使一个确定性的结果,不管系统或编译器:

#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; 
}