ฉันส่งผลลัพธ์ของ malloc หรือไม่

Mar 03 2009

ในคำถามนี้มีคนแนะนำในความคิดเห็นว่าฉันไม่ควรทิ้งผลลัพธ์mallocเช่น

int *sieve = malloc(sizeof(int) * length);

ค่อนข้างมากกว่า:

int *sieve = (int *) malloc(sizeof(int) * length);

ทำไมถึงเป็นเช่นนี้?

คำตอบ

2285 unwind Mar 03 2009 at 17:17

ไม่ ; คุณไม่ได้ส่งผลลัพธ์เนื่องจาก:

  • ไม่จำเป็นเนื่องจากvoid *จะเลื่อนไปเป็นประเภทตัวชี้อื่น ๆ โดยอัตโนมัติและปลอดภัยในกรณีนี้
  • มันเพิ่มความยุ่งเหยิงให้กับโค้ดทำให้การแคสต์ไม่ง่ายต่อการอ่าน
  • มันทำให้คุณทำซ้ำตัวเองซึ่งโดยทั่วไปไม่ดี
  • <stdlib.h>มันสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่ สิ่งนี้อาจทำให้เกิดข้อขัดข้อง (หรือแย่กว่านั้นคือไม่ทำให้เกิดความผิดพลาดจนกว่าจะถึงเวลาต่อมาในส่วนที่แตกต่างกันโดยสิ้นเชิงของโค้ด) พิจารณาว่าจะเกิดอะไรขึ้นหากพอยน์เตอร์และจำนวนเต็มมีขนาดต่างกัน จากนั้นคุณจะซ่อนคำเตือนโดยการส่งและอาจสูญเสียที่อยู่ที่ส่งคืนไป หมายเหตุ: เป็นฟังก์ชั่นโดยปริยาย C99 จะหายไปจาก C intและจุดนี้จะไม่เกี่ยวข้องเนื่องจากไม่มีสมมติฐานอัตโนมัติที่ฟังก์ชั่นที่ไม่ได้ประกาศกลับ

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

โปรดทราบว่าตามที่ผู้วิจารณ์ชี้ให้เห็นว่าข้างต้นพูดถึง C ตรงไม่ใช่ C ++ ฉันเชื่อมั่นในภาษา C และ C ++ เป็นภาษาที่แยกจากกัน

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

int *sieve = malloc(length * sizeof *sieve);

นอกจากนี้ยังเลื่อนlengthไปด้านหน้าเพื่อเพิ่มการมองเห็นและลดวงเล็บที่ซ้ำซ้อนด้วยsizeof; พวกเขามีความจำเป็นเท่านั้นเมื่ออาร์กิวเมนต์เป็นชื่อชนิด หลายคนดูเหมือนจะไม่รู้ (หรือไม่สนใจ) สิ่งนี้ซึ่งทำให้โค้ดของพวกเขามีรายละเอียดมากขึ้น จำไว้ว่า: sizeofไม่ใช่ฟังก์ชัน! :)


ในขณะที่ย้ายlengthไปด้านหน้าอาจทำให้การมองเห็นเพิ่มขึ้นในบางกรณีที่หายาก แต่เราควรใส่ใจด้วยว่าในกรณีทั่วไปควรเขียนนิพจน์เป็น:

int *sieve = malloc(sizeof *sieve * length);

ตั้งแต่การรักษาsizeofครั้งแรกในกรณีนี้เพื่อให้แน่ใจคูณจะทำอย่างน้อยsize_tคณิตศาสตร์

เปรียบเทียบ: malloc(sizeof *sieve * length * width)เมื่อเทียบกับmalloc(length * width * sizeof *sieve)ครั้งที่สองอาจล้นlength * widthเมื่อwidthและเป็นชนิดที่มีขนาดเล็กกว่าlengthsize_t

387 dirkgently Mar 03 2009 at 17:17

ใน C คุณไม่จำเป็นต้องร่ายค่าส่งคืนของmalloc. ตัวชี้เป็นโมฆะที่ส่งคืนโดยmallocจะถูกแปลงเป็นประเภทที่ถูกต้องโดยอัตโนมัติ อย่างไรก็ตามหากคุณต้องการให้โค้ดของคุณรวบรวมด้วยคอมไพเลอร์ C ++ จำเป็นต้องใช้แคสต์ ทางเลือกที่ต้องการในชุมชนคือการใช้สิ่งต่อไปนี้:

int *sieve = malloc(sizeof *sieve * length);

ซึ่งช่วยให้คุณไม่ต้องกังวลกับการเปลี่ยนทางด้านขวามือของนิพจน์หากคุณเคยเปลี่ยนประเภทของsieve.

แคสต์ไม่ดีอย่างที่ผู้คนชี้ให้เห็น โดยเฉพาะตัวชี้หล่อ

360 RonBurk Feb 14 2013 at 23:15

