Meneruskan array sebagai argumen ke fungsi di C

88

Saya menulis fungsi yang berisi array sebagai argumen, dan memanggilnya dengan melewatkan nilai array sebagai berikut.

Apa yang saya temukan adalah meskipun saya memanggil arraytest()fungsi dengan meneruskan nilai, salinan asli int arr[]diubah.

Bisakah Anda menjelaskan mengapa?

Mohan Mahajan
sumber
1
Anda meneruskan array dengan referensi tetapi Anda memodifikasi isinya - itulah mengapa Anda melihat perubahan dalam data
Shaun Wilde
main()harus kembali int.
underscore_d

Jawaban:

140

Saat mengoper array sebagai parameter, this

artinya sama persis dengan

sehingga Anda yang memodifikasi nilai dalam utama.

Untuk alasan sejarah, array bukanlah warga negara kelas satu dan tidak dapat diteruskan dengan nilai.

Bo Persson
sumber
3
Notasi mana yang lebih baik dalam keadaan apa?
Ramon Martinez
22
@ Ramon - Saya akan menggunakan opsi kedua, karena tampaknya kurang membingungkan dan lebih baik menunjukkan bahwa Anda tidak mendapatkan salinan array.
Bo Persson
2
Bisakah Anda menjelaskan "alasan historis"? Saya kira nilai-nilai yang lewat akan membutuhkan salinan dan membuang-buang memori .. terima kasih
Jacquelyn.Marquardt
5
@lucapozzobon - Awalnya C tidak memiliki nilai lulus, kecuali untuk nilai tunggal. Tidak sampai structditambahkan ke bahasa yang ini diubah. Dan kemudian dianggap terlambat untuk mengubah aturan untuk array. Sudah ada 10 pengguna. :-)
Bo Persson
1
... artinya persis sama seperti void arraytest(int a[1000])dll. Jawaban yang diperluas di sini: stackoverflow.com/a/51527502/4561887 .
Gabriel Staples
9

Jika Anda ingin meneruskan larik berdimensi tunggal sebagai argumen dalam suatu fungsi , Anda harus mendeklarasikan parameter formal dengan salah satu dari tiga cara berikut dan ketiga metode deklarasi tersebut menghasilkan hasil yang serupa karena masing-masing memberi tahu compiler bahwa pointer integer berjalan untuk diterima .

Jadi, Anda mengubah nilai aslinya.

Terima kasih !!!

Monirul Islam Milon
sumber
Saya sedang mencari contoh kedua Anda, dapatkah Anda jelaskan apa keuntungan dari masing-masing metode?
Puck
8

Anda tidak meneruskan array sebagai salinan. Ini hanya penunjuk yang menunjuk ke alamat di mana elemen pertama dari array ada di memori.

fyr
sumber
7

Anda mengirimkan alamat elemen pertama dari array

ob_dev
sumber
7

Array dalam C diubah, dalam banyak kasus, menjadi penunjuk ke elemen pertama dari array itu sendiri. Dan lebih detail lagi, array yang diteruskan ke fungsi selalu diubah menjadi pointer.

Berikut kutipan dari K & R2nd :

Ketika nama array dilewatkan ke suatu fungsi, yang dilewatkan adalah lokasi elemen awal. Di dalam fungsi yang dipanggil, argumen ini adalah variabel lokal, sehingga parameter nama array adalah penunjuk, yaitu variabel yang berisi alamat.

Penulisan:

memiliki arti yang sama dengan menulis:

Jadi, meskipun Anda tidak menulisnya secara eksplisit, hal itu seperti Anda meneruskan pointer dan Anda mengubah nilai di main.

Untuk lebih lanjut saya sangat menyarankan membaca ini .

Selain itu, Anda dapat menemukan jawaban lain tentang SO di sini

granmirupa
sumber
6

Anda mengirimkan nilai lokasi memori dari anggota pertama array.

Oleh karena itu, saat Anda mulai memodifikasi larik di dalam fungsi, Anda mengubah larik aslinya.

Ingat bahwa a[1]ini *(a+1).

