C23 (ISO/IEC 9899:2024) adalah lompatan fitur terbesar sejak C11. Standar ini mengadopsi banyak ide modern dari C++ dan praktik industri: nullptr sebagai pengganti NULL, true/false sebagai keyword (bukan lagi macro dari stdbool.h), constexpr untuk konstanta compile-time yang benar-benar typed, attribute [[nodiscard]]/[[deprecated]]/[[maybe_unused]] standar, typeof dan auto sebagai keyword resmi, serta #embed untuk memasukkan file binary langsung ke kode. Bab ini menunjukkan fitur-fitur utama dengan kode yang di-compile menggunakan gcc -std=c2x -pedantic. Kita juga akan melihat bagaimana C23 menghapus kebiasaan lama yang ambigu (mis. NULL yang bisa berarti 0 integer atau pointer null) dan menggantinya dengan konstruk yang lebih aman.

nullptr, true/false Keyword, dan constexpr #

Tiga perubahan paling terasa di C23 adalah: nullptr sebagai konstanta null pointer bertipe nullptr_t, true/false dipromosikan dari macro di stdbool.h menjadi keyword bahasa, dan constexpr untuk mendefinisikan konstanta compile-time yang benar-benar bertipe (bukan macro text substitution).

c
// File: c23_1.c - true/false sebagai keyword, nullptr, constexpr
#include <stdio.h>
#include <stdbool.h>  // masih bisa diinclude, true/false sekarang keyword

// constexpr: konstanta compile-time yang benar-benar typed
constexpr int MAX_BUF = 64;
constexpr double PI = 3.14159265358979;

// nullptr: pengganti NULL dan 0 sebagai pointer null
static int *cari(int *arr, int n, int target) {
    for (int i = 0; i < n; ++i) {
        if (arr[i] == target) {
            return &arr[i];
        }
    }
    return nullptr;   // nullptr bertipe nullptr_t, bukan int
}

int main(void) {
    // true/false sekarang keyword, tidak perlu #include <stdbool.h>
    bool lulus = true;
    if (lulus) {
        printf("Status: lulus\n");
    }

    int data[] = {10, 20, 30, 40, 50};
    int *p = cari(data, 5, 30);
    if (p != nullptr) {
        printf("Ditemukan di indeks %ld, nilai %d\n", p - data, *p);
    } else {
        printf("Tidak ditemukan\n");
    }

    // nullptr_t bisa di-compare dengan pointer apapun
    int *q = nullptr;
    if (q == nullptr) {
        printf("q adalah null pointer\n");
    }

    printf("MAX_BUF = %d\n", MAX_BUF);
    printf("PI      = %.14f\n", PI);
    return 0;
}

Compile dan jalankan dengan flag -std=c2x (GCC 14 masih memakai nama c2x; GCC 15+ akan menggunakan c23):

bash
gcc -std=c2x -Wall -Wextra -pedantic -o c23_1 c23_1.c
./c23_1

Output yang Dihasilkan:

text
Status: lulus
Ditemukan di indeks 2, nilai 30
q adalah null pointer
MAX_BUF = 64
PI      = 3.14159265358979

Mari kita telaah baris-baris kode di atas:

  1. Baris 7-8 (constexpr int MAX_BUF = 64; constexpr double PI = ...;): constexpr memberi tahu compiler bahwa variabel ini adalah konstanta compile-time yang dapat dipakai dalam konteks yang mensyaratkan konstanta (mis. ukuran array static, label case, initializer global). Berbeda dengan const, constexpr menjamin nilai benar-benar dievaluasi saat kompilasi, bukan saat runtime.
  2. Baris 11-18 (static int *cari(...) { ... return nullptr; }): Fungsi mengembalikan int *. Jika target tidak ditemukan, kita kembalikan nullptr alih-alih NULL atau 0. Tipe nullptr adalah nullptr_t yang implisit convertible ke pointer apa pun, tetapi tidak convertible ke int—ini mencegah bug klasik if (cari(...) == 0) yang ambigu antara “integer 0” dan “null pointer”.
  3. Baris 21-23 (bool lulus = true;): Tipe bool dengan nilai true/false adalah keyword resmi di C23. Sebelum C23, kita harus #include <stdbool.h> yang mendefinisikan bool, true, false sebagai macro. Di C23, header tersebut boleh di-include atau tidak—keyword tetap tersedia.
  4. Baris 27-32 (if (p != nullptr) ...): Pembandingan pointer dengan nullptr lebih eksplisit daripada if (p) atau if (p != NULL). Compiler tahu persis bahwa p dibandingkan dengan null pointer, bukan dengan integer 0.
  5. Baris 35-37 (int *q = nullptr; if (q == nullptr)): Variabel q diinisialisasi dengan nullptr bertipe nullptr_t. Konversi implisit dari nullptr_t ke int * didefinisikan oleh standar C23.

