ทำไมมูลค่าส่งคืนฟรีของนักแสดงจึงเป็นโมฆะ?


82

ฉันกำลังอ่านหนังสือ ( การเขียนโปรแกรมด้วย POSIX Threadsโดย Butenhof, 1997) ที่ใช้ C และฉันเจอบรรทัดต่อไปนี้:

(void)free(data);

นี่dataเป็นเพียงตัวชี้ไปยังโครงสร้างที่ปันส่วน

data = malloc(sizeof(my_struct_t));

ทำไมเป็นผลมาจากfreeการถูกโยนไปvoid?

จากความเข้าใจของฉันเกี่ยวกับ C สิ่งนี้ดูเหมือนจะไม่สมเหตุสมผลด้วยสองเหตุผล:

  • ฟังก์ชันฟรีส่งคืนแล้ว void
  • รหัสไม่ได้ใช้ค่าส่งคืน (มันไม่ได้ถูกกำหนดให้กับตัวแปร)

หนังสือเล่มนี้เขียนขึ้นในปี 1997 นี่เป็นเรื่องของมรดกหรือเปล่า

ผู้เขียนกล่าวว่าตัวอย่างทำงานบน Digital Unix 4.0d แต่ฉันก็ยังนึกภาพไม่ออกว่ามีเหตุผลอะไรที่จะส่งผลของฟังก์ชั่นหากคุณไม่ได้ใช้ผลลัพธ์นั้น


คำอธิบายที่เป็นไปได้บางส่วนสามารถดูได้ที่นี่: stackoverflow.com/questions/689677/…
Timbo

3
จากความอยากรู้วันที่ตีพิมพ์หนังสือ C ของคุณคืออะไร (หนังสือเล่มนี้คืออะไร?) ถ้าก่อนหน้านี้ประมาณปี 1995 อาจมีเหตุผลบางประการ - คอมไพเลอร์ C มาตรฐานไม่แพร่หลายก่อนหน้านี้ หากมีการเผยแพร่หลังจากนั้นและยังมีนักแสดง (และไม่มีคำอธิบายว่าทำไม) ให้กังวลเกี่ยวกับนิสัยที่ไม่ดีอื่น ๆ ที่สอนให้คุณ รับหนังสือเล่มใหม่กว่านี้!
Jonathan Leffler

10
ดูเหมือนว่าการเขียนโปรแกรมด้วย POSIX Threads
Eugene Sh

3
@JonathanLeffler ตามที่กล่าวไว้ในโพสต์ต้นฉบับของฉันหนังสือเล่มนี้ตีพิมพ์ในปี 1997 และใช้ UNIX 4.0d หนังสือเล่มนี้คือ "Programming with POSIX Threads" โดย David R. Butenhof จนถึงตอนนี้มันมีข้อมูลมากและเขียนโดยหนึ่งในผู้ให้ข้อมูลดั้งเดิมถึงมาตรฐาน POSIX เธรด
Adam Johnston

6
ฉันใช้สำเนาสิ่งนั้นในสัปดาห์ที่แล้วใช่แล้วมันยังมีประโยชน์ มันเขียนไว้ในปากของ 'ubiquitous standard C' (ฉันพูดว่า 'เกี่ยวกับ 1995') 'UNIX 4.0d' ดูเหมือน Digital UNIX - ซึ่งเป็นที่ที่ Butenhof ใช้งานได้และคำนำหน้าพูดถึงมัน ปฏิบัติต่อนักแสดงfree()เป็นเรื่องแปลกในหนังสือที่คุณไม่จำเป็นต้องเลียนแบบ มันมีความเกี่ยวข้องกึ่งนานมาแล้ว แต่มันไม่เกี่ยวข้องอีกต่อไป
Jonathan Leffler

คำตอบ:


100

หากเรากำลังพูดถึงfreeฟังก์ชั่นมาตรฐานแล้วต้นแบบของมันคือ

void free(void *ptr);

ดังนั้นนักแสดงจึงไร้ประโยชน์อย่างสมบูรณ์
ตอนนี้เก็งกำไรบ้าง

ผู้เขียนอาจจะลืมที่จะรวมถึงส่วนหัวประกาศต้นแบบนี้เพื่อให้คอมไพเลอร์คือสมมติพิมพ์กลับว่ามันเป็นstdlib.h intตอนนี้ในระหว่างการวิเคราะห์โค้ดนี้ผู้แปลได้เตือนเกี่ยวกับค่าส่งคืนที่ไม่ได้ใช้ในสิ่งที่คิดว่าเป็นvoidฟังก์ชันที่ไม่ใช่ฟังก์ชัน voidคำเตือนดังกล่าวมักจะเงียบโดยการเพิ่มการโยนไป


