การตั้งค่าตัวแปรเป็น NULL หลังจากว่าง


156

ใน บริษัท ของฉันมีกฎการเข้ารหัสที่บอกว่าหลังจากพ้นหน่วยความจำใด ๆ NULLตั้งค่าตัวแปร ตัวอย่างเช่น ...

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

ฉันรู้สึกว่าในกรณีเช่นรหัสที่แสดงด้านบนการตั้งค่าเป็นNULLไม่มีความหมายใด ๆ หรือว่าฉันขาดอะไรไป?

หากไม่มีความหมายในกรณีเช่นนี้ฉันจะดำเนินการกับ "ทีมคุณภาพ" เพื่อลบกฎการเข้ารหัสนี้ โปรดให้คำแนะนำ.


2
เป็นประโยชน์เสมอที่จะสามารถตรวจสอบว่าptr == NULLก่อนทำอะไรกับมัน ถ้าคุณไม่ลบพอยน์เตอร์พอยน์เตอร์ที่ว่างคุณจะได้รับptr != NULLแต่ก็ยังใช้พอยน์เตอร์ไม่ได้
Ki Jéy

ชี้ห้อยสามารถนำไปสู่ช่องโหว่โหว่เช่นใช้หลังจากฟรี
КонстантинВан

คำตอบ:


285

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

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

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


3
ฉันไม่เข้าใจว่าทำไมคุณจะ "เริ่มต้นพอยน์เตอร์ให้เป็น NULL ก่อนที่พวกเขาจะได้รับค่าตัวชี้ที่แท้จริง"?
Paul Biggar

26
@ พอล: ในกรณีเฉพาะประกาศสามารถอ่านint *nPtr=NULL;ได้ ตอนนี้ฉันจะยอมรับว่าสิ่งนี้จะซ้ำซ้อนกับ malloc ที่ติดตามในบรรทัดถัดไป อย่างไรก็ตามหากมีรหัสระหว่างการประกาศและการเริ่มต้นครั้งแรกใครบางคนอาจเริ่มใช้ตัวแปรแม้ว่าจะยังไม่มีค่า หากคุณเป็นโมฆะเริ่มต้นคุณจะได้รับ segfault โดยไม่ต้องคุณอาจอ่านหรือเขียนหน่วยความจำแบบสุ่มอีกครั้ง ในทำนองเดียวกันหากตัวแปรในภายหลังได้รับการเริ่มต้นตามเงื่อนไขเท่านั้นการเข้าถึงความผิดพลาดในภายหลังควรให้คุณเกิดปัญหาทันทีหากคุณจำได้ว่าจะเริ่มต้นเป็นโมฆะ
Martin v. Löwis

1
โดยส่วนตัวฉันคิดว่าใน codebase ที่ไม่มีเรื่องผิดพลาดใด ๆ ที่ได้รับข้อผิดพลาดสำหรับการยกเลิกการอ้างอิง null นั้นไม่ชัดเจนเท่ากับการได้รับข้อผิดพลาดในการยกเลิกการลงทะเบียนที่อยู่ที่คุณไม่ได้เป็นเจ้าของ โดยส่วนตัวฉันไม่เคยรำคาญ
wilhelmtell

9
โดยวิลเฮล์มจุดนั้นมีตัวชี้โมฆะเป็นตัวบ่งชี้ว่าคุณได้รับความผิดพลาดที่แน่นอนและตำแหน่งที่แท้จริงของปัญหา การเข้าถึงที่ไม่ถูกต้องอาจหรืออาจล้มเหลวและข้อมูลหรือพฤติกรรมที่เสียหายในรูปแบบที่ไม่คาดคิดในสถานที่ที่ไม่คาดคิด
Amit Naidu

4
ที่จริงแล้วการเริ่มต้นตัวชี้ไปที่ NULL มีข้อเสียเปรียบที่สำคัญอย่างน้อยหนึ่งข้อ: มันสามารถป้องกันคอมไพเลอร์จากการเตือนคุณเกี่ยวกับตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้น ยกเว้นว่าตรรกะของรหัสของคุณจริง ๆ แล้วจัดการค่าสำหรับตัวชี้อย่างชัดเจน (เช่นถ้า (nPtr == NULL) dosomething ... ) จะเป็นการดีกว่าถ้าปล่อยให้เป็นไปตามที่ควรจะเป็น
Eric

37

