Saya memahami bahwa penetapan array berdasarkan anggota tidak didukung, sehingga hal berikut tidak akan berfungsi:
int num1[3] = {1,2,3};
int num2[3];
num2 = num1; // "error: invalid array assignment"
Saya baru saja menerima ini sebagai fakta, membayangkan bahwa tujuan bahasa adalah untuk menyediakan kerangka kerja terbuka, dan membiarkan pengguna memutuskan bagaimana mengimplementasikan sesuatu seperti menyalin sebuah array.
Namun, berikut ini berhasil:
struct myStruct { int num[3]; };
struct myStruct struct1 = {{1,2,3}};
struct myStruct struct2;
struct2 = struct1;
Array num[3]
adalah anggota-bijaksana yang ditetapkan dari instansinya di struct1
, ke dalam instansinya di struct2
.
Mengapa penugasan array yang bijaksana didukung untuk struct, tetapi tidak secara umum?
edit : Komentar Roger Pate di utas std :: string dalam struct - Masalah salin / tugas? sepertinya menunjuk ke arah umum dari jawabannya, tetapi saya tidak cukup tahu untuk memastikannya sendiri.
sunting 2 : Banyak tanggapan yang sangat baik. Saya memilih Luther Blissett karena saya sebagian besar bertanya-tanya tentang alasan filosofis atau historis di balik perilaku tersebut, tetapi referensi James McNellis ke dokumentasi spesifikasi terkait juga berguna.
memcpy()
atau serupa.boost::array
( boost.org/doc/libs/release/doc/html/array.html ) dan sekarangstd::array
( en.cppreference.com/w/cpp/container/array ) adalah alternatif yang kompatibel dengan STL untuk array C tua yang berantakan. Mereka mendukung tugas menyalin.Jawaban:
Inilah pendapat saya:
Pengembangan Bahasa C menawarkan beberapa wawasan tentang evolusi tipe array di C:
Saya akan mencoba menguraikan hal array:
Pendahulu C B dan BCPL tidak memiliki tipe larik yang berbeda, deklarasi seperti:
auto V[10] (B) or let V = vec 10 (BCPL)
akan mendeklarasikan V sebagai pointer (tanpa tipe) yang diinisialisasi untuk menunjuk ke wilayah 10 "kata" memori yang tidak digunakan. B sudah digunakan
*
untuk pointer dereferencing dan memiliki[]
notasi tangan pendek,*(V+i)
dimaksudkanV[i]
, seperti di C / C ++ hari ini. Bagaimanapun,V
ini bukanlah sebuah array, itu masih sebuah pointer yang menunjuk ke beberapa memori. Ini menimbulkan masalah ketika Dennis Ritchie mencoba memperpanjang B dengan tipe struct. Dia ingin array menjadi bagian dari struct, seperti di C hari ini:struct { int inumber; char name[14]; };
Tetapi dengan konsep array B, BCPL sebagai pointer, ini akan membutuhkan
name
field untuk memuat pointer yang harus diinisialisasi pada saat runtime ke wilayah memori 14 byte dalam struct. Masalah inisialisasi / tata letak pada akhirnya diselesaikan dengan memberikan perlakuan khusus kepada array: Kompilator akan melacak lokasi array dalam struktur, di tumpukan, dll. Tanpa benar-benar memerlukan penunjuk ke data untuk terwujud, kecuali dalam ekspresi yang melibatkan array. Perlakuan ini memungkinkan hampir semua kode B untuk tetap berjalan dan merupakan sumber dari aturan "larik dikonversi ke penunjuk jika Anda melihatnya" . Ini adalah peretasan kompatibilitas, yang ternyata sangat berguna, karena memungkinkan array berukuran terbuka, dll.Dan inilah tebakan saya mengapa array tidak dapat ditetapkan: Karena array adalah pointer di B, Anda cukup menulis:
auto V[10]; V=V+5;
untuk me-rebase sebuah "array". Ini sekarang tidak ada artinya, karena basis variabel array bukan lagi nilai l. Jadi tugas ini tidak diizinkan, yang membantu menangkap beberapa program yang melakukan pemeringkatan ini pada array yang dideklarasikan. Dan kemudian gagasan ini macet: Karena array tidak pernah dirancang untuk menjadi kelas pertama yang dikutip dari sistem tipe C, mereka kebanyakan diperlakukan sebagai binatang khusus yang menjadi penunjuk jika Anda menggunakannya. Dan dari sudut pandang tertentu (yang mengabaikan bahwa C-array adalah hack yang gagal), pelarangan penetapan array masih masuk akal: Array terbuka atau parameter fungsi array diperlakukan sebagai penunjuk tanpa informasi ukuran. Kompilator tidak memiliki informasi untuk menghasilkan penetapan larik untuk mereka dan penugasan penunjuk diperlukan untuk alasan kompatibilitas.
/* Example how array assignment void make things even weirder in C/C++, if we don't want to break existing code. It's actually better to leave things as they are... */ typedef int vec[3]; void f(vec a, vec b) { vec x,y; a=b; // pointer assignment x=y; // NEW! element-wise assignment a=x; // pointer assignment x=a; // NEW! element-wise assignment }
Ini tidak berubah ketika revisi C pada tahun 1978 menambahkan penetapan struct ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Meskipun record adalah tipe yang berbeda di C, itu tidak mungkin untuk menetapkannya di awal K&R C. Anda harus menyalinnya dari segi anggota dengan memcpy dan Anda hanya dapat meneruskan pointer ke mereka sebagai parameter fungsi. Assigment (dan parameter passing) sekarang hanya didefinisikan sebagai memcpy dari memori mentah struct dan karena ini tidak dapat merusak kode yang sudah ada, maka dengan mudah diadpot. Sebagai efek samping yang tidak disengaja, ini secara implisit memperkenalkan beberapa jenis penugasan array, tetapi ini terjadi di suatu tempat di dalam struktur, jadi ini tidak bisa benar-benar memperkenalkan masalah dengan cara array digunakan.
sumber
int[10] c;
untuk membuat lvaluec
berperilaku sebagai larik yang terdiri dari sepuluh item, bukan sebagai penunjuk ke item pertama dari larik sepuluh item. Ada beberapa situasi di mana berguna untuk dapat membuat typedef yang mengalokasikan ruang saat digunakan untuk variabel, tetapi meneruskan penunjuk saat digunakan sebagai argumen fungsi, tetapi ketidakmampuan untuk memiliki nilai tipe array adalah kelemahan semantik yang signifikan dalam bahasa tersebut.Mengenai operator penugasan, standar C ++ mengatakan yang berikut (C ++ 03 §5.17 / 1):
Array bukanlah nilai l yang dapat dimodifikasi.
Namun, tugas ke objek tipe kelas didefinisikan secara khusus (§5.17 / 4):
Jadi, kita melihat untuk melihat apa yang dilakukan operator penugasan salinan yang dideklarasikan secara implisit untuk sebuah kelas (§12.8 / 13):
Jadi, untuk objek tipe kelas, array disalin dengan benar. Perhatikan bahwa jika Anda menyediakan operator penugasan salinan yang dideklarasikan pengguna, Anda tidak dapat memanfaatkan ini, dan Anda harus menyalin array elemen demi elemen.
Alasannya serupa di C (C99 §6.5.16 / 2):
Dan §6.3.2.1 / 1:
Di C, penugasan jauh lebih sederhana daripada di C ++ (§6.5.16.1 / 2):
Untuk penugasan objek tipe struct, operan kiri dan kanan harus memiliki tipe yang sama, jadi nilai operan kanan cukup disalin ke operan kiri.
sumber
=
memerlukan nilai r pada RHS dan larik tidak bisa menjadi nilai r ! Konversi lvalue-to-rvalue dilarang untuk array, diganti dengan lvalue-to-pointer.static_cast
tidak lebih baik dalam membuat nilai r karena didefinisikan dalam istilah yang sama.Di tautan ini: http://www2.research.att.com/~bs/bs_faq2.html ada bagian tentang penetapan array:
Dua masalah mendasar dengan array adalah itu
Dan saya pikir inilah perbedaan mendasar antara array dan struct. Variabel array adalah elemen data tingkat rendah dengan pengetahuan diri yang terbatas. Pada dasarnya, ini adalah bagian dari memori dan cara untuk mengindeksnya.
Jadi, kompilator tidak bisa membedakan antara int a [10] dan int b [20].
Struktur, bagaimanapun, tidak memiliki ambiguitas yang sama.
sumber
sizeof(a)
vs.sizeof(b)
atau meneruskana
kevoid f(int (&)[20]);
.Saya tahu, semua yang menjawab adalah pakar C / C ++. Tapi saya pikir, inilah alasan utamanya.
num2 = num1;
Di sini Anda mencoba mengubah alamat dasar larik, yang tidak diizinkan.
dan tentu saja, struct2 = struct1;
Di sini, objek struct1 ditugaskan ke objek lain.
sumber
num2 = num1
akan berperilaku sangat baik. Elemen-elemen darinum2
akan memiliki nilai yang sama dengan elemen yang terkaitnum1
.Alasan lain tidak ada upaya lebih lanjut dilakukan untuk daging sapi sampai array di C mungkin bahwa array tugas tidak akan yang berguna. Meskipun dapat dengan mudah dicapai di C dengan membungkusnya dalam sebuah struct (dan alamat struct dapat dengan mudah dilemparkan ke alamat array atau bahkan alamat elemen pertama array untuk diproses lebih lanjut) fitur ini jarang digunakan. Salah satu alasannya adalah bahwa array dengan ukuran berbeda tidak kompatibel sehingga membatasi manfaat penugasan atau, terkait, meneruskan fungsi berdasarkan nilai.
Sebagian besar fungsi dengan parameter array dalam bahasa di mana array adalah tipe kelas satu ditulis untuk array dengan ukuran arbitrer. Fungsi ini kemudian biasanya mengulangi jumlah elemen yang diberikan, sebuah informasi yang disediakan oleh array. (Dalam C ungkapannya, tentu saja, untuk melewatkan pointer dan jumlah elemen yang terpisah.) Sebuah fungsi yang menerima array hanya dengan satu ukuran tertentu tidak diperlukan sesering mungkin, jadi tidak banyak yang terlewat. (Ini berubah ketika Anda dapat menyerahkannya ke compiler untuk menghasilkan fungsi terpisah untuk ukuran array apa pun yang terjadi, seperti pada template C ++; inilah alasan mengapa
std::array
berguna.)sumber