การส่งตัวชี้ฟังก์ชันไปยังประเภทอื่น


90

สมมติว่าฉันมีฟังก์ชันที่ยอมรับvoid (*)(void*)ตัวชี้ฟังก์ชันเพื่อใช้เป็นการโทรกลับ:

ตอนนี้ถ้าฉันมีฟังก์ชันเช่นนี้:

ฉันสามารถทำได้อย่างปลอดภัยหรือไม่?

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


1
ฉันค่อนข้างเป็นมือใหม่ แต่ตัวชี้ฟังก์ชัน"void ( ) (โมฆะ ) หมายความว่าอย่างไร?. เป็นตัวชี้ไปยังฟังก์ชันที่ยอมรับโมฆะ * เป็นอาร์กิวเมนต์และส่งกลับโมฆะ
Digital Gal

2
@Myke: void (*func)(void *)หมายความว่าfuncเป็นตัวชี้ไปยังฟังก์ชันที่มีลายเซ็นประเภทเช่นvoid foo(void *arg). ใช่คุณพูดถูก
mk12

คำตอบ:


123

เท่าที่ C มาตรฐานเป็นห่วงถ้าคุณโยนตัวชี้ฟังก์ชั่นที่จะเป็นตัวชี้การทำงานของประเภทที่แตกต่างกันแล้วเรียกว่ามันเป็นพฤติกรรมที่ไม่ได้กำหนด ดูภาคผนวก J.2 (ข้อมูล):

พฤติกรรมไม่ได้กำหนดไว้ในสถานการณ์ต่อไปนี้:

  • ตัวชี้ใช้เพื่อเรียกใช้ฟังก์ชันที่ประเภทไม่เข้ากันกับประเภทชี้ไปที่ (6.3.2.3)

ส่วน 6.3.2.3 วรรค 8 อ่าน:

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

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

คำจำกัดความของความเข้ากันได้ค่อนข้างซับซ้อน สามารถพบได้ในหัวข้อ 6.7.5.3 ย่อหน้าที่ 15:

สำหรับสองประเภทฟังก์ชั่นจะเข้ากันได้ทั้งสองจะต้องระบุประเภทผลตอบแทนที่เข้ากันได้127

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

127) ถ้าฟังก์ชันทั้งสองประเภทเป็น '' แบบเก่า '' จะไม่มีการเปรียบเทียบประเภทพารามิเตอร์

กฎระเบียบในการพิจารณาว่าทั้งสองประเภทจะเข้ากันได้อธิบายไว้ในส่วน 6.2.7 และฉันจะไม่พูดกับพวกเขาที่นี่ตั้งแต่พวกเขากำลังค่อนข้างยาว แต่คุณสามารถอ่านได้บนร่างมาตรฐาน C99 นี้ (PDF)

กฎที่เกี่ยวข้องอยู่ในหัวข้อ 6.7.5.1 ย่อหน้าที่ 2:

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

ดังนั้นเนื่องจาก a void* เข้ากันไม่ได้กับ a struct my_struct*ตัวชี้ฟังก์ชันของประเภทvoid (*)(void*)จึงไม่สามารถทำงานร่วมกับตัวชี้ฟังก์ชันของประเภทvoid (*)(struct my_struct*)ได้ดังนั้นการคัดเลือกตัวชี้ฟังก์ชันนี้จึงเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ในทางเทคนิค

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

สิ่งที่คุณทำไม่ได้แน่นอน:

  • ส่งระหว่างตัวชี้ฟังก์ชันของรูปแบบการเรียกที่แตกต่างกัน คุณจะทำให้สแต็คยุ่งเหยิงและอย่างดีที่สุดก็ผิดพลาดที่แย่ที่สุดประสบความสำเร็จอย่างเงียบ ๆ ด้วยช่องโหว่ด้านความปลอดภัยขนาดใหญ่ ในการเขียนโปรแกรม Windows คุณมักจะส่งตัวชี้ฟังก์ชันไปรอบ ๆ Win32 คาดว่าทุกฟังก์ชั่นการโทรกลับจะใช้stdcallเรียกประชุม (ซึ่งมาโครCALLBACK, PASCALและWINAPIทั้งหมดขยายตัวออกไป) หากคุณส่งตัวชี้ฟังก์ชันที่ใช้รูปแบบการเรียก C มาตรฐาน ( cdecl) ความไม่ดีจะส่งผลให้
  • ใน C ++ ให้ส่งระหว่างตัวชี้ฟังก์ชันสมาชิกคลาสและตัวชี้ฟังก์ชันปกติ สิ่งนี้มักจะเดินทางไปยังมือใหม่ C ++ ฟังก์ชันสมาชิกคลาสมีthisพารามิเตอร์ที่ซ่อนอยู่และหากคุณส่งฟังก์ชันสมาชิกไปยังฟังก์ชันปกติจะไม่มีthisวัตถุใดที่จะใช้และอีกครั้งจะส่งผลให้เกิดความเสียหายอย่างมาก