การตั้งตัวชี้เป็นNULLหลังจากfreeนั้นเป็นวิธีปฏิบัติที่น่าสงสัยที่มักได้รับความนิยมในฐานะกฎ "โปรแกรมที่ดี" ในสถานที่ที่ผิดพลาดจริง ๆ มันเป็นหนึ่งในความจริงปลอมที่อยู่ในหมวดหมู่ "ฟังดูถูก" แต่ในความเป็นจริงแล้วไม่มีอะไรที่เป็นประโยชน์จริง ๆ (และบางครั้งนำไปสู่ผลกระทบด้านลบ)

ถูกกล่าวหาว่าการตั้งค่าตัวชี้เป็นNULLหลังจากfreeควรป้องกันปัญหา "ฟรีสองเท่า" ที่น่าสะพรึงกลัวเมื่อค่าตัวชี้เดียวกันถูกส่งไปยังfreeมากกว่าหนึ่งครั้ง ในความเป็นจริงแม้ว่าใน 9 รายจาก 10 จริง "คู่ฟรี" ปัญหาเกิดขึ้นเมื่อที่แตกต่างกันfreeตัวชี้วัตถุถือค่าตัวชี้เดียวกันจะใช้เป็นข้อโต้แย้ง จำเป็นต้องพูดการตั้งค่าตัวชี้ไปNULLหลังจากทำfreeอะไรอย่างเพื่อป้องกันปัญหาในกรณีดังกล่าว

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

สุดท้ายหากรหัสของคุณได้รับการออกแบบมาโดยเฉพาะเพื่อพึ่งพาเป็นค่าตัวชี้NULLหรือไม่NULLก็เป็นอย่างดีปรับการตั้งค่าตัวชี้ไปหลังจากNULL freeแต่ตามกฎทั่วไป "การปฏิบัติที่ดี" (เช่นเดียวกับใน "ตั้งค่าตัวชี้ของคุณเป็นNULLหลังfree") มันเป็นของปลอมที่รู้จักกันดีและไร้ประโยชน์อีกครั้งตามมาด้วยเหตุผลบางประการทางศาสนาเช่นเดียวกับลัทธิวูดู


1
อย่างแน่นอน. ฉันจำไม่ได้ว่าเคยก่อให้เกิดสองครั้งฟรีที่จะแก้ไขโดยการตั้งตัวชี้ไปที่ NULL หลังจากพ้น แต่ฉันได้ก่อให้เกิดมากมายที่จะไม่
LnxPrgr3

4
@AnT "พิรุธ" ค่อนข้างมาก ทุกอย่างขึ้นอยู่กับกรณีการใช้งาน หากค่าของตัวชี้เคยถูกใช้ในความจริง / เท็จมันไม่เพียง แต่เป็นวิธีปฏิบัติที่ถูกต้อง แต่เป็นวิธีปฏิบัติที่ดีที่สุด
Coder

1
@Coder ผิดโดยสิ้นเชิง ถ้าค่าของตัวชี้จะใช้ในความหมายที่ผิดพลาดจริงจะรู้หรือไม่ว่ามันชี้ไปที่วัตถุก่อนที่จะเรียกร้องให้ฟรีก็ไม่เพียง แต่ไม่ได้ปฏิบัติที่ดีที่สุดของมันที่ไม่ถูกต้อง ตัวอย่างเช่นfoo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;. ที่นี่การตั้งค่าbarเป็นNULLหลังจากการโทรถึงfreeจะทำให้ฟังก์ชั่นคิดว่ามันไม่เคยมีบาร์และคืนค่าที่ผิด!
David Schwartz

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

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

34

การตอบสนองส่วนใหญ่มุ่งเน้นไปที่การป้องกันการเป็นอิสระสองเท่า แต่การตั้งค่าตัวชี้เป็น NULL มีประโยชน์อื่น เมื่อคุณเพิ่มพอยน์เตอร์แล้วหน่วยความจำนั้นจะถูกจัดสรรใหม่โดยการเรียกไปที่ malloc อื่น หากคุณยังมีตัวชี้ดั้งเดิมอยู่รอบ ๆ คุณอาจท้ายด้วยบั๊กที่คุณพยายามใช้ตัวชี้หลังจากตัวแปรอื่นฟรีและทำให้เสียหายและเสียหายจากนั้นโปรแกรมของคุณเข้าสู่สถานะที่ไม่รู้จักและสิ่งเลวร้ายทุกประเภทอาจเกิดขึ้นได้ โชคดีที่ข้อมูลเสียหายถ้าคุณโชคไม่ดี) หากคุณตั้งค่าตัวชี้เป็น NULL หลังจากว่างความพยายามใด ๆ ในการอ่าน / เขียนผ่านตัวชี้นั้นในภายหลังจะส่งผลให้เกิด segfault ซึ่งโดยทั่วไปแล้วจะดีกว่าหากหน่วยความจำแบบสุ่มเสียหาย

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


