จะเกิดอะไรขึ้นถ้าฉันกำหนดอาร์เรย์ขนาด 0 ใน C / C ++


127

แค่อยากรู้ว่าจะเกิดอะไรขึ้นถ้าฉันกำหนดอาร์เรย์ที่มีความยาวเป็นศูนย์int array[0];ในโค้ด? GCC ไม่บ่นเลย

โปรแกรมตัวอย่าง

#include <stdio.h>

int main() {
    int arr[0];
    return 0;
}

การอธิบาย

ฉันกำลังพยายามหาว่าอาร์เรย์ที่มีความยาวเป็นศูนย์เริ่มต้นด้วยวิธีนี้แทนที่จะชี้ไปที่ความยาวตัวแปรในความคิดเห็นของ Darhazer นั้นได้รับการปรับให้เหมาะสมหรือไม่

นี่เป็นเพราะฉันต้องปล่อยโค้ดบางส่วนออกไปในป่าดังนั้นฉันจึงพยายามคิดว่าฉันต้องจัดการกับกรณีที่SIZEถูกกำหนดไว้0หรือไม่ซึ่งเกิดขึ้นในบางรหัสที่มีการกำหนดแบบคงที่int array[SIZE];

ฉันรู้สึกประหลาดใจจริงๆที่ GCC ไม่บ่นซึ่งนำไปสู่คำถามของฉัน จากคำตอบที่ฉันได้รับฉันเชื่อว่าการไม่มีคำเตือนส่วนใหญ่เกิดจากการรองรับโค้ดเก่าซึ่งไม่ได้รับการอัปเดตด้วยไวยากรณ์ [] ใหม่

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


51
@AlexanderCorwin: น่าเสียดายใน C ++ ด้วยพฤติกรรมที่ไม่ได้กำหนดส่วนขยายที่ไม่ได้มาตรฐานและความผิดปกติอื่น ๆ การลองทำอะไรด้วยตัวเองมักไม่ใช่เส้นทางสู่ความรู้
Benjamin Lindley

5
@JustinKirk ฉันเพิ่งถูกขังโดยการทดสอบและการยึดมันทำงาน และเนื่องจากคำวิจารณ์ที่ฉันได้รับในโพสต์ของฉันฉันได้เรียนรู้ว่าการทดสอบและการตรวจสอบการทำงานไม่ได้หมายความว่าถูกต้องและถูกกฎหมาย ดังนั้นการทดสอบตัวเองจึงใช้ไม่ได้ในบางครั้ง
StormByte

2
@JustinKirk ดูคำตอบของ Matthieuสำหรับตัวอย่างที่คุณจะใช้ นอกจากนี้ยังอาจมีประโยชน์ในเทมเพลตที่ขนาดอาร์เรย์เป็นพารามิเตอร์เทมเพลต ตัวอย่างในคำถามเห็นได้ชัดว่าไม่อยู่ในบริบท
Mark Ransom

2
@JustinKirk: จุดประสงค์ของ[]Python หรือแม้แต่""ใน C คืออะไร? บางครั้งคุณมีฟังก์ชันหรือมาโครที่ต้องใช้อาร์เรย์ แต่คุณไม่มีข้อมูลที่จะใส่ลงไป
dan04

15
"C / C ++" คืออะไร? นี่เป็นสองภาษาที่แยกจากกัน
Lightness Races in Orbit

คำตอบ:


86

อาร์เรย์ต้องไม่มีขนาดเป็นศูนย์

ISO 9899: 2011 6.7.6.2:

ถ้านิพจน์เป็นนิพจน์คงที่จะต้องมีค่ามากกว่าศูนย์

