C/C++ Arena

Step 5 of 8

The signed/unsigned comparison trap

When an expression mixes a signed and an unsigned integer of the same (or larger) size, C converts the signed one to unsigned before operating. For comparisons this produces results that look impossible:

#include <stdio.h>
#include <string.h>

int main(void) {
    int n = -1;
    size_t len = strlen("abc");
    printf("%d\n", len > (size_t)n);
    printf("%d\n", n < 0 || len > (size_t)n);
    unsigned int u = 1;
    printf("%d\n", -1 < (int)u);
    return 0;
}
0
1
1

The first line asks "is 3 greater than -1?" and gets false, because -1 converted to size_t is the largest possible value. (The example writes the conversion out as (size_t)n so it compiles without warnings, but plain len > n does exactly the same conversion behind your back.) The second line handles the negative case first, before any conversion happens, and gets the right answer. The third converts the unsigned side to int instead, which is safe here because 1 fits.

Small types are promoted first

Before doing arithmetic, C converts char and short (signed or unsigned) to int. These integer promotions mean small types don't overflow in the middle of an expression:

#include <stdio.h>

int main(void) {
    unsigned char a = 200, b = 100;
    int sum = a + b;                  /* both promoted to int: 300 */
    unsigned char wrapped = a + b;    /* 300 doesn't fit in 1 byte: stored as 300 - 256 = 44 */
    printf("%d %d\n", sum, wrapped);
    return 0;
}
300 44

The addition happens in int. Only storing the result in the 1-byte wrapped loses information, and since that's a conversion to an unsigned type it wraps around, which is well defined. Promotion is also why printf("%d", c) works for a char: it arrives as an int.

Where this bites

strlen, sizeof and container sizes are all unsigned (size_t). Comparing them with an int that might be negative, or subtracting two sizes (a_len - b_len is huge, not negative, when b is longer), are classic bugs.

Fixes

Your task

longer_than(s, n) should return 1 when s has more than n characters. It's wrong for negative n (every string is longer than -1 characters). Fix it without casting the length to int (strings can be longer than INT_MAX).

Challenge 2

last_index_of should return the index of the last c in s, or -1. Its loop counts down with a size_t, but i >= 0 is always true for an unsigned type, and for an empty string strlen(s) - 1 wraps around to a huge number. Fix the loop so it works for every string.

Challenge 3

has_room(used, cap, want) should say whether want more items fit: used + want <= cap. A negative want means items are being removed, which always fits. The current code is wrong for negative want, because the int is converted to a huge size_t before adding. Fix it without casting the sizes to int.

Previous: Fixed-width types Next: Floating-point rounding