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.

c
// 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:

bash
gcc -std=c11 -Wall -Wextra -pedantic -o c11_1 c11_1.c
./c11_1

Output yang Dihasilkan:

text
a : int
b : double
c : long
d : char

Mari kita telaah baris-baris kode di atas:

  1. Baris 6-19 (#define nama_tipe(X) _Generic(...)): Macro nama_tipe(X) memakai _Generic untuk memilih string literal yang sesuai dengan tipe argumen X. Saat macro dipanggil dengan a yang bertipe int, _Generic memilih clause int: dan mengembalikan "int". Pemilihan ini dilakukan saat kompilasi tanpa overhead runtime.
  2. Baris 22 (_Static_assert(sizeof(int) >= 4, "...");): Asersi compile-time. Jika sizeof(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 ke stderr lalu exit(1)—jauh lebih lambat dan tidak menggagalkan build.
  3. Baris 24-27 (int a; double b; long c; char d;): Variabel dengan berbagai tipe untuk mendemonstrasikan _Generic. Setiap panggilan nama_tipe(...) mengembalikan string yang benar untuk tipe tersebut.
  4. Baris 29-32 (printf("a : %s\n", nama_tipe(a));): Karena _Generic adalah ekspresi, ia bisa dipakai langsung sebagai argumen printf. 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.

c
// 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:

bash
gcc -std=c11 -Wall -Wextra -pedantic -o c11_2 c11_2.c
./c11_2

Output yang Dihasilkan:

text
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=kedua

Pada 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:

c
// 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):

bash
gcc -std=c11 -Wall -Wextra -pedantic -pthread -o c11_3 c11_3.c
./c11_3

Output yang Dihasilkan:

text
[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: 10

Fungsi 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.