คุณไม่หล่อเพราะ:

  • ทำให้โค้ดของคุณพกพาได้มากขึ้นระหว่าง C และ C ++ และจากประสบการณ์ SO แสดงให้เห็นว่าโปรแกรมเมอร์จำนวนมากอ้างว่าพวกเขากำลังเขียนเป็นภาษา C เมื่อพวกเขาเขียนด้วย C ++ (หรือ C บวกส่วนขยายของคอมไพเลอร์ในเครื่อง)
  • หากไม่ทำเช่นนั้นอาจซ่อนข้อผิดพลาดได้โปรดสังเกตตัวอย่าง SO ทั้งหมดของความสับสนว่าจะเขียนtype *เมื่อtype **ใดเทียบกับ
  • ความคิดที่ว่าจะช่วยให้คุณจากการสังเกตเห็นคุณล้มเหลวไปยัง#includeไฟล์ส่วนหัวที่เหมาะสมบอลเฉียงป่าต้นไม้ มันเหมือนกับการพูดว่า "อย่ากังวลกับความจริงที่คุณล้มเหลวในการขอให้คอมไพเลอร์บ่นว่าไม่เห็นต้นแบบ - stdlib.h ที่น่ารำคาญคือสิ่งสำคัญที่ต้องจำอย่างแท้จริง!"
  • มันกองกำลังพิเศษทางปัญญาข้ามการตรวจสอบ มันวางประเภทที่ต้องการ (ที่ถูกกล่าวหา) ไว้ข้างเลขคณิตที่คุณกำลังทำสำหรับขนาดดิบของตัวแปรนั้น ฉันพนันได้เลยว่าคุณสามารถทำการศึกษา SO ที่แสดงให้เห็นว่าmalloc()บั๊กถูกจับได้เร็วขึ้นมากเมื่อมีการร่าย เช่นเดียวกับการยืนยันคำอธิบายประกอบที่แสดงเจตนาลดจุดบกพร่อง
  • การทำซ้ำตัวเองด้วยวิธีที่เครื่องสามารถตรวจสอบได้มักเป็นความคิดที่ดี ในความเป็นจริงนั่นคือสิ่งที่ยืนยันได้และการใช้นักแสดงนี้เป็นการยืนยัน การยืนยันยังคงเป็นเทคนิคทั่วไปส่วนใหญ่ที่เรามีในการทำให้รหัสถูกต้องเนื่องจากทัวริงคิดแนวคิดนี้ขึ้นเมื่อหลายปีก่อน
174 quinmars Mar 03 2009 at 18:17

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

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

ด้วยวิธีนี้คุณยังสามารถเขียนได้อย่างกะทัดรัด:

int *sieve = NEW(int, 1);

และจะคอมไพล์สำหรับ C และ C ++

146 ashiquzzaman33 Oct 10 2015 at 04:23

จากWikipedia :

ข้อดีของการหล่อ

  • รวมถึงการแคสต์อาจอนุญาตให้โปรแกรม C หรือฟังก์ชันคอมไพล์เป็น C ++

  • นักแสดงอนุญาตให้ใช้ malloc เวอร์ชันก่อนปี 1989 ซึ่งเดิมส่งคืนถ่าน *

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

ข้อเสียในการหล่อ

  • ภายใต้มาตรฐาน ANSI C นักแสดงซ้ำซ้อน

  • การเพิ่มนักแสดงอาจปกปิดความล้มเหลวในการรวมส่วนหัวstdlib.hซึ่งพบต้นแบบสำหรับ malloc ในกรณีที่ไม่มีต้นแบบสำหรับ malloc มาตรฐานกำหนดให้คอมไพเลอร์ C ถือว่า malloc ส่งคืน int หากไม่มีการแคสต์จะมีการแจ้งเตือนเมื่อจำนวนเต็มนี้ถูกกำหนดให้กับตัวชี้ อย่างไรก็ตามด้วยนักแสดงคำเตือนนี้จะไม่ถูกสร้างขึ้นโดยซ่อนจุดบกพร่องไว้ ในสถาปัตยกรรมและโมเดลข้อมูลบางอย่าง (เช่น LP64 บนระบบ 64 บิตโดยที่ long และพอยน์เตอร์เป็น 64 บิตและ int คือ 32 บิต) ข้อผิดพลาดนี้อาจส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดได้เนื่องจาก malloc ที่ประกาศโดยปริยายจะส่งกลับ 32- ค่าบิตในขณะที่ฟังก์ชันที่กำหนดจริงส่งคืนค่า 64 บิต ขึ้นอยู่กับรูปแบบการเรียกใช้และรูปแบบหน่วยความจำซึ่งอาจส่งผลให้เกิดการซ้อนทับ ปัญหานี้มีโอกาสน้อยที่จะไม่มีใครสังเกตเห็นในคอมไพเลอร์สมัยใหม่เนื่องจากพวกเขาสร้างคำเตือนอย่างสม่ำเสมอว่ามีการใช้ฟังก์ชันที่ไม่ได้ประกาศดังนั้นคำเตือนจะยังคงปรากฏขึ้น ตัวอย่างเช่นพฤติกรรมเริ่มต้นของ GCC คือการแสดงคำเตือนที่อ่าน "การประกาศฟังก์ชันในตัวโดยปริยายที่เข้ากันไม่ได้" ไม่ว่าจะมีการแคสต์อยู่หรือไม่ก็ตาม

  • หากประเภทของตัวชี้มีการเปลี่ยนแปลงในการประกาศอาจต้องเปลี่ยนบรรทัดทั้งหมดที่มีการเรียกและส่ง malloc

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

