ความแตกต่างระหว่าง malloc และ calloc?


780

ความแตกต่างระหว่างการทำคืออะไร:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));

หรือ:

ptr = (char **) calloc (MAXELEMS, sizeof(char*));

เมื่อใดที่ควรใช้ calloc ผ่าน malloc หรือในทางกลับกัน



8
ใน C คุณสามารถเขียนข้อความด้านบนโดยทั่วไปได้ดังนี้:ptr = calloc(MAXELEMS, sizeof(*ptr));
chqrlie

7
โพสต์ที่น่าสนใจเกี่ยวกับความแตกต่างระหว่าง calloc และ malloc + memset vorpus.org/blog/why-does-calloc-exist
ddddavidee

2
@ddddavidee ฉันก็พบว่าบล็อกหลังจากฉันไม่พอใจกับคำตอบมากมายในเน็ต นาธาเนียลเจ. สมิ ธ สมควรได้รับ 100+ คะแนน SO สำหรับการวิเคราะห์ของเขา
อายุการใช้งาน

คำตอบ:


851

calloc()ให้บัฟเฟอร์แบบ zero-initialized ขณะที่malloc()ปล่อยให้หน่วยความจำไม่เริ่มต้น

สำหรับการจัดสรรขนาดใหญ่callocการใช้งานส่วนใหญ่ภายใต้ระบบปฏิบัติการหลักจะได้รับหน้าที่เป็นศูนย์จากระบบปฏิบัติการ (เช่นผ่าน POSIX mmap(MAP_ANONYMOUS)หรือ Windows VirtualAlloc) ดังนั้นจึงไม่จำเป็นต้องเขียนในพื้นที่ผู้ใช้ นี่คือวิธีที่ปกติmallocได้รับหน้าเพิ่มเติมจากระบบปฏิบัติการเช่นกัน; callocเพียงแค่ใช้ประโยชน์จากการรับประกันของระบบปฏิบัติการ

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

คอมไพเลอร์บางคนยังสามารถเพิ่มประสิทธิภาพ + memset malloc (0) เข้า calloc สำหรับคุณ แต่คุณควรใช้ calloc 0อย่างชัดเจนถ้าคุณต้องการหน่วยความจำในการอ่านเป็น

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


การใช้งานในตัวของcallocอาจทำให้callocหน่วยความจำศูนย์ถึงตัวเองถ้าไม่มีระบบปฏิบัติการหรือมันไม่ใช่ระบบปฏิบัติการที่ผู้ใช้หลายคนที่ศูนย์หน้าเพื่อหยุดการรั่วไหลของข้อมูลระหว่างกระบวนการ

บน Linux ที่ฝังตัว malloc สามารถทำได้mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)ซึ่งเปิดใช้งานสำหรับเมล็ดในตัวบางตัวเท่านั้นเนื่องจากไม่ปลอดภัยในระบบที่มีผู้ใช้หลายคน


224
ชุดการจัดสรร * เป็นตัวช่วยจำที่ค่อนข้างชัดเจน - clear-alloc, memory-alloc, การจัดสรรใหม่
Cascabel

43
ใช้ malloc () ถ้าคุณจะตั้งทุกอย่างที่คุณใช้ในพื้นที่ที่จัดสรร ใช้ calloc () ถ้าคุณจะปล่อยให้ส่วนต่าง ๆ ของข้อมูลไม่ได้กำหนดค่าเริ่มต้น - และมันจะเป็นประโยชน์หากมีส่วนที่ไม่มีการตั้งค่าเป็นศูนย์
Jonathan Leffler

268
callocไม่จำเป็นต้องมีราคาแพงมากนักเนื่องจากระบบปฏิบัติการสามารถใช้ลูกเล่นบางอย่างเพื่อเร่งความเร็วได้ ฉันรู้ว่า FreeBSD เมื่อมันได้รับเวลา CPU ที่ไม่ได้ใช้ให้ใช้เพื่อเรียกใช้กระบวนการง่ายๆที่เพิ่งไปรอบ ๆ และ zeroes บล็อกหน่วยความจำที่จัดสรรคืนและทำเครื่องหมายบล็อกซึ่งประมวลผลด้วยแฟล็ก ดังนั้นเมื่อคุณทำเช่นcallocนั้นก่อนอื่นคุณจะพยายามหาบล็อกที่มีค่าศูนย์ก่อนหน้านี้หนึ่งบล็อกและให้มันแก่คุณ - และเป็นไปได้มากว่ามันจะหาได้
Pavel Minaev