1
+1 นี่เป็นจุดที่ดีมาก ไม่ใช่เหตุผลเกี่ยวกับ "double free" (ซึ่งเป็นการหลอกลวงอย่างสมบูรณ์) แต่นี่เป็นสิ่งที่ ฉันไม่ใช่แฟนตัวยงของ NULL-ing เชิงกลของพอยfreeน์เตอร์ แต่มันก็สมเหตุสมผล
An

หากคุณสามารถเข้าถึงตัวชี้หลังจากปล่อยผ่านตัวชี้เดียวกันนั้นมีโอกาสมากขึ้นที่คุณจะเข้าถึงตัวชี้หลังจากปล่อยวัตถุที่ชี้ไปยังตัวชี้อื่น ๆ ดังนั้นสิ่งนี้ไม่ได้ช่วยคุณเลย - คุณยังต้องใช้กลไกอื่น ๆ เพื่อให้แน่ใจว่าคุณไม่สามารถเข้าถึงวัตถุผ่านตัวชี้หนึ่งหลังจากปล่อยผ่านวัตถุอื่น คุณอาจใช้วิธีการนั้นเพื่อป้องกันในกรณีตัวชี้เดียวกันเช่นกัน
David Schwartz

1
@DavidSchwartz: ฉันไม่เห็นด้วยกับความคิดเห็นของคุณ เมื่อฉันต้องเขียนสแต็คสำหรับการออกกำลังกายที่มหาวิทยาลัยเมื่อไม่กี่สัปดาห์ที่ผ่านมาฉันมีปัญหาฉันตรวจสอบไม่กี่ชั่วโมง ฉันเข้าถึงหน่วยความจำที่ว่างอยู่แล้วในบางจุด (ฟรีบางบรรทัดเร็วเกินไป) และบางครั้งก็นำไปสู่พฤติกรรมที่แปลกมาก ถ้าฉันจะตั้งค่าตัวชี้เป็น NULL หลังจากพ้นมันจะมี segfault "ง่าย" และฉันจะบันทึกสองสามชั่วโมงของการทำงาน ดังนั้น +1 สำหรับคำตอบนี้!
mozzbozz

2
@katze_sonne แม้แต่นาฬิกาที่หยุดก็ยังทำงานได้วันละสองครั้ง มีความเป็นไปได้มากกว่าที่การตั้งค่าพอยน์เตอร์เป็น NULL จะซ่อนข้อบกพร่องด้วยการป้องกันการเข้าถึงที่ผิดพลาดไปยังออบเจ็กต์ที่ถูกปล่อยออกมาจากการแยกรหัสในการตรวจสอบ NULL แล้วตรวจสอบวัตถุที่ควรตรวจสอบ (บางทีการตั้งค่าพอยน์เตอร์ให้เป็น NULL หลังจากว่างในการแก้ไขข้อบกพร่องเฉพาะอาจเป็นประโยชน์หรือตั้งค่าเป็นค่าอื่นที่ไม่ใช่ NULL ที่รับประกันว่า segfault อาจสมเหตุสมผล แต่ความงี่เง่านี้เกิดขึ้นเพื่อช่วยคุณเมื่อไม่โต้แย้งในความโปรดปราน .)
David Schwartz

@DavidSchwartz เอาล่ะฟังดูสมเหตุสมผล ... ขอบคุณสำหรับความคิดเห็นของคุณฉันจะพิจารณาเรื่องนี้ในอนาคต! :) +1
mozzbozz

20

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

ลองทำสิ่งนี้แทน:

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = NULL;
}
#else
#define myfree(p) do { void ** __p = (p); free(*(__p)); *(__p) = NULL; } while (0)
#endif

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

แก้ไข : เพิ่มทำ ... ในขณะที่แนะนำด้านล่างขอบคุณ


3
รุ่นแมโครมีข้อบกพร่องเล็กน้อยหากคุณใช้หลังจากคำสั่ง if ที่ไม่มีเครื่องหมายวงเล็บ
Mark Ransom

มีอะไร (โมฆะ) 0? รหัสนี้ทำ: ถ้า (x) myfree (& x); อื่น do_foo (); กลายเป็นถ้า (x) {ฟรี (* (& x)); * (& x) = null; } ถือเป็นโมฆะ 0; อื่น do_foo (); สิ่งอื่นคือข้อผิดพลาด
jmucchiello

มาโครนั้นเป็นสถานที่ที่สมบูรณ์แบบสำหรับโอเปอเรเตอร์คอมม่า: ฟรี ( (p)), * (p) = null แน่นอนปัญหาต่อไปคือมันประเมิน * (p) สองครั้ง มันควรเป็น {void * _pp = (p); ฟรี (* _ PP); * _pp = null; } มันไม่ใช่ความสนุกของโปรเซสเซอร์
jmucchiello

