ฉันส่งผลลัพธ์ของ malloc หรือไม่
ในคำถามนี้มีคนแนะนำในความคิดเห็นว่าฉันไม่ควรทิ้งผลลัพธ์malloc
เช่น
int *sieve = malloc(sizeof(int) * length);
ค่อนข้างมากกว่า:
int *sieve = (int *) malloc(sizeof(int) * length);
ทำไมถึงเป็นเช่นนี้?
คำตอบ
ไม่ ; คุณไม่ได้ส่งผลลัพธ์เนื่องจาก:
- ไม่จำเป็นเนื่องจาก
void *
จะเลื่อนไปเป็นประเภทตัวชี้อื่น ๆ โดยอัตโนมัติและปลอดภัยในกรณีนี้ - มันเพิ่มความยุ่งเหยิงให้กับโค้ดทำให้การแคสต์ไม่ง่ายต่อการอ่าน
- มันทำให้คุณทำซ้ำตัวเองซึ่งโดยทั่วไปไม่ดี
<stdlib.h>
มันสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่ สิ่งนี้อาจทำให้เกิดข้อขัดข้อง (หรือแย่กว่านั้นคือไม่ทำให้เกิดความผิดพลาดจนกว่าจะถึงเวลาต่อมาในส่วนที่แตกต่างกันโดยสิ้นเชิงของโค้ด) พิจารณาว่าจะเกิดอะไรขึ้นหากพอยน์เตอร์และจำนวนเต็มมีขนาดต่างกัน จากนั้นคุณจะซ่อนคำเตือนโดยการส่งและอาจสูญเสียที่อยู่ที่ส่งคืนไป หมายเหตุ: เป็นฟังก์ชั่นโดยปริยาย C99 จะหายไปจาก Cint
และจุดนี้จะไม่เกี่ยวข้องเนื่องจากไม่มีสมมติฐานอัตโนมัติที่ฟังก์ชั่นที่ไม่ได้ประกาศกลับ
เพื่อเป็นการชี้แจงโปรดทราบว่าฉันพูดว่า "คุณไม่ได้แคสต์" ไม่ใช่ "คุณไม่จำเป็นต้องแคสต์" ในความคิดของฉันมันเป็นความล้มเหลวในการรวมนักแสดงแม้ว่าคุณจะพูดถูกก็ตาม ไม่มีประโยชน์ที่จะทำ แต่ความเสี่ยงที่อาจเกิดขึ้นมากมายและการรวมตัวแสดงบ่งบอกว่าคุณไม่รู้เกี่ยวกับความเสี่ยง
โปรดทราบว่าตามที่ผู้วิจารณ์ชี้ให้เห็นว่าข้างต้นพูดถึง 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
และเป็นชนิดที่มีขนาดเล็กกว่าlength
size_t
ใน C คุณไม่จำเป็นต้องร่ายค่าส่งคืนของmalloc
. ตัวชี้เป็นโมฆะที่ส่งคืนโดยmalloc
จะถูกแปลงเป็นประเภทที่ถูกต้องโดยอัตโนมัติ อย่างไรก็ตามหากคุณต้องการให้โค้ดของคุณรวบรวมด้วยคอมไพเลอร์ C ++ จำเป็นต้องใช้แคสต์ ทางเลือกที่ต้องการในชุมชนคือการใช้สิ่งต่อไปนี้:
int *sieve = malloc(sizeof *sieve * length);
ซึ่งช่วยให้คุณไม่ต้องกังวลกับการเปลี่ยนทางด้านขวามือของนิพจน์หากคุณเคยเปลี่ยนประเภทของsieve
.
แคสต์ไม่ดีอย่างที่ผู้คนชี้ให้เห็น โดยเฉพาะตัวชี้หล่อ
คุณไม่หล่อเพราะ:
- ทำให้โค้ดของคุณพกพาได้มากขึ้นระหว่าง C และ C ++ และจากประสบการณ์ SO แสดงให้เห็นว่าโปรแกรมเมอร์จำนวนมากอ้างว่าพวกเขากำลังเขียนเป็นภาษา C เมื่อพวกเขาเขียนด้วย C ++ (หรือ C บวกส่วนขยายของคอมไพเลอร์ในเครื่อง)
- หากไม่ทำเช่นนั้นอาจซ่อนข้อผิดพลาดได้โปรดสังเกตตัวอย่าง SO ทั้งหมดของความสับสนว่าจะเขียน
type *
เมื่อtype **
ใดเทียบกับ - ความคิดที่ว่าจะช่วยให้คุณจากการสังเกตเห็นคุณล้มเหลวไปยัง
#include
ไฟล์ส่วนหัวที่เหมาะสมบอลเฉียงป่าต้นไม้ มันเหมือนกับการพูดว่า "อย่ากังวลกับความจริงที่คุณล้มเหลวในการขอให้คอมไพเลอร์บ่นว่าไม่เห็นต้นแบบ - stdlib.h ที่น่ารำคาญคือสิ่งสำคัญที่ต้องจำอย่างแท้จริง!" - มันกองกำลังพิเศษทางปัญญาข้ามการตรวจสอบ มันวางประเภทที่ต้องการ (ที่ถูกกล่าวหา) ไว้ข้างเลขคณิตที่คุณกำลังทำสำหรับขนาดดิบของตัวแปรนั้น ฉันพนันได้เลยว่าคุณสามารถทำการศึกษา SO ที่แสดงให้เห็นว่า
malloc()
บั๊กถูกจับได้เร็วขึ้นมากเมื่อมีการร่าย เช่นเดียวกับการยืนยันคำอธิบายประกอบที่แสดงเจตนาลดจุดบกพร่อง - การทำซ้ำตัวเองด้วยวิธีที่เครื่องสามารถตรวจสอบได้มักเป็นความคิดที่ดี ในความเป็นจริงนั่นคือสิ่งที่ยืนยันได้และการใช้นักแสดงนี้เป็นการยืนยัน การยืนยันยังคงเป็นเทคนิคทั่วไปส่วนใหญ่ที่เรามีในการทำให้รหัสถูกต้องเนื่องจากทัวริงคิดแนวคิดนี้ขึ้นเมื่อหลายปีก่อน
ตามที่คนอื่นระบุไว้ไม่จำเป็นสำหรับ 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 ++
จาก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
คุณต้องโยนผลมาจากการใช้งาน
ในภาษา C คุณสามารถแปลงvoid
ตัวชี้เป็นตัวชี้ชนิดอื่นโดยปริยายได้ดังนั้นจึงไม่จำเป็นต้องใช้แคสต์ การใช้อย่างใดอย่างหนึ่งอาจแนะนำให้ผู้สังเกตการณ์ทั่วไปทราบว่ามีเหตุผลบางประการที่จำเป็นซึ่งอาจทำให้เข้าใจผิดได้
คุณไม่ได้ส่งผลลัพธ์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 ปี?
ใน C คุณจะได้รับการแปลงโดยนัยจากvoid *
ตัวชี้ (ข้อมูล) อื่น ๆ
malloc()
ไม่จำเป็นต้องส่งค่าที่ส่งคืนมาในตอนนี้ แต่ฉันต้องการเพิ่มจุดหนึ่งที่ดูเหมือนว่าไม่มีใครชี้ให้เห็น:
ในสมัยโบราณกล่าวคือก่อนที่ANSI Cจะให้พอยน์เตอร์void *
ประเภททั่วไปchar *
เป็นประเภทสำหรับการใช้งานดังกล่าว ในกรณีนี้นักแสดงสามารถปิดคำเตือนของคอมไพเลอร์ได้
อ้างอิง: C FAQ
เพียงแค่เพิ่มประสบการณ์ของฉันการเรียนวิศวกรรมคอมพิวเตอร์ฉันเห็นว่าอาจารย์สองหรือสามคนที่ฉันเคยเห็นการเขียนด้วยภาษา C มักจะเหวี่ยงแห แต่คนที่ฉันถาม (ด้วย CV อันยิ่งใหญ่และความเข้าใจเกี่ยวกับ C) บอกฉันว่ามันไม่จำเป็นอย่างยิ่ง แต่ ใช้เฉพาะเจาะจงเท่านั้นและเพื่อให้นักเรียนมีความคิดที่เฉพาะเจาะจงอย่างแท้จริง โดยพื้นฐานแล้วการแคสต์จะไม่เปลี่ยนแปลงอะไรในวิธีการทำงานมันทำตามที่กล่าวไว้อย่างแน่นอนจัดสรรหน่วยความจำและการแคสต์ไม่ส่งผลใด ๆ คุณจะได้รับหน่วยความจำเดียวกันและแม้ว่าคุณจะส่งไปยังสิ่งอื่นโดยไม่ได้ตั้งใจ (และหลีกเลี่ยงคอมไพเลอร์ ข้อผิดพลาด) C จะเข้าถึงด้วยวิธีเดียวกัน
แก้ไข: การแคสต์มีจุดที่แน่นอน เมื่อคุณใช้สัญกรณ์อาร์เรย์โค้ดที่สร้างขึ้นจะต้องทราบจำนวนหน่วยความจำที่ต้องเลื่อนไปถึงจุดเริ่มต้นขององค์ประกอบถัดไปซึ่งทำได้โดยการแคสต์ วิธีนี้ทำให้คุณรู้ว่าสำหรับการดับเบิ้ลคุณไปข้างหน้า 8 ไบต์ในขณะที่ int คุณไป 4 และต่อไปเรื่อย ๆ ดังนั้นจึงไม่มีผลถ้าคุณใช้สัญกรณ์ตัวชี้ในสัญกรณ์อาร์เรย์จึงจำเป็น
ไม่บังคับให้ส่งผลลัพธ์ของผลลัพธ์malloc
เนื่องจากจะส่งคืนvoid*
และvoid*
สามารถชี้ไปยังประเภทข้อมูลใดก็ได้
นี่คือสิ่งที่คู่มืออ้างอิงไลบรารี GNU Cกล่าวว่า:
คุณสามารถจัดเก็บผลลัพธ์ของ
malloc
ตัวแปรพอยน์เตอร์ใด ๆ โดยไม่ต้องแคสต์เนื่องจาก ISO C จะแปลงประเภทvoid *
เป็นตัวชี้ประเภทอื่นโดยอัตโนมัติเมื่อจำเป็น แต่การแคสต์เป็นสิ่งที่จำเป็นในบริบทอื่นที่ไม่ใช่ตัวดำเนินการกำหนดหรือหากคุณอาจต้องการให้โค้ดของคุณทำงานในรูปแบบ C แบบดั้งเดิม
และแน่นอนว่ามาตรฐาน ISO C11 (p347) กล่าวว่า:
ตัวชี้จะส่งคืนหากการจัดสรรประสบความสำเร็จได้รับการจัดแนวอย่างเหมาะสมเพื่อให้สามารถกำหนดให้ตัวชี้ไปยังวัตถุประเภทใดก็ได้ที่มีข้อกำหนดการจัดตำแหน่งพื้นฐานแล้วใช้เพื่อเข้าถึงวัตถุดังกล่าวหรืออาร์เรย์ของวัตถุดังกล่าวในพื้นที่ที่จัดสรร (จนถึง พื้นที่ถูกยกเลิกอย่างชัดเจน)
ตัวชี้โมฆะเป็นตัวชี้ออบเจ็กต์ทั่วไปและ C รองรับการแปลงโดยนัยจากประเภทตัวชี้ที่เป็นโมฆะเป็นประเภทอื่น ๆ ดังนั้นจึงไม่จำเป็นต้องพิมพ์อย่างชัดเจน
อย่างไรก็ตามหากคุณต้องการให้โค้ดเดียวกันทำงานร่วมกันได้อย่างสมบูรณ์บนแพลตฟอร์ม C ++ ซึ่งไม่รองรับการแปลงโดยนัยคุณต้องทำการพิมพ์ดังนั้นทั้งหมดขึ้นอยู่กับการใช้งาน
ประเภทที่ส่งคืนเป็นโมฆะ * ซึ่งสามารถส่งไปยังชนิดของตัวชี้ข้อมูลที่ต้องการเพื่อให้สามารถหักล้างได้
ขึ้นอยู่กับภาษาโปรแกรมและคอมไพเลอร์ หากคุณใช้malloc
ในภาษา C ไม่จำเป็นต้องพิมพ์ร่ายเพราะมันจะพิมพ์ร่ายโดยอัตโนมัติ อย่างไรก็ตามหากคุณใช้ C ++ คุณควรพิมพ์ cast เพราะmalloc
จะส่งกลับvoid*
ประเภท
ในภาษา 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++) {
...
}
คนเคย GCC และ Clang นิสัยเสีย มันไม่ได้ดีแค่นั้น
ฉันรู้สึกสยองมากในช่วงหลายปีที่ผ่านมาโดยคอมไพเลอร์อายุมากที่ฉันต้องใช้ บ่อยครั้งที่ บริษัท และผู้จัดการใช้แนวทางอนุรักษ์นิยมในการเปลี่ยนแปลงคอมไพเลอร์และจะไม่แม้แต่จะทดสอบว่าคอมไพเลอร์ใหม่ (ที่มีการปฏิบัติตามมาตรฐานที่ดีกว่าและการเพิ่มประสิทธิภาพโค้ด) จะทำงานในระบบของพวกเขาได้หรือไม่ ความเป็นจริงในทางปฏิบัติสำหรับนักพัฒนาที่ทำงานคือเมื่อคุณเขียนโค้ดคุณจำเป็นต้องปกปิดฐานของคุณและน่าเสียดายที่การแคสต์ mallocs เป็นนิสัยที่ดีหากคุณไม่สามารถควบคุมคอมไพเลอร์ที่อาจนำไปใช้กับโค้ดของคุณได้
ฉันขอแนะนำให้หลายองค์กรใช้มาตรฐานการเข้ารหัสของตนเองและนั่นควรเป็นวิธีที่ผู้คนปฏิบัติตามหากมีการกำหนดไว้ ในกรณีที่ไม่มีคำแนะนำที่ชัดเจนฉันมักจะไปที่ส่วนใหญ่เพื่อรวบรวมทุกที่มากกว่าที่จะยึดมั่นกับมาตรฐาน
ข้อโต้แย้งที่ว่าไม่จำเป็นภายใต้มาตรฐานปัจจุบันนั้นค่อนข้างถูกต้อง แต่การโต้แย้งนั้นไม่ได้นำไปปฏิบัติในโลกแห่งความเป็นจริง เราไม่ได้เขียนโค้ดในโลกที่ปกครองโดยมาตรฐานของวันนี้โดยเฉพาะ แต่โดยการใช้งานจริงของสิ่งที่ฉันชอบเรียกว่า "สนามแห่งความเป็นจริงของผู้บริหารท้องถิ่น" และมันงอและบิดมากกว่าเวลาอวกาศ :-)
YMMV.
ฉันมักจะคิดว่าการแคสต์ Malloc เป็นการป้องกัน ไม่สวยไม่สมบูรณ์ แต่โดยทั่วไปปลอดภัย (สุจริตถ้าคุณไม่รวม stdlib.h แล้วคุณได้วิธีปัญหามากกว่าหล่อ malloc!)
ไม่คุณไม่ได้ส่งผลของ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 ++ มีรสชาติที่แตกต่างกันทั้งชุด
ฉันใส่การแคสต์เพียงเพื่อแสดงการไม่อนุมัติรูที่น่าเกลียดในระบบประเภทซึ่งอนุญาตให้โค้ดเช่นตัวอย่างข้อมูลต่อไปนี้คอมไพล์ได้โดยไม่ต้องมีการวินิจฉัยแม้ว่าจะไม่มีการใช้แคสต์เพื่อทำให้เกิด 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
หล่อค่าตอบแทนของ
ฉันชอบทำการแคสต์มากกว่า แต่ไม่ใช่ด้วยตนเอง สิ่งที่ฉันชอบคือการใช้g_new
และg_new0
มาโครจากคนกะล่อน ถ้าไม่ใช้ glib ฉันจะเพิ่มมาโครที่คล้ายกัน มาโครเหล่านี้ช่วยลดการทำซ้ำรหัสโดยไม่ลดทอนความปลอดภัยของประเภท หากคุณพิมพ์ผิดคุณจะได้รับการโยนโดยนัยระหว่างพอยน์เตอร์ที่ไม่ใช่โมฆะซึ่งจะทำให้เกิดคำเตือน (ข้อผิดพลาดใน C ++) หากคุณลืมใส่ส่วนหัวที่กำหนดg_new
และg_new0
คุณจะได้รับข้อผิดพลาด g_new
และg_new0
ทั้งสองใช้เวลาขัดแย้งกันซึ่งแตกต่างจากที่ใช้เวลาน้อยกว่าการขัดแย้งmalloc
calloc
เพียงเพิ่ม0
เพื่อรับหน่วยความจำเริ่มต้นเป็นศูนย์ สามารถคอมไพล์โค้ดด้วยคอมไพเลอร์ C ++ โดยไม่มีการเปลี่ยนแปลง
สิ่งที่ดีที่สุดที่ควรทำเมื่อเขียนโปรแกรมด้วยภาษา C เมื่อใดก็ตามที่ทำได้:
- ทำให้โปรแกรมของคุณคอมไพล์ผ่านคอมไพเลอร์ C โดยเปิดคำเตือนทั้งหมด
-Wall
และแก้ไขข้อผิดพลาดและคำเตือนทั้งหมด - ตรวจสอบให้แน่ใจว่าไม่มีตัวแปรที่ประกาศเป็น
auto
- จากนั้นคอมไพล์โดยใช้คอมไพเลอร์ C ++ พร้อม
-Wall
และ-std=c++11
. แก้ไขข้อผิดพลาดและคำเตือนทั้งหมด - ตอนนี้รวบรวมโดยใช้คอมไพเลอร์ C อีกครั้ง ตอนนี้โปรแกรมของคุณควรคอมไพล์โดยไม่มีการเตือนใด ๆ และมีจุดบกพร่องน้อยลง
ขั้นตอนนี้ช่วยให้คุณใช้ประโยชน์จากการตรวจสอบประเภท C ++ ที่เข้มงวดซึ่งจะช่วยลดจำนวนข้อบกพร่อง โดยเฉพาะอย่างยิ่งขั้นตอนนี้บังคับให้คุณรวมstdlib.h
หรือคุณจะได้รับ
malloc
ไม่ได้ประกาศภายในขอบเขตนี้
และยังบังคับให้คุณส่งผลmalloc
หรือคุณจะได้รับ
การแปลงไม่ถูกต้องจาก
void*
เป็นT*
หรือประเภทเป้าหมายของคุณคืออะไร
ประโยชน์เพียงอย่างเดียวจากการเขียนใน C แทน C ++ ที่ฉันสามารถหาได้คือ
- C มี ABI ที่ระบุไว้อย่างดี
- C ++ อาจสร้างโค้ดเพิ่มเติม [ข้อยกเว้น, RTTI, เทมเพลต, ความหลากหลายของรันไทม์ ]
สังเกตว่าข้อเสียประการที่สองในกรณีที่เหมาะจะหายไปเมื่อใช้เซ็ตย่อยร่วมกับ C ร่วมกับคุณสมบัติโพลีมอร์ฟิกแบบคงที่
สำหรับผู้ที่พบว่ากฎที่เข้มงวดของ C ++ ไม่สะดวกเราสามารถใช้คุณลักษณะ C ++ 11 กับประเภทที่อนุมานได้
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
การแคสต์มีไว้สำหรับ C ++ เท่านั้นไม่ใช่ C ในกรณีที่คุณใช้คอมไพเลอร์ C ++ คุณควรเปลี่ยนเป็นคอมไพเลอร์ C
การแคสต์ malloc ไม่จำเป็นใน C แต่จำเป็นต้องมีใน C ++
การแคสต์ไม่จำเป็นใน C เนื่องจาก:
void *
ได้รับการเลื่อนตำแหน่งเป็นตัวชี้ชนิดอื่นโดยอัตโนมัติและปลอดภัยในกรณีของ C.<stdlib.h>
มันสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่ ซึ่งอาจทำให้เกิดข้อขัดข้อง- หากพอยน์เตอร์และจำนวนเต็มมีขนาดแตกต่างกันแสดงว่าคุณกำลังซ่อนคำเตือนโดยการแคสต์และอาจสูญเสียที่อยู่ที่ส่งคืนไป
- หากชนิดของตัวชี้มีการเปลี่ยนแปลงเมื่อประกาศอาจต้องเปลี่ยนบรรทัดทั้งหมดที่
malloc
เรียกและส่ง
ในทางกลับกันการแคสต์อาจเพิ่มความสะดวกในการพกพาโปรแกรมของคุณ กล่าวคืออนุญาตให้โปรแกรม C หรือฟังก์ชันคอมไพล์เป็น C ++
แนวคิดที่อยู่เบื้องหลังตัวชี้โมฆะคือสามารถแคสต์ไปยังประเภทข้อมูลใดก็ได้นั่นคือสาเหตุที่ malloc คืนค่าเป็นโมฆะ นอกจากนี้คุณต้องระวังการพิมพ์อัตโนมัติ ดังนั้นจึงไม่บังคับให้โยนตัวชี้แม้ว่าคุณจะต้องทำก็ตาม ช่วยในการรักษารหัสให้สะอาดและช่วยแก้จุดบกพร่อง
ตัวชี้โมฆะเป็นตัวชี้ทั่วไปและ C สนับสนุนการแปลงโดยนัยจากประเภทตัวชี้ที่เป็นโมฆะเป็นประเภทอื่นดังนั้นจึงไม่จำเป็นต้องพิมพ์อย่างชัดเจน
อย่างไรก็ตามหากคุณต้องการให้โค้ดเดียวกันทำงานร่วมกันได้อย่างสมบูรณ์บนแพลตฟอร์ม C ++ ซึ่งไม่รองรับการแปลงโดยนัยคุณต้องทำการพิมพ์ดังนั้นทั้งหมดขึ้นอยู่กับการใช้งาน
ตามที่ระบุไว้อื่น ๆ ไม่จำเป็นสำหรับ C แต่สำหรับ C ++
รวมถึงการแคสต์อาจอนุญาตให้โปรแกรม C หรือฟังก์ชันคอมไพล์เป็น C ++
ในภาษา C นั้นไม่จำเป็นเนื่องจากโมฆะ * จะถูกเลื่อนไปเป็นประเภทตัวชี้อื่น ๆ โดยอัตโนมัติและปลอดภัย
แต่ถ้าคุณโยนแล้วว่าสามารถซ่อนข้อผิดพลาดหากคุณลืมใส่stdlib.h สิ่งนี้อาจทำให้เกิดข้อขัดข้อง (หรือแย่กว่านั้นคือไม่ทำให้เกิดข้อขัดข้องจนกว่าจะถึงวิธีในภายหลังในส่วนที่แตกต่างกันทั้งหมดของโค้ด)
เนื่องจากพบstdlib.hต้นแบบสำหรับ malloc ในกรณีที่ไม่มีต้นแบบสำหรับ malloc มาตรฐานกำหนดให้คอมไพลเลอร์ C ถือว่า malloc ส่งคืน int หากไม่มีการแคสต์จะมีการแจ้งเตือนเมื่อจำนวนเต็มนี้ถูกกำหนดให้กับตัวชี้ อย่างไรก็ตามด้วยนักแสดงคำเตือนนี้จะไม่ถูกสร้างขึ้นโดยซ่อนจุดบกพร่องไว้
คำถามนี้มีการละเมิดตามความคิดเห็น
บางครั้งฉันสังเกตเห็นความคิดเห็นเช่นนั้น:
อย่าโยนผลลัพธ์ของ malloc
หรือ
ทำไมคุณไม่ส่งผลลัพธ์ของ malloc
ในคำถามที่ OP ใช้การแคสต์ ความคิดเห็นนั้นมีไฮเปอร์ลิงก์สำหรับคำถามนี้
ที่อยู่ในใด ๆลักษณะที่ไม่เหมาะสมและไม่ถูกต้องเช่นกัน ไม่มีถูกและไม่มีผิดเมื่อมันเป็นเรื่องของสไตล์การเข้ารหัสของตัวเองอย่างแท้จริง
เหตุใดจึงเกิดขึ้น
ขึ้นอยู่กับสองเหตุผล:
คำถามนี้อิงตามความคิดเห็น ในทางเทคนิคคำถามควรถูกปิดไปแล้วเมื่อหลายปีก่อนตามความคิดเห็น คำถาม " Do I " หรือ " Don't I " หรือเทียบเท่ากับคำถาม" Should I " หรือ " Should I " คุณไม่สามารถตอบได้โดยไม่มีทัศนคติของความคิดเห็นของตัวเอง เหตุผลประการหนึ่งที่ต้องปิดคำถามเนื่องจาก "อาจนำไปสู่คำตอบตามความคิดเห็น" ดังที่แสดงไว้ที่นี่
คำตอบที่หลายคน (รวมทั้งที่เห็นได้ชัดที่สุดและได้รับการยอมรับคำตอบของ@unwind ) มีทั้งสมบูรณ์หรือเกือบทั้งหมดความคิดเห็นตาม (fe ลึกลับ "ถ่วง" ที่จะถูกเพิ่มเข้าไปในรหัสของคุณถ้าคุณไม่หล่อหรือการทำซ้ำตัวเองจะไม่ดี) และการแสดง แนวโน้มที่ชัดเจนและมุ่งเน้นที่จะละเว้นนักแสดง พวกเขาให้เหตุผลเกี่ยวกับความซ้ำซ้อนของการโยนในด้านหนึ่ง แต่ยังและยิ่งเลวร้ายลงให้เหตุผลในการแก้ข้อผิดพลาดที่เกิดจากข้อผิดพลาด / ล้มเหลวของการเขียนโปรแกรมของตัวเอง - ไม่ถ้าใครต้องการที่จะใช้
#include <stdlib.h>
malloc()
ฉันต้องการนำเสนอมุมมองที่แท้จริงของบางประเด็นที่กล่าวถึงโดยมีความคิดเห็นส่วนตัวของฉันน้อยลง ต้องสังเกตบางประเด็นโดยเฉพาะ:
คำถามที่อ่อนไหวมากเช่นนี้ที่จะตกอยู่ในความคิดเห็นของตัวเองต้องการคำตอบพร้อมข้อดีข้อเสียที่เป็นกลาง ไม่เพียง แต่ข้อเสียหรือข้อดี
คำตอบนี้มีภาพรวมข้อดีข้อเสียที่ดี:
https://stackoverflow.com/a/33047365/12139179
(โดยส่วนตัวแล้วฉันคิดว่าเพราะเหตุผลนั้นเป็นคำตอบที่ดีที่สุดจนถึงตอนนี้)
สาเหตุหนึ่งที่พบมากที่สุดที่เป็นสาเหตุของการละเว้นของนักแสดงคือนักแสดงอาจซ่อนจุดบกพร่อง
หากมีคนใช้นัยที่ประกาศ
malloc()
ว่าส่งคืนint
(ฟังก์ชันโดยนัยหายไปจากมาตรฐานตั้งแต่ C99) และsizeof(int) != sizeof(int*)
ดังที่แสดงในคำถามนี้เหตุใดโค้ดนี้จึง segfault บนสถาปัตยกรรม 64 บิต แต่ทำงานได้ดีบน 32 บิต
นักแสดงจะซ่อนข้อบกพร่อง
ขณะนี้เป็นจริงก็แสดงให้เห็นถึงครึ่งหนึ่งของเรื่องที่ละเลยของตัวละครเท่านั้นจะเป็นทางออกที่ไปข้างหน้านำข้อผิดพลาดที่ยิ่งใหญ่ - ไม่รวมถึงเมื่อมีการใช้
stdlib.h
malloc()
นี่จะไม่เป็นปัญหาร้ายแรงหากคุณ
ใช้คอมไพเลอร์ที่สอดคล้องกับ C99 ขึ้นไป (ซึ่งแนะนำและควรเป็นข้อบังคับ) และ
อย่าลืมใส่
stdlib.h
เมื่อคุณต้องการใช้malloc()
ในโค้ดของคุณซึ่งเป็นจุดบกพร่องอย่างมาก
บางคนโต้แย้งเกี่ยวกับการปฏิบัติตาม 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
- การแคสต์อาจไม่เป็นประโยชน์เมื่อโค้ดของคุณตามลำดับประเภทของตัวชี้ที่กำหนด (และประเภทของนักแสดง) จะเปลี่ยนแปลงแม้ว่าในกรณีส่วนใหญ่จะไม่น่าเป็นไปได้ จากนั้นคุณจะต้องดูแล / เปลี่ยนการร่ายทั้งหมดด้วยและหากคุณมีการเรียกใช้ฟังก์ชันการจัดการหน่วยความจำในโค้ดของคุณเพียงไม่กี่พันครั้งสิ่งนี้สามารถสรุปและลดประสิทธิภาพการบำรุงรักษาได้
สรุป:
ความจริงก็คือการโยนซ้ำซ้อนตามมาตรฐาน C (แล้วตั้งแต่ ANSI-C (C89 / C90)) หากตัวชี้ที่กำหนดชี้ไปที่วัตถุที่มีข้อกำหนดการจัดตำแหน่งพื้นฐาน (ซึ่งรวมถึงวัตถุส่วนใหญ่ทั้งหมด)
คุณไม่จำเป็นต้องทำการแคสต์เนื่องจากตัวชี้จะถูกจัดตำแหน่งโดยอัตโนมัติในกรณีนี้:
"ลำดับและความต่อเนื่องของการจัดเก็บที่จัดสรรโดยการเรียกต่อเนื่องไปยังฟังก์ชัน aligned_alloc, calloc, malloc และ realloc ไม่ได้ระบุไว้ตัวชี้จะส่งคืนหากการจัดสรรประสบความสำเร็จมีการจัดตำแหน่งอย่างเหมาะสมเพื่อให้สามารถกำหนดให้ตัวชี้ไปยังวัตถุประเภทใดก็ได้ที่มี ข้อกำหนดพื้นฐานในการจัดตำแหน่งและใช้เพื่อเข้าถึงวัตถุดังกล่าวหรืออาร์เรย์ของวัตถุดังกล่าวในพื้นที่ที่จัดสรร (จนกว่าจะมีการยกเลิกการจัดสรรพื้นที่อย่างชัดเจน) "
ที่มา: C18, §7.22.3 / 1
"การจัดตำแหน่งพื้นฐานคือการจัดตำแหน่งที่ถูกต้องน้อยกว่าหรือเท่ากับการ
_Alignof (max_align_t)
จัดตำแหน่งพื้นฐานจะได้รับการสนับสนุนโดยการใช้งานสำหรับวัตถุในระยะเวลาการจัดเก็บทั้งหมดข้อกำหนดการจัดตำแหน่งประเภทต่อไปนี้จะต้องเป็นการจัดตำแหน่งพื้นฐาน:- ประเภทพื้นฐานเกี่ยวกับปรมาณูที่มีคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด
- ประเภทการแจกแจงแบบปรมาณูคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด
- ประเภทตัวชี้อะตอมคุณสมบัติหรือไม่มีเงื่อนไขทั้งหมด
- อาร์เรย์ทุกประเภทที่ประเภทองค์ประกอบมีข้อกำหนดพื้นฐานในการจัดตำแหน่ง 57)
- ทุกประเภทที่ระบุในข้อ 7 เป็นประเภทออบเจ็กต์ที่สมบูรณ์
- โครงสร้างหรือยูเนี่ยนทุกประเภทซึ่งองค์ประกอบทั้งหมดมีประเภทที่มีข้อกำหนดพื้นฐานในการจัดตำแหน่งและไม่มีองค์ประกอบใดที่มีตัวระบุการจัดตำแหน่งที่ระบุการจัดตำแหน่งที่ไม่ใช่การจัดตำแหน่งพื้นฐาน
- ตามที่ระบุไว้ใน 6.2.1 การประกาศในภายหลังอาจซ่อนการประกาศก่อนหน้านี้ "
ที่มา: C18, §6.2.8 / 2
อย่างไรก็ตามหากคุณจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ที่กำหนดการนำไปใช้งานของข้อกำหนดการจัดตำแหน่งเพิ่มเติมจำเป็นต้องใช้ cast
จัดตำแหน่งขยาย
_Alignof (max_align_t)
เป็นตัวแทนจากที่สูงกว่าการจัดตำแหน่ง มีการกำหนดการนำไปใช้งานว่ารองรับการจัดแนวเพิ่มเติมหรือไม่และระยะเวลาการจัดเก็บที่รองรับหรือไม่ ประเภทที่มีข้อกำหนดในการจัดตำแหน่งเพิ่มเติมเป็นประเภทที่จัดชิดเกิน 58)ที่มา C18, §6.2.8 / 3
อย่างอื่นเป็นเรื่องของกรณีการใช้งานเฉพาะและความคิดเห็นของตัวเอง
โปรดใช้ความระมัดระวังในการให้ความรู้แก่ตนเอง
ฉันขอแนะนำให้คุณอ่านคำตอบทั้งหมดที่ทำอย่างรอบคอบก่อน (เช่นเดียวกับความคิดเห็นของพวกเขาที่อาจชี้ไปที่ความล้มเหลว) จากนั้นสร้างความคิดเห็นของคุณเองหากคุณหรือถ้าคุณไม่ได้ส่งผลmalloc()
ในบางกรณี
โปรดทราบ:
ไม่มีคำตอบที่ถูกและผิดสำหรับคำถามนั้น มันเป็นเรื่องของสไตล์และคุณเป็นคนตัดสินใจเองว่าคุณจะเลือกทางใด (ถ้าคุณไม่ได้ถูกบังคับโดยการศึกษาหรืองานแน่นอน) โปรดระวังที่และจะไม่ปล่อยให้หลอกให้คุณ
หมายเหตุสุดท้าย: ฉันโหวตให้ปิดคำถามนี้เมื่อเร็ว ๆ นี้โดยอิงตามความคิดเห็นซึ่งเป็นสิ่งที่จำเป็นมาตั้งแต่ปี หากคุณได้รับสิทธิ์ปิด / เปิดอีกครั้งฉันก็อยากเชิญให้คุณทำเช่นนั้นเช่นกัน
สำหรับฉันแล้วสิ่งที่นำกลับบ้านและข้อสรุปก็คือการแคสต์malloc
ใน C นั้นไม่จำเป็นเลย แต่ถ้าคุณร่ายมันจะไม่ส่งผลใด ๆmalloc
เพราะmalloc
จะยังคงจัดสรรพื้นที่ความทรงจำที่คุณร้องขอให้กับคุณ อีกอย่างกลับบ้านคือเหตุผลหรือหนึ่งในเหตุผลที่คนแคสต์และนี่คือเพื่อให้พวกเขาคอมไพล์โปรแกรมเดียวกันทั้งใน C หรือ C ++
อาจมีเหตุผลอื่น ๆ แต่เหตุผลอื่น ๆ ที่แน่นอนจะทำให้คุณตกอยู่ในปัญหาร้ายแรงไม่ช้าก็เร็ว
คุณทำได้ แต่ไม่จำเป็นต้องแคสต์เป็น C คุณต้องแคสต์หากโค้ดนั้นคอมไพล์เป็น C ++