danielmhanover
danielmhanover

Reputation: 3124

C Mysterious Overflow

Why does this code output -32768 and not 32768? Looks like an overflow but I cannot figure out where.

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

Upvotes: 0

Views: 78

Answers (2)

Lundin
Lundin

Reputation: 215114

There are several possible ways that this code can execute.

  • Either char is unsigned on your compiler. Then the expression will get evaluated as 0x80<<8 | 0x00 which gives 0x8000. If this fits inside an int on your system, the result will be 32768. Otherwise it will get converted to a signed format in some implementation-defined way. On a two's complement computer you will likely get the result -32768.

  • Or char is signed on your compiler. Then 0x80 can't fit inside it, but gets converted to a negative number in some implementation-defined way. On a two's complement computer it will likely get the value -128. You then left shift this negative value - this invokes undefined behavior (source: C11 6.5.7/4). This in turn could cause anything to happen: your program could crash or print nonsense, or there could be some specific non-standard behavior on your compiler such as treating the result as -32768.

The key here is that you shouldn't write code like this, which relies on numerous forms of poorly specified behavior. That's bad practice. The reason why you end up with this because you are using the native, primitive data types of C like char and int, which are poorly specified and therefore dangerous to use for bit manipulations.

Your code should be fixed into something safe, that will give a deterministic result no matter system or compiler:

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

Upvotes: 0

On your compiler char is signed.

On your compiler, 0x80 is converted to -0x80 to fit in a signed char.

So buffer[0] holds -128, and ((-128)<<8) | (0) evaluates to -32768.

Upvotes: 1

Related Questions