ฉันจะกำหนดขนาดของอาร์เรย์ใน C ได้อย่างไร


1031

ฉันจะกำหนดขนาดของอาร์เรย์ใน C ได้อย่างไร

นั่นคือจำนวนองค์ประกอบที่อาร์เรย์สามารถเก็บได้?


2
สำหรับประเภทความปลอดภัยดูstackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers
TS

3
ขอแสดงความยินดีกับการโหวต 1,000 ครั้ง!
SS Anne

คำตอบ:


1260

บทสรุปผู้บริหาร:

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

6
รหัสที่สร้างขึ้นจะเหมือนกันเนื่องจากคอมไพเลอร์ทราบประเภทของ * int_arr ณ เวลารวบรวม (ดังนั้นค่าของ sizeof (* int_arr)) มันจะเป็นค่าคงที่และคอมไพเลอร์สามารถปรับให้เหมาะสม
Mark Harrison

10
มันควรจะเป็นกรณีที่มีคอมไพเลอร์ทั้งหมดเนื่องจากผลของขนาดของกำหนดไว้เป็นค่าคงที่เวลารวบรวม
Mark Harrison

451
สำคัญ : อย่าหยุดอ่านที่นี่อ่านคำตอบต่อไป! ใช้งานได้กับอาร์เรย์ในสแต็กเท่านั้นเช่นหากคุณใช้ malloc () หรือเข้าถึงพารามิเตอร์ฟังก์ชั่นแสดงว่าคุณไม่มีโชค ดูด้านล่าง
Markus

7
สำหรับการเขียนโปรแกรม Windows API ใน C หรือ C ++ จะมีการARRAYSIZEกำหนด makro ไว้WinNT.h(ซึ่งได้รับความสนใจจากส่วนหัวอื่น ๆ ) ดังนั้นผู้ใช้ WinAPI จึงไม่จำเป็นต้องกำหนด Makro ของตัวเอง
Lumi

17
@ Markus มันทำงานได้สำหรับตัวแปรใด ๆ ที่มีประเภทอาร์เรย์; สิ่งนี้ไม่จำเป็นต้องเป็น "บนสแต็ก" static int a[20];เช่น แต่ความคิดเห็นของคุณมีประโยชน์สำหรับผู้อ่านที่อาจไม่เข้าใจความแตกต่างระหว่างอาร์เรย์และตัวชี้
MM

808

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

10
ทำไมlength of parameter:2ถ้าเพียงแค่ตัวชี้ไปยังองค์ประกอบอาร์เรย์ที่ 1 ถูกส่งผ่านไป
Bbvarghe

16
@Bvarvare นั่นเป็นเพราะตัวชี้ในระบบ 64 บิตคือ 8 ไบต์ (ขนาดของ (intArray)) แต่ ints ยังคง (ปกติ) ยาว 4 ไบต์ (ขนาดของ (intArray [0])
Elideb

13
@Pacerier: ไม่มีรหัสที่ถูกต้อง - วิธีการแก้ปัญหาตามปกติคือการส่งผ่านความยาวพร้อมกับอาร์เรย์เป็นอาร์กิวเมนต์ที่แยกต่างหาก
Jean Hominal

10
รอดังนั้นจึงไม่มีวิธีการเข้าถึงอาร์เรย์โดยตรงจากตัวชี้และดูขนาดของมัน? ใหม่ถึง C ที่นี่
sudo

7
@Michael Trouw: (sizeof array / sizeof *array)คุณสามารถใช้ไวยากรณ์ประกอบการถ้าเป็นที่ทำให้คุณรู้สึกดีขึ้น:
chqrlie

134

เป็นเรื่องน่าสังเกตว่าsizeofไม่ช่วยเมื่อจัดการกับค่าอาร์เรย์ที่สลายตัวไปยังตัวชี้: แม้ว่ามันจะชี้ไปที่จุดเริ่มต้นของอาร์เรย์ไปยังคอมไพเลอร์มันก็เหมือนกับตัวชี้ไปยังองค์ประกอบเดียวของอาร์เรย์นั้น . ตัวชี้ไม่ "จำ" สิ่งอื่นใดเกี่ยวกับอาร์เรย์ที่ใช้ในการเริ่มต้น

int a[10];
int* p = a;

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));