28
ฉันมักจะรู้สึกว่าถ้ารหัสของคุณกลายเป็น "ปลอดภัย" อันเป็นผลมาจากการจัดสรรแบบ zero-initing โดยค่าเริ่มต้นรหัสของคุณไม่ปลอดภัยพอไม่ว่าคุณจะใช้ malloc หรือ calloc การใช้ malloc เป็นตัวบ่งชี้ที่ดีว่าข้อมูลต้องการการเริ่มต้น - ฉันใช้ calloc เฉพาะในกรณีที่ 0 ไบต์นั้นมีความหมายจริง นอกจากนี้โปรดทราบว่า calloc ไม่จำเป็นต้องทำในสิ่งที่คุณคิดว่าเป็นประเภทที่ไม่ใช่อักขระ ไม่มีใครใช้การเป็นตัวแทนกับดักจริงๆหรือมากกว่านั้นไม่ใช่ IEEE ลอย แต่นั่นก็ไม่มีข้อแก้ตัวใด ๆ ที่คิดว่าโค้ดของคุณจะพกพาได้อย่างแท้จริงเมื่อไม่มี
Steve Jessop

18
@SteveJessop "ปลอดภัย" ไม่ใช่คำที่ถูกต้อง ฉันคิดว่า "กำหนด" เป็นคำที่ดีกว่า รหัสที่กำหนดได้มากกว่าการล้มเหลวที่ขึ้นอยู่กับเวลาและลำดับข้อมูลจะง่ายกว่าในการแยกความล้มเหลว บางครั้ง Calloc ก็เป็นวิธีที่ง่ายที่สุดในการได้รับระดับนั้นกับการเริ่มต้นอย่างชัดเจน
เดนนิส

362

ความแตกต่างที่รู้จักกันน้อยคือในระบบปฏิบัติการที่มีการจัดสรรหน่วยความจำในแง่ดีเช่น Linux ตัวชี้ที่ส่งคืนโดยmallocไม่ได้รับการสนับสนุนจากหน่วยความจำจริงจนกว่าโปรแกรมจะสัมผัสจริง

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

ดูตัวอย่างคำถาม SO นี้สำหรับการสนทนาเพิ่มเติมเกี่ยวกับพฤติกรรมของ malloc


49
callocไม่จำเป็นต้องเขียนเลขศูนย์ หากบล็อกที่จัดสรรนั้นส่วนใหญ่เป็นศูนย์หน้าใหม่ที่จัดทำโดยระบบปฏิบัติการบล็อกนั้นสามารถปล่อยให้ไม่มีใครแตะต้องได้ นี้แน่นอนต้องที่จะปรับระบบปฏิบัติการมากกว่าฟังก์ชั่นห้องสมุดทั่วไปด้านบนของcalloc mallocหรือผู้ดำเนินการสามารถcallocเปรียบเทียบแต่ละคำกับศูนย์ก่อน zeroing สิ่งนี้จะไม่ประหยัดเวลา แต่จะหลีกเลี่ยงการทำให้หน้าใหม่สกปรก
. GitHub หยุดช่วยน้ำแข็ง

3
@R .. บันทึกที่น่าสนใจ แต่ในทางปฏิบัติมีการใช้งานเช่นนี้ในป่าหรือไม่?
Isak Savo

10
dlmallocการใช้งานที่มีลักษณะคล้ายกันทั้งหมดข้ามขั้นตอนนี้memsetหากได้รับชิ้นผ่านmmapหน้าใหม่ที่ไม่ระบุชื่อ (หรือเทียบเท่า) โดยปกติแล้วการจัดสรรแบบนี้จะใช้สำหรับชิ้นที่ใหญ่กว่าเริ่มต้นที่ 256k หรือมากกว่านั้น ฉันไม่รู้การใช้งานที่เปรียบเทียบกับศูนย์ก่อนที่จะเขียนศูนย์นอกเหนือจากของฉันเอง
. GitHub หยุดช่วยน้ำแข็ง

1
omallocยังข้ามmemset; callocไม่จำเป็นต้องแตะหน้าใด ๆ ที่ไม่ได้ใช้งานโดยแอปพลิเคชัน (แคชหน้า) ตลอดไป แม้ว่าการใช้งานดั้งเดิมมากcallocแตกต่างกัน
mirabilos