ความคิดที่ไม่ดีอีกอย่างที่บางครั้งอาจใช้ได้ผล แต่ก็เป็นพฤติกรรมที่ไม่ได้กำหนดไว้ด้วย:

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

19
ประเด็นทั้งหมดvoid*คือมันเข้ากันได้กับตัวชี้อื่น ๆ ไม่ใช่หรือ? ไม่ควรมีปัญหาในการส่ง a struct my_struct*ไปยัง a void*ในความเป็นจริงคุณไม่ควรต้องแคสต์คอมไพเลอร์ก็ควรยอมรับ ตัวอย่างเช่นหากคุณส่งผ่านstruct my_struct*ไปยังฟังก์ชันที่ใช้ a void*ไม่จำเป็นต้องมีการแคสต์ ฉันขาดอะไรไปที่ทำให้สิ่งเหล่านี้เข้ากันไม่ได้?
brianmearns

2
คำตอบนี้อ้างอิงถึง "สิ่งนี้อาจใช้ได้บน x86 ... ": มีแพลตฟอร์มใดบ้างที่จะใช้ไม่ได้? ใครมีประสบการณ์เมื่อล้มเหลวนี้? qsort () สำหรับ C ดูเหมือนจะเป็นสถานที่ที่ดีในการส่งตัวชี้ฟังก์ชันถ้าเป็นไปได้
kevinarpe

4
@KCArpe: ตามแผนภูมิภายใต้หัวข้อ "Implementations of Member Function Pointers" ในบทความนี้คอมไพเลอร์ OpenWatcom 16 บิตบางครั้งใช้ชนิดตัวชี้ฟังก์ชันที่ใหญ่กว่า (4 ไบต์) มากกว่าชนิดตัวชี้ข้อมูล (2 ไบต์) ในการกำหนดค่าบางอย่าง อย่างไรก็ตาม POSIX สอดคล้องระบบต้องใช้การแสดงเหมือนกันสำหรับvoid*เป็นประเภทตัวชี้ฟังก์ชันดูข้อมูลจำเพาะ
Adam Rosenfield

3
ตอนนี้ลิงก์จาก @adam อ้างอิงถึงมาตรฐาน POSIX รุ่นปี 2016 ที่ส่วนที่เกี่ยวข้อง 2.12.3 ถูกลบออก คุณยังสามารถหาได้ในฉบับที่ 2008
Martin Trenkmann

7
@brianmearns ไม่ใช่void *เป็นเพียง "เข้ากันได้กับ" ตัวชี้อื่น ๆ (ที่ไม่ใช่ฟังก์ชัน) ในรูปแบบที่กำหนดไว้อย่างแม่นยำเท่านั้น (ซึ่งไม่เกี่ยวข้องกับความหมายของมาตรฐาน C กับคำว่า "เข้ากันได้" ในกรณีนี้) C อนุญาตให้ a void *มีขนาดใหญ่กว่าหรือเล็กกว่า a struct my_struct *หรือมีบิตในลำดับที่แตกต่างกันหรือทำให้เป็นเชิงลบหรืออะไรก็ได้ ดังนั้นvoid f(void *)และvoid f(struct my_struct *)สามารถABI-เข้ากันไม่ได้ C จะแปลงพอยน์เตอร์ให้คุณเองหากจำเป็น แต่ก็ทำไม่ได้และบางครั้งก็ไม่สามารถแปลงฟังก์ชันชี้ไปเพื่อรับประเภทอาร์กิวเมนต์ที่อาจแตกต่างกันได้
mtraceur

32

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

