เหตุใดส่วนมิติของอาร์เรย์จึงเป็นชนิดข้อมูล


14

ในขณะที่อ่านหนังสือ C ++ Primer ฉันเจอข้อความนี้: "จำนวนองค์ประกอบในอาเรย์เป็นส่วนหนึ่งของประเภทอาเรย์" ดังนั้นฉันต้องการค้นหาโดยใช้รหัสต่อไปนี้:

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

และน่าสนใจที่ผลลัพธ์ของ typeid ในสองอาร์เรย์แสดงว่ามันแตกต่างกัน

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

เพียงแค่ต้องการที่จะสามารถเข้าใจแนวคิดอย่างลึกซึ้ง


3
ไม่จำเป็นอย่างยิ่งที่จะต้องรวมข้อมูลขนาดไว้ในประเภท แต่มันมีประโยชน์
byxor

บทช่วยสอนเกี่ยวกับอาร์เรย์จะอธิบาย (1) ฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดย (3) เนื่องจากไม่มีวิธีการเปรียบเทียบอาร์เรย์ในตัว
HolyBlackCat

คำตอบ:


20

เบื้องหลังเกิดอะไรขึ้น

ไม่ใช่การจัดสรรแบบไดนามิกคือโดยนิยามภาชนะขนาดคงที่ขององค์ประกอบที่เป็นเนื้อเดียวกัน อาร์เรย์ของNองค์ประกอบของประเภทTจะวางในหน่วยความจำเป็นลำดับต่อเนื่องกันของวัตถุชนิดNT


เหตุใดจึงจำเป็นต้องให้อาร์เรย์มีชนิดที่มีขนาด

ฉันไม่เชื่อว่ามันเป็น "จำเป็น" สำหรับประเภทของอาร์เรย์ที่จะรวมขนาดของมัน - ตามความเป็นจริงคุณสามารถใช้ตัวชี้เพื่ออ้างถึงลำดับของTวัตถุที่ต่อเนื่องกัน ตัวชี้ดังกล่าวจะสูญเสียข้อมูลขนาดเกี่ยวกับอาร์เรย์

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

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

หรือเพื่อหาขนาดของอาร์เรย์ว่าเป็นนิพจน์คงที่

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

สิ่งนี้จะส่งผลกระทบต่อการเปรียบเทียบอาร์เรย์?

มันไม่ได้จริงๆ

คุณไม่สามารถเปรียบเทียบอาร์เรย์แบบ C ในลักษณะเดียวกับที่คุณเปรียบเทียบตัวเลขสองตัว (เช่นintวัตถุ) คุณจะต้องเขียนการเปรียบเทียบพจนานุกรมบางประเภทและตัดสินใจว่ามันหมายถึงอะไรสำหรับคอลเลกชันที่มีขนาดแตกต่างกัน std::vector<T>ระบุว่าและสามารถใช้ตรรกะเดียวกันกับอาร์เรย์ได้


โบนัส: C ++ 11 ขึ้นไปstd::arrayเป็นตัวห่อรอบอาเรย์แบบ C ที่มีอินเตอร์เฟสเหมือนคอนเทนเนอร์ ควรใช้กับอาร์เรย์แบบ C เนื่องจากสอดคล้องกับคอนเทนเนอร์อื่น ๆ มากกว่า (เช่นstd::vector<T>) และสนับสนุนการเปรียบเทียบด้วยพจนานุกรมจากกล่อง


2
"คุณจะต้องเขียนการเปรียบเทียบเชิงพจนานุกรมบางอย่างและตัดสินใจว่ามันมีความหมายอย่างไรสำหรับคอลเลกชันขนาดต่าง ๆ " คุณสามารถใช้std::equal(ผ่านstd::beginและstd::endที่กำหนดไว้สำหรับอาร์เรย์) ในกรณีนั้นอาร์เรย์ที่มีขนาดต่างกันจะไม่เท่ากัน
Stu

3
มันอาจจะคุ้มค่าที่จะสังเกตว่า range-based สำหรับลูปที่ range เป็น array จำเป็นต้องอ่านขนาดของอาเรย์ในเวลาที่รวบรวม - มันเป็นบิตที่ค่อนข้างดีกว่า (ขอบคุณ!) ไม่มีใครเขียนประเภทของอาเรย์ในตัวอย่างนี้ แต่ดูเหมือนว่าจะมากไปกว่าการบรรทุกเกินพิกัดตามขนาด
Milo Brandt

8

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

หากคุณมี struct กำหนดไว้เป็น (ตัวอย่าง)

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

จากนั้นเมื่อคุณสร้างวัตถุ:

A a{'a', 'b'};

คุณสามารถคิดถึงกระบวนการสร้างวัตถุเป็นกระบวนการ:

  • จัดสรรพื้นที่ 2 ไบต์ (บนสแต็ก แต่ไม่สำคัญสำหรับตัวอย่างนี้)
  • เรียกใช้ตัวสร้างวัตถุ (ในกรณีนี้คัดลอก'a'และ'b'ไปยังวัตถุ)

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

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

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


0

มีเหตุผลภายในสำหรับไลบรารีรันไทม์ หากคุณพิจารณาข้อความต่อไปนี้ตัวอย่างเช่น:

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

จากนั้นจะชัดเจนว่าปัญหาคืออะไร: ตัวอย่างเช่นการจัดการกับ unsigned int *มีความกังวลตัวเองด้วยหรือที่อยู่ของsizeof operatorunsigned int

มีคำอธิบายโดยละเอียดเพิ่มเติมสำหรับส่วนที่เหลือของสิ่งที่คุณเห็นที่นี่ แต่ส่วนใหญ่เป็นการสรุปสิ่งที่ครอบคลุมในภาษาการเขียนโปรแกรม C รุ่นที่ 2โดย Kernighan และ Ritchie เกี่ยวกับโปรแกรมที่พิมพ์ข้อความภาษาธรรมดาของประเภทที่ประกาศไว้ เชือก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.