การลบตัวชี้ NULL ปลอดภัยหรือไม่


299

การลบตัวชี้ NULL ปลอดภัยหรือไม่

และมันเป็นรูปแบบการเข้ารหัสที่ดีหรือไม่?


21
การปฏิบัติที่ดีคือการเขียนโปรแกรมภาษา C ++ deleteโดยไม่ต้องสายเดียวที่จะ ใช้RAIIแทน นั่นคือใช้std::vector<T> v(100);แทนT* p = new T[100];ใช้ตัวชี้สมาร์ทเช่นunique_ptr<T>และshared_ptr<T>ที่จะดูแลการลบแทนตัวชี้แบบดิบเป็นต้น
fredoverflow

8
ต้องขอบคุณmake_shared(c ++ 11) และmake_unique(c ++ 14) โปรแกรมของคุณควรมีศูนย์ของnewและdelete
sp2danny

2
อาจมีบางกรณีที่พบได้ยากซึ่งต้องมีการลบ / ใหม่เช่น atomic <T *>: atomic <unique_ptr <T>> ไม่อนุญาตและ atomic <shared_ptr <T>> มีค่าใช้จ่ายที่อาจไม่สามารถยอมรับได้ในบางกรณี
atb

2
ในการประกาศคลาสที่มีการจัดการทรัพยากรโดยใช้ RAII คุณต้องโทรหาใหม่และลบใช่ไหม?
VinGarcia

2
@VinGarcia ประเด็นก็คือว่าส่วนใหญ่ผู้ใช้ / ลูกค้า (นั่นคือ: ไม่ใช่ห้องสมุด) รหัสไม่ควรจะต้องเขียนหรือnew deleteคลาสที่ออกแบบมาเพื่อจัดการทรัพยากรที่ส่วนประกอบมาตรฐานไม่สามารถทำงานได้แน่นอนสามารถทำสิ่งที่พวกเขาต้องทำ แต่ประเด็นคือพวกเขาทำสิ่งที่น่าเกลียดกับหน่วยความจำที่พวกเขาจัดการไม่ใช่รหัสผู้ใช้ปลายทาง ดังนั้นให้สร้างไลบรารี่ / ผู้ช่วยของคุณเองเพื่อทำnew/ deleteและใช้คลาสนั้นแทนคลาส
underscore_d

คำตอบ:


265

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

ฉันจะรักถ้าdeleteโดยค่าเริ่มต้นคือการตั้งค่าพารามิเตอร์ให้เป็นโมฆะเช่นมา

#define my_delete(x) {delete x; x = NULL;}

(ฉันรู้เกี่ยวกับค่า R และ L แต่จะไม่ดีหรือไม่?)


72
โปรดทราบว่าอาจมีพอยน์เตอร์อื่น ๆ อีกหลายตัวที่ชี้ไปยังวัตถุเดียวกันแม้ว่าคุณจะตั้งค่าเป็น NULL เมื่อทำการลบ
sth

13
ในกรณีส่วนใหญ่ในรหัสของฉันตัวชี้จะออกนอกขอบเขตเมื่อถูกลบไปแล้ว ปลอดภัยกว่าการตั้งค่าเป็น NULL
jalf

142
การฝึกฝนอย่างมากของพระเจ้าไม่ได้ตั้งค่าตัวชี้เป็น NULL หลังจากลบ การตั้งค่าตัวชี้เป็น NULL หลังจากลบมันจะทำการปลอมข้อผิดพลาดการจัดสรรหน่วยความจำซึ่งเป็นสิ่งที่ไม่ดีมาก โปรแกรมที่ถูกต้องไม่ลบตัวชี้สองครั้งและโปรแกรมที่ลบตัวชี้สองครั้งควรล้มเหลว
เดมอน