10
calloc ของ glibc ตรวจสอบว่าได้รับหน่วยความจำใหม่จากระบบปฏิบัติการหรือไม่ ถ้าเป็นเช่นนั้นก็รู้ว่ามันไม่จำเป็นต้องเขียนเพราะ mmap (... , MAP_ANONYMOUS) จะส่งคืนหน่วยความจำที่เป็นศูนย์อยู่แล้ว
Peter Cordes

112

ข้อดีอย่างหนึ่งที่มักถูกมองข้ามcallocก็คือ (การใช้งานตามมาตรฐาน) ซึ่งจะช่วยปกป้องคุณจากช่องโหว่จำนวนเต็มล้น เปรียบเทียบ:

size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);

เมื่อเทียบกับ

size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);

อดีตอาจส่งผลให้การจัดสรรขนาดเล็กและหน่วยความจำล้นที่ตามมาหากมีค่ามากกว่าcount SIZE_MAX/sizeof *barหลังจะล้มเหลวโดยอัตโนมัติในกรณีนี้เนื่องจากวัตถุที่ไม่สามารถสร้างขนาดใหญ่

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


17
เห็นได้ชัดว่าการล้นทางคณิตศาสตร์เป็นสิ่งที่ทำให้เกิดช่องโหว่ของ OpenSSH ในปี 2545 บทความที่ดีจาก OpenBSD เกี่ยวกับภัยของเรื่องนี้ด้วยฟังก์ชั่นที่เกี่ยวข้องกับหน่วยความจำ: undeadly.org/cgi?action=article&sid=20060330071917
Philip P.

4
@KomradeP: น่าสนใจ น่าเศร้าบทความที่คุณเชื่อมโยงมีข้อมูลที่ผิดที่จุดเริ่มต้น ตัวอย่างที่มีcharคือไม่ล้น แต่แปลงการดำเนินงานที่กำหนดไว้เมื่อกำหนดกลับผลเป็นcharวัตถุ
. GitHub หยุดช่วยน้ำแข็ง

มันอาจจะมีเพื่อประกอบการอธิบายเท่านั้น เพราะคอมไพเลอร์นั้นมีแนวโน้มที่จะปรับตัวให้เหมาะสมอยู่ดี ฉันรวบรวมใน asm นี้: กด 1
Philip P.

1
@Tristopia: ประเด็นก็คือไม่ใช่ว่ารหัสนั้นเป็นประโยชน์ในการใช้งานทั้งหมด แต่มันไม่ถูกต้องโดยไม่มีข้อสันนิษฐานเพิ่มเติมและจึงไม่ถูกต้อง / การใช้งานแบบพกพา
.. GitHub หยุดช่วยน้ำแข็ง

3
@Tristopia: หากโหมดการคิดของคุณคือ " size_tเป็น 64- บิตดังนั้นจึงไม่มีปัญหา" นั่นเป็นวิธีคิดที่สมบูรณ์แบบที่จะนำไปสู่ข้อบกพร่องด้านความปลอดภัย size_tเป็นประเภทนามธรรมที่แสดงถึงขนาดและมีเหตุผลที่จะคิดว่าสินค้าโดยพลการของจำนวน 32 บิตและไม่มีsize_t(หมายเหตุ: sizeof *barสามารถในหลักการจะสูงกว่า 2 ^ 32 ในการดำเนินงาน C 64 บิต) size_tพอดีใน
. GitHub หยุดช่วยน้ำแข็ง

37

เอกสารทำให้ calloc มีลักษณะเหมือน malloc ซึ่งเพิ่งเริ่มต้นใช้หน่วยความจำไม่เป็นศูนย์ นี่ไม่ใช่ความแตกต่างหลัก! แนวคิดของ calloc คือการยกเลิกความหมายของ copy-on-write สำหรับการจัดสรรหน่วยความจำ เมื่อคุณจัดสรรหน่วยความจำด้วย calloc มันแผนที่ทั้งหมดไปยังหน้าทางกายภาพเดียวกันซึ่งเริ่มต้นเป็นศูนย์ เมื่อเพจใด ๆ ของหน่วยความจำที่จัดสรรถูกเขียนลงในฟิสิคัลเพจจะถูกจัดสรร มักใช้เพื่อสร้างตารางแฮชขนาดใหญ่ตัวอย่างเช่นเนื่องจากส่วนของแฮชที่ว่างเปล่าไม่ได้รับการสนับสนุนจากหน่วยความจำเพิ่มเติม (หน้า) พวกเขาชี้ไปที่หน้าเริ่มต้นเป็นศูนย์เดียวอย่างมีความสุขซึ่งสามารถแบ่งปันได้ระหว่างกระบวนการ

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