เช่น: ถ้าคุณต้องการที่จะรวบรวมโปรแกรม C เป็น C ++ (แม้ว่ามันจะเป็นภาษาที่แยกต่างหาก) mallocคุณต้องโยนผลมาจากการใช้งาน

108 PaulJWilliams Mar 03 2009 at 17:18

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

104 Lundin Mar 20 2014 at 22:53

คุณไม่ได้ส่งผลลัพธ์mallocเนื่องจากการทำเช่นนั้นจะเพิ่มความยุ่งเหยิงอย่างไร้จุดหมายให้กับโค้ดของคุณ

สาเหตุที่พบบ่อยที่สุดที่ผู้คนใช้ผลลัพธ์mallocก็เพราะพวกเขาไม่แน่ใจว่าภาษาซีทำงานอย่างไร นั่นเป็นสัญญาณเตือน: หากคุณไม่รู้ว่ากลไกของภาษานั้นทำงานอย่างไรอย่าเพิ่งเดา ค้นหาหรือถามใน Stack Overflow

ความคิดเห็นบางส่วน:

  • ตัวชี้ที่เป็นโมฆะสามารถแปลงเป็น / จากตัวชี้ประเภทอื่น ๆ ได้โดยไม่ต้องมีการร่ายอย่างชัดเจน (C11 6.3.2.3 และ 6.5.16.1)

  • อย่างไรก็ตาม C ++ จะไม่อนุญาตให้มีการส่งโดยนัยระหว่างvoid*และประเภทตัวชี้อื่น ดังนั้นใน C ++ การแคสต์จะถูกต้อง แต่ถ้าคุณเขียนโปรแกรมใน C ++ คุณควรใช้และไม่ได้new malloc()และคุณไม่ควรคอมไพล์โค้ด C โดยใช้คอมไพเลอร์ C ++

    หากคุณต้องการสนับสนุนทั้ง C และ C ++ ด้วยซอร์สโค้ดเดียวกันให้ใช้สวิตช์คอมไพเลอร์เพื่อทำเครื่องหมายความแตกต่าง อย่าพยายามปรับมาตรฐานภาษาทั้งสองด้วยรหัสเดียวกันเพราะไม่สามารถใช้ร่วมกันได้

  • หากคอมไพลเลอร์ C ไม่พบฟังก์ชันเนื่องจากคุณลืมใส่ส่วนหัวคุณจะได้รับข้อผิดพลาดเกี่ยวกับคอมไพเลอร์ / ลิงค์เกอร์ ดังนั้นหากคุณลืมใส่<stdlib.h>ว่าไม่ใช่เรื่องใหญ่คุณจะไม่สามารถสร้างโปรแกรมของคุณได้

  • ในคอมไพเลอร์โบราณที่เป็นไปตามเวอร์ชันของมาตรฐานซึ่งมีอายุมากกว่า 25 ปีการลืมใส่<stdlib.h>อาจทำให้เกิดพฤติกรรมที่เป็นอันตรายได้ intเพราะในมาตรฐานโบราณที่ฟังก์ชั่นโดยไม่ต้องเป็นต้นแบบที่มองเห็นได้โดยปริยายแปลงชนิดกลับไป การส่งผลลัพธ์mallocอย่างชัดเจนจะเป็นการซ่อนจุดบกพร่องนี้

    แต่นั่นเป็นเรื่องที่ไม่ใช่ประเด็น คุณไม่ได้ใช้คอมพิวเตอร์อายุ 25 ปีแล้วทำไมคุณถึงใช้คอมไพเลอร์อายุ 25 ปี?

95 EFraim Mar 03 2009 at 17:16

ใน C คุณจะได้รับการแปลงโดยนัยจากvoid *ตัวชี้ (ข้อมูล) อื่น ๆ

72 YuHao Jun 10 2013 at 00:31

malloc()ไม่จำเป็นต้องส่งค่าที่ส่งคืนมาในตอนนี้ แต่ฉันต้องการเพิ่มจุดหนึ่งที่ดูเหมือนว่าไม่มีใครชี้ให้เห็น:

ในสมัยโบราณกล่าวคือก่อนที่ANSI Cจะให้พอยน์เตอร์void *ประเภททั่วไปchar *เป็นประเภทสำหรับการใช้งานดังกล่าว ในกรณีนี้นักแสดงสามารถปิดคำเตือนของคอมไพเลอร์ได้

อ้างอิง: C FAQ

55 user3079666 Mar 29 2014 at 01:21

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

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

53 user968000 Feb 08 2013 at 05:22

ไม่บังคับให้ส่งผลลัพธ์ของผลลัพธ์mallocเนื่องจากจะส่งคืนvoid*และvoid*สามารถชี้ไปยังประเภทข้อมูลใดก็ได้

37 Slothworks Oct 10 2015 at 00:47

นี่คือสิ่งที่คู่มืออ้างอิงไลบรารี GNU Cกล่าวว่า:

คุณสามารถจัดเก็บผลลัพธ์ของmallocตัวแปรพอยน์เตอร์ใด ๆ โดยไม่ต้องแคสต์เนื่องจาก ISO C จะแปลงประเภทvoid *เป็นตัวชี้ประเภทอื่นโดยอัตโนมัติเมื่อจำเป็น แต่การแคสต์เป็นสิ่งที่จำเป็นในบริบทอื่นที่ไม่ใช่ตัวดำเนินการกำหนดหรือหากคุณอาจต้องการให้โค้ดของคุณทำงานในรูปแบบ C แบบดั้งเดิม

