ทำไมโมฆะใน C หมายถึงไม่โมฆะ?


25

ในภาษาที่พิมพ์อย่างรุนแรงเช่น Java และ C #, void(หรือVoid) เป็นชนิดส่งคืนสำหรับวิธีการดูเหมือนจะหมายถึง:

วิธีนี้จะไม่ส่งคืนสิ่งใดเลย ไม่มีอะไร ไม่มีการส่งคืน คุณจะไม่ได้รับอะไรจากวิธีนี้

สิ่งที่แปลกจริงๆคือใน C voidเป็นชนิดส่งคืนหรือเป็นชนิดพารามิเตอร์ method หมายความว่า:

มันอาจเป็นอะไรก็ได้ คุณต้องอ่านซอร์สโค้ดเพื่อค้นหา โชคดี. หากเป็นตัวชี้คุณควรรู้ว่าคุณกำลังทำอะไรอยู่

ลองพิจารณาตัวอย่างต่อไปนี้ใน C:

void describe(void *thing)
{
    Object *obj = thing;
    printf("%s.\n", obj->description);
}

void *move(void *location, Direction direction)
{
    void *next = NULL;

    // logic!

    return next;
}

เห็นได้ชัดว่าวิธีที่สองส่งกลับตัวชี้ซึ่งโดยความหมายอาจเป็นอะไรก็ได้

เนื่องจาก C เก่ากว่า Java และ C # ทำไมภาษาเหล่านี้จึงใช้voidความหมายว่า "ไม่มีอะไร" ในขณะที่ C ใช้เป็น "ไม่มีอะไรหรืออะไรเลย (เมื่อตัวชี้)"


138
ชื่อคำถามและข้อความพูดถึงvoidในขณะที่ตัวอย่างรหัสใช้void*ซึ่งเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง

4
นอกจากนี้โปรดทราบว่า Java และ C # มีแนวคิดเดียวกันพวกเขาเพียงเรียกมันว่าObjectในกรณีเดียวเพื่อทำให้เข้าใจผิด
Mooing Duck

4
@ Mephy พวกเขาไม่ได้พิมพ์อย่างแน่นอน? โดย C # โดยใช้การพิมพ์เป็ดคุณหมายถึงdynamicประเภทที่ไม่ค่อยใช้หรือไม่
Axarydax

9
@Mephy: ครั้งสุดท้ายที่ฉันตรวจสอบความหมายที่ขัดแย้งกันของวิกิพีเดียที่ระบุไว้สิบเอ็ดรายการสำหรับ "พิมพ์อย่างรุนแรง" การพูดว่า "C # ไม่ได้พิมพ์อย่างยิ่ง" จึงไม่มีความหมาย
Eric Lippert

8
@LightnessRacesinOrbit: ไม่ประเด็นคือว่าไม่มีคำจำกัดความที่เชื่อถือได้ของคำ วิกิพีเดียไม่ได้เป็นกำหนดทรัพยากรที่จะบอกคุณว่าสิทธิความหมายของคำที่เป็น มันเป็นทรัพยากรที่สื่อความหมาย ความจริงที่ว่ามันอธิบายความหมายสิบเอ็ดขัดแย้งแตกต่างกันคือหลักฐานว่าคำว่าไม่สามารถนำมาใช้ในการสนทนาโดยไม่ระมัดระวังกำหนดมัน การทำอย่างอื่นหมายความว่าอัตราเดิมพันเป็นสิ่งที่ดีมากผู้คนในการสนทนาจะพูดถึงอดีตไม่ใช่เพื่อกันและกัน
Eric Lippert

คำตอบ:


58

คำหลักvoid(ไม่ใช่ตัวชี้) หมายถึง "ไม่มี" ในภาษาเหล่านั้น สิ่งนี้สอดคล้องกัน

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

ฉันไม่สามารถหาเหตุผลทางประวัติศาสตร์ที่อยู่เบื้องหลังการนำกลับมาใช้voidเพื่อหมายถึง "ไม่มีอะไร" และ "อะไร" ในบริบทที่แตกต่างกันอย่างไรก็ตาม C ทำเช่นนี้ในที่อื่น ๆ ตัวอย่างเช่นstaticมีวัตถุประสงค์ที่แตกต่างกันในบริบทที่แตกต่างกัน เห็นได้ชัดว่าก่อนหน้านี้ในภาษา C สำหรับการนำคำหลักกลับมาใช้ซ้ำด้วยวิธีนี้

