3
votes

After doing some research I know in arithmetic expressions char and short will be promoted to int internally. But I am still wondering whether integer promotions like that will occur in assignment internally.

(So please don't give me links only concerning other expressions. I am asking about what happens internally in ASSIGNMENT expressions)

char ch1, ch2 = -1;
ch1 = ch2;  // Q

Q: Which of the following will happen internally?

1, The value of ch1 is directly assigned to ch2. Integer promotions won't happen here.

2, The value of ch1 is first promoted to int type (8 bits→32bits), then the 32 bits value is demoted to char type, 8 bits, the final result. Integer promotions happen here.

I have found this book: C Primer Plus and in Page 174 there is:

"...When appearing in an expression, char and short, both signed and unsigned, are automatically converted to int, or if necessary, to unsigned int..."

So I think it should be 2, but I have heard someone told me it should be 1, where integer promotions don't happen.

I am really confused. Could you help me please? Thank you in advance.

4
Note that if either #1 or #2 occurs, the resulting value is the same and any reasonable compiler would generate the same code.chux - Reinstate Monica
@chux I knew it but I hope to know what happens internally.Mensu
@Mensu, what do you mean by internally? Are you interested in a specific compiler or platform? How shall we know?Jens Gustedt
@JensGustedt I am sorry. I didn't consider the platform things. Maybe I just want to know the case in generally used platforms...Mensu

4 Answers

2
votes

The answer is Neither 1 nor 2.

The value of ch2 is directly assigned to ch1. With the assignment operator, the left-hand operand is the target.

There are no promotions; the behaviour is specified by C11 6.5.16.1/2:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

In the previous paragraph it is defined:

The type of an assignment expression is the type the left operand would have after lvalue conversion.

where "lvalue conversion" means lvalue-to-rvalue conversion, which has the effect of removing const, volatile and _Atomic qualifiers for the type. (It also has an effect on array types, but that is moot here as arrays cannot appear as the left operand of an assignment).

Your quote from "C Primer Plus" is a mistake by the book. Integer promotions do not occur in all expressions. In fact, they occur when the integer is an operand of an operator, and the definition of that operator specifies that its operands undergo the integer promotions.

Most operators do specify that, but the assignment operator, and sizeof for example, do not. You can check the C standard's specification of each operator to see whether that operator promotes its operands.

3
votes

From the C99 standard:

6.5.16.1 Simple assignment

2 In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

In your case since both the LHS and RHS are of the same type, there is no need for any conversion.

1
votes

As the comment @chux makes, I don't see how this is a concern. Since everything happens internally, you should treat it as a black box and should not rely on the behavior of such.

However, being curious, I took the snippet and compiled it to assembly. Let's see what's happening exactly!

This is the source C code. I saved it in a file called test.c:

int main() {
  char ch1 = -1;
  char ch2 = -1;
  ch1 = ch2;
}

And this is the assembly generated by gcc. You can generate it yourself by calling gcc -S test.c. Below is the relevant section:

...

    movb    $-1, -1(%rbp)
    movb    $-1, -2(%rbp)
    movb    -2(%rbp), %cl
    movb    %cl, -1(%rbp)
    popq    %rbp

...

So basically, we are pushing value -1 twice on the stack (%rbp), then moving the value stored on the second slot (ch2) to another temporary register %cl, and finally, assign it to the first slot, ch1.

Wait, so what is this temporary register business?! Well it turns out %cl is exactly one byte in size! So yes, no conversion takes place.

As an aside: down to assembly, there is no such thing as type conversions unless we don't have enough space to store one. For example, were we to change the value from -1 to, say, 65537 (just exceeding the short), then what we see is:

x = 65537;

becomes:

movl    $65537, -4(%rbp)

We are simply assigning 4 bytes on the stack for the variable x. So internally, promotion is simply allocating more space on the stack. When you demote an integer down to a char, we are just taking the last byte from the integer and stick it into a new slot on the stack. So in the case both are chars to begin with (both are assigned one byte slot on the stack), there really need not be a conversion. But of course, this depends on the compiler. You can have a really inefficient compiler that actually pushes ch2 to stack with size 1 MB, computes the factorial of ch1, sings a song, then assign ch1 with ch2. As I said in the beginning, you should treat this as a black box, and not count on it!

0
votes

By default the type of char is signed. So the ch2=-1 is a valid value. So there is no need for integer promotion.

Integer promotion happens only when 2 different types are mixed. The resultant type used for computation is the larger of the 2 types. For char to integer promotion it is as you mentioned in 2. First integer promotion takes place for calculation and at store time the values are truncated to actual store bucket size.