Designated initializer adalah fitur C99 yang memungkinkan kita menginisialisasi field struct atau elemen array berdasarkan nama atau indeks, bukan urutan posisi. Sebelum C99, kita harus mengingat urutan field struct dan mengisi semua field yang mendahului field yang ingin di-set. Dengan designated initializer, kita cukup menulis .nama = nilai untuk struct atau [index] = nilai untuk array. Pada chapter ini kita akan membandingkan inisialisasi posisional versus designated, lalu membangun konfigurasi default untuk struct dengan banyak field.

Designated Initializers untuk Struct dan Array #

Sintaks designated initializer untuk struct adalah .field = value, sedangkan untuk array adalah [index] = value. Field yang tidak ditulis otomatis diinisialisasi ke nol (atau NULL untuk pointer), sehingga kita bisa fokus hanya pada field yang penting.

c
#include <stdio.h>

struct Point {
    int x, y;
    const char *label;
};

int main(void) {
    /* Designated initializers untuk struct */
    struct Point p = {.x = 1, .y = 2, .label = "asal"};
    printf("p: (%d, %d) %s\n", p.x, p.y, p.label);

    /* Sebagian field -- sisanya nol */
    struct Point q = {.label = "tujuan"};
    printf("q: (%d, %d) %s\n", q.x, q.y, q.label);

    /* Designated initializers untuk array */
    int arr[5] = {[3] = 7, [0] = 1};
    printf("arr: ");
    for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
    printf("\n");

    /* Kombinasi: array of struct */
    struct Point pts[3] = {
        [0] = {.x = 10, .y = 20, .label = "a"},
        [2] = {.x = 30, .y = 40, .label = "c"},
    };
    for (int i = 0; i < 3; i++) {
        printf("pts[%d]: (%d, %d) %s\n", i, pts[i].x, pts[i].y, pts[i].label);
    }

    return 0;
}

Output yang Dihasilkan:

text
p: (1, 2) asal
q: (0, 0) tujuan
arr: 1 0 0 7 0 
pts[0]: (10, 20) a
pts[1]: (0, 0) (null)
pts[2]: (30, 40) c

Mari kita telaah baris-baris kode di atas:

  1. Baris 10 (struct Point p = {.x = 1, .y = 2, .label = "asal"};): tiap field disebut namanya secara eksplisit. Urutan field dalam initializer tidak harus sama dengan urutan deklarasi struct—.label bisa muncul lebih awal tanpa efek.
  2. Baris 14 (struct Point q = {.label = "tujuan"};): hanya label diisi. Field x dan y otomatis di-set ke 0 (zero-initialization). Tanpa designated initializer, kita harus menulis {0, 0, "tujuan"} yang rawan typo bila struct memiliki banyak field.
  3. Baris 18 (int arr[5] = {[3] = 7, [0] = 1};): dua indeks diisi eksplisit. Elemen arr[1], arr[2], arr[4] otomatis nol. Berguna untuk sparse array.
  4. Baris 23-26 (struct Point pts[3] = { [0] = {...}, [2] = {...} };): kombinasi designated array + designated struct. Indeks 1 tidak ditulis sehingga otomatis di-zero-init—pts[1].label menjadi NULL. Wajib memeriksa NULL sebelum mencetak atau memakai field.

Konfigurasi Default dengan Banyak Field #

Manfaat designated initializer paling terasa pada struct konfigurasi dengan banyak field. Bayangkan struct ServerConfig dengan 10 field—mengingat urutan field saat menulis {host, port, max_conn, ...} rawan tertukar, terutama bila ada field bertipe sama (misalnya port dan max_connections keduanya int).

c
#include <stdio.h>

/* Konfigurasi dengan banyak field -- designated mempermudah */
typedef struct {
    const char *host;
    int port;
    int max_connections;
    int timeout_seconds;
    int verbose;
    const char *log_path;
} ServerConfig;

/* Default values memakai designated initializer */
ServerConfig make_default(void) {
    return (ServerConfig){
        .host = "0.0.0.0",
        .port = 8080,
        .max_connections = 100,
        .timeout_seconds = 30,
        .verbose = 0,
        .log_path = "/var/log/server.log",
    };
}

int main(void) {
    ServerConfig cfg = make_default();
    printf("default:\n");
    printf("  host=%s port=%d max=%d timeout=%d verbose=%d log=%s\n",
           cfg.host, cfg.port, cfg.max_connections,
           cfg.timeout_seconds, cfg.verbose, cfg.log_path);

    /* Hanya ganti port dan verbose */
    ServerConfig dev = make_default();
    dev.port = 9000;
    dev.verbose = 1;
    printf("dev:\n");
    printf("  host=%s port=%d max=%d timeout=%d verbose=%d log=%s\n",
           dev.host, dev.port, dev.max_connections,
           dev.timeout_seconds, dev.verbose, dev.log_path);

    /* Designated initializers dengan offset array */
    int sparse[10] = {
        [1] = 10,
        [4] = 40,
        [9] = 90
    };
    printf("sparse: ");
    for (int i = 0; i < 10; i++) printf("%d ", sparse[i]);
    printf("\n");

    return 0;
}

Output yang Dihasilkan:

text
default:
  host=0.0.0.0 port=8080 max=100 timeout=30 verbose=0 log=/var/log/server.log
dev:
  host=0.0.0.0 port=9000 max=100 timeout=30 verbose=1 log=/var/log/server.log
sparse: 0 10 0 0 40 0 0 0 0 90 

Fungsi make_default() mengembalikan struct konfigurasi lengkap. Pemanggil tinggal mengoverride field yang berbeda (dev.port = 9000; dev.verbose = 1;) tanpa menyentuh field lain. Pola ini sangat umum di library C modern seperti libcurl dan SDL—pemanggil mengisi default lalu override beberapa field yang relevan.

Aturan Penting Designated Initializers #

  • Field harus disebut sekali saja. Menulis .x = 1, .x = 2 adalah undefined behavior (compiler biasanya memilih yang terakhir, tetapi tidak dijamin).
  • Indeks array [i] boleh berupa ekspresi konstan seperti [SIZE-1].
  • Pada C99, urutan designated initializer boleh acak. Pada C++ (yang mengadopsi fitur ini di C++20), urutan harus mengikuti deklarasi struct—hati-hati bila kode akan di-port.