และแน่นอนว่ามาตรฐาน ISO C11 (p347) กล่าวว่า:

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

35 Endeavour Jul 13 2014 at 13:42

ตัวชี้โมฆะเป็นตัวชี้ออบเจ็กต์ทั่วไปและ C รองรับการแปลงโดยนัยจากประเภทตัวชี้ที่เป็นโมฆะเป็นประเภทอื่น ๆ ดังนั้นจึงไม่จำเป็นต้องพิมพ์อย่างชัดเจน

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

30 swaps Apr 26 2013 at 23:43

ประเภทที่ส่งคืนเป็นโมฆะ * ซึ่งสามารถส่งไปยังชนิดของตัวชี้ข้อมูลที่ต้องการเพื่อให้สามารถหักล้างได้

29 Jeyamaran Mar 17 2014 at 20:16

ขึ้นอยู่กับภาษาโปรแกรมและคอมไพเลอร์ หากคุณใช้mallocในภาษา C ไม่จำเป็นต้องพิมพ์ร่ายเพราะมันจะพิมพ์ร่ายโดยอัตโนมัติ อย่างไรก็ตามหากคุณใช้ C ++ คุณควรพิมพ์ cast เพราะmallocจะส่งกลับvoid*ประเภท

28 AugustKarlstrom Sep 04 2015 at 18:52

ในภาษา C คุณสามารถกำหนดตัวชี้โมฆะให้กับตัวชี้ใดก็ได้ซึ่งเป็นเหตุผลว่าทำไมคุณจึงไม่ควรใช้ประเภทแคสต์ หากคุณต้องการการจัดสรรแบบ "ปลอดภัย" ฉันสามารถแนะนำฟังก์ชันมาโครต่อไปนี้ซึ่งฉันมักจะใช้ในโครงการ C ของฉัน:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

ด้วยสิ่งเหล่านี้คุณสามารถพูดได้ง่ายๆ

NEW_ARRAY(sieve, length);

สำหรับอาร์เรย์ที่ไม่ใช่ไดนามิกมาโครฟังก์ชันที่ต้องมีตัวที่สามคือ

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

ซึ่งทำให้อาร์เรย์ลูปปลอดภัยและสะดวกยิ่งขึ้น:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}
18 StephenG Dec 05 2015 at 00:27

คนเคย GCC และ Clang นิสัยเสีย มันไม่ได้ดีแค่นั้น

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

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

ข้อโต้แย้งที่ว่าไม่จำเป็นภายใต้มาตรฐานปัจจุบันนั้นค่อนข้างถูกต้อง แต่การโต้แย้งนั้นไม่ได้นำไปปฏิบัติในโลกแห่งความเป็นจริง เราไม่ได้เขียนโค้ดในโลกที่ปกครองโดยมาตรฐานของวันนี้โดยเฉพาะ แต่โดยการใช้งานจริงของสิ่งที่ฉันชอบเรียกว่า "สนามแห่งความเป็นจริงของผู้บริหารท้องถิ่น" และมันงอและบิดมากกว่าเวลาอวกาศ :-)

YMMV.

ฉันมักจะคิดว่าการแคสต์ Malloc เป็นการป้องกัน ไม่สวยไม่สมบูรณ์ แต่โดยทั่วไปปลอดภัย (สุจริตถ้าคุณไม่รวม stdlib.h แล้วคุณได้วิธีปัญหามากกว่าหล่อ malloc!)

16 Noname Aug 16 2017 at 19:25

ไม่คุณไม่ได้ส่งผลของmalloc().

โดยทั่วไปแล้วคุณไม่ได้โยนไปยังหรือจากvoid *

เหตุผลทั่วไปสำหรับการไม่ทำเช่นนั้นคือความล้มเหลวที่#include <stdlib.h>จะไม่มีใครสังเกตเห็น นี่ไม่ใช่ปัญหาอีกต่อไปแล้วเนื่องจาก C99 ทำให้การประกาศฟังก์ชันโดยปริยายผิดกฎหมายดังนั้นหากคอมไพเลอร์ของคุณสอดคล้องกับอย่างน้อย C99 คุณจะได้รับข้อความวินิจฉัย

แต่มีเหตุผลที่ดีกว่ามากที่จะไม่แนะนำตัวชี้ที่ไม่จำเป็น:

ใน C เป็นนักแสดงตัวชี้มักจะมีข้อผิดพลาด นี่เป็นเพราะกฎต่อไปนี้ ( §6.5 p7ใน N1570 ร่างล่าสุดสำหรับ C11):

วัตถุจะมีค่าที่เก็บไว้มันเข้าถึงได้โดยเฉพาะการแสดงออก lvalue ที่มีประเภทใดประเภทหนึ่งต่อไปนี้:
- ชนิดเข้ากันได้กับชนิดที่มีประสิทธิภาพของวัตถุ
- รุ่นที่มีคุณสมบัติเหมาะสมของรูปแบบเข้ากันได้กับชนิดที่มีประสิทธิภาพของวัตถุ
- ประเภทที่เป็นประเภทที่ลงนามหรือไม่ได้ลงนามที่สอดคล้องกับประเภทที่มีประสิทธิภาพของวัตถุ
- ประเภทที่ลงนามหรือประเภทที่ไม่ได้ลงนามที่สอดคล้องกับรุ่นที่มีคุณสมบัติเหมาะสมของประเภทที่มีประสิทธิภาพของวัตถุ
- ประเภทการรวมหรือการรวมที่มีหนึ่ง ของประเภทดังกล่าวในหมู่สมาชิก (รวมถึงเรียกซ้ำสมาชิกของกลุ่มย่อยหรือสหภาพที่มีอยู่) หรือ
- ประเภทอักขระ

