C ++ ใหม่ int [0] - มันจะจัดสรรหน่วยความจำหรือไม่


241

แอปทดสอบอย่างง่าย:

cout << new int[0] << endl;

เอาท์พุท:

0x876c0b8

ดังนั้นจึงดูเหมือนว่ามันใช้งานได้ มาตรฐานพูดถึงอะไรเกี่ยวกับเรื่องนี้? เป็นเรื่องถูกกฎหมายหรือไม่ที่จะ "จัดสรร" บล็อกหน่วยความจำที่ว่างเปล่าอยู่เสมอ?


4
+1 คำถามที่น่าสนใจมาก - แม้ว่าฉันจะไม่แน่ใจว่ามันสำคัญขนาดไหนในโค้ดจริง
Zifre

37
@ Zifre: ฉันขอความอยากรู้ แต่มันอาจสำคัญในโลกแห่งความเป็นจริงเช่นเมื่อคำนวณขนาดของบล็อกหน่วยความจำที่จัดสรรในบางวิธีและผลของการคำนวณอาจเป็นศูนย์จากนั้นไม่จำเป็นต้องเพิ่มข้อยกเว้นโดยตรง เพื่อไม่จัดสรรบล็อกขนาดศูนย์ .. เนื่องจากควรจัดสรรและลบบล็อกโดยไม่มีข้อผิดพลาด (หากบล็อกที่เป็นศูนย์ขนาดไม่ถูกยกเลิกการพิจารณา) โดยทั่วไปแล้วสิ่งนี้จะให้นามธรรมที่กว้างขึ้นของบล็อกหน่วยความจำ

2
@ emg-2: ในสถานการณ์ตัวอย่างของคุณจริง ๆ แล้วมันจะไม่สำคัญเพราะลบ [] ถูกกฎหมายอย่างสมบูรณ์ในตัวชี้ NULL :-)
Evan Teran

2
มันมีความเกี่ยวข้องกันโดยบังเอิญ - ดังนั้นฉันจึงแสดงความคิดเห็นที่นี่ แต่ C ++ ในหลาย ๆ ทางทำให้มั่นใจได้ว่าวัตถุที่แตกต่างมีที่อยู่ที่ไม่ซ้ำ ... แม้ว่าพวกเขาจะไม่ต้องการพื้นที่เก็บข้อมูลอย่างชัดเจนก็ตาม การทดลองที่เกี่ยวข้องจะเป็นการตรวจสอบขนาดของโครงสร้างที่ว่างเปล่า หรืออาร์เรย์ของโครงสร้างนั้น
Drew Dormann

2
หากต้องการอธิบายอย่างละเอียดเกี่ยวกับความคิดเห็นของ Shmoopty: โดยเฉพาะอย่างยิ่งเมื่อการเขียนโปรแกรมด้วยแม่แบบ (เช่นแม่แบบระดับนโยบายเช่น std :: allocator) เป็นเรื่องปกติใน C ++ ที่จะมีวัตถุขนาดศูนย์ รหัสทั่วไปอาจต้องจัดสรรวัตถุดังกล่าวแบบไดนามิกและใช้ตัวชี้ไปยังพวกเขาเพื่อเปรียบเทียบตัวตนของวัตถุ นี่คือเหตุผลที่โอเปอเรเตอร์ new () ส่งคืนพอยน์เตอร์เฉพาะสำหรับคำขอที่มีขนาดเป็นศูนย์ แม้ว่าเนื้อหา / สามัญมีความสำคัญน้อยกว่า แต่การใช้เหตุผลเดียวกันกับการจัดสรรอาเรย์และโอเปอเรเตอร์ใหม่ [] ()
เทรเวอร์โรบินสัน

คำตอบ:


233

จาก 5.3.4 / 7

เมื่อค่าของนิพจน์ใน direct-new-declarator นั้นเป็นศูนย์ฟังก์ชันการจัดสรรจะถูกเรียกให้จัดสรรอาร์เรย์โดยไม่มีองค์ประกอบ

จาก 3.7.3.1/2

ผลกระทบของการยกเลิกการลงทะเบียนตัวชี้ที่ส่งคืนเนื่องจากการร้องขอขนาดศูนย์ไม่ได้กำหนดไว้

ด้วย

