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).
#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:
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:
- Baris 8 (
int8_t i8 = -42;): tipeint8_tselalu 8-bit signed, rentang-128sampai127. Cocok untuk menyimpan data biner per byte. - Baris 9 (
uint8_t u8 = 255;): tipeuint8_t8-bit unsigned, rentang0sampai255. Lebih tepat untuk raw byte daripadacharkarena tidak ambigu antara signed dan unsigned. - Baris 14-15 (
int32_t,uint32_t): tipe paling sering dipakai untuk menyimpan angka biasa.int32_tcocok untuk ID,uint32_tuntuk nilai yang tidak mungkin negatif seperti sequence number. - Baris 16-17 (
int64_t,uint64_t): untuk timestamp Unix, file offset, atau nilai finansial besar. Literal diakhiriLL/ULLagar kompilator tidak memotong ke 32-bit. - Baris 19-26 (
PRId8,PRIu8, dst.): makro format specifier dari<inttypes.h>. Untukint8_tkita pakaiPRId8, untukuint64_tkita pakaiPRIu64. Makro ini di-expand ke string literal yang benar untuk platform (lihat chapterinttypes-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.
#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:
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.
#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:
=== Packet dump ===
magic = 0xCAFE
version = 1
flags = 0x05 (biner: 00000101)
sequence = 4660
timestamp = 1704067200
Network order 0x1234 -> host 0x3412Struktur 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>.