ข้อความข้างต้นเป็นจริงทั้งสำหรับอาร์เรย์ธรรมดา (ย่อหน้าที่ 1) สำหรับ VLA (อาร์เรย์ความยาวตัวแปร) พฤติกรรมจะไม่ได้กำหนดหากค่าของนิพจน์น้อยกว่าหรือเท่ากับศูนย์ (ย่อหน้าที่ 5) นี่คือข้อความเชิงบรรทัดฐานในมาตรฐาน C ไม่อนุญาตให้คอมไพเลอร์ใช้งานได้แตกต่างกัน

gcc -std=c99 -pedantic ให้คำเตือนสำหรับกรณีที่ไม่ใช่ VLA


34
"ต้องให้ข้อผิดพลาดจริง ๆ " - ความแตกต่างระหว่าง "คำเตือน" และ "ข้อผิดพลาด" ไม่ได้รับการยอมรับในมาตรฐาน (กล่าวถึงเฉพาะ "การวินิจฉัย") และสถานการณ์เดียวที่การรวบรวมจะต้องหยุดลง [เช่นความแตกต่างในโลกแห่งความจริง ระหว่างคำเตือนและข้อผิดพลาด] กำลังเผชิญหน้ากับ#errorคำสั่ง
Random832

12
FYI เป็นกฎทั่วไป (C หรือ C ++) มาตรฐานของรัฐเพียง แต่สิ่งที่คอมไพเลอร์จะต้องอนุญาตให้แต่ไม่ใช่สิ่งที่พวกเขาจะต้องไม่อนุญาตให้ ในบางกรณีพวกเขาจะระบุว่าคอมไพลเลอร์ควรออก "การวินิจฉัย" แต่จะมีความเฉพาะเจาะจงมากที่สุดเท่าที่จะทำได้ ส่วนที่เหลือปล่อยให้ผู้จำหน่ายคอมไพเลอร์ แก้ไข: สิ่งที่ Random832 พูดด้วย
mcmcc

8
@Lundin "คอมไพเลอร์ไม่ได้รับอนุญาตให้สร้างไบนารีที่มีอาร์เรย์ที่มีความยาวเป็นศูนย์" มาตรฐานบอกว่าไม่มีอะไรแน่นอน มีเพียงบอกว่าต้องสร้างข้อความวินิจฉัยอย่างน้อยหนึ่งข้อความเมื่อให้ซอร์สโค้ดมีอาร์เรย์ที่มีนิพจน์คงที่ความยาวเป็นศูนย์สำหรับขนาดของมัน สถานการณ์เดียวที่มาตรฐานห้ามไม่ให้คอมไพลเลอร์สร้างไบนารีคือถ้าพบ#errorคำสั่งพรีโปรเซสเซอร์
Random832

5
@Lundin การสร้างไบนารีสำหรับกรณีที่ถูกต้องทั้งหมดเป็นไปตาม # 1 และการสร้างหรือไม่สร้างขึ้นสำหรับกรณีที่ไม่ถูกต้องจะไม่ส่งผลกระทบ การพิมพ์คำเตือนก็เพียงพอสำหรับ # 3 ลักษณะการทำงานนี้ไม่มีความเกี่ยวข้องกับ # 2 เนื่องจากมาตรฐานไม่ได้กำหนดลักษณะการทำงานของซอร์สโค้ดนี้
Random832

13
@ ลุนดิน: ประเด็นก็คือคำพูดของคุณผิด คอมไพเลอร์ที่สอดคล้องจะได้รับอนุญาตให้สร้างไบนารีที่มีอาร์เรย์ความยาวเป็นศูนย์, ตราบใดที่การวินิจฉัยจะออก
Keith Thompson

85

ไม่อนุญาตให้ใช้ตามมาตรฐาน

อย่างไรก็ตามการปฏิบัติในปัจจุบันในคอมไพเลอร์ C ถือว่าการประกาศเหล่านั้นเป็นการประกาศสมาชิกอาร์เรย์แบบยืดหยุ่น ( FAM ) :