นี่คือเรื่องราวการเพิ่มประสิทธิภาพหนึ่งเรื่องเกี่ยวกับหัวข้อ: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/


26

ขนาดของบล็อกหน่วยความจำที่จัดสรรไม่แตกต่างกัน callocเพียงเติมบล็อกหน่วยความจำด้วยรูปแบบศูนย์ทั้งหมดบิตแบบฟิสิคัล ในทางปฏิบัติมันมักจะสันนิษฐานว่าวัตถุที่อยู่ในบล็อกหน่วยความจำที่จัดสรรด้วยcallocมีค่าเริ่มต้นราวกับว่าพวกเขาเริ่มต้นด้วยตัวอักษร0เช่นจำนวนเต็มควรมีค่า0ตัวแปรตัวแปรจุดลอยตัว - มูลค่าของ0.0ตัวชี้ - ค่าตัวชี้โมฆะที่เหมาะสม และอื่น ๆ

จากจุดอวดความรู้ในมุมมองของ แต่calloc(เช่นเดียวกับmemset(..., 0, ...)) จะรับประกันเฉพาะที่จะต้องเริ่มต้น (มีเลขศูนย์) unsigned charวัตถุชนิด ทุกอย่างอื่นไม่รับประกันว่าจะเริ่มต้นอย่างถูกต้องและอาจมีการเรียกว่าการเป็นตัวแทนกับดักซึ่งทำให้พฤติกรรมที่ไม่ได้กำหนด กล่าวอีกนัยหนึ่งสำหรับประเภทอื่นใดนอกเหนือunsigned charจาก patterm all-zero-bits ดังกล่าวข้างต้นอาจเป็นตัวแทนของค่าที่ผิดกฎหมายการเป็นตัวแทนกับดัก

ต่อมาในหนึ่งใน Corrigenda ทางเทคนิคถึงมาตรฐาน C99 พฤติกรรมถูกกำหนดไว้สำหรับจำนวนเต็มทุกประเภท (ซึ่งสมเหตุสมผล) นั่นคืออย่างเป็นทางการในภาษา C ปัจจุบันคุณสามารถเริ่มต้นประเภทจำนวนเต็มด้วยcalloc(และmemset(..., 0, ...)) การใช้มันเพื่อเริ่มต้นสิ่งอื่นในกรณีทั่วไปนำไปสู่พฤติกรรมที่ไม่ได้กำหนดจากมุมมองของภาษา C

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

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


ตามย่อหน้า "รายละเอียดที่สำคัญอื่น": ดูเหมือนว่าจะทำให้memset(p, v, n * sizeof type);เกิดปัญหาเพราะn * sizeof typeอาจล้น เดาว่าฉันจะต้องใช้การfor(i=0;i<n;i++) p[i]=v;วนซ้ำสำหรับโค้ดที่มีประสิทธิภาพ
chux - Reinstate Monica

มันจะมีประโยชน์หากมีวิธีการมาตรฐานที่รหัสสามารถยืนยันว่าการดำเนินการจะต้องใช้ all-bits-zero เป็นตัวชี้โมฆะ (ปฏิเสธการรวบรวมเป็นอย่างอื่น) เนื่องจากมีการใช้งานที่ใช้การเป็นตัวแทนตัวชี้โมฆะอื่น ๆ ค่อนข้างหายาก; รหัสที่ไม่ต้องใช้กับการใช้งานดังกล่าวอาจเร็วขึ้นหากสามารถใช้ calloc () หรือ memset เพื่อเริ่มต้นอาร์เรย์ของพอยน์เตอร์
supercat

@chux ไม่มีถ้าอาร์เรย์มีnองค์ประกอบอยู่ที่องค์ประกอบที่มีขนาดsizeof typeแล้วn*sizeof typeไม่สามารถล้นเพราะขนาดสูงสุดของวัตถุใด ๆ SIZE_MAXที่จะต้องน้อยกว่า
12431234123412341234123

