Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C hex constant type

Tags:

c

gcc

I wrote the following c code:

#include <stdio.h>

int main () {
  printf("%d\n", -1 >> 8);
  return 0;
}

I compile this code with gcc 4.6.3 on my x86_64 using the -m32 flag. I get -1 printed out as I would expect, the shift occurs arithmetically using two's complement representation resulting in -1.

Now if I instead write

printf("%d\n", 0xFFFFFFFF >> 8);

I get 16777215. I would have expected this constant to be interpreted as an int (signed) and then the shift to be arithmetic which would result in -1 again. I've looked through the latest C standard and I can't seem to understand why this is the case. Any ideas?

like image 477
dschatz Avatar asked Sep 19 '12 17:09

dschatz


2 Answers

According to the C99 standard (6.4.4.1), hexadecimal constants will be the first type on this list that can represent them:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

The hex literal 0xFFFFFFFF does not fit in an int (which can hold the values -0x80000000 to 0x7FFFFFFF), but does fit in unsigned int, and therefore its type will be unsigned. Right-shifting the unsigned value 0xFFFFFFFF by 8 gives 16777215.

like image 157
interjay Avatar answered Oct 22 '22 17:10

interjay


Undecorated integral literals have different type depending on whether they are decimal or not (6.4.4.1/5 in C11, Table 6 in C++11):

  • decimal literals, i.e. [1-9][0-9]*, are always signed.

  • hexadecimal and octal literals are either signed or unsigned. If the value is to large for a signed type, but small enough to fit the unsigned type of the same width, it will be unsigned. (This is what happens to your hex constant.)

Right-shifting negative integers is implementation-defined, and you happen to get sign-extension. Right-shifting the unsigned value is simple division by two.

like image 34
Kerrek SB Avatar answered Oct 22 '22 16:10

Kerrek SB