ความแตกต่างระหว่างการจัดสรรหน่วยความจำแบบคงที่และการจัดสรรหน่วยความจำแบบไดนามิก


100

ฉันต้องการทราบว่าอะไรคือความแตกต่างระหว่างการจัดสรรหน่วยความจำแบบคงที่และการจัดสรรหน่วยความจำแบบไดนามิก

คุณช่วยอธิบายเรื่องนี้ด้วยตัวอย่างได้ไหม

คำตอบ:


98

การจัดสรรมีสามประเภท ได้แก่ แบบคงที่อัตโนมัติและแบบไดนามิก

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

การจัดสรรหน่วยความจำอัตโนมัติเกิดขึ้นสำหรับตัวแปร (ไม่คงที่) ที่กำหนดไว้ภายในฟังก์ชันและโดยปกติจะถูกเก็บไว้ในสแตก (แม้ว่ามาตรฐาน C จะไม่ได้กำหนดว่าจะใช้สแต็ก) คุณไม่จำเป็นต้องสำรองหน่วยความจำเพิ่มเติมโดยใช้หน่วยความจำเหล่านี้ แต่ในทางกลับกันยังมีการควบคุมอายุการใช้งานของหน่วยความจำนี้อย่าง จำกัด เช่น: ตัวแปรอัตโนมัติในฟังก์ชันจะอยู่ที่นั่นจนกว่าฟังก์ชันจะเสร็จสิ้น

void func() {
    int i; /* `i` only exists during `func` */
}

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

int* func() {
    int* mem = malloc(1024);
    return mem;
}

int* mem = func(); /* still accessible */

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

free(mem);

2
แน่ใจว่าคุณสามารถควบคุมอายุการใช้งานของตัวแปรได้ ... คุณเป็นคนตัดสินใจขอบเขตใช่ไหม
Luchian Grigore

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

5
-1 คำตอบนี้ผิด คุณสับสนระหว่างตัวแปรคงที่และตัวแปรอัตโนมัติ
brice

2
ประโยคของคุณเองอ่าน: " คงหมายถึงการจัดสรรที่หน่วยความจำสำหรับตัวแปรของคุณจะถูกโดยอัตโนมัติจัดสรร" นี่คือผิด ดูว่าหน้าคู่มือสำหรับ libc ของ GNUพูดถึงเรื่องนี้อย่างไร
brice

1
@EliBendersky มันถูก rephrased ตอนนี้ ตรวจสอบว่าตอนนี้ถูกต้องหรือไม่
Suraj Jain

120

นี่เป็นคำถามสัมภาษณ์มาตรฐาน:

การจัดสรรหน่วยความจำแบบไดนามิก

มีการจัดสรรหน่วยความจำที่รันไทม์ใช้calloc(), malloc()และเพื่อน ๆ บางครั้งเรียกว่าหน่วยความจำ 'ฮีป' แม้ว่าจะไม่มีส่วนเกี่ยวข้องกับการอ้างอิงโครงสร้างข้อมูลฮีปก็ตาม

int * a = malloc(sizeof(int));

หน่วยความจำฮีปจะคงอยู่จนกว่าfree()จะถูกเรียก กล่าวอีกนัยหนึ่งคือคุณควบคุมอายุการใช้งานของตัวแปร

การจัดสรรหน่วยความจำอัตโนมัติ

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

int a = 43;

โปรดทราบว่าขอบเขตไม่จำเป็นต้องหมายถึงฟังก์ชัน ขอบเขตสามารถซ้อนกันภายในฟังก์ชันและตัวแปรจะอยู่ในขอบเขตเฉพาะภายในบล็อกที่มีการประกาศ โปรดทราบด้วยว่าไม่ได้ระบุตำแหน่งที่จัดสรรหน่วยความจำนี้ (ในระบบที่มีเหตุผลมันจะอยู่บนสแต็กหรือลงทะเบียนเพื่อการปรับให้เหมาะสม)

การจัดสรรหน่วยความจำแบบคงที่

มีการจัดสรรที่รวบรวมเวลา*และอายุการใช้งานของตัวแปรในความทรงจำคงที่คืออายุการใช้งานของโปรแกรม

ใน C สามารถจัดสรรหน่วยความจำแบบคงที่ได้โดยใช้staticคีย์เวิร์ด ขอบเขตคือหน่วยคอมไพล์เท่านั้น

สิ่งที่ได้รับน่าสนใจมากขึ้นเมื่อexternคำหลักที่ถือว่าเป็น เมื่อexternตัวแปรที่กำหนดไว้ในหน่วยความจำคอมไพเลอร์จัดสรรสำหรับมัน เมื่อมีการประกาศexternตัวแปรคอมไพเลอร์ต้องการให้กำหนดตัวแปรไว้ที่อื่น ความล้มเหลวในการประกาศ / กำหนดตัวแปรจะทำให้เกิดปัญหาการเชื่อมโยงในขณะที่ความล้มเหลวในการประกาศ / กำหนดตัวแปรจะทำให้เกิดปัญหาในการคอมไพล์externstatic

