Question

I have the following C code:

typedef unsigned char uint8_t;

void main(void) {
    uint8_t a = 1, b = 2, res;
    res = a + b;
}   

When I compile this code using gcc -Wconversion, I get the following warning:

test.c: In function 'main':
test.c:5:10: warning: conversion to 'uint8_t' from 'int' may alter its value [-Wconversion]

Can someone please explain why this warning appears? All three variables are of type uint8_t, so I don't really understand where the int comes from.

Was it helpful?

Solution

I don't really understand where the int comes from.

int comes from the C language standard. All operands of arithmetic operators are promoted before performing their operation. In this case uint8_t is promoted to an int, so you need a cast to avoid the warning:

res = (uint8_t)(a + b);

Here is how the standard defines integer promotions:

6.3.1.1 If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.

Since int can hold all possible values of uint8_t, a and b are promoted to int for the addition operation.

OTHER TIPS

Just to add to the existing answer about integer promotions, it might also be worth explaining what -Wconversion is warning you about.

Since a and b are both uint8_ts, the result of a + b might not fit in another uint8_t. By assigning the result back into a uint8_t, you force the compiler to perform a conversion which might change the value. Hence, the res variable might not actually represent the actual value of a + b.

For example, if a and b were both 0xff, then:

  • a + b is 0x1fe and has type int
  • (uint8_t)(a + b) is 0xfe

You should read more about C's int promotion rules. That's the rule, so the compiler must obey. Without it, some "trivial" code won't work (like you expected). For example

char d[4] = {0xFF, 0xFE, 0x80, 40};
int i = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
int j = d[2] - d[0]*d[1];

In some compilers for 8-bit microcontrollers there is an option to disable C standard conformation to speed up maths since always promoting a char to int to do the maths may cause unnecessary operations and slow down the process. It also consumes more memory which is valueable in such embedded systems. Of course it will make some code not portable or not work at all, but that's the trade off.

Modern microprocessors are 16 bit or more, so using char won't save you from any operation but may also increase code size and speed because of the sign adjustment before/after each operations. Most RISC architectures don't even have instruction for operating on operand different from its native size except for load/store and sign extending. So it's recommended to use the native int size for all temporaries. Types smaller than native register size should only be used in case of very large arrays that could increase cache miss, to save or read data from some other types of memory or for compatibility with other systems/libraries

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top