Menu

Parameter Fungsi di C: Pass by Value, Pointer, dan Array

C mengirim semuanya by value: fungsi menerima salinan, tidak pernah aslinya. Apa artinya dalam praktik, bagaimana pointer meniru pass by reference, kenapa array berperilaku berbeda, dan cara mengembalikan beberapa hasil.

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

Parameter dan Argumen

Dua kata yang sering dipakai bergantian, tetapi layak dibedakan:

  • Parameter adalah variabel di definisi fungsi, misalnya int n di int square(int n).
  • Argumen adalah nilai yang kamu berikan di tempat pemanggilan, misalnya 4 di square(4).

Memanggil sebuah fungsi membuat parameternya sebagai variabel lokal baru lalu menyalin argumen ke dalamnya. Salinan itulah topik utama halaman ini.

C Mengirim by Value, Selalu

Inilah aturannya, dan C tidak punya pengecualian: fungsi menerima salinan dari argumennya. Meng-assign nilai ke parameter hanya mengubah salinannya.

value tetap bernilai 42. Fungsi menerima salinan bernama x, mengubah salinan itu menjadi nol, dan salinannya hilang saat fungsi selesai. Mengganti nama parameternya menjadi value tidak mengubah apa pun: nama tidak menghubungkan keduanya; keduanya adalah dua variabel yang berbeda.

Hal yang sama berlaku untuk setiap tipe: int, double, char, pointer, bahkan seluruh struct (disalin field demi field). Kalau kamu berasal dari Python, Java, atau JavaScript, perhatikan bahwa bahasa-bahasa itu mengirim referensi objek by value, jadi mengubah objek di dalam fungsi terlihat dari luar. C tidak punya itu: C menyalin objeknya sendiri.

Pass by value adalah fitur, bukan keterbatasan. Fungsi tidak bisa merusak data pemanggil secara tidak sengaja, sehingga jauh lebih mudah dinalar.

Kegagalan Klasik: swap

Contoh dari buku pelajaran adalah fungsi yang mencoba menukar dua nilai:

Logikanya benar: fungsi itu memang menukar a dan b. Tetapi a dan b adalah salinan, jadi hasil kerjanya dibuang begitu fungsi selesai.

Meniru Pass by Reference dengan Pointer

Agar sebuah fungsi bisa mengubah variabel milik pemanggil, berikan alamat variabel itu, bukan nilainya. Alamat itu sendiri dikirim by value, yaitu pointer-nya disalin, tetapi salinannya tetap menunjuk ke objek aslinya, jadi menulis lewat pointer itu mencapai memori milik pemanggil.

Tiga potong sintaks membawa seluruh idenya:

  • int *a di daftar parameter mendeklarasikan a sebagai "pointer ke int".
  • &x di tempat pemanggilan menghasilkan alamat x.
  • *a di dalam fungsi berarti "int yang ditunjuk a", yang bisa dibaca dan di-assign.

Inilah yang dimaksud programmer C dengan "pass by reference", dan layak dinyatakan dengan tepat: ini tetap pass by value. Yang disalin adalah pointer-nya. Meng-assign nilai ke a itu sendiri (a = NULL;) hanya mengubah salinannya; meng-assign ke *a mengubah variabel milik pemanggil. Halaman pointer membahas model ini secara lengkap.

Harganya, tempat pemanggilan tidak lagi memberitahumu apakah sebuah variabel bisa berubah, dan justru karena itulah & wajib ditulis. swap(x, y) tidak akan bisa dikompilasi; kamu harus menulis swap(&x, &y), dan & itu adalah penanda yang terlihat bahwa pemanggilan ini bisa mengubah x.

Mengembalikan Beberapa Hasil

return menghasilkan satu nilai, jadi output tambahan dikirim lewat parameter pointer. Bentuk yang umum adalah kode status sebagai nilai kembalian dan hasil sebenarnya ditulis lewat pointer:

Perhatikan bahwa q dan r tetap menyimpan nilai sebelumnya saat pemanggilan gagal, karena fungsi sudah return sebelum menulis apa pun. Itulah kontrak yang harus dipatuhi pemanggil, dan karena itu pula pemeriksaan status dilakukan sebelum memakai output-nya.

Alternatifnya adalah mengembalikan struct yang berisi kedua field, yang sama sekali menghindari pointer dan sering kali menjadi desain yang lebih rapi kalau nilai-nilainya memang saling terkait.

Array Itu Berbeda

Array tidak mengikuti aturan salinan, dan ini menjebak hampir semua orang. Saat kamu mengirim array, ia berubah menjadi pointer ke elemen pertamanya, jadi fungsi menerima sebuah alamat, bukan salinan datanya.

