Search code examples
cc89size-t

Adding or assigning an integer literal to a size_t


In C I see a lot of code that adds or assigns an integer literal to a size_t variable.

size_t foo = 1;
foo += 1;

What conversion takes place here, and can it ever happen that a size_t is "upgraded" to an int and then converted back to a size_t? Would that still wraparound if I was at the max?

size_t foo = SIZE_MAX;
foo += 1;

Is that defined behavior? It's an unsigned type size_t which is having a signed int added to it (that may be a larger type?) and the converted back to a size_t. Is there risk of signed integer overflow?

Would it make sense to write something like foo + bar + (size_t)1 instead of foo + bar + 1? I never see code like that, but I'm wondering if it's necessary if integer promotions are troublesome.

C89 doesn't say how a size_t will be ranked or what exactly it is:

The value of the result is implementation-defined, and its type (an unsigned integral type) is size_t defined in the header.


Solution

  • The current C standard allows for a possibility of an implementation that would cause undefined behavior when executing the following code, however such implementation does not exist, and probably never will:

    size_t foo = SIZE_MAX;
    foo += 1;
    

    The type size_t is as unsigned type1, with a minimum range:2 [0,65535].

    The type size_t may be defined as a synonym for the type unsigned short. The type unsigned short may be defined having 16 precision bits, with the range: [0,65535]. In that case the value of SIZE_MAX is 65535.

    The type int may be defined having 16 precision bits (plus one sign bit), two's complement representation, and range: [-65536,65535].

    The expression foo += 1, is equivalent to foo = foo + 1 (except that foo is evaluated only once but that is irrelevant here). The variable foo will get promoted using integer promotions3. It will get promoted to type int because type int can represent all values of type size_t and rank of size_t, being a synonym for unsigned short, is lower than the rank of int. Since the maximum values of size_t, and int are the same, the computation causes a signed overflow, causing undefined behavior.

    This holds for the current standard, and it should also hold for C89 since it doesn't have any stricter restrictions on types.

    Solution for avoiding signed overflow for any imaginable implementation is to use an unsigned int integer constant:

    foo += 1u;
    

    In that case if foo has a lower rank than int, it will be promoted to unsigned int using usual arithmetic conversions.


    1 (Quoted from ISO/IEC 9899/201x 7.19 Common definitions 2)
    size_t
    which is the unsigned integer type of the result of the sizeof operator;

    2 (Quoted from ISO/IEC 9899/201x 7.20.3 Limits of other integer types 2)
    limit of size_t
    SIZE_MAX 65535

    3 (Quoted from ISO/IEC 9899/201x 6.3.1.1 Boolean, characters, and integers 2)
    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; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.