Header <stddef.h> adalah salah satu header paling fundamental di C. Walaupun isinya sedikit, hampir setiap program C mengandalkannya. Header ini mendefinisikan tipe size_t (untuk merepresentasikan ukuran objek), ptrdiff_t (hasil pengurangan dua pointer), konstanta NULL (pointer null), dan makro offsetof (mengambil offset field dalam struct). Tipe-tipe ini compiler-defined, artinya implementasinya bergantung pada platform—di sistem 64-bit, size_t adalah 8 byte. Pada chapter ini kita akan meninjau keempat simbol tersebut beserta penggunaannya.
size_t: Tipe untuk Ukuran Objek #
Tipe size_t didefinisikan sebagai unsigned integer type yang cukup besar untuk menyimpan ukuran objek maksimum yang dapat dialokasikan. Fungsi-fungsi standar seperti strlen, sizeof, malloc semua memakai size_t sebagai tipe ukuran. Untuk mencetak size_t dengan printf, gunakan format specifier %zu.
#include <stdio.h>
#include <stddef.h>
#include <string.h>
int main(void) {
/* size_t: tipe untuk merepresentasikan ukuran objek */
size_t panjang = strlen("Belajar");
size_t ukuran_array = sizeof(int[10]);
printf("strlen(\"Belajar\") = %zu\n", panjang);
printf("sizeof(int[10]) = %zu byte\n", ukuran_array);
printf("sizeof(size_t) = %zu byte\n", sizeof(size_t));
/* ptrdiff_t: tipe untuk hasil pengurangan dua pointer */
int arr[] = {10, 20, 30, 40, 50};
int *p1 = &arr[1];
int *p2 = &arr[4];
ptrdiff_t selisih = p2 - p1;
printf("\np2 - p1 = %td elemen\n", selisih);
/* NULL: konstanta pointer null */
int *q = NULL;
printf("\nq = %p (NULL)\n", (void *)q);
if (q == NULL) {
printf("q belum dialokasikan\n");
}
return 0;
}Output yang Dihasilkan:
strlen("Belajar") = 7
sizeof(int[10]) = 40 byte
sizeof(size_t) = 8 byte
p2 - p1 = 3 elemen
q = (nil) (NULL)
q belum dialokasikanMari kita telaah baris-baris kode di atas:
- Baris 7 (
size_t panjang = strlen("Belajar");): fungsistrlendari<string.h>mengembalikansize_t. Menyimpan hasilnya keintberisiko truncation bila string lebih panjang dariINT_MAX. - Baris 9 (
printf("... = %zu\n", panjang)): format specifier%zuadalah cara portabel mencetaksize_t. Memakai%ddapat memicu undefined behavior karenasize_tbiasanya lebih besar dariintdi platform 64-bit. - Baris 13 (
ptrdiff_t selisih = p2 - p1;): pengurangan dua pointer menghasilkanptrdiff_t(signed). Pada contoh,&arr[4] - &arr[1]menghasilkan3karena selisih indeks adalah4 - 1. Format specifier untukptrdiff_tadalah%td. - Baris 18 (
int *q = NULL;):NULLadalah makro yang di-expand ke konstanta pointer null. Implementasinya bervariasi:((void*)0)di banyak sistem, atau0literal. - Baris 19 (
printf("q = %p\n", (void *)q);): specifier%pmencetak alamat pointer. Hasilnya(nil)menunjukkan pointer null. Cast kevoid *diperlukan karena%pmengharapkanvoid *, bukanint *.
NULL: Pointer Null #
NULL adalah konstanta yang merepresentasikan pointer null—pointer yang tidak menunjuk ke lokasi memori manapun. Konvensi di C: fungsi yang mengembalikan pointer memakai NULL sebagai penanda kegagalan (fopen, malloc, getenv). Pemanggil wajib memeriksa NULL sebelum memakai pointer.
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
int main(void) {
/* NULL sebagai penanda awal pointer yang belum dialokasikan */
int *data = NULL;
printf("Inisial: data = %p\n", (void *)data);
/* Alokasi 100 elemen */
data = malloc(100 * sizeof(int));
if (data == NULL) {
fprintf(stderr, "malloc gagal\n");
return 1;
}
/* Pakai data */
for (int i = 0; i < 100; i++) data[i] = i * 2;
printf("Setelah alokasi: data[99] = %d\n", data[99]);
/* Bebaskan, lalu set ke NULL untuk mencegah double-free */
free(data);
data = NULL;
/* Pemeriksaan NULL melindungi dari dereferensi setelah free */
if (data != NULL) {
printf("Mengakses data[0] = %d\n", data[0]);
} else {
printf("data sudah NULL, aman dari dereferensi\n");
}
return 0;
}Output yang Dihasilkan:
Inisial: data = (nil)
Setelah alokasi: data[99] = 198
data sudah NULL, aman dari dereferensiPola inisialisasi int *data = NULL; adalah praktik penting: bila kita lupa mengalokasikan memori, dereferensi *data akan langsung segfault di sistem modern, bukan mengakses memori acak yang mungkin berhasil. Setelah free(data), segera set data = NULL untuk mencegah double-free dan use-after-free—pemanggilan free(NULL) well-defined dan tidak melakukan apa-apa, sedangkan free dua kali pada pointer yang sama adalah undefined behavior.
offsetof: Mengambil Offset Field Struct #
Makro offsetof(type, member) mengembalikan offset byte dari awal struct ke field member. Hasilnya bertipe size_t. Makro ini sangat berguna untuk memahami layout struct, mendiagnosis padding, atau mengimplementasikan struktur data generik seperti linked list intrusif.
#include <stdio.h>
#include <stddef.h>
/* Struct dengan beberapa field berbeda tipe */
typedef struct {
char id; /* 1 byte, padding 3 byte */
int nilai; /* 4 byte */
double rata_rata; /* 8 byte */
char nama[16]; /* 16 byte */
} siswa;
int main(void) {
printf("=== Layout struct siswa ===\n");
printf("sizeof(siswa) = %zu byte\n", sizeof(siswa));
printf("offsetof(siswa, id) = %zu\n", offsetof(siswa, id));
printf("offsetof(siswa, nilai) = %zu\n", offsetof(siswa, nilai));
printf("offsetof(siswa, rata_rata) = %zu\n", offsetof(siswa, rata_rata));
printf("offsetof(siswa, nama) = %zu\n", offsetof(siswa, nama));
/* Demonstrasikan alignment dengan padding */
printf("\nsizeof(char) = %zu\n", sizeof(char));
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(double) = %zu\n", sizeof(double));
printf("sizeof(char[16])= %zu\n", sizeof(char[16]));
/* Total tanpa padding: 1+4+8+16 = 29 byte. */
/* Actual size: 32 byte karena padding setelah id. */
return 0;
}Output yang Dihasilkan:
=== Layout struct siswa ===
sizeof(siswa) = 32 byte
offsetof(siswa, id) = 0
offsetof(siswa, nilai) = 4
offsetof(siswa, rata_rata) = 8
offsetof(siswa, nama) = 16
sizeof(char) = 1
sizeof(int) = 4
sizeof(double) = 8
sizeof(char[16])= 16Mari kita analisis layout: id di offset 0, lalu tiga byte padding agar nilai (yang butuh alignment 4-byte) mulai di offset 4. Field rata_rata (alignment 8-byte) langsung di offset 8 karena 4 + 4 = 8 sudah rapi. Field nama di offset 16—sebelumnya 8 + 8 = 16, tidak perlu padding. Total tanpa padding adalah 1 + 4 + 8 + 16 = 29 byte, tetapi sizeof(siswa) = 32 byte karena trailing padding agar alignment struct tetap konsisten bila ada array siswa[]. Memahami layout struct penting saat kita berkomunikasi dengan kode yang ditulis dalam bahasa lain (misalnya FFI Python, atau file format biner).