โดยทั่วไป จำนวนหน่วยความจำที่จำเป็นสำหรับตัวแปรเฉพาะจะถูกระบุก่อนกระบวนการคอมไพล์โดยการประกาศตัวแปรนั้น หากจำเป็นต้องสร้างตัวแปรโดยไม่ทราบขนาดล่วงหน้า ระบบจะใช้หน่วยความจำแบบไดนามิก การจอง และ การปลดปล่อย ปัญหาหน่วยความจำในโปรแกรม C++ สามารถเกิดขึ้นได้ตลอดเวลา มีการดำเนินการ การกระจาย หน่วยความจำได้สองวิธี:
- โดยใช้ฟังก์ชัน มอลลอค, โทรล็อค, จัดสรรใหม่และ ฟรี;
- ผ่านตัวดำเนินการ ใหม่และ ลบ.
การทำงาน มอลลอค เงินสำรองบล็อกเซลล์หน่วยความจำที่ต่อเนื่องกันสำหรับการจัดเก็บ วัตถุที่ระบุและส่งคืนตัวชี้ไปที่เซลล์แรกของบล็อกนี้ การเรียกใช้ฟังก์ชันดูเหมือนว่า:
เป็นโมฆะ *malloc(ขนาด);
ที่นี่ ขนาด- ค่าจำนวนเต็มที่ไม่ได้ลงนามซึ่งกำหนดขนาดของพื้นที่หน่วยความจำที่จัดสรรเป็นไบต์ หากการสำรองหน่วยความจำสำเร็จ ฟังก์ชันจะกลับมา ตัวแปรประเภท เป็นโมฆะ *ซึ่งสามารถลดเหลือเท่าใดก็ได้ ประเภทที่ต้องการตัวชี้
การทำงาน - โทรล็อคมีไว้สำหรับการจัดสรรหน่วยความจำด้วย รายการด้านล่างหมายความว่าจะถูกเน้น หมายเลของค์ประกอบโดย ขนาดไบต์
เป็นโมฆะ *calloc(nime, ขนาด);
ฟังก์ชั่นนี้จะส่งคืนตัวชี้ไปยังพื้นที่ที่เลือกหรือ โมฆะเมื่อไม่สามารถจัดสรรหน่วยความจำได้ คุณสมบัติพิเศษของฟังก์ชันคือการรีเซ็ตองค์ประกอบที่เลือกทั้งหมดให้เป็นศูนย์
การทำงาน จัดสรรใหม่ปรับขนาดหน่วยความจำที่จัดสรรไว้ก่อนหน้านี้ พวกเขาพูดกับเธอแบบนี้:
ถ่าน * realloc (โมฆะ * p, ขนาด);
ที่นี่ พี- ชี้ไปยังพื้นที่หน่วยความจำที่ต้องเปลี่ยนขนาด ขนาด. หากที่อยู่ของพื้นที่หน่วยความจำเปลี่ยนแปลงอันเป็นผลมาจากฟังก์ชัน ที่อยู่ใหม่จะกลับมาเป็นผล ถ้า มูลค่าที่แท้จริงพารามิเตอร์แรก โมฆะจากนั้นฟังก์ชัน จัดสรรใหม่ทำงานเหมือนกับฟังก์ชัน มอลลอคนั่นคือจัดสรรพื้นที่หน่วยความจำขนาด ขนาดไบต์
หากต้องการเพิ่มหน่วยความจำที่จัดสรรให้ใช้ฟังก์ชัน ฟรี. พวกเขาพูดกับเธอแบบนี้:
เป็นโมฆะฟรี (โมฆะ * ขนาด p);
ที่นี่ พี- ชี้ไปยังตำแหน่งหน่วยความจำที่จัดสรรไว้ก่อนหน้านี้โดยฟังก์ชัน มอลลอค, โทรล็อคหรือ จัดสรรใหม่.
ผู้ประกอบการ ใหม่และ ลบฟังก์ชั่นที่คล้ายกัน มอลลอคและ ฟรี. ใหม่จัดสรรหน่วยความจำ และอาร์กิวเมนต์เดียวของมันคือนิพจน์ที่ระบุจำนวนไบต์ที่จะสงวนไว้ ตัวดำเนินการส่งคืนตัวชี้ไปยังจุดเริ่มต้นของบล็อกหน่วยความจำที่จัดสรร ผู้ดำเนินการ ลบเพิ่มหน่วยความจำ อาร์กิวเมนต์คือที่อยู่ของเซลล์แรกของบล็อกที่ต้องการปล่อย
อาร์เรย์แบบไดนามิก- อาร์เรย์ ความยาวตัวแปรหน่วยความจำที่ได้รับการจัดสรรระหว่างการทำงานของโปรแกรม การจัดสรรหน่วยความจำดำเนินการโดยฟังก์ชัน คาลลอค,มัลลอคหรือผู้ปฏิบัติงาน ใหม่. ที่อยู่ขององค์ประกอบแรกของตำแหน่งหน่วยความจำที่จัดสรรจะถูกเก็บไว้ในตัวแปรที่ประกาศเป็นตัวชี้ ตัวอย่างเช่น ข้อความต่อไปนี้หมายความว่ามีการอธิบายตัวชี้ มาสและกำหนดที่อยู่ของจุดเริ่มต้นของพื้นที่ต่อเนื่องกัน หน่วยความจำแบบไดนามิกเน้นโดยใช้โอเปอเรเตอร์ ใหม่:
int *mas=ใหม่ int;
จำนวนหน่วยความจำที่จัดสรรเพียงพอที่จะเก็บค่า int ได้ 10 ค่า
จริงๆ แล้วในตัวแปรนั้น มาสที่อยู่ถูกเก็บไว้ องค์ประกอบเป็นศูนย์อาร์เรย์แบบไดนามิก ดังนั้นที่อยู่ขององค์ประกอบแรกถัดไปในพื้นที่หน่วยความจำที่จัดสรรคือ มาส+1, ก มาส+i คือที่อยู่ขององค์ประกอบ i-th องค์ประกอบ i-th ของอาร์เรย์ไดนามิกสามารถเข้าถึงได้ตามปกติด้วย mas[i] หรือด้วยวิธีอื่น *(มาส +ไอ) . สิ่งสำคัญคือต้องแน่ใจว่าคุณไม่เกินขอบเขตของพื้นที่หน่วยความจำที่จัดสรร
เมื่อไร อาร์เรย์แบบไดนามิก(เมื่อใดก็ได้ระหว่างการทำงานของโปรแกรม) ไม่จำเป็นอีกต่อไป จากนั้นจึงสามารถปลดปล่อยหน่วยความจำได้โดยใช้ฟังก์ชัน ฟรีหรือผู้ปฏิบัติงาน ลบ.
ฉันเสนอให้พิจารณางานหลายอย่างที่เสริมบทเรียนนี้:
ปัญหาที่ 1
ค้นหาผลรวมขององค์ประกอบจริงของอาร์เรย์ไดนามิก
//ตัวอย่างการใช้งาน ฟังก์ชันมัลลอคและฟรี #include "stdafx.h" #include //ตัวอย่างการใช้ฟังก์ชัน malloc และฟังก์ชันอิสระ #รวม "stdafx.h" #รวม ใช้เนมสเปซมาตรฐาน ; int หลัก() ฉัน, n; ลอย * เป็น; //ตัวชี้ให้ลอย ลอย ; ศาล<<
"\n"
;
cin
>> ไม่มี ; //ป้อนขนาดอาร์เรย์ //จัดสรรหน่วยความจำสำหรับอาร์เรย์ที่มีองค์ประกอบจริง n ตัว a = (float * ) malloc (n * ขนาดของ (float ) ) ; ศาล<<
"ป้อนอาร์เรย์ A\n";
//ใส่องค์ประกอบอาร์เรย์ สำหรับ (i = 0 ; i<
n
;
i
++
)
cin >> * (a + i) ; //การสะสมผลรวมขององค์ประกอบอาร์เรย์ สำหรับ (s = 0 , i = 0 ; i<
n
;
i
++
)
s += * (ก + ฉัน) ; //ส่งออกค่าจำนวน ศาล<<
"S="
<<
s
<<
"\n"
;
//เพิ่มหน่วยความจำ ฟรี(ก); ระบบ("หยุดชั่วคราว"); กลับ 0 ; แก้ไขอาร์เรย์แบบไดนามิกของจำนวนเต็มเพื่อให้องค์ประกอบที่เป็นบวกกลายเป็นลบและในทางกลับกัน ในการแก้ปัญหา เราจะคูณแต่ละองค์ประกอบด้วย -1 //ตัวอย่างการใช้ตัวดำเนินการใหม่และลบ #include "stdafx.h" #include // ตัวอย่างการใช้ตัวดำเนินการใหม่และลบ #รวม "stdafx.h" #รวม ใช้เนมสเปซมาตรฐาน ; int หลัก() setlocale(LC_ALL, "มาตุภูมิ"); ฉัน, n; //ป้อนจำนวนองค์ประกอบอาร์เรย์ ศาล<<
"n="
;
cin
>> ไม่มี ; //การจัดสรรหน่วยความจำ int * a = int ใหม่ [ n ] ; ศาล<<
"ป้อนองค์ประกอบอาร์เรย์:\n";
//อาร์เรย์อินพุต ในขณะที่รวบรวมข้อมูลเพื่อเขียนบทความนี้ ฉันจำความคุ้นเคยครั้งแรกกับพอยน์เตอร์ได้ - ฉันรู้สึกเศร้ามาก... ดังนั้นหลังจากอ่านหลายหัวข้อในหัวข้อนี้จากหนังสือเกี่ยวกับการเขียนโปรแกรมใน C ++ หลายเล่มแล้วจึงตัดสินใจใช้เส้นทางที่แตกต่างและนำเสนอ หัวข้อของตัวชี้ C++ ตามลำดับที่ฉันคิดว่าจำเป็น ฉันจะให้คำจำกัดความสั้นๆ ทันที และเราจะดูคำแนะนำในการใช้งานจริงโดยใช้ตัวอย่าง บทความถัดไป () จะสรุปความแตกต่าง การใช้พอยน์เตอร์กับสตริงแบบ C (อาร์เรย์อักขระ) และสิ่งสำคัญที่ต้องจำ ตัวชี้ในภาษา C++ คือตัวแปรที่เก็บที่อยู่ของข้อมูล (ค่า) ไว้ในหน่วยความจำ ไม่ใช่ตัวข้อมูลเอง
หลังจากดูตัวอย่างต่อไปนี้ คุณจะเข้าใจสิ่งสำคัญ - เหตุใดเราจึงต้องมีพอยน์เตอร์ในการเขียนโปรแกรม วิธีประกาศและใช้งาน สมมติว่าในโปรแกรมเราต้องสร้างอาร์เรย์จำนวนเต็ม ซึ่งเป็นขนาดที่แน่นอนซึ่งเราไม่ทราบก่อนที่โปรแกรมจะเริ่มทำงาน นั่นคือเราไม่ทราบว่าผู้ใช้จะต้องป้อนตัวเลขจำนวนเท่าใดในอาร์เรย์นี้ แน่นอนว่าเราสามารถเล่นได้อย่างปลอดภัยและประกาศอาร์เรย์ขององค์ประกอบหลายพันรายการ (เช่น 5,000) สิ่งนี้ (ในความเห็นส่วนตัวของเรา) น่าจะเพียงพอสำหรับผู้ใช้ในการทำงาน ใช่ – แน่นอน – นี่อาจจะเพียงพอแล้ว แต่อย่าลืมว่าอาร์เรย์นี้จะใช้พื้นที่มากใน RAM (5,000 * 4 (ประเภท int) = 20,000 ไบต์) เราได้รักษาความปลอดภัยให้กับตัวเองแล้ว และผู้ใช้จะเติมองค์ประกอบอาร์เรย์ของเราเพียง 10 รายการเท่านั้น ปรากฎว่ามีการใช้งานจริง 40 ไบต์ และ 19,960 ไบต์เป็นการสิ้นเปลืองหน่วยความจำ การใช้ RAM อย่างไม่สมเหตุสมผล #รวม #รวม ใช้เนมสเปซมาตรฐาน ; int หลัก() const int SizeOfArray = 5,000 ; int arrWithDigits [ SizeOfArray ] = ( ) ; ศาล<<
"อาร์เรย์ครอบครองในหน่วยความจำ"<<
sizeof
(arrWithDigits
)
<<
" байт"
<<
endl
;
จำนวน int = 0 ; ศาล<<
“คุณจะป้อนตัวเลขจำนวนเท่าใดในอาร์เรย์?”;
cin >> จำนวน ; ศาล<<
"จำเป็นจริงๆ"<<
amount
*
sizeof
(int
)
<<
" байт"
<<
endl
;
สำหรับ (int i = 0 ; i<
amount
;
i
++
)
ศาล<<
i
+
1
<<
"-е число: "
;
cin >> arrWithDigits [ ฉัน ] ; ศาล<<
endl
;
สำหรับ (int i = 0 ; i<
amount
;
i
++
)
ศาล<<
arrWithDigits
[
i
]
<<
" "
;
ศาล<<
endl
;
กลับ 0 ; ไปยังฟังก์ชันไลบรารีมาตรฐาน ขนาดของ()ผ่านอาร์เรย์ที่ประกาศไว้ arrWithDigitsบรรทัดที่ 10 มันจะกลับไปยังตำแหน่งของการเรียกขนาดเป็นไบต์ที่อาร์เรย์นี้ครอบครองในหน่วยความจำ สำหรับคำถามที่ว่า “คุณจะใส่ตัวเลขจำนวนเท่าใดลงในอาร์เรย์?” คำตอบคือ 10 ในบรรทัดที่ 15 เป็นนิพจน์ จำนวน * ขนาดของ (int)จะเท่ากับ 10 * 4 เนื่องจากฟังก์ชัน ขนาดของ(int)จะส่งกลับ 4 (ขนาดเป็นไบต์ของประเภท int) จากนั้นให้ป้อนตัวเลขจากแป้นพิมพ์แล้วโปรแกรมจะแสดงบนหน้าจอ ปรากฎว่าองค์ประกอบ 4990 ที่เหลือจะเก็บค่าศูนย์ไว้ จึงไม่มีประโยชน์ที่จะแสดงให้พวกเขาเห็น ข้อมูลหลักบนหน้าจอ: อาร์เรย์ใช้ 20,000 ไบต์ แต่ในความเป็นจริงต้องใช้ 40 ไบต์ จะออกจากสถานการณ์นี้ได้อย่างไร? บางคนอาจต้องการเขียนโปรแกรมใหม่เพื่อให้ผู้ใช้ป้อนขนาดของอาร์เรย์จากแป้นพิมพ์ และหลังจากป้อนค่าแล้ว ให้ประกาศอาร์เรย์ด้วยจำนวนองค์ประกอบที่ต้องการ แต่สิ่งนี้ไม่สามารถทำได้หากไม่มีพอยน์เตอร์ อย่างที่คุณจำได้ ขนาดของอาร์เรย์จะต้องเป็นค่าคงที่ นั่นคือ ต้องเริ่มต้นค่าคงที่จำนวนเต็มก่อนที่จะประกาศอาร์เรย์ และเราไม่สามารถพร้อมท์ให้ป้อนข้อมูลจากคีย์บอร์ดได้ ทดลองและตรวจสอบ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ในโค้ดต่อไปนี้ เราจะใช้พอยน์เตอร์และโอเปอเรเตอร์ที่ใหม่สำหรับคุณ ใหม่(จัดสรรหน่วยความจำ) และ ลบ(เพิ่มหน่วยความจำ) การใช้ RAM อย่างชาญฉลาดโดยใช้พอยน์เตอร์ #รวม #รวม #รวม ใช้เนมสเปซมาตรฐาน ; int หลัก() setlocale(LC_ALL, "มาตุภูมิ"); int sizeOfArray = 0 ; // ขนาดอาร์เรย์ (ป้อนโดยผู้ใช้) ศาล<<
"หากต้องการสร้างอาร์เรย์ของตัวเลข ให้ป้อนขนาด: ";
cin >> sizeOfArray ; // ความสนใจ! int* arrWithDigits - การประกาศตัวชี้ // ไปยังชิ้นส่วนของหน่วยความจำที่ใหม่จะจัดสรร int * arrWithDigits = int ใหม่ [ sizeOfArray ] ; สำหรับ (int i = 0 ; i<
sizeOfArray
;
i
++
)
arrWithDigits[i] = i+1; ศาล<<
arrWithDigits
[
i
]
<<
" "
;
ศาล<<
endl
;
ลบ arrWithDigits ; // เพิ่มหน่วยความจำ กลับ 0 ; ผู้ใช้ป้อนค่าจากแป้นพิมพ์ - บรรทัด 12 ตัวชี้ถูกกำหนดไว้ด้านล่าง: int * arrWithDigitsรายการนี้หมายความว่า arrWithDigitsเป็นตัวชี้ มันถูกสร้างขึ้นเพื่อจัดเก็บที่อยู่ของเซลล์ที่จะระบุจำนวนเต็ม ในกรณีของเรา arrWithDigitsจะชี้ไปที่เซลล์อาร์เรย์ที่มีดัชนี 0 เครื่องหมาย *
- เช่นเดียวกับที่ใช้ในการคูณ ขึ้นอยู่กับบริบท คอมไพลเลอร์จะ "เข้าใจ" ว่านี่คือการประกาศพอยน์เตอร์ ไม่ใช่การคูณ ถัดมาเป็นป้าย =
และผู้ปฏิบัติงาน ใหม่ซึ่งจัดสรรชิ้นส่วนของหน่วยความจำ เราจำได้ว่าหน่วยความจำของเราควรได้รับการจัดสรรให้กับอาร์เรย์ ไม่ใช่สำหรับตัวเลขตัวเดียว บันทึก ใหม่ int [sizeOfArray]สามารถถอดรหัสได้ดังนี้: ใหม่(จัดสรรหน่วยความจำ) ภายใน(สำหรับเก็บจำนวนเต็ม)
(ในปริมาณ ขนาดของอาร์เรย์
). ดังนั้นในบรรทัดที่ 16 จึงได้กำหนดไว้ อาร์เรย์แบบไดนามิก. ซึ่งหมายความว่าหน่วยความจำจะถูกจัดสรร (หรือไม่ได้จัดสรร) ในขณะที่โปรแกรมกำลังทำงานอยู่ และไม่ใช่ในระหว่างการคอมไพล์ ดังที่เกิดขึ้นกับอาร์เรย์ปกติ นั่นคือการจัดสรรหน่วยความจำขึ้นอยู่กับการพัฒนาโปรแกรมและการตัดสินใจที่เกิดขึ้นโดยตรงในการทำงาน ในกรณีของเรา ขึ้นอยู่กับว่าผู้ใช้ป้อนอะไรเข้าไปในตัวแปร ขนาดของอาร์เรย์ บรรทัดที่ 25 ใช้ตัวดำเนินการ ลบ. มันจะปล่อยโอเปอเรเตอร์ที่ได้รับการจัดสรร ใหม่หน่วยความจำ. เพราะ ใหม่จัดสรรหน่วยความจำสำหรับจัดเก็บอาเรย์จากนั้นเมื่อปล่อยมันคุณจะต้องทำให้คอมไพเลอร์ชัดเจนว่าจำเป็นต้องปล่อยหน่วยความจำของอาเรย์ให้ว่างและไม่ใช่แค่เซลล์ศูนย์ซึ่งชี้ไปที่ arrWithDigits. ดังนั้นระหว่าง ลบและชื่อตัวชี้จะอยู่ในวงเล็บเหลี่ยม –
ลบ arrWithDigits ;จำไว้ว่าทุกครั้งที่มีการจัดสรรหน่วยความจำโดยใช้ ใหม่คุณต้องเพิ่มหน่วยความจำนี้โดยใช้ ลบ. แน่นอนว่าเมื่อโปรแกรมหยุดทำงาน หน่วยความจำที่โปรแกรมครอบครองอยู่ก็จะถูกปลดปล่อยโดยอัตโนมัติ แต่จงใช้ตัวดำเนินการให้เป็นนิสัย ใหม่และ ลบจับคู่กับ ท้ายที่สุดแล้ว โปรแกรมสามารถมีอาร์เรย์ได้ 5-6 ตัว เป็นต้น และหากคุณเพิ่มหน่วยความจำทุกครั้งที่ไม่จำเป็นอีกต่อไปในโปรแกรมที่ทำงานอยู่ในอนาคต หน่วยความจำจะถูกใช้อย่างชาญฉลาดมากขึ้น สมมติว่าในโปรแกรมของเราเราเติมอาร์เรย์ด้วยค่าสิบค่า ต่อไป เราคำนวณผลรวมและบันทึกไว้ในตัวแปรบางตัว เพียงเท่านี้ เราจะไม่ทำงานกับอาร์เรย์นี้อีกต่อไป โปรแกรมยังคงทำงานต่อไปและมีการสร้างอาร์เรย์ไดนามิกใหม่เพื่อจุดประสงค์บางอย่าง ในกรณีนี้ ขอแนะนำให้เพิ่มหน่วยความจำที่อาร์เรย์แรกครอบครอง จากนั้นเมื่อจัดสรรหน่วยความจำให้กับอาเรย์อื่นหน่วยความจำนี้จะสามารถนำกลับมาใช้ใหม่ในโปรแกรมได้ ลองพิจารณาการใช้พอยน์เตอร์เป็นพารามิเตอร์ของฟังก์ชัน ในการเริ่มต้น ให้พิมพ์และคอมไพล์โค้ดต่อไปนี้ ในนั้นฟังก์ชันจะได้รับตัวแปรสองตัวและเสนอให้ทำการเปลี่ยนแปลงค่าของมัน พยายามเปลี่ยนตัวแปรที่ส่งผ่านไปยังฟังก์ชัน #รวม #รวม #รวม ใช้เนมสเปซมาตรฐาน ; โมฆะการเปลี่ยนแปลงข้อมูล (int varForCh1 , int varForCh2 ) ; int หลัก() setlocale(LC_ALL, "มาตุภูมิ"); ตัวแปร intForChange_1 = 0 ; ตัวแปร intForChange_2 = 0 ; ศาล<<
"variableForChange_1 = "
<<
variableForChange_1
<<
endl
;
ศาล<<
"variableForChange_2 = "
<<
variableForChange_2
<<
endl
;
ศาล<<
endl
;
changeData(ตัวแปรForChange_1, ตัวแปรForChange_2); ศาล<<
endl
;
ศาล<<
"variableForChange_1 = "
<<
variableForChange_1
<<
endl
;
ศาล<<
"variableForChange_2 = "
<<
variableForChange_2
<<
endl
;
กลับ 0 ; โมฆะ changeData (int varForCh1, int varForCh2) ศาล<<
"ป้อนค่าใหม่สำหรับตัวแปรแรก: ";
cin >> varForCh1 ; ศาล<<
"ป้อนค่าใหม่สำหรับตัวแปรตัวที่สอง: ";
cin >> varForCh2 ; รันโปรแกรมและป้อนค่าตัวแปรใหม่ ผลลัพธ์จะเห็นว่าเมื่อฟังก์ชันเสร็จสิ้น ตัวแปรต่างๆ จะไม่เปลี่ยนแปลงและมีค่าเท่ากับ 0 อย่างที่คุณจำได้ ฟังก์ชันนี้ไม่ได้ทำงานโดยตรงกับตัวแปร แต่จะสร้างสำเนาที่ตรงกันทุกประการ สำเนาเหล่านี้จะถูกทำลายหลังจากออกจากฟังก์ชัน นั่นคือฟังก์ชันได้รับตัวแปรบางตัวเป็นพารามิเตอร์ สร้างสำเนา ทำงานกับมัน และทำลายมัน ตัวแปรนั้นจะยังคงไม่เปลี่ยนแปลง การใช้พอยน์เตอร์ทำให้เราสามารถส่งที่อยู่ตัวแปรไปยังฟังก์ชันได้ จากนั้นฟังก์ชันจะสามารถทำงานกับข้อมูลตัวแปรตามที่อยู่ได้โดยตรง มาทำการเปลี่ยนแปลงโปรแกรมก่อนหน้ากัน การเปลี่ยนค่าตัวแปรโดยใช้พอยน์เตอร์ #รวม วัตถุประสงค์ของการบรรยาย: ศึกษาการประกาศ การจัดสรร และการปล่อยหน่วยความจำสำหรับอาร์เรย์ไดนามิกมิติเดียว การเข้าถึงองค์ประกอบ เรียนรู้วิธีการแก้ปัญหาโดยใช้อาร์เรย์ไดนามิกมิติเดียวในภาษา C++ เมื่อใช้โครงสร้างข้อมูลจำนวนมาก มักจะเกิดขึ้นว่าต้องมีขนาดตัวแปรขึ้นอยู่กับขนาดของโครงสร้างข้อมูล เวลานำโปรแกรม ในกรณีเหล่านี้มีความจำเป็นต้องใช้ การจัดสรรหน่วยความจำแบบไดนามิก. หนึ่งในโครงสร้างข้อมูลที่พบบ่อยที่สุดคืออาร์เรย์ ซึ่งขนาดไม่ได้ถูกกำหนดหรือแก้ไขตั้งแต่แรก ตาม มาตรฐานภาษาอาร์เรย์คือชุดขององค์ประกอบ ซึ่งแต่ละองค์ประกอบมีคุณสมบัติเหมือนกัน องค์ประกอบทั้งหมดเหล่านี้จะถูกวางไว้ในตำแหน่งหน่วยความจำที่อยู่ติดกันเป็นแถว โดยเริ่มจากที่อยู่ที่ตรงกับจุดเริ่มต้นของอาร์เรย์ นั่นคือจำนวนองค์ประกอบทั้งหมดของอาเรย์และขนาดของหน่วยความจำที่จัดสรรไว้จะถูกระบุอย่างสมบูรณ์และไม่ซ้ำกันโดยคำจำกัดความของอาเรย์ แต่นี่ไม่สะดวกเสมอไป บางครั้งจำเป็นต้องปรับขนาดหน่วยความจำที่จัดสรรสำหรับอาเรย์เพื่อแก้ไขปัญหาเฉพาะ โดยไม่ทราบขนาดล่วงหน้าและไม่สามารถแก้ไขได้ การก่อตัวของอาร์เรย์ที่มีขนาดตัวแปร (อาร์เรย์แบบไดนามิก) สามารถจัดระเบียบได้โดยใช้พอยน์เตอร์และเครื่องมือ การจัดสรรหน่วยความจำแบบไดนามิก. อาร์เรย์แบบไดนามิกเป็นอาร์เรย์ที่มีขนาดไม่คงที่ล่วงหน้าและสามารถเปลี่ยนแปลงได้ระหว่างการทำงานของโปรแกรม เพื่อปรับขนาด อาร์เรย์แบบไดนามิก ภาษาโปรแกรม C++ ซึ่งสนับสนุนอาร์เรย์ดังกล่าว จัดเตรียมสิ่งพิเศษไว้ ฟังก์ชั่นในตัวหรือการดำเนินงาน อาร์เรย์แบบไดนามิกให้โอกาสในการทำงานกับข้อมูลได้อย่างยืดหยุ่นมากขึ้นเนื่องจากช่วยให้คุณไม่ต้องทำนายปริมาณข้อมูลที่เก็บไว้ แต่สามารถปรับขนาดของอาร์เรย์ตามปริมาณที่ต้องการจริง ภายใต้การประกาศมิติเดียว อาร์เรย์แบบไดนามิกเข้าใจการประกาศของตัวชี้ไปยังตัวแปรประเภทที่กำหนดเพื่อให้ตัวแปรนี้สามารถใช้เป็น อาร์เรย์แบบไดนามิก. ไวยากรณ์: พิมพ์ *ชื่ออาร์เรย์; ประเภท – ประเภทขององค์ประกอบที่กำลังประกาศ อาร์เรย์แบบไดนามิก. องค์ประกอบ อาร์เรย์แบบไดนามิกไม่สามารถมีฟังก์ชันและองค์ประกอบได้ ประเภทเป็นโมฆะ. ตัวอย่างเช่น: int *a; สองเท่า *d; ในตัวอย่างเหล่านี้ a และ d เป็นตัวชี้ไปยังจุดเริ่มต้นของตำแหน่งหน่วยความจำที่จัดสรร พอยน์เตอร์รับค่าที่อยู่ของพื้นที่หน่วยความจำที่จัดสรรสำหรับค่าประเภท int และประเภท double ตามลำดับ ดังนั้นเมื่อจัดสรรหน่วยความจำแบบไดนามิกสำหรับอาร์เรย์แบบไดนามิกคุณควรอธิบายตัวชี้ที่เกี่ยวข้องซึ่งจะกำหนดค่าที่อยู่ของจุดเริ่มต้นของพื้นที่หน่วยความจำที่จัดสรรไว้ เพื่อเป็นการจัดสรรหน่วยความจำให้กับมิติเดียว อาร์เรย์แบบไดนามิกใน C++ มี 2 วิธี 1) โดยการดำเนินการ new ซึ่งจัดสรรส่วนของหน่วยความจำไดนามิกที่มีขนาดที่เหมาะสมสำหรับการจัดเก็บอาเรย์และไม่อนุญาตให้เตรียมใช้งานองค์ประกอบอาเรย์ได้ ไวยากรณ์: ArrayName = ชนิดใหม่ [ConstantTypeExpression]; ArrayName – ตัวระบุอาร์เรย์ นั่นคือชื่อของตัวชี้สำหรับบล็อกหน่วยความจำที่จัดสรร ประเภทนิพจน์คงที่– กำหนดจำนวนองค์ประกอบ ( มิติข้อมูล) ของอาเรย์. นิพจน์ของประเภทคงที่จะถูกประเมิน ณ เวลารวบรวม ตัวอย่างเช่น: int *mas; mas = int ใหม่ ; /*จัดสรรหน่วยความจำแบบไดนามิกขนาด 100*sizeof(int) ไบต์*/ double *m = new double [n]; /*จัดสรรหน่วยความจำแบบไดนามิกขนาด n*sizeof(double) ไบต์*/ long (*lm); lm = ยาวใหม่ ; /*จัดสรรหน่วยความจำแบบไดนามิกขนาด 2*4*ขนาด (ยาว) ไบต์*/ เมื่อจัดสรรหน่วยความจำแบบไดนามิก จะต้องระบุขนาดของอาร์เรย์ให้ครบถ้วน 2) โดยใช้ฟังก์ชันไลบรารี malloc (calloc) ซึ่งใช้ในการจัดสรรหน่วยความจำแบบไดนามิก ไวยากรณ์: ArrayName = (ชนิด *) malloc(N*ขนาดของ(ชนิด)); ArrayName = (ชนิด *) calloc(N, ขนาดของ(ชนิด)); ArrayName – ตัวระบุอาร์เรย์ นั่นคือชื่อของตัวชี้สำหรับบล็อกหน่วยความจำที่จัดสรร Type – ประเภทของตัวชี้ไปยังอาร์เรย์ N – จำนวนองค์ประกอบอาร์เรย์ ตัวอย่างเช่น: ลอย *a; a=(ลอย *)malloc(10*ขนาดของ(ลอย)); // หรือ a=(float *)calloc(10,sizeof(float)); /*จัดสรรหน่วยความจำแบบไดนามิกขนาด 10*ขนาด (ลอย) ไบต์*/ เนื่องจากฟังก์ชัน malloc(calloc) กลับมา ตัวชี้ที่ไม่ได้พิมพ์เป็นโมฆะ * ดังนั้นจึงจำเป็นต้องแปลงผลลัพธ์ // การประกาศอาร์เรย์ไดนามิกสองมิติจำนวน 10 องค์ประกอบ: float **ptrarray = ลอยใหม่* ; // สองบรรทัดในอาร์เรย์ สำหรับ (จำนวน int = 0; นับ< 2; count++) ptrarray = โฟลตใหม่ ; // และห้าคอลัมน์ // โดยที่ ptrarray เป็นอาร์เรย์ของพอยน์เตอร์ไปยังพื้นที่หน่วยความจำที่จัดสรรสำหรับอาร์เรย์ของจำนวนจริงประเภท float ขั้นแรก มีการประกาศตัวชี้ลำดับที่สอง float **ptrarray ซึ่งอ้างอิงถึงอาร์เรย์ของตัวชี้ float* โดยที่ขนาดของอาร์เรย์คือสอง .
หลังจากนั้นใน for loop แต่ละบรรทัดของอาร์เรย์จะถูกประกาศ บรรทัดที่ 2หน่วยความจำได้รับการจัดสรรให้กับห้าองค์ประกอบ ผลลัพธ์ที่ได้คือ ptrarray อาร์เรย์แบบไดนามิก 2 มิติ ลองพิจารณาตัวอย่างการเพิ่มหน่วยความจำที่จัดสรรไว้สำหรับอาร์เรย์ไดนามิก 2 มิติ // การเพิ่มหน่วยความจำที่จัดสรรสำหรับอาร์เรย์ไดนามิกสองมิติ: สำหรับ (จำนวน int = 0; นับ< 2; count++) ลบ ptrarray; // โดยที่ 2 คือจำนวนบรรทัดในอาร์เรย์ #รวม int *a; // ชี้ไปที่อาร์เรย์ ระบบ("chcp 1251"); scanf("%d", &n); scanf("%d", &m); // การจัดสรรหน่วยความจำ a = (int*) malloc(n*m*sizeof(int)); // การป้อนองค์ประกอบอาร์เรย์ สำหรับ(i=0; ผม สำหรับ(j=0; เจ printf("a[%d][%d] = ", i, j); scanf("%d", (a+i*m+j)); // องค์ประกอบอาร์เรย์เอาท์พุต สำหรับ(i=0; ผม สำหรับ(j=0; เจ printf("%5d ", *(a+i*m+j)); // 5 คนรู้จักสำหรับองค์ประกอบอาร์เรย์ รับชาร์(); รับชาร์(); ผลการดำเนินการ กรอกจำนวนบรรทัด: 3 ป้อนจำนวนคอลัมน์: 4 อีกวิธีหนึ่งในการจัดสรรหน่วยความจำแบบไดนามิกสำหรับอาร์เรย์สองมิติก็เป็นไปได้เช่นกัน - โดยใช้อาร์เรย์ของพอยน์เตอร์ ในการทำเช่นนี้คุณต้องมี: ฟังก์ชัน malloc() ส่งคืนตัวชี้ไปยังไบต์แรกของพื้นที่หน่วยความจำขนาดขนาดที่ได้รับการจัดสรรจากพื้นที่หน่วยความจำที่จัดสรรแบบไดนามิก ถ้ามีหน่วยความจำไม่เพียงพอในพื้นที่หน่วยความจำแบบไดนามิก ตัวชี้ null จะถูกส่งกลับ #รวม อินท์**ก; // ตัวชี้ไปยังตัวชี้ไปยังสตริง ระบบ("chcp 1251"); printf("กรอกจำนวนบรรทัด: "); scanf("%d", &n); printf("ระบุจำนวนคอลัมน์: "); scanf("%d", &m); // การจัดสรรหน่วยความจำสำหรับพอยน์เตอร์ให้กับสตริง a = (int**)malloc(n*ขนาดของ(int*)); // การป้อนองค์ประกอบอาร์เรย์ สำหรับ(i=0; ผม // การจัดสรรหน่วยความจำเพื่อจัดเก็บสตริง a[i] = (int*)malloc(m*sizeof(int)); สำหรับ(j=0; เจ printf("a[%d][%d] = ", i, j); scanf("%d", &a[i][j]); // องค์ประกอบอาร์เรย์เอาท์พุต สำหรับ(i=0; ผม สำหรับ(j=0; เจ printf("%5d ", a[i][j]); // 5 คนรู้จักสำหรับองค์ประกอบอาร์เรย์ ฟรี(ก[i]); // เพิ่มหน่วยความจำให้กับสตริง รับชาร์(); รับชาร์(); ผลลัพธ์ของการรันโปรแกรมจะคล้ายกับกรณีก่อนหน้า การใช้การจัดสรรหน่วยความจำแบบไดนามิกสำหรับตัวชี้แถว คุณสามารถจัดสรรอาร์เรย์ว่างได้ ฟรี
เป็นอาร์เรย์สองมิติ (เมทริกซ์) ซึ่งขนาดของแถวอาจแตกต่างกันได้ ข้อดีของการใช้อาร์เรย์อิสระคือ คุณไม่จำเป็นต้องจัดสรรหน่วยความจำคอมพิวเตอร์มากเกินไปเพื่อรองรับสตริงที่มีความยาวสูงสุดที่เป็นไปได้ ที่จริงแล้ว อาร์เรย์อิสระคืออาร์เรย์หนึ่งมิติของตัวชี้ไปยังอาร์เรย์ข้อมูลหนึ่งมิติ พอยน์เตอร์ ตัวชี้คือตัวแปรที่มีค่าเป็นที่อยู่ซึ่งมีข้อมูลอยู่ ที่อยู่คือจำนวนเซลล์หน่วยความจำที่อยู่ในหรือจากที่ข้อมูลตั้งอยู่ ตามประเภทข้อมูลใน SI พอยน์เตอร์จะแบ่งออกเป็น: ตัวชี้ที่พิมพ์คือตัวชี้ที่มีที่อยู่ของข้อมูลบางประเภท (ระบบหรือผู้ใช้) ตัวชี้ที่ไม่ได้พิมพ์คือตัวชี้ที่มีที่อยู่ข้อมูลประเภทที่ไม่ระบุ (เพียงที่อยู่) ประกาศตัวชี้; การตั้งค่าตัวชี้; การเข้าถึงค่าที่อยู่ที่ตัวชี้ การประกาศ (คำอธิบาย) ของตัวชี้ในภาษา SI มีรูปแบบดังต่อไปนี้: พิมพ์ *ชื่อ [=value]; เมื่อประกาศ ตัวชี้ใน SI จะสามารถเริ่มต้นได้โดยการระบุค่าที่สอดคล้องกันผ่านเครื่องหมายกำหนด ค่านี้ต้องเป็นที่อยู่ซึ่งเขียนในรูปแบบใดรูปแบบหนึ่งต่อไปนี้: เป็นโมฆะ (id เป็นโมฆะ); ตัวชี้อื่น; ที่อยู่ที่เปลี่ยนแปลงได้ (ผ่านการดำเนินการรับที่อยู่) นิพจน์ที่แสดงถึงเลขคณิตของพอยน์เตอร์ ที่อยู่ที่เป็นผลมาจากการจัดสรรหน่วยความจำแบบไดนามิก #รวม int var; // ตัวแปรจำนวนเต็มปกติ int *ptrVar; // ตัวชี้จำนวนเต็ม (ptrVar ต้องเป็นประเภท int เนื่องจากจะอ้างอิงถึงตัวแปรประเภท int) ptrVar = // กำหนดที่อยู่ของเซลล์หน่วยความจำให้พอยน์เตอร์ โดยที่ค่าของตัวแปร var อยู่ scanf("%d", &var); // ตัวแปร var มีค่าที่ป้อนจากคีย์บอร์ด printf("%d\n", *ptrVar); // ค่าเอาต์พุตผ่านพอยน์เตอร์ ผลการดำเนินการ: 6 6 การบรรยายครั้งที่ 3 ฟังก์ชั่น. ฟังก์ชันคือโมดูลโปรแกรมที่มีชื่อที่ระบุทางวากยสัมพันธ์ซึ่งดำเนินการเฉพาะหรือกลุ่มของการดำเนินการ แต่ละฟังก์ชันมีส่วนต่อประสานและการนำไปใช้งานของตัวเอง อินเทอร์เฟซฟังก์ชัน – ส่วนหัวของฟังก์ชัน ซึ่งระบุชื่อของฟังก์ชัน รายการพารามิเตอร์ และประเภทของค่าที่ส่งคืน คำอธิบายของฟังก์ชันในภาษา SI จะดำเนินการที่ใดก็ได้ในโปรแกรมนอกเหนือจากคำอธิบายของฟังก์ชันอื่นๆ และประกอบด้วยสามองค์ประกอบ: 1. ต้นแบบฟังก์ชัน 2. ส่วนหัวของฟังก์ชัน; 3.ฟังก์ชั่นร่างกาย. ต้นแบบฟังก์ชันเป็นส่วนเสริมของคำอธิบายฟังก์ชันซึ่งมีจุดประสงค์เพื่อประกาศฟังก์ชันที่มีอินเทอร์เฟซที่สอดคล้องกับต้นแบบที่กำหนด การประกาศต้นแบบมีรูปแบบดังต่อไปนี้: พิมพ์ชื่อ (รายการประเภทพารามิเตอร์ที่เป็นทางการ); พารามิเตอร์ฟังก์ชันคือค่าที่ส่งผ่านไปยังฟังก์ชันเมื่อมีการเรียกใช้ ส่วนหัวของฟังก์ชัน – คำอธิบายส่วนอินเทอร์เฟซของฟังก์ชัน ซึ่งประกอบด้วย: ประเภทค่าที่ส่งคืน ชื่อของฟังก์ชัน และรายการพารามิเตอร์ที่เป็นทางการของฟังก์ชัน ไวยากรณ์สำหรับการประกาศส่วนหัวของฟังก์ชันคือ: พิมพ์ชื่อ (รายการพารามิเตอร์ที่เป็นทางการ) ตัวอย่างของส่วนหัวของฟังก์ชัน: Int func(int i, ดับเบิ้ล x, ดับเบิ้ล y) ฟังก์ชั่นโมฆะ (int ind, char *string) ฟังก์ชั่นคู่ (เป็นโมฆะ) เนื้อความของฟังก์ชันคือส่วนการใช้งานที่มีโค้ดโปรแกรมที่จะดำเนินการเมื่อมีการเรียกใช้ฟังก์ชัน เนื้อหาของฟังก์ชันจะมาต่อจากส่วนหัวของฟังก์ชันเสมอ (ไม่สามารถแยกออกจากกันได้) และจะอยู่ในเครื่องหมายปีกกา การใช้ฟังก์ชันใน SI เพื่อคำนวณแฟกทอเรียลของตัวเลข แฟกทอเรียลคู่ (ไม่ได้ลงนาม); แฟกทอเรียลคู่ (หมายเลขที่ไม่ได้ลงนาม) ข้อเท็จจริงสองเท่า = 1.0; สำหรับ(ไม่ได้ลงนาม i=1;i<=num;i++) ข้อเท็จจริง *= (สองเท่า)i; กลับข้อเท็จจริง; โครงสร้าง. โครงสร้างเป็นชนิดข้อมูลที่ซับซ้อนซึ่งแสดงถึงชุดขององค์ประกอบประเภทต่างๆ ที่เรียงลำดับในหน่วยความจำ แต่ละองค์ประกอบในโครงสร้างมีชื่อเป็นของตัวเองและเรียกว่าฟิลด์ การประกาศใน SI ของโครงสร้างดูเหมือนว่า: โครงสร้าง [ชื่อประเภท] ฟิลด์_1; ฟิลด์_2; สนาม_N; ) [รายการตัวแปร]; การประกาศฟิลด์โครงสร้างสามารถทำได้โดยไม่ต้องเริ่มต้นเท่านั้น หากหลายฟิลด์ที่ติดตามกันในคำอธิบายโครงสร้างมีประเภทเดียวกัน เพื่ออธิบายฟิลด์เหล่านั้น คุณสามารถใช้ไวยากรณ์เพื่อประกาศตัวแปรหลายตัวที่เป็นประเภทเดียวกันได้ ไฟล์. ไฟล์คือพื้นที่ที่มีชื่อข้อมูลบนสื่อบันทึกข้อมูลบางชนิด ประเภทไฟล์ (สัมพันธ์กับภาษา SI): การดำเนินการเพิ่มเติม: โหมดการเปิด ไฟล์ที่มี SI การเปลี่ยนเส้นทางสตรีม ฟังก์ชันส่งคืน: การปิดไฟล์ ฟังก์ชันส่งคืน: สิ้นสุดการตรวจสอบไฟล์ ฟังก์ชันส่งคืน: กำลังเปิดไฟล์ข้อความ อ่านจากไฟล์ข้อความ การอ่านที่จัดรูปแบบ ฟังก์ชันส่งคืน: ฟังก์ชันส่งคืน: ฟังก์ชันส่งคืน: เขียนเป็นข้อความ ไฟล์เอสไอ เอาต์พุตที่จัดรูปแบบแล้ว เขียนเป็นไฟล์ไบนารี การนำทางไฟล์ การอ่านออฟเซ็ตปัจจุบันในไฟล์: SEEK_SET (0) – จากจุดเริ่มต้นของไฟล์ รับสัญญาณข้อผิดพลาด: การบัฟเฟอร์ ฟังก์ชั่นล้างบัฟเฟอร์: สร้างบัฟเฟอร์ขนาด BUFSIZ ใช้ก่อนอินพุตหรือเอาต์พุตไปยังสตรีม ไฟล์ชั่วคราว ฟังก์ชั่นการสร้างไฟล์ชั่วคราว: การลบและเปลี่ยนชื่อ ฟังก์ชั่นการลบไฟล์: การบรรยายครั้งที่ 4 ซ้อนกัน. สแต็กเป็นสิ่งที่ตรงกันข้ามกับคิวเนื่องจากทำงานแบบเข้าหลังออกก่อน (LIFO) หากต้องการเห็นภาพปึก ให้นึกถึงแผ่นปึก จานแรกที่วางอยู่บนโต๊ะจะถูกใช้เป็นอันดับสุดท้าย และจานสุดท้ายที่วางอยู่ด้านบนจะถูกใช้ก่อน สแต็กมักใช้ในซอฟต์แวร์ระบบ รวมถึงคอมไพเลอร์และล่าม เมื่อทำงานกับสแต็ก การดำเนินการหลักคือการแทรกและดึงองค์ประกอบ การดำเนินการเหล่านี้มักเรียกว่า "พุช" และ "ป๊อป" ดังนั้น ในการใช้งานสแต็ก คุณจะต้องเขียนสองฟังก์ชัน: push() ซึ่ง "ส่ง" ค่าไปยังสแต็ก และ pop() ซึ่ง "ป๊อป" ค่าจากสแต็ก คุณต้องจัดสรรพื้นที่หน่วยความจำที่จะใช้เป็นสแต็กด้วย เพื่อจุดประสงค์นี้ คุณสามารถจัดสรรอาร์เรย์หรือจัดสรรหน่วยความจำแบบไดนามิกโดยใช้ฟังก์ชันภาษา C ที่จัดเตรียมไว้สำหรับการจัดสรรหน่วยความจำแบบไดนามิก เช่นเดียวกับคิว ฟังก์ชันดึงข้อมูลจะนำองค์ประกอบออกจากรายการและลบออกหากยังไม่ได้จัดเก็บไว้ที่อื่น ด้านล่างนี้คือรูปแบบทั่วไปของฟังก์ชัน push() และ pop() ที่ทำงานบนอาร์เรย์จำนวนเต็ม สแต็กข้อมูลประเภทอื่นๆ สามารถจัดระเบียบได้โดยการเปลี่ยนประเภทข้อมูลพื้นฐานของอาร์เรย์ int ถึง = 0; /* ด้านบนของสแต็ก */ /* ผลักองค์ประกอบลงบนสแต็ก */ โมฆะผลักดัน (int i) ถ้า(tos >= MAX) ( printf("สแต็คเต็ม\n"); /* รับองค์ประกอบด้านบนของสแต็ก */ ถ้า(tos< 0) { printf("สแต็คว่างเปล่า\n"); กองกลับ; ตัวแปร tos ("ด้านบนของสแต็ก") มีดัชนีด้านบนของสแต็ก เมื่อใช้ฟังก์ชันเหล่านี้ จำเป็นต้องคำนึงถึงกรณีที่สแต็กเต็มหรือว่างเปล่า ในกรณีของเรา สัญญาณของสแต็กว่างคือ tos เท่ากับศูนย์ และสัญญาณของสแต็กล้นคือ to เพิ่มขึ้นมากจนค่าของมันจะชี้ไปที่ใดที่หนึ่งเลยเซลล์สุดท้ายของอาร์เรย์ ตัวอย่างการทำงานกับสแต็ก สแตกจะอยู่ในหน่วยความจำที่จัดสรรแบบไดนามิก และไม่ใช่ในอาร์เรย์ที่มีขนาดคงที่ แม้ว่าตัวอย่างง่ายๆ นี้จะไม่จำเป็นต้องใช้การจัดสรรหน่วยความจำแบบไดนามิก เราจะมาดูวิธีใช้หน่วยความจำแบบไดนามิกเพื่อจัดเก็บข้อมูลสแต็ก /* เครื่องคิดเลขง่ายๆ ที่มีสี่ขั้นตอน */ #รวม #รวม อินท์ *p; /* ตัวชี้ไปยังพื้นที่หน่วยความจำว่าง */ int *ถึง; /* ตัวชี้ไปที่ด้านบนของสแต็ก */ int *บอส; /* ตัวชี้ไปที่ด้านล่างของสแต็ก */ โมฆะผลักดัน (int i); p = (int *) malloc(สูงสุด*ขนาดของ(int)); /* รับหน่วยความจำสำหรับสแต็ก */ printf("เกิดข้อผิดพลาดขณะจัดสรรหน่วยความจำ\n"); บอส = p + MAX-1; printf("เครื่องคิดเลขสี่ขั้นตอน\n"); printf("กด "q" เพื่อออก\n"); printf("%d\n", a+b); printf("%d\n", b-a); printf("%d\n", b*a); printf("หารด้วย 0.\n"); printf("%d\n", b/a); case ".": /* แสดงเนื้อหาที่อยู่ด้านบนของ stack */ printf("ค่าปัจจุบันที่ด้านบนของสแต็ก: %d\n", a); ) ในขณะที่(*s != "q"); /* ผลักองค์ประกอบลงบนสแต็ก */ โมฆะผลักดัน (int i) ถ้า(p > บอส) ( printf("สแต็คเต็ม\n"); /* รับองค์ประกอบด้านบนจากสแต็ก */ ถ้า(น< tos) { printf("สแต็คว่างเปล่า\n"); คิว. คิวคือรายการข้อมูลเชิงเส้นที่ได้รับการประมวลผลแบบเข้าก่อนออกก่อน หลักการนี้ (และคิวที่เป็นโครงสร้างข้อมูล) บางครั้งเรียกว่า FIFO ซึ่งหมายความว่าองค์ประกอบแรกที่อยู่ในคิวจะได้รับจากองค์ประกอบนั้นก่อน องค์ประกอบที่สองที่วางจะถูกลบออกในลำดับที่สอง เป็นต้น นี่เป็นวิธีเดียวที่จะทำงานกับคิวได้ ไม่อนุญาตให้เข้าถึงแต่ละองค์ประกอบโดยสุ่ม ลองนึกภาพว่าคิวทำงานอย่างไร ขอแนะนำสองฟังก์ชัน: qstore() และ qretrieve() (จาก "store" - "save", "retrieve" - "receive") ฟังก์ชัน qstore() วางองค์ประกอบไว้ที่ส่วนท้ายของคิว และฟังก์ชัน qretrieve() จะลบองค์ประกอบออกจากจุดเริ่มต้นของคิวและส่งกลับค่าของมัน ตารางแสดงลำดับของการดำเนินการดังกล่าว โปรดทราบว่าการดำเนินการดึงข้อมูลจะลบองค์ประกอบออกจากคิวและทำลายองค์ประกอบนั้น เว้นแต่จะถูกเก็บไว้ที่อื่น ดังนั้นหลังจากดึงข้อมูลองค์ประกอบทั้งหมดแล้ว คิวจะว่างเปล่า ในการเขียนโปรแกรม คิวถูกใช้เพื่อแก้ไขปัญหาต่างๆ มากมาย งานประเภทหนึ่งที่ได้รับความนิยมมากที่สุดคือการจำลอง คิวยังใช้ในตัวกำหนดเวลางานของระบบปฏิบัติการและในการบัฟเฟอร์ I/O /* ตัวกำหนดเวลากิจกรรมขนาดเล็ก */ #รวม #รวม #รวม #รวม ถ่าน *p, *qretrieve(โมฆะ); เป็นโมฆะป้อน (เป็นโมฆะ), qstore (ถ่าน * q), ทบทวน (เป็นโมฆะ), ลบ_ap (เป็นโมฆะ); สำหรับ(t=0; เสื้อ< MAX; ++t) p[t] = NULL; /* иницилизировать массив พอยน์เตอร์ว่าง */ printf("ป้อน (E), รายการ (L), ลบ (R), ออก (Q): "); *s = ท็อปเปอร์(*s); /* แทรกการนัดหมายใหม่ลงในคิว */ เป็นโมฆะป้อน (เป็นโมฆะ) printf("ป้อนการนัดหมาย %d: ", spos+1); if(*s==0) แตก; /* ไม่มีการบันทึก */ p = (ถ่าน *) malloc(strlen(s)+1); printf("หน่วยความจำไม่เพียงพอ\n"); ถ้า(*s) qstore(p); /* ดูเนื้อหาของคิว */ การตรวจสอบเป็นโมฆะ (เป็นโมฆะ) สำหรับ(t=rpos; t< spos; ++t) printf("%d. %s\n", t+1, p[t]); /* ลบการนัดหมายออกจากคิว */ เป็นโมฆะ Delete_ap (เป็นโมฆะ) if((p=qretrieve())==NULL) กลับ; printf("%s\n", p); /* ใส่การนัดหมาย */ เป็นโมฆะ qstore (ถ่าน * q) printf("รายการเต็ม\n"); /* รับการนัดหมาย */ ถ่าน *qretrieve(เป็นโมฆะ) ถ้า(rpos==spos) ( printf("ไม่มีการประชุมอีกต่อไป\n"); กลับพี; รายการ. รายการวงจรที่เชื่อมโยงเดี่ยวคือการประกาศโครงสร้างแบบเรียกซ้ำหรือเป็นตัวชี้ไปยังโครงสร้างประเภทนั้นเอง: int data;//ช่องข้อมูล s *ถัดไป;//องค์ประกอบถัดไป ) *first,*curr;//องค์ประกอบแรกและปัจจุบัน การเริ่มต้น: ครั้งแรก -> ถัดไป = สกุลเงิน; เพื่อรับองค์ประกอบแรกให้ใช้ first->data เพื่อเพิ่มองค์ประกอบใหม่: curr->next=new s; curr=curr->next;//ไปที่อันสุดท้าย และเพื่อให้ได้องค์ประกอบที่ 50 ให้วนซ้ำรายการ: curr=first;//ไปที่อันดับแรก สำหรับ(int i=0;i<50;i++) ถ้า(curr->ถัดไป!=NULL) curr=curr->ถัดไป; ข้อมูลที่เกี่ยวข้อง. ครั้งแรกบนเว็บไซต์นี้ เอาล่ะไปกันเลย ฉันยังใหม่กับ C++ และกำลังเขียนหนังสือ "โครงสร้างข้อมูลโดยใช้ C++ 2nd ed, D.S. Malik" ในหนังสือ Malik เสนอสองวิธีในการสร้างอาร์เรย์สองมิติแบบไดนามิก ในวิธีแรก คุณจะประกาศตัวแปรเป็นอาร์เรย์ของพอยน์เตอร์ โดยที่พอยน์เตอร์แต่ละตัวเป็นประเภทจำนวนเต็ม เช่น Int *บอร์ด; จากนั้นใช้ for-loop เพื่อสร้าง "คอลัมน์" เมื่อใช้อาร์เรย์ของพอยน์เตอร์เป็น "แถว" วิธีที่สอง คุณใช้ตัวชี้ไปยังตัวชี้ Int **บอร์ด; บอร์ด = int ใหม่* ; คำถามของฉันคือ: วิธีไหนดีกว่ากัน? วิธี ** นั้นง่ายกว่าสำหรับฉันที่จะเห็นภาพ แต่วิธีแรกสามารถใช้ได้ในลักษณะเดียวกันมาก ทั้งสองวิธีสามารถใช้เพื่อสร้างอาร์เรย์ 2 มิติแบบไดนามิกได้ แก้ไข: ไม่ชัดเจนเพียงพอตามที่ระบุไว้ข้างต้น นี่คือรหัสที่ฉันลอง: แถว Int, คอลัมน์; ศาล<< "Enter row size:";
cin >>แถว; ศาล<< "\ncol:";
cin >>คอล; int *p_board; สำหรับ (int i=0; i< row; i++)
p_board[i] = new int;
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_board[i][j] = j;
cout << p_board[i][j] << " ";
}
cout << endl;
}
cout << endl << endl;
int **p_p_board;
p_p_board = new int* ;
for (int i=0; i < row; i++)
p_p_board[i] = new int;
for (int i=0; i < row; i++)
{
for (int j=0; j < col; j++)
{
p_p_board[i][j] = j;
cout << p_p_board[i][j] << " ";
}
cout << endl;
} 4 คำตอบ วิธีแรกไม่สามารถใช้เพื่อสร้างได้ พลวัตอาร์เรย์ 2 มิติเพราะ: Int *บอร์ด; คุณได้จัดสรรอาร์เรย์ของพอยน์เตอร์ 4 ตัวให้กับ int ต่อกอง. ดังนั้นหากคุณเติมพอยน์เตอร์ทั้ง 4 ตัวเหล่านี้ด้วยอาเรย์แบบไดนามิก: สำหรับ (int i = 0; i< 4; ++i) {
board[i] = new int;
}
สิ่งที่คุณจะได้คืออาร์เรย์ 2 มิติ คงที่จำนวนบรรทัด (ในกรณีนี้คือ 4) และ พลวัตจำนวนคอลัมน์ (ในกรณีนี้คือ 10) ดังนั้นไดนามิกจึงไม่ใช่ อย่างเต็มที่เนื่องจากเมื่อทำการจัดสรรอาร์เรย์บนสแต็กคุณ ต้องระบุ ขนาดคงที่, เช่น. รู้จักกันใน เวลา. พลวัตอาร์เรย์ถูกเรียกว่า พลวัตเพราะไม่จำเป็นต้องรู้ขนาดของมัน รวบรวมเวลาแต่สามารถกำหนดได้ด้วยตัวแปรบางตัวใน ระหว่างการดำเนินการ. อีกครั้งเมื่อคุณทำ: Int *บอร์ด; ค่าคงที่ x = 4; //<--- `const` qualifier is absolutely needed in this case!
int *board[x];
คุณระบุค่าคงที่ที่ทราบมา รวบรวมเวลา(ในกรณีนี้คือ 4 หรือ x) เพื่อให้คอมไพเลอร์สามารถทำได้ในขณะนี้ เลือกไว้ล่วงหน้าหน่วยความจำนี้สำหรับอาเรย์ของคุณ และเมื่อโปรแกรมของคุณถูกโหลดเข้าสู่หน่วยความจำ มันก็จะมีหน่วยความจำสำหรับอาเรย์บอร์ดเท่านี้อยู่แล้ว จึงเรียกว่า คงที่, เช่น. เพราะขนาด ฮาร์ดโค้ดและ ไม่สามารถเปลี่ยนแปลงแบบไดนามิกได้(ที่รันไทม์) ในทางกลับกัน เมื่อคุณทำ: Int **บอร์ด; บอร์ด = int ใหม่*; อินท์ x = 10; //<--- Notice that it does not have to be `const` anymore!
int **board;
board = new int*[x];
คอมไพเลอร์ไม่ทราบว่าอาร์เรย์บอร์ดจะต้องใช้หน่วยความจำเท่าใดดังนั้นจึงไม่ทราบ จัดสรรไว้ล่วงหน้าทั้งหมด. แต่เมื่อคุณรันโปรแกรม ขนาดของอาเรย์จะถูกกำหนดโดยค่าของตัวแปร x (ที่รันไทม์) และพื้นที่ที่สอดคล้องกันสำหรับอาเรย์บอร์ดจะถูกจัดสรรให้กับสิ่งที่เรียกว่า พวง- พื้นที่หน่วยความจำที่โปรแกรมทั้งหมดที่ทำงานบนคอมพิวเตอร์ของคุณสามารถจัดสรรได้ ไม่ทราบล่วงหน้า(ณ เวลาคอมไพล์) สรุปความจำเพื่อการใช้งานส่วนตัว ด้วยเหตุนี้ หากต้องการสร้างอาร์เรย์ 2D แบบไดนามิก คุณต้องใช้วิธีที่สอง: Int **บอร์ด; บอร์ด = int ใหม่*; // อาร์เรย์ไดนามิก (ขนาด 10) ของพอยน์เตอร์เป็น int for (int i = 0; i< 10; ++i) {
board[i] = new int;
// each i-th pointer is now pointing to dynamic array (size 10) of actual int values
}
เราเพิ่งสร้างอาร์เรย์ 2 มิติขนาด 10 คูณ 10 ขึ้นมา หากต้องการผ่านมันและเติมด้วยค่าจริง เช่น 1 เราสามารถใช้ลูปที่ซ้อนกันได้: สำหรับ (int i = 0; i< 10; ++i) { // for each row
for (int j = 0; j < 10; ++j) { // for each column
board[i][j] = 1;
}
} สิ่งที่คุณอธิบายสำหรับวิธีที่สองสร้างเฉพาะอาร์เรย์ 1D เท่านั้น: Int *board = int ใหม่; สิ่งนี้เพียงจัดสรรอาร์เรย์ด้วย 10 องค์ประกอบ บางทีคุณอาจหมายถึงบางสิ่งเช่นนี้: Int **board = int ใหม่*; สำหรับ (int i = 0; i< 4; i++) {
board[i] = new int;
}
ในกรณีนี้ เราจัดสรร 4 int* จากนั้นแต่ละจุดไปยังอาร์เรย์ที่จัดสรรแบบไดนามิก 10 int ตอนนี้เราเปรียบเทียบสิ่งนี้กับบอร์ด int*; . ข้อแตกต่างที่สำคัญคือเมื่อใช้อาร์เรย์ดังกล่าว จะต้องทราบจำนวน "แถว" ณ เวลาคอมไพล์ เนื่องจากอาร์เรย์ต้องมีขนาดเวลาคอมไพล์คงที่ คุณอาจประสบปัญหาหากคุณต้องการส่งคืนอาร์เรย์นี้จาก int* s เนื่องจากอาร์เรย์จะถูกทำลายเมื่อสิ้นสุดขอบเขต วิธีการจัดสรรทั้งแถวและคอลัมน์แบบไดนามิกจำเป็นต้องมีการวัดที่ซับซ้อนมากขึ้นเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ คุณควรเพิ่มหน่วยความจำดังนี้: สำหรับ (int i = 0; i< 4; i++) {
delete board[i];
}
delete board;
เลยต้องแนะนำให้ใช้ภาชนะมาตรฐานแทน คุณสามารถใช้ std::array ในทั้งสองกรณี มิติภายในของคุณสามารถตั้งค่าแบบไดนามิกได้ (เช่น นำมาจากตัวแปร) แต่ความแตกต่างอยู่ที่มิติภายนอก คำถามนี้โดยพื้นฐานแล้วเทียบเท่ากับสิ่งต่อไปนี้: คือ int* x = int ใหม่; "ดีกว่า" มากกว่า int x ? คำตอบคือ "ไม่ เว้นแต่คุณจะต้องเลือกขนาดอาเรย์นั้นแบบไดนามิก" รหัสนี้ทำงานได้ดีกับข้อกำหนดไลบรารีภายนอกน้อยมาก และแสดงการใช้งานพื้นฐานของ int **array คำตอบนี้แสดงว่าอาร์เรย์ แต่ละมีขนาดไดนามิกและวิธีการกำหนดอาร์เรย์เชิงเส้นที่มีขนาดไดนามิกให้กับอาร์เรย์สาขาที่มีขนาดไดนามิก โปรแกรมนี้รับอาร์กิวเมนต์จาก STDIN ในรูปแบบต่อไปนี้: 2 2
3 1 5 4
5 1 2 8 9 3
0 1
1 3
รหัสโปรแกรมอยู่ด้านล่าง... #รวม นี่เป็นการใช้งานที่ง่ายมากของ int main และขึ้นอยู่กับ std::cin และ std::cout เท่านั้น แบร์โบน แต่ก็ดีพอที่จะแสดงวิธีการทำงานกับอาร์เรย์หลายมิติอย่างง่าย
ปัญหาที่ 2
ในกรณีนี้เจ้าหน้าที่จะจุดไฟให้เราเป็นสีแดง >>
เนื่องจากค่าคงที่ไม่สามารถเปลี่ยนแปลงได้
ที่นี่เราได้รับคำเตือนว่าขนาดของอาร์เรย์ไม่สามารถเป็นค่าของตัวแปรปกติได้ ต้องการค่าคงที่!
การประกาศอาร์เรย์ไดนามิกมิติเดียว
การจัดสรรหน่วยความจำสำหรับอาเรย์ไดนามิกหนึ่งมิติ
#รวม
#รวม
เป็นโมฆะหลัก ()
{
}
- จัดสรรบล็อก RAM สำหรับอาร์เรย์ของพอยน์เตอร์
- จัดสรรบล็อก RAM สำหรับอาร์เรย์หนึ่งมิติซึ่งเป็นแถวของเมทริกซ์ที่ต้องการ
- เขียนที่อยู่ของบรรทัดลงในอาร์เรย์ของพอยน์เตอร์
#รวม
#รวม
เป็นโมฆะหลัก ()
{
}
ข้อความ;
ไบนารี่.
การดำเนินการพื้นฐานกับไฟล์:
1.การเปิดไฟล์
2.การอ่านและการเขียนข้อมูล
3.การปิดไฟล์
1. การนำทางไฟล์
2. การจัดการข้อผิดพลาดเมื่อทำงานกับไฟล์
3.การลบและเปลี่ยนชื่อไฟล์
4.คำอธิบายตัวแปร
FILE * freopen (const char * ชื่อไฟล์, const char * โหมด, FILE * สตรีม);
ตัวชี้ไปยังไฟล์ - ทุกอย่างเรียบร้อยดี
NULL – ข้อผิดพลาดแทนที่
int fclose (ไฟล์ * สตรีม);
0 – ปิดไฟล์สำเร็จ
1 – เกิดข้อผิดพลาดในการปิดไฟล์
int feof(ไฟล์ *สตรีม);
สตรีม - ตัวชี้ไปยังไฟล์ที่เปิด
0 – หากยังไม่ถึงจุดสิ้นสุดของไฟล์
!0 – ถึงจุดสิ้นสุดของไฟล์แล้ว
พารามิเตอร์ตัวที่สองระบุอักขระ t เพิ่มเติม (เป็นทางเลือก):
rt, น้ำหนัก, ที่, rt+, น้ำหนัก+, ที่+
int fscanf(ไฟล์ *สตรีม, รูปแบบ const char *, ...);
>0 – จำนวนตัวแปรที่อ่านสำเร็จ
0 - ไม่มีการอ่านตัวแปรใดสำเร็จ
EOF - ถึงข้อผิดพลาดหรือถึงจุดสิ้นสุดของไฟล์
กำลังอ่านบรรทัด
บัฟเฟอร์ - ทุกอย่างเรียบร้อยดี
กำลังอ่านบรรทัด
ถ่าน * fgets (ถ่าน * บัฟเฟอร์, int maxlen, FILE * สตรีม);
บัฟเฟอร์ - ทุกอย่างเรียบร้อยดี
NULL – ข้อผิดพลาดหรือถึงจุดสิ้นสุดของไฟล์
การอ่านสัญลักษณ์
int fgetc(ไฟล์ *สตรีม);
ฟังก์ชันส่งคืน:
รหัสสัญลักษณ์ - หากทุกอย่างเรียบร้อยดี
EOF – หากเกิดข้อผิดพลาดหรือถึงจุดสิ้นสุดของไฟล์
นำตัวละครกลับเข้าสู่กระแส
int ungetc(int c, FILE *สตรีม);
ฟังก์ชันส่งคืน:
รหัสสัญลักษณ์ – หากทุกอย่างสำเร็จ
EOF – มีข้อผิดพลาดเกิดขึ้น
int fprintf(FILE *กระแส, const char *รูปแบบ, ...);
ฟังก์ชันส่งคืน:
จำนวนตัวอักษรที่เขียน - หากทุกอย่างเป็นปกติ
ค่าลบ – หากมีข้อผิดพลาด
การเขียนสตริง
int fputs(const char *string, FILE *stream);
ฟังก์ชันส่งคืน:
จำนวนตัวอักษรที่เขียน - ทุกอย่างเรียบร้อยดี
EOF – มีข้อผิดพลาดเกิดขึ้น
เขียนสัญลักษณ์
int fputc (int c, FILE * สตรีม);
ฟังก์ชันส่งคืน:
รหัสของสัญลักษณ์ที่บันทึกไว้ - ทุกอย่างเรียบร้อยดี
EOF – มีข้อผิดพลาดเกิดขึ้น
การเปิดไฟล์ไบนารี
พารามิเตอร์ตัวที่สองระบุสัญลักษณ์ b เพิ่มเติม (จำเป็น): rb, wb, ab, rb+, wb+, ab+
อ่านจากไฟล์ไบนารี
size_t fread (โมฆะ * บัฟเฟอร์, ขนาด size_t, size_t num, FILE * สตรีม);
ฟังก์ชันส่งคืนจำนวนบล็อกที่อ่าน หากน้อยกว่า num แสดงว่าเกิดข้อผิดพลาดหรือไปถึง
จุดสิ้นสุดของไฟล์
size_t fwrite (const เป็นโมฆะ * บัฟเฟอร์, ขนาด size_t, size_t num, FILE * สตรีม);
ฟังก์ชันส่งคืนจำนวนบล็อกที่เขียน หากน้อยกว่า num แสดงว่าเกิดข้อผิดพลาด
int ftell ยาว (ไฟล์ * สตรีม);
การเปลี่ยนออฟเซ็ตปัจจุบันในไฟล์:
int fseek (ไฟล์ * สตรีม, int offset แบบยาว, int origin);
SEEK_CUR (1) – จากตำแหน่งปัจจุบัน
SEEK_END (2) – จากท้ายไฟล์
ฟังก์ชันส่งคืน:
0 – ทุกอย่างเรียบร้อยดี
!0 – เกิดข้อผิดพลาด
ย้ายไปที่จุดเริ่มต้นของไฟล์:
ย้อนกลับเป็นโมฆะ (FILE * สตรีม);
การอ่านตำแหน่งปัจจุบันในไฟล์:
int fgetpos(ไฟล์ *สตรีม, fpos_t *pos);
การตั้งค่าตำแหน่งปัจจุบันในไฟล์:
int fsetpos(ไฟล์ *สตรีม, const fpos_t *pos);
ฟังก์ชันส่งคืน:
0 - ทุกอย่างสำเร็จ
!0 – เกิดข้อผิดพลาด
โครงสร้าง fpos_t:
typedef โครงสร้าง fpos_t (
หายไปนาน;
mbstate_t wstate;
) fpos_t;
int ferror (ไฟล์ * สตรีม);
ฟังก์ชันจะส่งกลับค่าที่ไม่ใช่ศูนย์หากเกิดข้อผิดพลาด
ฟังก์ชั่นรีเซ็ตข้อผิดพลาด:
เป็นโมฆะ clearerr (FILE * สตรีม);
ฟังก์ชั่นข้อความแสดงข้อผิดพลาด:
เป็นโมฆะ perror (const char *string);
int fflush (ไฟล์ * สตรีม);
ฟังก์ชันส่งคืน:
0 – ทุกอย่างเรียบร้อยดี
EOF – มีข้อผิดพลาดเกิดขึ้น
ฟังก์ชั่นการจัดการบัฟเฟอร์:
เป็นโมฆะ setbuf (ไฟล์ * สตรีม, ถ่าน * บัฟเฟอร์);
FILE * tmpfile (เป็นโมฆะ);
สร้างไฟล์ชั่วคราวในโหมด wb+ หลังจากปิดไฟล์แล้ว ไฟล์หลังจะถูกลบโดยอัตโนมัติ
ฟังก์ชั่นการสร้างชื่อไฟล์ชั่วคราว:
ถ่าน * tmpnam (ถ่าน * บัฟเฟอร์);
int ลบ (const char * ชื่อไฟล์);
ฟังก์ชั่นการเปลี่ยนชื่อไฟล์:
int เปลี่ยนชื่อ (const char *fname, const char *nname);
ฟังก์ชันส่งคืน:
0 – หากสำเร็จ
!0 – มิฉะนั้น
การกระทำ เนื้อหาคิว
คิวสโตร์(A) ก
คิวสโตร์(B) เอบี
คิวสโตร์(C) เอ บี ซี
qretrieve() ส่งคืน A บี ซี
คิวสโตร์(D) บี ซี ดี
qretrieve() ส่งคืน B ซีดี
qretrieve() ส่งคืน C ดี