นอกจากนี้ยังเป็นที่รู้จักกันเป็นกฎ aliasing เข้มงวด ดังนั้นรหัสต่อไปนี้คือพฤติกรรมที่ไม่ได้กำหนด :

long x = 5;
double *p = (double *)&x;
double y = *p;

และบางครั้งก็น่าแปลกใจที่มีสิ่งต่อไปนี้เช่นกัน:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

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

tl; dr

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


หมายเหตุด้านข้าง:

  • มีหลายกรณีที่คุณเป็นจริงต้องโยนไปvoid *เช่นถ้าคุณต้องการที่จะพิมพ์ตัวชี้:

    int x = 5;
    printf("%p\n", (void *)&x);
    

    การแคสต์มีความจำเป็นที่นี่เนื่องจากprintf()เป็นฟังก์ชันแบบแปรผันดังนั้นการแปลงโดยนัยจึงไม่ทำงาน

  • ใน C ++ สถานการณ์จะแตกต่างกัน ประเภทตัวชี้การหล่อเป็นเรื่องปกติ (และถูกต้อง) เมื่อจัดการกับวัตถุของคลาสที่ได้รับ ดังนั้นจึงสมเหตุสมผลที่ใน C ++ การแปลงเป็นและจากไม่ได้void *เป็นนัย C ++ มีรสชาติที่แตกต่างกันทั้งชุด

15 Kaz Mar 30 2016 at 07:23

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

double d;
void *p = &d;
int *q = p;

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

ในความเป็นจริงแนวปฏิบัติที่ดีคือการรวมmalloc(และเพื่อน) ด้วยฟังก์ชันที่ส่งคืนunsigned char *และโดยทั่วไปจะไม่ใช้void *ในโค้ดของคุณ หากคุณต้องการตัวชี้ทั่วไปไปยังวัตถุใด ๆ ให้ใช้char *หรือunsigned char *และมีการร่ายทั้งสองทิศทาง การพักผ่อนอย่างหนึ่งที่สามารถผ่อนคลายได้ก็คือการใช้ฟังก์ชันที่เหมือนmemsetและmemcpyไม่มีการร่าย

ในหัวข้อการแคสต์และความเข้ากันได้ของ C ++ หากคุณเขียนโค้ดของคุณเพื่อให้คอมไพล์เป็นทั้ง C และ C ++ (ซึ่งในกรณีนี้คุณต้องส่งค่าส่งคืนmallocเมื่อกำหนดให้กับสิ่งอื่นที่ไม่ใช่void *) คุณสามารถทำประโยชน์ได้มาก สำหรับตัวคุณเอง: คุณสามารถใช้มาโครในการแคสต์ซึ่งแปลเป็นสไตล์ C ++ เมื่อคอมไพล์เป็น C ++ แต่ลดเป็น C cast เมื่อคอมไพล์เป็น C:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

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

จากนั้นต่อไปหากคุณคอมไพล์โค้ดด้วย C ++ เป็นประจำโค้ดจะบังคับใช้การแคสต์ที่เหมาะสม ตัวอย่างเช่นหากคุณใช้strip_qualเพียงเพื่อลบconstหรือvolatileแต่โปรแกรมเปลี่ยนไปในลักษณะที่เกี่ยวข้องกับการแปลงประเภทในขณะนี้คุณจะได้รับการวินิจฉัยและคุณจะต้องใช้การแคสผสมกันเพื่อให้ได้ Conversion ที่ต้องการ

เพื่อช่วยให้คุณปฏิบัติตามมาโครเหล่านี้คอมไพเลอร์ GNU C ++ (ไม่ใช่ C!) มีคุณสมบัติที่สวยงามนั่นคือการวินิจฉัยที่เป็นทางเลือกซึ่งสร้างขึ้นสำหรับการแคสต์สไตล์ C ทั้งหมด

     -Wold-style-cast (C ++ และ Objective-C ++ เท่านั้น)
         เตือนหากใช้การร่ายแบบเก่า (สไตล์ C) ไปยังประเภทที่ไม่ใช่โมฆะ
         ภายในโปรแกรม C ++ แคสต์รูปแบบใหม่ (dynamic_cast,
         static_cast, reinterpret_cast และ const_cast) มีความเสี่ยงน้อยกว่า
         ไปจนถึงเอฟเฟกต์ที่ไม่ได้ตั้งใจและค้นหาได้ง่ายขึ้นมาก

หากโค้ด C ของคุณรวบรวมเป็น C ++ คุณสามารถใช้-Wold-style-castตัวเลือกนี้เพื่อค้นหาการเกิดขึ้นทั้งหมดของ(type)ไวยากรณ์การแคสต์ที่อาจเล็ดลอดเข้าไปในโค้ดและติดตามการวินิจฉัยเหล่านี้โดยแทนที่ด้วยตัวเลือกที่เหมาะสมจากมาโครด้านบน (หรือ a รวมกันถ้าจำเป็น)