ในขอบเขตไฟล์คำสำคัญแบบคงที่เป็นทางเลือก (นอกฟังก์ชัน):

int a = 32;

แต่ไม่อยู่ในขอบเขตของฟังก์ชัน (ภายในฟังก์ชัน):

static int a = 32;

ในทางเทคนิคexternและstaticเป็นสองคลาสที่แยกจากกันของตัวแปรใน C.

extern int a; /* Declaration */
int a; /* Definition */

*หมายเหตุเกี่ยวกับการจัดสรรหน่วยความจำแบบคงที่

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

มันอาจจะดีกว่าที่จะคิดว่าการจัดสรรหน่วยความจำแบบคงที่ถูกจัดการโดยคอมไพเลอร์มากกว่าการจัดสรรที่รวบรวมเวลา

ตัวอย่างเช่นคอมไพเลอร์อาจสร้างdataส่วนขนาดใหญ่ในไบนารีที่คอมไพล์แล้วและเมื่อโปรแกรมถูกโหลดในหน่วยความจำที่อยู่ภายในdataส่วนของโปรแกรมจะถูกใช้เป็นตำแหน่งของหน่วยความจำที่จัดสรร สิ่งนี้มีข้อเสียที่ชัดเจนในการทำให้ไบนารีที่คอมไพล์มีขนาดใหญ่มากหากใช้หน่วยความจำแบบคงที่จำนวนมาก เป็นไปได้ที่จะเขียนไบนารีหลายกิกะไบต์ที่สร้างขึ้นจากโค้ดน้อยกว่าครึ่งโหล อีกทางเลือกหนึ่งคือให้คอมไพลเลอร์ฉีดรหัสการเริ่มต้นซึ่งจะจัดสรรหน่วยความจำด้วยวิธีอื่นก่อนที่โปรแกรมจะทำงาน รหัสนี้จะแตกต่างกันไปตามแพลตฟอร์มและระบบปฏิบัติการเป้าหมาย ในทางปฏิบัติคอมไพเลอร์สมัยใหม่ใช้ฮิวริสติกส์เพื่อตัดสินใจว่าจะใช้ตัวเลือกใดต่อไปนี้ คุณสามารถลองทำสิ่งนี้ด้วยตัวเองโดยเขียนโปรแกรม C ขนาดเล็กที่จัดสรรอาร์เรย์แบบคงที่ขนาดใหญ่ของรายการ 10k, 1m, 10m, 100m, 1G หรือ 10G สำหรับคอมไพเลอร์จำนวนมากขนาดไบนารีจะเพิ่มขึ้นเรื่อย ๆ ตามขนาดของอาร์เรย์และเมื่อผ่านจุดหนึ่งไป

ลงทะเบียนหน่วยความจำ

คลาสหน่วยความจำสุดท้ายคือตัวแปร 'register' ตามที่คาดไว้ตัวแปร register ควรได้รับการจัดสรรบนรีจิสเตอร์ของ CPU แต่การตัดสินใจนั้นถูกปล่อยให้คอมไพเลอร์ คุณไม่สามารถเปลี่ยนตัวแปร register ให้เป็นการอ้างอิงโดยใช้ address-of

register int meaning = 42;
printf("%p\n",&meaning); /* this is wrong and will fail at compile time. */

คอมไพเลอร์สมัยใหม่ส่วนใหญ่ฉลาดกว่าคุณในการเลือกตัวแปรที่ควรใส่ในรีจิสเตอร์ :)

อ้างอิง:


3
หมายเหตุ: ฉันขอแนะนำint * a = malloc(sizeof(*a));แทนเพื่อหลีกเลี่ยงการทำซ้ำประเภทของaไฟล์. สิ่งนี้ทำให้ง่ายขึ้นมากหากมีการaเปลี่ยนแปลงประเภท
Shahbaz

1
เรียกโดยย่อว่าฮีป แต่ไม่มีส่วนเกี่ยวข้องกับโครงสร้างข้อมูลฮีป กองในกรณีนี้หมายถึงสถานที่ที่ยุ่งเหยิง
ไดนามิก

2
"การจัดสรรหน่วยความจำแบบคงที่ ... ถูกจัดสรรในเวลาคอมไพล์" คุณหมายถึงขนาดการจัดสรรที่กำหนดในเวลาคอมไพล์หรือไม่ การตั้งค่านอกเหนือจากหน่วยความจำจะไม่เกิดขึ้นที่รันไทม์เท่านั้นหรือ?
lf215