5
มาโครไม่ควรอยู่ในวงเล็บเหลี่ยม แต่ควรอยู่ในdo { } while(0)บล็อกเพื่อif(x) myfree(x); else dostuff();ไม่ให้แตก
คริสลัทซ์

3
ดังที่ Lutz กล่าวว่าร่างกายของมาโครdo {X} while (0)เป็น IMO วิธีที่ดีที่สุดในการสร้างร่างกายมาโครที่ "รู้สึกเหมือนและทำงานเหมือน" ฟังก์ชั่น คอมไพเลอร์ส่วนใหญ่จะปรับลูปให้เหมาะสมอยู่ดี
Mike Clark

7

หากคุณไปถึงตัวชี้ที่ว่าง () d มันอาจแตกหรือไม่ หน่วยความจำนั้นอาจถูกจัดสรรใหม่ไปยังส่วนอื่นของโปรแกรมของคุณจากนั้นคุณจะได้รับความเสียหายของหน่วยความจำ

หากคุณตั้งค่าตัวชี้เป็น NULL ถ้าคุณเข้าถึงตัวชี้โปรแกรมจะขัดข้องด้วย segfault ไม่อีกต่อไปบางครั้งก็ใช้งานได้ '' ไม่เกิน ,, ล่มในลักษณะที่ไม่สามารถคาดเดาได้ '' วิธีแก้ข้อบกพร่องง่ายขึ้น


5
โปรแกรมไม่ได้มีปัญหากับ segfault เสมอไป หากวิธีการที่คุณเข้าถึงตัวชี้หมายความว่ามีการชดเชยออฟเซ็ตขนาดใหญ่เพียงพอก่อนที่จะทำการลงทะเบียนอีกครั้งอาจถึงหน่วยความจำที่กำหนดแอดเดรสได้: ((MyHugeStruct *) 0) -> fieldNearTheEnd และนั่นคือแม้กระทั่งก่อนที่คุณจะจัดการกับฮาร์ดแวร์ที่ไม่ได้แบ่ง segfault ในการเข้าถึง 0 เลย อย่างไรก็ตามโปรแกรมมีแนวโน้มที่จะเกิดข้อผิดพลาดกับ segfault
Steve Jessop

7

การตั้งค่าตัวชี้ไปยังfreeหน่วยความจำ 'd หมายความว่าการพยายามเข้าถึงหน่วยความจำนั้นผ่านตัวชี้จะล้มเหลวทันทีแทนที่จะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด มันทำให้ง่ายขึ้นมากในการพิจารณาว่าสิ่งใดผิดพลาด

ฉันเห็นการโต้เถียงของคุณ: เมื่อnPtrพ้นขอบเขตnPtr = NULLแล้วดูเหมือนจะไม่มีเหตุผลอะไรที่จะทำให้มันเป็นNULLจริง อย่างไรก็ตามในกรณีของstructสมาชิกหรือที่อื่นที่ตัวชี้ไม่ได้ออกนอกขอบเขตในทันทีมันเหมาะสมกว่า ไม่ชัดเจนในทันทีว่าจะใช้ตัวชี้นั้นอีกครั้งหรือไม่โดยใช้รหัสที่ไม่ควรใช้

อาจเป็นไปได้ว่ามีการระบุกฎโดยไม่แยกความแตกต่างระหว่างสองกรณีนี้เนื่องจากเป็นการยากที่จะบังคับใช้กฎโดยอัตโนมัติ มันไม่เจ็บที่จะตั้งค่าพอยน์เตอร์ให้เป็นNULLอิสระ แต่มันมีศักยภาพที่จะชี้ปัญหาใหญ่ ๆ


7

ข้อผิดพลาดที่พบบ่อยที่สุดใน c คือฟรีสองครั้ง โดยทั่วไปคุณทำอะไรแบบนั้น

free(foobar);
/* lot of code */
free(foobar);

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

if(foobar != null){
  free(foobar);
}

ที่จะต้องสังเกตด้วยว่าfree(NULL)จะไม่ทำอะไรดังนั้นคุณไม่จำเป็นต้องเขียนคำสั่ง if ฉันไม่ได้เป็นกูรูด้านระบบปฏิบัติการจริงๆ แต่ฉันก็ยังสวยอยู่ดีตอนนี้ระบบปฏิบัติการส่วนใหญ่จะใช้งานฟรีสองเท่า