15
@ อลิซ: มันไม่เกี่ยวข้องกับสิ่งที่มาตรฐานพูดในแง่นั้น มาตรฐานที่กำหนดไว้การลบตัวชี้โมฆะนั้นถูกต้องด้วยเหตุผลที่ไร้เหตุผลบางอย่างเมื่อ 30 ปีก่อนดังนั้นจึงเป็นสิ่งที่ถูกกฎหมาย (น่าจะเป็นมรดก C) แต่การลบตัวชี้เดียวกันสองครั้ง (แม้หลังจากเปลี่ยนรูปแบบบิต) ยังคงเป็นข้อผิดพลาดของโปรแกรมอย่างรุนแรง ไม่ใช่โดยถ้อยคำของมาตรฐาน แต่โดยตรรกะของโปรแกรมและความเป็นเจ้าของ เช่นเดียวกับการลบพอยน์เตอร์พอยน์เตอร์เนื่องจากพอยน์เตอร์พอยน์เตอร์นั้นไม่ตรงกับวัตถุดังนั้นจึงไม่สามารถลบอะไรได้ โปรแกรมต้องทราบอย่างแน่นอนว่าวัตถุนั้นถูกต้องและใครเป็นเจ้าของและเมื่อสามารถลบได้
Damon

27
@Damon อย่างไรก็ตามแม้จะมีการยกเลิกกฎการเป็นเจ้าของ draconian ของคุณเหล่านี้โครงสร้างการล็อคฟรีมีความแข็งแกร่งกว่าการล็อคแบบพื้นฐาน และใช่เพื่อนร่วมงานของฉันรักฉันจริง ๆ สำหรับโปรไฟล์การดำเนินการที่ปรับปรุงโครงสร้างเหล่านี้ให้และความปลอดภัยของเธรดที่เข้มงวดซึ่งพวกเขารักษาไว้ซึ่งช่วยให้ง่ายต่อการใช้เหตุผลเกี่ยวกับรหัส (เหมาะสำหรับการบำรุงรักษา) อย่างไรก็ตามการโจมตีส่วนตัวโดยนัยของคุณไม่เกี่ยวข้องกับคำจำกัดความของความถูกต้องความถูกต้องหรือความเป็นเจ้าของ สิ่งที่คุณเสนอคือกฎง่ายๆ แต่ไม่ใช่กฎสากลหรือเป็นมาตรฐานที่ประดิษฐานไว้
Alice

77

จากร่างมาตรฐาน C ++ 0x

$ 5.3.5 / 2 - "[... ] ในอีกทางเลือกหนึ่งค่าของตัวถูกดำเนินการลบอาจเป็นค่าตัวชี้ null [... '"

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

นอกจากมาตรฐาน C $ 7.20.3.2 ยังบอกด้วยว่า 'ฟรี' บนตัวชี้ NULL จะไม่ดำเนินการใด ๆ

ฟังก์ชั่นฟรีทำให้พื้นที่ที่ชี้ไปโดย ptr ถูกจัดสรรคืนนั่นคือทำให้พร้อมสำหรับการจัดสรรเพิ่มเติม หาก ptr เป็นตัวชี้ null จะไม่มีการดำเนินการใด ๆ เกิดขึ้น


2
ฉันต้องการคำตอบนี้สำหรับการอ้างอิงหากไม่ได้แนะนำการไม่มีประสิทธิภาพของโค้ดที่ไม่ได้รับการเพิ่มประสิทธิภาพโดยเจตนา ในฐานะที่เป็นรัฐตอบรับการยอมรับการลบตัวชี้โมฆะเป็น no-op ดังนั้นการตรวจสอบว่าตัวชี้เป็นโมฆะก่อนที่จะลบมันจะไม่เกี่ยวข้องอย่างสมบูรณ์หรือไม่
codetaku

47

ใช่มันปลอดภัย

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


เนื่องจากประโยคก่อนหน้าก่อให้เกิดความสับสนตัวอย่าง - ซึ่งไม่ได้ยกเว้นความปลอดภัย - ของสิ่งที่ถูกอธิบาย:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

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


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