สวัสดีฉันมีข้อสงสัยหากคุณยังคงตอบสนอง :( แล้วการจัดสรรหน่วยความจำอัตโนมัติล่ะคอมไพลเลอร์จะจัดเก็บที่อยู่ในส่วนข้อมูลสำหรับตัวแปรในระบบเหล่านี้และส่งต่อไปยังไฟล์ปฏิบัติการหรือไม่และเมื่อโค้ดดำเนินการ (และเข้าสู่ขอบเขต ) ที่อยู่เหล่านี้จะถูกใช้เป็นตำแหน่งของหน่วยความจำที่จัดสรรจริง ๆ หรือจริง ๆ แล้วจะได้รับการจัดสรรในเวลาทำงานเท่านั้นโดยไม่มีการสร้างและจัดการที่อยู่โดยคอมไพเลอร์ของฉัน
LocalHost

1
@LocalHost ตัวแปรอัตโนมัติถูกกำหนดขอบเขตตามอายุการใช้งานของบริบท (วงเล็บปีกกา) ที่กำหนดไว้ ซึ่งมักจะถูกจัดสรรบน call stack ที่รันไทม์ มันไม่ได้ถูกเก็บไว้ในส่วนข้อมูลอย่างแน่นอน คุณสามารถอ่านมาตรฐาน C18 ได้ที่นี่: (6.2.4.5-7) web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/…
brice

3

การจัดสรรหน่วยความจำแบบคงที่:

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

การจัดสรรหน่วยความจำแบบไดนามิก:

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

1
"การจัดสรรหน่วยความจำแบบคงที่ [... ] ใช้โครงสร้างข้อมูลที่เรียกว่าสแต็กสำหรับการใช้การจัดสรรแบบคงที่" ไม่ถูกต้องและทำให้เข้าใจผิด โปรดดูโพสต์ของฉันสำหรับความแตกต่างระหว่างการจัดสรรอัตโนมัติและแบบคงที่ หน่วยความจำแบบคงที่อาจใช้สแตก สิ่งนี้ขึ้นอยู่กับการนำไปใช้อย่างมากและอาจใช้หลายกลยุทธ์สำหรับการนำไปใช้งานเดียวกัน ฉันไม่แน่ใจว่าคุณหมายถึงอะไร "ประสิทธิภาพน้อย" เช่นกัน @Trieu Toan คุณเปลี่ยนความหมายของคำตอบนี้ด้วยการแก้ไขที่ไม่ดี
brice

2

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

การจัดสรรหน่วยความจำแบบไดนามิก:ใช้ฟังก์ชันเช่น malloc () หรือ calloc () เพื่อรับหน่วยความจำแบบไดนามิกหากใช้ฟังก์ชันเหล่านี้เพื่อรับหน่วยความจำแบบไดนามิกและค่าที่ส่งคืนโดยฟังก์ชันเหล่านี้จะถูกกำหนดให้กับตัวแปรตัวชี้การกำหนดดังกล่าวเรียกว่าหน่วยความจำแบบไดนามิก การจัดสรรหน่วยความจำถูกกำหนดระหว่างเวลาทำงาน


1

ความแตกต่างระหว่างการจัดสรรหน่วยความจำแบบคงที่และการจัดสรรหน่วยความจำแบบไดนามิก

หน่วยความจำถูกจัดสรรก่อนที่โปรแกรมจะเริ่มทำงาน (ระหว่างการคอมไพล์)
มีการจัดสรรหน่วยความจำระหว่างการทำงานของโปรแกรม

ไม่มีการดำเนินการจัดสรรหน่วยความจำหรือการยกเลิกการจัดสรรระหว่างการดำเนินการ
การผูกหน่วยความจำถูกสร้างขึ้นและถูกทำลายในระหว่างการดำเนินการ

ตัวแปรยังคงจัดสรรอย่างถาวร
จัดสรรเมื่อหน่วยโปรแกรมทำงานอยู่เท่านั้น

ดำเนินการโดยใช้สแต็กและฮีป
ดำเนินการโดยใช้กลุ่มข้อมูล

จำเป็นต้องใช้ตัวชี้ในการเข้าถึงตัวแปร
ไม่จำเป็นต้องมีการจัดสรรพอยน์เตอร์แบบไดนามิก

ดำเนินการเร็วกว่าไดนามิก
การดำเนินการช้ากว่าแบบคงที่

ต้องการพื้นที่หน่วยความจำเพิ่มเติม
ต้องการพื้นที่หน่วยความจำน้อยลง


1
การจัดสรรหน่วยความจำแบบคงที่ถูกจัดสรรบน Stack ในขณะที่การจัดสรรหน่วยความจำแบบไดนามิกถูกจัดสรรบน Heap
Usman Kurd

@UsmanKurd โดยทั่วไปไม่ถูกต้องเกี่ยวกับหน่วยความจำแบบคงที่ ดูคำตอบของฉัน
brice

0

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


-1

การจัดสรรหน่วยความจำแบบคงที่ หน่วยความจำที่จัดสรรจะอยู่ในสแต็ก

int a[10];

การจัดสรรหน่วยความจำแบบไดนามิก หน่วยความจำที่จัดสรรจะอยู่ในฮีป

int *a = malloc(sizeof(int) * 10);

และหลังควรเป็นd ฟรีเนื่องจากไม่มี Garbage Collector (GC) ใน C

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