1
@ แมกนัส: มาตรฐานกำหนด sizeof ให้ผลเป็นจำนวนไบต์ในวัตถุและ sizeof (ถ่าน) ที่เป็นหนึ่งเสมอ จำนวนบิตในไบต์เป็นการใช้งานเฉพาะ แก้ไข: ANSI C ++ มาตรามาตรฐาน 5.3.3 Sizeof: "ตัวดำเนินการ sizeof ให้จำนวนไบต์ในการแทนออบเจกต์ของตัวถูกดำเนินการ [... ] sizeof (char), sizeof (ถ่านที่ลงนามแล้ว) และ sizeof (ถ่านที่ไม่ได้ลงชื่อ) 1; ผลลัพธ์ของขนาดที่นำไปใช้กับชนิดพื้นฐานอื่น ๆ คือการกำหนดการใช้งาน "
Skizz

ส่วน 1.6 โมเดลหน่วยความจำ C ++: "หน่วยเก็บข้อมูลพื้นฐานในโมเดลหน่วยความจำ C ++ คือไบต์ไบต์อย่างน้อยใหญ่พอที่จะมีสมาชิกของชุดอักขระการดำเนินการขั้นพื้นฐานและประกอบด้วยลำดับบิตที่ต่อเนื่องกันจำนวน ซึ่งมีการกำหนดการใช้งาน "
Skizz

2
ฉันจำได้ว่า CRAY มี C ด้วยchar32 บิต มาตรฐานทั้งหมดบอกว่าเป็นค่าจำนวนเต็มตั้งแต่ 0 ถึง 127 สามารถแสดงและช่วงอย่างน้อย -127 ถึง 127 (ลงนามถ่าน) หรือ 0 ถึง 255 (ถ่านไม่ได้ลงนาม)
vonbrand

5
นี่คือการตอบสนองที่ยอดเยี่ยม ฉันต้องการแสดงความคิดเห็นว่าการยืนยันข้างต้นทั้งหมดได้รับการประเมินเป็น TRUE
Javad

49

ขนาดของ "เคล็ดลับ" เป็นวิธีที่ดีที่สุดที่ฉันรู้ด้วยการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ แต่ (สำหรับฉันนี่เป็นสัตว์เลี้ยงที่สำคัญที่สุด) การเปลี่ยนแปลงที่สำคัญในการใช้วงเล็บ

เมื่อรายการ 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);

ตอนนี้คุณได้รับการคุ้มครอง แน่นอนว่าคุณทำซ้ำชื่อของตัวแปร แต่มีความเป็นไปได้สูงที่จะแตกหักในวิธีที่คอมไพเลอร์สามารถตรวจพบได้หากคุณเปลี่ยน


Btw พวกเขามีคำแนะนำเหมือนกันในระดับโปรเซสเซอร์หรือไม่ ไม่sizeof(int)จำเป็นต้องมีคำแนะนำน้อยกว่าsizeof(foo)?
Pacerier

@Pierier: ไม่พวกเขาเหมือนกัน คิดว่าเมื่อเทียบกับint x = 1+1; int x = (1+1);ที่นี่วงเล็บมีความสวยงามอย่างแท้จริง
quetzalcoatl

@Aidiakapi ไม่จริงพิจารณา C99 VLA
คลาย

@unwind ขอบคุณฉันยืนแก้ไข หากต้องการแก้ไขความคิดเห็นของฉันsizeofจะคงที่ใน C ++ และ C89 เสมอ ด้วยอาร์เรย์ความยาวผันแปรของ C99 มันอาจถูกประเมินที่รันไทม์
Aidiakapi

2
sizeofอาจเป็นตัวดำเนินการ แต่ควรถือว่าเป็นฟังก์ชันตาม Linus Torvalds ฉันเห็นด้วย. อ่านเหตุผลของเขาที่นี่: lkml.org/lkml/2012/7/11/103
Dr. Person Person II

37
int size = (&arr)[1] - arr;

ลองดูลิงค์นี้สำหรับคำอธิบาย


7
nitpick ขนาดเล็ก: ptrdiff_tผลของตัวชี้การลบมีประเภท (โดยทั่วไปในระบบ 64 บิตจะเป็นประเภทที่ใหญ่กว่าint) แม้ว่าคุณintจะเปลี่ยนเป็นptrdiff_tรหัสนี้ แต่ก็ยังมีข้อผิดพลาดหากกินarrพื้นที่มากกว่าครึ่งหนึ่ง
MM

