Pertanyaan Mengapa C ++ mendukung penugasan memberwise dari array dalam struct, tetapi tidak secara umum?


Saya memahami bahwa penugasan server yang berpihak tidak didukung, sehingga hal-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, mencari tahu bahwa tujuan bahasa adalah menyediakan kerangka terbuka, dan membiarkan pengguna memutuskan bagaimana mengimplementasikan sesuatu seperti menyalin array.

Namun, yang berikut ini berfungsi:

struct myStruct {int num[3];};
myStruct struct1={{1,2,3}};
myStruct struct2;
struct2 = struct1;

Array num[3] adalah anggota yang ditugaskan dari instance di struct1, menjadi contohnya di struct2.

Mengapa penunjukan anggota-bijaksana dari array didukung untuk struct, tetapi tidak secara umum?

sunting: Roger PateKomentar di utas std :: string di struct - masalah Copy / assignment? nampaknya menunjuk ke arah umum dari jawaban, tetapi saya tidak cukup tahu untuk mengkonfirmasinya sendiri.

sunting 2: Banyak tanggapan luar biasa. saya memilih Luther BlissettKarena saya kebanyakan bertanya-tanya tentang alasan filosofis atau historis di balik perilaku, tetapi James McNellisReferensi ke dokumentasi spesifikasi terkait juga berguna.


75
2017-08-09 03:10


asal


Jawaban:


Inilah pendapat saya tentang hal ini:

Pengembangan Bahasa C menawarkan beberapa wawasan dalam evolusi tipe array dalam C:

Saya akan mencoba untuk menguraikan hal array:

Pelopor C B dan BCPL tidak memiliki tipe array yang berbeda, deklarasi seperti:

auto V[10] (B)
or 
let V = vec 10 (BCPL)

akan menyatakan V sebagai penunjuk (untyped) yang diinisialisasi untuk menunjuk ke wilayah yang tidak digunakan dari 10 "kata" memori. B sudah digunakan * untuk dereferencing pointer dan memiliki []notasi tangan pendek, *(V+i) berarti V[i], sama seperti di C / C ++ hari ini. Namun, V bukan sebuah array, itu masih merupakan pointer yang harus menunjuk ke beberapa memori. Ini menyebabkan 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 B, konsep BCPL dari array sebagai pointer, ini akan membutuhkan name bidang berisi penunjuk yang harus diinisialisasi saat runtime ke wilayah memori dari 14 byte dalam struct. Masalah inisialisasi / tata letak akhirnya diselesaikan dengan memberikan array perlakuan khusus: Compiler akan melacak lokasi array dalam struktur, pada stack dll tanpa benar-benar membutuhkan pointer ke data untuk terwujud, kecuali dalam ekspresi yang melibatkan array. Perawatan ini memungkinkan hampir semua kode B untuk tetap berjalan dan merupakan sumber dari "array dikonversi ke penunjuk jika Anda melihatnya" aturan. Ini adalah hack kompatiblitas, yang ternyata sangat berguna, karena memungkinkan array ukuran terbuka, dll.

Dan inilah tebakan saya mengapa array tidak dapat ditetapkan: Karena array adalah pointer di B, Anda bisa menulis:

auto V[10];
V=V+5;

untuk me-rebase "array". Ini sekarang tidak ada artinya, karena basis dari variabel array tidak lagi bernilai. Jadi ini tidak diijinkan, yang membantu menangkap beberapa program yang melakukan rebase ini pada array yang dinyatakan. Dan kemudian gagasan ini terjebak: Karena array tidak pernah dirancang untuk menjadi kelas pertama dari sistem tipe C, mereka kebanyakan diperlakukan sebagai binatang istimewa yang menjadi penunjuk jika Anda menggunakannya. Dan dari sudut pandang tertentu (yang mengabaikan bahwa C-array adalah hack gagal), melarang penugasan array masih masuk akal: Sebuah array terbuka atau parameter fungsi array diperlakukan sebagai pointer tanpa informasi ukuran. Compiler tidak memiliki informasi untuk menghasilkan penugasan array untuk mereka dan penunjukan penunjuk diperlukan untuk alasan kompatibilitas. Memperkenalkan penugasan array untuk array yang dideklarasikan akan mengenalkan bug meskipun hasil yang palsu (adalah penugasan penunjuk = ba atau copy elementwise?) Dan masalah lainnya (bagaimana Anda menyampaikan larik berdasarkan nilai?) Tanpa benar-benar menyelesaikan masalah - cukup buat semuanya eksplisit dengan memcpy!