@ 12431234123412341234123 จริงเกี่ยวกับขนาดอาร์เรย์ <= SIZE_MAXแต่ยังไม่มีอาร์เรย์ที่นี่ ตัวชี้ที่ส่งคืนจากcalloc()สามารถชี้ไปยังหน่วยความจำที่จัดสรรเกินSIZE_MAXได้ การนำไปใช้งานหลายอย่าง จำกัด ผลิตภัณฑ์ของ 2 args ไปcalloc()ที่SIZE_MAXแต่ C spec ไม่ได้กำหนดขีด จำกัด นั้น
chux - Reinstate Monica

21

จากบทความการเปรียบเทียบสนุกกับ calloc () และศูนย์หน้าในบล็อกของ Georg Hager

เมื่อจัดสรรหน่วยความจำโดยใช้ calloc () จำนวนหน่วยความจำที่ร้องขอจะไม่ถูกจัดสรรทันที หน้าทั้งหมดที่เป็นของบล็อกหน่วยความจำจะเชื่อมต่อกับหน้าเดียวที่มีเลขศูนย์ทั้งหมดด้วยเวทมนตร์ MMU (ลิงก์ด้านล่าง) หากหน้าดังกล่าวเป็นเพียงการอ่าน (ซึ่งเป็นจริงสำหรับอาร์เรย์ b, c และ d ในเวอร์ชันมาตรฐานเดิม) ข้อมูลจะถูกจัดเตรียมจากหน้าศูนย์เดียวซึ่งแน่นอนว่าเหมาะกับแคช มากสำหรับเคอร์เนลวนหน่วยความจำที่ถูกผูกไว้ หากหน้าถูกเขียนไปยัง (ไม่ว่าจะเกิดอะไรขึ้น) จะเกิดความผิดพลาดหน้า“ ของจริง” จะถูกแมปและหน้าศูนย์จะถูกคัดลอกไปยังหน่วยความจำ สิ่งนี้เรียกว่า copy-on-write ซึ่งเป็นวิธีการเพิ่มประสิทธิภาพที่เป็นที่รู้จักกันดี (ซึ่งฉันได้สอนหลายครั้งในการบรรยาย C ++) หลังจากนั้น,


ลิงค์อยู่ที่ไหน
Rupesh Yadav

2
บรรทัดแรกของคำตอบมีลิงค์ไปยังบล็อกของ Georg Hager
Ashish Chavan

11

calloc โดยทั่วไป malloc+memset 0

โดยทั่วไปจะดีกว่าเล็กน้อยที่จะใช้malloc+memsetอย่างชัดเจนโดยเฉพาะเมื่อคุณทำสิ่งที่ชอบ:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

นั่นเป็นสิ่งที่ดีกว่าเพราะsizeof(Item)รู้ว่าคอมไพเลอร์ ณ เวลารวบรวมและคอมไพเลอร์ส่วนใหญ่จะแทนที่ด้วยคำแนะนำที่ดีที่สุดเท่าที่จะเป็นไปได้สำหรับหน่วยความจำศูนย์ ในทางกลับกันหากmemsetเกิดขึ้นcallocขนาดพารามิเตอร์ของการจัดสรรจะไม่ถูกรวบรวมในcallocรหัสและmemsetมักจะเรียกจริงซึ่งมักจะมีรหัสที่จะทำไบต์ - by - byte เติมจนขอบเขตยาวกว่าวงจรที่จะเติม เพิ่มหน่วยความจำเป็นsizeof(long)ชิ้นและในที่สุดไบต์ต่อไบต์จะเต็มพื้นที่ที่เหลือ แม้ว่าตัวจัดสรรจะฉลาดพอที่จะโทรมาบ้างaligned_memsetมันจะยังคงเป็นลูปทั่วไป

หนึ่งข้อยกเว้นที่น่าสังเกตคือเมื่อคุณทำ malloc / calloc ของหน่วยความจำขนาดใหญ่มาก (บาง power_of_two กิโลไบต์) ซึ่งการจัดสรรกรณีอาจทำได้โดยตรงจากเคอร์เนล ในขณะที่เมล็ดในระบบปฏิบัติการโดยทั่วไปจะเป็นศูนย์ออกหน่วยความจำทั้งหมดที่พวกเขาให้ไปด้วยเหตุผลด้านความปลอดภัย calloc ฉลาดพอที่อาจเพียงแค่ส่งกลับมาพร้อมกับ zeroing เพิ่มเติม อีกครั้ง - หากคุณเพิ่งจัดสรรสิ่งที่คุณรู้ว่ามีขนาดเล็กคุณอาจจะดีกว่าด้วยประสิทธิภาพของ malloc + memset