แม้ว่าขนาดของพื้นที่ที่ร้องขอ [โดยใหม่] เป็นศูนย์คำขออาจล้มเหลว

ซึ่งหมายความว่าคุณสามารถทำได้ แต่คุณไม่สามารถถูกต้องตามกฎหมาย (ในลักษณะที่กำหนดไว้อย่างดีในทุกแพลตฟอร์ม) ยกเลิกการจำหน่วยความจำที่คุณได้รับ - คุณสามารถส่งไปยังการลบแถวลำดับและคุณควรลบ

นี่คือบันทึกย่อที่น่าสนใจ (เช่นไม่ใช่ส่วนหนึ่งเชิงมาตรฐานของมาตรฐาน แต่รวมถึงเพื่อวัตถุประสงค์ในการอธิบาย) ที่แนบมากับประโยคจาก 3.7.3.1/2

[32 ความตั้งใจที่จะมีตัวดำเนินการใหม่ () สามารถนำไปใช้ได้โดยการเรียก malloc () หรือ calloc () ดังนั้นกฎจึงเหมือนกันอย่างมีนัยสำคัญ C ++ แตกต่างจาก C ในการขอศูนย์เป็นศูนย์เพื่อส่งกลับตัวชี้ที่ไม่ใช่โมฆะ]


ฉันได้รับความจำรั่วหากไม่ลบ คาดหวังหรือไม่ อย่างน้อยฉันก็ไม่ได้คาดหวัง
EralpB

12
@EralpB: ในทางตรงกันข้ามแม้ว่าคำขอของคุณจะเป็นศูนย์การจัดสรรนี้จะเกิดขึ้นกับฮีปโดยที่คำขอหนึ่งมีการดำเนินการเก็บหนังสือหลายเล่มเช่นการจัดสรรและการเริ่มต้นฮีปการ์ดยามก่อนและหลังโซนที่กำหนดโดยผู้จัดสรร โครงสร้างที่น่ากลัวอื่น ๆ ที่ซับซ้อน การพ้นความหมายคือการทำบัญชีย้อนหลัง
v.oddou

3
@EralpB ใช่ฉันคิดว่าคุณสามารถคาดหวังว่าหน่วยความจำรั่วทุกครั้งที่คุณไม่สมดุลnew[]กับ a delete[]- ไม่ว่าขนาดใด โดยเฉพาะอย่างยิ่งเมื่อคุณโทรหาnew[i]คุณจำเป็นต้องมีหน่วยความจำมากกว่าที่คุณพยายามจัดสรรเพื่อจัดเก็บขนาดของอาร์เรย์ (ซึ่งจะถูกใช้ในภายหลังdelete[]เมื่อทำการจัดสรรคืน)
pqnet

24

ใช่มันถูกกฎหมายที่จะจัดสรรอาร์เรย์ขนาดศูนย์แบบนี้ แต่คุณต้องลบมันด้วย


1
คุณมีหนังเรื่องนี้ไหม? เราทุกคนรู้ว่าint ar[0];ผิดกฎหมายทำไมจึงเป็นเรื่องใหม่
Motti

4
เป็นที่น่าสนใจว่า C ++ ไม่แข็งแรงนักเมื่อมีการห้ามวัตถุขนาดศูนย์ ลองนึกถึงการเพิ่มประสิทธิภาพคลาสฐานที่ว่างเปล่า: ที่นี่เช่นกันวัตถุย่อยคลาสฐานที่ว่างเปล่าอาจมีขนาดเป็นศูนย์ ในทางตรงกันข้าม C Standard พยายามอย่างเต็มที่เพื่อให้แน่ใจว่าไม่มีวัตถุที่ถูกสร้างเป็นศูนย์ขนาด: ในการนิยาม malloc (0) มันบอกว่าเอฟเฟกต์ของการรัน malloc ด้วยอาร์กิวเมนต์ที่ไม่เป็นศูนย์เช่น และในโครงสร้าง {... ; T n []; }; เมื่อไม่มีที่ว่างสำหรับจัดสรรอาเรย์ (FAM) มันบอกว่ามันจะทำงานราวกับว่า "n" มีองค์ประกอบหนึ่ง (ในทั้งสองกรณีการใช้วัตถุในทางใดทางหนึ่งคือ UB เช่น xn [0])
Johannes Schaub - litb

