Deepu
Deepu

Reputation: 7610

Signed and unsigned characters behavior while printing in C

I have compiled the following C Program in Code::Blocks 10.05 on Windows 7.

int main()
{
    unsigned char a=-5;
    signed char b=-5;
    char c=-5;
    printf("%d  %d  %d \n",a,b,c);
    printf("%u  %u  %u \n",a,b,c);
}

I expected the following output

251 -5 -5
251 251 251

I have reasoned like this,

-5 is represented as 1111 1011 in 2's Complement Notation. Hence 251 will be printed.

But I got the following output.

251 -5 -5 
251 4294967291 4294967291

I know 4294967291 is the 32 bit 2's Complement representation of -5.

But why the unsigned char a is printed as 251 instead of 4294967291?

Upvotes: 3

Views: 227

Answers (4)

Peter
Peter

Reputation: 36597

unsigned integral types (including unsigned char) work with modulo arithmetic. For unsigned char, that is modulo UCHAR_MAX + 1, where UCHAR_MAX is an implementation-defined value in <limits.h> with a typical value with most implementations of 255 (255 is also the minimum value that the standard permits for UCHAR_MAX). Adding 256 to -5 (i.e. to get a remainder between 0 and 255 in order to initialise a in your code) will explain why a has a value of 251.

As to why any of the values are printing at all, the values are being promoted to be of type int when passed to printf(). This promotion is unrelated to your format string but is what the %d format specifier expects, so the values print as you see.

Upvotes: 2

o11c
o11c

Reputation: 16056

@Vality is slightly wrong in his answer. Because unsigned char must hold values up to at least 255, then if uint8_t exists, CHAR_BIT must be 8.

There's no reason to use the nasty PRI macros, just use this:

unsigned char a = -5;
signed char b = -5;
char c = -5;

printf("%hhd  %hhd  %hhd \n", a, b, c);
printf("%hhu  %hhu  %hhu \n", a, b, c);

Upvotes: 1

user1084944
user1084944

Reputation:

Because a holds the value 251, not the value -5.

Upvotes: 0

Vality
Vality

Reputation: 6607

printf is a variadic function, which causes (thanks to @TheParamagneticCroissant for the correction) the integral arguments to become promoted to int or unsigned int. Which according to the standard promotion rules will sign extend them appropriately. This is causing the numbers you are getting to be the results after this promotion process.

Instead you can use "%" PRIu8 to print 8 bit unsigned and "%" PRId8 to print 8 bit signed numbers. However beware that the arguments will still be promoted and then printf will downcast them again according to the format specifier, which may possibly still change that value but I am not sure of that from memory.

In addition char is not guaranteed in C to be 8 bit, it is better to include the <stdint.h> header and use the proper fixed width types.

A fixed version of your code would be as so:

#include <stdint.h>
int main()
{
    uint8_t a=-5;
    int8_t b=-5;
    printf("%" PRId8 "%" PRId8 "\n", a, b);
    printf("%" PRIu8 "%" PRIu8 "\n", a, b);
}

Note I do not include an equivalent of a char as all the fixed width types are of defined sign so there is no direct equivalent of char, which is of undefined signedness.

Upvotes: 4

Related Questions