C99 6.7.2.1, §16 : ในกรณีพิเศษองค์ประกอบสุดท้ายของโครงสร้างที่มีสมาชิกที่มีชื่อมากกว่าหนึ่งคนอาจมีประเภทอาร์เรย์ที่ไม่สมบูรณ์ สิ่งนี้เรียกว่าสมาชิกอาร์เรย์ที่ยืดหยุ่น

ไวยากรณ์มาตรฐานของ FAM คือ:

struct Array {
  size_t size;
  int content[];
};

แนวคิดก็คือคุณจะจัดสรรมัน:

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

คุณอาจใช้มันแบบคงที่ (นามสกุล gcc):

Array a = { 3, { 1, 2, 3 } };

สิ่งนี้เรียกอีกอย่างว่าโครงสร้างเบาะท้าย (คำนี้มีมาก่อนการตีพิมพ์มาตรฐาน C99) หรือแฮ็คโครงสร้าง (ขอบคุณ Joe Wreschnig ที่ชี้ให้เห็น)

อย่างไรก็ตามไวยากรณ์นี้เป็นมาตรฐาน (และรับประกันผลกระทบ) เมื่อเร็ว ๆ นี้ใน C99 ก่อนหน้านี้จำเป็นต้องมีขนาดคงที่

  • 1 เป็นวิธีพกพาที่จะไปแม้ว่ามันจะค่อนข้างแปลก
  • 0 แสดงเจตนาได้ดีกว่า แต่ไม่ถูกกฎหมายเท่าที่มาตรฐานเกี่ยวข้องและได้รับการสนับสนุนเป็นส่วนเสริมโดยคอมไพเลอร์บางตัว (รวมถึง gcc)

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


@ ลุนดิน: ฉันไม่เห็น VLA ใด ๆ ที่นี่ขนาดทั้งหมดเป็นที่รู้จักในเวลาคอมไพล์ อาร์เรย์ที่มีความยืดหยุ่นระยะมาจากgcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.htmlและ DOE มีคุณสมบัติint content[];ที่นี่เท่าที่ผมเข้าใจ เนื่องจากฉันไม่ค่อยเข้าใจในแง่ของศิลปะ C ... คุณช่วยยืนยันได้ไหมว่าเหตุผลของฉันดูเหมือนถูกต้องหรือไม่?
Matthieu M.

@ MatthieuM: C99 6.7.2.1, §16: ในกรณีพิเศษองค์ประกอบสุดท้ายของโครงสร้างที่มีสมาชิกที่มีชื่อมากกว่าหนึ่งคนอาจมีประเภทอาร์เรย์ที่ไม่สมบูรณ์ สิ่งนี้เรียกว่าสมาชิกอาร์เรย์ที่ยืดหยุ่น
Christoph

สำนวนนี้รู้จักกันในชื่อ"struct hack"และฉันได้พบกับผู้คนที่คุ้นเคยกับชื่อนั้นมากกว่า "tail-padded structure" (ไม่เคยได้ยินมาก่อนยกเว้นอาจเป็นการอ้างอิงทั่วไปในการเพิ่มโครงสร้างสำหรับความเข้ากันได้ของ ABI ในอนาคต ) หรือ "สมาชิกอาร์เรย์แบบยืดหยุ่น" ซึ่งฉันได้ยินครั้งแรกใน C99

1
การใช้อาร์เรย์ขนาด 1 สำหรับแฮ็คโครงสร้างจะหลีกเลี่ยงการมีคอมไพเลอร์ไม่เป็นระเบียบ แต่เป็นเพียง "แบบพกพา" เนื่องจากผู้เขียนคอมไพเลอร์ดีพอที่จะรับทราบการใช้งานดังกล่าวว่าเป็นมาตรฐานโดยพฤตินัย หากไม่มีข้อห้ามเกี่ยวกับอาร์เรย์ที่มีขนาดเป็นศูนย์การใช้อาร์เรย์องค์ประกอบเดียวของโปรแกรมเมอร์ในฐานะตัวแทนแบบ crummy และทัศนคติทางประวัติศาสตร์ของผู้เขียนคอมไพเลอร์ว่าควรตอบสนองความต้องการของโปรแกรมเมอร์แม้ว่าจะไม่ได้กำหนดตามมาตรฐานก็ตามผู้เขียนคอมไพเลอร์สามารถทำได้ ได้รับการปรับfoo[x]ให้เหมาะสมอย่างง่ายดายและมีประโยชน์foo[0]เมื่อใดก็ตามที่fooเป็นอาร์เรย์องค์ประกอบเดียว
supercat

