Menu

typedef di C: Memberi Nama Tipe, Idiom typedef struct, dan Kapan Tidak Memakainya

Cara typedef memberi nama baru pada tipe C yang sudah ada: idiom typedef struct dengan dan tanpa tag, typedef untuk function pointer dan array, serta satu kasus di mana typedef justru merugikan: menyembunyikan pointer.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

typedef melakukan satu hal kecil: memberi nama lain pada tipe yang sudah ada. Tidak ada tipe baru yang dibuat, tidak ada memori yang disisihkan, dan tidak ada yang berubah pada perilaku nilainya. Ini adalah alat untuk memberi nama, dan di C, di mana nama tipe bawaannya panjang (unsigned long long, struct Employee, void (*)(int)), nama yang bagus sangat berharga.

Bentuk Dasar

Sintaksnya terbaca seperti deklarasi variabel dengan typedef ditempelkan di depannya. Di tempat nama variabel biasanya berada, kamu menaruh nama tipe yang baru:

unsigned long  count;    // declares a variable named count
typedef unsigned long  ulong;   // declares a TYPE named ulong

Itulah seluruh aturannya, dan aturan ini menjelaskan setiap typedef yang akan pernah kamu baca, termasuk yang bentuknya aneh di bagian selanjutnya halaman ini.

Perhatikan apa yang tidak kamu dapatkan dari pasangan Celsius/Fahrenheit: keamanan tipe. Keduanya hanyalah double, jadi mengirim nilai Fahrenheit ke tempat yang mengharapkan Celsius tetap bisa di-compile tanpa masalah. Nama-nama itu mendokumentasikan maksud untuk manusia; nama itu tidak membuat pengecekan apa pun.

Idiom typedef struct

Inilah alasan sebagian besar program C memakai typedef sama sekali. Di C, nama tipe sebuah struct menyertakan kata kuncinya:

struct Point { int x; int y; };
struct Point p;              /* the word "struct" every single time */

Typedef meringkasnya:

Baca dengan aturan tadi: buang typedef dan kamu mendapat struct Point {...} Point;, yaitu deklarasi variabel bernama Point. Tambahkan lagi typedef dan Point menjadi nama tipe.

Dengan Tag atau Tanpa Tag

Kamu bisa membuang tag-nya sama sekali, sehingga menghasilkan struct anonim yang hanya punya nama typedef:

typedef struct {
    int x;
    int y;
} Point;          /* no "struct Point" exists - only "Point" */

Bentuk ini lebih rapi, dan untuk record data biasa ini gaya yang umum. Tetapi bentuk ini gagal begitu struct-nya perlu menyebut dirinya sendiri, karena nama typedef baru bisa dipakai setelah deklarasinya sendiri selesai:

/* Does NOT compile: "Node" is not a type yet inside its own braces. */
typedef struct {
    int value;
    Node *next;
} Node;

Pertahankan tag-nya dan referensi ke diri sendiri bisa berjalan, karena struct Node sudah bisa dipakai begitu tag-nya terlihat:

Aturan praktisnya: pertahankan tag-nya. Biayanya hanya satu kata, namanya sama dengan nama typedef sehingga tidak membingungkan, dan pintu tetap terbuka untuk referensi ke diri sendiri dan untuk forward declaration di header. Lihat halaman struct dan pointer untuk melihat node berantai itu berkembang menjadi apa.

Forward Declaration dan Tipe Opaque

Karena pointer ke struct punya ukuran yang diketahui bahkan saat isi struct-nya belum diketahui, sebuah header bisa membagikan sebuah tipe tanpa memperlihatkan anggotanya:

/* stack.h */
typedef struct Stack Stack;     /* declared, not defined */

Stack *stack_create(void);
void   stack_push(Stack *s, int value);
int    stack_pop(Stack *s);
void   stack_destroy(Stack *s);

struct Stack { ... }; yang lengkap berada di stack.c, dan tidak ada kode di luar yang bisa menyentuh anggotanya. Pola tipe opaque ini adalah cara library C menegakkan enkapsulasi, dan itulah persisnya FILE di pustaka standar: kamu mendapat FILE * dari fopen tanpa diberi tahu apa pun tentang isinya. Lihat penanganan file.

typedef untuk enum dan union

Idiom yang sama berlaku untuk dua jenis tipe gabungan lainnya:

Tanpa typedef, keduanya akan ditulis enum Color c; dan union Number n;. Lihat halaman enum dan union untuk tahu apa yang sebenarnya mereka lakukan.

typedef untuk Function Pointer