1
ฉันคิดsizeof (type)ว่าคาดว่าจะไม่กลับมาเป็นศูนย์ ดูตัวอย่าง: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

แม้แต่สิ่งที่เรียกว่า "การเพิ่มประสิทธิภาพคลาสฐานที่ว่างเปล่า" ก็มีความเกี่ยวข้องเท่านั้นเนื่องจากการยืนยันว่าวัตถุทั้งหมด (ซึ่งตรงกันข้ามกับไบต์ทั้งหมดภายในวัตถุ) ต้องมีที่อยู่ที่ไม่ซ้ำกัน C ++ สามารถทำได้ง่ายขึ้นหากรหัสที่สนใจจริง ๆ เกี่ยวกับวัตถุที่มีที่อยู่ที่ไม่ซ้ำกันจำเป็นต้องมีเพื่อให้แน่ใจว่ามีขนาดไม่เป็นศูนย์
supercat

15

มาตรฐานพูดถึงอะไรเกี่ยวกับเรื่องนี้? เป็นเรื่องถูกกฎหมายหรือไม่ที่จะ "จัดสรร" บล็อกหน่วยความจำที่ว่างเปล่าอยู่เสมอ?

วัตถุทุกชิ้นมีเอกลักษณ์เฉพาะตัวเช่นที่อยู่เฉพาะซึ่งหมายถึงความยาวที่ไม่เป็นศูนย์ (จำนวนหน่วยความจำจริงจะเพิ่มขึ้นอย่างเงียบ ๆ หากคุณขอศูนย์ไบต์)

หากคุณจัดสรรมากกว่าหนึ่งในวัตถุเหล่านี้คุณจะพบว่าพวกเขามีที่อยู่ที่แตกต่างกัน


"ทุกวัตถุมีเอกลักษณ์เฉพาะตัวเช่นที่อยู่เฉพาะซึ่งหมายถึงความยาวไม่เป็นศูนย์" - จริง แต่ผิด วัตถุมีที่อยู่ แต่ตัวชี้ไปยังวัตถุสามารถชี้ไปที่หน่วยความจำแบบสุ่ม "จำนวนหน่วยความจำที่แท้จริงจะเพิ่มขึ้นอย่างเงียบ ๆ หากคุณถามศูนย์ไบต์" - ไม่แน่ใจ operator []เก็บขนาดของอาเรย์ด้วย (ดูที่isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ) ดังนั้นหากการดำเนินการจัดสรรจำนวนไบต์กับข้อมูลก็สามารถจัดสรรสำหรับการนับไบต์และ 0 ไบต์สำหรับข้อมูลกลับ 1 ตัวชี้อดีตที่ผ่านมา
ม้า

ความยาวที่ไม่เป็นศูนย์ของหน่วยความจำที่จัดสรรไม่ใช่ความยาวที่ไม่เป็นศูนย์ของหน่วยความจำที่ใช้งานได้ ประเด็นของฉันคือว่าตัวชี้สองตัวไปยังวัตถุที่แตกต่างกันสองตัวไม่ควรชี้ไปยังที่อยู่เดียวกัน
ChrisW

1
มาตรฐาน ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ) จะพูดจริง ๆ (3.7.4.1/2) ที่การโทรที่แตกต่างกันoperator new[]ควรกลับไปชี้ที่แตกต่างกัน BTW new expressionมีกฎเพิ่มเติมบางอย่าง (5.3.4) ฉันไม่พบร่องรอยใด ๆ ที่newมีขนาด 0 จำเป็นต้องจัดสรรอะไรจริงๆ ขออภัยฉันลงคะแนนเพราะฉันพบว่าคำตอบของคุณไม่ได้ตอบคำถาม แต่ให้ข้อโต้แย้งบางอย่าง
ม้า

@Steed หน่วยความจำในการจัดเก็บความยาวของบล็อกสามารถคำนวณโดยใช้พื้นที่ที่อยู่ สำหรับอาร์เรย์ที่มีขนาดเป็นศูนย์อาจเป็นไปได้ที่new[]การนำไปใช้เพื่อส่งคืนที่อยู่ในช่วงที่ไม่มีการแมปหน่วยความจำแบบไดนามิกดังนั้นจึงไม่ได้ใช้หน่วยความจำใด ๆ จริงๆ (ในขณะที่ใช้พื้นที่ที่อยู่)
pqnet