1
@RobertSsupportsMonicaCellio: มันเป็นแสดงให้เห็นอย่างชัดเจนในคำตอบ แต่ในตอนท้าย ฉันได้โหลดคำอธิบายไว้ล่วงหน้าเช่นกันเพื่อให้ชัดเจนขึ้นตั้งแต่เริ่มต้น
Matthieu M.

58

ใน Standard C และ C ++ ไม่อนุญาตให้ใช้อาร์เรย์ขนาดศูนย์..

หากคุณใช้ GCC ให้รวบรวมด้วย-pedanticตัวเลือก มันจะเตือนโดยพูดว่า:

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

ในกรณีของ C ++ จะให้คำเตือนที่คล้ายกัน


9
ใน Visual C ++ 2010:error C2466: cannot allocate an array of constant size 0
Mark Ransom

4
- ข้อผิดพลาดเพียงแค่เปลี่ยนคำเตือนทั้งหมดให้เป็นข้อผิดพลาดซึ่งไม่ได้แก้ไขพฤติกรรมที่ไม่ถูกต้องของคอมไพเลอร์ GCC
Lundin

C ++ Builder 2009 ยังให้ข้อผิดพลาดอย่างถูกต้อง:[BCC32 Error] test.c(3): E2021 Array must have at least one element
Lundin

1
แต่-pedantic -Werrorคุณก็ทำได้เช่นกัน-pedantic-errors
Stephan Dollberg

3
std::arrayอาร์เรย์ศูนย์ขนาดไม่มากในสิ่งเดียวกันเป็นศูนย์ขนาดใหญ่ (นอกเหนือจาก: ฉันจำได้ แต่ไม่พบแหล่งที่มาที่ VLAs ได้รับการพิจารณาและถูกปฏิเสธอย่างชัดเจนจากการอยู่ใน C ++)

27

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

char someCondition[ condition ];

ถ้าconditionเป็นเท็จแสดงว่าฉันได้รับข้อผิดพลาดเวลาคอมไพล์ เนื่องจากคอมไพเลอร์อนุญาตสิ่งนี้ฉันจึงใช้:

char someCondition[ 2 * condition - 1 ];

สิ่งนี้ให้ขนาด 1 หรือ -1 และฉันไม่เคยพบคอมไพเลอร์ที่ยอมรับขนาด -1


นี่คือแฮ็คที่น่าสนใจที่จะใช้สำหรับ
Alex Koay

10
ฉันคิดว่ามันเป็นเคล็ดลับทั่วไปในการเขียนโปรแกรมเมตา ฉันจะไม่แปลกใจถ้ามีการนำไปSTATIC_ASSERTใช้
James Kanze

ทำไมไม่ลอง:#if condition \n #error whatever \n #endif
Jerfov2

1
@ Jerfov2 เพราะอาจไม่ทราบเงื่อนไขในเวลาก่อนการประมวลผลเพียงเวลาคอมไพล์
rmeador

9

ฉันจะเพิ่มว่ามีทั้งหน้าของเอกสารออนไลน์ของ GCC ในเรื่องนี้

คำพูดบางส่วน:

อนุญาตให้ใช้อาร์เรย์ที่มีความยาวเป็นศูนย์ใน GNU C

ใน ISO C90 คุณจะต้องให้เนื้อหามีความยาว 1

และ

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

ดังนั้นคุณสามารถทำได้

