Standar C11 mengatakan array, baik ukuran dan panjang variabel "harus memiliki nilai lebih besar dari nol." Apa justifikasi untuk tidak mengizinkan panjang 0?
Khusus untuk array panjang variabel, sangat masuk akal untuk memiliki ukuran nol setiap sesekali. Ini juga berguna untuk array statis ketika ukurannya dari opsi konfigurasi makro atau membangun.
Menariknya GCC (dan dentang) menyediakan ekstensi yang memungkinkan array panjang nol. Java juga memungkinkan array dengan panjang nol.
struct { int p[1],q[1]; } foo; int *pp = p+1;
,pp
akan menjadi pointer yang sah, tetapi*pp
tidak akan memiliki alamat yang unik. Mengapa logika yang sama tidak dapat berlaku dengan array nol panjang? Katakan bahwa diberikanint q[0];
dalam struktur ,q
akan merujuk ke alamat yang validitasnya akan seperti yang ada padap+1
contoh di atas.Jawaban:
Masalah yang akan saya pertaruhkan adalah bahwa array C hanyalah pointer ke awal alokasi memori yang dialokasikan. Memiliki ukuran 0 berarti Anda memiliki pointer ke ... tidak ada? Anda tidak dapat memiliki apa-apa, jadi harus ada sesuatu yang sewenang-wenang yang dipilih. Anda tidak dapat menggunakan
null
, karena dengan demikian array 0 panjang Anda akan terlihat seperti pointer nol. Dan pada saat itu setiap implementasi yang berbeda akan memilih perilaku sewenang-wenang yang berbeda, yang menyebabkan kekacauan.sumber
sizeof
0, dan bagaimana itu akan menyebabkan masalah. Semua itu bisa dijelaskan dengan menggunakan konsep dan terminologi yang tepat tanpa kehilangan kejelasan atau kejelasan. Mencampur array dan pointer hanya berisiko menyebarkan array = pointer kesalahpahaman (yang lebih penting dalam konteks lain) tanpa manfaat.Mari kita lihat bagaimana sebuah array biasanya diletakkan dalam memori:
Perhatikan bahwa tidak ada objek terpisah yang bernama
arr
yang menyimpan alamat elemen pertama; ketika array muncul dalam ekspresi, C menghitung alamat elemen pertama sesuai kebutuhan.Jadi, mari kita berpikir tentang hal ini: array 0-elemen akan memiliki tidak ada penyimpanan set samping untuk itu, berarti tidak ada yang menghitung alamat array yang dari (dengan kata lain, tidak ada objek pemetaan untuk pengenal). Itu seperti mengatakan, "Saya ingin membuat
int
variabel yang tidak menggunakan memori." Ini operasi yang tidak masuk akal.Edit
Array Java adalah hewan yang sepenuhnya berbeda dari array C dan C ++; mereka bukan tipe primitif, tetapi tipe referensi yang berasal
Object
.Edit 2
Poin yang muncul dalam komentar di bawah ini - batasan "lebih besar dari 0" hanya berlaku untuk array di mana ukurannya ditentukan melalui ekspresi konstan ;
sebuah VLA dibolehkan memiliki panjang 0Mendeklarasikan VLA dengan ekspresi non-konstan bernilai 0 bukanlah pelanggaran kendala, tetapi itu memunculkan perilaku yang tidak terdefinisi.Sudah jelas bahwa VLA adalah hewan yang berbeda dari array biasa
, dan implementasinya dapat memungkinkan untuk ukuran 0. Mereka tidak dapat dideklarasikanstatic
atau pada lingkup file, karena ukuran objek tersebut harus diketahui sebelum program dimulai.Juga tidak ada artinya bahwa pada C11, implementasi tidak diperlukan untuk mendukung VLA.
sumber
T a[0]
sebagaiT *a
, tetapi mengapa tidak menggunakan sajaT *a
?struct
hack. Saya tidak pernah menggunakannya secara pribadi; tidak pernah bekerja pada masalah yang membutuhkanstruct
tipe berukuran bervariasi .Anda biasanya ingin array ukuran nol (pada kenyataannya variabel) Anda mengetahui ukurannya pada saat dijalankan. Kemudian kemas dalam
struct
dan gunakan anggota array fleksibel , seperti misalnya:Tentunya anggota array fleksibel harus menjadi yang terakhir
struct
dan Anda perlu memiliki sesuatu sebelumnya. Seringkali itu akan menjadi sesuatu yang terkait dengan panjang runtime yang ditempati anggota array fleksibel.Tentu saja Anda akan mengalokasikan:
AFAIK, ini sudah mungkin di C99, dan itu sangat berguna.
BTW, anggota array fleksibel tidak ada di C ++ (karena akan sulit untuk menentukan kapan dan bagaimana mereka harus dibangun & dihancurkan). Namun lihat masa depan std :: dynarray
sumber
Jika ekspresi
type name[count]
ditulis dalam beberapa fungsi maka Anda memberi tahu kompiler C untuk mengalokasikan padasizeof(type)*count
byte frame tumpukan dan menghitung alamat elemen pertama dalam array.Jika ekspresi
type name[count]
ditulis di luar semua definisi fungsi dan struct maka Anda memberi tahu kompiler C untuk mengalokasikan padasizeof(type)*count
byte segmen data dan menghitung alamat elemen pertama dalam array.name
sebenarnya adalah objek konstan yang menyimpan alamat elemen pertama dalam array dan setiap objek yang menyimpan alamat dari beberapa memori disebut pointer, jadi ini adalah alasan Anda memperlakukanname
sebagai pointer daripada array. Perhatikan bahwa array dalam C hanya dapat diakses melalui pointer.Jika
count
adalah ekspresi konstan yang mengevaluasi ke nol maka Anda memberitahu kompiler C untuk mengalokasikan nol byte baik pada frame stack atau segmen data dan mengembalikan alamat elemen pertama dalam array, tetapi masalah dalam melakukan ini adalah bahwa elemen pertama dari array nol panjang tidak ada dan Anda tidak dapat menghitung alamat dari sesuatu yang tidak ada.Ini rasional bahwa elemen no.
count+1
tidak ada dalamcount
array -length, jadi ini adalah alasan bahwa kompiler C melarang untuk mendefinisikan array zero-length sebagai variabel di dalam dan di luar fungsi, karena apa isi dariname
itu? Alamatname
toko apa sebenarnya?Jika
p
adalah pointer maka ekspresip[n]
sama dengan*(p + n)
Di mana tanda bintang * dalam ekspresi kanan adalah operasi dereferensi penunjuk, yang berarti mengakses memori yang ditunjuk oleh
p + n
atau mengakses memori yang alamatnya disimpanp + n
, di manap + n
penunjuk ekspresi, ia mengambil alamatp
dan menambahkan ke alamat ini nomor,n
kalikan dengan ukuran dari jenis pointerp
.Apakah mungkin menambahkan alamat dan nomor?
Ya itu mungkin, karena alamat adalah bilangan bulat unsigned yang biasanya ditampilkan dalam notasi heksadesimal.
sumber
N
telahN+1
mengaitkan alamat, yang pertamaN
mengidentifikasi byte unik dan yang terakhirN
dimana setiap titik hanya melewati salah satu byte tersebut. Definisi seperti itu akan bekerja dengan baik bahkan dalam kasus degenerasi di manaN
0.Jika Anda ingin pointer ke alamat memori, nyatakan satu. Array sebenarnya menunjuk pada sejumlah memori yang telah Anda pesan. Array peluruhan ke pointer ketika diteruskan ke fungsi, tetapi jika memori yang mereka tuju ada di heap, tidak ada masalah. Tidak ada alasan untuk mendeklarasikan ukuran array nol.
sumber
Dari masa C89 asli, ketika Standar C menetapkan bahwa sesuatu memiliki Perilaku Tidak Terdefinisi, yang dimaksud adalah "Lakukan apa pun yang akan membuat implementasi pada platform target tertentu yang paling cocok untuk tujuan yang dimaksudkan". Penulis Standar tidak ingin mencoba menebak perilaku apa yang paling cocok untuk tujuan tertentu. Implementasi C89 yang ada dengan ekstensi VLA mungkin memiliki perilaku yang berbeda, tetapi logis, ketika diberi ukuran nol (misalnya beberapa mungkin telah memperlakukan array sebagai ekspresi alamat yang menghasilkan NULL, sementara yang lain memperlakukannya sebagai ekspresi alamat yang mungkin sama dengan alamat variabel arbitrer lain, tetapi bisa dengan aman menambahkan nol tanpa menjebaknya). Jika ada kode yang bergantung pada perilaku yang berbeda, penulis Standar tidak akan
Daripada mencoba menebak implementasi yang mungkin dilakukan, atau menyarankan bahwa perilaku apa pun harus dianggap lebih unggul daripada yang lain, penulis Standar hanya mengizinkan pelaksana untuk menggunakan penilaian dalam menangani kasus itu sesuai dengan keinginan mereka. Implementasi yang menggunakan malloc () di belakang layar mungkin memperlakukan alamat array sebagai NULL (jika malloc ukuran-nol menghasilkan nol), yang menggunakan perhitungan stack-address mungkin menghasilkan pointer yang cocok dengan beberapa alamat variabel lain, dan beberapa implementasi lain mungkin melakukan hal-hal lain. Saya tidak berpikir mereka berharap bahwa penulis kompiler akan keluar dari jalan mereka untuk membuat kasus sudut nol berperilaku dengan cara yang tidak berguna sengaja.
sumber