Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The %x format specifier with an unsigned char in C [duplicate]

Tags:

c

I ran across the following example program and I don't exactly understand it's output:

#include <stdio.h>

int main( void ) {

    unsigned char i, m =0xFF, n=0x1;

    for ( i = 0; i != 8; i++,n+=n, m/=2 )
        printf("%5x %5x %5x %5x %5x %5x\n", n,m,n&m,n|m,n^m,~n);

    return 0;
}

It prints out:

    1    ff     1    ff    fe fffffffe
    2    7f     2    7f    7d fffffffd
    4    3f     4    3f    3b fffffffb
    8    1f     8    1f    17 fffffff7
   10     f     0    1f    1f ffffffef
   20     7     0    27    27 ffffffdf
   40     3     0    43    43 ffffffbf
   80     1     0    81    81 ffffff7f

The problem is that last column. Since it's unsigned char I would expect it to print out just 2 places in every column. ~n produces an unsigned char as it's result, but it seems like it's being cast to a signed 32 bit value and sign extended by the %5x specifier.

How is that possible, what's going on here?

like image 716
Robert S. Barnes Avatar asked Aug 25 '11 10:08

Robert S. Barnes


People also ask

Is %d signed or unsigned?

unsigned int x; then 'x' can take values from 0 to 4,294,967,295 (since 'x' is now an unsigned variable). It is not necessary to give signed keyword to any variable because all the variables are signed by default. In printf, %d is used for signed integers whereas %u is used with unsigned integers.

What is %U and %D in C?

%d is a signed integer, while %u is an unsigned integer. Pointers (when treated as numbers) are usually non-negative. If you actually want to display a pointer, use the %p format specifier.

What does %U in C mean?

%u. It is used to print the unsigned integer value where the unsigned integer means that the variable can hold only positive value.


2 Answers

Integer types are promoted when they are used in arithmetic operations (this has nothing to do with printf, by the way).

So, for example,

unsigned char x = 0xff;
int y = ~x; // x is promoted to 0x000000ff, then changed to 0xffffff00
unsigned char z = ~x; // truncated back to 0x00

Integer promotion causes various problems:

unsigned char x = 1;
if (x << 8)
    puts("x << 8 is true"); // does print
x <<= 8;
if (x)
    puts("x <<= 8 is true"); // does not print

The two ways to truncate things are casting and masks. Use whatever you prefer.

unsigned char x = 0xab;
printf("x = %02x\n", (unsigned char) x);
printf("x = %02x\n", x & 0xff);

Integer promotion doesn't always happen, and it's not the only kind of implicit cast. It's also a bit subtle and the exact rules are difficult to remember. You only really need to worry about it if you're working with 64-bit numbers, because 1U << 32 could end up being 0 or 1 or something else entirely. (It's often 1 on x86).

like image 101
Dietrich Epp Avatar answered Oct 19 '22 23:10

Dietrich Epp


The issue is that it's promoted to an int when you pass it into printf's varags - as in Dietrich's answer - when you negate it.

Unfortunately you'll need to strip it down to a byte to pass in, i.e. (~n & 0xff).

like image 36
Rup Avatar answered Oct 19 '22 22:10

Rup