C11 (ISO/IEC 9899:2011) adalah standar yang menjadikan C bahasa modern untuk pemrograman concurrency dan type-generic. C11 menambahkan memory model resmi (atomic operations yang terdefinisi), header stdatomic.h (atomics), header threads.h (thread portabel), keyword _Generic untuk dispatch berdasarkan tipe, _Static_assert untuk asersi compile-time, _Alignas/_Alignof untuk kontrol alignment, anonymous struct/union dalam struct, serta API bounds-checked seperti gets_s. Bab ini menunjukkan setiap fitur utama dengan kode yang di-compile menggunakan gcc -std=c11 -pedantic (dan -pthread untuk thread). Kita juga akan melihat bagaimana _Generic menggantikan macro-macro rumit untuk implementasi type-generic di C99.
_Generic dan _Static_assert #
_Generic adalah operator compile-time yang memilih ekspresi berdasarkan tipe argumen. Fitur ini menggantikan trik macro lama seperti typeof dan __builtin_types_compatible_p yang spesifik GCC. _Static_assert memungkinkan kita menegakkan invariant saat kompilasi—jika gagal, build berhenti dengan pesan yang jelas.
// File: c11_1.c - _Generic dan _Static_assert
#include <stdio.h>
#include <math.h>
#define nama_tipe(X) _Generic((X), \
char: "char", \
signed char:"signed char",\
unsigned char:"unsigned char", \
short: "short", \
unsigned short:"unsigned short", \
int: "int", \
unsigned: "unsigned int",\
long: "long", \
unsigned long:"unsigned long", \
long long: "long long", \
float: "float", \
double: "double", \
long double:"long double",\
default: "tidak dikenal")
// _Static_assert: cek saat compile-time
_Static_assert(sizeof(int) >= 4, "int harus minimal 32-bit di platform ini");
int main(void) {
int a = 10;
double b = 3.14;
long c = 100000L;
char d = 'X';
printf("a : %s\n", nama_tipe(a));
printf("b : %s\n", nama_tipe(b));
printf("c : %s\n", nama_tipe(c));
printf("d : %s\n", nama_tipe(d));
return 0;
}Compile dan jalankan:
gcc -std=c11 -Wall -Wextra -pedantic -o c11_1 c11_1.c
./c11_1Output yang Dihasilkan:
a : int
b : double
c : long
d : charMari kita telaah baris-baris kode di atas:
- Baris 6-19 (
#define nama_tipe(X) _Generic(...)): Macronama_tipe(X)memakai_Genericuntuk memilih string literal yang sesuai dengan tipe argumenX. Saat macro dipanggil denganayang bertipeint,_Genericmemilih clauseint:dan mengembalikan"int". Pemilihan ini dilakukan saat kompilasi tanpa overhead runtime. - Baris 22 (
_Static_assert(sizeof(int) >= 4, "...");): Asersi compile-time. Jikasizeof(int)kurang dari 4 byte (mis. di platform 16-bit), build gagal dengan pesan di parameter kedua. Tanpa_Static_assert, kita harus menulis runtime check yang menulis kestderrlaluexit(1)—jauh lebih lambat dan tidak menggagalkan build. - Baris 24-27 (
int a; double b; long c; char d;): Variabel dengan berbagai tipe untuk mendemonstrasikan_Generic. Setiap panggilannama_tipe(...)mengembalikan string yang benar untuk tipe tersebut. - Baris 29-32 (
printf("a : %s\n", nama_tipe(a));): Karena_Genericadalah ekspresi, ia bisa dipakai langsung sebagai argumenprintf. Compiler menyelesaikan dispatch tipe saat kompilasi, sehingga hasilnya sama cepatnya dengan menulis string literal secara langsung.
_Alignas, _Alignof, dan Anonymous Union #
C11 menambahkan kontrol alignment eksplisit lewat _Alignas (minta alignment tertentu) dan _Alignof (query alignment default tipe). Selain itu, struct sekarang boleh berisi anonymous struct/union tanpa nama, sehingga anggotanya dapat diakses langsung dari parent struct—berguna untuk tagged union dan polymorphic data.
// File: c11_2.c - _Alignas/_Alignof, anonymous struct/union
#include <stdio.h>
#include <stdalign.h>
#include <stdint.h>
typedef struct {
int id;
union { // anonymous union
int angka;
float pecahan;
};
char label[16];
} Nilai;
int main(void) {
// _Alignof: query alignment yang dipakai compiler
printf("Alignment int : %zu\n", _Alignof(int));
printf("Alignment double: %zu\n", _Alignof(double));
printf("Alignment Nilai : %zu\n", _Alignof(Nilai));
// _Alignas: minta alignment khusus
_Alignas(16) char buffer[64];
printf("Alamat buffer : %p\n", (void*)buffer);
printf("Alamat kelipatan 16? %s\n",
((uintptr_t)buffer % 16 == 0) ? "ya" : "tidak");
// Anonymous union: akses anggota tanpa nama union
Nilai n = { .id = 1, .label = "utama" };
n.angka = 42;
printf("id=%d, angka=%d, label=%s\n", n.id, n.angka, n.label);
Nilai m = { .id = 2, .label = "kedua" };
m.pecahan = 3.14f;
printf("id=%d, pecahan=%.2f, label=%s\n", m.id, m.pecahan, m.label);
return 0;
}Compile dan jalankan:
gcc -std=c11 -Wall -Wextra -pedantic -o c11_2 c11_2.c
./c11_2Output yang Dihasilkan:
Alignment int : 4
Alignment double: 8
Alignment Nilai : 4
Alamat buffer : 0x7ffe3488c830
Alamat kelipatan 16? ya
id=1, angka=42, label=utama
id=2, pecahan=3.14, label=keduaPada contoh di atas, _Alignof(int) mengembalikan 4 karena x86-64 GCC meng-align int ke 4-byte boundary. _Alignof(double) mengembalikan 8. Deklarasi _Alignas(16) char buffer[64] memaksa buffer ditempatkan di alamat kelipatan 16—berguna untuk SIMD intrinsik dan DMA hardware yang mensyaratkan alignment. Anonymous union union { int angka; float pecahan; }; di dalam struct Nilai memungkinkan kita menulis n.angka dan m.pecahan langsung tanpa menyebut nama union—pola yang umum untuk tagged value atau variant.
stdatomic.h dan threads.h: Concurrency Resmi #
C11 adalah standar C pertama yang mendefinisikan memory model untuk multithreading. Header stdatomic.h menyediakan tipe atomic (_Atomic int, _Atomic void*) dan operasi atomic (atomic_fetch_add, atomic_load, atomic_store, atomic_compare_exchange). Header threads.h menyediakan API thread portabel (thrd_create, thrd_join, mtx_lock, cnd_wait) yang menyerupai C++ <thread>. Program berikut menjalankan dua thread yang meng-increment counter atomic yang sama:
// File: c11_3.c - stdatomic.h: atomic counter
#include <stdio.h>
#include <stdatomic.h>
#include <threads.h>
static _Atomic int counter = 0;
static int worker(void *arg) {
int id = *(int*)arg;
for (int i = 0; i < 5; ++i) {
int cur = atomic_fetch_add(&counter, 1);
printf("[worker %d] counter: %d -> %d\n", id, cur, cur + 1);
}
return 0;
}
int main(void) {
thrd_t t1, t2;
int id1 = 1, id2 = 2;
thrd_create(&t1, worker, &id1);
thrd_create(&t2, worker, &id2);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
printf("Counter akhir: %d\n", counter);
return 0;
}Compile dengan link ke pthread (C11 threads di Linux diimplementasikan di atas pthread):
gcc -std=c11 -Wall -Wextra -pedantic -pthread -o c11_3 c11_3.c
./c11_3Output yang Dihasilkan:
[worker 2] counter: 0 -> 1
[worker 2] counter: 1 -> 2
[worker 2] counter: 2 -> 3
[worker 2] counter: 3 -> 4
[worker 2] counter: 4 -> 5
[worker 1] counter: 5 -> 6
[worker 1] counter: 6 -> 7
[worker 1] counter: 7 -> 8
[worker 1] counter: 8 -> 9
[worker 1] counter: 9 -> 10
Counter akhir: 10Fungsi atomic_fetch_add(&counter, 1) secara atomic mengembalikan nilai lama counter dan menambahnya dengan 1. Tanpa atomic, dua thread yang bersamaan membaca dan menulis counter akan menghasilkan race condition, dan counter akhir bisa kurang dari 10. Dengan _Atomic int, setiap increment terjadi secara terurut, dan counter akhir selalu 10. Header threads.h menyediakan fungsi thrd_create dan thrd_join yang setara dengan pthread_create/pthread_join di POSIX, tetapi portabel ke Windows dan platform lain yang mendukung C11.