alex
sumber
1
Saya kira ada () hilang untuk * a + 1 harus * (a + 1)
ShinTakezou
@Shin Terima kasih, sudah lama sejak saya bermain dengan C.
alex
6

Di C, kecuali untuk beberapa kasus khusus, referensi array selalu "meluruh" ke pointer ke elemen pertama dari array. Oleh karena itu, tidak mungkin melewatkan array "berdasarkan nilai". Larik dalam panggilan fungsi akan diteruskan ke fungsi sebagai penunjuk, yang analog dengan meneruskan larik dengan referensi.

EDIT: Ada tiga kasus khusus di mana array tidak membusuk ke pointer ke elemen pertamanya:

  1. sizeof atidak sama dengan sizeof (&a[0]).
  2. &atidak sama dengan &(&a[0])(dan tidak persis sama dengan &a[0]).
  3. char b[] = "foo"tidak sama dengan char b[] = &("foo").
Thom Smith
sumber
Jika saya meneruskan array ke suatu fungsi. Katakanlah misalnya saya membuat array int a[10]dan menetapkan nilai acak setiap elemen. Sekarang jika saya meneruskan array ini ke fungsi menggunakan int y[]atau int y[10]atau int *y. Dan kemudian dalam fungsi itu saya menggunakan sizeof(y)Jawaban akan menjadi penunjuk byte telah dialokasikan. Jadi dalam kasus ini, itu akan membusuk sebagai penunjuk, Akan Bermanfaat Jika Anda memasukkan ini juga. Lihat postimg.org/image/prhleuezd
Suraj Jain
Jika saya menggunakan sizeofoperasi dalam fungsi di array yang kita definisikan semula maka itu akan membusuk sebagai array, tetapi jika saya meneruskan fungsi lain maka di sana gunakan sizeofoperator itu akan membusuk sebagai pointer.
Suraj Jain
Saya tahu ini sudah tua. Dua pertanyaan jika ada yang kebetulan melihat ini :) 1. @ThomSmith menulis bahwa &atidak sama dengan &a[0]kapana is an array. Bagaimana? Dalam program pengujian saya, keduanya menunjukkan hal yang sama, baik dalam fungsi di mana array dideklarasikan, dan saat diteruskan ke fungsi yang berbeda. 2. Penulis menulis bahwa " char b[] = "foo"tidak sama dengan char b[] = &("foo")". Bagi saya, yang terakhir bahkan tidak dapat dikompilasi. Apa hanya aku?
Aviv Cohn
6

Meneruskan array multidimensi sebagai argumen ke suatu fungsi. Meneruskan satu larik redup sebagai argumen kurang lebih sepele. Mari kita lihat kasus yang lebih menarik dari melewatkan larik 2 dim. Di C Anda tidak bisa menggunakan pointer ke pointer construct ( int **) sebagai ganti dari 2 array dim. Mari kita buat contoh:

Di sini saya telah menentukan fungsi yang mengambil argumen pertama sebagai penunjuk ke array 5 bilangan bulat. Saya dapat memberikan argumen array 2 dim yang memiliki 5 kolom:

Anda mungkin mendapat ide untuk mendefinisikan fungsi yang lebih umum yang dapat menerima larik 2 dim dan mengubah tanda tangan fungsi sebagai berikut:

Kode ini akan dikompilasi tetapi Anda akan mendapatkan error runtime saat mencoba menetapkan nilai dengan cara yang sama seperti pada fungsi pertama. Jadi di C array multidimensi tidak sama dengan pointer ke pointer ... ke pointer. An int(*arr)[5]adalah penunjuk ke larik 5 elemen, an int(*arr)[6] adalah penunjuk ke larik yang terdiri dari 6 elemen, dan mereka adalah penunjuk ke tipe yang berbeda!

Nah, bagaimana cara mendefinisikan argumen fungsi untuk dimensi yang lebih tinggi? Sederhana, kita ikuti saja polanya! Berikut adalah fungsi yang sama yang disesuaikan untuk mengambil larik 3 dimensi:

