4

GCC warns this code:

unsigned char i = 1;
unsigned char j = 2;
i += j;

says:

warning: conversion to 'unsigned char' from 'int' may alter its value [-Wconversion]
   i += j;
        ^

It seems that j is implicitly converted to int.
Why the implicit conversion occurs for the addition of same-type variables?

equal-l2
  • 899
  • 7
  • 17
  • 4
    That's how C works. There are implicit conversions to word size signed integer for historical reasons targeting efficiency. – Jon Chesterfield Mar 14 '17 at 11:01
  • The correct term is _implicit conversion_. Cast means that an operator `(type)` is used by the programmer, so it is always explicit. A cast will force a conversion, but conversions can also happen when no cast is present. – Lundin Mar 14 '17 at 12:59
  • @Lundin Fixed. Thanks. – equal-l2 Mar 14 '17 at 13:57

2 Answers2

7

Here's what the C11 draft standard says about (almost) this very case in §5.1.2.3 (page 15):

EXAMPLE 2

In executing the fragment

char c1, c2;
/* ... */
c1 = c1 + c2;

the ‘‘integer promotions’’ require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum. Provided the addition of two chars can be done without §5.1.2.3 Environment 15 ISO/IEC 9899:201x Committee Draft — April 12, 2011 N1570 overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only produce the same result, possibly omitting the promotions.

So, it's done since the standard requires that it is done. If you look the generated code, it's quite (very) possible that the compiler is clever enough not to do the conversion at all in practice.

unwind
  • 391,730
  • 64
  • 469
  • 606
4

C mandates integer promotions, quoting C11 N1570/6.3.1.1p2

The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.

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

So your unsigned char variable is used in such an expression and as such is converted to int, since i += j is functionally equivalent to i = i + j.

If you know for certain the result is in the range of an unsigned char, then just apply a cast to the result of the addition before assigning, like this:

i = (unsigned char)(i + j)
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • This works but I think in this situation the `char` data type is being used inappropriately. I mean why would one declare storage meant for literals `1` and `2` as `char`s in the first place? If the storage locations were meant to hold both integers and characters one can easily use an integer type, since characters use smaller storage. – Lym Mar 14 '17 at 11:46
  • @Lym - One reason would space considerations. Not something you'd often think about in a full on hosted system, but for small offsets one may feel obligated to choose the smallest type they can. (Of course `uint8_t` is a better choice). – StoryTeller - Unslander Monica Mar 14 '17 at 11:48
  • If you don't mind my asking, have you ever found yourself in a situation or read code that took this approach. Seems to me like being willing to lose some data, due to truncation would be a bad idea in any situation. Just looking for a case study. – Lym Mar 14 '17 at 11:54
  • @Lym - Myself personally? I haven't used data types less then an integer. But I've read enough about embedded programming to learn different considerations may come into play when your available space is severely limited. – StoryTeller - Unslander Monica Mar 14 '17 at 11:57
  • I guess one of the other things I was curious about was if the behavior, when one casts to a lower type was even defined, something like [http://stackoverflow.com/questions/6752567/casting-a-large-number-type-to-a-smaller-type] looks like it is. Thanks for the explanation. – Lym Mar 14 '17 at 12:03
  • @Lym - I should note that the cast isn't there to make this valid C. From a purely language specification point of view, it's valid even without the cast. The cast merely silences the warning, by signalling to the compiler that you do this knowingly. – StoryTeller - Unslander Monica Mar 14 '17 at 12:05
  • @Lym 8 bit microcontroller systems use 8 bit integer types all the time, since that's the most effective type to use. It is both a speed concern and a memory concern on such systems. 8 bit system compilers would still have to evaluate the implicit integer promotion of the expression to `int`, to see if it yielded any side effects. If it can rule out any side effects caused by the promotion, the compiler can optimize the whole calculation to use 8 bit integer arithmetic only, making the code run much faster than it would when using `int`. – Lundin Mar 14 '17 at 12:55