การรักษาของการแปลงนี้เป็นที่ใหญ่ที่สุดแบบสแตนด์อโลนเหตุผลทางเทคนิคเดียวสำหรับการทำงานใน "Clean C": รวม C และ C ++ ภาษาซึ่งในทางกลับกันในทางเทคนิค justifies mallocหล่อค่าตอบแทนของ

15 proski Jun 29 2016 at 14:30

ฉันชอบทำการแคสต์มากกว่า แต่ไม่ใช่ด้วยตนเอง สิ่งที่ฉันชอบคือการใช้g_newและg_new0มาโครจากคนกะล่อน ถ้าไม่ใช้ glib ฉันจะเพิ่มมาโครที่คล้ายกัน มาโครเหล่านี้ช่วยลดการทำซ้ำรหัสโดยไม่ลดทอนความปลอดภัยของประเภท หากคุณพิมพ์ผิดคุณจะได้รับการโยนโดยนัยระหว่างพอยน์เตอร์ที่ไม่ใช่โมฆะซึ่งจะทำให้เกิดคำเตือน (ข้อผิดพลาดใน C ++) หากคุณลืมใส่ส่วนหัวที่กำหนดg_newและg_new0คุณจะได้รับข้อผิดพลาด g_newและg_new0ทั้งสองใช้เวลาขัดแย้งกันซึ่งแตกต่างจากที่ใช้เวลาน้อยกว่าการขัดแย้งmalloc callocเพียงเพิ่ม0เพื่อรับหน่วยความจำเริ่มต้นเป็นศูนย์ สามารถคอมไพล์โค้ดด้วยคอมไพเลอร์ C ++ โดยไม่มีการเปลี่ยนแปลง

14 user877329 Jun 12 2015 at 22:23

สิ่งที่ดีที่สุดที่ควรทำเมื่อเขียนโปรแกรมด้วยภาษา C เมื่อใดก็ตามที่ทำได้:

  1. ทำให้โปรแกรมของคุณคอมไพล์ผ่านคอมไพเลอร์ C โดยเปิดคำเตือนทั้งหมด-Wallและแก้ไขข้อผิดพลาดและคำเตือนทั้งหมด
  2. ตรวจสอบให้แน่ใจว่าไม่มีตัวแปรที่ประกาศเป็น auto
  3. จากนั้นคอมไพล์โดยใช้คอมไพเลอร์ C ++ พร้อม-Wallและ-std=c++11. แก้ไขข้อผิดพลาดและคำเตือนทั้งหมด
  4. ตอนนี้รวบรวมโดยใช้คอมไพเลอร์ C อีกครั้ง ตอนนี้โปรแกรมของคุณควรคอมไพล์โดยไม่มีการเตือนใด ๆ และมีจุดบกพร่องน้อยลง

ขั้นตอนนี้ช่วยให้คุณใช้ประโยชน์จากการตรวจสอบประเภท C ++ ที่เข้มงวดซึ่งจะช่วยลดจำนวนข้อบกพร่อง โดยเฉพาะอย่างยิ่งขั้นตอนนี้บังคับให้คุณรวมstdlib.hหรือคุณจะได้รับ

malloc ไม่ได้ประกาศภายในขอบเขตนี้

และยังบังคับให้คุณส่งผลmallocหรือคุณจะได้รับ

การแปลงไม่ถูกต้องจากvoid*เป็นT*

หรือประเภทเป้าหมายของคุณคืออะไร

ประโยชน์เพียงอย่างเดียวจากการเขียนใน C แทน C ++ ที่ฉันสามารถหาได้คือ

  1. C มี ABI ที่ระบุไว้อย่างดี
  2. C ++ อาจสร้างโค้ดเพิ่มเติม [ข้อยกเว้น, RTTI, เทมเพลต, ความหลากหลายของรันไทม์ ]

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

สำหรับผู้ที่พบว่ากฎที่เข้มงวดของ C ++ ไม่สะดวกเราสามารถใช้คุณลักษณะ C ++ 11 กับประเภทที่อนุมานได้

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
12 Noname Sep 10 2015 at 22:58

การแคสต์มีไว้สำหรับ C ++ เท่านั้นไม่ใช่ C ในกรณีที่คุณใช้คอมไพเลอร์ C ++ คุณควรเปลี่ยนเป็นคอมไพเลอร์ C

10 Aashish Oct 03 2017 at 20:13

การแคสต์ malloc ไม่จำเป็นใน C แต่จำเป็นต้องมีใน C ++

การแคสต์ไม่จำเป็นใน C เนื่องจาก:

  • void * ได้รับการเลื่อนตำแหน่งเป็นตัวชี้ชนิดอื่นโดยอัตโนมัติและปลอดภัยในกรณีของ C.
  • <stdlib.h>มันสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่ ซึ่งอาจทำให้เกิดข้อขัดข้อง
  • หากพอยน์เตอร์และจำนวนเต็มมีขนาดแตกต่างกันแสดงว่าคุณกำลังซ่อนคำเตือนโดยการแคสต์และอาจสูญเสียที่อยู่ที่ส่งคืนไป
  • หากชนิดของตัวชี้มีการเปลี่ยนแปลงเมื่อประกาศอาจต้องเปลี่ยนบรรทัดทั้งหมดที่mallocเรียกและส่ง

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

