Function prototype adalah bentuk function declaration yang secara eksplisit mencantumkan tipe parameter. Istilah “prototype” sering dipakai bergantian dengan “declaration”, tetapi secara teknis prototype adalah deklarasi yang memberi tahu compiler jumlah dan tipe parameter secara presisi. Prototype menjadi penghubung antara pemanggil dan implementasi fungsi, terutama saat keduanya berada di file yang berbeda. Pada chapter ini kita akan menelusuri bagaimana prototype menyalakan type checking compiler, perbedaan prototype modern dengan gaya K&R lama, serta peran prototype bagi fungsi variadic.

Prototype Menyalakan Type Checking #

Saat compiler menemukan pemanggilan fungsi yang sebelumnya sudah diprototype-kan, compiler akan memeriksa:

  1. Jumlah argument harus sama dengan jumlah parameter (kecuali fungsi variadic).
  2. Tipe setiap argument akan dikonversi ke tipe parameter bila kompatibel (misalnya int ke double).
  3. Bila tidak kompatibel, compiler mengeluarkan warning atau error.

Berikut contoh prototype yang memungkinkan konversi argumen int ke double secara otomatis.

c
#include <stdio.h>

/* Prototype memungkinkan compiler melakukan type checking */
double kali(double a, double b);

int main(void) {
    /* Compiler melakukan konversi otomatis berkat prototype */
    double hasil = kali(3, 4);     /* int 3, 4 dikonversi ke double */
    printf("3 x 4 = %.2f\n", hasil);

    /* Bila argumen tidak cocok, compiler mengeluarkan warning */
    hasil = kali(2.5, 4.0);
    printf("2.5 x 4.0 = %.2f\n", hasil);
    return 0;
}

double kali(double a, double b) {
    return a * b;
}

Output yang Dihasilkan:

text
3 x 4 = 12.00
2.5 x 4.0 = 10.00

Mari kita telaah baris-baris kode di atas:

  1. Baris 4 (double kali(double a, double b);): prototype yang menyatakan bahwa fungsi kali mengembalikan double dan menerima dua parameter bertipe double. Tanpa prototype, compiler tidak akan tahu bahwa konversi int ke double perlu dilakukan.
  2. Baris 8 (double hasil = kali(3, 4);): argument 3 dan 4 bertipe int. Berkat prototype, compiler menyisipkan konversi implisit dari int ke double sebelum memanggil fungsi.
  3. Baris 12 (double kali(double a, double b)): definisi fungsi. Tanda kurung kurawal { } mengindikasikan ini adalah definition, bukan prototype.
  4. Baris 13 (return a * b;): hasil perkalian double * double = double dikembalikan ke pemanggil.

Prototype vs Gaya K&R Lama #

Pada era K&R (pra-C89), deklarasi fungsi sering ditulis dengan tanda kurung kosong int f();. Bentuk ini bukan prototype—compiler hanya tahu bahwa f mengembalikan int, tetapi tidak tahu apa-apa tentang parameter. Pemanggil dapat melewatkan jumlah argumen berapa pun tanpa compiler mengeluarkan peringatan.

c
#include <stdio.h>

/* Gaya K&R lama: tanda kurung kosong berarti "tanpa informasi parameter" */
int jumlahkan();

int main(void) {
    /* Compiler tidak dapat memverifikasi jumlah/tipe argumen */
    int a = jumlahkan(3);          /* Hanya 1 argumen, fungsi butuh 2! */
    printf("Hasil: %d\n", a);
    return 0;
}

/* Definisi sebenarnya mengharapkan dua int */
int jumlahkan(int x, int y) {
    return x + y;
}

Kode di atas tidak dapat diandalkan. Mari kita kompilasi dengan flag -Wstrict-prototypes agar compiler mengeluarkan peringatan tentang gaya K&R:

bash
gcc -std=c11 -Wall -Wextra -Wstrict-prototypes -o test_proto2_kr test_proto2_kr.c

Output compiler:

text
test_proto2_kr.c:4:1: warning: function declaration isn't a prototype [-Wstrict-prototypes]
    4 | int jumlahkan();
      | ^~~

Output program saat dijalankan (berisi nilai sampah karena argumen kedua tidak terisi):

text
Hasil: 666726267

Nilai 666726267 jelas bukan hasil yang dimaksud—ini muncul karena fungsi jumlahkan membaca parameter kedua y dari register memori yang tidak diinisialisasi dengan nilai yang valid. Perilaku ini undefined behavior dan hasilnya dapat berbeda pada setiap eksekusi atau compiler.

Prototype untuk Fungsi Variadic #

Fungsi variadic adalah fungsi yang menerima jumlah argument bervariasi (contoh paling terkenal: printf). Prototype fungsi variadic menggunakan tanda elipsis ... di akhir daftar parameter. Compiler memerlukan prototype yang lengkap agar tahu kapan harus menerapkan default argument promotions (misalnya float dipromosikan ke double, short ke int).

c
#include <stdio.h>

/* Prototype dengan jumlah argumen variabel (variadic) */
void log_pesan(const char *level, int count, ...);

int main(void) {
    log_pesan("INFO", 3, "memori", "disk", "cpu");
    log_pesan("ERROR", 1, "konektivitas");
    return 0;
}

/* Definition menggunakan stdarg.h untuk mengakses argumen variabel */
#include <stdarg.h>

void log_pesan(const char *level, int count, ...) {
    printf("[%s] %d item: ", level, count);
    va_list ap;
    va_start(ap, count);
    for (int i = 0; i < count; i++) {
        const char *item = va_arg(ap, const char *);
        printf("%s ", item);
    }
    va_end(ap);
    printf("\n");
}

Output yang Dihasilkan:

text
[INFO] 3 item: memori disk cpu 
[ERROR] 1 item: konektivitas 

Fungsi log_pesan menerima parameter tetap (level, count) diikuti ... yang menandai argumen variabel. Parameter count berfungsi sebagai penanda jumlah argumen variabel yang dilewatkan—polo ini sama dengan yang dipakai oleh printf (yang menentukan jumlah argumen dari format specifier). Macro va_start, va_arg, dan va_end dari <stdarg.h> dipakai untuk mengakses argumen variabel satu per satu.

Dengan pondasi prototype yang kokoh, kita siap melangkah ke chapter Parameter dan Argument untuk membahas cara melewatkan nilai, reference, dan jenis parameter yang dapat dipakai dalam fungsi C.