2
@MM nitpick ขนาดเล็กอีกตัว: พื้นที่ของที่อยู่ไม่ใหญ่เท่ากับขนาดตัวชี้ของระบบส่วนใหญ่ทั้งนี้ขึ้นอยู่กับสถาปัตยกรรมระบบของคุณ ตัวอย่างเช่น Windows จำกัด พื้นที่ที่อยู่สำหรับแอปพลิเคชัน 64 บิตเป็น 8TB หรือ 44 บิต ดังนั้นแม้ว่าคุณจะมีอาร์เรย์ที่มีขนาดใหญ่กว่าครึ่งหนึ่งของพื้นที่ที่อยู่ของคุณ 4.1TB แต่ก็จะไม่เป็นข้อบกพร่อง เฉพาะในกรณีที่ที่อยู่ของคุณเกิน 63 บิตในระบบเหล่านั้นเป็นไปได้ที่จะพบข้อผิดพลาดดังกล่าว โดยทั่วไปไม่ต้องกังวลกับมัน
Aidiakapi

1
@Aidiakapi บน x86 Linux 32 บิตหรือบน Windows พร้อม/3Gตัวเลือกที่คุณมีการแบ่งผู้ใช้ / เคอร์เนล 3G / 1G ซึ่งช่วยให้คุณมีขนาดอาร์เรย์ได้ถึง 75% ของขนาดพื้นที่ที่อยู่
Ruslan

1
พิจารณาfoo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];ว่าเป็นตัวแปรระดับโลก buf3[]การประกาศล้มเหลวเนื่องจาก(&buf1)[1] - buf1ไม่ใช่ค่าคงที่
chux - Reinstate Monica

2
นี่คือพฤติกรรมที่ไม่ได้กำหนดทางเทคนิคตามมาตรฐานอย่างชัดเจนไม่อนุญาตให้ dereferencing ผ่านจุดสิ้นสุดของอาร์เรย์ (แม้ว่าคุณจะไม่พยายามอ่านค่าที่เก็บไว้)
MM

26

คุณสามารถใช้ตัวดำเนินการ sizeof แต่มันจะไม่ทำงานสำหรับฟังก์ชั่นเพราะมันจะใช้การอ้างอิงของตัวชี้ที่คุณสามารถทำต่อไปนี้เพื่อค้นหาความยาวของอาร์เรย์:

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

รหัสเดิมพบได้ที่นี่: โปรแกรม C เพื่อค้นหาจำนวนองค์ประกอบในอาร์เรย์


21

หากคุณทราบชนิดข้อมูลของอาร์เรย์คุณสามารถใช้สิ่งต่อไปนี้

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(ชื่ออาร์เรย์) เป็นตัวชี้


4
int noofele = sizeof(arr)/sizeof(int);int noofele = 9;เป็นเพียงครึ่งทางดีกว่าการเข้ารหัส การใช้sizeof(arr)รักษาความยืดหยุ่นควรเปลี่ยนขนาดอาร์เรย์ ยังsizeof(int)ต้องการการปรับปรุงควรarr[]เปลี่ยนประเภท ดีกว่าที่จะใช้sizeof(arr)/sizeof(arr[0])แม้ว่าชนิดเป็นที่รู้จักกันดี ไม่ชัดเจนว่าทำไมใช้intสำหรับnoofeleเทียบกับประเภทที่ส่งกลับโดยsize_t sizeof()
chux - Reinstate Monica

19

แมโคร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!) เพราะคุณไม่มีการแสดงออกด้วย 'อาร์เรย์' ประเภทใด แต่จริงๆแล้วประเด็นเกี่ยวกับคำบรรยายย่อยการประเมินผลของโปรเซสเซอร์ที่ฉันคิดว่าเป็นสิ่งสำคัญ


2
ขอบคุณสำหรับคำอธิบาย เวอร์ชั่นดั้งเดิมส่งผลให้เกิดข้อผิดพลาดในการคอมไพล์ เสียงดังรายงาน "ค่าตัวห้อยไม่ใช่อาร์เรย์ตัวชี้หรือเวกเตอร์" ดูเหมือนว่าพฤติกรรมที่เป็นที่นิยมในกรณีนี้แม้ว่าความคิดเห็นของคุณเกี่ยวกับลำดับการประเมินในมาโครนั้นทำได้ดี
Mark Harrison

1
ฉันไม่ได้นึกถึงคอมไพเลอร์ที่ร้องเรียนว่าเป็นการแจ้งเตือนอัตโนมัติของประเภทที่ไม่ถูกต้อง ขอบคุณ!

