2

I'm pretty much assuming this is a stupid question... but I can't really find the answer for it. So I'm asking this here.

For the purpose of learning about implicit type casting, I'm running the following code on C.

#include <stdio.h>

int main()
{
    unsigned char i;
    char cnt = -1;

    int a[255];

    for (int k = 0; k < 255; k++)
    {
        a[k] = k;
    }
    for (i = cnt - 2; i < cnt; i--)
    {
        a[i] += a[i + 1];
        printf("%d\n", a[i]);
    }

    return 0;
}

When I ran this program, nothing happened.

I was able to found out that the loop condition of for-loop was false at the first iteration, so the program exited the for-loop right away.

However, I don't get the reason why.

As far as I know, C does implicit casting when assigning or comparing variables with different types. So I thought that on i = cnt - 2, the minus operation makes the value -3, and then implicit casting assigns i with a value 253.

Then, shouldn't the condition i < cnt be true since (by another implicit casting of cnt because of comparison of signed and unsigned char) 253 is smaller than 255?

If it isn't, why is this false? Is there something that I missed or is there some exception that I don't know?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 253 is not smaller than -3 :) cnt data type is not unsigned char, just char -128 to 127. If u make unsigned char cnt = -1. It will works fine. – Tevfik Kadan Sep 02 '21 at 12:04
  • @Tevfik Kadan I'm kinda confused. It did worked when I did a same as you said, but shouldn't ```cnt``` be automatically casted into ```unsigned char``` and become ```255``` when doing ```i < cnt```? I just tried ```printf("%d\n", (unsigned char)253 < (char) -1)``` and it also gave false... – asdfqwerzxcv215 Sep 02 '21 at 12:14
  • Both operands of the comparison are cast to `int`, with no intermediate step to `unsigned char` for `cnt` (or `(char)-1`). – the busybee Sep 02 '21 at 12:19
  • Think of `i < cnt` as `((int) i) < ((int) cnt)` – Support Ukraine Sep 02 '21 at 12:24
  • Thanks! So unsigned char, short doesn't casts to char or short but casts to int... – asdfqwerzxcv215 Sep 02 '21 at 12:27
  • 3
    Whether plain char is signed or not is implementation defined. *the purpose of learning about implicit type casting* There is no such thing as implicit type casting. All casting is explicit by definition. If you want to learn about implicit type conversions, the first thing to learn is to **avoid them at all costs**. – n. m. could be an AI Sep 02 '21 at 12:34
  • https://en.cppreference.com/w/c/language/conversion - a lot of info, but the relevant parts: "Usual arithmetic conversions" applies to every `<` operator. When no floating point type is involved, case 4 there says integer promotions happen first. And integer promotions will promote both types to `int` (except on strange systems where `sizeof(char)==sizeof(int)`). – aschepler Sep 02 '21 at 12:56
  • 2
    Refer to [this discussion on implicit type promotions](https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules). (As noted, _implicit_ casts are not a thing. Casts are always explicit.) – ryyker Sep 02 '21 at 13:00
  • 1
    This is a cast: (_type_). It is using the cast operator. That's the only cast that exist in C and it is always explicit by the programmer, triggering an explicit conversion. There are also various implicit conversions. But there are no implicit casts. – Lundin Sep 02 '21 at 13:10
  • I have a vague memory that some post-K&R1, pre-C89 compilers *would* promote `unsigned char` and `unsigned short` to `unsigned int` instead of `int` (even if `int` could represent all the values of the original type). It's a bit of a pre-standard, grey area because K&R1 never included `unsigned char` or `unsigned short`, and `char` always got promoted to `int` whether or not it was unsigned. – Ian Abbott Sep 02 '21 at 13:29
  • This is C++ but I’d suggest you give it a watch, it will provide more insight than comments on here: https://youtu.be/iNeHHczBTIs – LEF Sep 02 '21 at 15:04
  • Thanks for all the comments and answers. I was able to clarify the rule of implicit conversion and the difference of 'casting' and 'conversion'... I'm not sure if I fully digested but was able to catch the point. I'll try understanding these fully as short as I can :) – asdfqwerzxcv215 Sep 04 '21 at 07:20

2 Answers2

4

Your question is not stupid at all. You were close to the solution: i is assigned the value -3 but the implicit conversion to the type of i, unsigned char, changes the value to 253.

For a more precise explanation, there are multiple issues in your test code:

  • char may be signed or unsigned depending on the platform and compiler configuration, so char cnt = -1; may store the value -1 or 255 into cnt, or even some other value if char is unsigned and has more than 8 bits.

  • The behavior of for (i = cnt - 2; i < cnt; i--) also depends on whether char is signed or unsigned by default:

    • in all cases, the test i < cnt is evaluated with both operands converted to int (or unsigned int in the rare case where sizeof(int)==1). If int can represent all values of types char and unsigned char, this conversion does not change the values.

    • if char is unsigned and has 8 bits, cnt has the value 255 so i is initialized with the value 253 and the loop runs 254 times with i from 253 down to 0, then i-- stores the value 255 again into i, for which the test i < cnt evaluates to false. The loop prints 507, then 759, ... 32385.

    • if char is signed and has 8 bits, as is probably the case on your system, cnt has the value -1 and i is initialized with the value -3 converted to unsigned char, which is 253. The initial test i < cnt evaluates as 253 < -1, which is false, causing the loop body to be skipped immediately.

You can force char to be unsigned by default by giving the compiler the appropriate flag (eg: gcc -funsigned-char) and test how the behavior changes. Using Godbolt's compiler explorer, you can see that gcc generates just 2 instructions to return 0 in the signed (default) case and the expected output in the unsigned case.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    You have done a good job here of pointing out one of the unsettling things about the ambiguities of the `char` default type in C. I found [this conversation](https://stackoverflow.com/questions/2054939/is-char-signed-or-unsigned-by-default) on the topic had a calming affect. – ryyker Sep 02 '21 at 13:17
2

For starters let's assume that the type char behaves as the type signed char.

In this condition

i < cnt

the both operands are implicitly converted to the type int due to the integer promotions.

From the C Standard (6.5.8 Relational operators)

3 If both of the operands have arithmetic type, the usual arithmetic conversions are performed.

and (6.3.1.8 Usual arithmetic conversions)

1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:

... Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

If both operands have the same type, then no further conversion is needed

and (6.3.1.1 Boolean, characters, and integers)

  1. ...If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.

Thus the positive value of i represented like 0000 0000 1111 1101 after the integer promotions will be greater than the negative value 1111 1111 1111 1111.

So the condition of the for loop at once evaluates to logical false because the positive value 253 of the type int is greater than the negative value -1 of the type int.

Here is a demonstrative program.

#include <stdio.h>

int main(void) 
{
    char cnt = -1;
    unsigned char i = cnt - 2;
    
    printf( "cnt = %x\n", ( unsigned int )cnt );
    printf( "i = %x\n", ( unsigned int )i );
    
    printf ( "i < cnt is %s\n", i < cnt ? "true" : "false" );
    
    return 0;
}

The program output is

cnt = ffffffff
i = fd
i < cnt is false
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335