การใช้อาร์เรย์มาตรฐานใน C พร้อมการสลายตัวแบบธรรมชาติจากอาร์เรย์เป็น ptr
@ Bo Persson ระบุอย่างถูกต้องในคำตอบที่ยอดเยี่ยมของเขาที่นี่ :
เมื่อส่งอาร์เรย์เป็นพารามิเตอร์สิ่งนี้
void arraytest(int a[])
หมายความว่าเหมือนกับ
void arraytest(int *a)
อย่างไรก็ตามขอฉันเพิ่มด้วยว่าทั้งสองรูปแบบข้างต้นยัง:
หมายความว่าเหมือนกับ
void arraytest(int a[0])
ซึ่งหมายความว่าเหมือนกับ
void arraytest(int a[1])
ซึ่งหมายความว่าเหมือนกับ
void arraytest(int a[2])
ซึ่งหมายความว่าเหมือนกับ
void arraytest(int a[1000])
เป็นต้น
ในทุกตัวอย่างอาร์เรย์ข้างต้นประเภทพารามิเตอร์อินพุตจะสลายตัวเป็น anint *
และสามารถเรียกได้โดยไม่มีคำเตือนและไม่มีข้อผิดพลาดแม้จะ-Wall -Wextra -Werror
เปิดตัวเลือกการสร้างไว้แล้วก็ตาม(ดูที่repo ของฉันที่นี่สำหรับรายละเอียดเกี่ยวกับตัวเลือกการสร้างทั้ง 3 นี้) เช่น นี้:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
เป็นเรื่องของความเป็นจริง "ขนาด" ค่า (ก[0]
, [1]
, [2]
, [1000]
ฯลฯ ) ภายในพารามิเตอร์อาร์เรย์ที่นี่เป็นที่เห็นได้ชัดเพียงเพื่อความงาม / วัตถุประสงค์เอกสารด้วยตนเองและสามารถเป็นจำนวนเต็มบวกใด ๆ (size_t
พิมพ์ฉันคิด) ที่คุณต้องการ!
อย่างไรก็ตามในทางปฏิบัติคุณควรใช้เพื่อระบุขนาดขั้นต่ำของอาร์เรย์ที่คุณคาดหวังว่าฟังก์ชันจะได้รับดังนั้นเมื่อเขียนโค้ดคุณจะติดตามและตรวจสอบได้ง่าย มาตรฐานMISRA-C-2012 ( ซื้อ / ดาวน์โหลด PDF เวอร์ชัน 236-pg 2012 ของมาตรฐานราคา 15.00 ปอนด์ที่นี่ ) ไปไกลถึงสถานะ (เน้นเพิ่ม):
กฎข้อ 17.5 อาร์กิวเมนต์ของฟังก์ชันที่สอดคล้องกับพารามิเตอร์ที่ประกาศว่ามีประเภทอาร์เรย์ต้องมีจำนวนองค์ประกอบที่เหมาะสม
...
หากพารามิเตอร์ถูกประกาศเป็นอาร์เรย์ที่มีขนาดที่ระบุอาร์กิวเมนต์ที่เกี่ยวข้องในการเรียกใช้ฟังก์ชันแต่ละครั้งควรชี้ไปที่วัตถุที่มีองค์ประกอบอย่างน้อยเท่าอาร์เรย์
...
การใช้ตัวประกาศอาร์เรย์สำหรับพารามิเตอร์ฟังก์ชันระบุอินเทอร์เฟซของฟังก์ชันได้ชัดเจนกว่าการใช้ตัวชี้ จำนวนองค์ประกอบต่ำสุดที่ฟังก์ชันคาดหวังไว้นั้นมีการระบุไว้อย่างชัดเจนในขณะที่ตัวชี้ไม่สามารถทำได้
กล่าวอีกนัยหนึ่งก็คือพวกเขาแนะนำให้ใช้รูปแบบขนาดที่ชัดเจนแม้ว่าในทางเทคนิคมาตรฐาน C จะไม่ได้บังคับใช้ก็ตามแต่อย่างน้อยก็ช่วยชี้แจงให้คุณทราบในฐานะนักพัฒนาและสำหรับคนอื่น ๆ ที่ใช้โค้ดว่าอาร์เรย์ขนาดใดที่ฟังก์ชันคาดหวัง คุณจะผ่านเข้าไป
บังคับประเภทความปลอดภัยในอาร์เรย์ใน C
ดังที่ @Winger Sendon ชี้ให้เห็นในความคิดเห็นด้านล่างคำตอบของฉันเราสามารถบังคับให้ C รักษาประเภทอาร์เรย์ให้แตกต่างกันตามขนาดอาร์เรย์ !
ขั้นแรกคุณต้องตระหนักว่าในตัวอย่างของฉันข้างต้นโดยใช้สิ่งint array1[2];
นี้: arraytest(array1);
ทำให้array1
สลายตัวเป็นint *
ไฟล์. อย่างไรก็ตามหากคุณใช้ที่อยู่ array1
แทนและโทรarraytest(&array1)
ไปคุณจะมีพฤติกรรมที่แตกต่างไปจากเดิมอย่างสิ้นเชิง! ตอนนี้ยังไม่สลายตัวเป็นint *
! ประเภทของ&array1
คือint (*)[2]
ซึ่งหมายถึง"ตัวชี้ไปยังอาร์เรย์ขนาด 2 ของ int"หรือ"ตัวชี้ไปยังอาร์เรย์ขนาด 2 ของประเภท int"แทน ดังนั้นคุณสามารถ FORCE C เพื่อตรวจสอบความปลอดภัยของประเภทในอาร์เรย์เช่นนี้:
void arraytest(int (*a)[2])
{
}
รูปแบบนี้เป็นเรื่องยากที่จะอ่าน แต่คล้ายกับที่ของตัวชี้ฟังก์ชัน เครื่องมือออนไลน์cdeclบอกเราว่าint (*a)[2]
: "ประกาศเป็นตัวชี้ไปยังอาร์เรย์ 2 ของ int" (ตัวชี้ไปที่อาร์เรย์ 2 int
วินาที) อย่าสับสนกับเวอร์ชันที่ไม่มีวงเล็บ: int * a[2]
ซึ่งหมายความว่า: "ประกาศเป็นอาร์เรย์ 2 ของตัวชี้เป็น int" (อาร์เรย์ 2 พอยน์เตอร์ถึงint
)
ตอนนี้ฟังก์ชันนี้ต้องการให้คุณเรียกใช้ด้วยตัวดำเนินการแอดเดรส ( &
) เช่นนี้โดยใช้เป็นพารามิเตอร์อินพุตชี้ไปที่อาร์เรย์ของขนาดที่ถูกต้อง!:
int array1[2];
arraytest(&array1);
อย่างไรก็ตามสิ่งนี้จะทำให้เกิดคำเตือน:
int array1[2];
arraytest(array1);
คุณอาจจะทดสอบรหัสที่นี่
ในการบังคับให้คอมไพเลอร์ C เปลี่ยนคำเตือนนี้เป็นข้อผิดพลาดดังนั้นคุณต้องเรียกarraytest(&array1);
ใช้เฉพาะอาร์เรย์อินพุตที่มีขนาดและประเภทที่ถูกต้องเท่านั้นให้int array1[2];
เพิ่ม-Werror
ตัวเลือกการสร้างของคุณ หากเรียกใช้โค้ดทดสอบด้านบนบน onlinegdb.com ให้คลิกไอคอนรูปเฟืองที่ด้านบนขวาและคลิกที่ "Extra Compiler Flags" เพื่อพิมพ์ตัวเลือกนี้ในตอนนี้คำเตือนนี้:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
จะกลายเป็นข้อผิดพลาดของการสร้างนี้:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
โปรดทราบว่าคุณยังสามารถสร้างตัวชี้ "type safe" ให้กับอาร์เรย์ขนาดที่กำหนดได้เช่นนี้
int array[2];
int (*array_p)[2] = &array;
... แต่ฉันไม่จำเป็นต้องแนะนำสิ่งนี้เพราะมันทำให้ฉันนึกถึงการแสดงตลก C ++ จำนวนมากที่ใช้ในการบังคับความปลอดภัยของประเภททุกที่ด้วยต้นทุนที่สูงเป็นพิเศษสำหรับความซับซ้อนของไวยากรณ์ภาษาความฟุ่มเฟือยและความยากลำบากในการออกแบบโค้ดซึ่งฉันไม่ชอบและ เคยพูดคุยกันหลายครั้งก่อนหน้านี้ (เช่นดู"My Thoughts on C ++" ที่นี่ )
สำหรับการทดสอบและการทดลองเพิ่มเติมโปรดดูที่ลิงค์ด้านล่าง
อ้างอิง
ดูลิงก์ด้านบน นอกจากนี้:
- การทดลองโค้ดของฉันออนไลน์: https://onlinegdb.com/B1RsrBDFD