Attribute Standar: [[nodiscard]], [[deprecated]], [[maybe_unused]] #

C23 memfinalisasi sintaks attribute [[...]] yang diperkenalkan di C23 (sebelum C11, GCC memakai __attribute__((...)) sebagai ekstensi). Attribute standar utama adalah [[nodiscard]] (warning jika return value dibuang), [[deprecated]] (warning jika fungsi dipanggil), dan [[maybe_unused]] (menekan warning unused).

c
// File: c23_2.c - Attributes [[nodiscard]], [[deprecated]], [[maybe_unused]]
#include <stdio.h>

// [[nodiscard]]: warning jika pemanggil membuang return value
[[nodiscard]] int hitung_total(int *arr, int n) {
    int total = 0;
    for (int i = 0; i < n; ++i) total += arr[i];
    return total;
}

// [[deprecated]]: warning jika fungsi dipanggil
[[deprecated("gunakan versi_v2() sebagai gantinya")]]
int versi_lama(void) {
    return 42;
}

// [[maybe_unused]]: menekan warning -Wunused
[[maybe_unused]] static int helper_tidak_dipakai(void) {
    return 99;
}

int main(void) {
    int data[] = {1, 2, 3, 4, 5};

    // Pembuang return value memicu warning karena [[nodiscard]]
    int t = hitung_total(data, 5);
    printf("Total: %d\n", t);
    return 0;
}

Compile dan jalankan:

bash
gcc -std=c2x -Wall -Wextra -pedantic -o c23_2 c23_2.c
./c23_2

Output yang Dihasilkan:

text
Total: 15

Untuk melihat warning yang dipicu [[nodiscard]], modifikasi main agar membuang return value:

c
// File: c23_2b.c - Demo warning [[nodiscard]]
#include <stdio.h>
[[nodiscard]] int hitung_total(int *arr, int n) {
    int total = 0;
    for (int i = 0; i < n; ++i) total += arr[i];
    return total;
}
int main(void) {
    int data[] = {1, 2, 3};
    hitung_total(data, 3);   // hasil dibuang - harus memicu warning
    return 0;
}

Compile dan amati warning yang muncul:

bash
gcc -std=c2x -Wall -Wextra -pedantic -o c23_2b c23_2b.c

Output yang Dihasilkan:

text
c23_2b.c: In function 'main':
c23_2b.c:9:5: warning: ignoring return value of 'hitung_total', declared with attribute 'nodiscard' [-Wunused-result]
    9 |     hitung_total(data, 3);   // hasil dibuang - harus memicu warning
      |     ^~~~~~~~~~~~~~~~~~~~~
c23_2b.c:2:19: note: declared here
    2 | [[nodiscard]] int hitung_total(int *arr, int n) {
      |                   ^~~~~~~~~~~~

Attribute [[nodiscard]] sangat berguna untuk fungsi yang return value-nya penting (mis. alokator, error code, atau hasil komputasi mahal). Attribute [[deprecated]] memberi pesan migrasi yang jelas saat API berubah. Attribute [[maybe_unused]] menggantikan trik (void)x; lama untuk menekan warning variabel tidak terpakai.

typeof, auto, dan #embed #

C23 mempromosikan beberapa ekstensi GCC menjadi standar resmi: typeof (mengambil tipe dari ekspresi), auto (type inference untuk variabel lokal), dan #embed (memasukkan file binary sebagai initializer). Program berikut memperlihatkan typeof dan auto:

c
// File: c23_3.c - typeof dan auto sebagai keyword standar
#include <stdio.h>

// typeof: jadi keyword resmi di C23 (sebelumnya ekstensi GCC)
static int jumlah_array(int n, typeof(int) *arr) {
    typeof(int) total = 0;
    for (int i = 0; i < n; ++i) {
        total += arr[i];
    }
    return total;
}

// auto: type inference untuk variabel lokal
static int demo_auto(void) {
    auto x = 42;       // int
    auto y = 3.14;     // double
    auto z = "halo";   // const char *
    (void)z;
    return x + (int)y;
}

int main(void) {
    int data[] = {10, 20, 30, 40};
    printf("Jumlah   : %d\n", jumlah_array(4, data));
    printf("demo_auto: %d\n", demo_auto());
    return 0;
}

Compile dan jalankan:

bash
gcc -std=c2x -Wall -Wextra -pedantic -o c23_3 c23_3.c
./c23_3

Output yang Dihasilkan:

text
Jumlah   : 100
demo_auto: 45