+1 สำหรับการเตือนว่าการใช้งานทั่วไปของฟังก์ชันในไลบรารีระบบไม่จำเป็นต้องเร็วกว่าการดำเนินการเดียวกันในรหัสผู้ใช้
Patrick Schlüter

1
นอกจากนี้ยังมีจุดที่สองที่ทำให้calloc()ช้ากว่าmalloc(): การคูณสำหรับขนาด calloc()จำเป็นต้องใช้การคูณทั่วไป (ถ้าsize_tเป็น 64 บิตแม้การดำเนินการ 64 บิตที่มีราคาแพงมาก * 64 บิต = 64 บิต) ในขณะที่ malloc () มักจะมีค่าคงที่เวลารวบรวม
Patrick Schlüter

4
glibc calloc มีสมาร์ทโฟนจำนวนหนึ่งในการตัดสินใจว่าจะล้างชิ้นที่ส่งคืนได้อย่างมีประสิทธิภาพมากที่สุดเช่นบางครั้งต้องการเพียงการล้างและยังสามารถล้างได้ถึง 9 * ขนาด (size_t) หน่วยความจำหน่วยความจำล้าง 3 struct foo { char a,b,c; };ไบต์ในช่วงเวลาที่ไม่ได้ไปจะได้เร็วขึ้นเพียงเพราะคุณกำลังแล้วจะใช้การระงับ callocดีกว่าmalloc+ เสมอmemsetหากคุณจะล้างพื้นที่ทั้งหมดตลอดmallocเวลา callocมีการตรวจสอบอย่างระมัดระวัง แต่มีประสิทธิภาพสำหรับองค์ประกอบ int * ขนาดที่มากเกินไป
Peter Cordes

8

ความแตกต่าง 1:

malloc() มักจะจัดสรรบล็อกหน่วยความจำและมันจะเริ่มต้นส่วนหน่วยความจำ

calloc() จัดสรรบล็อกหน่วยความจำและเริ่มต้นบล็อกหน่วยความจำทั้งหมดเป็น 0

ความแตกต่าง 2:

หากคุณพิจารณาmalloc()ไวยากรณ์มันจะใช้เวลาเพียง 1 อาร์กิวเมนต์ ลองพิจารณาตัวอย่างต่อไปนี้ด้านล่าง:

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );

เช่นหากคุณต้องการจัดสรรหน่วยความจำ 10 บล็อกสำหรับประเภท int

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

หากคุณพิจารณาcalloc()ไวยากรณ์มันจะใช้เวลา 2 ข้อโต้แย้ง ลองพิจารณาตัวอย่างต่อไปนี้ด้านล่าง:

data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));

ตัวอย่าง: หากคุณต้องการจัดสรรหน่วยความจำ 10 บล็อกสำหรับประเภท int และกำหนดค่าเริ่มต้นทั้งหมดเป็นศูนย์

int *ptr = (int *) calloc(10, (sizeof(int)));

ความคล้ายคลึงกัน:

ทั้งสองmalloc()และcalloc()จะคืนค่าเป็นโมฆะ * โดยค่าเริ่มต้นหากไม่ใช่ประเภท casted!


และทำไมคุณถึงเก็บ data_type และ cast_type ต่างกัน
จำหน่ายหมด

7

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

  • calloc()จัดสรรพื้นที่หน่วยความจำความยาวจะเป็นผลคูณของพารามิเตอร์ callocเติมหน่วยความจำด้วย ZERO's และส่งคืนพอยน์เตอร์ไปที่ไบต์แรก ถ้ามันล้มเหลวในการค้นหาพื้นที่เพียงพอมันจะส่งกลับNULLตัวชี้

ไวยากรณ์: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); เช่นptr_var=(type *)calloc(n,s);

  • malloc()จัดสรรบล็อกหน่วยความจำเดียวขนาด REQUSTED SIZE และส่งกลับพอยน์เตอร์ไปยังไบต์แรก หากไม่สามารถระบุจำนวนหน่วยความจำที่ต้องการได้จะส่งคืนพอยน์เตอร์พอยน์เตอร์

ไวยากรณ์: ฟังก์ชั่นใช้เวลาหนึ่งอาร์กิวเมนต์ซึ่งเป็นจำนวนไบต์ที่จะจัดสรรในขณะที่ptr_var=(cast_type *)malloc(Size_in_bytes);malloc()calloc()ฟังก์ชั่นใช้เวลาสองมีปากเสียงหนึ่งเป็นจำนวนขององค์ประกอบและอื่น ๆ ที่เป็นจำนวนไบต์ที่จะจัดสรรสำหรับแต่ละองค์ประกอบเหล่านั้น นอกจากนี้calloc()เริ่มต้นพื้นที่ที่จัดสรรให้เป็นศูนย์ในขณะที่malloc()ไม่ได้