int arr[0] = { 1 };

และบูม :-)


ฉันสามารถทำเช่นint a[0]นั้นa[0] = 1 a[1] = 2??
Suraj Jain

2
@SurajJain หากคุณต้องการเขียนทับสแต็กของคุณ :-) C ไม่ได้ตรวจสอบดัชนีเทียบกับขนาดของอาร์เรย์ที่คุณเขียนดังนั้นคุณสามารถทำได้a[100000] = 5แต่ถ้าคุณโชคดีคุณก็จะทำให้แอปของคุณพังถ้าคุณโชคดี: -)
xanatos

Int a [0]; หมายถึงอาร์เรย์ตัวแปร (อาร์เรย์ขนาดศูนย์) ตอนนี้ฉันจะกำหนดได้อย่างไร
Suraj Jain

@SurajJain ส่วนใดของ "C ไม่ตรวจสอบดัชนีเทียบกับขนาดของอาร์เรย์ที่คุณเขียน" ไม่ชัดเจน? ไม่มีการตรวจสอบดัชนีใน C คุณสามารถเขียนหลังจากสิ้นสุดอาร์เรย์และทำให้คอมพิวเตอร์ขัดข้องหรือเขียนทับบิตที่มีค่าของหน่วยความจำของคุณ ดังนั้นถ้าคุณมีอาร์เรย์ 0 องค์ประกอบคุณสามารถเขียนหลังจากสิ้นสุดองค์ประกอบ 0
xanatos


9

การใช้อาร์เรย์ที่มีความยาวเป็นศูนย์อีกวิธีหนึ่งคือการสร้างออบเจ็กต์ที่มีความยาวตัวแปร (pre-C99) อาร์เรย์ที่มีความยาวเป็นศูนย์แตกต่างจากอาร์เรย์แบบยืดหยุ่นซึ่งมี [] โดยไม่มี 0

ยกมาจากgcc doc :

อนุญาตให้ใช้อาร์เรย์ที่มีความยาวเป็นศูนย์ใน GNU C ซึ่งมีประโยชน์มากในฐานะองค์ประกอบสุดท้ายของโครงสร้างที่เป็นส่วนหัวของวัตถุที่มีความยาวตัวแปร:

 struct line {
   int length;
   char contents[0];
 };
 
 struct line *thisline = (struct line *)
   malloc (sizeof (struct line) + this_length);
 thisline->length = this_length;

ใน ISO C99 คุณจะใช้สมาชิกอาร์เรย์ที่ยืดหยุ่นซึ่งแตกต่างกันเล็กน้อยในด้านไวยากรณ์และความหมาย:

  • สมาชิกอาร์เรย์ที่ยืดหยุ่นถูกเขียนเป็นเนื้อหา [] โดยไม่มี 0
  • สมาชิกอาร์เรย์ที่ยืดหยุ่นมีประเภทที่ไม่สมบูรณ์ดังนั้นจึงไม่สามารถใช้ตัวดำเนินการ sizeof ได้

ตัวอย่างในโลกแห่งความเป็นจริงคืออาร์เรย์ที่มีความยาวเป็นศูนย์ของstruct kdbus_itemในkdbus.h (โมดูลเคอร์เนลของ Linux)