3
มีเหตุผลที่จะไม่ใช้(sizeof (x) / sizeof (*x))หรือไม่
ร้ายแรง

16

สำหรับอาร์เรย์หลายมิติมันซับซ้อนกว่าเล็กน้อย บ่อยครั้งที่คนกำหนดค่าคงที่ของแมโครที่ชัดเจนเช่น

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

ฯลฯ ไม่มีที่สิ้นสุด


15
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)ตามที่อธิบายไว้ในทุ่มเทของฉันคำตอบ
RobertS สนับสนุน Monica Cellio

15

ขนาดของอาร์เรย์ใน 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

4
อยากรู้อยากเห็นว่ารหัสที่ใช้size_t size_of_elementยังintมีint n = sizeof (a) / sizeof (a[0]); และไม่ได้size_t n = sizeof (a) / sizeof (a[0]);
Chux - คืนสิทธิ์ให้กับโมนิกา

1
สวัสดี @Yogeesh HT คุณช่วยตอบข้อสงสัยของ chux ได้ไหม ฉันยังอยากรู้มากว่า int n = sizeof (a) / sizeof (a [0]) ให้ความยาวของอาเรย์อย่างไรและทำไมเราไม่ใช้ size_t สำหรับความยาวของอาเรย์ ใครตอบได้บ้าง
สมอง

1
@Brain sizeof (a) ให้ขนาดขององค์ประกอบทั้งหมดที่มีอยู่ในอาร์เรย์ a sizeof (a [0]) ให้ขนาดขององค์ประกอบที่ 1 สมมติว่า a = {1,2,3,4,5} sizeof (a) = 20bytes (ถ้า sizeof (int) = 4bytes ทวีคูณ 5), sizeof (a [0]) = 4bytes ดังนั้น 20/4 = 5 กล่าวคือไม่มีองค์ประกอบ
Yogeesh HT

2
@YogeeshHT สำหรับอาร์เรย์มีขนาดใหญ่มากชอบchar a[INT_MAX + 1u];, int nที่ใช้ในการint n = sizeof (a) / sizeof (a[0]);ไม่เพียงพอ (มันเป็น UB) การใช้size_t n = sizeof (a) / sizeof (a[0]);ไม่ทำให้เกิดปัญหานี้
chux - Reinstate Monica

13

ฉันจะแนะนำไม่ให้ใช้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);
}

อัปเดต (23 / apr / 2020): -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()ปลอดภัยอย่างสมบูรณ์และดังนั้นอนุพันธ์ทั้งหมดจะปลอดภัย


ปรับปรุง: libbsd ให้__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))

3
คุณจะอธิบายได้ไหมว่าทำไมการลงคะแนนแบบนี้? แสดงให้เห็นว่าการแก้ปัญหาการก่อสร้างที่ไม่ปลอดภัยและร่วมกัน (กsizeof(arr)) ที่จะไม่แสดงอื่น ๆARRAY_BYTES(arr):
Cacahuete Frito

2
ARRAY_SIZE เป็นเรื่องปกติมากพอที่จะใช้อย่างอิสระและ ARRAY_BYTES นั้นมีความชัดเจนมากในชื่อของมันควรจะกำหนดไว้ถัดจาก ARRAY_SIZE เพื่อให้ผู้ใช้สามารถเห็นทั้งสองได้อย่างง่ายดายและจากการใช้งานฉันไม่คิดว่าทุกคนที่อ่านรหัสมีข้อสงสัย มันทำ สิ่งที่ฉันหมายถึงคือการไม่ใช้แบบง่ายsizeofแต่ใช้สิ่งปลูกสร้างนี้แทน ถ้าคุณรู้สึกว่าการเขียนสิ่งปลูกสร้างเหล่านี้ทุกครั้งคุณอาจจะทำผิดพลาด (โดยทั่วไปถ้าคุณคัดลอกวางและยังพบบ่อยมากถ้าคุณเขียนพวกเขาทุกครั้งเพราะพวกเขามีวงเล็บจำนวนมาก) ...
Cacahuete Frito

3
... ดังนั้นฉันยืนอยู่บนข้อสรุปหลัก: ซิงเกิ้ลsizeofไม่ปลอดภัยอย่างชัดเจน (เหตุผลอยู่ที่คำตอบ) และไม่ได้ใช้มาโคร แต่ใช้สิ่งก่อสร้างที่ฉันให้ไว้ทุกครั้งไม่ปลอดภัยมากขึ้นดังนั้นวิธีเดียวที่จะไป คือมาโคร
Cacahuete Frito

