Saya memiliki fungsi yang ingin saya ambil, sebagai parameter, array 2D ukuran variabel.
Sejauh ini saya punya ini:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
Dan saya telah mendeklarasikan array di tempat lain dalam kode saya:
double anArray[10][10];
Namun, menelepon myFunction(anArray)
memberi saya kesalahan.
Saya tidak ingin menyalin array ketika saya meneruskannya. Setiap perubahan yang dilakukan myFunction
harus mengubah keadaan anArray
. Jika saya mengerti dengan benar, saya hanya ingin memberikan argumen sebagai pointer ke array 2D. Fungsi perlu menerima array dengan ukuran yang berbeda juga. Jadi misalnya, [10][10]
dan [5][5]
. Bagaimana saya bisa melakukan ini?
c++
arrays
pointers
multidimensional-array
RogerDarwin
sumber
sumber
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Sebut saja sepertiint mat[3][5]; func(mat[0], 3, 5);
Jawaban:
Ada tiga cara untuk meneruskan array 2D ke suatu fungsi:
Parameternya adalah array 2D
Parameter adalah array yang berisi pointer
Parameternya adalah pointer ke pointer
sumber
array
denganarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
.Ukuran tetap
1. Lulus dengan referensi
Dalam C ++ melewatkan array dengan referensi tanpa kehilangan informasi dimensi mungkin yang paling aman, karena orang tidak perlu khawatir pemanggil melewati dimensi yang salah (flag compiler ketika ketidakcocokan). Namun, ini tidak dimungkinkan dengan array dinamis (freestore); ini bekerja untuk otomatis ( array biasanya stack-living ) yaitu dimensi harus diketahui pada waktu kompilasi.
2. Lewati dengan pointer
Setara C dari metode sebelumnya melewati array dengan pointer. Ini tidak boleh disalahartikan dengan melewati tipe pointer yang rusak (3) , yang merupakan metode umum dan populer, meskipun kurang aman daripada yang satu ini tetapi lebih fleksibel. Seperti (1) , gunakan metode ini ketika semua dimensi array diperbaiki dan diketahui pada waktu kompilasi. Perhatikan bahwa saat memanggil fungsi, alamat array harus dikirimkan
process_2d_array_pointer(&a)
dan bukan alamat elemen pertama dengan pembusukanprocess_2d_array_pointer(a)
.Ukuran Variabel
Ini diwarisi dari C tetapi kurang aman, kompiler tidak memiliki cara untuk memeriksa, menjamin bahwa pemanggil melewati dimensi yang diperlukan. Fungsi hanya bank pada apa yang penelepon masuk sebagai dimensi. Ini lebih fleksibel daripada yang di atas karena array dengan panjang yang berbeda dapat diteruskan ke mereka selalu.
Harus diingat bahwa tidak ada yang lewat array langsung ke fungsi dalam C [sementara di C + + mereka dapat dilewatkan sebagai referensi (1) ]; (2) meneruskan pointer ke array dan bukan array itu sendiri. Selalu melewati array apa adanya menjadi operasi pointer-copy yang difasilitasi oleh sifat array yang membusuk menjadi pointer .
3. Lewati (nilai) sebuah pointer ke tipe yang sudah membusuk
Meskipun
int array[][10]
diperbolehkan, saya tidak akan merekomendasikan ini di atas sintaks di atas karena sintaks di atas menjelaskan bahwa pengenalarray
adalah pointer tunggal ke array 10 integer, sementara sintaks ini terlihat seperti array 2D tetapi adalah pointer yang sama untuk sebuah array 10 bilangan bulat. Di sini kita tahu jumlah elemen dalam satu baris (yaitu ukuran kolom, 10 di sini) tetapi jumlah baris tidak diketahui dan karenanya dilewatkan sebagai argumen. Dalam hal ini ada beberapa keamanan karena kompiler dapat menandai ketika pointer ke array dengan dimensi kedua tidak sama dengan 10 dilewatkan. Dimensi pertama adalah bagian yang bervariasi dan dapat dihilangkan. Lihat di sini untuk alasan mengapa hanya dimensi pertama yang boleh dihilangkan.4. Lewati pointer ke pointer
Sekali lagi ada sintaks alternatif
int *array[10]
yang sama denganint **array
. Dalam sintaksis[10]
ini diabaikan karena meluruh menjadi pointer sehingga menjadiint **array
. Mungkin itu hanya isyarat kepada pemanggil bahwa array yang lewat harus memiliki setidaknya 10 kolom, bahkan kemudian diperlukan jumlah baris. Dalam kasus apa pun kompilator tidak menandai pelanggaran panjang / ukuran (hanya memeriksa apakah tipe yang dilewati adalah pointer ke pointer), maka memerlukan jumlah baris dan kolom sebagai parameter masuk akal di sini.Catatan: (4) adalah opsi yang paling tidak aman karena hampir tidak memiliki pemeriksaan tipe apa pun dan yang paling tidak nyaman. Seseorang tidak dapat secara sah melewatkan array 2D ke fungsi ini; C-FAQ mengutuk solusi yang biasa dilakukan
int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
karena berpotensi menyebabkan perilaku yang tidak terdefinisi karena perataan array. Cara yang benar untuk melewatkan array dalam metode ini membawa kita ke bagian yang tidak nyaman, yaitu kita membutuhkan array pointer tambahan (dengan pengganti) dengan masing-masing elemennya menunjuk ke masing-masing baris dari array aktual yang akan dilewati; pengganti ini kemudian diteruskan ke fungsi (lihat di bawah); semua ini untuk mendapatkan pekerjaan yang sama dilakukan sebagai metode di atas yang lebih aman, bersih dan mungkin lebih cepat.Inilah program driver untuk menguji fungsi-fungsi di atas:
sumber
b[i] = a[i];
menjadi, katakanlahb[i] = new int[10];
,. Satu juga dapat membuatb
dialokasikan secara dinamisint **b = int *[5];
dan masih akan berfungsi apa adanya.array[i][j]
kerja pengalamatan ke fungsi di 4) ? Karena telah menerima ptr ke ptr dan tidak tahu nilai dimensi terakhir, mana yang diperlukan untuk melakukan pergeseran untuk pengalamatan yang benar?array[i][j]
hanya aritmatika pointer yaitu nilai pointerarray
, itu akan menambahi
dan merujuk hasilnya sebagaiint*
, yang akan menambahj
dan referensi lokasi itu, membacaint
. Jadi, tidak, tidak perlu tahu dimensi apa pun untuk ini. Tapi, itulah intinya! Kompilator mengambil kata-kata programmer dengan keyakinan dan jika programmer salah, perilaku yang tidak jelas terjadi kemudian. Inilah alasan saya menyebutkan bahwa case 4 adalah opsi yang paling tidak aman.Sebuah modifikasi terhadap saran pertama shengy, Anda dapat menggunakan templat untuk membuat fungsi menerima variabel array multi-dimensi (alih-alih menyimpan array pointer yang harus dikelola dan dihapus):
Pernyataan cetak ada di sana untuk menunjukkan bahwa array dilewatkan oleh referensi (dengan menampilkan alamat variabel)
sumber
%p
untuk mencetak pointer, dan bahkan kemudian, Anda harus melemparkannya kevoid *
, jikaprintf()
tidak memanggil perilaku yang tidak ditentukan. Selain itu, Anda tidak boleh menggunakan alamat (&
) operator saat memanggil fungsi, karena fungsi mengharapkan argumen tipedouble (*)[size_y]
, sedangkan Anda saat ini meneruskannyadouble (*)[10][10]
dandouble (*)[5][5]
.Terkejut bahwa belum ada yang menyebutkan ini, tetapi Anda dapat dengan mudah mencetak pada apa pun 2D yang mendukung semantik [] [].
Ini bekerja dengan
std::vector<std::vector<T>>
struktur data 2D "seperti array", seperti , atau tipe yang ditentukan pengguna untuk memaksimalkan penggunaan kembali kode.sumber
Anda dapat membuat templat fungsi seperti ini:
Kemudian Anda memiliki kedua ukuran dimensi melalui R dan C. Fungsi yang berbeda akan dibuat untuk setiap ukuran array, jadi jika fungsi Anda besar dan Anda menyebutnya dengan berbagai ukuran array yang berbeda, ini mungkin mahal. Anda bisa menggunakannya sebagai pembungkus fungsi seperti ini:
Ini memperlakukan array sebagai satu dimensi, dan menggunakan aritmatika untuk mencari tahu offset indeks. Dalam hal ini, Anda akan mendefinisikan template seperti ini:
sumber
size_t
adalah tipe yang lebih baik untuk indeks array daripadaint
.anArray[10][10]
bukan penunjuk ke penunjuk, itu adalah potongan memori yang berdekatan yang cocok untuk menyimpan 100 nilai tipe ganda, yang dikompilasi oleh kompiler yang tahu cara mengatasinya karena Anda menentukan dimensi. Anda harus meneruskannya ke fungsi sebagai array. Anda dapat menghilangkan ukuran dimensi awal, sebagai berikut:Namun, ini tidak akan membiarkan Anda melewati array dengan dimensi terakhir selain sepuluh.
Solusi terbaik dalam C ++ adalah menggunakan
std::vector<std::vector<double> >
: itu hampir seefisien, dan secara signifikan lebih nyaman.sumber
Array dimensi tunggal meluruh menjadi penunjuk pointer yang menunjuk ke elemen pertama dalam array. Sementara array 2D meluruh ke pointer yang menunjuk ke baris pertama. Jadi, prototipe fungsi seharusnya -
Saya lebih suka
std::vector
daripada array mentah.sumber
Anda dapat melakukan sesuatu seperti ini ...
Output Anda akan sebagai berikut ...
sumber
Berikut adalah contoh vektor vektor
keluaran:
sumber
Kita dapat menggunakan beberapa cara untuk meneruskan array 2D ke suatu fungsi:
Menggunakan pointer tunggal kita harus mengetikkan array 2D.
Menggunakan pointer ganda Dengan cara ini, kami juga mengetikkan array 2d
sumber
Satu hal penting untuk melewati array multidimensi adalah:
First array dimension
tidak perlu ditentukan.Second(any any further)dimension
harus ditentukan.1.Ketika hanya dimensi kedua yang tersedia secara global (baik sebagai makro atau sebagai konstanta global)
2.Menggunakan pointer tunggal : Dalam metode ini, kita harus mengetikkan larik 2D saat beralih ke fungsi.
sumber
Anda dapat menggunakan fasilitas template di C ++ untuk melakukan ini. Saya melakukan sesuatu seperti ini:
masalah dengan pendekatan ini adalah bahwa untuk setiap nilai col yang Anda berikan, definisi fungsi baru dipakai menggunakan template. begitu,
instantiate templat dua kali untuk menghasilkan 2 definisi fungsi (satu di mana col = 3 dan satu di mana col = 5).
sumber
Jika Anda ingin menyampaikan
int a[2][3]
kepadavoid func(int** pp)
Anda perlu langkah-langkah tambahan sebagai berikut.Seperti yang pertama
[2]
dapat ditentukan secara implisit, itu dapat disederhanakan lebih lanjut sebagai.sumber
Jika Anda ingin meneruskan array 2-d berukuran dinamis ke suatu fungsi, menggunakan beberapa pointer bisa bekerja untuk Anda.
sumber
Anda diizinkan menghilangkan dimensi paling kiri dan akhirnya Anda memiliki dua opsi:
Ini sama dengan pointer:
Peluruhan array dimensi N ke pointer ke array dimensi N-1 diizinkan oleh standar C ++ , karena Anda dapat kehilangan dimensi paling kiri dan masih dapat mengakses elemen array dengan benar dengan informasi dimensi N-1.
Detail di sini
Meskipun, array dan pointer tidak sama : sebuah array dapat meluruh menjadi sebuah pointer, tetapi sebuah pointer tidak membawa status tentang ukuran / konfigurasi data yang ditunjuknya.
A
char **
adalah penunjuk ke blok memori yang berisi pointer karakter , yang dengan sendirinya menunjuk ke blok memori karakter. Achar [][]
adalah blok memori tunggal yang berisi karakter. Ini berdampak pada bagaimana kompiler menerjemahkan kode dan bagaimana kinerja akhir akan.Sumber
sumber