ตลอดทั้งรหัสมีหลายกรณีของการหล่อจากประเภท (1) ถึง (2):

  1. typedef int (*CompareFunc) (const void *a, const void *b)
  2. typedef int (*CompareDataFunc) (const void *b, const void *b, void *user_data)

เป็นเรื่องปกติที่จะ chain-thru ด้วยการเรียกแบบนี้:

ดูตัวคุณเองที่นี่ในg_array_sort(): http://git.gnome.org/browse/glib/tree/glib/garray.c

คำตอบข้างต้นมีรายละเอียดและน่าจะถูกต้อง - หากคุณนั่งอยู่ในคณะกรรมการมาตรฐาน อดัมและโยฮันเนสสมควรได้รับเครดิตสำหรับคำตอบที่ค้นคว้ามาอย่างดี อย่างไรก็ตามคุณจะพบว่ารหัสนี้ใช้งานได้ดี แย้ง? ใช่. พิจารณาสิ่งนี้: GLib รวบรวม / ทำงาน / ทดสอบบนแพลตฟอร์มจำนวนมาก (Linux / Solaris / Windows / OS X) ที่มีคอมไพเลอร์ / ตัวเชื่อม / เคอร์เนลโหลดเดอร์ (GCC / CLang / MSVC) ที่หลากหลาย ฉันเดาว่ามาตรฐานจะแย่

ฉันใช้เวลาคิดหาคำตอบเหล่านี้ นี่คือข้อสรุปของฉัน:

  1. หากคุณกำลังเขียนไลบรารีการติดต่อกลับสิ่งนี้อาจใช้ได้ Caveat emptor - ใช้ด้วยความเสี่ยงของคุณเอง
  2. อย่างอื่นอย่าทำ

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

คำถามที่สำคัญกว่าในการค้นคว้า: ใครบางคนสามารถหาแพลตฟอร์ม / คอมไพเลอร์ / ตัวเชื่อมโยง / ตัวโหลดที่เคล็ดลับนี้ใช้ไม่ได้ ? จุดบราวนี่ที่สำคัญสำหรับสิ่งนั้น ฉันพนันได้เลยว่ามีโปรเซสเซอร์ / ระบบฝังตัวบางตัวที่ไม่ชอบ อย่างไรก็ตามสำหรับคอมพิวเตอร์เดสก์ท็อป (และอาจเป็นมือถือ / แท็บเล็ต) เคล็ดลับนี้อาจยังใช้ได้


10
สถานที่ที่ใช้ไม่ได้อย่างแน่นอนคือคอมไพเลอร์ Emscripten LLVM เป็น Javascript ดูรายละเอียดในgithub.com/kripken/emscripten/wiki/Asm-pointer-casts
Ben Lings

2
Upated อ้างอิงเกี่ยวกับEmscripten
ysdx

4
ลิงก์ @BenLings ที่โพสต์จะพังในอนาคตอันใกล้นี้ ได้ย้ายไปที่kripken.github.io/emscripten-site/docs/porting/guidelines/…
Alex Reinking

10

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

คอมไพเลอร์ที่ดีจะสร้างโค้ดสำหรับ my_callback_helper เฉพาะในกรณีที่จำเป็นจริงๆซึ่งในกรณีนี้คุณก็ยินดีที่ได้ทำ


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

คอมไพเลอร์ทั้งหมดที่ฉันทดสอบด้วยจะสร้างโค้ดให้my_callback_helperเว้นแต่จะมีการอินไลน์เสมอ jmp my_callback_functionนี้แน่นอนไม่จำเป็นต้องเป็นสิ่งเดียวที่มันมีแนวโน้มที่จะทำคือ คอมไพเลอร์อาจต้องการตรวจสอบให้แน่ใจว่าแอดเดรสของฟังก์ชันต่างกัน แต่น่าเสียดายที่มันทำเช่นนี้แม้ว่าฟังก์ชันจะถูกทำเครื่องหมายด้วย C99 inline(เช่น "ไม่สนใจที่อยู่")
yyny