9 iec2011007 Jul 29 2015 at 12:53

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

9 dhanagovindarajan Jul 18 2016 at 17:36

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

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

9 Mohit Nov 08 2016 at 17:09
  1. ตามที่ระบุไว้อื่น ๆ ไม่จำเป็นสำหรับ C แต่สำหรับ C ++

  2. รวมถึงการแคสต์อาจอนุญาตให้โปรแกรม C หรือฟังก์ชันคอมไพล์เป็น C ++

  3. ในภาษา C นั้นไม่จำเป็นเนื่องจากโมฆะ * จะถูกเลื่อนไปเป็นประเภทตัวชี้อื่น ๆ โดยอัตโนมัติและปลอดภัย

  4. แต่ถ้าคุณโยนแล้วว่าสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่stdlib.h สิ่งนี้อาจทำให้เกิดข้อขัดข้อง (หรือแย่กว่านั้นคือไม่ทำให้เกิดข้อขัดข้องจนกว่าจะถึงวิธีในภายหลังในส่วนที่แตกต่างกันทั้งหมดของโค้ด)

    เนื่องจากพบstdlib.hต้นแบบสำหรับ malloc ในกรณีที่ไม่มีต้นแบบสำหรับ malloc มาตรฐานกำหนดให้คอมไพลเลอร์ C ถือว่า malloc ส่งคืน int หากไม่มีการแคสต์จะมีการแจ้งเตือนเมื่อจำนวนเต็มนี้ถูกกำหนดให้กับตัวชี้ อย่างไรก็ตามด้วยนักแสดงคำเตือนนี้จะไม่ถูกสร้างขึ้นโดยซ่อนจุดบกพร่องไว้

4 RobertSsupportsMonicaCellio Jun 13 2020 at 02:20

คำถามนี้มีการละเมิดตามความคิดเห็น

บางครั้งฉันสังเกตเห็นความคิดเห็นเช่นนั้น:

อย่าโยนผลลัพธ์ของ malloc

หรือ

ทำไมคุณไม่ส่งผลลัพธ์ของ malloc

ในคำถามที่ OP ใช้การแคสต์ ความคิดเห็นนั้นมีไฮเปอร์ลิงก์สำหรับคำถามนี้

ที่อยู่ในใด ๆลักษณะที่ไม่เหมาะสมและไม่ถูกต้องเช่นกัน ไม่มีถูกและไม่มีผิดเมื่อมันเป็นเรื่องของสไตล์การเข้ารหัสของตัวเองอย่างแท้จริง


เหตุใดจึงเกิดขึ้น

ขึ้นอยู่กับสองเหตุผล:

  1. คำถามนี้อิงตามความคิดเห็น ในทางเทคนิคคำถามควรถูกปิดไปแล้วเมื่อหลายปีก่อนตามความคิดเห็น คำถาม " Do I " หรือ " Don't I " หรือเทียบเท่ากับคำถาม" Should I " หรือ " Should I " คุณไม่สามารถตอบได้โดยไม่มีทัศนคติของความคิดเห็นของตัวเอง เหตุผลประการหนึ่งที่ต้องปิดคำถามเนื่องจาก "อาจนำไปสู่คำตอบตามความคิดเห็น" ดังที่แสดงไว้ที่นี่

  2. คำตอบที่หลายคน (รวมทั้งที่เห็นได้ชัดที่สุดและได้รับการยอมรับคำตอบของ@unwind ) มีทั้งสมบูรณ์หรือเกือบทั้งหมดความคิดเห็นตาม (fe ลึกลับ "ถ่วง" ที่จะถูกเพิ่มเข้าไปในรหัสของคุณถ้าคุณไม่หล่อหรือการทำซ้ำตัวเองจะไม่ดี) และการแสดง แนวโน้มที่ชัดเจนและมุ่งเน้นที่จะละเว้นนักแสดง พวกเขาให้เหตุผลเกี่ยวกับความซ้ำซ้อนของการโยนในด้านหนึ่ง แต่ยังและยิ่งเลวร้ายลงให้เหตุผลในการแก้ข้อผิดพลาดที่เกิดจากข้อผิดพลาด / ล้มเหลวของการเขียนโปรแกรมของตัวเอง - ไม่ถ้าใครต้องการที่จะใช้#include <stdlib.h>malloc()