นั่นเป็นเหตุผลหลักว่าทำไมทุกภาษาที่มีการรวบรวมขยะ (Java, dotnet) ภูมิใจอย่างมากที่ไม่มีปัญหานี้และไม่ต้องออกจากการจัดการหน่วยความจำโดยรวม


11
คุณสามารถโทรได้ฟรี () โดยไม่ตรวจสอบ - ฟรี (NULL) หมายถึงไม่ทำอะไรเลย
เหลืองอำพัน

5
นั่นไม่ได้ซ่อนข้อบกพร่อง? (เช่นปล่อยมากเกินไป)
Georg Schölly

1
ขอบคุณมากฉันเข้าใจแล้ว ฉันพยายาม:p = (char *)malloc(.....); free(p); if(p!=null) //p!=null is true, p is not null although freed { free(p); //Note: checking doesnot prevent error here }
Shaobo วัง

5
อย่างที่บอกไปแล้วว่าfree(void *ptr) ไม่สามารถเปลี่ยนค่าของตัวชี้ที่ส่งผ่านได้ มันสามารถเปลี่ยนเนื้อหาของตัวชี้ที่ข้อมูลที่เก็บไว้ตามที่อยู่ที่แต่ไม่ได้อยู่เองหรือค่าของตัวชี้ ที่จะต้องมีfree(void **ptr)(ซึ่งเห็นได้ชัดว่าไม่ได้รับอนุญาตตามมาตรฐาน) หรือมาโคร (ซึ่งได้รับอนุญาตและพกพาได้อย่างสมบูรณ์แบบ แต่คนไม่ชอบแมโคร) นอกจากนี้ C ไม่ได้เกี่ยวกับความสะดวกสบายมันเกี่ยวกับการให้โปรแกรมเมอร์ควบคุมได้มากเท่าที่พวกเขาต้องการ หากพวกเขาไม่ต้องการค่าใช้จ่ายเพิ่มเติมของการตั้งค่าพอยNULLน์เตอร์มันก็ไม่ควรถูกบังคับ
คริสลัทซ์

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


4

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

หากคุณปล่อยให้ตัวชี้ NULL แล้วความพยายามใด ๆ ที่จะใช้มันจะไป 0x0 dereference และความผิดพลาดที่นั่นซึ่งเป็นเรื่องง่ายที่จะแก้ปัญหา พอยน์เตอร์แบบสุ่มที่ชี้ไปยังหน่วยความจำแบบสุ่มนั้นยากที่จะทำการดีบัก เห็นได้ชัดว่าไม่จำเป็น แต่ก็เป็นเหตุผลว่าทำไมมันถึงอยู่ในเอกสารแนวปฏิบัติที่ดีที่สุด


อย่างน้อยใน Windows debug builds จะตั้งค่าหน่วยความจำเป็น 0xddddddddd ดังนั้นเมื่อคุณใช้ตัวชี้เพื่อลบหน่วยความจำที่คุณรู้จักทันที ควรมีกลไกที่คล้ายกันในทุกแพลตฟอร์ม
i_am_jorf

2
เช่นเดียวกัน, บล็อกหน่วยความจำที่ถูกลบอาจจะได้รับการจัดสรรใหม่และกำหนดให้กับวัตถุอื่นตามเวลาที่คุณใช้ตัวชี้อีกครั้ง
Constantin

4

จากมาตรฐาน ANSI C:

void free(void *ptr);

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

"พฤติกรรมที่ไม่ได้กำหนด" มักจะเป็นความผิดพลาดของโปรแกรม เพื่อหลีกเลี่ยงปัญหานี้จะปลอดภัยในการรีเซ็ตตัวชี้เป็น NULL free () ตัวเองไม่สามารถทำสิ่งนี้ได้เนื่องจากมันถูกส่งผ่านเพียงตัวชี้ไม่ใช่ตัวชี้ไปยังตัวชี้ คุณยังสามารถเขียนรุ่นที่ปลอดภัยกว่าได้ฟรี () ซึ่งตัวชี้ NULLs:

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

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

@DrPizza: ฉันเพิ่งพบข้อโต้แย้งว่าทำไมคนเราควรตั้งเป็นNULLเพื่อหลีกเลี่ยงข้อผิดพลาดที่กำบัง stackoverflow.com/questions/1025589/…ดูเหมือนว่าจะมีข้อผิดพลาดบางอย่างซ่อนอยู่
Georg Schölly

1
โปรดระวังว่า void pointer-to-pointer มีปัญหา: c-faq.com/ptrs/genericpp.html
รักษาความปลอดภัย

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