@pqnet: การใช้งานที่มีคุณภาพดีควรอนุญาตให้มีจำนวนรอบใหม่ / ลบได้ไม่ จำกัด ใช่หรือไม่ บนแพลตฟอร์มที่มีตัวชี้ 64- บิตมันอาจจะสมเหตุสมผลที่จะพูดว่าคอมพิวเตอร์ที่ดำเนินการwhile(!exitRequested) { char *p = new char[0]; delete [] p; }วนรอบโดยไม่มีการรีไซเคิลตัวชี้จะยุบตัวเป็นฝุ่นก่อนที่มันจะหมดพื้นที่ที่อยู่ แต่บนแพลตฟอร์มที่มีตัวชี้ 32 บิตที่จะ สมมติฐานที่สมเหตุสมผลน้อยกว่ามาก
supercat

14

ใช่มันเป็นกฎหมายอย่างสมบูรณ์จะจัดสรรบล็อกขนาด0 newคุณไม่สามารถทำอะไรที่เป็นประโยชน์กับมันได้เนื่องจากไม่มีข้อมูลที่ถูกต้องให้คุณเข้าถึง int[0] = 5;ผิดกฎหมาย

อย่างไรก็ตามฉันเชื่อว่ามาตรฐานอนุญาตให้สิ่งต่าง ๆmalloc(0)กลับมาNULLอีก

คุณจะต้องdelete []ชี้ไปที่สิ่งที่คุณได้รับจากการจัดสรรเช่นกัน


5
เกี่ยวกับ malloc คุณพูดถูก - เป็นการนำไปปฏิบัติ สิ่งนี้มักถูกมองว่าเป็นความผิดพลาด

ฉันคิดว่าคำถามที่น่าสนใจคือ: NULL เวอร์ชันใหม่ที่ส่งคืน NULL สามารถกำหนดขนาดเป็น 0 ได้หรือไม่?
Evan Teran

1
ซีแมนทิกส์ของใหม่และ malloc ไม่ได้เชื่อมโยง แต่อย่างใดตามมาตรฐาน

ไม่ได้บอกว่าพวกเขาเป็น ... ถูกถามโดยเฉพาะเกี่ยวกับรุ่นใหม่ของ nothrow
Evan Teran

@Evan - เวอร์ชัน nothrow ของการส่งคืนใหม่เป็นโมฆะหากการร้องขอล้มเหลว - ไม่เพียง แต่ถ้าขนาดการร้องขอเป็น 0 @Neil - ไม่เชื่อมโยงในเชิงบรรทัดฐาน - แต่เชื่อมโยงโดยเจตนา (เช่นตัวดำเนินการใหม่อาจถูกนำมาใช้ วิธีรอบ) - ดูเชิงอรรถฉันรวมอยู่ในคำตอบของฉัน
Faisal Vali

1

อยากรู้อยากเห็น C ++ ต้องการตัวดำเนินการใหม่ส่งกลับตัวชี้ที่ถูกต้องแม้ว่าจะมีการร้องขอศูนย์ไบต์ (การขอให้มีพฤติกรรมที่ทำให้เกิดเสียงแปลก ๆ ทำให้สิ่งต่าง ๆ ในภาษาง่ายขึ้น)

ฉันพบว่ามีผลบังคับใช้ C ++ Third Editionกล่าวเช่นนี้ใน "รายการ 51: ปฏิบัติตามข้อตกลงเมื่อเขียนใหม่และลบ"


0

ฉันรับประกันว่า int ใหม่ [0] จะทำให้คุณมีพื้นที่เพิ่มขึ้นเนื่องจากฉันได้ทำการทดสอบแล้ว

ตัวอย่างเช่นการใช้หน่วยความจำของ

int **arr = new int*[1000000000];

มีขนาดเล็กกว่าอย่างมีนัยสำคัญ

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

การใช้หน่วยความจำของข้อมูลโค้ดที่สองลบด้วยโค้ดตัวอย่างแรกคือหน่วยความจำที่ใช้สำหรับ int ใหม่จำนวนมาก [0]

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