Variadic function adalah fungsi yang menerima jumlah argumen yang tidak tetap—bisa nol, satu, atau sepuluh. Contoh paling terkenal adalah printf dan keluarganya: kita bisa menulis printf("halo") atau printf("%d + %d = %d", a, b, a+b) dengan signature yang sama. C menyediakan mekanisme variadic lewat header <stdarg.h> dan tipe va_list. Pada chapter ini kita akan menulis sum, rata_rata, dan fungsi log_line yang meniru pola printf. Pemahaman mendalam tentang <stdarg.h> akan dibahas di chapter berikutnya.
Mengapa Variadic Function Diperlukan #
Bayangkan kita ingin menulis fungsi sum yang bisa menerima berapapun angka. Tanpa variadic, kita harus menulis overload (yang tidak ada di C) atau memakai array: sum(int *arr, int n). Pendekatan array memaksa pemanggil mengemas argumen ke array, tidak nyaman untuk panggilan cepat dengan beberapa nilai. Variadic memberi solusi: pemanggil menulis argumen langsung, dan fungsi membaca satu per satu lewat va_list.
#include <stdio.h>
#include <stdarg.h>
/* Fungsi variadic: jumlahkan count angka */
int sum(int count, ...) {
va_list ap;
va_start(ap, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(ap, int);
}
va_end(ap);
return total;
}
/* Fungsi variadic: rata-rata double */
double rata_rata(int count, ...) {
va_list ap;
va_start(ap, count);
double total = 0.0;
for (int i = 0; i < count; i++) {
total += va_arg(ap, double);
}
va_end(ap);
return count > 0 ? total / count : 0.0;
}
/* Cetak pasangan key-value string */
void log_pairs(int count, ...) {
va_list ap;
va_start(ap, count);
for (int i = 0; i < count; i++) {
const char *key = va_arg(ap, const char *);
const char *val = va_arg(ap, const char *);
printf(" %s = %s\n", key, val);
}
va_end(ap);
}
int main(void) {
printf("sum(3, 10, 20, 30) = %d\n", sum(3, 10, 20, 30));
printf("sum(5, 1, 2, 3, 4, 5) = %d\n", sum(5, 1, 2, 3, 4, 5));
printf("sum(0) = %d\n", sum(0));
printf("rata_rata(4, 1.0, 2.0, 3.0, 4.0) = %.2f\n",
rata_rata(4, 1.0, 2.0, 3.0, 4.0));
printf("pasangan:\n");
log_pairs(2, "user", "budi", "halaman", "/home");
return 0;
}Output yang Dihasilkan:
sum(3, 10, 20, 30) = 60
sum(5, 1, 2, 3, 4, 5) = 15
sum(0) = 0
rata_rata(4, 1.0, 2.0, 3.0, 4.0) = 2.50
pasangan:
user = budi
halaman = /homeMari kita telaah baris-baris kode di atas:
- Baris 6 (
int sum(int count, ...)): tanda...(ellipsis) menandakan fungsi menerima jumlah argumen variabel setelahcount. Standar C mensyaratkan parameter bernama terakhir sebelum...—countmenjadi anchor yang dipakaiva_start. - Baris 8 (
va_start(ap, count);): makrova_startmenginisialisasiva_list apagar menunjuk ke argumen pertama setelahcount. Wajib dipanggil sebelumva_arg. - Baris 11 (
total += va_arg(ap, int);): makrova_argmengembalikan argumen berikutnya dan secara otomatis memajukanap. Tipeintharus persis cocok dengan tipe argumen yang dikirim pemanggil—compiler tidak melakukan konversi otomatis di sini. - Baris 13 (
va_end(ap);): makrova_endmembersihkanva_list. Wajib dipanggil sebelum fungsi kembali—pada beberapa platformva_listmenyimpan resource yang harus dilepas. - Baris 18-27 (
rata_rata): sama dengansumtetapi tipedouble. Perhatikan bahwava_arg(ap, double)akan salah bila pemanggil mengirimint—karena default argument promotion,floatotomatis menjadidouble, tetapiinttetapint. Pemanggil harus konsisten mengirimdouble. - Baris 30-39 (
log_pairs): membaca dua argumen string per iterasi. Pola key-value umum di API logging variadic.
Pola printf-Like dengan vprintf #
Sering kita ingin menulis fungsi yang menerima format string dan argumen, lalu meneruskan ke printf dengan prefix tambahan. C menyediakan vprintf, vsprintf, dan vsnprintf yang menerima va_list bukan ... langsung.
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
/* Wrapper printf-like yang menambah prefix ke setiap baris */
void log_line(const char *prefix, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
printf("[%s] ", prefix);
vprintf(fmt, ap);
printf("\n");
va_end(ap);
}
/* Hitung maksimum dari sekian int (akhir dengan 0 sentinel) */
int maxn(int first, ...) {
va_list ap;
va_start(ap, first);
int best = first;
int v;
while ((v = va_arg(ap, int)) != 0) {
if (v > best) best = v;
}
va_end(ap);
return best;
}
int main(void) {
log_line("INFO", "memulai aplikasi versi %d.%d", 1, 7);
log_line("ERROR", "gagal membuka %s (kode %d)", "data.txt", 13);
log_line("WARN", "penggunaan memori %d%%", 87);
printf("max(3, 9, 5, 12, 7, 0) = %d\n", maxn(3, 9, 5, 12, 7, 0));
printf("max(42, 0) = %d\n", maxn(42, 0));
return 0;
}Output yang Dihasilkan:
[INFO] memulai aplikasi versi 1.7
[ERROR] gagal membuka data.txt (kode 13)
[WARN] penggunaan memori 87%
max(3, 9, 5, 12, 7, 0) = 12
max(42, 0) = 42Fungsi log_line menerima ..., lalu meneruskan va_list ke vprintf. Pola ini menghindari kita menulis parser format sendiri—pustaka standar sudah melakukannya dengan aman (memeriksa %d, %s, dll). Fungsi maxn memakai pola berbeda: argumen pertama eksplisit (first), diikuti ... dengan sentinel 0 sebagai penanda akhir.
Default Argument Promotion #
Pada fungsi variadic, argumen menjalani default argument promotion:
char,short,signed char,unsigned char,unsigned shortdipromosikan keint(atauunsigned intbilainttidak muat).floatdipromosikan kedouble.
Karena itu, va_arg(ap, char) adalah salah—harus va_arg(ap, int). Demikian pula va_arg(ap, float) harus va_arg(ap, double).