โมฆะหมายถึงอะไรและวิธีการใช้งาน?


147

วันนี้เมื่อฉันอ่านรหัสของผู้อื่นฉันเห็นสิ่งที่ชอบvoid *func(void* i);สิ่งนี้void*หมายความว่าที่นี่สำหรับชื่อฟังก์ชันและประเภทตัวแปรตามลำดับ

นอกจากนี้เราต้องใช้ตัวชี้ประเภทนี้เมื่อใดและจะใช้งานอย่างไร


2
คุณใช้หนังสือ C เล่มใด คุณกำลังขอส่วนที่ดีกว่าของบททั้งหมด
cnicutar




ใช้คิวจากและmalloc callocหน้าคนจะกล่าวต่อไปว่า: "... ส่งกลับตัวชี้ไปยังหน่วยความจำที่จัดสรรซึ่งจัดตำแหน่งไว้อย่างเหมาะสมสำหรับชนิดข้อมูลในตัว"
หุ่นยนต์

คำตอบ:


175

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

void *มักใช้ในสถานที่ที่คุณต้องทำงานกับตัวชี้ชนิดต่าง ๆ ในรหัสเดียวกัน ตัวอย่างหนึ่งที่อ้างถึงโดยทั่วไปคือฟังก์ชั่นห้องสมุดqsort:

void qsort(void *base, size_t nmemb, size_t size, 
           int (*compar)(const void *, const void *));

baseคือที่อยู่ของอาเรย์nmembคือจำนวนองค์ประกอบในอาเรย์sizeเป็นขนาดของแต่ละองค์ประกอบและcomparเป็นตัวชี้ไปยังฟังก์ชันที่เปรียบเทียบสององค์ประกอบของอาเรย์ มันถูกเรียกเช่น:

int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);

นิพจน์อาร์เรย์iArr, dArrและlArrจะถูกแปลงโดยปริยายจากประเภทอาร์เรย์ชนิดชี้ในการเรียกใช้ฟังก์ชันและแต่ละคนจะถูกแปลงโดยปริยายจาก "ชี้ไปint/ double/ long" เป็น "ตัวชี้ไปvoid"

ฟังก์ชันเปรียบเทียบจะมีลักษณะดังนี้:

int compareInt(const void *lhs, const void *rhs)
{
  const int *x = lhs;  // convert void * to int * by assignment
  const int *y = rhs;

  if (*x > *y) return 1;
  if (*x == *y) return 0;
  return -1;
}

โดยการยอมรับvoid *, qsortสามารถทำงานกับอาร์เรย์ของชนิดใด ๆ

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

qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);

compareIntคาดว่าข้อโต้แย้งของมันจะชี้ไปที่ints แต่จริงๆแล้วทำงานกับdoubles ไม่มีทางที่จะจับปัญหานี้ในเวลารวบรวม; คุณจะจบลงด้วยอาเรย์ที่ถูกพลาด


5
มันไม่ได้รับประกันว่าvoid*สามารถส่งไปยังตัวชี้ฟังก์ชันได้ แต่สำหรับตัวชี้ข้อมูลสิ่งที่คุณพูดถือ
Vatine

ก่อนที่ตัวชี้โมฆะจะพร้อมใช้งาน "ถ่าน *" ถูกใช้แทน แต่ความว่างเปล่านั้นดีกว่าเพราะไม่สามารถนำไปใช้ในการเปลี่ยนแปลงอะไรได้โดยตรง
user50619

22

การใช้โมฆะ * หมายถึงฟังก์ชั่นสามารถใช้ตัวชี้ที่ไม่จำเป็นต้องเป็นประเภทเฉพาะ ตัวอย่างเช่นในฟังก์ชั่นซ็อกเก็ตคุณมี

send(void * pData, int nLength)

นี่หมายความว่าคุณสามารถเรียกมันได้หลายวิธีตัวอย่างเช่น

char * data = "blah";
send(data, strlen(data));

POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));

ดังนั้นมันก็เหมือนกับ generics ในภาษาอื่น ๆ แต่ไม่มีการตรวจสอบประเภทใช่ไหม?
อนุรักษ์นิยม Sendon

3
ฉันคิดว่ามันจะคล้ายกัน แต่เนื่องจากไม่มีการตรวจสอบประเภทการทำผิดพลาดอาจทำให้เกิดผลลัพธ์ที่แปลกมากหรือทำให้โปรแกรมขัดข้องทันที
TheSteve

7

C มีความโดดเด่นในเรื่องนี้ หนึ่งสามารถพูดได้voidคือความว่างเปล่าvoid*คือทุกสิ่ง (สามารถเป็นได้ทุกอย่าง)

มันมีขนาดเล็กแค่นี้*ซึ่งสร้างความแตกต่าง

เรเน่ได้ชี้ให้เห็น A void *คือตัวชี้ไปยังบางตำแหน่ง สิ่งที่มีวิธีการ "ตีความ" ที่เหลือสำหรับผู้ใช้

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

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


5
void*

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


3

คุณสามารถดูบทความเกี่ยวกับพอยน์เตอร์นี้ได้ที่http://www.cplusplus.com/doc/tutorial/pointers/และอ่านบท: โมฆะพอยน์เตอร์

ใช้ได้กับภาษา C ด้วย

ตัวชี้โมฆะชนิดของตัวชี้เป็นตัวชี้ชนิดพิเศษ ใน C ++, โมฆะหมายถึงการขาดประเภทดังนั้นตัวชี้โมฆะเป็นตัวชี้ที่ชี้ไปที่ค่าที่ไม่มีประเภท

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


3

ตัวชี้โมฆะเรียกว่าตัวชี้ทั่วไป ฉันต้องการอธิบายด้วยสถานการณ์ตัวอย่าง pthread

ฟังก์ชั่นเธรดจะมีต้นแบบเป็น

void *(*start_routine)(void*)

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

void *PrintHello(void *threadid)
{
   long tid;

   // ***Arg sent in main is retrieved   ***
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main (int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0; t<NUM_THREADS; t++){
      //*** t will be type cast to void* and send as argument.
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);   
      if (rc){
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }    
   /* Last thing that main() should do */
   pthread_exit(NULL);
}

ทำไมจึงต้องโทรpthread_exit(NULL);แทนreturn 0;เมื่อสิ้นสุดสายหลัก?
Seabass77

1
@ Seabass77 โปรดอ้างอิงstackoverflow.com/questions/3559463/…
Jeyaram

1

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


1

มาตรฐาน C11 (n1570) §6.2.2.3 al1 p55 พูดว่า:

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

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


0

ฟังก์ชั่นใช้ตัวชี้ไปยังประเภทใดก็ได้และคืนค่าดังกล่าว


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