4
@Chris Lutz: Hogwash หากคุณเขียนโค้ดที่ทำให้ตัวชี้เดียวกันเป็นอิสระสองครั้งโปรแกรมของคุณจะมีข้อผิดพลาดแบบลอจิคัล การปิดบังข้อผิดพลาดเชิงตรรกะโดยการทำให้โปรแกรมล้มเหลวไม่ได้หมายความว่าโปรแกรมนั้นถูกต้อง: มันยังทำอะไรที่ไร้สาระอยู่ ไม่มีสถานการณ์สมมติใดที่การเขียนความเป็นอิสระสองเท่านั้นถูกต้อง
DrPizza

4

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

อย่างไรก็ตาม - ฉันจะไม่ตั้งค่าตัวแปรให้เป็นโมฆะหากตัวชี้ไม่ควรใช้อีกครั้ง แต่บ่อยครั้งที่การออกแบบระดับที่สูงกว่าทำให้ฉันมีเหตุผลที่จะตั้งค่าให้เป็นโมฆะ ตัวอย่างเช่นหากตัวชี้เป็นสมาชิกของชั้นเรียนและฉันได้ลบสิ่งที่ชี้ไปแล้ว "สัญญา" ถ้าคุณชอบของชั้นเรียนคือสมาชิกที่จะชี้ไปที่สิ่งที่ถูกต้องได้ตลอดเวลาดังนั้นมันจะต้องถูกตั้งค่าเป็นโมฆะ ด้วยเหตุผลนั้น ความแตกต่างเล็กน้อย แต่ฉันคิดว่าสำคัญ

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

รูปแบบที่พบบ่อยคือการตั้งค่าทุกตัวชี้สมาชิกของเว็บไซต์เพื่อโมฆะในตัวสร้างและมีการโทร destructor ลบเกี่ยวกับตัวชี้ใด ๆ เพื่อให้ข้อมูลว่าการออกแบบของคุณว่าชั้นเรียนที่เป็นเจ้าของ ชัดเจนในกรณีนี้คุณต้องตั้งค่าตัวชี้เป็น NULL เมื่อคุณลบบางสิ่งเพื่อระบุว่าคุณไม่ได้เป็นเจ้าของข้อมูลใด ๆ มาก่อน

ดังนั้นเพื่อสรุปใช่ฉันมักจะตั้งตัวชี้ไปที่ NULL หลังจากลบบางสิ่งบางอย่าง แต่มันเป็นส่วนหนึ่งของการออกแบบที่มีขนาดใหญ่ขึ้นและความคิดเกี่ยวกับผู้ที่เป็นเจ้าของข้อมูลมากกว่าเนื่องจากการสุ่มสี่สุ่มห้าตามกฎมาตรฐานการเข้ารหัส ฉันจะไม่ทำเช่นนั้นในตัวอย่างของคุณเพราะฉันคิดว่าไม่มีประโยชน์ที่จะทำเช่นนั้นและมันเพิ่ม "ความยุ่งเหยิง" ซึ่งในประสบการณ์ของฉันมีหน้าที่รับผิดชอบต่อข้อผิดพลาดและรหัสที่ไม่ดีเช่นเดียวกับสิ่งนี้


4

เมื่อเร็ว ๆ นี้ฉันเจอคำถามเดียวกันหลังจากหาคำตอบ ฉันมาถึงข้อสรุปนี้:

เป็นการปฏิบัติที่ดีที่สุดและต้องปฏิบัติตามสิ่งนี้เพื่อให้สามารถพกพาได้ในทุกระบบ (ฝังตัว)

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

ไปเพื่อเสมอ

free(ptr);
ptr = NULL;

3

กฎนี้มีประโยชน์เมื่อคุณพยายามหลีกเลี่ยงสถานการณ์ต่อไปนี้:

1) คุณมีฟังก์ชั่นที่ยาวมาก ๆ พร้อมตรรกะที่ซับซ้อนและการจัดการหน่วยความจำและคุณไม่ต้องการใช้ตัวชี้ซ้ำเพื่อลบหน่วยความจำในภายหลังโดยไม่ตั้งใจ

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

ในสถานการณ์ของคุณมันไม่สมเหตุสมผลเลย แต่ถ้าฟังก์ชั่นใช้งานได้นานขึ้นมันอาจจะสำคัญ

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

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


2

เพื่อเพิ่มสิ่งที่คนอื่นพูดวิธีการที่ดีอย่างหนึ่งของการใช้งานตัวชี้คือการตรวจสอบเสมอว่าเป็นตัวชี้ที่ถูกต้องหรือไม่ สิ่งที่ต้องการ:


if(ptr)
   ptr->CallSomeMethod();

