1
int main()
{
  char ch1 = 128;
  unsigned char ch2 = 128;
  printf("%d\n", (int)ch1);
  printf("%d\n", (int)ch2); 
}

The first printf statement outputs -128 and second 128. According to me both ch1 and ch2 will have same binary representation of the number stored: 10000000. So when I typecast both the values to integers how they end up being different value?

atul_pant
  • 99
  • 8
  • 1
    `char ch1 = 128;` is resulting in signed overflow, since 127 is the largest value that fits in an 8-bit 2's complement signed char. Effectively, it's `char ch1 = -128;` – user3386109 Jun 20 '20 at 21:59

4 Answers4

8

First of all, a char can be signed or unsigned and that depends on the compiler implementation. But, as you got different results. Then, your compiler treats char as signed.

A signed char can only hold values from -128 to 127. So, a value of 128 for signed char overflows to -128.

But an unsigned char can hold values from 0 to 255. So, a value of 128 remains the same.

Youssef13
  • 3,836
  • 3
  • 24
  • 41
1

An unsigned char can have a value of 0 to 255. A signed char can have a value of -128 to 127. Setting a signed char to 128 in your compiler probably wrapped around to the lowest possible value, which is -128.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
diatrevolo
  • 2,782
  • 26
  • 45
  • Yes I get it that signed char will store it as -128. But should that remain -128 if I typecasted this char to int? – atul_pant Jun 20 '20 at 22:03
  • Yes. `(int)(char)128 == -128` is true (on your platform). – KamilCuk Jun 20 '20 at 22:03
  • `A char can have a value of 0-255.`, That's **not** true. You should say: `An unsigned char can have a value of 0-255.`. Because **char** by itself could be signed or unsigned depending on the compiler implementation. The C specs doesn't state whether a char should be signed or unsigned. (I personally came across two compilers that behaves differently) – Youssef13 Jun 20 '20 at 22:05
  • @KamilCuk How? Because what I think the binary representation of both signed and unsigned char will be 10000000. So now if I type cast it to int, does compiler puts 1's after msb in case of signed char and 0's in case of unsigned? – atul_pant Jun 20 '20 at 22:07
  • `what I think the binary representation of both signed and unsigned char will be 10000000` Yes and `10000000` is `-127` with a signed 8-bit number. – KamilCuk Jun 20 '20 at 22:08
  • That’s a good point @Andreas Wenzel, I thought this when typing it out. I’ll edit that too. – diatrevolo Jun 20 '20 at 22:18
  • @atul_pant The compiler doesn't just look at the bit pattern when type casting. It also looks at the type of variable, specifically it behaves differently if the type is signed versus unsigned. – user3386109 Jun 20 '20 at 22:34
  • 1
    @Youssef13: And similarly, your answer should say an `unsigned char` can have values of **at least** 0 to 255. Furthermore, whether converting 128 to a signed `char` produces −128 is implementation defined. And a `char` may be signed or unsigned, but it is never a `signed char` nor an `unsigned char`; it remains a distinct type. – Eric Postpischil Jun 20 '20 at 22:35
  • @EricPostpischil is correct. Not all implementations use [two's complement](https://en.wikipedia.org/wiki/Two's_complement) for representing negative values. – Andreas Wenzel Jun 20 '20 at 22:50
  • These are the types of exchanges that stack overflow should be all about. – diatrevolo Jun 21 '20 at 00:06
  • @user3386109: can you please explain what you mean by compiler checking type of variable too while typecasting? May be my fundamental understanding of the typecasting is wrong. So please can you tell or mention some source from where I can read. Thank you – atul_pant Jun 21 '20 at 06:28
  • When casting to a larger type, [signed types are sign extended, whereas unsigned types are zero extended](https://en.wikipedia.org/wiki/Sign_extension). – user3386109 Jun 21 '20 at 06:33
1

For starters these castings

printf("%d\n", (int)ch1);
printf("%d\n", (int)ch2);

are redundant. You could just write

printf("%d\n", ch1);
printf("%d\n", ch2);

because due to the default argument promotions integer types with the rank that is less than the rank of the type int are promoted to the type int if an object of this type can represent the value stored in an object of an integer type with less rank.

The type char can behave either as the type signed char or unsigned char depending on compiler options.

From the C Standard (5.2.4.2.1 Sizes of integer types <limits.h>)

2 If the value of an object of type char is treated as a signed integer when used in an expression, the value of CHAR_MIN shall be the same as that of SCHAR_MIN and the value of CHAR_MAX shall be the same as that of SCHAR_MAX. Otherwise, the value of CHAR_MIN shall be 0 and the value of CHAR_MAX shall be the same as that of UCHAR_MAX. 20) The value UCHAR_MAX shall equal 2CHAR_BIT − 1.

So it seems by default the used compiler treats the type char as signed char.

As a result in the first declaration

char ch1 = 128;  
unsigned char ch2 = 128;

the internal representation 0x80 of the value 128 was interpreted as a signed value because the sign bit is set. And this value is equal to -128.

So you got that the first call of printf outputted the value -128

printf("%d\n", (int)ch1);

while the second call of printf where there is used an object of the type unsigned char

printf("%d\n", (int)ch2);

outputted the value 128.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Your fundamental error here is a misunderstanding of what a cast (or any conversion) does in C. It does not reinterpret bits. It's purely an operation on values.

Assuming plain char is signed, ch1 has value -128 and ch2 has value 128. Both -128 and 128 are representable in int, and therefore the cast does not change their value. (Moreover, writing it is redundant since the default promotions automatically convert variadic arguments of types lower-rank than int up to int.) Conversions can only change the value of an expression when the original value is not representable in the destination type.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Can you tell me what do you mean by "It does not reinterpret bits. It's purely an operation on values" ? What exactly does casting a type to other type means, especially in this context ? – atul_pant Jun 21 '20 at 06:21
  • A cast is an operator that produces an explicit conversion between types. Conversions can also happen implicitly in plenty of contexts. When I say it "does not reinterpret bits", I mean that all conversions in C are defined in terms of values. If the value being converted is representable in the destination type, the value is unchanged and the only effect of the conversion is a change in type (which may affect how it participates in a larger expression). If the value is not representable in the destination type, there are rules for how the result is produced; in some cases it's impl-defined. – R.. GitHub STOP HELPING ICE Jun 21 '20 at 15:58