Mengapa dimensi array merupakan bagian dari tipenya?

14

Saat membaca buku C ++ Primer, saya menemukan pernyataan ini: "Jumlah elemen dalam array adalah bagian dari tipe array." Jadi saya ingin mencari tahu menggunakan kode berikut:

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

Dan yang menarik, hasil dari typeid pada kedua array menunjukkan bahwa keduanya berbeda.

  • Apa yang terjadi di balik layar?
  • Mengapa array perlu memiliki tipe yang mencakup ukurannya? Apakah hanya karena ukurannya tidak boleh berubah?
  • Bagaimana ini akan memengaruhi membandingkan array?

Hanya ingin bisa memahami konsep secara mendalam.

gurita
sumber
3
Ini tidak sepenuhnya diperlukan untuk memasukkan informasi ukuran dalam jenis, tetapi ini berguna
byxor
Setiap tutorial tentang array akan menjelaskan (1). Saya tidak yakin apa yang Anda maksud dengan (3), karena tidak ada cara builtin untuk membandingkan array.
HolyBlackCat

Jawaban:

20

Apa yang terjadi di balik layar?

Menurut definisi, yang dialokasikan secara non-dinamis adalah sebuah wadah berukuran tetap dari elemen-elemen homogen. Array Nelemen tipe Tdiletakkan dalam memori sebagai urutan yang berdekatan dari Nobjek tipe T.


Mengapa array harus memiliki tipe yang mencakup ukurannya?

Saya tidak percaya itu "perlu" untuk jenis array untuk memasukkan ukurannya - sebagai fakta, Anda dapat menggunakan pointer untuk merujuk ke urutan Tobjek yang berdekatan . Pointer tersebut akan kehilangan informasi ukuran tentang array.

Namun, hal ini berguna untuk dimiliki. Ini meningkatkan keamanan jenis, dan menyandikan informasi berguna pada waktu kompilasi yang dapat digunakan dalam berbagai cara. Sebagai contoh, Anda dapat menggunakan referensi-ke-array untuk membebani array dengan ukuran yang berbeda

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

atau untuk mengetahui ukuran array sebagai ekspresi konstan

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

Bagaimana ini akan memengaruhi membandingkan array?

Sebenarnya tidak.

Anda tidak dapat membandingkan array gaya-C dengan cara yang sama seperti membandingkan dua angka (misalnya intobjek). Anda harus menulis semacam perbandingan leksikografis, dan memutuskan apa artinya untuk koleksi dengan ukuran berbeda. std::vector<T>menyediakan itu , dan logika yang sama dapat diterapkan ke array.


Bonus: C ++ 11 dan di atasnya menyediakan std::array, yang merupakan pembungkus di sekitar larik C-style dengan antarmuka seperti wadah. Itu harus disukai untuk array gaya-C karena lebih konsisten dengan wadah lain (misalnya std::vector<T>), dan juga mendukung perbandingan leksikografis di luar kotak.

Vittorio Romeo
sumber
2
"Anda harus menulis semacam perbandingan leksikografis, dan memutuskan apa artinya untuk koleksi dengan ukuran berbeda." Anda bisa menggunakan std::equal(via std::begindan std::endyang didefinisikan untuk array). Dalam hal ini, array ukuran yang berbeda tidak sama.
Stu
3
Mungkin perlu dicatat bahwa range-based untuk loop yang range-nya adalah array perlu membaca ukuran array pada waktu kompilasi - ini sedikit lebih subtil karena (untungnya!) Orang tidak pernah menulis tipe array dalam contoh ini, tetapi tampaknya muncul lebih banyak daripada kelebihan beban berdasarkan ukuran.
Milo Brandt
8

Jumlah ruang yang dialokasikan untuk objek saat Anda membuatnya tergantung sepenuhnya pada tipenya. Alokasi yang saya bicarakan bukan alokasi dari newatau malloc, tetapi ruang yang dialokasikan sehingga Anda dapat menjalankan konstruktor dan menginisialisasi objek Anda.

Jika Anda memiliki struct yang didefinisikan sebagai (misalnya)

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

Kemudian ketika Anda membangun objek:

A a{'a', 'b'};

Anda dapat menganggap proses membangun objek sebagai proses:

  • Alokasikan 2 byte ruang (di stack, tapi di mana tidak masalah untuk contoh ini)
  • Jalankan konstruktor objek (dalam hal ini salin 'a'dan 'b'ke objek)

Penting untuk dicatat bahwa 2 byte ruang yang dibutuhkan sepenuhnya ditentukan oleh jenis objek, argumen ke fungsi tidak masalah. Jadi, untuk sebuah array prosesnya sama, kecuali sekarang jumlah ruang yang dibutuhkan tergantung pada jumlah elemen dalam array.

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

Jadi jenis adan bharus mencerminkan fakta yang amembutuhkan ruang yang cukup untuk 1 karakter dan bkebutuhan ruang yang cukup untuk 5 karakter. Itu berarti bahwa ukuran array ini tidak dapat tiba-tiba berubah, setelah array 5-elemen dibuat itu selalu array 5-elemen. Untuk memiliki objek mirip-array yang ukurannya dapat bervariasi, Anda perlu alokasi memori dinamis, yang harus dicakup buku Anda pada titik tertentu.

SirGuy
sumber
0

Itu karena alasan internal untuk perpustakaan runtime. Jika Anda mempertimbangkan pernyataan berikut, misalnya:

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

Maka menjadi jelas apa masalahnya: Misalnya, pengalamatan unsigned int *harus berkaitan dengan sizeof operatoratau pengalamatan unsigned int.

Ada penjelasan yang lebih rinci untuk sisa dari apa yang Anda lihat di sini, tetapi sebagian besar merupakan rekapitulasi dari apa yang tercakup dalam Bahasa Pemrograman C, Edisi ke-2 oleh Kernighan dan Ritchie mengenai program yang mencetak teks bahasa biasa dari jenis yang dinyatakan. tali.

CR Ward
sumber