50
แต่ทราบว่าถ้าโยนได้รับการแนะนำสำหรับเหตุผลที่สันนิษฐานแล้วใช้มันเพื่อปิดเสียงเตือนที่ไม่ถูกต้อง คอมไพเลอร์จะเป็นสาเหตุของประเภทที่แตกต่างfreeจากที่มีจริงด้วยผลลัพธ์ที่การโทรมีพฤติกรรมที่ไม่ได้กำหนด (สมมติว่าความหมาย C90 ที่การเรียกฟังก์ชั่นที่ไม่ได้ประกาศไม่ได้แสดง UB โดยธรรมชาติในทุกกรณี) ในทางปฏิบัติมันเป็นไปได้ที่จะส่งผลให้สุจริตพฤติกรรมที่ไม่เหมาะสมในบางระบบ วิธีการแก้ไขที่ถูกต้องคือให้การประกาศที่ถูกต้องสำหรับฟังก์ชัน
John Bollinger

11
ตัวอย่างใน "การเขียนโปรแกรมด้วย POSIX เธรด" ซ้ำ ๆ ไม่สามารถรวมส่วนหัวมาตรฐานที่เกี่ยวข้อง บางทีนี่อาจเป็นแนวปฏิบัติที่ไม่ดีของผู้เขียนพวกเขาอาจใช้การตั้งค่าคอมไพเลอร์ที่ไม่ได้มาตรฐานซึ่งรวม libs มาตรฐานทั้งหมดเป็นค่าเริ่มต้น
Lundin

74

มันจะเป็นสิ่งที่สืบทอดมา!

ก่อนที่จะมีมาตรฐาน C free()ฟังก์ชั่นจะเป็นประเภท (โดยปริยาย) int- เพราะยังไม่มีประเภทที่เชื่อถือได้voidสำหรับการส่งคืน ไม่มีการส่งคืนค่า

เมื่อรหัสถูกแก้ไขครั้งแรกเพื่อทำงานกับคอมไพเลอร์ C มาตรฐานมันอาจจะไม่รวม<stdlib.h>(เพราะมันไม่ได้อยู่ก่อนมาตรฐาน) รหัสเดิมจะเขียนextern char *malloc();(อาจจะโดยไม่ต้องextern) สำหรับฟังก์ชั่นการจัดสรร (เช่นเดียวกันcalloc()และrealloc()) free()และไม่จำเป็นต้องประกาศ และรหัสก็จะส่งคืนค่ากลับไปเป็นประเภทที่ถูกต้อง - เพราะมันเป็นสิ่งที่จำเป็นในบางระบบ (รวมถึงระบบที่ฉันเรียนรู้ด้วย)

บางครั้งต่อมาตัว(void)ละครที่ถูกเพิ่มเข้ามาเพื่อบอกคอมไพเลอร์ (หรือมีแนวโน้มมากขึ้นlint) ว่า "ค่าตอบแทนจากfree()การถูกละเว้นโดยจงใจ" เพื่อหลีกเลี่ยงการร้องเรียน แต่มันจะเป็นการดีกว่าถ้าจะเพิ่ม<stdlib.h>และให้การประกาศextern void free(void *vp);แจ้งlintหรือคอมไพเลอร์ทราบว่าไม่มีค่าที่จะเพิกเฉย

JFTR: ย้อนกลับไปในช่วงกลางยุค 80, ICL Perq นั้น แต่เดิมบนสถาปัตยกรรมเชิงคำและที่char *อยู่สำหรับตำแหน่งหน่วยความจำนั้นแตกต่างจากตัวชี้ 'Anything_else' ไปยังตำแหน่งเดียวกัน มันเป็นเรื่องสำคัญที่จะต้องประกาศchar *malloc()อย่างใด มันสำคัญมากที่จะส่งผลลัพธ์จากประเภทตัวชี้อื่น ๆ นักแสดงเปลี่ยนจำนวนที่ CPU ใช้จริง (นอกจากนี้ยังมีความยินดีอย่างมากเมื่อหน่วยความจำหลักในระบบของเราได้รับการอัพเกรดจาก 1 MiB เป็น 2 MiB - เนื่องจากเคอร์เนลใช้ประมาณ 3/4 MiB หมายความว่าโปรแกรมผู้ใช้สามารถใช้ 1 1/4 MiB ก่อนการเพจเป็นต้น)