1
@EJP: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }เค้าร่างไม่น่าเชื่อในเครือของฟังก์ชั่นอาจจะ: ฉันไม่ได้แสดงโครงสร้างบล็อกหรือการข้ามใด ๆ แต่การdelete[]ดำเนินการในตอนท้ายนั้นปลอดภัยเนื่องจากการเริ่มต้นในตอนเริ่มต้น หากสิ่งที่เพิ่มขึ้นไปยังจุดสิ้นสุดหลังจากx1ได้รับการจัดสรรและก่อนที่จะy1ถูกจัดสรรและไม่มีการเริ่มต้นy1แล้วก็จะมีพฤติกรรมที่ไม่ได้กำหนด - และในขณะที่รหัสสามารถทดสอบความว่างเปล่า (จากx1และy1) ก่อนการลบไม่จำเป็นต้องทำ ดังนั้น.
Jonathan Leffler

22

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

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


20
เวลาที่คนต้องการลบตัวชี้ NULL คือเมื่อพวกเขาไม่แน่ใจว่ามันมี NULL หรือไม่ถ้าพวกเขารู้ว่ามันเป็น NULL พวกเขาจะไม่พิจารณาลบและด้วยเหตุนี้จึงขอ ;-)
Tony Delroy

@ โทนี่: จุดของฉันเป็นเพียงว่ามันจะไม่มีผลและการปรากฏตัวของรหัสดังกล่าวซึ่งลบตัวชี้ซึ่งบางครั้งมี NULL ไม่จำเป็นต้องไม่ดี
Brian R. Bondy

3
การตรวจสอบซ้ำซ้อนของ IMO นั้นไม่ดีอย่างแน่นอนสำหรับประสิทธิภาพความสามารถในการอ่านและการบำรุงรักษา
paulm

@paulm OP แน่นอนว่าไม่ได้พูดถึงเรื่องเลวร้ายแบบนั้นอีกแล้ว Seg Fault / UB ที่แย่กว่านั้นอีก
ฤดูหนาว


3

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


คุณสามารถเพิ่มคำอธิบายสำหรับคำตอบของคุณได้ไหม?
Marcin Nabiałek

-2

ฉันพบว่ามันไม่ปลอดภัย (VS2010) เพื่อลบ [] NULL (เช่นไวยากรณ์ของอาร์เรย์) ฉันไม่แน่ใจว่าเป็นไปตามมาตรฐาน C ++ หรือไม่

มันคือความปลอดภัยในการลบโมฆะ (ไวยากรณ์เกลา)


6
นี่เป็นสิ่งผิดกฎหมายและฉันไม่เชื่อ
Konrad Rudolph

5
คุณควรจะสามารถลบตัวชี้โมฆะใด ๆ ดังนั้นถ้ามันเป็นสิ่งที่ผิดสำหรับคุณคุณอาจจะมีข้อผิดพลาดในรหัสที่แสดงมัน
Mysticial

3
§5.3.2ในทางเลือกที่สอง (ลบอาร์เรย์) ค่าของตัวถูกดำเนินการของการลบอาจเป็นค่าตัวชี้โมฆะหรือค่าตัวชี้ที่เป็นผลมาจากอาร์เรย์นิพจน์ก่อนหน้าใหม่
sp2danny

1
@Opux พฤติกรรมของ VS2010 ถูกกล่าวหาในคำตอบ ตามที่อธิบายไว้ในความคิดเห็นอื่น ๆ delete[] NULLมันปลอดภัยที่จะ
Konrad Rudolph

1
@Opux นั่นเป็นเหตุผลที่ฉันเขียนว่า "ฉันไม่เชื่อ" มากกว่าที่จะ "ผิด" แต่ฉันก็ยังทำไม่ได้และมันเป็นการละเมิดมาตรฐานที่น่ารังเกียจและโง่เขลา โดยทั่วไปแล้ว VC ++ นั้นค่อนข้างดีในการทำตามข้อ จำกัด ของมาตรฐาน
Konrad Rudolph
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.