ฉันต้องการนำเสนอมุมมองที่แท้จริงของบางประเด็นที่กล่าวถึงโดยมีความคิดเห็นส่วนตัวของฉันน้อยลง ต้องสังเกตบางประเด็นโดยเฉพาะ:

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

    คำตอบนี้มีภาพรวมข้อดีข้อเสียที่ดี:

    https://stackoverflow.com/a/33047365/12139179

    (โดยส่วนตัวแล้วฉันคิดว่าเพราะเหตุผลนั้นเป็นคำตอบที่ดีที่สุดจนถึงตอนนี้)


  1. สาเหตุหนึ่งที่พบมากที่สุดที่เป็นสาเหตุของการละเว้นของนักแสดงคือนักแสดงอาจซ่อนจุดบกพร่อง

    หากมีคนใช้นัยที่ประกาศmalloc()ว่าส่งคืนint(ฟังก์ชันโดยนัยหายไปจากมาตรฐานตั้งแต่ C99) และsizeof(int) != sizeof(int*)ดังที่แสดงในคำถามนี้

    เหตุใดโค้ดนี้จึง segfault บนสถาปัตยกรรม 64 บิต แต่ทำงานได้ดีบน 32 บิต

    นักแสดงจะซ่อนข้อบกพร่อง

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

    นี่จะไม่เป็นปัญหาร้ายแรงหากคุณ

    1. ใช้คอมไพเลอร์ที่สอดคล้องกับ C99 ขึ้นไป (ซึ่งแนะนำและควรเป็นข้อบังคับ) และ

    2. อย่าลืมใส่stdlib.hเมื่อคุณต้องการใช้malloc()ในโค้ดของคุณซึ่งเป็นจุดบกพร่องอย่างมาก


  1. บางคนโต้แย้งเกี่ยวกับการปฏิบัติตาม C ++ ของรหัส C เนื่องจากนักแสดงมีหน้าที่ใน C ++

    ก่อนอื่นต้องพูดโดยทั่วไป: การคอมไพล์โค้ด C ด้วยคอมไพเลอร์ C ++ ไม่ใช่แนวปฏิบัติที่ดี

    C และ C ++ เป็นภาษาที่แตกต่างกันอย่างสิ้นเชิงสองภาษาที่มีความหมายต่างกัน

    แต่ถ้าคุณต้องการ / จำเป็นต้องทำให้รหัส C สอดคล้องกับ C ++ และในทางกลับกันให้ใช้สวิตช์คอมไพเลอร์แทนการแคสต์ใด ๆ

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

    • https://stackoverflow.com/a/34094068/12139179

    • https://stackoverflow.com/a/36297486/12139179

    • https://stackoverflow.com/a/33044300/12139179


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

สรุป:

ความจริงก็คือการโยนซ้ำซ้อนตามมาตรฐาน C (แล้วตั้งแต่ ANSI-C (C89 / C90)) หากตัวชี้ที่กำหนดชี้ไปที่วัตถุที่มีข้อกำหนดการจัดตำแหน่งพื้นฐาน (ซึ่งรวมถึงวัตถุส่วนใหญ่ทั้งหมด)

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

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

ที่มา: C18, §7.22.3 / 1


"การจัดตำแหน่งพื้นฐานคือการจัดตำแหน่งที่ถูกต้องน้อยกว่าหรือเท่ากับการ_Alignof (max_align_t)จัดตำแหน่งพื้นฐานจะได้รับการสนับสนุนโดยการใช้งานสำหรับวัตถุในระยะเวลาการจัดเก็บทั้งหมดข้อกำหนดการจัดตำแหน่งประเภทต่อไปนี้จะต้องเป็นการจัดตำแหน่งพื้นฐาน:

- ประเภทพื้นฐานเกี่ยวกับปรมาณูที่มีคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด

- ประเภทการแจกแจงแบบปรมาณูคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด

- ประเภทตัวชี้อะตอมคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด

- อาร์เรย์ทุกประเภทที่ประเภทองค์ประกอบมีข้อกำหนดพื้นฐานในการจัดตำแหน่ง 57)

- ทุกประเภทที่ระบุในข้อ 7 เป็นประเภทออบเจ็กต์ที่สมบูรณ์

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

  1. ตามที่ระบุไว้ใน 6.2.1 การประกาศในภายหลังอาจซ่อนการประกาศก่อนหน้านี้ "

ที่มา: C18, §6.2.8 / 2

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

จัดตำแหน่งขยาย_Alignof (max_align_t)เป็นตัวแทนจากที่สูงกว่าการจัดตำแหน่ง มีการกำหนดการนำไปใช้งานว่ารองรับการจัดแนวเพิ่มเติมหรือไม่และระยะเวลาการจัดเก็บที่รองรับหรือไม่ ประเภทที่มีข้อกำหนดในการจัดตำแหน่งเพิ่มเติมเป็นประเภทที่จัดชิดเกิน 58)

ที่มา C18, §6.2.8 / 3

อย่างอื่นเป็นเรื่องของกรณีการใช้งานเฉพาะและความคิดเห็นของตัวเอง

โปรดใช้ความระมัดระวังในการให้ความรู้แก่ตนเอง

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

โปรดทราบ:

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


หมายเหตุสุดท้าย: ฉันโหวตให้ปิดคำถามนี้เมื่อเร็ว ๆ นี้โดยอิงตามความคิดเห็นซึ่งเป็นสิ่งที่จำเป็นมาตั้งแต่ปี หากคุณได้รับสิทธิ์ปิด / เปิดอีกครั้งฉันก็อยากเชิญให้คุณทำเช่นนั้นเช่นกัน

pasignature Mar 05 2020 at 22:56

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

อาจมีเหตุผลอื่น ๆ แต่เหตุผลอื่น ๆ ที่แน่นอนจะทำให้คุณตกอยู่ในปัญหาร้ายแรงไม่ช้าก็เร็ว

ivan.ukr May 22 2020 at 14:03

คุณทำได้ แต่ไม่จำเป็นต้องแคสต์เป็น C คุณต้องแคสต์หากโค้ดนั้นคอมไพล์เป็น C ++