ทำเครื่องหมายตัวชี้อย่างชัดเจนว่าเป็น NULL หลังจากปล่อยให้ใช้งานได้ใน C / C ++


5
ในหลายกรณีที่ตัวชี้ NULL ไม่มีเหตุผลควรเขียนการยืนยันแทน
Erich Kitzmueller

2

นี่อาจเป็นข้อโต้แย้งในการเริ่มต้นพอยน์เตอร์ทั้งหมดให้เป็น NULL แต่สิ่งที่เป็นเช่นนี้อาจเป็นข้อผิดพลาดลับๆล่อๆ:

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}

void caller() {
  some_func();
  other_func();
}

pสิ้นสุดในตำแหน่งเดียวกันบนสแต็กเหมือนอดีตnPtrดังนั้นจึงอาจยังคงมีตัวชี้ที่ใช้ได้ การมอบหมายให้*pอาจเขียนทับสิ่งที่ไม่เกี่ยวข้องทุกประเภทและนำไปสู่ข้อบกพร่องที่น่าเกลียด โดยเฉพาะอย่างยิ่งหากคอมไพเลอร์เริ่มต้นตัวแปรท้องถิ่นด้วยศูนย์ในโหมดดีบัก แต่ไม่ได้เปิดใช้งานการปรับให้เหมาะสมที่สุด ดังนั้นการสร้าง debug จะไม่แสดงสัญญาณของบั๊กในขณะที่ release builds ระเบิดขึ้นแบบสุ่ม ...


2

การตั้งค่าตัวชี้ที่เพิ่งถูกปลดปล่อยให้เป็น NULL นั้นไม่ใช่ข้อบังคับ แต่เป็นแนวปฏิบัติที่ดี ด้วยวิธีนี้คุณสามารถหลีกเลี่ยง 1) โดยใช้การชี้อิสระ 2) ปล่อยให้เป็นอิสระ


2

การตั้งค่าตัวชี้ไปที่ NULL คือการปกป้อง agains ที่เรียกว่า double-free - สถานการณ์เมื่อ free () ถูกเรียกมากกว่าหนึ่งครั้งสำหรับที่อยู่เดียวกันโดยไม่ต้องจัดสรรบล็อกใหม่ที่ที่อยู่นั้น

ฟรีสองครั้งนำไปสู่พฤติกรรมที่ไม่ได้กำหนด - โดยปกติกองการทุจริตหรือหยุดทำโปรแกรมทันที โทรฟรี () สำหรับตัวชี้ NULL ไม่ทำอะไรเลยและรับประกันได้ว่าปลอดภัย

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


2

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

แต่ ... ระวังตัวด้วย ไม่ใช่ทุกระบบที่ทำให้เกิด segfault ถ้าคุณยกเลิกการอ้างอิง NULL บน (อย่างน้อยบางเวอร์ชัน) AIX, * (int *) 0 == 0 และ Solaris มีความเข้ากันได้กับคุณลักษณะ "AIX" นี้เป็นทางเลือก


2

สำหรับคำถามเดิม: การตั้งค่าตัวชี้เป็น NULL โดยตรงหลังจากการปล่อยเนื้อหาเป็นการเสียเวลาอย่างสมบูรณ์หากรหัสตรงตามข้อกำหนดทั้งหมดจะถูกดีบั๊กอย่างสมบูรณ์และจะไม่ถูกแก้ไขอีกครั้ง ในทางกลับกัน NULL การป้องกันตัวชี้ที่ได้รับการปลดปล่อยจะมีประโยชน์มากเมื่อมีคนคิดเพิ่มบล็อกใหม่ของรหัสภายใต้ฟรี () เมื่อการออกแบบของโมดูลดั้งเดิมไม่ถูกต้องและในกรณีของมัน -compiles-but-not-do-what-I-want bugs

ไม่ว่าในระบบใดมีเป้าหมายที่ไม่สามารถหาสิ่งที่ถูกได้ง่ายที่สุดและค่าใช้จ่ายที่ไม่สามารถลดลงได้ของการวัดที่ไม่ถูกต้อง ใน C เรามีชุดเครื่องมือที่คมชัดและแข็งแกร่งมากซึ่งสามารถสร้างสิ่งต่าง ๆ ในมือของช่างฝีมือและทำดาเมจทุกประเภทของการบาดเจ็บเชิงเปรียบเทียบเมื่อจัดการอย่างไม่เหมาะสม บางอย่างยากที่จะเข้าใจหรือใช้อย่างถูกต้อง และผู้คนที่ไม่ชอบความเสี่ยงตามธรรมชาติทำสิ่งที่ไม่มีเหตุผลเช่นการตรวจสอบตัวชี้ค่า NULL ก่อนโทรฟรีด้วย ...

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

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


