Untuk menulis kode yang aman terhadap integer overflow, kita perlu tahu batas nilai setiap tipe. Hard-coding batas seperti 2147483647 sangat rentan kesalahan dan tidak portabel. Standar C menyediakan header <limits.h> untuk batas tipe integer dan <float.h> untuk batas tipe floating point. Kedua header ini berisi makro praproses yang nilainya disesuaikan compiler dengan ukuran tipe aktual di platform. Pada Chapter ini kita akan memetakan konstanta-konstanta tersebut dan melihat bagaimana menggunakannya untuk pemeriksaan rentang yang aman.
Konstanta limits.h untuk Tipe Integer #
Header <limits.h> mendefinisikan makro yang menyatakan batas minimum dan maksimum setiap tipe integer. Daftar lengkap konstanta yang paling sering dipakai:
| Makro | Tipe | Nilai (x86_64) |
|---|---|---|
CHAR_BIT | bit per char | 8 |
SCHAR_MIN/SCHAR_MAX | signed char | -128 / 127 |
UCHAR_MAX | unsigned char | 255 |
SHRT_MIN/SHRT_MAX | short | -32.768 / 32.767 |
USHRT_MAX | unsigned short | 65.535 |
INT_MIN/INT_MAX | int | -2.147.483.648 / 2.147.483.647 |
UINT_MAX | unsigned int | 4.294.967.295 |
LONG_MIN/LONG_MAX | long | -9.223.372.036.854.775.808 / 9.223.372.036.854.775.807 |
ULONG_MAX | unsigned long | 18.446.744.073.709.551.615 |
LLONG_MIN/LLONG_MAX | long long | -9.223.372.036.854.775.808 / 9.223.372.036.854.775.807 |
ULLONG_MAX | unsigned long long | 18.446.744.073.709.551.615 |
#include <stdio.h>
#include <limits.h>
int main(void) {
printf("Konstanta limits.h untuk tipe integer:\n");
printf("CHAR_BIT = %d (bit per char)\n", CHAR_BIT);
printf("SCHAR_MIN = %d, SCHAR_MAX = %d\n", SCHAR_MIN, SCHAR_MAX);
printf("UCHAR_MAX = %u\n", UCHAR_MAX);
printf("SHRT_MIN = %d, SHRT_MAX = %d\n", SHRT_MIN, SHRT_MAX);
printf("USHRT_MAX = %u\n", USHRT_MAX);
printf("INT_MIN = %d\n", INT_MIN);
printf("INT_MAX = %d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("LONG_MIN = %ld\n", LONG_MIN);
printf("LONG_MAX = %ld\n", LONG_MAX);
printf("ULONG_MAX = %lu\n", ULONG_MAX);
printf("LLONG_MIN = %lld\n", LLONG_MIN);
printf("LLONG_MAX = %lld\n", LLONG_MAX);
printf("ULLONG_MAX = %llu\n", ULLONG_MAX);
return 0;
}Output yang Dihasilkan:
Konstanta limits.h untuk tipe integer:
CHAR_BIT = 8 (bit per char)
SCHAR_MIN = -128, SCHAR_MAX = 127
UCHAR_MAX = 255
SHRT_MIN = -32768, SHRT_MAX = 32767
USHRT_MAX = 65535
INT_MIN = -2147483648
INT_MAX = 2147483647
UINT_MAX = 4294967295
LONG_MIN = -9223372036854775808
LONG_MAX = 9223372036854775807
ULONG_MAX = 18446744073709551615
LLONG_MIN = -9223372036854775808
LLONG_MAX = 9223372036854775807
ULLONG_MAX = 18446744073709551615Mari kita telaah baris-baris kode di atas:
- Baris 2 (
#include <limits.h>): menyertakan header yang mendefinisikan seluruh makro batas tipe integer. - Baris 6 (
CHAR_BIT): jumlah bit perchar; dijamin minimal 8 oleh standar, hampir selalu persis 8 di platform modern. - Baris 7-8 (
SCHAR_*danUCHAR_MAX): batas tipesigned chardanunsigned char. Perhatikan tidak adaCHAR_MIN/CHAR_MAXgenerik karenacharbisa bertanda atau tidak tergantung compiler. - Baris 11-12 (
INT_MIN,INT_MAX): batas tipeint4 byte—rentang -2^31 hingga 2^31-1. - Baris 15 (
LLONG_MAX): batas ataslong long, yaitu 2^63-1; nilai ini sama denganLONG_MAXdi platform LP64 (Linux/macOS x86_64).
Konstanta float.h untuk Tipe Floating Point #
Header <float.h> menyediakan konstanta untuk tipe floating point. Yang paling sering dipakai:
| Makro | Tipe | Makna |
|---|---|---|
FLT_MIN / DBL_MIN / LDBL_MIN | float/double/long double | Nilai positif terkecil (bukan nol) |
FLT_MAX / DBL_MAX / LDBL_MAX | Nilai positif terbesar | |
FLT_EPSILON / DBL_EPSILON / LDBL_EPSILON | Selisih antara 1.0 dan nilai terkecil berikutnya | |
FLT_DIG / DBL_DIG / LDBL_DIG | Jumlah digit desimal yang dijamin akurat |
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main(void) {
printf("Konstanta float.h:\n");
printf("FLT_MIN = %e (min positive)\n", FLT_MIN);
printf("FLT_MAX = %e\n", FLT_MAX);
printf("FLT_EPSILON = %e\n", FLT_EPSILON);
printf("FLT_DIG = %d (digit desimal akurat)\n", FLT_DIG);
printf("DBL_MIN = %e\n", DBL_MIN);
printf("DBL_MAX = %e\n", DBL_MAX);
printf("DBL_EPSILON = %e\n", DBL_EPSILON);
printf("DBL_DIG = %d\n", DBL_DIG);
return 0;
}Output yang Dihasilkan:
Konstanta float.h:
FLT_MIN = 1.175494e-38 (min positive)
FLT_MIN = 3.402823e+38
FLT_EPSILON = 1.192093e-07
FLT_DIG = 6 (digit desimal akurat)
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308
DBL_EPSILON = 2.220446e-16
DBL_DIG = 15Pada baris 7, FLT_MIN adalah nilai positif terkecil yang bisa direpresentasikan float (sekitar 1.18 × 10^-38), bukan nol atau bilangan negatif. Pada baris 8, FLT_MAX adalah nilai positif terbesar (sekitar 3.4 × 10^38). Pada baris 9, FLT_EPSILON menunjukkan presisi mesin: selisih antara 1.0f dan float terkecil yang lebih besar dari 1.0f. Pada baris 10, FLT_DIG = 6 berarti float hanya dijamin akurat untuk 6 digit desimal; sisanya bisa berubah karena pembulatan.
Memakai limits.h untuk Pemeriksaan Rentang #
Manfaat utama <limits.h> adalah memungkinkan pemeriksaan rentang secara aman sebelum melakukan cast atau operasi yang berisiko overflow. Sebagai contoh, sebelum mengubah long ke int, kita perlu memastikan nilainya berada dalam rentang INT_MIN hingga INT_MAX.
#include <stdio.h>
#include <limits.h>
int main(void) {
/* Memeriksa rentang input sebelum diproses */
long nilai = 5000000L;
if (nilai >= INT_MIN && nilai <= INT_MAX) {
int aman = (int)nilai;
printf("nilai %ld muat di int: %d\n", nilai, aman);
} else {
printf("nilai %ld tidak muat di int\n", nilai);
}
/* Kasus overflow potensial */
long besar = (long)INT_MAX + 100L;
if (besar >= INT_MIN && besar <= INT_MAX) {
printf("besar muat di int\n");
} else {
printf("besar = %ld tidak muat di int (INT_MAX = %d)\n",
besar, INT_MAX);
}
return 0;
}Output yang Dihasilkan:
nilai 5000000 muat di int: 5000000
besar = 2147483747 tidak muat di int (INT_MAX = 2147483647)Pada baris 9, nilai 5.000.000 berada dalam rentang INT_MIN/INT_MAX sehingga cast ke int aman dilakukan. Pada baris 17, besar dihitung sebagai INT_MAX + 100, yaitu 2.147.483.747, yang melebihi INT_MAX. Karena pemeriksaan rentang dilakukan lebih dulu, cabang else yang dieksekusi dan tidak ada cast berbahaya yang terjadi. Pola ini harus menjadi kebiasaan setiap kali kita melakukan konversi antar tipe yang ukurannya berbeda.
Dengan kedua header <limits.h> dan <float.h>, kita punya referensi resmi untuk seluruh batas tipe. Selanjutnya, kita akan mempelajari cara mendefinisikan konstanta sendiri lewat #define dan keyword const.