2
IMHO ไม่มีเหตุผลที่ดีที่มาตรฐานจะห้ามอาร์เรย์ที่มีความยาวเป็นศูนย์ มันสามารถมีวัตถุขนาดศูนย์ได้ดีในฐานะสมาชิกของโครงสร้างและถือว่าพวกมันมีvoid*วัตถุประสงค์ในการคำนวณทางคณิตศาสตร์ (ดังนั้นการเพิ่มหรือลบพอยน์เตอร์ให้กับวัตถุที่มีขนาดศูนย์จะไม่ได้รับอนุญาต) ในขณะที่สมาชิกอาร์เรย์แบบยืดหยุ่นส่วนใหญ่จะดีกว่าอาร์เรย์ขนาดศูนย์ แต่พวกเขายังสามารถทำหน้าที่เป็น "ยูเนี่ยน" ให้กับนามแฝงได้โดยไม่ต้องเพิ่มระดับของ "วากยสัมพันธ์" เพิ่มเติมให้กับสิ่งต่อไปนี้ (เช่นกำหนดให้struct foo {unsigned char as_bytes[0]; int x,y; float z;}สามารถเข้าถึงสมาชิกได้x.. z...
supercat

... โดยตรงโดยไม่ต้องพูดเช่นmyStruct.asFoo.xฯลฯ ยิ่งไปกว่านั้น IIRC, C squawks ด้วยความพยายามใด ๆ ที่จะรวมสมาชิกอาร์เรย์ที่ยืดหยุ่นไว้ในโครงสร้างดังนั้นจึงเป็นไปไม่ได้ที่จะมีโครงสร้างซึ่งรวมถึงสมาชิกอาร์เรย์แบบยืดหยุ่นอื่น ๆ ที่มีความยาวที่ทราบ เนื้อหา.
supercat

@supercat เหตุผลที่ดีคือการรักษาความสมบูรณ์ของกฎเกี่ยวกับการเข้าถึงขอบเขตอาร์เรย์ภายนอก ในฐานะสมาชิกคนสุดท้ายของโครงสร้างสมาชิกอาร์เรย์แบบยืดหยุ่น C99 จะได้รับผลเช่นเดียวกับอาร์เรย์ขนาดศูนย์ GCC แต่ไม่จำเป็นต้องเพิ่มกรณีพิเศษให้กับกฎอื่น ๆ IMHO เป็นการปรับปรุงที่sizeof x->contentsเป็นข้อผิดพลาดใน ISO C ซึ่งต่างจากการคืนค่า 0 ใน gcc อาร์เรย์ขนาดศูนย์ที่ไม่ใช่สมาชิกของโครงสร้างทำให้เกิดปัญหาอื่น ๆ อีกมากมาย
MM

@MM: พวกเขาจะเกิดปัญหาอะไรหากการลบพอยน์เตอร์สองตัวที่เท่ากันไปยังอ็อบเจ็กต์ที่มีขนาดศูนย์ถูกกำหนดว่าให้ผลเป็นศูนย์ (เช่นเดียวกับการลบพอยน์เตอร์ที่เท่ากันไปยังอ็อบเจ็กต์ขนาดใดก็ได้) และการลบพอยน์เตอร์ที่ไม่เท่ากันไปยังอ็อบเจ็กต์ที่มีขนาดศูนย์ถูกกำหนดให้เป็นการให้ผล ไม่ระบุมูลค่า? ถ้า Standard ได้ระบุว่าการนำไปใช้งานอาจอนุญาตให้มี Struct ที่มี FAM ฝังอยู่ภายในโครงสร้างอื่นโดยที่องค์ประกอบถัดไปในโครงสร้างหลังเป็นอาร์เรย์ที่มีองค์ประกอบประเภทเดียวกับ FAM หรือโครงสร้างที่ขึ้นต้นด้วยอาร์เรย์ดังกล่าว และมีเงื่อนไขว่า ...
supercat

... มันรับรู้ว่า FAM เป็นนามแฝงของอาร์เรย์ (หากกฎการจัดตำแหน่งจะทำให้อาร์เรย์ลงจอดที่ออฟเซ็ตที่แตกต่างกันจะต้องมีการวินิจฉัย) ซึ่งจะมีประโยชน์มาก ตามที่เป็นอยู่ไม่มีวิธีที่ดีที่จะมีวิธีการที่ยอมรับตัวชี้ไปยังโครงสร้างของรูปแบบทั่วไปstruct {int n; THING dat[];}และสามารถทำงานกับสิ่งต่างๆที่มีระยะเวลาคงที่หรืออัตโนมัติได้
supercat

6

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

แฮ็คโครงสร้างตามที่ใช้กันทั่วไปโดยใช้อาร์เรย์ขนาด 1 นั้นหลบหลีกและฉันไม่คิดว่ามีข้อกำหนดใด ๆ ที่คอมไพเลอร์จะละเว้นจากการทำลายมัน ยกตัวอย่างเช่นผมจะคาดหวังว่าถ้าคอมไพเลอร์เห็นint a[1]ก็จะเป็นภายในสิทธิในการเรื่องเป็นa[i] a[0]หากมีคนพยายามแก้ไขปัญหาการจัดตำแหน่งของการแฮ็กโครงสร้างผ่านทางไฟล์

typedef struct {
  ขนาด uint32_t;
  ข้อมูล uint8_t [4]; // ใช้สี่ตัวเพื่อหลีกเลี่ยงการเว้นช่องว่างในการทิ้งขนาดของโครงสร้าง
}

คอมไพเลอร์อาจฉลาดขึ้นและคิดว่าขนาดอาร์เรย์เป็นสี่:

; ตามที่เขียน
  foo = myStruct-> ข้อมูล [i];
; ตามที่ตีความ (สมมติว่าฮาร์ดแวร์น้อยที่สุด)
  foo = ((* (uint32_t *) myStruct-> ข้อมูล) >> (i << 3)) & 0xFF;

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


1
สมาชิกของอาร์เรย์ที่มีความยืดหยุ่นได้ถูกเพิ่มเข้าไป C99 เป็นรุ่นที่ถูกต้องตามกฎหมายของสับที่ struct
MM

Standard กล่าวว่าการเข้าถึงสมาชิกอาร์เรย์ที่แตกต่างกันจะไม่ขัดแย้งกันซึ่งจะทำให้การปรับให้เหมาะสมนั้นเป็นไปไม่ได้
Ben Voigt

@BenVoigt: มาตรฐานภาษา C ไม่ได้ระบุเอฟเฟกต์ของการเขียนไบต์และอ่านคำที่มีพร้อมกัน แต่ 99.9% ของโปรเซสเซอร์ระบุว่าการเขียนจะสำเร็จและคำนั้นจะมีทั้งเวอร์ชันใหม่หรือเก่าของ ไบต์พร้อมกับเนื้อหาที่ไม่เปลี่ยนแปลงของไบต์อื่น หากคอมไพเลอร์กำหนดเป้าหมายไปที่โปรเซสเซอร์ดังกล่าวความขัดแย้งจะเป็นอย่างไร
supercat

@supercat: มาตรฐานภาษา C รับประกันว่าการเขียนพร้อมกันสององค์ประกอบอาร์เรย์ที่แตกต่างกันจะไม่ขัดแย้งกัน ดังนั้นข้อโต้แย้งของคุณว่า (อ่านขณะเขียน) ใช้ได้ผลไม่เพียงพอ
Ben Voigt

@BenVoigt: ถ้าชิ้นส่วนของโค้ดเช่นเขียนไปยังองค์ประกอบอาร์เรย์ 0, 1 และ 2 ในบางลำดับจะไม่ได้รับอนุญาตให้อ่านทั้งสี่องค์ประกอบเป็นแบบยาวแก้ไขสามและเขียนกลับทั้งสี่ แต่ฉัน คิดว่ามันจะได้รับอนุญาตให้อ่านทั้งสี่เป็นแบบยาวแก้ไขสามเขียนกลับ 16 บิตล่างเป็นแบบสั้นและบิต 16-23 เป็นไบต์ คุณจะไม่เห็นด้วยกับสิ่งนั้นหรือไม่? และโค้ดที่จำเป็นในการอ่านองค์ประกอบของอาร์เรย์เท่านั้นที่จะได้รับอนุญาตให้อ่านแบบยาวและใช้สิ่งนั้น
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.