Java และ C # แตกต่างกันมากพอที่จะทำให้การหยุดพักชั่วคราวเพื่อแก้ไขปัญหาเหล่านี้ได้ Java และ "ปลอดภัย" C # ยังไม่อนุญาตให้มีตัวชี้ดิบและไม่จำเป็นต้องมีความเข้ากันได้ง่าย C (C # ไม่ปลอดภัยจะช่วยให้คำแนะนำ แต่ส่วนใหญ่ของรหัส C # ไม่ตกอยู่ในหมวดหมู่นี้) สิ่งนี้ทำให้พวกเขาสามารถเปลี่ยนแปลงสิ่งต่าง ๆ ได้โดยไม่ต้องกังวลเกี่ยวกับความเข้ากันได้ย้อนหลัง วิธีหนึ่งในการทำเช่นนี้คือการแนะนำคลาสObjectที่รูทของลำดับชั้นซึ่งทุกคลาสสืบทอดเพื่อให้การObjectอ้างอิงทำหน้าที่เดียวกันvoid*โดยไม่ต้องมีปัญหาเรื่องประเภทและการจัดการหน่วยความจำดิบ


2
ฉันจะให้ข้อมูลอ้างอิงเมื่อฉันกลับถึงบ้าน

4
They also do not allow raw pointers and do not need easy C compatibility.เท็จ C # มีพอยน์เตอร์ พวกเขามักจะ (และควรจะ) ออก แต่พวกเขาอยู่ที่นั่น
หมอผี

8
เกี่ยวกับสาเหตุที่พวกเขานำกลับมาใช้ใหม่void: เหตุผลที่ชัดเจนคือหลีกเลี่ยงการแนะนำคำหลักใหม่ มีพวกเขาแนะนำให้รู้จักกับคำหลักพิเศษ (เช่นunknown) จากนั้นvoid*จะเป็นความหมาย (และอาจผิดกฎหมาย) สร้างและจะถูกต้องตามกฎหมายเท่านั้นในรูปแบบของunknown unknown*
jamesdlin

20
แม้จะอยู่ในvoid *, voidหมายถึง "อะไร" ไม่ "อะไร" คุณไม่สามารถยกเลิกการvoid *ลงทะเบียนได้คุณต้องส่งมันไปที่ประเภทตัวชี้อื่นก่อน
oefe

2
@oefe ฉันคิดว่ามันเป็นความหมายที่จะแยกผมว่าเพราะvoidและvoid*เป็นชนิดที่แตกต่างกัน (ที่จริงvoidคือ "ชนิดไม่มี") มันทำให้ความรู้สึกมากที่สุดเท่าที่บอกว่า " intในint*หมายถึงบางสิ่งบางอย่าง." ไม่เมื่อคุณประกาศตัวแปรตัวชี้คุณกำลังพูดว่า "นี่คือที่อยู่หน่วยความจำที่สามารถถือบางสิ่งได้" ในกรณีที่void*คุณกำลังพูดว่า "ที่อยู่นี้มีบางอย่างแต่บางสิ่งไม่สามารถอนุมานได้จากประเภทของตัวชี้"

32

voidและvoid*เป็นสองสิ่งที่แตกต่างกัน voidใน C หมายถึงสิ่งเดียวกับใน Java ไม่มีค่าส่งคืน A void*คือตัวชี้ที่ไม่มีประเภท

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

เมื่อพิจารณาถึงบริบทสิ่งเดียวที่คุณสามารถทำได้ในเชิงตรรกะกับ dereferenced void*คือเพิกเฉยซึ่งเป็นลักษณะการทำงานของvoidประเภท


1
ฉันเห็นด้วยจนกระทั่งคุณไปถึง "เพิกเฉย" เล็กน้อย เป็นไปได้ว่าสิ่งที่ส่งคืนมีข้อมูลเกี่ยวกับวิธีการตีความ มันอาจจะเทียบเท่ากับ C ของตัวแปรพื้นฐาน "เมื่อคุณได้รับสิ่งนี้ลองดูว่ามันคืออะไร - ฉันไม่สามารถบอกคุณล่วงหน้าว่าคุณจะพบอะไร"
Floris

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

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

4
@RobertHarvey ใช่แล้ว แต่คุณไม่ได้ทำการยกเลิกตัวชี้ void คุณกำลังชี้โมฆะตัวชี้ไปที่ตัวชี้ถ่านแล้วลงทะเบียนตัวชี้ถ่าน
user253751

4
@ Floris gccไม่เห็นด้วยกับคุณ หากคุณพยายามใช้ค่าให้gccสร้างข้อความแสดงข้อผิดพลาดerror: void value not ignored as it ought to beขึ้น
kasperd

19

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


ที่ช่วยในการ ในฐานะคนที่เรียนรู้ C หลังจากหลายปีที่ใช้ภาษาเชิงวัตถุพอยน์เตอร์เป็นสิ่งที่เป็นแนวคิดใหม่สำหรับฉัน ดูเหมือนว่าเมื่อส่งคืนตัวชี้voidจะเทียบเท่ากับ*ภาษาเช่น ActionScript 3 ซึ่งหมายถึง "ประเภทการส่งคืนใด ๆ "
Naftuli Kay

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

2
ฉันก็จะบอกว่าvoid*จุดที่ "โมฆะ" ตัวชี้เปลือยที่ไม่มีสิ่งใดชี้ไปที่ คุณยังสามารถส่งมันเหมือนตัวชี้อื่น ๆ ได้
Robert

11

มากระชับคำศัพท์กันหน่อย

จากมาตรฐาน C 2011 ออนไลน์ :

6.2.5 ประเภท
...
19 voidประเภทประกอบด้วยชุดค่าที่ว่างเปล่า; เป็นประเภทวัตถุที่ไม่สมบูรณ์ที่ไม่สามารถทำให้เสร็จ
...
6.3 การแปลง
...
6.3.2.2 โมฆะ

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

6.3.2.3 พอยน์เตอร์

1 ตัวชี้ไปที่voidอาจถูกแปลงเป็นหรือจากตัวชี้เป็นวัตถุชนิดใดก็ได้ ตัวชี้ไปยังวัตถุชนิดใด ๆ อาจถูกแปลงเป็นตัวชี้เพื่อโมฆะและกลับมาอีกครั้ง; ผลลัพธ์จะเปรียบเทียบเท่ากับตัวชี้ดั้งเดิม

voidแสดงออกไม่มีคุณค่า (แม้ว่ามันอาจจะมีผลข้างเคียง) ถ้าฉันมีฟังก์ชั่นที่กำหนดไว้เพื่อส่งกลับvoidเช่น:

void foo( void ) { ... }

จากนั้นโทร

foo();

ไม่ได้ประเมินค่า; ฉันไม่สามารถกำหนดผลลัพธ์ให้กับทุกสิ่งได้เนื่องจากไม่มีผลลัพธ์

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

คุณไม่สามารถอ้างอิงโดยตรงกับ void * ; คุณต้องกำหนดให้กับชนิดตัวชี้วัตถุอื่นก่อนจึงจะสามารถเข้าถึงวัตถุที่ชี้ไปยัง

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

ใช่การใช้คำหลักเดียวกันสำหรับสองแนวคิดที่แตกต่างกัน (ไม่มีค่าเทียบกับตัวชี้ทั่วไป) ทำให้เกิดความสับสน แต่ไม่ใช่ว่าไม่มีแบบอย่างมาก่อน staticมีความหมายที่แตกต่างหลากหลายทั้งใน C และ C ++


9

ใน Java และ C # ดูเหมือนว่าโมฆะเป็นประเภทส่งคืนสำหรับวิธีการ: วิธีนี้จะไม่ส่งคืนสิ่งใด ไม่มีอะไร ไม่มีการส่งคืน คุณจะไม่ได้รับอะไรจากวิธีนี้

คำสั่งนั้นถูกต้อง มันถูกต้องสำหรับ C และ C ++ เช่นกัน

ใน C เป็นโมฆะเป็นชนิดส่งคืนหรือเป็นชนิดพารามิเตอร์วิธีหมายถึงสิ่งที่แตกต่างกัน

คำสั่งนั้นไม่ถูกต้อง voidเป็นชนิดส่งคืนใน C หรือ C ++ หมายถึงสิ่งเดียวกันกับที่ทำใน C # และ Java คุณมีความสับสนกับvoid พวกเขาแตกต่างอย่างสิ้นเชิงvoid*

มันจะไม่ทำให้เกิดความสับสนว่าvoidและvoid*หมายถึงสองสิ่งที่แตกต่างอย่างสิ้นเชิง?

อ๋อ

ตัวชี้คืออะไร ตัวชี้โมฆะคืออะไร?

ชี้เป็นค่าซึ่งอาจจะdereferenced dereferencing ถูกต้องชี้ให้สถานที่เก็บของแหลม-ประเภท ชี้โมฆะเป็นตัวชี้ซึ่งไม่มีโดยเฉพาะอย่างยิ่งชี้พิมพ์; มันจะต้องแปลงเป็นชนิดตัวชี้เฉพาะเจาะจงมากขึ้นก่อนที่ค่าถูกdereferencedการผลิตที่มีสถานที่เก็บ

โมฆะพอยน์เตอร์เหมือนกันใน C, C ++, Java และ C # หรือไม่?

Java ไม่มีพอยน์เตอร์พอยน์เตอร์; C # ทำ พวกเขาเหมือนกับใน C และ C ++ - ค่าตัวชี้ที่ไม่มีประเภทเฉพาะที่เกี่ยวข้องกับมันที่จะต้องถูกแปลงเป็นประเภทที่เฉพาะเจาะจงมากขึ้นก่อนที่จะลงทะเบียนเพื่อสร้างที่เก็บข้อมูล

เนื่องจาก C เก่ากว่า Java และ C # ทำไมภาษาเหล่านี้จึงถือเป็นโมฆะในความหมาย "ไม่มีอะไร" ในขณะที่ C ใช้เป็น "ไม่มีอะไรหรืออะไร (เมื่อตัวชี้)"?

คำถามนั้นไม่เชื่อมโยงกันเพราะมันอนุมานถึงความเท็จ ลองถามคำถามที่ดีกว่า

เหตุใด Java และ C # จึงใช้หลักการที่voidเป็นประเภทการส่งคืนที่ถูกต้องแทนที่จะใช้การประชุม Visual Basic ที่ฟังก์ชั่นจะไม่กลับเป็นโมฆะ

เพื่อให้คุ้นเคยกับโปรแกรมเมอร์ที่มาถึง Java หรือ C # จากภาษาที่voidเป็นประเภทส่งคืน

เหตุใด C # จึงนำมาใช้ในการประชุมที่สับสนซึ่งvoid*มีความหมายแตกต่างไปจากเดิมอย่างสิ้นเชิงvoid?

เพื่อให้คุ้นเคยกับโปรแกรมเมอร์ที่มาจาก C # จากภาษาที่void*เป็นประเภทพอยน์เตอร์


5
void describe(void *thing);
void *move(void *location, Direction direction);

ฟังก์ชั่นแรกไม่ส่งคืนอะไรเลย ฟังก์ชั่นที่สองส่งกลับตัวชี้โมฆะ ฉันจะประกาศฟังก์ชั่นที่สองเป็น

void* move(void* location, Direction direction);

หากอาจช่วยได้ถ้าคุณคิดว่า "โมฆะ" เป็นความหมาย "ไม่มีความหมาย" ค่าส่งคืนของdescribeคือ "ไม่มีความหมาย" การส่งคืนค่าจากฟังก์ชันดังกล่าวนั้นผิดกฎหมายเพราะคุณบอกกับผู้แปลว่าค่าส่งคืนนั้นไม่มีความหมาย คุณไม่สามารถจับค่าส่งคืนจากฟังก์ชันนั้นได้เนื่องจากไม่มีความหมาย คุณไม่สามารถประกาศตัวแปรประเภทvoidเนื่องจากไม่มีความหมาย ตัวอย่างเช่นvoid nonsense;ไร้สาระและในความเป็นจริงผิดกฎหมาย โปรดสังเกตว่าอาเรย์ของโมฆะvoid void_array[42];นั้นเป็นเรื่องไร้สาระเช่นกัน

ตัวชี้เป็นโมฆะ ( void*) เป็นสิ่งที่แตกต่าง มันเป็นตัวชี้ไม่ใช่อาร์เรย์ อ่านvoid*ตามความหมายของตัวชี้ที่ชี้ไปยังสิ่งที่ "ไม่มีความหมาย" ตัวชี้คือ "ไม่มีความหมาย" ซึ่งหมายความว่ามันไม่สมเหตุสมผลที่จะตรวจสอบตัวชี้ดังกล่าว การพยายามทำสิ่งนั้นผิดกฎหมาย รหัสที่ dereferences ตัวชี้โมฆะจะไม่คอมไพล์

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

สุดท้ายเหตุผลที่คุณมักจะเห็นmoveการประกาศให้เป็นvoid *move(...)มากกว่าvoid* move(...)เป็นเพราะการวางดอกจันถัดจากชื่อมากกว่าประเภทคือการปฏิบัติที่พบบ่อยมากใน C. เหตุผลก็คือว่าการประกาศดังต่อไปนี้คุณจะได้รับในปัญหาใหญ่: int* iptr, jptr;นี้จะทำให้คุณ intคิดว่าคุณจะประกาศสองตัวชี้ไปยัง คุณไม่ใช่. ประกาศนี้จะทำให้มากกว่าตัวชี้ไปยังอีกด้วย การประกาศที่เหมาะสมคือคุณสามารถทำเป็นเครื่องหมายดอกจันที่เป็นของประเภทหากคุณยึดติดกับการปฏิบัติของการประกาศเพียงหนึ่งตัวแปรต่อคำสั่งการประกาศ แต่โปรดจำไว้ว่าคุณแกล้งทำเป็นjptrintintint *iptr, *jptr;


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

@CodyGray - ขอบคุณ! แก้ไขที่ รหัสที่ dereferences ตัวชี้โมฆะจะคอมไพล์ (ตราบใดที่ตัวชี้ไม่ได้void* null_ptr = 0;)
David Hammen

2

ใส่เพียงแค่ไม่มีความแตกต่าง ฉันขอยกตัวอย่างให้คุณ

บอกว่าฉันมีชั้นเรียนที่เรียกว่ารถ

Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type

ผมเชื่อว่านี่สับสนที่นี่จะตามออกวิธีการที่คุณจะกำหนดVSvoid void*return-type of voidหมายความว่า "ฉันคืนค่าอะไร" ในขณะที่ชนิด return ของvoid*หมายถึง "ฉันกลับตัวชี้ชนิดไม่มีอะไร"

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

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

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

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

สำหรับเราโปรแกรมเมอร์นั่นหมายความว่าข้อมูลส่วนใหญ่ที่เราต้องดูแลด้วยตนเองใน C / C ++ นั้นได้รับการดูแลจากเราโดย Object คลาสทำให้ไม่จำเป็นต้องใช้ตัวพิมพ์พิเศษที่เป็นตัวชี้


"โมฆะของฉัน * ชี้ไปที่สิ่งที่ไม่มีประเภทที่กำหนด" - ไม่แน่นอน มันถูกต้องมากกว่าที่จะบอกว่ามันเป็นตัวชี้ไปยังค่าความยาวเป็นศูนย์ (เกือบเหมือนอาร์เรย์ที่มีองค์ประกอบ 0 รายการ แต่ไม่มีข้อมูลประเภทที่เกี่ยวข้องกับอาร์เรย์
Scott Whitlock

2

ฉันจะพยายามบอกว่าใครจะนึกถึงสิ่งเหล่านี้ใน C. ภาษาราชการในมาตรฐาน C ไม่สบายใจจริง ๆvoidและฉันจะไม่พยายามสอดคล้องกับมันทั้งหมด

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

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

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


พูดอย่างเคร่งครัดก็สามารถโยนจากและไปยังประเภทใดชี้วัตถุโดยไม่สูญเสียข้อมูล แต่ก็ไม่เสมอไปและกลับจาก โดยทั่วไปการส่งจากvoid*ตัวชี้ไปยังตัวชี้ประเภทอื่นอาจสูญเสียข้อมูล (หรือแม้กระทั่งมี UB ฉันจำไม่ได้ด้วยมือ) แต่ถ้าค่าที่void*มาจากการส่งตัวชี้ประเภทเดียวกันกับที่คุณกำลังส่งไปแล้ว มันไม่
Steve Jessop

0

ใน C # และ Java ทุกคลาสจะได้รับมาจากObjectคลาส ดังนั้นเมื่อใดก็ตามที่เราต้องการที่จะผ่านการอ้างอิงถึง "สิ่งที่" Objectเราสามารถใช้การอ้างอิงจากประเภท

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


0

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

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

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

การใช้void*เป็นศัพท์สำหรับตัวชี้ไปยังสิ่งที่ชนิดโดยพลการเป็นเท่าที่ฉันรู้เฉพาะ C และอนุพันธ์โดยตรงเช่น C ++; ภาษาอื่นที่ฉันรู้จักในการจัดการกับแนวคิดโดยใช้ประเภทที่มีชื่อชัดเจน


-1

คุณสามารถนึกถึงคำสำคัญvoidเช่นว่างเปล่าstruct( ใน C99 empty structs ไม่ได้รับอนุญาตอย่างชัดเจนใน. NET และ C ++ พวกเขาใช้หน่วยความจำมากกว่า 0 และสุดท้ายฉันจำทุกอย่างใน Java เป็นคลาส):

struct void {
};

... ซึ่งหมายความว่าเป็นประเภทที่มีความยาว 0 นี่คือวิธีการทำงานเมื่อคุณเรียกใช้ฟังก์ชันที่ส่งคืนvoid... แทนที่จะจัดสรร 4 ไบต์หรือมากกว่านั้นในสแต็กสำหรับค่าส่งคืนจำนวนเต็มจะไม่เปลี่ยนตัวชี้สแต็กเลย (เพิ่มขึ้นตามความยาว 0 )

ดังนั้นหากคุณประกาศตัวแปรบางอย่างเช่น:

int a;
void b;
int c;

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


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