Untuk menentukan ukuran array Anda dalam byte, Anda dapat menggunakan sizeof
operator:
int a[17];size_t n =sizeof(a);
Di komputer saya, panjang int adalah 4 byte, jadi n adalah 68.
Untuk menentukan jumlah elemen dalam array, kita dapat membagi ukuran total array dengan ukuran elemen array. Anda bisa melakukan ini dengan tipenya, seperti ini:
int a[17];size_t n =sizeof(a)/sizeof(int);
dan dapatkan jawaban yang tepat (68/4 = 17), tetapi jika jenis
aperubahan Anda akan memiliki bug jahat jika Anda lupa untuk mengubah sizeof(int)juga.
Jadi pembagi yang disukai adalah sizeof(a[0])atau setara sizeof(*a), ukuran elemen pertama array.
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Keuntungan lain adalah bahwa Anda sekarang dapat dengan mudah parameterisasi nama array di makro dan dapatkan:
#define NELEMS(x)(sizeof(x)/sizeof((x)[0]))int a[17];size_t n = NELEMS(a);
Kode yang dihasilkan akan sama, karena kompiler mengetahui tipe * int_arr pada waktu kompilasi (dan karenanya nilai sizeof (* int_arr)). Ini akan menjadi konstanta, dan kompiler dapat mengoptimalkannya.
Mark Harrison
10
Seharusnya demikian halnya dengan semua kompiler, karena hasil sizeof didefinisikan sebagai konstanta waktu kompilasi.
Mark Harrison
451
Penting : Jangan berhenti membaca di sini, baca jawaban berikutnya! Ini hanya berfungsi untuk array pada stack , mis. Jika Anda menggunakan malloc () atau mengakses parameter fungsi, Anda kurang beruntung. Lihat di bawah.
Markus
7
Untuk pemrograman Windows API dalam C atau C ++, ada ARRAYSIZEmakro yang didefinisikan di WinNT.h(yang ditarik oleh header lainnya). Jadi pengguna WinAPI tidak perlu mendefinisikan makro mereka sendiri.
Lumi
17
@ Markus berfungsi untuk variabel apa pun yang memiliki tipe array; ini tidak harus "on the stack". Misalnya static int a[20];. Tetapi komentar Anda bermanfaat bagi pembaca yang mungkin tidak menyadari perbedaan antara array dan pointer.
MM
808
The sizeofcara adalah cara yang tepat IFF Anda berurusan dengan array tidak diterima sebagai parameter. Array yang dikirim sebagai parameter ke suatu fungsi diperlakukan sebagai pointer, sehingga sizeofakan mengembalikan ukuran pointer, bukan array.
Dengan demikian, fungsi-fungsi di dalam metode ini tidak berfungsi. Sebagai gantinya, selalu berikan parameter tambahan yang size_t sizemenunjukkan jumlah elemen dalam array.
Uji:
#include<stdio.h>#include<stdlib.h>void printSizeOf(int intArray[]);void printLength(int intArray[]);int main(int argc,char* argv[]){intarray[]={0,1,2,3,4,5,6};
printf("sizeof of array: %d\n",(int)sizeof(array));
printSizeOf(array);
printf("Length of array: %d\n",(int)(sizeof(array)/sizeof(array[0])));
printLength(array);}void printSizeOf(int intArray[]){
printf("sizeof of parameter: %d\n",(int)sizeof(intArray));}void printLength(int intArray[]){
printf("Length of parameter: %d\n",(int)(sizeof(intArray)/sizeof(intArray[0])));}
Output (dalam OS Linux 64-bit):
sizeof of array:28sizeof of parameter:8Length of array:7Length of parameter:2
Output (dalam OS windows 32-bit):
sizeof of array:28sizeof of parameter:4Length of array:7Length of parameter:1
mengapa length of parameter:2jika hanya pointer ke elemen array pertama yang dilewati?
Bbvarghe
16
@Bbvarghe Itu karena pointer dalam sistem 64bit adalah 8 byte (sizeof (intArray)), tetapi int masih (biasanya) 4 byte panjang (sizeof (intArray [0])).
Elideb
13
@ Peracerier: Tidak ada kode yang benar - solusi yang biasa adalah untuk melewatkan panjang bersama dengan array sebagai argumen terpisah.
Jean Hominal
10
Tunggu, jadi tidak ada cara untuk mengakses array secara langsung dari pointer dan melihat ukurannya? Baru di C di sini.
sudo
7
@ Michael Trouw: Anda dapat menggunakan sintaks Operator jika adalah merek Anda merasa lebih baik: (sizeof array / sizeof *array).
chqrlie
134
Perlu dicatat bahwa sizeoftidak membantu ketika berhadapan dengan nilai array yang telah membusuk ke pointer: meskipun menunjuk ke awal array, ke kompiler itu sama dengan pointer ke elemen tunggal array itu . Pointer tidak "mengingat" hal lain tentang array yang digunakan untuk menginisialisasi itu.
int a[10];int* p = a;
assert(sizeof(a)/sizeof(a[0])==10);
assert(sizeof(p)==sizeof(int*));
assert(sizeof(*p)==sizeof(int));
@ Magnus: Standar mendefinisikan sizeof sebagai menghasilkan jumlah byte dalam objek dan sizeof (char) selalu satu. Jumlah bit dalam byte adalah implementasi spesifik. Sunting: ANSI C ++ bagian standar 5.3.3 Sizeof: "Sizeof operator menghasilkan jumlah byte dalam representasi objek dari operandnya. [...] sizeof (char), sizeof (char terdaftar) dan sizeof (unsigned char) adalah 1; hasil dari ukuran yang diterapkan pada tipe fundamental lainnya ditentukan oleh implementasi. "
Skizz
Bagian 1.6 Model memori C ++: "Unit penyimpanan dasar dalam model memori C ++ adalah byte. Byte paling tidak cukup besar untuk mengandung anggota dari set karakter eksekusi dasar dan terdiri dari urutan bit yang berdekatan, jumlah yang didefinisikan implementasi. "
Skizz
2
Saya ingat bahwa CRAY memiliki C dengan char32 bit. Semua standar mengatakan bahwa nilai integer dari 0 hingga 127 dapat diwakili, dan jangkauannya setidaknya -127 hingga 127 (char ditandatangani) atau 0 hingga 255 (char tidak ditandatangani).
vonbrand
5
Ini adalah respons yang sangat baik. Saya ingin berkomentar bahwa semua pernyataan di atas dievaluasi ke TRUE.
Javad
49
Ukuran "trik" adalah cara terbaik yang saya tahu, dengan satu perubahan kecil tapi (bagi saya, ini menjadi kesal hewan peliharaan) perubahan penting dalam penggunaan tanda kurung.
Seperti yang dijelaskan oleh entri Wikipedia, C's sizeofbukan fungsi; ini operator . Dengan demikian, tidak memerlukan tanda kurung di sekitar argumennya, kecuali argumennya adalah nama jenis. Ini mudah diingat, karena membuat argumen terlihat seperti ekspresi pemain, yang juga menggunakan tanda kurung.
Jadi: Jika Anda memiliki yang berikut ini:
int myArray[10];
Anda dapat menemukan jumlah elemen dengan kode seperti ini:
size_t n =sizeof myArray /sizeof*myArray;
Bagi saya, itu jauh lebih mudah daripada alternatif dengan tanda kurung. Saya juga menyukai penggunaan tanda bintang di bagian kanan divisi, karena ini lebih ringkas daripada pengindeksan.
Tentu saja, ini semua waktu kompilasi juga, jadi tidak perlu khawatir tentang divisi yang mempengaruhi kinerja program. Jadi gunakan formulir ini di mana pun Anda bisa.
Itu selalu terbaik untuk menggunakan sizeof pada objek yang sebenarnya ketika Anda memilikinya, daripada pada jenis, karena Anda tidak perlu khawatir tentang membuat kesalahan dan menyatakan jenis yang salah.
Misalnya, Anda memiliki fungsi yang menampilkan beberapa data sebagai aliran byte, misalnya melintasi jaringan. Mari kita panggil fungsi tersebut send(), dan buat sebagai argumen sebagai penunjuk ke objek yang akan dikirim, dan jumlah byte dalam objek. Jadi, prototipe menjadi:
void send(constvoid*object,size_t size);
Dan kemudian Anda perlu mengirim integer, sehingga Anda membuat kodenya seperti ini:
int foo =4711;
send(&foo,sizeof(int));
Sekarang, Anda telah memperkenalkan cara halus menembak diri sendiri di kaki, dengan menentukan jenis foodi dua tempat. Jika satu berubah tetapi yang lain tidak, kode rusak. Jadi, selalu lakukan seperti ini:
send(&foo,sizeof foo);
Sekarang kamu terlindungi. Tentu, Anda menduplikasi nama variabel, tetapi itu memiliki kemungkinan besar untuk memecahkan dengan cara kompiler dapat mendeteksi, jika Anda mengubahnya.
Btw, apakah itu instruksi yang identik di tingkat prosesor? Apakah sizeof(int)memerlukan instruksi yang lebih rendah daripada sizeof(foo)?
Pacerier
@ Peracerier: tidak, mereka identik. Pikirkan int x = 1+1;versus int x = (1+1);. Di sini, tanda kurung murni semata-mata hanya estetika.
quetzalcoatl
@Aidiakapi Itu tidak benar, pertimbangkan C99 VLA.
bersantai
@wwind Terima kasih, saya berdiri dikoreksi. Untuk memperbaiki komentar saya, sizeofakan selalu konstan di C ++ dan C89. Dengan array panjang variabel C99, itu dapat dievaluasi saat runtime.
Aidiakapi
2
sizeofmungkin operator tetapi harus diperlakukan sebagai fungsi sesuai dengan Linus Torvalds. Saya setuju. Baca rasionalnya di sini: lkml.org/lkml/2012/7/11/103
Nitpick kecil: hasil pengurangan pointer memiliki tipe ptrdiff_t. (Biasanya pada sistem 64-bit, ini akan menjadi tipe yang lebih besar dari int). Bahkan jika Anda mengubah intke ptrdiff_tdalam kode ini, masih ada bug jika arrmemakan lebih dari setengah ruang alamat.
MM
2
@ MM Nitpick kecil lainnya: Tergantung pada arsitektur sistem Anda, ruang alamat tidak hampir sebesar ukuran pointer pada kebanyakan sistem. Windows misalnya membatasi ruang alamat untuk aplikasi 64-bit menjadi 8TB atau 44 bit. Jadi, bahkan jika Anda memiliki array yang lebih besar dari setengah ruang alamat Anda 4.1TB misalnya, itu tidak akan menjadi bug. Hanya jika ruang alamat Anda melebihi 63-bit pada sistem itu, bahkan mungkin menemukan bug seperti itu. Secara umum, jangan khawatir tentang hal itu.
Aidiakapi
1
@Aidiakapi pada 32-bit x86 Linux atau Windows dengan /3Gopsi Anda memiliki pemisah / kernel 3G / 1G, yang memungkinkan Anda memiliki ukuran array hingga 75% dari ukuran ruang alamat.
Ruslan
1
Pertimbangkan foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];sebagai variabel global. buf3[]deklarasi gagal karena (&buf1)[1] - buf1tidak konstan.
chux - Reinstate Monica
2
Ini adalah perilaku yang secara teknis tidak terdefinisi karena standar secara eksplisit tidak mengizinkan dereferencing melewati akhir array (bahkan jika Anda tidak mencoba membaca nilai yang disimpan)
MM
26
Anda dapat menggunakan operator sizeof tetapi tidak akan berfungsi untuk fungsi karena akan mengambil referensi pointer Anda dapat melakukan hal berikut untuk menemukan panjang array:
Jika Anda mengetahui tipe data array, Anda dapat menggunakan sesuatu seperti:
int arr[]={23,12,423,43,21,43,65,76,22};int noofele =sizeof(arr)/sizeof(int);
Atau jika Anda tidak tahu tipe data array, Anda dapat menggunakan sesuatu seperti:
noofele =sizeof(arr)/sizeof(arr[0]);
Catatan: Hal ini hanya berfungsi jika array tidak didefinisikan pada saat run (seperti malloc) dan array tidak diteruskan dalam suatu fungsi. Dalam kedua kasus, arr(nama array) adalah sebuah pointer.
int noofele = sizeof(arr)/sizeof(int);hanya setengah jalan lebih baik daripada coding int noofele = 9;. Menggunakan sizeof(arr)mempertahankan fleksibilitas jika ukuran array berubah. Namun sizeof(int)kebutuhan pembaruan harus jenis arr[]perubahan. Lebih baik digunakan sizeof(arr)/sizeof(arr[0])meski jenisnya sudah terkenal. Tidak jelas mengapa menggunakan intuntuk noofelevs. size_t, jenis yang dikembalikan oleh sizeof().
chux
19
Makro ARRAYELEMENTCOUNT(x)bahwa setiap orang menggunakan evaluasi salah . Ini, secara realistis, hanya masalah sensitif, karena Anda tidak dapat memiliki ekspresi yang menghasilkan tipe 'array'.
Ini benar-benar tidak ada hubungannya dengan ukuran array secara eksplisit. Saya baru saja memperhatikan banyak kesalahan karena tidak benar-benar mengamati cara kerja preprosesor C. Anda selalu membungkus parameter makro, bukan ekspresi yang mungkin terlibat di dalamnya.
Ini benar; contoh saya adalah yang buruk. Tapi sebenarnya itulah yang seharusnya terjadi. Seperti yang saya sebutkan sebelumnya p + 1akan berakhir sebagai tipe pointer dan membatalkan seluruh makro (seperti jika Anda mencoba menggunakan makro dalam suatu fungsi dengan parameter pointer).
Pada akhirnya, dalam contoh khusus ini , kesalahannya tidak terlalu penting (jadi saya hanya membuang waktu semua orang; huzzah!), Karena Anda tidak memiliki ekspresi dengan jenis 'array'. Tapi sebenarnya poin tentang subtitle evaluasi preprosesor saya pikir adalah yang penting.
Terima kasih untuk penjelasannya. Versi asli menghasilkan kesalahan waktu kompilasi. Dentang laporan "nilai subskrip bukan array, pointer, atau vektor". Ini kelihatannya merupakan perilaku yang lebih disukai dalam hal ini, meskipun komentar Anda tentang urutan evaluasi di makro telah diterima dengan baik.
Mark Harrison
1
Saya belum memikirkan kompiler sebagai pemberitahuan otomatis dari jenis yang salah. Terima kasih!
3
Apakah ada alasan untuk tidak menggunakan (sizeof (x) / sizeof (*x))?
seriousdev
16
Untuk array multidimensi , ini sedikit lebih rumit. Seringkali orang mendefinisikan konstanta makro eksplisit, yaitu
Bergantung pada tipe yang arraydimiliki, Anda tidak perlu menggunakan sizeof(array) / sizeof(array[0])apakah arraymerupakan array dari keduanya char, unsigned charatau signed char- Kutipan dari C18,6.5.3.4 / 4: "Ketika sizeof diterapkan pada operan yang memiliki tipe char, unsigned char, atau char yang ditandatangani , (atau versi yang memenuhi syarat) hasilnya adalah 1. " Dalam hal ini Anda dapat melakukan sizeof(array)seperti yang dijelaskan dalam jawaban khusus saya .
RobertS mendukung Monica Cellio
15
Ukuran array di C:
int a[10];size_t size_of_array =sizeof(a);// Size of array aint n =sizeof(a)/sizeof(a[0]);// Number of elements in array asize_t size_of_element =sizeof(a[0]);// Size of each element in array a // Size of each element = size of type
Penasaran bahwa kode yang digunakan size_t size_of_elementbelum intdengan int n = sizeof (a) / sizeof (a[0]); dan tidaksize_t n = sizeof (a) / sizeof (a[0]);
chux - mengembalikan Monica
1
Hai @Yogeesh HT, bisakah Anda menjawab keraguan tentang chux. Saya juga sangat ingin tahu bagaimana int n = sizeof (a) / sizeof (a [0]) memberikan panjang array dan mengapa kita tidak menggunakan size_t untuk panjang array. Adakah yang bisa menjawabnya?
Brain
1
@ Ukuran otak (a) memberikan ukuran semua elemen yang ada dalam array ukuran (a [0]) memberikan ukuran elemen pertama. Misalkan a = {1,2,3,4,5}; sizeof (a) = 20bytes (jika sizeof (int) = 4bytes kalikan 5), sizeof (a [0]) = 4bytes, jadi 20/4 = 5 yaitu tidak ada unsur
Yogeesh HT
2
@YogeeshHT Untuk array yang sangat besar seperti char a[INT_MAX + 1u];, int nseperti yang digunakan di int n = sizeof (a) / sizeof (a[0]);tidak cukup (ini adalah UB). Penggunaan size_t n = sizeof (a) / sizeof (a[0]);tidak menimbulkan masalah ini.
chux - Reinstate Monica
13
Saya akan menyarankan untuk tidak pernah menggunakan sizeof(bahkan jika itu dapat digunakan) untuk mendapatkan salah satu dari dua ukuran array yang berbeda, baik dalam jumlah elemen atau dalam byte, yang merupakan dua kasus terakhir yang saya tunjukkan di sini. Untuk masing-masing dari dua ukuran, makro yang ditunjukkan di bawah ini dapat digunakan untuk membuatnya lebih aman. Alasannya adalah untuk memperjelas maksud kode untuk pengelola, dan perbedaan sizeof(ptr)dari sizeof(arr)pandangan pertama (yang ditulis dengan cara ini tidak jelas), sehingga bug kemudian jelas bagi semua orang yang membaca kode.
Saya tidak setuju dengan solusi yang disediakan Linus, yaitu tidak pernah menggunakan notasi array untuk parameter fungsi.
Saya suka notasi array sebagai dokumentasi bahwa pointer sedang digunakan sebagai array. Tapi itu berarti bahwa solusi bodoh-bukti perlu diterapkan sehingga tidak mungkin untuk menulis kode kereta.
Dari sebuah array kita memiliki tiga ukuran yang mungkin ingin kita ketahui:
Ukuran elemen array
Jumlah elemen dalam array
Ukuran dalam byte yang digunakan array dalam memori
Ukuran elemen array
Yang pertama sangat sederhana, dan tidak masalah jika kita berurusan dengan array atau pointer, karena dilakukan dengan cara yang sama.
qsort() membutuhkan nilai ini sebagai argumen ketiga.
Untuk dua ukuran lainnya, yang merupakan topik pertanyaan, kami ingin memastikan bahwa kami berurusan dengan array, dan memecah kompilasi jika tidak, karena jika kita berurusan dengan pointer, kita akan mendapatkan nilai yang salah . Ketika kompilasi rusak, kita akan dapat dengan mudah melihat bahwa kita tidak berurusan dengan sebuah array, tetapi dengan sebuah pointer sebagai gantinya, dan kita hanya perlu menulis kode dengan variabel atau makro yang menyimpan ukuran file. Array di belakang pointer.
Jumlah elemen dalam array
Yang ini adalah yang paling umum, dan banyak jawaban telah memberi Anda ARRAY_SIZE makro yang khas:
Mengingat bahwa hasil ARRAY_SIZE biasanya digunakan dengan variabel bertanda jenis ptrdiff_t, ada baiknya untuk menentukan varian yang ditandatangani dari makro ini:
Array dengan lebih dari PTRDIFF_MAXanggota akan memberikan nilai yang tidak valid untuk versi makro yang ditandatangani ini, tetapi dari membaca C17 :: 6.5.6.9, array seperti itu sudah bermain dengan api. Hanya ARRAY_SIZEdan size_tharus digunakan dalam kasus-kasus itu.
Versi kompiler terbaru, seperti GCC 8, akan memperingatkan Anda ketika Anda menerapkan makro ini ke sebuah pointer, sehingga aman (ada metode lain untuk membuatnya aman dengan kompiler lama).
Ia bekerja dengan membagi ukuran dalam byte dari seluruh array dengan ukuran setiap elemen.
Contoh penggunaan:
void foo(ptrdiff_t nmemb){char buf[nmemb];
fgets(buf, ARRAY_SIZE(buf), stdin);}void bar(ptrdiff_t nmemb){int arr[nmemb];for(ptrdiff_t i =0; i < ARRAY_SSIZE(arr); i++)
arr[i]= i;}
Jika fungsi-fungsi ini tidak menggunakan array, tetapi sebagai parameter sebagai gantinya, kode sebelumnya tidak akan dikompilasi, jadi tidak mungkin untuk memiliki bug (mengingat bahwa versi kompiler baru-baru ini digunakan, atau bahwa beberapa trik lain digunakan) , dan kami perlu mengganti panggilan makro dengan nilai:
void foo(ptrdiff_t nmemb,char buf[nmemb]){
fgets(buf, nmemb, stdin);}void bar(ptrdiff_t nmemb,int arr[nmemb]){for(ptrdiff_t i =0; i < nmemb; i++)
arr[i]= i;}
Ukuran dalam byte yang digunakan array dalam memori
ARRAY_SIZE umumnya digunakan sebagai solusi untuk kasus sebelumnya, tetapi kasus ini jarang ditulis dengan aman, mungkin karena itu kurang umum.
Cara umum untuk mendapatkan nilai ini adalah menggunakan sizeof(arr). Masalahnya: sama dengan yang sebelumnya; jika Anda memiliki pointer bukan array, program Anda akan menjadi gila.
Solusi untuk masalah ini melibatkan penggunaan makro yang sama seperti sebelumnya, yang kami tahu aman (itu merusak kompilasi jika diterapkan pada pointer):
Cara kerjanya sangat sederhana: itu membatalkan divisi yang ARRAY_SIZEmelakukannya, jadi setelah pembatalan matematika Anda berakhir hanya dengan satu sizeof(arr), tetapi dengan keamanan tambahan ARRAY_SIZEkonstruksi.
memset() membutuhkan nilai ini sebagai argumen ketiga.
Seperti sebelumnya, jika array diterima sebagai parameter (pointer), array tidak akan dikompilasi, dan kami harus mengganti panggilan makro dengan nilai:
Hari ini saya menemukan bahwa peringatan baru di GCC hanya berfungsi jika makro didefinisikan di header yang bukan header sistem. Jika Anda mendefinisikan makro di header yang diinstal di sistem Anda (biasanya /usr/local/include/atau /usr/include/) ( #include <foo.h>), kompiler TIDAK akan memancarkan peringatan (saya mencoba GCC 9.3.0).
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a),typeof(b))#define is_array(a)(!is_same_type((a),&(a)[0]))#defineStatic_assert_array(a)_Static_assert(is_array(a),"Not a `[]` !")#define ARRAY_SIZE(arr)( \
{ \
Static_assert_array(arr); \
sizeof(arr)/sizeof((arr)[0]); \
} \
)
Sekarang ARRAY_SIZE()benar-benar aman, dan karenanya semua turunannya akan aman.
Pembaruan: libbsd menyediakan __arraycount() :
Libbsd menyediakan makro __arraycount()dalam <sys/cdefs.h>, yang tidak aman karena tidak memiliki sepasang tanda kurung, tetapi kita dapat menambahkan sendiri tanda kurung itu, dan oleh karena itu kita bahkan tidak perlu menulis divisi di header kita (mengapa kita menduplikasi kode yang sudah ada? ). Makro itu didefinisikan dalam header sistem, jadi jika kami menggunakannya kami terpaksa menggunakan makro di atas.
Beberapa sistem menyediakan nitems()di <sys/param.h>sebaliknya, dan beberapa sistem menyediakan baik. Anda harus memeriksa sistem Anda, dan menggunakan yang Anda miliki, dan mungkin menggunakan beberapa persyaratan preprocessor untuk portabilitas dan mendukung keduanya.
Pembaruan: Izinkan makro untuk digunakan pada cakupan file:
Sayangnya, ({})ekstensi gcc tidak dapat digunakan pada ruang lingkup file. Untuk dapat menggunakan makro pada ruang lingkup file, pernyataan statis harus ada di dalam sizeof(struct {}). Kemudian, gandakan dengan 0tidak mempengaruhi hasilnya. Seorang pemain (int)mungkin baik untuk mensimulasikan fungsi yang kembali (int)0(dalam hal ini tidak perlu, tetapi kemudian dapat digunakan kembali untuk hal-hal lain).
Maukah Anda menjelaskan mengapa downvote? Ini menunjukkan solusi untuk konstruksi yang tidak aman dan umum ( sizeof(arr)) yang tidak ditampilkan di tempat lain: ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE cukup umum untuk digunakan secara bebas, dan ARRAY_BYTES sangat eksplisit dalam namanya, harus didefinisikan di sebelah ARRAY_SIZE sehingga pengguna dapat melihat keduanya dengan mudah, dan dengan penggunaannya, saya tidak berpikir siapa pun yang membaca kode memiliki keraguan tentang apa itu benar. Yang saya maksud adalah untuk tidak menggunakan yang sederhana sizeof, tetapi gunakan konstruksi ini sebagai gantinya; jika Anda merasa ingin menulis konstruksi ini setiap kali, Anda kemungkinan akan membuat kesalahan (sangat umum jika Anda menyalin tempel, dan juga sangat umum jika Anda menulisnya setiap kali karena mereka memiliki banyak tanda kurung) ...
Cacahuete Frito
3
..., jadi saya berdiri pada kesimpulan utama: satu sizeofjelas tidak aman (alasannya ada dalam jawaban), dan tidak menggunakan makro tetapi menggunakan konstruksi yang saya berikan, setiap kali, bahkan lebih tidak aman, jadi satu-satunya cara untuk pergi adalah makro.
Cacahuete Frito
3
@ MarkHarrison Saya tahu perbedaan antara pointer dan array. Tetapi ada saat-saat saya memiliki fungsi yang kemudian saya refactored menjadi fungsi-fungsi kecil, dan apa yang pertama adalah array, kemudian adalah pointer, dan itu adalah satu titik di mana jika Anda lupa untuk mengubah sizeof, Anda mengacaukannya, dan mudah untuk tidak melihat salah satu dari itu.
Cacahuete Frito
3
@ Hyde Juga saya tahu perbedaannya tidak berarti semua orang tahu perbedaannya, dan mengapa tidak menggunakan sesuatu yang pada dasarnya menghilangkan 100% bug itu? Bug itu hampir berhasil masuk ke Linux; itu tiba di Linus, yang berarti melewati banyak pemeriksaan, dan yang juga berarti ada kemungkinan bahwa bug yang sama telah berhasil masuk ke Linux di bagian lain dari kernel, seperti katanya.
Cacahuete Frito
11
"Anda telah memperkenalkan cara halus menembak diri sendiri di kaki"
Array 'asli' tidak menyimpan ukurannya. Oleh karena itu disarankan untuk menyimpan panjang array dalam variabel / const yang terpisah, dan meneruskannya setiap kali Anda melewati array, yaitu:
Anda HARUS selalu menghindari array asli (kecuali jika Anda tidak bisa, dalam hal ini, pikirkan saja). Jika Anda menulis C ++, gunakan wadah 'vektor' STL . "Dibandingkan dengan array, mereka memberikan kinerja yang hampir sama", dan mereka jauh lebih berguna!
// vector is a template, the <int> means it is a vector of intsvector<int> numbers;// push_back() puts a new value at the end (or back) of the vectorfor(int i =0; i <10; i++)
numbers.push_back(i);// Determine the size of the array
cout << numbers.size();
Perhatikan bahwa ini hanya berfungsi untuk array aktual, bukan pointer yang mengarah ke array.
David Schwartz
5
Jika Anda benar-benar ingin melakukan ini untuk menyebarkan array Anda, saya sarankan menerapkan struktur untuk menyimpan pointer ke tipe yang Anda inginkan dari array dan integer yang mewakili ukuran array. Kemudian Anda bisa mengopernya ke fungsi Anda. Cukup tetapkan nilai variabel array (pointer ke elemen pertama) ke pointer itu. Kemudian Anda bisa pergi Array.arr[i]untuk mendapatkan elemen ke-i dan menggunakanArray.size untuk mendapatkan jumlah elemen dalam array.
Saya memasukkan beberapa kode untuk Anda. Ini tidak terlalu berguna tetapi Anda dapat memperluasnya dengan lebih banyak fitur. Jujur saja, jika ini adalah hal yang Anda inginkan, Anda harus berhenti menggunakan C dan menggunakan bahasa lain dengan fitur-fitur ini.
/* Absolutely no one should use this...
By the time you're done implementing it you'll wish you just passed around
an array and size to your functions *//* This is a static implementation. You can get a dynamic implementation and
cut out the array in main by using the stdlib memory allocation methods,
but it will work much slower since it will store your array on the heap */#include<stdio.h>#include<string.h>/*
#include "MyTypeArray.h"
*//* MyTypeArray.h
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/typedefstructMyType{int age;char name[20];}MyType;typedefstructMyTypeArray{int size;MyType*arr;}MyTypeArray;MyType new_MyType(int age,char*name);MyTypeArray newMyTypeArray(int size,MyType*first);/*
#endif
End MyTypeArray.h *//* MyTypeArray.c */MyType new_MyType(int age,char*name){MyType d;
d.age = age;
strcpy(d.name, name);return d;}MyTypeArray new_MyTypeArray(int size,MyType*first){MyTypeArray d;
d.size = size;
d.arr = first;return d;}/* End MyTypeArray.c */void print_MyType_names(MyTypeArray d){int i;for(i =0; i < d.size; i++){
printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);}}int main(){/* First create an array on the stack to store our elements in.
Note we could create an empty array with a size instead and
set the elements later. */MyType arr[]={new_MyType(10,"Sam"), new_MyType(3,"Baxter")};/* Now create a "MyTypeArray" which will use the array we just
created internally. Really it will just store the value of the pointer
"arr". Here we are manually setting the size. You can use the sizeof
trick here instead if you're sure it will work with your compiler. */MyTypeArrayarray= new_MyTypeArray(2, arr);/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);return0;}
Tidak dapat mengunggah kode yang melakukannya strcpy(d.name, name);tanpa penanganan overflow.
chux
4
Cara terbaik adalah Anda menyimpan informasi ini, misalnya, dalam struktur:
typedefstruct{int*array;int elements;} list_s;
Terapkan semua fungsi yang diperlukan seperti membuat, menghancurkan, memeriksa kesetaraan, dan semua yang Anda butuhkan. Lebih mudah untuk lulus sebagai parameter.
Apa alasannya untuk int elementsvs size_t elements?
chux
3
Fungsi sizeofmengembalikan jumlah byte yang digunakan oleh array Anda di memori. Jika Anda ingin menghitung jumlah elemen dalam array Anda, Anda harus membagi nomor itu dengan sizeoftipe variabel array. Katakanlah int array[10];, jika integer tipe variabel di komputer Anda adalah 32 bit (atau 4 byte), untuk mendapatkan ukuran array Anda, Anda harus melakukan hal berikut:
Anda dapat menggunakan &operator. Ini kode sumbernya:
#include<stdio.h>#include<stdlib.h>int main(){int a[10];int*p;
printf("%p\n",(void*)a);
printf("%p\n",(void*)(&a+1));
printf("---- diff----\n");
printf("%zu\n",sizeof(a[0]));
printf("The size of array a is %zu\n",((char*)(&a+1)-(char*)a)/(sizeof(a[0])));return0;};
Berikut adalah contoh keluarannya
15492166721549216712---- diff----4The size of array a is 10
Saya tidak downvote, tapi ini seperti memukul paku dengan batu bata karena Anda tidak melihat palu yang terletak di sebelah Anda. Juga, orang cenderung tidak suka menggunakan variabel yang tidak diinisialisasi ... tapi di sini saya kira itu melayani tujuan Anda dengan cukup baik.
Dmitri
2
@ Dmitri tidak ada variabel tidak diinisialisasi yang diakses di sini
MM
1
Hmmm. Pengurangan Pointer mengarah ke ptrdiff_t. sizeof()hasil dalam size_t. C tidak mendefinisikan mana yang lebih luas atau lebih tinggi / peringkat yang sama. Jadi jenis hasil bagi ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))tidak tentu size_tdan dengan demikian pencetakan dengan zdapat menyebabkan UB. Cukup menggunakan printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);sudah cukup.
chux
1
(char *)(&a+1)-(char *)abukan konstanta dan dapat dihitung pada saat run-time, bahkan dengan ukuran tetap a[10]. sizeof(a)/sizeof(a[0])konstan dilakukan pada waktu kompilasi dalam kasus ini.
Di samping jawaban yang sudah disediakan, saya ingin menunjukkan kasus khusus dengan menggunakan
sizeof(a)/sizeof(a[0])
Jika aarray char, unsigned charatau signed charAnda tidak perlu menggunakan sizeofdua kali sejak sizeofekspresi dengan satu operan dari jenis ini selalu menghasilkan 1.
Kutipan dari C18,6.5.3.4 / 4:
" Ketika sizeofditerapkan pada operan yang memiliki jenis char, unsigned charatau signed char, (atau versi yang berkualitas daripadanya) hasilnya adalah 1."
Dengan demikian, sizeof(a) / sizeof (a[0])akan sama dengan NUMBER OF ARRAY ELEMENTS / 1jika aadalah array bertipe char, unsigned charatau signed char. Pembagian melalui 1 adalah mubazir.
Dalam hal ini, Anda cukup menyingkat dan melakukan:
sizeof(a)
Sebagai contoh:
char a[10];size_t length =sizeof(a);
Jika Anda ingin bukti, berikut ini tautan ke GodBolt .
Meskipun demikian, divisi menjaga keamanan, jika jenisnya berubah secara signifikan (meskipun kasus ini jarang terjadi).
Anda mungkin lebih suka untuk tetap menerapkan makro dengan divisi, karena jenisnya dapat berubah di masa mendatang (meskipun mungkin tidak mungkin), dan divisi tersebut diketahui pada waktu kompilasi, sehingga kompiler akan mengoptimalkannya (jika tidak harap ubah kompiler Anda).
Cacahuete Frito
1
@CacahueteFrito Ya, saya sudah memikirkannya sementara itu juga. Saya menganggapnya sebagai catatan tambahan. Terima kasih.
RobertS mendukung Monica Cellio
-1
Catatan: Yang ini dapat memberi Anda perilaku tidak terdefinisi seperti yang ditunjukkan oleh MM dalam komentar.
int a[10];int size =(*(&a+1)-a);
Untuk lebih jelasnya lihat di
sini
dan juga di sini .
Ini adalah perilaku yang secara teknis tidak terdefinisi; yang *Operator tidak dapat diterapkan pada pointer masa-the-end
MM
3
"perilaku tidak terdefinisi" berarti Standar C tidak mendefinisikan perilaku. Jika Anda mencobanya di program Anda maka apa pun bisa terjadi
MM
@ MM yang Anda katakan *(&a+1) - a;berbeda dari di (&a)[1] - a;atas, jangan keduanya *(&a+1)dan (&a)[1]dihitung sebagai 1 melewati akhir?
QuentinUK
@ QuentinUK dua ekspresi Anda sama, x[y]didefinisikan sebagai*(x + (y))
MM
@ MM saya pikir begitu. Tetapi jawaban yang lain, oleh Arjun Sreedharan, memiliki 38 panah ke atas dan ini memiliki -1. Dan jawaban Arjun Sreedharan tidak menyebutkan perilaku yang tidak terdefinisi.
Jawaban:
Ringkasan bisnis plan:
Jawaban lengkap:
Untuk menentukan ukuran array Anda dalam byte, Anda dapat menggunakan
sizeof
operator:Di komputer saya, panjang int adalah 4 byte, jadi n adalah 68.
Untuk menentukan jumlah elemen dalam array, kita dapat membagi ukuran total array dengan ukuran elemen array. Anda bisa melakukan ini dengan tipenya, seperti ini:
dan dapatkan jawaban yang tepat (68/4 = 17), tetapi jika jenis
a
perubahan Anda akan memiliki bug jahat jika Anda lupa untuk mengubahsizeof(int)
juga.Jadi pembagi yang disukai adalah
sizeof(a[0])
atau setarasizeof(*a)
, ukuran elemen pertama array.Keuntungan lain adalah bahwa Anda sekarang dapat dengan mudah parameterisasi nama array di makro dan dapatkan:
sumber
ARRAYSIZE
makro yang didefinisikan diWinNT.h
(yang ditarik oleh header lainnya). Jadi pengguna WinAPI tidak perlu mendefinisikan makro mereka sendiri.static int a[20];
. Tetapi komentar Anda bermanfaat bagi pembaca yang mungkin tidak menyadari perbedaan antara array dan pointer.The
sizeof
cara adalah cara yang tepat IFF Anda berurusan dengan array tidak diterima sebagai parameter. Array yang dikirim sebagai parameter ke suatu fungsi diperlakukan sebagai pointer, sehinggasizeof
akan mengembalikan ukuran pointer, bukan array.Dengan demikian, fungsi-fungsi di dalam metode ini tidak berfungsi. Sebagai gantinya, selalu berikan parameter tambahan yang
size_t size
menunjukkan jumlah elemen dalam array.Uji:
Output (dalam OS Linux 64-bit):
Output (dalam OS windows 32-bit):
sumber
length of parameter:2
jika hanya pointer ke elemen array pertama yang dilewati?(sizeof array / sizeof *array)
.Perlu dicatat bahwa
sizeof
tidak membantu ketika berhadapan dengan nilai array yang telah membusuk ke pointer: meskipun menunjuk ke awal array, ke kompiler itu sama dengan pointer ke elemen tunggal array itu . Pointer tidak "mengingat" hal lain tentang array yang digunakan untuk menginisialisasi itu.sumber
char
32 bit. Semua standar mengatakan bahwa nilai integer dari 0 hingga 127 dapat diwakili, dan jangkauannya setidaknya -127 hingga 127 (char ditandatangani) atau 0 hingga 255 (char tidak ditandatangani).Ukuran "trik" adalah cara terbaik yang saya tahu, dengan satu perubahan kecil tapi (bagi saya, ini menjadi kesal hewan peliharaan) perubahan penting dalam penggunaan tanda kurung.
Seperti yang dijelaskan oleh entri Wikipedia, C's
sizeof
bukan fungsi; ini operator . Dengan demikian, tidak memerlukan tanda kurung di sekitar argumennya, kecuali argumennya adalah nama jenis. Ini mudah diingat, karena membuat argumen terlihat seperti ekspresi pemain, yang juga menggunakan tanda kurung.Jadi: Jika Anda memiliki yang berikut ini:
Anda dapat menemukan jumlah elemen dengan kode seperti ini:
Bagi saya, itu jauh lebih mudah daripada alternatif dengan tanda kurung. Saya juga menyukai penggunaan tanda bintang di bagian kanan divisi, karena ini lebih ringkas daripada pengindeksan.
Tentu saja, ini semua waktu kompilasi juga, jadi tidak perlu khawatir tentang divisi yang mempengaruhi kinerja program. Jadi gunakan formulir ini di mana pun Anda bisa.
Itu selalu terbaik untuk menggunakan sizeof pada objek yang sebenarnya ketika Anda memilikinya, daripada pada jenis, karena Anda tidak perlu khawatir tentang membuat kesalahan dan menyatakan jenis yang salah.
Misalnya, Anda memiliki fungsi yang menampilkan beberapa data sebagai aliran byte, misalnya melintasi jaringan. Mari kita panggil fungsi tersebut
send()
, dan buat sebagai argumen sebagai penunjuk ke objek yang akan dikirim, dan jumlah byte dalam objek. Jadi, prototipe menjadi:Dan kemudian Anda perlu mengirim integer, sehingga Anda membuat kodenya seperti ini:
Sekarang, Anda telah memperkenalkan cara halus menembak diri sendiri di kaki, dengan menentukan jenis
foo
di dua tempat. Jika satu berubah tetapi yang lain tidak, kode rusak. Jadi, selalu lakukan seperti ini:Sekarang kamu terlindungi. Tentu, Anda menduplikasi nama variabel, tetapi itu memiliki kemungkinan besar untuk memecahkan dengan cara kompiler dapat mendeteksi, jika Anda mengubahnya.
sumber
sizeof(int)
memerlukan instruksi yang lebih rendah daripadasizeof(foo)
?int x = 1+1;
versusint x = (1+1);
. Di sini, tanda kurung murni semata-mata hanya estetika.sizeof
akan selalu konstan di C ++ dan C89. Dengan array panjang variabel C99, itu dapat dievaluasi saat runtime.sizeof
mungkin operator tetapi harus diperlakukan sebagai fungsi sesuai dengan Linus Torvalds. Saya setuju. Baca rasionalnya di sini: lkml.org/lkml/2012/7/11/103Lihat tautan ini untuk penjelasan
sumber
ptrdiff_t
. (Biasanya pada sistem 64-bit, ini akan menjadi tipe yang lebih besar dariint
). Bahkan jika Anda mengubahint
keptrdiff_t
dalam kode ini, masih ada bug jikaarr
memakan lebih dari setengah ruang alamat./3G
opsi Anda memiliki pemisah / kernel 3G / 1G, yang memungkinkan Anda memiliki ukuran array hingga 75% dari ukuran ruang alamat.foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
sebagai variabel global.buf3[]
deklarasi gagal karena(&buf1)[1] - buf1
tidak konstan.Anda dapat menggunakan operator sizeof tetapi tidak akan berfungsi untuk fungsi karena akan mengambil referensi pointer Anda dapat melakukan hal berikut untuk menemukan panjang array:
Kode awalnya ditemukan di sini: Program C untuk menemukan jumlah elemen dalam array
sumber
Jika Anda mengetahui tipe data array, Anda dapat menggunakan sesuatu seperti:
Atau jika Anda tidak tahu tipe data array, Anda dapat menggunakan sesuatu seperti:
Catatan: Hal ini hanya berfungsi jika array tidak didefinisikan pada saat run (seperti malloc) dan array tidak diteruskan dalam suatu fungsi. Dalam kedua kasus,
arr
(nama array) adalah sebuah pointer.sumber
int noofele = sizeof(arr)/sizeof(int);
hanya setengah jalan lebih baik daripada codingint noofele = 9;
. Menggunakansizeof(arr)
mempertahankan fleksibilitas jika ukuran array berubah. Namunsizeof(int)
kebutuhan pembaruan harus jenisarr[]
perubahan. Lebih baik digunakansizeof(arr)/sizeof(arr[0])
meski jenisnya sudah terkenal. Tidak jelas mengapa menggunakanint
untuknoofele
vs.size_t
, jenis yang dikembalikan olehsizeof()
.Makro
ARRAYELEMENTCOUNT(x)
bahwa setiap orang menggunakan evaluasi salah . Ini, secara realistis, hanya masalah sensitif, karena Anda tidak dapat memiliki ekspresi yang menghasilkan tipe 'array'.Sebenarnya dievaluasi sebagai:
Sedangkan
Ini mengevaluasi dengan benar untuk:
Ini benar-benar tidak ada hubungannya dengan ukuran array secara eksplisit. Saya baru saja memperhatikan banyak kesalahan karena tidak benar-benar mengamati cara kerja preprosesor C. Anda selalu membungkus parameter makro, bukan ekspresi yang mungkin terlibat di dalamnya.
Ini benar; contoh saya adalah yang buruk. Tapi sebenarnya itulah yang seharusnya terjadi. Seperti yang saya sebutkan sebelumnya
p + 1
akan berakhir sebagai tipe pointer dan membatalkan seluruh makro (seperti jika Anda mencoba menggunakan makro dalam suatu fungsi dengan parameter pointer).Pada akhirnya, dalam contoh khusus ini , kesalahannya tidak terlalu penting (jadi saya hanya membuang waktu semua orang; huzzah!), Karena Anda tidak memiliki ekspresi dengan jenis 'array'. Tapi sebenarnya poin tentang subtitle evaluasi preprosesor saya pikir adalah yang penting.
sumber
(sizeof (x) / sizeof (*x))
?Untuk array multidimensi , ini sedikit lebih rumit. Seringkali orang mendefinisikan konstanta makro eksplisit, yaitu
Tetapi konstanta ini dapat dievaluasi pada waktu kompilasi juga dengan ukuran :
Perhatikan bahwa kode ini berfungsi dalam C dan C ++. Untuk array dengan penggunaan lebih dari dua dimensi
dll, tak terhingga.
sumber
sumber
array
dimiliki, Anda tidak perlu menggunakansizeof(array) / sizeof(array[0])
apakaharray
merupakan array dari keduanyachar
,unsigned char
atausigned char
- Kutipan dari C18,6.5.3.4 / 4: "Ketika sizeof diterapkan pada operan yang memiliki tipe char, unsigned char, atau char yang ditandatangani , (atau versi yang memenuhi syarat) hasilnya adalah 1. " Dalam hal ini Anda dapat melakukansizeof(array)
seperti yang dijelaskan dalam jawaban khusus saya .Ukuran array di C:
sumber
size_t size_of_element
belumint
denganint n = sizeof (a) / sizeof (a[0]);
dan tidaksize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
,int n
seperti yang digunakan diint n = sizeof (a) / sizeof (a[0]);
tidak cukup (ini adalah UB). Penggunaansize_t n = sizeof (a) / sizeof (a[0]);
tidak menimbulkan masalah ini.Saya akan menyarankan untuk tidak pernah menggunakan
sizeof
(bahkan jika itu dapat digunakan) untuk mendapatkan salah satu dari dua ukuran array yang berbeda, baik dalam jumlah elemen atau dalam byte, yang merupakan dua kasus terakhir yang saya tunjukkan di sini. Untuk masing-masing dari dua ukuran, makro yang ditunjukkan di bawah ini dapat digunakan untuk membuatnya lebih aman. Alasannya adalah untuk memperjelas maksud kode untuk pengelola, dan perbedaansizeof(ptr)
darisizeof(arr)
pandangan pertama (yang ditulis dengan cara ini tidak jelas), sehingga bug kemudian jelas bagi semua orang yang membaca kode.TL; DR:
must_be_array(arr)
(Didefinisikan di bawah) IS diperlukan seperti halnya-Wsizeof-pointer-div
buggy (per April / 2020):Ada bug penting mengenai topik ini: https://lkml.org/lkml/2015/9/3/428
Saya tidak setuju dengan solusi yang disediakan Linus, yaitu tidak pernah menggunakan notasi array untuk parameter fungsi.
Saya suka notasi array sebagai dokumentasi bahwa pointer sedang digunakan sebagai array. Tapi itu berarti bahwa solusi bodoh-bukti perlu diterapkan sehingga tidak mungkin untuk menulis kode kereta.
Dari sebuah array kita memiliki tiga ukuran yang mungkin ingin kita ketahui:
Ukuran elemen array
Yang pertama sangat sederhana, dan tidak masalah jika kita berurusan dengan array atau pointer, karena dilakukan dengan cara yang sama.
Contoh penggunaan:
qsort()
membutuhkan nilai ini sebagai argumen ketiga.Untuk dua ukuran lainnya, yang merupakan topik pertanyaan, kami ingin memastikan bahwa kami berurusan dengan array, dan memecah kompilasi jika tidak, karena jika kita berurusan dengan pointer, kita akan mendapatkan nilai yang salah . Ketika kompilasi rusak, kita akan dapat dengan mudah melihat bahwa kita tidak berurusan dengan sebuah array, tetapi dengan sebuah pointer sebagai gantinya, dan kita hanya perlu menulis kode dengan variabel atau makro yang menyimpan ukuran file. Array di belakang pointer.
Jumlah elemen dalam array
Yang ini adalah yang paling umum, dan banyak jawaban telah memberi Anda ARRAY_SIZE makro yang khas:
Mengingat bahwa hasil ARRAY_SIZE biasanya digunakan dengan variabel bertanda jenis
ptrdiff_t
, ada baiknya untuk menentukan varian yang ditandatangani dari makro ini:Array dengan lebih dari
PTRDIFF_MAX
anggota akan memberikan nilai yang tidak valid untuk versi makro yang ditandatangani ini, tetapi dari membaca C17 :: 6.5.6.9, array seperti itu sudah bermain dengan api. HanyaARRAY_SIZE
dansize_t
harus digunakan dalam kasus-kasus itu.Versi kompiler terbaru, seperti GCC 8, akan memperingatkan Anda ketika Anda menerapkan makro ini ke sebuah pointer, sehingga aman (ada metode lain untuk membuatnya aman dengan kompiler lama).
Ia bekerja dengan membagi ukuran dalam byte dari seluruh array dengan ukuran setiap elemen.
Contoh penggunaan:
Jika fungsi-fungsi ini tidak menggunakan array, tetapi sebagai parameter sebagai gantinya, kode sebelumnya tidak akan dikompilasi, jadi tidak mungkin untuk memiliki bug (mengingat bahwa versi kompiler baru-baru ini digunakan, atau bahwa beberapa trik lain digunakan) , dan kami perlu mengganti panggilan makro dengan nilai:
Ukuran dalam byte yang digunakan array dalam memori
ARRAY_SIZE
umumnya digunakan sebagai solusi untuk kasus sebelumnya, tetapi kasus ini jarang ditulis dengan aman, mungkin karena itu kurang umum.Cara umum untuk mendapatkan nilai ini adalah menggunakan
sizeof(arr)
. Masalahnya: sama dengan yang sebelumnya; jika Anda memiliki pointer bukan array, program Anda akan menjadi gila.Solusi untuk masalah ini melibatkan penggunaan makro yang sama seperti sebelumnya, yang kami tahu aman (itu merusak kompilasi jika diterapkan pada pointer):
Cara kerjanya sangat sederhana: itu membatalkan divisi yang
ARRAY_SIZE
melakukannya, jadi setelah pembatalan matematika Anda berakhir hanya dengan satusizeof(arr)
, tetapi dengan keamanan tambahanARRAY_SIZE
konstruksi.Contoh penggunaan:
memset()
membutuhkan nilai ini sebagai argumen ketiga.Seperti sebelumnya, jika array diterima sebagai parameter (pointer), array tidak akan dikompilasi, dan kami harus mengganti panggilan makro dengan nilai:
Pembaruan (23 / apr / 2020):
-Wsizeof-pointer-div
bermasalah :Hari ini saya menemukan bahwa peringatan baru di GCC hanya berfungsi jika makro didefinisikan di header yang bukan header sistem. Jika Anda mendefinisikan makro di header yang diinstal di sistem Anda (biasanya
/usr/local/include/
atau/usr/include/
) (#include <foo.h>
), kompiler TIDAK akan memancarkan peringatan (saya mencoba GCC 9.3.0).Jadi kita sudah
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
dan ingin membuatnya aman. Kami membutuhkan C11_Static_assert()
dan beberapa ekstensi GCC: Pernyataan dan Pernyataan dalam Ekspresi , __builtin_types_compatible_p :Sekarang
ARRAY_SIZE()
benar-benar aman, dan karenanya semua turunannya akan aman.Pembaruan: libbsd menyediakan
__arraycount()
:Libbsd menyediakan makro
__arraycount()
dalam<sys/cdefs.h>
, yang tidak aman karena tidak memiliki sepasang tanda kurung, tetapi kita dapat menambahkan sendiri tanda kurung itu, dan oleh karena itu kita bahkan tidak perlu menulis divisi di header kita (mengapa kita menduplikasi kode yang sudah ada? ). Makro itu didefinisikan dalam header sistem, jadi jika kami menggunakannya kami terpaksa menggunakan makro di atas.Beberapa sistem menyediakan
nitems()
di<sys/param.h>
sebaliknya, dan beberapa sistem menyediakan baik. Anda harus memeriksa sistem Anda, dan menggunakan yang Anda miliki, dan mungkin menggunakan beberapa persyaratan preprocessor untuk portabilitas dan mendukung keduanya.Pembaruan: Izinkan makro untuk digunakan pada cakupan file:
Sayangnya,
({})
ekstensi gcc tidak dapat digunakan pada ruang lingkup file. Untuk dapat menggunakan makro pada ruang lingkup file, pernyataan statis harus ada di dalamsizeof(struct {})
. Kemudian, gandakan dengan0
tidak mempengaruhi hasilnya. Seorang pemain(int)
mungkin baik untuk mensimulasikan fungsi yang kembali(int)0
(dalam hal ini tidak perlu, tetapi kemudian dapat digunakan kembali untuk hal-hal lain).sumber
sizeof(arr)
) yang tidak ditampilkan di tempat lain:ARRAY_BYTES(arr)
.sizeof
, tetapi gunakan konstruksi ini sebagai gantinya; jika Anda merasa ingin menulis konstruksi ini setiap kali, Anda kemungkinan akan membuat kesalahan (sangat umum jika Anda menyalin tempel, dan juga sangat umum jika Anda menulisnya setiap kali karena mereka memiliki banyak tanda kurung) ...sizeof
jelas tidak aman (alasannya ada dalam jawaban), dan tidak menggunakan makro tetapi menggunakan konstruksi yang saya berikan, setiap kali, bahkan lebih tidak aman, jadi satu-satunya cara untuk pergi adalah makro."Anda telah memperkenalkan cara halus menembak diri sendiri di kaki"
Array 'asli' tidak menyimpan ukurannya. Oleh karena itu disarankan untuk menyimpan panjang array dalam variabel / const yang terpisah, dan meneruskannya setiap kali Anda melewati array, yaitu:
Anda HARUS selalu menghindari array asli (kecuali jika Anda tidak bisa, dalam hal ini, pikirkan saja). Jika Anda menulis C ++, gunakan wadah 'vektor' STL . "Dibandingkan dengan array, mereka memberikan kinerja yang hampir sama", dan mereka jauh lebih berguna!
Lihat: http://www.cplusplus.com/reference/stl/vector/
sumber
enum
deklarasi.sumber
Jika Anda benar-benar ingin melakukan ini untuk menyebarkan array Anda, saya sarankan menerapkan struktur untuk menyimpan pointer ke tipe yang Anda inginkan dari array dan integer yang mewakili ukuran array. Kemudian Anda bisa mengopernya ke fungsi Anda. Cukup tetapkan nilai variabel array (pointer ke elemen pertama) ke pointer itu. Kemudian Anda bisa pergi
Array.arr[i]
untuk mendapatkan elemen ke-i dan menggunakanArray.size
untuk mendapatkan jumlah elemen dalam array.Saya memasukkan beberapa kode untuk Anda. Ini tidak terlalu berguna tetapi Anda dapat memperluasnya dengan lebih banyak fitur. Jujur saja, jika ini adalah hal yang Anda inginkan, Anda harus berhenti menggunakan C dan menggunakan bahasa lain dengan fitur-fitur ini.
sumber
strcpy(d.name, name);
tanpa penanganan overflow.Cara terbaik adalah Anda menyimpan informasi ini, misalnya, dalam struktur:
Terapkan semua fungsi yang diperlukan seperti membuat, menghancurkan, memeriksa kesetaraan, dan semua yang Anda butuhkan. Lebih mudah untuk lulus sebagai parameter.
sumber
int elements
vssize_t elements
?Fungsi
sizeof
mengembalikan jumlah byte yang digunakan oleh array Anda di memori. Jika Anda ingin menghitung jumlah elemen dalam array Anda, Anda harus membagi nomor itu dengansizeof
tipe variabel array. Katakanlahint array[10];
, jika integer tipe variabel di komputer Anda adalah 32 bit (atau 4 byte), untuk mendapatkan ukuran array Anda, Anda harus melakukan hal berikut:sumber
Anda dapat menggunakan
&
operator. Ini kode sumbernya:Berikut adalah contoh keluarannya
sumber
ptrdiff_t
.sizeof()
hasil dalamsize_t
. C tidak mendefinisikan mana yang lebih luas atau lebih tinggi / peringkat yang sama. Jadi jenis hasil bagi((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
tidak tentusize_t
dan dengan demikian pencetakan denganz
dapat menyebabkan UB. Cukup menggunakanprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
sudah cukup.(char *)(&a+1)-(char *)a
bukan konstanta dan dapat dihitung pada saat run-time, bahkan dengan ukuran tetapa[10]
.sizeof(a)/sizeof(a[0])
konstan dilakukan pada waktu kompilasi dalam kasus ini.Jawaban paling sederhana:
sumber
Solusi yang lebih elegan
sumber
Di samping jawaban yang sudah disediakan, saya ingin menunjukkan kasus khusus dengan menggunakan
Jika
a
arraychar
,unsigned char
atausigned char
Anda tidak perlu menggunakansizeof
dua kali sejaksizeof
ekspresi dengan satu operan dari jenis ini selalu menghasilkan1
.Kutipan dari C18,6.5.3.4 / 4:
Dengan demikian,
sizeof(a) / sizeof (a[0])
akan sama denganNUMBER OF ARRAY ELEMENTS / 1
jikaa
adalah array bertipechar
,unsigned char
atausigned char
. Pembagian melalui 1 adalah mubazir.Dalam hal ini, Anda cukup menyingkat dan melakukan:
Sebagai contoh:
Jika Anda ingin bukti, berikut ini tautan ke GodBolt .
Meskipun demikian, divisi menjaga keamanan, jika jenisnya berubah secara signifikan (meskipun kasus ini jarang terjadi).
sumber
Catatan: Yang ini dapat memberi Anda perilaku tidak terdefinisi seperti yang ditunjukkan oleh MM dalam komentar.
Untuk lebih jelasnya lihat di sini dan juga di sini .
sumber
*
Operator tidak dapat diterapkan pada pointer masa-the-end*(&a+1) - a;
berbeda dari di(&a)[1] - a;
atas, jangan keduanya*(&a+1)
dan(&a)[1]
dihitung sebagai 1 melewati akhir?x[y]
didefinisikan sebagai*(x + (y))