Bagaimana cara menentukan ukuran array saya di C?

Jawaban:

1261

Ringkasan bisnis plan:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

Jawaban lengkap:

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);
Mark Harrison
sumber
6
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[])
{
    int array[] = { 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: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2

Output (dalam OS windows 32-bit):

sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
Elideb
sumber
10
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 Hoff
sumber
1
@ 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(const void *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.

beristirahat
sumber
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
Dr. Person Person II
37
int size = (&arr)[1] - arr;

Lihat tautan ini untuk penjelasan

Arjun Sreedharan
sumber
7
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:

len = sizeof(arr)/sizeof(arr[0])

Kode awalnya ditemukan di sini: Program C untuk menemukan jumlah elemen dalam array

Mohd Shibli
sumber
21

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.

Abhitesh khatri
sumber
4
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'.

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))

ARRAYELEMENTCOUNT(p + 1);

Sebenarnya dievaluasi sebagai:

(sizeof (p + 1) / sizeof (p + 1[0]));

Sedangkan

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])

ARRAYELEMENTCOUNT(p + 1);

Ini mengevaluasi dengan benar untuk:

(sizeof (p + 1) / sizeof (p + 1)[0]);

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.

Peter Mortensen
sumber
2
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

#define g_rgDialogRows   2
#define g_rgDialogCols   7

static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
    { " ",  " ",    " ",    " 494", " 210", " Generic Sample Dialog", " " },
    { " 1", " 330", " 174", " 88",  " ",    " OK",        " " },
};

Tetapi konstanta ini dapat dievaluasi pada waktu kompilasi juga dengan ukuran :

#define rows_of_array(name)       \
    (sizeof(name   ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name)    \
    (sizeof(name[0]) / sizeof(name[0][0]))

static char* g_rgDialog[][7] = { /* ... */ };

assert(   rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);

Perhatikan bahwa kode ini berfungsi dalam C dan C ++. Untuk array dengan penggunaan lebih dari dua dimensi

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

dll, tak terhingga.

Andreas Spindler
sumber
15
sizeof(array) / sizeof(array[0])
Ted Percival
sumber
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 a
int n = sizeof (a) / sizeof (a[0]);    // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a                                          
                                       // Size of each element = size of type
Yogeesh HT
sumber
4
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.


TL; DR:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

#define ARRAY_BYTES(arr)    (sizeof((arr)[0]) * ARRAY_SIZE(arr))

must_be_array(arr)(Didefinisikan di bawah) IS diperlukan seperti halnya -Wsizeof-pointer-divbuggy (per April / 2020):

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

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
  • 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.

Contoh penggunaan:

void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
        qsort(arr, nmemb, sizeof(arr[0]), cmp);
}

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:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))

Mengingat bahwa hasil ARRAY_SIZE biasanya digunakan dengan variabel bertanda jenis ptrdiff_t, ada baiknya untuk menentukan varian yang ditandatangani dari makro ini:

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

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):

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

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.

Contoh penggunaan:

void foo(ptrdiff_t nmemb)
{
        int arr[nmemb];

        memset(arr, 0, ARRAY_BYTES(arr));
}

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:

void foo(ptrdiff_t nmemb, int arr[nmemb])
{

        memset(arr, 0, sizeof(arr[0]) * nmemb);
}

Pembaruan (23 / apr / 2020): -Wsizeof-pointer-divbermasalah :

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 :

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_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.

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        __arraycount((arr));                                            \
}                                                                       \
)

#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

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).

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

#define ARRAY_SIZE(arr)         (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Cacahuete Frito
sumber
3
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:

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

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 ints
vector<int> numbers;  

// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
    numbers.push_back(i);

// Determine the size of the array
cout << numbers.size();

Lihat: http://www.cplusplus.com/reference/stl/vector/

Ohad
sumber
Saya telah membaca bahwa cara yang tepat untuk mendeklarasikan konstanta integer dalam C adalah dengan menggunakan enumdeklarasi.
Raffi Khatchadourian
Pertanyaannya adalah tentang C, bukan C ++. Jadi tidak ada STL.
Cacahuete Frito
11
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Andy Nugent
sumber
6
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
*/
typedef struct MyType
{
   int age;
   char name[20];
} MyType;
typedef struct MyTypeArray
{
   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. */
   MyTypeArray array = new_MyTypeArray(2, arr);
   /* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
   print_MyType_names(array);
   return 0;
}
Joel Dentici
sumber
1
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:

typedef struct {
     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.

Paulo Pinheiro
sumber
6
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:

int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Keivan
sumber
2

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])));


    return 0;
};

Berikut adalah contoh keluarannya

1549216672
1549216712
---- diff----
4
The size of array a is 10
Shih-En Chou
sumber
7
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.
chux - Reinstate Monica
1

Jawaban paling sederhana:

#include <stdio.h>

int main(void) {

    int a[] = {2,3,4,5,4,5,6,78,9,91,435,4,5,76,7,34};//for Example only
    int size;

    size = sizeof(a)/sizeof(a[0]);//Method

    printf ("size = %d",size);
    return 0;
}
Jency
sumber
1

Solusi yang lebih elegan

size_t size = sizeof(a) / sizeof(*a);
Azatik1000
sumber
0

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).

RobertS mendukung Monica Cellio
sumber
1
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 .

Pygirl
sumber
2
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.
QuentinUK