Search code examples
cgccgcc-warning

Why does arithmetic operations on (more than) three unsigned chars always trigger a warning when using -Wconversion?


Compiling the following code

// foo.c

typedef unsigned char uint8_t;

int main() {
    uint8_t a = 1;
    uint8_t b = 10;
    uint8_t c = 12;

    uint8_t k = a + b + c;
}

using GCC 11.2 (also other GCC versions) with the following full-command:

gcc -Wconversion foo.c

Results in the following warning:


Output of x86-64 gcc 11.2 (Compiler #3)
<source>: In function 'main':
<source>:11:17: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
   11 |     uint8_t k = a + b + c;
      |                 ^
Compiler returned: 0

This does not happen when adding two unsigned chars, i.e. a + b, for example. It happens when using any combination of arithmetic operations between more than three unsigned chars. And it does not happen with clang version 12.0.1.

Why does a + b + c, for example, result in an int that must be cast to uint8_t before assigning to k to supress the warning?


Solution

  • This is due to how integer promotion works in C. The C standard specifies that if an int can hold the values of the types of a and b - then they are converted to int for the operation.

    This is the case with uint8_t and int.

    Quote from the standard (6.3.1.1):

    Every integer type has an integer conversion rank defined as follows

    ...

    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. 48) All other types are unchanged by the integer promotions


    In old versions of gcc - this warning was triggered by addition of two operands like a + b. In GCC 10 this was removed by default from -Wconversion, but this warning is available with -Warth-conversion. From the GCC 10 release notes:

    -Warith-conversion re-enables warnings from -Wconversion, -Wfloat-conversion, and -Wsign-conversion that are now off by default for an expression where the result of an arithmetic operation will not fit in the target type due to promotion, but the operands of the expression do fit in the target type.

    i.e. since GCC 10 the reason a + b doesn't trigger a warning with -Wconversion is that both of the operands of the express are uint8_t, which do fit in the target type (int).

    With a + b + c this is no longer the case, because a + b becomes an int, and the expression becomes int + uint8_t - which seems to not satisfy the criteria that "the operands of the expression do fit in the target type".