Seperti yang Anda harapkan, argumen ini dapat mengambil 3 larik redup yang memiliki 4 elemen dalam dimensi kedua dan dalam 5 elemen dimensi ketiga sebagai argumen. Hal seperti ini tidak masalah:

Tetapi kita harus menentukan semua ukuran dimensi hingga yang pertama.

Andrushenko Alexander
sumber
6

Penggunaan array standar dalam C dengan peluruhan tipe alami dari array ke ptr

@Bo Persson dengan benar menyatakan dalam jawaban hebatnya di sini :

Saat mengoper array sebagai parameter, this

artinya sama persis dengan

Namun, izinkan saya menambahkan juga bahwa kedua bentuk di atas juga:

  1. artinya sama persis dengan

  2. yang artinya sama persis dengan

  3. yang artinya sama persis dengan

  4. yang artinya sama persis dengan

  5. dll.

Dalam setiap contoh larik di atas, jenis parameter input meluruh menjadiint * , dan dapat dipanggil tanpa peringatan dan tanpa kesalahan, bahkan dengan opsi build -Wall -Wextra -Werrordiaktifkan (lihat repo saya di sini untuk detail tentang 3 opsi build ini), seperti ini:

Sebagai soal fakta, "ukuran" nilai ( [0], [1], [2], [1000], dll) dalam parameter array di sini adalah ternyata hanya untuk tujuan / estetika diri dokumentasi, dan dapat setiap bilangan bulat positif ( size_ttipe saya pikir) yang Anda inginkan!

Namun, dalam praktiknya, Anda harus menggunakannya untuk menentukan ukuran minimum larik yang Anda harapkan akan diterima oleh fungsi tersebut, sehingga saat menulis kode, Anda dapat dengan mudah melacak dan memverifikasi. Standar MISRA-C-2012 ( beli / unduh standar PDF versi 236-pg 2012 seharga £ 15,00 di sini ) sejauh menyatakan (penekanan ditambahkan):

Aturan 17.5 Argumen fungsi yang sesuai dengan parameter yang dideklarasikan memiliki tipe array harus memiliki jumlah elemen yang sesuai.

...

Jika parameter dideklarasikan sebagai array dengan ukuran yang ditentukan, argumen yang sesuai di setiap panggilan fungsi harus mengarah ke objek yang memiliki setidaknya sebanyak array.

...

Penggunaan deklarator larik untuk parameter fungsi menentukan antarmuka fungsi dengan lebih jelas daripada menggunakan penunjuk. Jumlah minimum elemen yang diharapkan oleh fungsi tersebut secara eksplisit dinyatakan, sedangkan ini tidak dimungkinkan dengan pointer.

Dengan kata lain, mereka merekomendasikan penggunaan format ukuran eksplisit, meskipun standar C secara teknis tidak memberlakukannya-- setidaknya membantu menjelaskan kepada Anda sebagai pengembang, dan kepada orang lain yang menggunakan kode, larik ukuran apa yang diharapkan fungsi tersebut. Anda untuk lulus.


Memaksakan keamanan tipe pada larik di C

Seperti yang ditunjukkan @Winger Sendon dalam komentar di bawah jawaban saya, kami dapat memaksa C untuk memperlakukan tipe array agar berbeda berdasarkan ukuran array !

Pertama, Anda harus menyadari bahwa dalam contoh saya di atas, menggunakan int array1[2];seperti ini: arraytest(array1);menyebabkan array1membusuk secara otomatis menjadi file int *. NAMUN, jika Anda mengambil alamat array1 alih - alih dan menelepon arraytest(&array1), Anda mendapatkan perilaku yang sama sekali berbeda! Sekarang, TIDAK membusuk menjadi int *! Sebaliknya, tipe &array1is int (*)[2], yang berarti "penunjuk ke larik berukuran 2 dari int" , atau "penunjuk ke larik berukuran 2 jenis int" . Jadi, Anda dapat FORCE C untuk memeriksa keamanan tipe pada array, seperti ini:

Sintaks ini sulit dibaca, tetapi mirip dengan fungsi penunjuk . Alat online, cdecl , memberitahu kita bahwa itu int (*a)[2]berarti: "mendeklarasikan a sebagai penunjuk ke larik 2 dari int" (penunjuk ke larik 2 ints). JANGAN bingung dengan versi ini dengan tanda kurung OUT:, int * a[2]yang berarti: "deklarasikan sebagai array 2 pointer ke int" (array 2 pointer ke int).

Sekarang, fungsi ini MEMBUTUHKAN Anda untuk memanggilnya dengan operator alamat ( &) seperti ini, menggunakan sebagai parameter masukan sebuah POINTER KE ARRAY DARI UKURAN YANG BENAR !:

Namun, ini akan menghasilkan peringatan:

Anda dapat menguji kode ini di sini .

Untuk memaksa compiler C mengubah peringatan ini menjadi error, sehingga Anda HARUS selalu memanggil arraytest(&array1);hanya menggunakan larik input dengan ukuran dan jenis yang benar ( int array1[2];dalam kasus ini), tambahkan -Werrorke opsi build Anda. Jika menjalankan kode tes di atas pada onlinegdb.com, lakukan ini dengan mengklik ikon roda gigi di kanan atas dan klik pada "Tanda Kompilator Tambahan" untuk mengetikkan opsi ini. Sekarang, peringatan ini:

akan berubah menjadi kesalahan versi ini:


Perhatikan bahwa Anda juga dapat membuat pointer "type safe" ke array dengan ukuran tertentu, seperti ini:

... tapi saya tidak selalu merekomendasikan ini, karena ini mengingatkan saya pada banyak kejenakaan C ++ yang digunakan untuk memaksa keamanan tipe di mana-mana, dengan biaya yang sangat tinggi dari kompleksitas sintaks bahasa, verbositas, dan kesulitan merancang kode, dan yang saya tidak suka dan telah mengoceh berkali-kali sebelumnya (mis .: lihat "My Thoughts on C ++" di sini ).


Untuk pengujian dan eksperimen tambahan, lihat juga tautan di bawah.

Referensi

Lihat tautan di atas. Juga:

  1. Percobaan kode saya online: https://onlinegdb.com/B1RsrBDFD
Gabriel Staples
sumber
2
void arraytest(int (*a)[1000])lebih baik karena kompiler akan error jika ukurannya salah.
Pemain sayap Sendon
@WingerSendon, saya tahu ada beberapa seluk-beluk yang perlu saya verifikasi di sini, dan sintaks itu membingungkan (seperti sintaks fungsi ptr membingungkan), jadi saya meluangkan waktu dan akhirnya memperbarui jawaban saya dengan bagian baru yang besar berjudul Forcing type safety on arrays in C, mencakup Anda titik.
Gabriel Staples
@ GabrielStaples, Terima kasih. Jawaban Anda sangat membantu. Dapatkah Anda merujuk saya referensi untuk mempelajari c tingkat lanjut dengan cara ini?
daryooosh
@daryooosh, sayangnya, saya tidak bisa. Saya tidak punya referensi bagus. Saya telah mengambil ini sedikit di sini, sedikit di sana, dengan menggali lebih dalam selama bertahun-tahun. Hal terbaik yang bisa saya lakukan adalah memberi tahu Anda bahwa saya sesekali menjatuhkan beberapa dari apa yang saya pelajari seperti ini ke dalam repo eRCaGuy_hello_world saya di sini. Perlu diingat bahwa barang keselamatan tipe C yang saya gunakan di atas harus digunakan SANGAT hemat. Ini akan mempersulit kode Anda dan menurunkan satu ton keterbacaan, dan tidak sepadan. Fokus pada sintaks sederhana jika memungkinkan, dan buat semuanya mudah dibaca.
Gabriel Staples
Perhatikan juga buku teks C klasik kanonik adalah buku K&R The C Programming Language : en.wikipedia.org/wiki/The_C_Programming_Language .
Gabriel Staples
0

Array selalu diteruskan dengan referensi jika Anda menggunakan a[]atau *a:

anil karikatti
sumber
Saya meningkatkan ini. Saya tidak yakin mengapa itu tidak disukai.
Gabriel Staples