6

calloc()ฟังก์ชั่นที่มีการประกาศใน<stdlib.h>ส่วนหัวมีคู่ของประโยชน์มากกว่าmalloc()ฟังก์ชั่น

  1. มันจัดสรรหน่วยความจำเป็นจำนวนองค์ประกอบขนาดที่กำหนดและ
  2. มันเริ่มต้นหน่วยความจำที่จัดสรรเพื่อให้บิตทั้งหมดเป็นศูนย์

6

malloc()และcalloc()เป็นฟังก์ชั่นจากไลบรารีมาตรฐาน C ที่อนุญาตการจัดสรรหน่วยความจำแบบไดนามิกซึ่งหมายความว่าทั้งคู่อนุญาตการจัดสรรหน่วยความจำระหว่างรันไทม์

ต้นแบบของพวกเขามีดังนี้:

void *malloc( size_t n);
void *calloc( size_t n, size_t t)

มีความแตกต่างสองหลักระหว่างสอง:

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

  • ไวยากรณ์: malloc()รับ 1 อาร์กิวเมนต์ (ขนาดที่จะจัดสรร) และcalloc()รับสองอาร์กิวเมนต์ (จำนวนบล็อกที่จะจัดสรรและขนาดของแต่ละบล็อก)

ค่าส่งคืนจากทั้งคู่เป็นตัวชี้ไปยังบล็อกของหน่วยความจำที่จัดสรรไว้หากประสบความสำเร็จ มิฉะนั้นจะคืนค่า NULLเพื่อระบุว่าการจัดสรรหน่วยความจำล้มเหลว

ตัวอย่าง:

int *arr;

// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int)); 

// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));

ฟังก์ชั่นเดียวกับที่calloc()สามารถทำได้โดยใช้malloc()และmemset():

// allocate memory for 10 integers with garbage values   
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int)); 

โปรดทราบว่าmalloc()มีการใช้งานมากกว่าcalloc()เนื่องจากเร็วกว่า หากต้องการให้ค่าเริ่มต้นเป็นศูนย์ให้ใช้calloc()แทน


5

ข้อแตกต่างที่ยังไม่ได้กล่าวถึง: จำกัด ขนาด

void *malloc(size_t size)SIZE_MAXเท่านั้นที่สามารถจัดสรรได้ถึง

void *calloc(size_t nmemb, size_t size);SIZE_MAX*SIZE_MAXสามารถจัดสรรขึ้นประมาณ

ความสามารถนี้ไม่ได้ใช้บ่อยในหลาย ๆ แพลตฟอร์มที่มีการกำหนดแอดเดรสเชิงเส้น ระบบดังกล่าว จำกัดด้วยcalloc()nmemb * size <= SIZE_MAX

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

size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);

พิจารณาสิ่งต่อไปนี้ซึ่งทำให้สามารถจัดสรรได้มากขึ้น

size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);

ตอนนี้ถ้าระบบดังกล่าวสามารถจัดหาการจัดสรรขนาดใหญ่เป็นเรื่องอื่น ส่วนใหญ่ในวันนี้จะไม่ ถึงกระนั้นมันก็เกิดขึ้นมาหลายปีแล้วเมื่อSIZE_MAXมีจำนวน 65535 กฎหมายของมัวร์สงสัยว่าสิ่งนี้จะเกิดขึ้นประมาณปี 2030 ด้วยรุ่นหน่วยความจำบางรุ่นSIZE_MAX == 4294967295และพูลหน่วยความจำใน 100 GBytes


2
โดยทั่วไป size_t จะสามารถถือขนาดของวัตถุชนิดใหญ่ที่สุดที่โปรแกรมสามารถจัดการได้ ระบบที่ size_t คือ 32 บิตไม่น่าจะสามารถจัดการการจัดสรรที่มีขนาดใหญ่กว่า 4294967295 ไบต์และระบบที่สามารถจัดการการจัดสรรที่ขนาดนั้นจะทำให้size_tมีขนาดใหญ่กว่า 32 บิต คำถามเดียวก็คือว่าการใช้callocกับค่าที่มีผลิตภัณฑ์เกินกว่าSIZE_MAXสามารถพึ่งพาเพื่อให้ผลตอบแทนเป็นศูนย์แทนที่จะส่งกลับตัวชี้ไปยังการจัดสรรที่เล็กลง
supercat

