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