ฉันไม่แน่ใจว่าถูกต้อง ความคิดเห็นอื่นจากการตอบกลับอื่นด้านบน (โดย @mtraceur) กล่าวว่า a void *อาจมีขนาดแตกต่างจาก a ได้struct *(ฉันคิดว่าผิดเพราะไม่งั้นmallocจะเสีย แต่ความคิดเห็นนั้นมี 5 upvotes ดังนั้นฉันจึงให้เครดิตกับมัน ถ้า @mtraceur ถูกต้องคำตอบที่คุณเขียนจะไม่ถูกต้อง
cesss

@ กระบวนการ: มันไม่สำคัญเลยถ้าขนาดแตกต่างกัน การแปลงไปและกลับvoid*ยังคงต้องทำงาน ในระยะสั้นvoid*อาจมีบิตมากกว่านี้ แต่ถ้าคุณส่ง a struct*ไปยังvoid*บิตพิเศษเหล่านั้นอาจเป็นศูนย์และการโยนกลับสามารถทิ้งศูนย์เหล่านั้นได้อีกครั้ง
MSalters

@MSalters: ฉันไม่รู้จริงๆว่าvoid *(ในทางทฤษฎี) จะแตกต่างจากกstruct *. ฉันใช้ vtable ใน C และฉันใช้thisตัวชี้C ++ - ish เป็นอาร์กิวเมนต์แรกของฟังก์ชันเสมือน เห็นได้ชัดว่าthisต้องเป็นตัวชี้ไปที่โครงสร้าง "ปัจจุบัน" (ที่ได้รับ) ดังนั้นฟังก์ชันเสมือนจึงต้องการต้นแบบที่แตกต่างกันขึ้นอยู่กับโครงสร้างที่นำมาใช้ฉันคิดว่าการใช้void *thisอาร์กิวเมนต์จะแก้ไขทุกอย่างได้ แต่ตอนนี้ฉันได้เรียนรู้ว่ามันเป็นพฤติกรรมที่ไม่ได้กำหนด ...
cesss

6

คุณมีประเภทฟังก์ชันที่เข้ากันได้หากประเภทการส่งคืนและประเภทพารามิเตอร์เข้ากันได้โดยพื้นฐานแล้ว (มันซับซ้อนกว่าในความเป็นจริง :)) ความเข้ากันได้จะเหมือนกับ "ประเภทเดียวกัน" เพียง แต่หละหลวมกว่าที่จะอนุญาตให้มีประเภทต่างๆได้ แต่ยังคงมีรูปแบบบางอย่างที่บอกว่า "ประเภทเหล่านี้เกือบจะเหมือนกัน" ตัวอย่างเช่นใน C89 โครงสร้างสองโครงสร้างเข้ากันได้ถ้าเหมือนกัน แต่ชื่อต่างกัน C99 ดูเหมือนจะเปลี่ยนไป อ้างจากเอกสารเหตุผล c (ขอแนะนำให้อ่าน btw!):

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

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


4

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

ฉันหวังว่าฉันจะทำให้ชัดเจนขึ้นโดยการแสดงสิ่งที่ไม่ได้ผล:

หรือ...

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


0

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

ดังนั้นฉันคิดว่าคุณควรจะทำได้อย่างปลอดภัย


2
คุณจะปลอดภัยเท่านั้นตราบเท่าที่ - พstructอยน์เตอร์และพvoidอยน์เตอร์มีการแสดงบิตที่เข้ากันได้ ไม่รับประกันว่าจะเป็นเช่นนั้น
Christoph

1
คอมไพเลอร์ยังสามารถส่งผ่านข้อโต้แย้งในรีจิสเตอร์ และไม่ใช่เรื่องแปลกที่จะใช้การลงทะเบียนที่แตกต่างกันสำหรับการลอยตัว ints หรือตัวชี้
MSalters

0

ตัวชี้โมฆะเข้ากันได้กับตัวชี้ประเภทอื่น ๆ มันเป็นหัวใจสำคัญของการทำงานของ malloc และ mem ( memcpy, memcmp) โดยทั่วไปใน C (แทนที่จะเป็น C ++) NULLเป็นมาโครที่กำหนดให้เป็น((void *)0).

ดู 6.3.2.3 (รายการ 1) ใน C99:

ตัวชี้เป็นโมฆะอาจถูกแปลงเป็นหรือจากตัวชี้ไปเป็นประเภทวัตถุหรือไม่สมบูรณ์


สิ่งนี้ขัดแย้งกับคำตอบของ Adam Rosenfieldดูย่อหน้าสุดท้ายและความคิดเห็น
ผู้ใช้

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