C/C++ Arena

Step 4 of 8

Fixed-width types

Because int and long vary between platforms, any data that leaves the program (files, network messages, hashes, checksums) must use types with an exact, guaranteed size. <stdint.h> provides them:

Type Size Typical use
int8_t / uint8_t 1 byte raw bytes, pixels
int16_t / uint16_t 2 bytes audio samples, small protocol fields
int32_t / uint32_t 4 bytes file formats, protocols
int64_t / uint64_t 8 bytes big counters, hashes, timestamps

<inttypes.h> provides matching printf macros, because the right specifier differs by platform: printf("%" PRIu64 "\n", x); (the macro expands to a string like "lu" or "llu", and adjacent string literals are joined).

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

uint32_t checksum(const char *s) {
    uint32_t sum = 0;
    for (; *s; s++) {
        sum = sum * 31 + (uint8_t)*s;
    }
    return sum;
}

int main(void) {
    uint64_t big = UINT64_C(1) << 40;
    printf("%" PRIu64 "\n", big);
    printf("%" PRIu32 "\n", checksum("hello"));
    printf("%zu %zu\n", sizeof(int8_t), sizeof(uint64_t));
    return 0;
}
1099511627776
99162322
1 8

checksum relies on unsigned wraparound: the multiplications overflow constantly, and that's fine (and deterministic) with uint32_t. The cast (uint8_t)*s treats each character as a byte from 0 to 255, so results are the same on platforms where char is signed.

UINT64_C(1) makes a literal of the right 64-bit type, so shifting it by 40 doesn't overflow a 32-bit int.

Your turn: implement the 64-bit FNV-1a hash, a simple, widely used string hash:

hash = 14695981039346656037
for each byte b of the string:
    hash = hash XOR b
    hash = hash * 1099511628211      (wrapping, which uint64_t does for free)

Previous: Signed overflow is undefined Next: The signed/unsigned comparison trap