Di sinilah typedef berhenti menjadi sekadar kenyamanan dan hampir menjadi keharusan. Sintaks mentah sebuah function pointer terkenal sulit dibaca:

void (*handler)(int, const char *);        /* a variable */
void register_cb(void (*cb)(int, const char *));   /* a parameter */

Beri nama tipenya sekali dan keduanya jadi mudah dibaca:

Terapkan lagi aturan membuang typedef: int (*IntBinaryOp)(int, int); saja akan mendeklarasikan variabel bernama IntBinaryOp yang menunjuk ke fungsi yang menerima dua int dan mengembalikan int. Dengan typedef, itu menjadi nama tipenya. Selengkapnya di halaman function pointer.

typedef vs #define

Keduanya terlihat bisa saling menggantikan untuk alias sederhana, padahal tidak:

#define INT_PTR int *
typedef int *int_ptr;

INT_PTR  a, b;    /* expands to: int *a, b;  -- b is a plain int! */
int_ptr  c, d;    /* both c and d are int *          */

#define adalah substitusi teks buta yang dilakukan sebelum compiler melihat kodenya; typedef adalah deklarasi sungguhan yang dipahami compiler, dan berlaku untuk setiap nama dalam daftar. Pakai typedef untuk tipe, dan simpan #define untuk konstanta dan makro.

Saat typedef Merugikan

Typedef juga bisa menyembunyikan informasi yang dibutuhkan pembaca. Contoh paling tajam adalah menyembunyikan pointer:

typedef struct Node *NodeRef;    /* the star disappears */

NodeRef n = get_node();

Melihat NodeRef n, pembaca tidak bisa tahu apakah n bisa NULL, apakah harus dibebaskan, apakah n dan salinannya berbagi objek yang sama, atau apakah anggotanya diakses dengan . atau ->. Keempat pertanyaan itu langsung terjawab oleh struct Node *n.

Pustaka standar setuju: typedef dibuat untuk struct-nya (FILE), dan tanda bintangnya dibiarkan di tempat pemakaian (FILE *fp). Dari situ muncul dua pedoman yang masuk akal:

  • Buatlah typedef untuk struct, union, enum, dan function pointer, karena kata-kata yang dibuang tidak membawa informasi apa pun.
  • Jangan membuat typedef untuk pointer hanya demi mempersingkatnya, dan jangan membuat typedef untuk tipe dasar demi menyamarkannya (typedef int Boolean; mengundang orang untuk menyimpan 7 di dalamnya; pakai bool dari <stdbool.h>, yang dibahas di halaman boolean).

Nama-nama berlebar tetap milik pustaka standar di <stdint.h> (uint32_t, int64_t, size_t) semuanya adalah typedef, dan itulah argumen terbaik untuk fitur ini: namanya menyatakan dengan tepat apa isinya, portabel, dan tidak ada yang disembunyikan.

Pertanyaan yang sering diajukan

Apa fungsi typedef di C?

Memberi nama kedua pada tipe yang sudah ada. typedef unsigned long ulong; berarti ulong dan unsigned long adalah tipe yang sama sejak saat itu. Tidak ada tipe baru dan tidak ada penyimpanan baru yang dibuat, hanya cara yang lebih pendek atau lebih deskriptif untuk menulis tipe yang sudah kamu punya.

Apa itu idiom typedef struct di C?

typedef struct Point { int x; int y; } Point; mendeklarasikan struct sekaligus memberi nama tipe Point dalam satu pernyataan, sehingga kamu bisa menulis Point p; alih-alih struct Point p;. Mempertahankan tag-nya (struct Point) penting jika struct itu perlu merujuk ke dirinya sendiri, seperti node linked list.

Apa perbedaan typedef dan #define?

typedef ditangani oleh compiler dan membuat alias tipe sungguhan; #define adalah substitusi teks yang dilakukan preprocessor sebelum kompilasi. Perbedaan itu terasa pada pointer: #define PTR int* membuat PTR a, b; diekspansi menjadi int* a, b;, dan hanya a yang menjadi pointer. typedef int *PTR; membuat keduanya pointer.

Sebaiknya saya membuat typedef untuk tipe pointer di C?

Biasanya tidak. typedef struct Node *NodeRef; menyembunyikan fakta bahwa tipenya adalah pointer, sehingga pembaca tidak bisa tahu apakah sebuah variabel bisa NULL, harus dibebaskan, atau butuh ->. FILE * di pustaka standar sengaja membiarkan tanda bintangnya terlihat untuk alasan yang sama. Buat typedef untuk struct-nya, bukan untuk pointer ke struct itu.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai