Implementation-Defined Behavior (IDB) adalah situasi di mana standar C mengizinkan compiler memilih perilaku, tetapi compiler wajib mendokumentasikan pilihannya. Berbeda dengan undefined behavior (yang tidak ada jaminan apa pun), IDB memberi konsistensi dalam satu compiler tetapi berpotensi berbeda antar compiler atau platform. Contoh klasik: sizeof(int) yang bisa 2, 4, atau 8 byte bergantung pada arsitektur. Pada chapter ini kita akan memeriksa IDB utama yang sering ditemui di kode C praktis dan cara menulis kode yang portabel tanpa mengandalkan asumsi spesifik.
IDB pada sizeof dan limits.h #
Ukuran tipe primitif tidak ditentukan oleh standar C—standar hanya mensyaratkan batas minimum. int harus mampu menyimpan setidaknya [-32767, 32767] (16 bit), tetapi ukuran aktualnya 32 bit pada sebagian besar platform modern. long di Linux 64-bit adalah 8 byte, sedangkan di Windows 64-bit tetap 4 byte (model LLP64). Header <limits.h> menyediakan konstanta yang mengikuti nilai aktual compiler.
#include <stdio.h>
#include <limits.h>
int main(void) {
/* sizeof(int) dan CHAR_BIT bervariasi per platform */
printf("sizeof(int) = %zu byte\n", sizeof(int));
printf("sizeof(long) = %zu byte\n", sizeof(long));
printf("sizeof(void *) = %zu byte\n", sizeof(void *));
printf("CHAR_BIT = %d\n", CHAR_BIT);
printf("INT_MAX = %d\n", INT_MAX);
printf("INT_MIN = %d\n", INT_MIN);
printf("LONG_MAX = %ld\n", LONG_MAX);
/* Sign of char: implementation-defined; di sini demo signed char */
int val = 200;
signed char sc = (signed char)val;
printf("\nsigned char sc = (signed char)200 -> nilai = %d\n", (int)sc);
/* Right shift pada signed int: implementation-defined */
int neg = -16;
int shifted = neg >> 1;
printf("(-16) >> 1 = %d\n", shifted);
return 0;
}Output yang Dihasilkan:
sizeof(int) = 4 byte
sizeof(long) = 8 byte
sizeof(void *) = 8 byte
CHAR_BIT = 8
INT_MAX = 2147483647
INT_MIN = -2147483648
LONG_MAX = 9223372036854775807
signed char sc = (signed char)200 -> nilai = -56
(-16) >> 1 = -8Mari kita telaah baris-baris kode di atas:
- Baris 6-12 (
sizeof(int),sizeof(long),sizeof(void *)): ukuran tipe bergantung pada platform. Pada Linux x86-64 dengan GCC,int= 4 byte,long= 8 byte (model LP64). Pada Windows 64-bit dengan MSVC,long= 4 byte (model LLP64). Kode yang mengasumsikansizeof(long) == 8akan gagal di Windows. - Baris 13 (
CHAR_BIT): jumlah bit per byte. Standar C mensyaratkan minimum 8, tetapi platform eksotis (DSP TMS320) memilikiCHAR_BIT = 16. Kode yang mengasumsikan8 bits per bytetidak portabel ke platform tersebut. - Baris 14-16 (
INT_MAX,INT_MIN,LONG_MAX): konstanta dari<limits.h>memberi batas aktual compiler. Pakai konstanta ini, bukan angka magic seperti2147483647. - Baris 19-20 (
signed char sc = (signed char)200;): konversi dariint200 kesigned charmenghasilkan nilai implementation-defined karena 200 melampauiSCHAR_MAX(127). Pada x86, hasilnya -56 (two’s complement wrapping). Kompiler lain dapat menghasilkan nilai berbeda—standar hanya mensyaratkan konsistensi dalam satu kompilasi. - Baris 23-24 (
int shifted = neg >> 1;di mananeg = -16): right shift padasigned intnegatif adalah implementation-defined. Pada x86, hasilnya arithmetic shift (mempertahankan sign), sehingga(-16) >> 1 = -8. Kompiler lain dapat memilih logical shift (mengisi dengan 0), menghasilkan nilai besar positif.
Endianness juga IDB #
Layout byte integer multi-byte di memori (endianness) adalah IDB. Pada little-endian (x86, ARM dalam mode default), byte paling tidak signifikan disimpan di alamat terendah. Pada big-endian (SPARC, PowerPC), byte paling signifikan disimpan di alamat terendah.
#include <stdio.h>
#include <limits.h>
/* Demonstrasikan limits.h untuk kode portabel */
int main(void) {
printf("Batas portabel dari <limits.h>:\n");
printf(" CHAR_BIT = %d\n", CHAR_BIT);
printf(" SCHAR_MIN = %d, SCHAR_MAX = %d\n", SCHAR_MIN, SCHAR_MAX);
printf(" UCHAR_MAX = %u\n", UCHAR_MAX);
printf(" INT_MIN = %d, INT_MAX = %d\n", INT_MIN, INT_MAX);
printf(" UINT_MAX = %u\n", UINT_MAX);
printf(" LONG_MIN = %ld, LONG_MAX = %ld\n", LONG_MIN, LONG_MAX);
printf(" ULONG_MAX = %lu\n", ULONG_MAX);
/* Endianness: implementation-defined */
unsigned int v = 0x01020304;
unsigned char *p = (unsigned char *)&v;
printf("\nLayout byte dari 0x%08X: %02X %02X %02X %02X\n",
v, p[0], p[1], p[2], p[3]);
if (p[0] == 0x04) {
printf("endianness: little-endian\n");
} else if (p[0] == 0x01) {
printf("endianness: big-endian\n");
} else {
printf("endianness: unknown\n");
}
return 0;
}Output yang Dihasilkan:
Batas portabel dari <limits.h>:
CHAR_BIT = 8
SCHAR_MIN = -128, SCHAR_MAX = 127
UCHAR_MAX = 255
INT_MIN = -2147483648, INT_MAX = 2147483647
UINT_MAX = 4294967295
LONG_MIN = -9223372036854775808, LONG_MAX = 9223372036854775807
ULONG_MAX = 18446744073709551615
Layout byte dari 0x01020304: 04 03 02 01
endianness: little-endianCasting &v ke unsigned char * memungkinkan kita melihat byte per byte. Pada little-endian, byte pertama (p[0]) berisi 0x04 (paling tidak signifikan). Kode jaringan yang mengirim integer ke wire harus mengkonversi ke network byte order (big-endian) lewat htonl/htons bila tidak ingin terikat platform.
Daftar IDB Umum #
sizeof(int),sizeof(long),sizeof(void *): ukuran tipe.CHAR_BIT: bit per byte (minimum 8).- Sign of
char: bisasignedatauunsigneddefault (GCC menggunakansigned). - Right shift pada
signed intnegatif: arithmetic atau logical. - Konversi floating-point ke integer yang overflow: nilai tidak terdefinisi oleh implementation.
- Layout struct (padding, alignment): implementation-defined.
- Behavior of
realloc(NULL, size)danrealloc(ptr, 0): standar menetapkan ini, tetapi versi POSIX bisa berbeda. - Number of bits in
unsigned long(jika beda dariint).