ฉันจะกำหนดขนาดของอาร์เรย์ใน C ได้อย่างไร
นั่นคือจำนวนองค์ประกอบที่อาร์เรย์สามารถเก็บได้?
ฉันจะกำหนดขนาดของอาร์เรย์ใน C ได้อย่างไร
นั่นคือจำนวนองค์ประกอบที่อาร์เรย์สามารถเก็บได้?
คำตอบ:
บทสรุปผู้บริหาร:
int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
คำตอบแบบเต็ม:
ในการกำหนดขนาดของอาร์เรย์ในหน่วยไบต์คุณสามารถใช้sizeof
โอเปอเรเตอร์:
int a[17];
size_t n = sizeof(a);
บนคอมพิวเตอร์ของฉัน ints มีความยาว 4 ไบต์ดังนั้น n คือ 68
ในการกำหนดจำนวนองค์ประกอบในอาเรย์เราสามารถหารขนาดทั้งหมดของอาเรย์โดยขนาดขององค์ประกอบอาเรย์ คุณสามารถทำได้ด้วยประเภทดังนี้:
int a[17];
size_t n = sizeof(a) / sizeof(int);
และรับคำตอบที่ถูกต้อง (68/4 = 17) แต่ถ้าประเภทของการ
a
เปลี่ยนแปลงคุณจะมีข้อผิดพลาดที่น่ารังเกียจถ้าคุณลืมที่จะเปลี่ยนsizeof(int)
เช่นกัน
ดังนั้นตัวหารที่ต้องการคือsizeof(a[0])
หรือเทียบเท่าsizeof(*a)
ขนาดขององค์ประกอบแรกของอาร์เรย์
int a[17];
size_t n = sizeof(a) / sizeof(a[0]);
ข้อดีอีกอย่างคือตอนนี้คุณสามารถทำให้พารามิเตอร์ชื่ออาร์เรย์ในแมโครได้อย่างง่ายดายและรับ:
#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
int a[17];
size_t n = NELEMS(a);
ARRAYSIZE
กำหนด makro ไว้WinNT.h
(ซึ่งได้รับความสนใจจากส่วนหัวอื่น ๆ ) ดังนั้นผู้ใช้ WinAPI จึงไม่จำเป็นต้องกำหนด Makro ของตัวเอง
static int a[20];
เช่น แต่ความคิดเห็นของคุณมีประโยชน์สำหรับผู้อ่านที่อาจไม่เข้าใจความแตกต่างระหว่างอาร์เรย์และตัวชี้
sizeof
วิธีเป็นวิธีที่เหมาะสมIFFคุณจะจัดการกับอาร์เรย์ไม่ได้รับเป็นพารามิเตอร์ อาร์เรย์ที่ส่งเป็นพารามิเตอร์ไปยังฟังก์ชันจะถือว่าเป็นตัวชี้ดังนั้นsizeof
จะส่งคืนขนาดของตัวชี้แทนที่จะเป็นอาร์เรย์
ดังนั้นฟังก์ชั่นภายในวิธีการนี้จะไม่ทำงาน ให้ส่งพารามิเตอร์เพิ่มเติมเสมอเพื่อsize_t size
ระบุจำนวนองค์ประกอบในอาร์เรย์
ทดสอบ:
#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]) ));
}
เอาต์พุต (ใน Linux OS 64 บิต):
sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
เอาต์พุต (ในระบบปฏิบัติการ windows 32 บิต):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
length of parameter:2
ถ้าเพียงแค่ตัวชี้ไปยังองค์ประกอบอาร์เรย์ที่ 1 ถูกส่งผ่านไป
(sizeof array / sizeof *array)
คุณสามารถใช้ไวยากรณ์ประกอบการถ้าเป็นที่ทำให้คุณรู้สึกดีขึ้น:
เป็นเรื่องน่าสังเกตว่าsizeof
ไม่ช่วยเมื่อจัดการกับค่าอาร์เรย์ที่สลายตัวไปยังตัวชี้: แม้ว่ามันจะชี้ไปที่จุดเริ่มต้นของอาร์เรย์ไปยังคอมไพเลอร์มันก็เหมือนกับตัวชี้ไปยังองค์ประกอบเดียวของอาร์เรย์นั้น . ตัวชี้ไม่ "จำ" สิ่งอื่นใดเกี่ยวกับอาร์เรย์ที่ใช้ในการเริ่มต้น
int a[10];
int* p = a;
assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
char
32 บิต มาตรฐานทั้งหมดบอกว่าเป็นค่าจำนวนเต็มตั้งแต่ 0 ถึง 127 สามารถแสดงและช่วงอย่างน้อย -127 ถึง 127 (ลงนามถ่าน) หรือ 0 ถึง 255 (ถ่านไม่ได้ลงนาม)
ขนาดของ "เคล็ดลับ" เป็นวิธีที่ดีที่สุดที่ฉันรู้ด้วยการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ แต่ (สำหรับฉันนี่เป็นสัตว์เลี้ยงที่สำคัญที่สุด) การเปลี่ยนแปลงที่สำคัญในการใช้วงเล็บ
เมื่อรายการ Wikipedia ชัดเจน C ของsizeof
ไม่ใช่หน้าที่ มันเป็นผู้ประกอบการ ดังนั้นจึงไม่จำเป็นต้องใช้วงเล็บในการโต้แย้งเว้นแต่ว่าอาร์กิวเมนต์เป็นชื่อประเภท ง่ายต่อการจดจำเนื่องจากทำให้อาร์กิวเมนต์มีลักษณะเหมือนนิพจน์ที่ส่งซึ่งใช้วงเล็บ
ดังนั้น: หากคุณมีสิ่งต่อไปนี้:
int myArray[10];
คุณสามารถค้นหาจำนวนองค์ประกอบด้วยรหัสดังนี้:
size_t n = sizeof myArray / sizeof *myArray;
สำหรับฉันแล้วการอ่านง่ายกว่าทางเลือกอื่นที่มีวงเล็บ ฉันชอบที่จะใช้เครื่องหมายดอกจันในส่วนทางขวาของแผนกเนื่องจากมีความรัดกุมมากกว่าการทำดัชนี
แน่นอนว่านี่เป็นเวลารวบรวมทั้งหมดดังนั้นไม่จำเป็นต้องกังวลเกี่ยวกับการแบ่งที่ส่งผลต่อประสิทธิภาพของโปรแกรม ดังนั้นใช้แบบฟอร์มนี้ได้ทุกที่
เป็นการดีที่สุดที่จะใช้ขนาดของวัตถุจริงเมื่อคุณมีหนึ่งชิ้นแทนที่จะเป็นชนิดตั้งแต่นั้นคุณไม่ต้องกังวลเกี่ยวกับการทำผิดพลาดและระบุประเภทที่ไม่ถูกต้อง
ตัวอย่างเช่นสมมติว่าคุณมีฟังก์ชั่นที่ส่งออกข้อมูลบางอย่างเป็นสตรีมของไบต์เช่นข้ามเครือข่าย ลองเรียกใช้ฟังก์ชั่นsend()
และทำให้มันเป็นอาร์กิวเมนต์ที่ตัวชี้ไปยังวัตถุที่จะส่งและจำนวนไบต์ในวัตถุ ดังนั้นต้นแบบจะกลายเป็น:
void send(const void *object, size_t size);
จากนั้นคุณต้องส่งจำนวนเต็มเพื่อให้คุณเขียนโค้ดดังนี้:
int foo = 4711;
send(&foo, sizeof (int));
ตอนนี้คุณได้แนะนำวิธีการถ่ายภาพด้วยตัวคุณเองด้วยการระบุประเภทของfoo
สองสถานที่ หากมีการเปลี่ยนแปลง แต่อย่างใดอย่างหนึ่งไม่ได้รหัสจะแบ่ง ดังนั้นทำเช่นนี้เสมอ:
send(&foo, sizeof foo);
ตอนนี้คุณได้รับการคุ้มครอง แน่นอนว่าคุณทำซ้ำชื่อของตัวแปร แต่มีความเป็นไปได้สูงที่จะแตกหักในวิธีที่คอมไพเลอร์สามารถตรวจพบได้หากคุณเปลี่ยน
sizeof(int)
จำเป็นต้องมีคำแนะนำน้อยกว่าsizeof(foo)
?
int x = 1+1;
int x = (1+1);
ที่นี่วงเล็บมีความสวยงามอย่างแท้จริง
sizeof
จะคงที่ใน C ++ และ C89 เสมอ ด้วยอาร์เรย์ความยาวผันแปรของ C99 มันอาจถูกประเมินที่รันไทม์
sizeof
อาจเป็นตัวดำเนินการ แต่ควรถือว่าเป็นฟังก์ชันตาม Linus Torvalds ฉันเห็นด้วย. อ่านเหตุผลของเขาที่นี่: lkml.org/lkml/2012/7/11/103
int size = (&arr)[1] - arr;
ลองดูลิงค์นี้สำหรับคำอธิบาย
ptrdiff_t
ผลของตัวชี้การลบมีประเภท (โดยทั่วไปในระบบ 64 บิตจะเป็นประเภทที่ใหญ่กว่าint
) แม้ว่าคุณint
จะเปลี่ยนเป็นptrdiff_t
รหัสนี้ แต่ก็ยังมีข้อผิดพลาดหากกินarr
พื้นที่มากกว่าครึ่งหนึ่ง
/3G
ตัวเลือกที่คุณมีการแบ่งผู้ใช้ / เคอร์เนล 3G / 1G ซึ่งช่วยให้คุณมีขนาดอาร์เรย์ได้ถึง 75% ของขนาดพื้นที่ที่อยู่
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
ว่าเป็นตัวแปรระดับโลก buf3[]
การประกาศล้มเหลวเนื่องจาก(&buf1)[1] - buf1
ไม่ใช่ค่าคงที่
คุณสามารถใช้ตัวดำเนินการ sizeof แต่มันจะไม่ทำงานสำหรับฟังก์ชั่นเพราะมันจะใช้การอ้างอิงของตัวชี้ที่คุณสามารถทำต่อไปนี้เพื่อค้นหาความยาวของอาร์เรย์:
len = sizeof(arr)/sizeof(arr[0])
รหัสเดิมพบได้ที่นี่: โปรแกรม C เพื่อค้นหาจำนวนองค์ประกอบในอาร์เรย์
หากคุณทราบชนิดข้อมูลของอาร์เรย์คุณสามารถใช้สิ่งต่อไปนี้
int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};
int noofele = sizeof(arr)/sizeof(int);
หรือถ้าคุณไม่ทราบชนิดข้อมูลของอาร์เรย์คุณสามารถใช้สิ่งต่อไปนี้
noofele = sizeof(arr)/sizeof(arr[0]);
หมายเหตุ: สิ่งนี้ใช้ได้เฉพาะเมื่อไม่ได้กำหนดอาเรย์ในขณะใช้งาน (เช่น malloc) และอาเรย์จะไม่ผ่านในฟังก์ชัน ในทั้งสองกรณีarr
(ชื่ออาร์เรย์) เป็นตัวชี้
int noofele = sizeof(arr)/sizeof(int);
int noofele = 9;
เป็นเพียงครึ่งทางดีกว่าการเข้ารหัส การใช้sizeof(arr)
รักษาความยืดหยุ่นควรเปลี่ยนขนาดอาร์เรย์ ยังsizeof(int)
ต้องการการปรับปรุงควรarr[]
เปลี่ยนประเภท ดีกว่าที่จะใช้sizeof(arr)/sizeof(arr[0])
แม้ว่าชนิดเป็นที่รู้จักกันดี ไม่ชัดเจนว่าทำไมใช้int
สำหรับnoofele
เทียบกับประเภทที่ส่งกลับโดยsize_t
sizeof()
แมโครARRAYELEMENTCOUNT(x)
ว่าทุกคนจะทำให้การใช้ประเมินไม่ถูกต้อง สิ่งนี้แนบเนียนเป็นเพียงเรื่องละเอียดอ่อนเพราะคุณไม่สามารถใช้นิพจน์ที่ส่งผลให้เกิดประเภท 'อาร์เรย์'
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))
ARRAYELEMENTCOUNT(p + 1);
ประเมินผลจริงเป็น:
(sizeof (p + 1) / sizeof (p + 1[0]));
แต่ทว่า
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])
ARRAYELEMENTCOUNT(p + 1);
ประเมินอย่างถูกต้องเป็น:
(sizeof (p + 1) / sizeof (p + 1)[0]);
นี่ไม่ได้มีอะไรเกี่ยวข้องกับขนาดของอาร์เรย์อย่างชัดเจน ฉันเพิ่งสังเกตเห็นข้อผิดพลาดจำนวนมากจากการไม่สังเกตว่าการทำงานของตัวประมวลผลล่วงหน้า C เป็นอย่างไร คุณล้อมพารามิเตอร์แมโครเสมอไม่ใช่นิพจน์ที่อาจเกี่ยวข้อง
สิ่งนี้ถูกต้อง ตัวอย่างของฉันไม่ดี แต่นั่นคือสิ่งที่จะเกิดขึ้นจริง ดังที่ฉันได้กล่าวไปแล้วว่าp + 1
จะจบลงด้วยการเป็นประเภทตัวชี้และทำให้ทั้งแมโครเป็นโมฆะ (เช่นถ้าคุณพยายามใช้มาโครในฟังก์ชันที่มีพารามิเตอร์ตัวชี้)
ในตอนท้ายของวันในกรณีพิเศษนี้ความผิดไม่สำคัญ (ดังนั้นฉันแค่เสียเวลาของทุกคน huzzah!) เพราะคุณไม่มีการแสดงออกด้วย 'อาร์เรย์' ประเภทใด แต่จริงๆแล้วประเด็นเกี่ยวกับคำบรรยายย่อยการประเมินผลของโปรเซสเซอร์ที่ฉันคิดว่าเป็นสิ่งสำคัญ
(sizeof (x) / sizeof (*x))
หรือไม่
สำหรับอาร์เรย์หลายมิติมันซับซ้อนกว่าเล็กน้อย บ่อยครั้งที่คนกำหนดค่าคงที่ของแมโครที่ชัดเจนเช่น
#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", " " },
};
แต่คงที่เหล่านี้สามารถประเมินที่รวบรวมเวลาเกินไปกับsizeof :
#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);
โปรดทราบว่ารหัสนี้ทำงานใน C และ C ++ สำหรับอาร์เรย์ที่มีมากกว่าสองมิติให้ใช้
sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
ฯลฯ ไม่มีที่สิ้นสุด
sizeof(array) / sizeof(array[0])
array
ได้คุณไม่จำเป็นต้องใช้sizeof(array) / sizeof(array[0])
ถ้าarray
เป็นอาร์เรย์ของทั้งสองchar
, unsigned char
หรือsigned char
- อ้างจาก C18,6.5.3.4 / 4: "เมื่อ sizeof ถูกนำไปใช้ถูกดำเนินการที่มีประเภทถ่าน, ถ่านที่ไม่ได้ลงชื่อหรือลงนามถ่าน , (หรือรุ่นที่ผ่านการรับรอง) ผลลัพธ์ที่ได้คือ 1 " ในกรณีนี้คุณก็สามารถทำsizeof(array)
ตามที่อธิบายไว้ในทุ่มเทของฉันคำตอบ
ขนาดของอาร์เรย์ใน 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
size_t size_of_element
ยังint
มีint n = sizeof (a) / sizeof (a[0]);
และไม่ได้size_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
, int n
ที่ใช้ในการint n = sizeof (a) / sizeof (a[0]);
ไม่เพียงพอ (มันเป็น UB) การใช้size_t n = sizeof (a) / sizeof (a[0]);
ไม่ทำให้เกิดปัญหานี้
ฉันจะแนะนำไม่ให้ใช้sizeof
(แม้ว่าจะสามารถใช้) เพื่อให้ได้สองขนาดที่แตกต่างกันของอาร์เรย์ไม่ว่าจะเป็นจำนวนขององค์ประกอบหรือในไบต์ซึ่งเป็นสองกรณีสุดท้ายที่ฉันแสดงที่นี่ สำหรับสองขนาดแต่ละขนาดสามารถใช้มาโครที่แสดงด้านล่างเพื่อให้ปลอดภัยยิ่งขึ้น เหตุผลก็คือการทำให้ความตั้งใจของรหัสนั้นชัดเจนสำหรับผู้ดูแลรักษาและความแตกต่างsizeof(ptr)
จากการsizeof(arr)
มองแวบแรก (ซึ่งเขียนในลักษณะนี้ไม่ชัดเจน) ดังนั้นข้อบกพร่องนั้นชัดเจนสำหรับทุกคนที่อ่านรหัส
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)
(กำหนดไว้ด้านล่าง) จำเป็นต้องใช้ตามที่-Wsizeof-pointer-div
เป็นรถ (ตั้งแต่เมษายน 2563):
#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__; \
} \
) \
)
มีข้อบกพร่องที่สำคัญเกี่ยวกับหัวข้อนี้: https://lkml.org/lkml/2015/9/3/428
ฉันไม่เห็นด้วยกับวิธีการแก้ปัญหาที่ Linus ให้ซึ่งจะไม่ใช้สัญกรณ์อาร์เรย์สำหรับพารามิเตอร์ของฟังก์ชั่น
ฉันชอบสัญกรณ์อาร์เรย์เป็นเอกสารที่ตัวชี้กำลังถูกใช้เป็นอาร์เรย์ แต่นั่นหมายความว่าจะต้องใช้วิธีแก้ปัญหาที่ไม่สามารถพิสูจน์ได้ดังนั้นจึงเป็นไปไม่ได้ที่จะเขียนรหัสบั๊กกี้
จากอาร์เรย์เรามีสามขนาดที่เราอาจต้องการทราบ:
อันแรกนั้นง่ายมากและมันไม่สำคัญว่าเราจะจัดการกับอาเรย์หรือตัวชี้เพราะมันทำแบบเดียวกัน
ตัวอย่างการใช้งาน:
void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
qsort()
ต้องการค่านี้เป็นอาร์กิวเมนต์ที่สาม
สำหรับอีกสองขนาดซึ่งเป็นหัวข้อของคำถามเราต้องการตรวจสอบให้แน่ใจว่าเรากำลังจัดการกับอาเรย์และทำลายการคอมไพล์ถ้าไม่ใช่เพราะถ้าเราจัดการกับพอยน์เตอร์เราจะได้รับค่าที่ไม่ถูกต้อง . เมื่อการรวบรวมถูกทำลายเราจะสามารถเห็นได้อย่างง่ายดายว่าเราไม่ได้จัดการกับอาร์เรย์ แต่มีตัวชี้แทนและเราจะต้องเขียนโค้ดด้วยตัวแปรหรือแมโครที่เก็บขนาดของ อาร์เรย์ด้านหลังตัวชี้
อันนี้เป็นคำที่พบบ่อยที่สุดและคำตอบมากมายที่ให้มากับแมโครทั่วไปคือ ARRAY_SIZE:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
เนื่องจากผลลัพธ์ของ ARRAY_SIZE มักใช้กับตัวแปรที่ลงนามptrdiff_t
แล้วจึงเป็นการดีที่จะกำหนดตัวแปรที่ลงนามของแมโครนี้:
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
อาร์เรย์ที่มีPTRDIFF_MAX
สมาชิกมากกว่าจะให้ค่าที่ไม่ถูกต้องสำหรับแมโครรุ่นที่ลงนามแล้ว แต่จากการอ่าน C17 :: 6.5.6.9 อาร์เรย์เช่นนี้กำลังเล่นอยู่โดยไฟ เฉพาะARRAY_SIZE
และsize_t
ควรใช้ในกรณีเหล่านั้น
คอมไพเลอร์เวอร์ชันล่าสุดเช่น GCC 8 จะเตือนคุณเมื่อคุณใช้แมโครนี้กับตัวชี้ดังนั้นมันจึงปลอดภัย (มีวิธีอื่นที่จะทำให้ปลอดภัยกับคอมไพเลอร์รุ่นเก่า)
มันทำงานได้โดยการหารขนาดเป็นไบต์ของอาร์เรย์ทั้งหมดด้วยขนาดของแต่ละองค์ประกอบ
ตัวอย่างการใช้งาน:
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;
}
หากฟังก์ชั่นเหล่านี้ไม่ได้ใช้อาร์เรย์ แต่ให้เป็นพารามิเตอร์แทนรหัสเดิมจะไม่คอมไพล์ดังนั้นจึงเป็นไปไม่ได้ที่จะมีข้อบกพร่อง (เนื่องจากมีการใช้คอมไพเลอร์เวอร์ชันล่าสุดหรือใช้กลอุบายอื่น ๆ ) และเราต้องแทนที่การเรียกแมโครด้วยค่า:
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;
}
ARRAY_SIZE
มักใช้เป็นวิธีแก้ไขปัญหากับกรณีก่อนหน้า แต่กรณีนี้ไม่ค่อยเขียนอย่างปลอดภัยอาจเป็นเพราะพบได้น้อยกว่า
sizeof(arr)
วิธีที่ใช้กันจะได้รับค่านี้คือการใช้งาน ปัญหา: เหมือนกับก่อนหน้านี้; หากคุณมีตัวชี้แทนที่จะเป็นอาร์เรย์โปรแกรมของคุณจะไปที่ถั่ว
วิธีการแก้ปัญหาเกี่ยวข้องกับการใช้แมโครเดียวกันเหมือนก่อนซึ่งเรารู้ว่าจะปลอดภัย (มันแบ่งการรวบรวมถ้ามันถูกนำไปใช้กับตัวชี้):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
วิธีการทำงานนั้นง่ายมาก: มันยกเลิกการแบ่งที่ARRAY_SIZE
ทำดังนั้นหลังจากการยกเลิกทางคณิตศาสตร์คุณจะได้รับเพียงหนึ่งเดียวsizeof(arr)
แต่ด้วยความปลอดภัยที่เพิ่มขึ้นของการARRAY_SIZE
ก่อสร้าง
ตัวอย่างการใช้งาน:
void foo(ptrdiff_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
memset()
ต้องการค่านี้เป็นอาร์กิวเมนต์ที่สาม
ก่อนหน้านี้หากได้รับอาร์เรย์เป็นพารามิเตอร์ (ตัวชี้) มันจะไม่รวบรวมและเราจะต้องแทนที่การเรียกแมโครด้วยค่า:
void foo(ptrdiff_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
-Wsizeof-pointer-div
เป็นรถ :วันนี้ฉันพบว่าคำเตือนใหม่ใน GCC จะทำงานเฉพาะเมื่อมีการกำหนดมาโครในส่วนหัวที่ไม่ใช่ส่วนหัวของระบบ หากคุณกำหนดมาโครในส่วนหัวที่ติดตั้งในระบบของคุณ (ปกติ/usr/local/include/
หรือ/usr/include/
) ( #include <foo.h>
) คอมไพเลอร์จะไม่ส่งเสียงเตือน (ฉันลองใช้ GCC 9.3.0)
ดังนั้นเราจึงมี#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
และต้องการทำให้มันปลอดภัย เราจะต้องมี C11 _Static_assert()
และส่วนขยาย GCC บางส่วน: คำชี้แจงและการประกาศในนิพจน์ , __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]); \
} \
)
ตอนนี้ARRAY_SIZE()
ปลอดภัยอย่างสมบูรณ์และดังนั้นอนุพันธ์ทั้งหมดจะปลอดภัย
__arraycount()
:Libbsdให้มาโคร__arraycount()
ใน<sys/cdefs.h>
ซึ่งไม่ปลอดภัยเพราะมันขาดคู่ของวงเล็บ แต่เราสามารถเพิ่มวงเล็บเหล่านั้นเองและดังนั้นเราไม่จำเป็นต้องเขียนส่วนในส่วนหัวของเรา (ทำไมเราจะทำซ้ำรหัสที่มีอยู่แล้ว? ) มาโครนั้นถูกกำหนดไว้ในส่วนหัวของระบบดังนั้นหากเราใช้งานเราจะบังคับให้ใช้มาโครด้านบน
#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))
ระบบบางระบบมีให้nitems()
ใน<sys/param.h>
แทนและบางระบบให้ทั้งสอง คุณควรตรวจสอบระบบของคุณและใช้ระบบที่คุณมีและอาจใช้เงื่อนไข preprocessor บางอย่างเพื่อความสะดวกในการพกพาและรองรับทั้งคู่
ขออภัย({})
ส่วนขยาย gcc ไม่สามารถใช้ที่ขอบเขตไฟล์ sizeof(struct {})
เพื่อให้สามารถใช้มาโครที่ขอบเขตไฟล์ยืนยันคงที่จะต้องอยู่ภายใน จากนั้นคูณด้วยโดย0
ไม่ส่งผลต่อผลลัพธ์ การส่งไปยัง(int)
อาจเป็นการดีที่จะจำลองฟังก์ชันที่ส่งคืน(int)0
(ในกรณีนี้ไม่จำเป็น แต่จากนั้นจะสามารถใช้ซ้ำสำหรับสิ่งอื่น ๆ )
#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))
sizeof(arr)
) ที่จะไม่แสดงอื่น ๆARRAY_BYTES(arr)
:
sizeof
แต่ใช้สิ่งปลูกสร้างนี้แทน ถ้าคุณรู้สึกว่าการเขียนสิ่งปลูกสร้างเหล่านี้ทุกครั้งคุณอาจจะทำผิดพลาด (โดยทั่วไปถ้าคุณคัดลอกวางและยังพบบ่อยมากถ้าคุณเขียนพวกเขาทุกครั้งเพราะพวกเขามีวงเล็บจำนวนมาก) ...
sizeof
ไม่ปลอดภัยอย่างชัดเจน (เหตุผลอยู่ที่คำตอบ) และไม่ได้ใช้มาโคร แต่ใช้สิ่งก่อสร้างที่ฉันให้ไว้ทุกครั้งไม่ปลอดภัยมากขึ้นดังนั้นวิธีเดียวที่จะไป คือมาโคร
"คุณได้แนะนำวิธีการยิงด้วยตัวคุณเอง"
อาร์เรย์ C 'เนทีฟ' ไม่ได้เก็บขนาดไว้ ดังนั้นจึงแนะนำให้บันทึกความยาวของอาเรย์ในตัวแปร / const ที่แยกต่างหากและส่งผ่านเมื่อใดก็ตามที่คุณผ่านอาเรย์นั่นคือ:
#define MY_ARRAY_LENGTH 15
int myArray[MY_ARRAY_LENGTH];
คุณควรหลีกเลี่ยงอาร์เรย์ดั้งเดิม (ยกเว้นกรณีที่คุณไม่สามารถคำนึงถึงเท้าของคุณ) หากคุณกำลังเขียน C ++ ให้ใช้คอนเทนเนอร์ของเวกเตอร์ ' STL "เมื่อเทียบกับอาร์เรย์พวกเขาให้ประสิทธิภาพที่เหมือนกันเกือบ" และมีประโยชน์มากกว่า!
// 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();
enum
ประกาศ
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
หากคุณต้องการทำเช่นนี้เพื่อส่งผ่านอาร์เรย์ของคุณฉันขอแนะนำให้ใช้โครงสร้างเพื่อเก็บตัวชี้ไปยังประเภทที่คุณต้องการอาร์เรย์ของและจำนวนเต็มแทนขนาดของอาร์เรย์ จากนั้นคุณสามารถส่งผ่านสิ่งนั้นไปยังฟังก์ชั่นของคุณ เพียงกำหนดค่าตัวแปรอาร์เรย์ (ตัวชี้ไปยังองค์ประกอบแรก) ให้กับตัวชี้นั้น จากนั้นคุณสามารถไปArray.arr[i]
รับองค์ประกอบที่ i และใช้Array.size
เพื่อรับจำนวนองค์ประกอบในอาร์เรย์
ฉันรวมรหัสบางอย่างให้คุณ มันไม่ได้มีประโยชน์มากนัก แต่คุณก็สามารถขยายมันได้ด้วยคุณสมบัติอื่น ๆ เพื่อความซื่อสัตย์แม้ว่าหากสิ่งเหล่านี้เป็นสิ่งที่คุณต้องการคุณควรหยุดใช้ C และใช้ภาษาอื่นด้วยคุณสมบัติเหล่านี้
/* 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;
}
strcpy(d.name, name);
ไม่มีการจัดการโอเวอร์โฟลว์
วิธีที่ดีที่สุดคือคุณบันทึกข้อมูลนี้ตัวอย่างเช่นในโครงสร้าง:
typedef struct {
int *array;
int elements;
} list_s;
ใช้ฟังก์ชั่นที่จำเป็นทั้งหมดเช่นสร้างทำลายตรวจสอบความเท่าเทียมกันและทุกอย่างที่คุณต้องการ มันง่ายกว่าที่จะผ่านเป็นพารามิเตอร์
int elements
เทียบกับ size_t elements
?
ฟังก์ชั่นsizeof
จะคืนค่าจำนวนไบต์ที่อาเรย์ของคุณใช้ในหน่วยความจำ หากคุณต้องการคำนวณจำนวนองค์ประกอบในอาเรย์ของคุณคุณควรหารจำนวนนั้นด้วยsizeof
ประเภทตัวแปรของอาเรย์ สมมติว่าint array[10];
ถ้าจำนวนเต็มชนิดตัวแปรในคอมพิวเตอร์ของคุณเป็น 32 บิต (หรือ 4 ไบต์) เพื่อให้ได้ขนาดของอาร์เรย์คุณควรทำดังนี้:
int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
คุณสามารถใช้&
โอเปอเรเตอร์ นี่คือรหัสที่มา:
#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;
};
นี่คือตัวอย่างเอาต์พุต
1549216672
1549216712
---- diff----
4
The size of array a is 10
ptrdiff_t
ชี้การลบนำไปสู่ ผลในการsizeof()
size_t
C ไม่ได้กำหนดว่าจะกว้างหรือสูงกว่า / อันดับเดียวกัน ดังนั้นประเภทของความฉลาดทาง((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
จึงไม่แน่นอนsize_t
และด้วยการพิมพ์ด้วยz
สามารถนำไปสู่ UB เพียงแค่ใช้printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
ก็เพียงพอแล้ว
(char *)(&a+1)-(char *)a
ไม่ใช่ค่าคงที่และอาจคำนวณได้ในขณะใช้งานแม้ว่าจะเป็นขนาดคงที่a[10]
ก็ตาม sizeof(a)/sizeof(a[0])
ทำคงที่ ณ เวลารวบรวมในกรณีนี้
คำตอบที่ง่ายที่สุด:
#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;
}
ทางออกที่หรูหรากว่าจะเป็น
size_t size = sizeof(a) / sizeof(*a);
นอกเหนือจากคำตอบที่มีให้แล้วฉันต้องการชี้ให้เห็นกรณีพิเศษจากการใช้
sizeof(a) / sizeof (a[0])
ถ้าa
เป็นทั้งอาร์เรย์ของchar
, unsigned char
หรือsigned char
คุณไม่จำเป็นต้องใช้sizeof
สองครั้งตั้งแต่การแสดงออกกับหนึ่งในตัวถูกดำเนินการของประเภทนี้จะส่งผลให้เสมอsizeof
1
อ้างอิงจาก C18,6.5.3.4 / 4:
" เมื่อ
sizeof
นำมาใช้กับตัวถูกดำเนินการที่มีการพิมพ์char
,unsigned char
หรือsigned char
(หรือรุ่นที่มีคุณสมบัติดังกล่าว) ผลที่ได้คือ1
."
ดังนั้นsizeof(a) / sizeof (a[0])
จะเทียบเท่ากับNUMBER OF ARRAY ELEMENTS / 1
ถ้าa
เป็นอาร์เรย์ชนิดchar
, หรือunsigned char
signed char
การหารผ่าน 1 ซ้ำซ้อน
ในกรณีนี้คุณสามารถย่อและทำ:
sizeof(a)
ตัวอย่างเช่น:
char a[10];
size_t length = sizeof(a);
หากคุณต้องการหลักฐานที่นี่คือการเชื่อมโยงไปยังGodBolt
อย่างไรก็ตามฝ่ายรักษาความปลอดภัยถ้าประเภทการเปลี่ยนแปลงอย่างมีนัยสำคัญ (แม้ว่ากรณีเหล่านี้จะหายาก)
หมายเหตุ:สิ่งนี้สามารถทำให้คุณไม่ได้กำหนดพฤติกรรมตามที่ MM ระบุในความคิดเห็น
int a[10];
int size = (*(&a+1)-a) ;
ดูรายละเอียดเพิ่มเติม ที่นี่ และที่นี่
*
ผู้ประกอบการอาจจะไม่ถูกนำไปใช้เป็นตัวชี้ที่ผ่านมาที่ปลาย
*(&a+1) - a;
แตกต่างจาก(&a)[1] - a;
ข้างบนใช่ทั้งสองอย่าง *(&a+1)
และ(&a)[1]
นับเป็น 1 เมื่อสิ้นสุดใช่หรือไม่
x[y]
ถูกกำหนดเป็น*(x + (y))