3
@ MarkHarrison ฉันรู้ถึงความแตกต่างระหว่างพอยน์เตอร์และอาร์เรย์ แต่มีบางครั้งที่ฉันมีฟังก์ชั่นที่ฉันปรับโครงสร้างในภายหลังเป็นฟังก์ชั่นเล็ก ๆ น้อย ๆ และสิ่งแรกคืออาร์เรย์ต่อมาคือตัวชี้และนั่นคือจุดหนึ่งที่ถ้าคุณลืมที่จะเปลี่ยนขนาดของมัน หนึ่งในนั้น.
Cacahuete Frito

3
@ ไฮนอกจากนี้ฉันรู้ว่าความแตกต่างไม่ได้หมายความว่าทุกคนรู้ถึงความแตกต่างและทำไมไม่ใช้สิ่งที่โดยทั่วไปแล้วกำจัดข้อบกพร่องเหล่านั้น 100%? จุดบกพร่องนั้นเกือบทำให้มันเป็น Linux; มันมาถึง Linus ซึ่งหมายความว่ามันผ่านการตรวจสอบจำนวนมากและยังหมายความว่ามีความเป็นไปได้ที่ข้อผิดพลาดเดียวกันได้ทำให้มันเป็น Linux ในส่วนอื่นของเคอร์เนลตามที่เขาพูด
Cacahuete Frito

11

"คุณได้แนะนำวิธีการยิงด้วยตัวคุณเอง"

อาร์เรย์ 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();

ดู: http://www.cplusplus.com/reference/stl/vector/


ฉันได้อ่านว่าวิธีที่เหมาะสมในการประกาศค่าคงที่จำนวนเต็มใน C คือการใช้การenumประกาศ
Raffi Khatchadourian

คำถามเกี่ยวกับ C ไม่ใช่ C ++ ดังนั้นไม่มี STL
Cacahuete Frito


5

หากคุณต้องการทำเช่นนี้เพื่อส่งผ่านอาร์เรย์ของคุณฉันขอแนะนำให้ใช้โครงสร้างเพื่อเก็บตัวชี้ไปยังประเภทที่คุณต้องการอาร์เรย์ของและจำนวนเต็มแทนขนาดของอาร์เรย์ จากนั้นคุณสามารถส่งผ่านสิ่งนั้นไปยังฟังก์ชั่นของคุณ เพียงกำหนดค่าตัวแปรอาร์เรย์ (ตัวชี้ไปยังองค์ประกอบแรก) ให้กับตัวชี้นั้น จากนั้นคุณสามารถไป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;
}

1
ไม่สามารถอัปโหลดโค้ดที่strcpy(d.name, name);ไม่มีการจัดการโอเวอร์โฟลว์
chux - Reinstate Monica

4

วิธีที่ดีที่สุดคือคุณบันทึกข้อมูลนี้ตัวอย่างเช่นในโครงสร้าง:

typedef struct {
     int *array;
     int elements;
} list_s;

ใช้ฟังก์ชั่นที่จำเป็นทั้งหมดเช่นสร้างทำลายตรวจสอบความเท่าเทียมกันและทุกอย่างที่คุณต้องการ มันง่ายกว่าที่จะผ่านเป็นพารามิเตอร์


6
เหตุผลใดที่int elementsเทียบกับ size_t elements?
chux - Reinstate Monica

3

ฟังก์ชั่นsizeofจะคืนค่าจำนวนไบต์ที่อาเรย์ของคุณใช้ในหน่วยความจำ หากคุณต้องการคำนวณจำนวนองค์ประกอบในอาเรย์ของคุณคุณควรหารจำนวนนั้นด้วยsizeofประเภทตัวแปรของอาเรย์ สมมติว่าint array[10];ถ้าจำนวนเต็มชนิดตัวแปรในคอมพิวเตอร์ของคุณเป็น 32 บิต (หรือ 4 ไบต์) เพื่อให้ได้ขนาดของอาร์เรย์คุณควรทำดังนี้:

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

2

คุณสามารถใช้&โอเปอเรเตอร์ นี่คือรหัสที่มา:

#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

