Header <stdint.h> diperkenalkan di C99 untuk mengatasi masalah portabilitas tipe integer. Pada C klasik, int bisa 16-bit atau 32-bit, dan long bisa 32-bit atau 64-bit tergantung platform—membuat kode sulit dipindahkan antar sistem. <stdint.h> menyediakan tipe dengan lebar bit eksplisit (int8_t, int16_t, int32_t, int64_t) sehingga ukuran tidak ambigu. Header ini juga menyediakan kategori tipe lain: int_least*_t (terkecil dengan minimal N bit), int_fast*_t (tercepat dengan minimal N bit), intptr_t (cukup besar untuk menyimpan pointer), dan intmax_t (integer terbesar yang tersedia). Pada chapter ini kita akan meninjau setiap kategori beserta kapan memakainya.

Tipe Fixed-Width: int8_t, int16_t, int32_t, int64_t #

Tipe fixed-width menjamin ukuran persis N bit. int32_t selalu 32-bit (4 byte) di semua platform yang menyediakannya. Untuk tiap tipe signed, ada padanan unsigned (uint8_t, uint16_t, dst).

c
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>   /* untuk PRId32, dll - lihat chapter inttypes-h */

int main(void) {
    /* Tipe integer dengan lebar eksplisit */
    int8_t   i8  = -42;
    uint8_t  u8  = 255;
    int16_t  i16 = -1234;
    uint16_t u16 = 60000;
    int32_t  i32 = -1000000;
    uint32_t u32 = 4000000000U;
    int64_t  i64 = -9000000000LL;
    uint64_t u64 = 18000000000ULL;

    printf("int8_t    = %" PRId8  " (ukuran %zu byte)\n", i8,  sizeof(i8));
    printf("uint8_t   = %" PRIu8  " (ukuran %zu byte)\n", u8,  sizeof(u8));
    printf("int16_t   = %" PRId16 " (ukuran %zu byte)\n", i16, sizeof(i16));
    printf("uint16_t  = %" PRIu16 " (ukuran %zu byte)\n", u16, sizeof(u16));
    printf("int32_t   = %" PRId32 " (ukuran %zu byte)\n", i32, sizeof(i32));
    printf("uint32_t  = %" PRIu32 " (ukuran %zu byte)\n", u32, sizeof(u32));
    printf("int64_t   = %" PRId64 " (ukuran %zu byte)\n", i64, sizeof(i64));
    printf("uint64_t  = %" PRIu64 " (ukuran %zu byte)\n", u64, sizeof(u64));

    return 0;
}

Output yang Dihasilkan:

text
int8_t    = -42 (ukuran 1 byte)
uint8_t   = 255 (ukuran 1 byte)
int16_t   = -1234 (ukuran 2 byte)
uint16_t  = 60000 (ukuran 2 byte)
int32_t   = -1000000 (ukuran 4 byte)
uint32_t  = 4000000000 (ukuran 4 byte)
int64_t   = -9000000000 (ukuran 8 byte)
uint64_t  = 18000000000 (ukuran 8 byte)

Mari kita telaah baris-baris kode di atas:

  1. Baris 8 (int8_t i8 = -42;): tipe int8_t selalu 8-bit signed, rentang -128 sampai 127. Cocok untuk menyimpan data biner per byte.
  2. Baris 9 (uint8_t u8 = 255;): tipe uint8_t 8-bit unsigned, rentang 0 sampai 255. Lebih tepat untuk raw byte daripada char karena tidak ambigu antara signed dan unsigned.
  3. Baris 14-15 (int32_t, uint32_t): tipe paling sering dipakai untuk menyimpan angka biasa. int32_t cocok untuk ID, uint32_t untuk nilai yang tidak mungkin negatif seperti sequence number.
  4. Baris 16-17 (int64_t, uint64_t): untuk timestamp Unix, file offset, atau nilai finansial besar. Literal diakhiri LL/ULL agar kompilator tidak memotong ke 32-bit.
  5. Baris 19-26 (PRId8, PRIu8, dst.): makro format specifier dari <inttypes.h>. Untuk int8_t kita pakai PRId8, untuk uint64_t kita pakai PRIu64. Makro ini di-expand ke string literal yang benar untuk platform (lihat chapter inttypes-h).

Tipe Minimal dan Fastest: int_least dan int_fast #

Tipe int_least*_t memberi jaminan “minimal N bit, atau lebih bila perlu”. Tipe int_fast*_t memberi “minimal N bit, tetapi boleh lebih besar bila lebih cepat diakses”. Pada x86_64, int_fast16_t dan int_fast32_t sering berukuran 8 byte karena akses 64-bit sama cepatnya dengan 32-bit tetapi dapat memanfaatkan register penuh.

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

