Fungsi non-void mengembalikan sebuah nilai ke pemanggil melalui statement return. Nilai kembalian dapat berupa bilangan sederhana, struktur data, atau—dengan hati-hati—pointer ke memori yang dikelola fungsi tersebut. Cara mengembalikan nilai memengaruhi kebenaran program: mengembalikan pointer ke variabel lokal yang sudah mati akan menyebabkan undefined behavior, sedangkan melewatkan pointer output adalah pola aman untuk mengembalikan lebih dari satu nilai. Pada chapter ini kita akan menelusuri tiga pola utama return value: return by value, output parameter, dan return pointer.
Return by Value #
Return by value adalah bentuk paling sederhana: fungsi mengembalikan nilai melalui return, dan compiler menyalin nilai tersebut ke pemanggil (atau mengoptimalkan salinan via Return Value Optimization). Untuk tipe primitif seperti int dan double, mekanisme ini sangat efisien.
#include <stdio.h>
/* Return by value - hasil disalin ke pemanggil */
int kuadrat(int x) {
return x * x;
}
double rerata(int a, int b) {
return (a + b) / 2.0;
}
int main(void) {
int k = kuadrat(9);
double r = rerata(7, 8);
printf("Kuadrat 9 = %d\n", k);
printf("Rata-rata 7 dan 8 = %.2f\n", r);
/* Bisa langsung dipakai dalam ekspresi tanpa disimpan */
printf("Hasil ekspresi: %.2f\n", rerata(10, 20) + kuadrat(3));
return 0;
}Output yang Dihasilkan:
Kuadrat 9 = 81
Rata-rata 7 dan 8 = 7.50
Hasil ekspresi: 24.00Mari kita telaah baris-baris kode di atas:
- Baris 4 (
int kuadrat(int x)): header fungsi yang mengembalikanint. Setiap pemanggilkuadrat(9)akan menerima nilai balik bertipeint. - Baris 5 (
return x * x;): ekspresix * xdievaluasi, lalu hasilnya dikirim balik ke pemanggil. Setelahreturn, fungsi selesai dieksekusi untuk pemanggilan ini. - Baris 8 (
return (a + b) / 2.0;): pembagian oleh literal2.0(bertipedouble) memaksa(a + b)dipromosikan kedouble, sehingga hasil pembagian tidak terpotong. - Baris 17 (
rerata(10, 20) + kuadrat(3)): pemanggilan fungsi dapat langsung dipakai sebagai operand dalam ekspresi yang lebih besar. Hasilrerata(10, 20)adalah15.0, ditambahkuadrat(3)=9, menghasilkan24.0.
Mengembalikan Banyak Nilai via Pointer Output #
C hanya mengizinkan satu nilai kembalian per fungsi. Untuk mengembalikan lebih dari satu nilai—misalnya hasil perhitungan sekaligus status sukses/gagal—kita memakai pola output parameter: fungsi mengembalikan status via return, dan menulis hasil aktual ke memori yang ditunjuk pointer parameter.
#include <stdio.h>
#include <stdbool.h>
/* Pola output parameter: kembalikan status via return value, hasil via pointer */
bool bagi(double a, double b, double *hasil) {
if (b == 0.0) {
return false; /* Sinyal gagal */
}
*hasil = a / b;
return true; /* Sinyal sukses */
}
int main(void) {
double h = 0.0;
if (bagi(10.0, 4.0, &h)) {
printf("10 / 4 = %.3f\n", h);
} else {
printf("Pembagian gagal\n");
}
if (bagi(10.0, 0.0, &h)) {
printf("10 / 0 = %.3f\n", h);
} else {
printf("10 / 0 ditolak (division by zero dicegah)\n");
}
return 0;
}Output yang Dihasilkan:
10 / 4 = 2.500
10 / 0 ditolak (division by zero dicegah)Pada contoh di atas, fungsi bagi mengembalikan bool (sukses/gagal). Parameter double *hasil adalah pointer output: fungsi menulis hasil pembagian ke memori yang ditunjuk hasil bila pembagian valid. Pemanggil main mengirim alamat variabel lokal h (&h) sebagai argument. Setelah pemanggilan sukses, variabel h berisi hasil pembagian. Pola ini sangat umum di pustaka standar C—misalnya strtol mengembalikan nilai long, sekaligus menulis posisi akhir parsing melalui parameter char **endptr.
Mengembalikan Pointer dengan Aman #
Mengembalikan pointer adalah hal yang valid, tetapi ada satu aturan kritis: alamat yang dikembalikan harus menunjuk ke memori yang masih hidup setelah fungsi selesai. Variabel lokal biasa mati saat fungsi selesai, sehingga mengembalikan alamatnya menghasilkan dangling pointer.
#include <stdio.h>
/* Mengembalikan pointer ke variabel lokal static (aman) */
int *ambil_counter(void) {
static int counter = 99;
return &counter; /* OK: lifetime static = sepanjang program */
}
/* Contoh BERBAHAYA (diberi komentar agar tetap compile):
*
* int *berbahaya(void) {
* int lokal = 42; <-- variabel lokal biasa
* return &lokal; <-- UB: lokal hilang saat fungsi selesai
* }
*
* Pointer yang dikembalikan akan menunjuk ke memori yang sudah dibebaskan
* (disebut dangling pointer). Pembacaan melalui pointer ini memberi
* hasil tak terdefinisi.
*/
int main(void) {
int *p = ambil_counter();
printf("Nilai yang ditunjuk: %d\n", *p);
*p = 200; /* Mengubah nilai counter melalui pointer */
printf("Setelah diubah: %d\n", *ambil_counter());
return 0;
}Output yang Dihasilkan:
Nilai yang ditunjuk: 99
Setelah diubah: 200Pada contoh di atas, variabel counter dideklarasikan dengan kata kunci static. Variabel static lokal tetap hidup sepanjang program berjalan, sehingga alamatnya aman untuk dikembalikan ke pemanggil. Modifikasi melalui pointer *p = 200 mempengaruhi counter itu sendiri—pemanggilan kedua ambil_counter() mengembalikan nilai 200 (bukan 99), karena static hanya diinisialisasi sekali.
Return di Fungsi void #
Fungsi void tidak mengembalikan nilai, tetapi statement return; (tanpa ekspresi) tetap dapat dipakai untuk keluar dari fungsi lebih awal—pola early return. Pola ini bermanfaat untuk pengecekan guard di awal fungsi. Kita akan membahas ini lebih rinci pada chapter void Functions berikutnya.
Dengan pemahaman return value yang kokoh, kita siap melangkah ke chapter void Functions untuk membahas fungsi tanpa return, pola early return, dan kapan fungsi void adalah pilihan yang tepat.