7
ฉันไม่ได้ลงคะแนน แต่มันเหมือนกับการตอกตะปูด้วยก้อนอิฐเพราะคุณไม่ได้สังเกตเห็นค้อนที่อยู่ข้างๆคุณ นอกจากนี้ผู้คนมักจะขมวดคิ้วเมื่อใช้ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้น ... แต่ที่นี่ฉันคิดว่ามันเหมาะกับวัตถุประสงค์ของคุณมากพอ
Dmitri

2
@Dmitri ไม่มีตัวแปรแบบไม่มีการเข้าถึงที่นี่
MM

1
อืมม ptrdiff_tชี้การลบนำไปสู่ ผลในการsizeof() size_tC ไม่ได้กำหนดว่าจะกว้างหรือสูงกว่า / อันดับเดียวกัน ดังนั้นประเภทของความฉลาดทาง((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]);ก็เพียงพอแล้ว
chux - Reinstate Monica

1
(char *)(&a+1)-(char *)aไม่ใช่ค่าคงที่และอาจคำนวณได้ในขณะใช้งานแม้ว่าจะเป็นขนาดคงที่a[10]ก็ตาม sizeof(a)/sizeof(a[0])ทำคงที่ ณ เวลารวบรวมในกรณีนี้
chux - Reinstate Monica

1

คำตอบที่ง่ายที่สุด:

#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;
}


0

นอกเหนือจากคำตอบที่มีให้แล้วฉันต้องการชี้ให้เห็นกรณีพิเศษจากการใช้

sizeof(a) / sizeof (a[0])

ถ้าaเป็นทั้งอาร์เรย์ของchar, unsigned charหรือsigned charคุณไม่จำเป็นต้องใช้sizeofสองครั้งตั้งแต่การแสดงออกกับหนึ่งในตัวถูกดำเนินการของประเภทนี้จะส่งผลให้เสมอsizeof1

อ้างอิงจาก 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


อย่างไรก็ตามฝ่ายรักษาความปลอดภัยถ้าประเภทการเปลี่ยนแปลงอย่างมีนัยสำคัญ (แม้ว่ากรณีเหล่านี้จะหายาก)


1
คุณอาจต้องการที่จะใช้แมโครกับการหารเนื่องจากชนิดอาจเปลี่ยนแปลงในอนาคต (แม้ว่าอาจไม่น่าจะเกิดขึ้น) และการหารนั้นรู้จักกันในเวลารวบรวมดังนั้นคอมไพเลอร์จะปรับมันให้เหมาะสม (ถ้ามันไม่ได้โปรดเปลี่ยน คอมไพเลอร์ของคุณ)
Cacahuete Frito

1
@CacahueteFrito ใช่ฉันเคยคิดเกี่ยวกับเรื่องนั้นในขณะเดียวกัน ฉันเอามันเป็นบันทึกด้านข้างในคำตอบ ขอบคุณ.
RobertS สนับสนุน Monica Cellio

-1

หมายเหตุ:สิ่งนี้สามารถทำให้คุณไม่ได้กำหนดพฤติกรรมตามที่ MM ระบุในความคิดเห็น

int a[10];
int size = (*(&a+1)-a) ;

ดูรายละเอียดเพิ่มเติม ที่นี่ และที่นี่


2
นี่คือพฤติกรรมที่ไม่ได้กำหนดทางเทคนิค; *ผู้ประกอบการอาจจะไม่ถูกนำไปใช้เป็นตัวชี้ที่ผ่านมาที่ปลาย
MM

3
"พฤติกรรมที่ไม่ได้กำหนด" หมายถึงมาตรฐาน C ไม่ได้กำหนดพฤติกรรม หากคุณลองในโปรแกรมของคุณแล้วจะมีอะไรเกิดขึ้นได้บ้าง
MM

@MM คุณพูดว่า*(&a+1) - a;แตกต่างจาก(&a)[1] - a;ข้างบนใช่ทั้งสองอย่าง *(&a+1)และ(&a)[1]นับเป็น 1 เมื่อสิ้นสุดใช่หรือไม่
QuentinUK

@QuentinUK นิพจน์ทั้งสองของคุณเหมือนกันx[y]ถูกกำหนดเป็น*(x + (y))
MM

@ MM ฉันคิดอย่างนั้น แต่คำตอบอื่น ๆ โดย Arjun Sreedharan มีลูกศรขึ้น 38 ลูกและนี่มี -1 และคำตอบของ Arjun Sreedharan ไม่ได้เอ่ยถึงพฤติกรรมที่ไม่ได้กำหนด
QuentinUK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.