int main(void) {
    /* int_least*_t: tipe terkecil dengan minimal N bit */
    int_least8_t  le8  = 7;
    int_least16_t le16 = 1234;
    int_least32_t le32 = 123456;
    int_least64_t le64 = 123456789012LL;

    printf("int_least8_t  = %" PRIdLEAST8 "  (ukuran %zu byte)\n", le8,  sizeof(le8));
    printf("int_least16_t = %" PRIdLEAST16 " (ukuran %zu byte)\n", le16, sizeof(le16));
    printf("int_least32_t = %" PRIdLEAST32 " (ukuran %zu byte)\n", le32, sizeof(le32));
    printf("int_least64_t = %" PRIdLEAST64 " (ukuran %zu byte)\n", le64, sizeof(le64));

    /* int_fast*_t: tipe tercepat dengan minimal N bit */
    int_fast8_t  fe8  = 7;
    int_fast16_t fe16 = 1234;
    int_fast32_t fe32 = 123456;
    int_fast64_t fe64 = 123456789012LL;

    printf("\nint_fast8_t  = %" PRIdFAST8 "  (ukuran %zu byte)\n", fe8,  sizeof(fe8));
    printf("int_fast16_t = %" PRIdFAST16 " (ukuran %zu byte)\n", fe16, sizeof(fe16));
    printf("int_fast32_t = %" PRIdFAST32 " (ukuran %zu byte)\n", fe32, sizeof(fe32));
    printf("int_fast64_t = %" PRIdFAST64 " (ukuran %zu byte)\n", fe64, sizeof(fe64));

    /* intptr_t: tipe integer cukup besar untuk menyimpan pointer.
       Ukuran tergantung platform - di sini kita tampilkan sizeof saja */
    printf("\nsizeof(intptr_t)  = %zu byte\n", sizeof(intptr_t));
    printf("sizeof(uintptr_t) = %zu byte\n", sizeof(uintptr_t));
    printf("sizeof(void*)     = %zu byte (sama dengan intptr_t di platform ini)\n",
           sizeof(void*));

    /* intmax_t: tipe integer terbesar yang tersedia */
    intmax_t maks = 9223372036854775807LL;
    printf("\nintmax_t = %" PRIdMAX " (ukuran %zu byte)\n", maks, sizeof(maks));

    return 0;
}

Output yang Dihasilkan:

text
int_least8_t  = 7  (ukuran 1 byte)
int_least16_t = 1234 (ukuran 2 byte)
int_least32_t = 123456 (ukuran 4 byte)
int_least64_t = 123456789012 (ukuran 8 byte)

int_fast8_t  = 7  (ukuran 1 byte)
int_fast16_t = 1234 (ukuran 8 byte)
int_fast32_t = 123456 (ukuran 8 byte)
int_fast64_t = 123456789012 (ukuran 8 byte)

sizeof(intptr_t)  = 8 byte
sizeof(uintptr_t) = 8 byte
sizeof(void*)     = 8 byte (sama dengan intptr_t di platform ini)

intmax_t = 9223372036854775807 (ukuran 8 byte)

Pada platform x86_64 Linux: int_least16_t adalah int16_t (2 byte), tetapi int_fast16_t adalah int64_t (8 byte). Implementasi glibc memilih 64-bit untuk int_fast16_t/int_fast32_t karena pada arsitektur 64-bit modern, akses 64-bit sama cepatnya dengan 32-bit dan dapat menghindari sign-extension ketika nilai dipakai dalam ekspresi yang lebih lebar. Tipe intptr_t berguna saat kita perlu menyimpan pointer sebagai integer (misal untuk hashing)—di platform 64-bit, ukurannya 8 byte sesuai sizeof(void*). Tipe intmax_t adalah alias ke tipe integer terbesar yang tersedia (long long di banyak platform).

Studi Kasus: Paket Protokol Jaringan #

Salah satu kegunaan terpenting <stdint.h> adalah mendefinisikan binary structure untuk protokol jaringan atau format file. Kita ingin setiap field punya ukuran bit yang sama persis di semua platform sehingga data dapat ditransfer dan didecode dengan benar.

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

/* Struktur untuk menyimpan data biner dari protokol jaringan */
typedef struct {
    uint16_t magic;       /* 0xCAFE */
    uint8_t  version;     /* 1 */
    uint8_t  flags;       /* bitfield */
    uint32_t sequence;    /* nomor urut */
    int64_t  timestamp;   /* waktu */
} packet_t;

int main(void) {
    /* Susun paket dengan field terdefinisi lebar */
    packet_t pkt;
    pkt.magic    = 0xCAFE;
    pkt.version  = 1;
    pkt.flags    = 0x05;   /* bit 0 dan 2 aktif */
    pkt.sequence = 0x00001234;
    pkt.timestamp = 1704067200LL;

    printf("=== Packet dump ===\n");
    printf("magic     = 0x%04" PRIX16 "\n", pkt.magic);
    printf("version   = %" PRIu8 "\n", pkt.version);
    printf("flags     = 0x%02" PRIX8 " (biner: ", pkt.flags);
    for (int i = 7; i >= 0; i--) {
        printf("%d", (pkt.flags >> i) & 1);
    }
    printf(")\n");
    printf("sequence  = %" PRIu32 "\n", pkt.sequence);
    printf("timestamp = %" PRId64 "\n", pkt.timestamp);

    /* Konversi network byte order (big-endian) ke host */
    /* htons/htonl ada di <arpa/inet.h>, di sini kita tunjukkan manual */
    uint16_t network_order = 0x1234;
    uint16_t host_order = ((network_order & 0xFF) << 8) | ((network_order >> 8) & 0xFF);
    printf("\nNetwork order 0x%04" PRIX16 " -> host 0x%04" PRIX16 "\n",
           network_order, host_order);

    return 0;
}

Output yang Dihasilkan:

text
=== Packet dump ===
magic     = 0xCAFE
version   = 1
flags     = 0x05 (biner: 00000101)
sequence  = 4660
timestamp = 1704067200

Network order 0x1234 -> host 0x3412

Struktur packet_t mendefinisikan layout yang sama persis di semua platform—uint16_t magic selalu 2 byte, int64_t timestamp selalu 8 byte. Loop for (int i = 7; i >= 0; i--) menampilkan representasi biner dari flags: kita shift bit kanan i posisi lalu & 1 untuk mengisolasi bit terakhir. Output 00000101 sesuai 0x05—bit 0 dan bit 2 aktif. Konversi network order (big-endian) ke host order ditunjukkan manual: byte tinggi dan rendah ditukar. Pada Linux glibc, lebih bersih memakai htons/ntohs dari <arpa/inet.h>.