เห็นด้วยเกี่ยวกับลักษณะทั่วไปแต่สเปค C ช่วยให้การจัดสรรเกินcalloc() SIZE_MAXมันได้เกิดขึ้นในอดีตที่ผ่านมามี 16 บิตsize_tและหน่วยความจำยังคงลดราคาผมเห็นไม่มีเหตุผลมันจะไม่สามารถเกิดขึ้นได้ก้าวไปข้างหน้าถึงแม้ว่ามันจะไม่ได้พบบ่อย
chux - Reinstate Monica

1
มาตรฐาน C ทำให้มันเป็นไปได้สำหรับโค้ดเพื่อขอSIZE_MAXจัดสรรขนาดที่มีเกิน แน่นอนว่าไม่จำเป็นต้องมีสถานการณ์ใด ๆ ที่การจัดสรรดังกล่าวอาจประสบความสำเร็จ ฉันไม่แน่ใจว่ามีประโยชน์อย่างใดอย่างหนึ่งจากการบังคับใช้ว่าการใช้งานที่ไม่สามารถจัดการการจัดสรรดังกล่าวจะต้องส่งคืนNULL(โดยเฉพาะอย่างยิ่งเนื่องจากเป็นเรื่องปกติสำหรับการใช้งานบางอย่างที่มีmallocตัวชี้กลับไปยังพื้นที่ที่ยังไม่ได้ตกลง มัน).
supercat

นอกจากนี้ที่ผ่านมาอาจมีระบบในอดีตซึ่งมีช่วงการกำหนดแอดเดรสที่ใช้ได้เกินจำนวนเต็มที่แทนค่าได้มากที่สุดฉันไม่เห็นความเป็นไปได้ที่เกิดขึ้นจริงอีกครั้งเนื่องจากจะต้องใช้ความจุในการจัดเก็บหลายพันล้านกิกะไบต์ แม้ว่ากฎของมัวร์จะยังคงดำเนินต่อไปจากจุดที่ 32 บิตสิ้นสุดลงจะเพียงพอที่จะถึงจุดที่ 64 บิตหยุดให้เพียงพอจะใช้เวลาสองเท่าตราบเท่าที่ได้รับจากจุดที่ 16 บิตก็เพียงพอที่จะจุดที่ 32 ไม่ได้ 'T
supercat

1
ทำไมการดำเนินการซึ่งสามารถรองรับการจัดสรรเดียวในส่วนที่เกินจาก 4G ได้กำหนดsize_tที่จะuint64_t?
supercat

2

จำนวนบล็อก:
malloc () กำหนดบล็อกเดียวของหน่วยความจำที่ร้องขอ
calloc () กำหนดบล็อกหลายหน่วยความจำที่ร้องขอ

การเริ่มต้น:
malloc () - ไม่ชัดเจนและเริ่มต้นหน่วยความจำที่จัดสรร
calloc () - เริ่มต้นหน่วยความจำที่จัดสรรโดยศูนย์

ความเร็ว:
malloc () เร็ว
calloc () ช้ากว่า malloc ()

อาร์กิวเมนต์และไวยากรณ์:
malloc () รับ 1 อาร์กิวเมนต์:

  1. ไบต์

    • จำนวนไบต์ที่จะจัดสรร

calloc () รับ 2 อาร์กิวเมนต์:

  1. ความยาว

    • จำนวนบล็อกหน่วยความจำที่จะจัดสรร
  2. ไบต์
    • จำนวนไบต์ที่จะจัดสรรที่แต่ละหน่วยความจำ
void *malloc(size_t bytes);         
void *calloc(size_t length, size_t bytes);      

ลักษณะของการจัดสรรหน่วยความจำ:
ฟังก์ชั่น malloc จะกำหนดหน่วยความจำของ 'ขนาด' ที่ต้องการจากฮีปที่มีอยู่
ฟังก์ชัน calloc กำหนดหน่วยความจำที่มีขนาดเท่ากับอะไร 'num * size'

ความหมายเกี่ยวกับชื่อ:
ชื่อ malloc หมายถึง "การจัดสรรหน่วยความจำ"
ชื่อ calloc หมายถึง "การจัดสรรที่ต่อเนื่องกัน"

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