Array milik pemanggil benar-benar berubah, tanpa & di mana pun, karena di konteks ini data sudah merupakan alamat. Ada tiga konsekuensinya:

int a[] dan int *a artinya persis sama di daftar parameter. Bahkan int a[100] juga; ukurannya diabaikan. Menulis int a[] mendokumentasikan maksudnya, tetapi jangan membacanya sebagai "sebuah array sedang disalin".

sizeof di dalam fungsi itu salah. Inilah bug-nya:

Di main, sizeof(data) bernilai 20 byte dan panjangnya dihitung sebagai 5. Di dalam fungsi, sizeof(a) adalah ukuran pointer, yaitu 8 di sistem 64-bit, jadi "panjangnya" menjadi 2. Ukuran array memang tidak tersedia di sana. Selalu kirim panjangnya sebagai parameter terpisah. Compiler modern memberi peringatan untuk kasus ini dengan -Wall.

Gunakan const kalau kamu tidak akan menulis. const int a[] menyatakan bahwa fungsi hanya membaca, yang ditegakkan compiler dan bisa diandalkan pembaca:

int sum(const int a[], int n);        /* promises not to modify the array */

String juga array, jadi hal yang sama berlaku: void greet(const char *name) adalah signature standar untuk fungsi yang membaca string tanpa mengubahnya.

Bagaimana dengan Biayanya?

Mengirim struct besar by value menyalin setiap byte-nya di setiap pemanggilan. Untuk struct dengan beberapa field, itu tidak masalah dan sering lebih cepat daripada indireksi yang ditambahkan pointer. Untuk struct yang besar, kirim pointer, dan tandai dengan const kalau fungsinya hanya membaca:

struct Config { char name[64]; int flags[32]; double weights[128]; };

void applyConfig(const struct Config *cfg);   /* no 512-byte copy per call */

const struct Config *cfg berarti "pointer ke Config yang tidak akan saya ubah", yang memberi efisiensi pointer sekaligus keamanan sebuah salinan.

Kesalahan Umum

  • Berharap parameter biasa bisa mengubah variabel milik pemanggil. Tidak bisa. Kirim pointer.
  • Lupa & di tempat pemanggilan. swap(x, y) padahal swap menginginkan pointer adalah error tipe: compiler menangkapnya, tetapi hanya kalau ada prototipe di scope.
  • Lupa * di dalam fungsi. a = b; pada dua parameter pointer menukar salinan lokalnya, bukan nilainya. Yang kamu inginkan adalah *a = *b;.
  • Memakai sizeof pada parameter array. Selalu salah. Kirim panjangnya.
  • Mengembalikan pointer ke variabel lokal. Variabel lokal mati saat fungsi selesai, jadi pemanggil memegang dangling pointer, yang merupakan undefined behavior. Kembalikan by value, tulis ke buffer yang disediakan pemanggil, atau alokasikan dengan malloc.
  • Tidak memeriksa parameter pointer terhadap NULL. Fungsi yang melakukan dereference pada apa pun yang diberikan kepadanya akan crash kalau argumennya null.

Pertanyaan yang sering diajukan

Apakah C punya pass by reference?

Tidak. C hanya punya satu mode: pass by value. Fungsi selalu menerima salinan dari setiap argumen, jadi meng-assign nilai ke parameter tidak pernah menyentuh variabel milik pemanggil. Yang terlihat seperti pass by reference sebenarnya adalah mengirim pointer by value: pointer-nya disalin, tetapi tetap menunjuk ke objek milik pemanggil, sehingga fungsi bisa mengubah objek itu lewat pointer.

Bagaimana cara mengirim array ke fungsi di C?

Tulis parameternya sebagai int a[] atau int *a, yang artinya sama, lalu kirim panjangnya sebagai parameter terpisah: int sum(const int a[], int n). Array-nya tidak disalin; fungsi menerima pointer ke elemen pertamanya, jadi perubahan pada a[i] terlihat oleh pemanggil.

Kenapa sizeof tidak berfungsi pada parameter array di C?

Karena parameter itu sebenarnya pointer. sizeof(a) di dalam fungsi memberikan ukuran pointer (biasanya 8 byte), bukan ukuran array, jadi sizeof(a)/sizeof(a[0]) menghasilkan sesuatu seperti 2, bukan jumlah elemennya. Selalu kirim panjangnya secara eksplisit.

Bagaimana fungsi C bisa mengembalikan lebih dari satu nilai?

Kirim pointer untuk hasil tambahannya dan tulis lewat pointer itu: int divide(int a, int b, int *quotient, int *remainder) mengembalikan flag sukses dan mengisi kedua output. Alternatifnya adalah mengembalikan struct yang membungkus nilai-nilai itu.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai