เหตุใดจึงมีการอ้างสิทธิ์การยกเลิกการลงทะเบียนประเภทตัวชี้คำเตือนเฉพาะคอมไพเลอร์?


38

ฉันได้อ่านโพสต์ต่างๆ ในStack Overflow RE: ข้อผิดพลาดพอยน์เตอร์ที่พิมพ์ผิดประเภท ความเข้าใจของฉันคือข้อผิดพลาดโดยพื้นฐานแล้วคำเตือนของคอมไพเลอร์เกี่ยวกับอันตรายของการเข้าถึงวัตถุผ่านตัวชี้ประเภทอื่น (แม้ว่าจะมีข้อยกเว้นเกิดขึ้น) ซึ่งเป็นคำเตือนที่เข้าใจได้และสมเหตุสมผล char*

คำถามของฉันเฉพาะรหัสด้านล่าง: เหตุใดการกำหนดที่อยู่ของตัวชี้ให้มีvoid**คุณสมบัติสำหรับคำเตือนนี้ (เลื่อนเป็นข้อผิดพลาดผ่าน-Werror)

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

// main.c
#include <stdlib.h>

typedef struct Foo
{
  int i;
} Foo;

void freeFunc( void** obj )
{
  if ( obj && * obj )
  {
    free( *obj );
    *obj = NULL;
  }
}

int main( int argc, char* argv[] )
{
  Foo* f = calloc( 1, sizeof( Foo ) );
  freeFunc( (void**)(&f) );

  return 0;
}

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

มีวิธีแก้ปัญหาโดยไม่ใช้ lvaluesที่จะทำให้คำเตือน / ข้อผิดพลาดเฉพาะคอมไพเลอร์นี้สงบหรือไม่? คือฉันเข้าใจว่าและทำไมสิ่งนี้จะแก้ไขปัญหาได้ แต่ฉันต้องการหลีกเลี่ยงวิธีการนี้เพราะฉันต้องการใช้ประโยชน์จากfreeFunc() NULLที่ตั้งใจจะหาเรื่อง:

void* tmp = f;
freeFunc( &tmp );
f = NULL;

ผู้รวบรวมปัญหา (หนึ่งในหนึ่ง):

user@8d63f499ed92:/build$ /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc --version && /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-fc3-linux-gnu-gcc (GCC) 3.4.5
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c: In function `main':
./main.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules

user@8d63f499ed92:/build$

คอมไพเลอร์ไม่บ่น (หนึ่งในหลาย ๆ ):

user@8d63f499ed92:/build$ /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc --version && /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-rh73-linux-gnu-gcc (GCC) 3.2.3
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

user@8d63f499ed92:/build$

อัปเดต: ฉันได้ค้นพบคำเตือนเพิ่มเติมจะปรากฏขึ้นเฉพาะเมื่อคอมไพล์ด้วย-O2(ยังคงอยู่กับ "คอมไพเลอร์ปัญหา" เท่านั้น)



1
"a void**ยังเป็นเพียงแค่ตัวชี้นี่ควรจะเป็นการคัดเลือกนักแสดงที่ปลอดภัย" ว้าวมี skippy! ดูเหมือนคุณจะมีข้อสมมติฐานพื้นฐานเกิดขึ้น พยายามคิดให้น้อยลงในแง่ของไบต์และคานและอื่น ๆ ในแง่ของ abstractions เพราะนั่นคือสิ่งที่คุณกำลังเขียนโปรแกรมด้วย
Lightness Races ใน Orbit

7
อาจเป็นไปได้ว่าคอมไพเลอร์ที่คุณใช้อยู่มีอายุ15และ17ปี! ฉันจะไม่พึ่งพาทั้งสองอย่าง
Tavian Barnes

4
@TavianBarnes นอกจากนี้หากคุณต้องพึ่งพา GCC 3 ไม่ว่าด้วยเหตุผลใดมันเป็นการดีที่สุดที่จะใช้รุ่นสุดท้ายที่มีจุดสิ้นสุดซึ่งก็คือ 3.4.6 ฉันคิดว่า ทำไมไม่ใช้ประโยชน์จากการแก้ไขทั้งหมดที่มีให้กับซีรี่ส์นั้นก่อนที่จะหยุดพัก
Kaz

มาตรฐานการเข้ารหัส C ++ ประเภทใดที่กำหนดช่องว่างเหล่านั้นทั้งหมด?
Peter Mortensen

คำตอบ:


33

ค่าของชนิดเป็นตัวชี้ไปยังวัตถุชนิดอีกด้วยvoid** void*ประเภทของวัตถุไม่ได้เป็นวัตถุของการพิมพ์Foo*void*

มีการแปลงนัยระหว่างเป็นค่าชนิดและFoo* void*การแปลงนี้อาจเปลี่ยนการแสดงค่า ในทำนองเดียวกันคุณสามารถเขียนint n = 3; double x = n;และสิ่งนี้มีพฤติกรรมที่กำหนดไว้อย่างดีของการตั้งxค่า3.0แต่double *p = (double*)&n;มีพฤติกรรมที่ไม่ได้กำหนด (และในทางปฏิบัติจะไม่ถูกตั้งค่าpเป็น "ตัวชี้ไปที่3.0" ในสถาปัตยกรรมทั่วไปใด ๆ )

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

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

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

คุณสามารถสร้างfreefuncมาโครได้:

#define FREE_SINGLE_REFERENCE(p) (free(p), (p) = NULL)

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


1
และมันเป็นเรื่องดีที่จะรู้ว่าแม้ว่าFoo*และvoid*มีตัวแทนที่เหมือนกันในสถาปัตยกรรมของคุณก็ยังไม่ได้กำหนดให้พิมพ์พวกเขา
Tavian Barnes

12

A void *ได้รับการปฏิบัติเป็นพิเศษโดยมาตรฐาน C บางส่วนเนื่องจากมันอ้างถึงประเภทที่ไม่สมบูรณ์ การรักษานี้ไม่ได้ขยายไปvoid **ตามที่มันจะvoid *ชี้ไปที่ชนิดที่สมบูรณ์โดยเฉพาะ

กฎนามแฝงที่เข้มงวดบอกว่าคุณไม่สามารถแปลงตัวชี้ประเภทหนึ่งไปเป็นตัวชี้ประเภทอื่นและยกเลิกการอ้างถึงตัวชี้นั้นในภายหลัง ข้อยกเว้นเพียงอย่างเดียวคือเมื่อแปลงเป็นประเภทตัวอักษรซึ่งช่วยให้คุณอ่านการเป็นตัวแทนของวัตถุ

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

#define freeFunc(obj) (free(obj), (obj) = NULL)

ซึ่งคุณสามารถโทรแบบนี้:

freeFunc(f);

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

#define freeFunc(obj) ({ typeof (&(obj)) ptr = &(obj); free(*ptr); *ptr = NULL; })

3
+1 สำหรับการให้การใช้งานที่ดีขึ้นของพฤติกรรมที่ตั้งใจไว้ ปัญหาเดียวที่ฉันเห็น#defineคือคือมันจะประเมินobjสองครั้ง แต่ฉันไม่ทราบวิธีที่ดีในการหลีกเลี่ยงการประเมินครั้งที่สอง แม้แต่นิพจน์คำสั่ง (ส่วนขยาย GNU) จะไม่ทำตามที่คุณจำเป็นต้องกำหนดให้objหลังจากที่คุณใช้ค่าของมันแล้ว
cmaster - คืนสถานะโมนิกา

2
@cmaster: ถ้าคุณยินดีที่จะใช้ส่วนขยาย GNU เช่นการแสดงออกทางคำสั่งแล้วคุณสามารถใช้typeofเพื่อหลีกเลี่ยงการประเมินครั้งที่สอง:obj #define freeFunc(obj) ({ typeof(&(obj)) ptr = &(obj); free(*ptr); *ptr = NULL; })
ruakh

@ruakh เจ๋งมาก :-) จะดีมากถ้า dbush จะแก้ไขมันให้เป็นคำตอบดังนั้นมันจะไม่ได้รับการลบความคิดเห็นจำนวนมาก
cmaster - คืนสถานะโมนิกา

9

การอ้างอิงตัวชี้ชนิดที่ถูกลงโทษคือ UB และคุณไม่สามารถนับได้ว่าจะเกิดอะไรขึ้น

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

sizeof(Foo*) != sizeof(void*)กรณีที่อาจช่วยให้คุณเข้าใจว่าทำไมประเภทเล่นสำนวนในกรณีนี้สามารถจะร้ายก็คือว่าการทำงานของคุณจะไม่ทำงานบนสถาปัตยกรรมที่ ที่ได้รับอนุญาตตามมาตรฐานแม้ว่าฉันจะไม่รู้จักใด ๆ ในปัจจุบันซึ่งเป็นเรื่องจริง

วิธีแก้ปัญหาคือการใช้แมโครแทนฟังก์ชัน

โปรดทราบว่าfreeยอมรับพอยน์เตอร์ที่เป็นโมฆะ


2
sizeof Foo* != sizeof void*น่าสนใจที่จะเป็นไปได้ว่า ฉันไม่เคยพบว่าขนาดตัวชี้ "ในป่า" ขึ้นอยู่กับประเภทดังนั้นในช่วงหลายปีที่ผ่านมาฉันคิดว่ามันเป็นจริงว่าขนาดตัวชี้นั้นเท่ากันทั้งหมดในสถาปัตยกรรมที่กำหนด
StoneThrow

1
@Stonethrow ตัวอย่างมาตรฐานคือพอยน์เตอร์พอยน์เตอร์ที่ใช้เพื่อระบุไบต์ในสถาปัตยกรรม addressable word แต่ผมคิดว่าคำว่าปัจจุบันเครื่องใช้แอดเดรสทางเลือก sizeof ถ่าน == sizeof คำ
AProgrammer

2
โปรดทราบว่าจะต้องใส่เครื่องหมายวงเล็บให้กับขนาดของ ...
Antti Haapala

@StoneThrow: ไม่คำนึงถึงขนาดตัวชี้การวิเคราะห์นามแฝงแบบอิงประเภททำให้ไม่ปลอดภัย สิ่งนี้ช่วยให้คอมไพเลอร์ปรับให้เหมาะสมโดยสมมติว่าการจัดเก็บผ่านfloat*จะไม่แก้ไขint32_tวัตถุดังนั้นเช่นint32_t*ไม่ต้องการint32_t *restrict ptrให้คอมไพเลอร์คิดว่ามันไม่ได้ชี้ไปยังหน่วยความจำเดียวกัน เช่นเดียวกับร้านค้าผ่านสิ่งที่void**ถูกสมมติว่าจะไม่แก้ไขFoo*วัตถุ
Peter Cordes

4

รหัสนี้ไม่ถูกต้องตามมาตรฐาน C ดังนั้นจึงอาจใช้งานได้ในบางกรณี แต่ไม่จำเป็นต้องพกพา

"กฎนามแฝงที่เข้มงวด" สำหรับการเข้าถึงค่าผ่านตัวชี้ที่ถูกส่งไปยังตัวชี้ประเภทอื่นพบได้ใน 6.5 ย่อหน้า 7:

วัตถุต้องมีค่าที่เก็บไว้เข้าถึงได้โดยนิพจน์ lvalue ที่มีประเภทใดประเภทหนึ่งต่อไปนี้:

  • ประเภทที่เข้ากันได้กับชนิดของวัตถุที่มีประสิทธิภาพ

  • รุ่นที่ผ่านการรับรองประเภทที่เข้ากันได้กับชนิดของวัตถุที่มีประสิทธิภาพ

  • ประเภทที่เป็นประเภทที่ลงนามหรือไม่ได้ลงนามที่สอดคล้องกับประเภทที่มีประสิทธิภาพของวัตถุ

  • ประเภทที่เป็นประเภทที่ลงนามหรือไม่ได้ลงนามซึ่งสอดคล้องกับรุ่นที่ผ่านการรับรองของประเภทที่มีประสิทธิภาพของวัตถุ

  • ประเภทรวมหรือสหภาพที่มีหนึ่งในประเภทดังกล่าวในหมู่สมาชิก (รวมถึง recursively เป็นสมาชิกของกลุ่มย่อยหรือสหภาพที่มีอยู่) หรือ

  • ประเภทตัวละคร

ในของคุณ*obj = NULL;คำสั่งวัตถุที่มีประเภทที่มีประสิทธิภาพFoo*แต่มีการเข้าถึงโดยการแสดงออก lvalue มีประเภท*objvoid*

ใน 6.7.5.1 วรรค 2 เรามี

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

ดังนั้นvoid*และFoo*ไม่ใช่ประเภทที่เข้ากันได้หรือประเภทที่รองรับด้วยการเพิ่มตัวระบุและไม่เหมาะกับตัวเลือกอื่น ๆ ของกฎนามแฝงที่เข้มงวด

แม้ว่าจะไม่ใช่เหตุผลทางเทคนิคที่รหัสไม่ถูกต้อง แต่ก็มีความเกี่ยวข้องกับส่วนหมายเหตุ 6.2.5 วรรค 26:

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

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


2

นอกเหนือจากคำตอบอื่น ๆ ที่ได้กล่าวไปแล้วนี่คือรูปแบบการต่อต้านแบบดั้งเดิมใน C และหนึ่งที่ควรถูกเผาด้วยไฟ มันปรากฏใน:

  1. ฟังก์ชั่นฟรีและไม่มีค่าเช่นเดียวกับที่คุณพบคำเตือน
  2. ฟังก์ชันการจัดสรรที่หลีกเลี่ยงการสำนวน C มาตรฐานของการส่งคืนvoid *(ซึ่งไม่ได้รับจากปัญหานี้เนื่องจากเกี่ยวข้องกับการแปลงค่าแทนที่จะเป็นประเภท punning ) แทนที่จะส่งกลับค่าสถานะข้อผิดพลาดและจัดเก็บผลลัพธ์ผ่านตัวชี้ไปยังตัวชี้

สำหรับตัวอย่างอื่นของ (1) มีกรณีที่น่าอับอายยาวนานในav_freeฟังก์ชั่นของ ffmpeg / libavcodec ฉันเชื่อว่าในที่สุดมันก็ถูกแก้ไขด้วยแมโครหรือกลอุบายอื่น ๆ แต่ฉันไม่แน่ใจ

สำหรับ (2) ทั้งสองcudaMallocและposix_memalignเป็นตัวอย่าง

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


คุณมีลิงก์อธิบายเพิ่มเติมเกี่ยวกับสาเหตุที่ (1) เป็นรูปแบบต่อต้านหรือไม่ ฉันไม่คิดว่าฉันคุ้นเคยกับสถานการณ์ / ข้อโต้แย้งนี้และต้องการเรียนรู้เพิ่มเติม
StoneThrow

1
@StoneThrow: มันง่ายมาก - ความตั้งใจคือการป้องกันการใช้ผิดวัตถุประสงค์โดยการลบวัตถุที่เก็บตัวชี้ไปยังหน่วยความจำที่ถูกปลดปล่อย แต่วิธีเดียวที่สามารถทำได้คือถ้าผู้โทรกำลังเก็บตัวชี้ในวัตถุของ พิมพ์void *และหล่อ / แปลงทุกครั้งที่ต้องการตรวจสอบมัน มันไม่น่าเป็นไปได้ หากผู้เรียกเก็บตัวชี้ประเภทอื่น ๆ วิธีเดียวที่จะเรียกใช้ฟังก์ชันโดยไม่เรียกใช้ UB คือการคัดลอกตัวชี้ไปยังวัตถุ temp ประเภทvoid *แล้วส่งที่อยู่ของสิ่งนั้นไปยังฟังก์ชันที่ว่างแล้วมันก็แค่ ...
. GitHub หยุดช่วยเหลือน้ำแข็ง

1
... nulls วัตถุ temp แทนหน่วยเก็บข้อมูลจริงที่ผู้เรียกมีตัวชี้ แน่นอนว่าสิ่งที่เกิดขึ้นจริงก็คือผู้ใช้ฟังก์ชั่นท้ายทำการ(void **)แสดงโดยสร้างพฤติกรรมที่ไม่ได้กำหนด
.. GitHub หยุดช่วยเหลือน้ำแข็ง

2

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

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

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