/* 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 tugas struct ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Meskipun catatan adalah tipe-tipe berbeda dalam C, tidak mungkin untuk menugaskan mereka di awal K & R C. Anda harus menyalinnya dengan anggota-anggotanya dengan memcpy dan Anda hanya dapat memberikan pointer kepada mereka sebagai parameter fungsi. Assigment (dan parameter passing) sekarang hanya didefinisikan sebagai memcpy dari memori baku struct dan karena ini tidak dapat memecahkan kode exsisting, itu mudah dilimpahkan. Sebagai efek samping yang tidak diinginkan, ini secara implisit memperkenalkan beberapa jenis penugasan, tetapi ini terjadi di suatu tempat di dalam struktur, jadi ini tidak bisa benar-benar memperkenalkan masalah dengan cara array digunakan.


38
2017-08-09 07:16



Mengenai operator penugasan, standar C ++ menyatakan hal-hal berikut (C ++ 03 §5.17 / 1):

Ada beberapa operator penugasan ... semua membutuhkan lnilai yang dapat dimodifikasi sebagai operan kiri mereka

Array bukan lvalue yang dapat dimodifikasi.

Namun, tugas untuk objek tipe kelas didefinisikan secara khusus (§5.17 / 4):

Penugasan ke objek kelas ditentukan oleh operator penugasan salinan.

Jadi, kita melihat untuk melihat apa operator penetapan salinan yang dinyatakan secara implisit untuk suatu kelas (§12.8 / 13):

Operator penugasan fotokopi yang ditetapkan secara implisit untuk kelas X melakukan penugasan memberorientasi dari subobjectnya. ... Setiap subobjek ditetapkan dengan cara yang sesuai dengan jenisnya:
  ...
  - jika sub-objek adalah larik, setiap elemen ditetapkan, dengan cara yang sesuai dengan jenis elemen
  ...

Jadi, untuk objek tipe kelas, array disalin dengan benar. Perhatikan bahwa jika Anda memberikan operator penetapan salinan yang ditetapkan oleh pengguna, Anda tidak dapat mengambil keuntungan dari ini, dan Anda harus menyalin elemen-demi-elemen array.


Alasannya serupa di C (C99 §6.5.16 / 2):

Operator penugasan harus memiliki nilai l yang termodifikasi sebagai operan kirinya.

Dan §6.3.2.1 / 1:

Nilai yang termodifikasi adalah nilai yang tidak memiliki tipe larik ... [batasan lain mengikuti]

Di C, tugas jauh lebih sederhana daripada di C ++ (§6.5.16.1 / 2):

Dalam penugasan sederhana (=), nilai operan kanan dikonversi ke jenis   ekspresi tugas dan menggantikan nilai yang disimpan dalam objek yang ditunjuk oleh kiri   operan.

Untuk penugasan objek struct-type, operand kiri dan kanan harus memiliki tipe yang sama, sehingga nilai operan kanan hanya disalin ke operan kiri.


24
2017-08-09 03:42



Di tautan ini: http://www2.research.att.com/~bs/bs_faq2.html  ada bagian tentang penetapan larik:

Dua masalah mendasar dengan array adalah itu

  • sebuah array tidak tahu ukurannya sendiri
  • nama array mengkonversi ke pointer ke elemen pertama pada provokasi terkecil

Dan saya pikir ini adalah perbedaan mendasar antara array dan struct. Variabel array adalah elemen data tingkat rendah dengan pengetahuan diri terbatas. Secara mendasar, itu adalah sepotong memori dan cara untuk mengindeks ke dalamnya.

Jadi, compiler tidak bisa membedakan antara int a [10] dan int b [20].

Namun, struktur tidak memiliki ambiguitas yang sama.


2
2017-08-09 03:40



Saya tahu, semua orang yang menjawab adalah pakar dalam C / C ++. Tapi saya pikir, inilah alasan utamanya.

num2 = num1;

Di sini Anda mencoba mengubah alamat dasar array, yang tidak diizinkan.

dan tentu saja, struct2 = struct1;

Di sini, struct1 objek ditugaskan untuk objek lain.


0
2017-08-09 12:29