9
ฉันเพิ่งเปิดสำเนา K&R ฉบับที่ 1 ซึ่งมีการนำไปใช้free()ในวันที่ 177 intซึ่งผลตอบแทนโดยปริยาย
อดีต nihilo

9
แน่นอน - voidถูกเพิ่มในบางระบบ (Unix System III,) ก่อนที่จะมีการเผยแพร่มาตรฐาน แต่นั่นไม่ใช่ส่วนหนึ่งของ C เมื่อ K&R 1st Edn เขียน (1978) มีการประกาศฟังก์ชันที่ไม่ส่งคืนค่าโดยไม่มีประเภทส่งคืน (ซึ่งหมายความว่าส่งคืนint) และตราบใดที่คุณไม่ได้ใช้ค่าที่ไม่ได้ส่งคืนแสดงว่าไม่มีปัญหา มาตรฐาน C90 นั้นจะต้องปฏิบัติต่อโค้ดประเภทนั้นอย่างถูกต้อง - มันจะล้มเหลวอย่างน่าเสียดายเพราะไม่มีมาตรฐาน แต่ C99 ลบกฎ 'นัยint' และ 'ประกาศฟังก์ชันโดยนัย' ไม่ใช่ทุกรหัสในโลกที่ได้ตามทัน
Jonathan Leffler

5
Op อ้างว่าหนังสือเล่มนี้ถูกเขียนขึ้นในปี 1997 สิ่งที่คุณกำลังพูดถึงอยู่ที่นี่คือมาตรฐานขั้นต้น "K&R C" และดูเหมือนว่าไม่น่าที่ใครจะเขียนหนังสือเกี่ยวกับเรื่องนี้เลย หนังสือดังกล่าวเพียงเล่มเดียวที่มีอยู่ในความรู้ของฉันคือ K&R 1st edition
Lundin

มันเป็นวิธีปฏิบัติที่บ่อยครั้ง (ถ้าเป็นเพ้อฝัน) ที่จะไม่รวมส่วนหัวหากคุณคิดว่าคุณสามารถหลีกเลี่ยงการประกาศโดยปริยายเพราะผู้คนคิดว่ามันจะลดเวลาในการสร้าง
Spencer

ใครบ้างที่ใช้(void)นักแสดงเพื่อprintf()?
Luis Colorado

11

นักแสดงนี้ไม่ต้องการ มันอาจจะไม่ได้เป็นในขณะที่ C ได้รับมาตรฐานในรูปแบบของ C89

หากได้รับก็จะได้รับเนื่องจากการประกาศโดยปริยาย ซึ่งมักจะหมายความว่าคนที่เขียนรหัสลืม#include <stdlib.h>และมีการใช้ตัววิเคราะห์แบบคงที่ นี้ไม่ได้เป็นวิธีแก้ปัญหาที่ดีที่สุดและเป็นความคิดที่ดีมากจะได้รับเพียงแค่#include <stdlib.h>แทน นี่คือข้อความบางส่วนจาก C89 เกี่ยวกับการประกาศโดยนัย:

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

extern int identifier();

ปรากฏ

แต่นั่นก็แปลกเพราะพวกเขาไม่ได้แคสติ้งผลลัพธ์ของmallocทั้งสองและmallocและfreeอยู่ในไฟล์ส่วนหัวเดียวกัน

อาจเป็นไปได้ว่านี่เป็นเพียงความผิดพลาดหรือวิธีการบอกผู้อ่านที่freeไม่ส่งผลใด ๆ


5
เพียงเพราะภาษาได้รับมาตรฐานไม่ได้หมายความว่าทุกคนอัปเดต toolchain และรหัสของพวกเขาทันทีเพื่อให้สอดคล้องกับมัน เป็นไปได้สำหรับ "K&R" C แบบเก่าที่จะติดอยู่อีกประมาณ 8 ปี ถึงกระนั้นฉันยอมรับว่ามันแปลกที่เครื่องมือวิเคราะห์แบบคงที่จะต้องใช้การคำนวณfreemallocแต่ไม่ได้สำหรับ
dan04

4
@ dan04 คุณมักจะใช้ผลลัพธ์ของ malloc;) ฉันไม่มีแฟนของการเขียนสิ่งต่าง ๆ เช่น (void) printf (... ) เพื่อหยุดคอมไพล์คำเตือนการถ่มน้ำลายคอมไพเลอร์ แต่ "ต้องคอมไพล์โดยไม่มีคำเตือนแม้แต่คนโง่ ๆ " เกิดขึ้นในหลายโครงการ
richardb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.