Net
Net

Reputation: 21

A strange phenomenon using casting in C

the following code:

int main() {
    int small_num   = 0x12345678;
    int largest_num = 0xFFFFFFFF;
    printf("small: without casting to short: 0x%.8x, with casting to short: 0x%.8x\n", small_num>>16, (short)(small_num>>16));
    printf("large: without casting to short: 0x%.8x, with casting to short: 0x%.8x\n", largest_num>>16, (short)(largest_num>>16));
    return 0;
}

gives me the output (using codepad):

small: without casting to short: 0x00001234, with casting to short: 0x00001234
large: without casting to short: 0xffffffff, with casting to short: 0xffffffff

That's seems extremely strange. Anyone have an idea why it happens this way?

Upvotes: 2

Views: 133

Answers (2)

Nisse Engström
Nisse Engström

Reputation: 4752

Short answer

The hexadecimal constant has type unsigned int. When converted to signed int the value becomes -1. Right-shifting a negative value usually leaves the sign-bit unchanged, so -1 >> 16 is still -1. A short int passed to a variadic function gets promoted to signed int which, when interpreted as an unsigned int by the %x conversion specifier, prints out 0xffffffff.

Long answer

However, your code is broken for a number of reasons.

Integer conversion

int largest_num = 0xFFFFFFFF;

The type of the hexadecimal constant is the first of the following types in which its value can be represented: int, unsigned int, long int, unsigned long int, long long int, unsigned long long int.

If int has more than 32 bits, you're fine. If int has 32 bits or less, The result is implementation-defined (or an implementation-defined signal is raised).

Usually, largest_num will have all bits set and have the value -1.

Shifting a negative value

largest_num>>16

If the value of largest_num is negative, the resulting value is implementation-defined. Usually, the sign bit is left unchanged so that -1 right-shifted is still -1.

Integer promotion

printf ("0x%.8x\n", (short)(largest_num>>16));

When you pass a short int to a variadic function, the value will be promoted to int. A negative value will be preserved when converted to the new type.

However, the "%x" conversion specifier expects an unsigned int argument. Because unsigned int and signed int are not compatible types, the behaviour of the code is undefined. Usually, the bits of the signed int is re-interpreted as an unsigned int, which results in the original value of the hexadecimal constant.

Calling a variadic function

printf(...);

printf() is a variadic function. Variadic functions (typically) use different calling conventions than ordinary functions. Your code invokes undefined behaviour if you don't have a valid declaration of print() in scope.

The usual way to provide a declaration for printf() is to #include <stdio.h>.


Source: n1570 (the last public draft of the current C standard).

Upvotes: 0

user3751454
user3751454

Reputation: 41

When you are casting to (short) in the printf call, then the compiler will cast it from short back to int, which is the parameter which is passed to printf. Therefore, 1234 will be mapped to 1234, and ffff (which is exactly -1) is mapped to ffffffff. Note that negative integers are expanded from short to long by adding "on bits" on their left.

Upvotes: 2

Related Questions