คำตอบที่ดี. ฉันต้องการเพิ่มสิ่งหนึ่ง การตรวจสอบฐานข้อมูลบั๊กเป็นการดีที่จะทำด้วยเหตุผลหลายประการ แต่ในบริบทของคำถามเดิมโปรดจำไว้ว่ามันยากที่จะทราบว่ามีการป้องกันปัญหาตัวชี้ที่ไม่ถูกต้องจำนวนเท่าใดหรืออย่างน้อยก็ถูกจับได้ตั้งแต่เนิ่น ๆ เพื่อที่จะไม่ได้ทำให้มันกลายเป็นฐานข้อมูลบั๊ก ประวัติข้อผิดพลาดให้หลักฐานที่ดีกว่าสำหรับการเพิ่มกฎการเข้ารหัส
jimhark

2

มีสองเหตุผล:

หลีกเลี่ยงการล่มเมื่อปล่อยสองครั้ง

เขียนโดยRageZในคำถามที่ซ้ำกัน

ข้อผิดพลาดที่พบบ่อยที่สุดใน c คือฟรีสองครั้ง โดยทั่วไปคุณทำอะไรแบบนั้น

free(foobar);
/* lot of code */
free(foobar);

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

if(foobar != NULL){
  free(foobar);
}

ที่จะต้องสังเกตด้วยว่าfree(NULL) จะไม่ทำอะไรดังนั้นคุณไม่จำเป็นต้องเขียนคำสั่ง if ฉันไม่ได้เป็นกูรูด้านระบบปฏิบัติการจริงๆ แต่ฉันก็ยังสวยอยู่ดีตอนนี้ระบบปฏิบัติการส่วนใหญ่จะใช้งานฟรีสองเท่า

นั่นเป็นเหตุผลหลักว่าทำไมทุกภาษาที่มีการรวบรวมขยะ (Java, dotnet) ภูมิใจมากที่ไม่มีปัญหานี้และไม่ต้องออกไปพัฒนาการจัดการหน่วยความจำโดยรวม

หลีกเลี่ยงการใช้พอยน์เตอร์ที่เป็นอิสระแล้ว

เขียนโดยมาร์ติน v. Lowisในคำตอบอื่น

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

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

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


1

ในขณะที่คุณมีทีมงานประกันคุณภาพให้ฉันเพิ่มประเด็นย่อยเกี่ยวกับ QA เครื่องมือ QA อัตโนมัติสำหรับ C จะตั้งค่าสถานะการมอบหมายให้แก่พอยน์เตอร์ที่เป็น "การมอบหมายที่ไร้ประโยชน์ptr " ตัวอย่างเช่น PC-lint / FlexeLint จาก Gimpel Software กล่าว tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

มีวิธีการเลือกระงับข้อความเพื่อให้คุณสามารถตอบสนองความต้องการทั้งสองด้านได้หากทีมของคุณตัดสินใจ


1

ขอแนะนำให้ประกาศตัวแปรตัวชี้ด้วยNULLเช่น,

int *ptr = NULL;

สมมุติว่าptrชี้ไปยังที่อยู่หน่วยความจำ0x1000 หลังจากที่ใช้free(ptr)ก็มักจะแนะนำให้ลบล้างตัวแปรชี้ด้วยการประกาศอีกครั้งเพื่อเป็นโมฆะ เช่น:

free(ptr);
ptr = NULL;

ถ้าไม่ได้อีกครั้งประกาศให้เป็นโมฆะตัวแปรชี้ยังช่วยในการชี้ไปยังที่อยู่เดียวกัน ( 0x1000 ) ตัวแปรชี้นี้เรียกว่าตัวชี้ห้อยต่องแต่ง หากคุณกำหนดตัวแปรพอยน์เตอร์ตัวอื่น (สมมติว่าq ) และจัดสรรที่อยู่ให้กับตัวชี้ใหม่แบบไดนามิกอาจมีโอกาสที่จะใช้ที่อยู่เดียวกัน ( 0x1000 ) โดยตัวแปรตัวชี้ใหม่ หากในกรณีที่คุณใช้ตัวชี้ ( ptr ) เดียวกันและอัปเดตค่าตามที่อยู่ที่ชี้โดยตัวชี้เดียวกัน ( ptr ) จากนั้นโปรแกรมจะสิ้นสุดการเขียนค่าไปยังตำแหน่งที่qชี้ (ตั้งแต่pและqคือ ชี้ไปยังที่อยู่เดียวกัน (0x1000 ))

เช่น

*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.

1

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

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

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

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