Episode ini membangun fungsi sebagai unit modular: definisi dan prototipe, scope variabel, parameter passing by value, serta nilai kembali. Kalian juga akan menyusun program multi-file dengan header file, dan mengenal recursion, inline function, dan function pointer.

Program yang sehat tidak ditulis sebagai satu blok raksasa, melainkan dipecah menjadi fungsi-fungsi kecil yang masing-masing punya satu tanggung jawab. Episode 5 membahas cara membangun dan merakit fungsi tersebut menjadi sebuah program modular.
Fungsi di C bekerja dengan konsep passing by value: ketika sebuah variabel dikirim ke fungsi, yang dikirim adalah salinan nilainya, bukan variabel aslinya. Konsep ini sering membingungkan, tetapi justru menjadi kunci desain yang aman. Untuk memodifikasi data asli, kalian memerlukan pointer — topik yang akan dibedah di episode 7.
Kalian juga akan belajar memisahkan kode ke header file dan source file, membuat project multi-file, serta mengenal recursion, inline function, dan function pointer yang membuka jalan menuju callback dan pemrograman generik.
Sebuah fungsi terdiri dari tipe kembalian, nama, daftar parameter, dan badan fungsi. Fungsi yang tidak mengembalikan nilai dideklarasikan dengan tipe void:
cat > fungsi.c <<'EOF'
#include <stdio.h>
int tambah(int a, int b);
int main(void) {
int hasil = tambah(3, 4);
printf("Hasil: %d\n", hasil);
return 0;
}
int tambah(int a, int b) {
return a + b;
}
EOF
gcc -Wall -Wextra fungsi.c -o fungsi && ./fungsiBaris int tambah(int a, int b); disebut prototipe: deklarasi yang memberi tahu compiler tentang tanda tangan fungsi sebelum definisinya muncul. Dengan prototipe, main bisa memanggil tambah meski definisinya diletakkan setelahnya.
Setiap variabel punya scope yang menentukan di mana ia bisa diakses. Variabel lokal hanya hidup di dalam blok tempat ia dideklarasikan, termasuk parameter fungsi. Variabel global dideklarasikan di luar semua fungsi dan bisa diakses di mana saja — tetapi gunakan sehemat mungkin karena menyulitkan pelacakan siapa yang mengubah nilai.
Ketika sebuah fungsi dipanggil, nilai argumen disalin ke parameter. Perubahan di dalam fungsi tidak memengaruhi variabel asli di pemanggil:
cat > byvalue.c <<'EOF'
#include <stdio.h>
void ubah(int x) {
x = 99;
}
int main(void) {
int nilai = 5;
ubah(nilai);
printf("nilai tetap %d\n", nilai);
return 0;
}
EOF
gcc -Wall -Wextra byvalue.c -o byvalue && ./byvalueMeski ubah mengisi x dengan 99, variabel nilai di main tetap bernilai 5. Ini perilaku passing by value: hanya salinan yang dikirim. Untuk mengubah nilai asli, fungsi harus menerima alamatnya lewat pointer, seperti yang akan dibahas di episode 7.
Fungsi mengembalikan hasil lewat statement return. Nilai ini bisa langsung dipakai atau disimpan ke variabel. Aturan sederhana: jika sebuah fungsi menghasilkan nilai yang dipakai di tempat lain, buat ia mengembalikan nilai itu, bukan menulis ke variabel global.
Untuk project yang tumbuh besar, pisahkan antarmuka dari implementasi. Header file .h berisi deklarasi yang boleh dilihat file lain, sedangkan source file .c berisi definisi. Susunan standar sebuah modul:
cat > math_utils.h <<'EOF'
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int kuadrat(int x);
#endif
EOF
cat > math_utils.c <<'EOF'
#include "math_utils.h"
int kuadrat(int x) {
return x * x;
}
EOF
cat > main.c <<'EOF'
#include <stdio.h>
#include "math_utils.h"
int main(void) {
printf("Kuadrat 6 = %d\n", kuadrat(6));
return 0;
}
EOF
gcc -Wall -Wextra math_utils.c main.c -o app && ./appPerintah gcc math_utils.c main.c -o app mengompilasi dua source file sekaligus dan menautkannya. Simbol MATH_UTILS_H pada #ifndef berfungsi sebagai include guard yang mencegah header diproses dua kali — topik yang akan dibahas rinci di episode 10.
Setiap file .c yang di-preprocess disebut translation unit. Antarmuka antar translation unit dijembatani oleh header. Disiplin yang baik: setiap file .c meng-include header yang sesuai, dan setiap fungsi yang dipakai dari luar harus dideklarasikan di header.
Fungsi rekursif memanggil dirinya sendiri. Setiap panggilan membutuhkan kasus dasar yang menghentikan rekursi, jika tidak akan terjadi stack overflow. Contoh klasik faktorial:
#include <stdio.h>
int faktorial(int n) {
if (n <= 1) {
return 1;
}
return n * faktorial(n - 1);
}
int main(void) {
printf("%d\n", faktorial(5));
return 0;
}Fungsi faktorial(5) memanggil dirinya sendiri hingga mencapai n = 1 sebagai kasus dasar. Rekursi elegan untuk struktur seperti pohon, tetapi untuk iterasi sederhana, loop for lebih efisien.
Keyword inline memberi saran kepada compiler untuk menyisipkan badan fungsi langsung di titik panggilan, menghindari overhead pemanggilan. Keyword ini hanya saran; compiler yang menentukan keputusan akhir.
Function pointer menyimpan alamat sebuah fungsi, sehingga fungsi bisa dipilih dan dipanggil secara dinamis:
#include <stdio.h>
int tambah(int a, int b) { return a + b; }
int kali(int a, int b) { return a * b; }
int main(void) {
int (*operasi)(int, int) = kali;
printf("%d\n", operasi(4, 5));
return 0;
}Deklarasi int (*operasi)(int, int) membuat variabel yang menunjuk fungsi berparameter dua int dan mengembalikan int. Function pointer adalah dasar untuk callback, tabel dispatch, dan pemrograman generik.
Tip
Sebuah fungsi sebaiknya melakukan satu hal dan satu hal saja. Jika fungsi kalian melebihi layar editor, pertimbangkan untuk memecahnya. Ini membuat testing dan code review menjadi jauh lebih mudah.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kita akan membahas tipe data kompleks dan array — array satu dan multi dimensi, string sebagai array karakter, struct, union, dan enum sebagai tipe komposit, hingga typedef, alias, dan struktur data sederhana.