Saya menulis fungsi yang berisi array sebagai argumen, dan memanggilnya dengan melewatkan nilai array sebagai berikut.
void arraytest(int a[])
{
// changed the array a
a[0]=a[0]+a[1];
a[1]=a[0]-a[1];
a[0]=a[0]-a[1];
}
void main()
{
int arr[]={1,2};
printf("%d \t %d",arr[0],arr[1]);
arraytest(arr);
printf("\n After calling fun arr contains: %d\t %d",arr[0],arr[1]);
}
Apa yang saya temukan adalah meskipun saya memanggil arraytest()
fungsi dengan meneruskan nilai, salinan asli int arr[]
diubah.
Bisakah Anda menjelaskan mengapa?
c
arrays
function
parameters
parameter-passing
Mohan Mahajan
sumber
sumber
main()
harus kembaliint
.Jawaban:
Saat mengoper array sebagai parameter, this
void arraytest(int a[])
artinya sama persis dengan
void arraytest(int *a)
sehingga Anda yang memodifikasi nilai dalam utama.
Untuk alasan sejarah, array bukanlah warga negara kelas satu dan tidak dapat diteruskan dengan nilai.
sumber
struct
ditambahkan ke bahasa yang ini diubah. Dan kemudian dianggap terlambat untuk mengubah aturan untuk array. Sudah ada 10 pengguna. :-)void arraytest(int a[1000])
dll. Jawaban yang diperluas di sini: stackoverflow.com/a/51527502/4561887 .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 .
int func(int arr[], ...){ . . . } int func(int arr[SIZE], ...){ . . . } int func(int* arr, ...){ . . . }
Jadi, Anda mengubah nilai aslinya.
Terima kasih !!!
sumber
Anda tidak meneruskan array sebagai salinan. Ini hanya penunjuk yang menunjuk ke alamat di mana elemen pertama dari array ada di memori.
sumber
Anda mengirimkan alamat elemen pertama dari array
sumber
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 :
Penulisan:
void arraytest(int a[])
memiliki arti yang sama dengan menulis:
void arraytest(int *a)
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
sumber
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)
.sumber
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:
sizeof a
tidak sama dengansizeof (&a[0])
.&a
tidak sama dengan&(&a[0])
(dan tidak persis sama dengan&a[0]
).char b[] = "foo"
tidak sama denganchar b[] = &("foo")
.sumber
int a[10]
dan menetapkan nilai acak setiap elemen. Sekarang jika saya meneruskan array ini ke fungsi menggunakanint y[]
atauint y[10]
atauint *y
. Dan kemudian dalam fungsi itu saya menggunakansizeof(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/prhleuezdsizeof
operasi dalam fungsi di array yang kita definisikan semula maka itu akan membusuk sebagai array, tetapi jika saya meneruskan fungsi lain maka di sana gunakansizeof
operator itu akan membusuk sebagai pointer.&a
tidak 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 denganchar b[] = &("foo")
". Bagi saya, yang terakhir bahkan tidak dapat dikompilasi. Apa hanya aku?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:void assignZeros(int(*arr)[5], const int rows) { for (int i = 0; i < rows; i++) { for (int j = 0; j < 5; j++) { *(*(arr + i) + j) = 0; // or equivalent assignment arr[i][j] = 0; } }
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:
int arr1[1][5] int arr1[2][5] ... int arr1[20][5] ...
Anda mungkin mendapat ide untuk mendefinisikan fungsi yang lebih umum yang dapat menerima larik 2 dim dan mengubah tanda tangan fungsi sebagai berikut:
void assignZeros(int ** arr, const int rows, const int cols) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { *(*(arr + i) + j) = 0; } } }
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, anint(*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:
void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) { for (int i = 0; i < dim1; i++) { for (int j = 0; j < dim2; j++) { for (int k = 0; k < dim3; k++) { *(*(*(arr + i) + j) + k) = 0; // or equivalent assignment arr[i][j][k] = 0; } } } }
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:
arr[1][4][5] arr[2][4][5] ... arr[10][4][5] ...
Tetapi kita harus menentukan semua ukuran dimensi hingga yang pertama.
sumber
Penggunaan array standar dalam C dengan peluruhan tipe alami dari array ke ptr
@Bo Persson dengan benar menyatakan dalam jawaban hebatnya di sini :
Namun, izinkan saya menambahkan juga bahwa kedua bentuk di atas juga:
artinya sama persis dengan
void arraytest(int a[0])
yang artinya sama persis dengan
void arraytest(int a[1])
yang artinya sama persis dengan
void arraytest(int a[2])
yang artinya sama persis dengan
void arraytest(int a[1000])
dll.
Dalam setiap contoh larik di atas, jenis parameter input meluruh menjadi
int *
, dan dapat dipanggil tanpa peringatan dan tanpa kesalahan, bahkan dengan opsi build-Wall -Wextra -Werror
diaktifkan (lihat repo saya di sini untuk detail tentang 3 opsi build ini), seperti ini:int array1[2]; int * array2 = array1; // works fine because `array1` automatically decays from an array type // to `int *` arraytest(array1); // works fine because `array2` is already an `int *` arraytest(array2);
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_t
tipe 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):
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);
menyebabkanarray1
membusuk secara otomatis menjadi fileint *
. NAMUN, jika Anda mengambil alamatarray1
alih - alih dan meneleponarraytest(&array1)
, Anda mendapatkan perilaku yang sama sekali berbeda! Sekarang, TIDAK membusuk menjadiint *
! Sebaliknya, tipe&array1
isint (*)[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:void arraytest(int (*a)[2]) { // my function here }
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 2int
s). JANGAN bingung dengan versi ini dengan tanda kurung OUT:,int * a[2]
yang berarti: "deklarasikan sebagai array 2 pointer ke int" (array 2 pointer keint
).Sekarang, fungsi ini MEMBUTUHKAN Anda untuk memanggilnya dengan operator alamat (
&
) seperti ini, menggunakan sebagai parameter masukan sebuah POINTER KE ARRAY DARI UKURAN YANG BENAR !:int array1[2]; // ok, since the type of `array1` is `int (*)[2]` (ptr to array of // 2 ints) arraytest(&array1); // you must use the & operator here to prevent // `array1` from otherwise automatically decaying // into `int *`, which is the WRONG input type here!
Namun, ini akan menghasilkan peringatan:
int array1[2]; // WARNING! Wrong type since the type of `array1` decays to `int *`: // main.c:32:15: warning: passing argument 1 of ‘arraytest’ from // incompatible pointer type [-Wincompatible-pointer-types] // main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’ arraytest(array1); // (missing & operator)
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-Werror
ke 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:
int array[2]; // "type safe" ptr to array of size 2 of int: int (*array_p)[2] = &array;
... 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:
sumber
void arraytest(int (*a)[1000])
lebih baik karena kompiler akan error jika ukurannya salah.Forcing type safety on arrays in C
, mencakup Anda titik.Array selalu diteruskan dengan referensi jika Anda menggunakan
a[]
atau*a
:int* printSquares(int a[], int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; } int* printSquares(int *a, int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; }
sumber