อนุญาตให้ใช้ดัชนีอาร์เรย์เชิงลบใน C หรือไม่


115

ฉันเพิ่งอ่านโค้ดบางส่วนและพบว่าบุคคลนั้นกำลังใช้arr[-2]เพื่อเข้าถึงองค์ประกอบที่ 2 ก่อนหน้าarrนี้:

|a|b|c|d|e|f|g|
       ^------------ arr[0]
         ^---------- arr[1]
   ^---------------- arr[-2]

อนุญาตหรือไม่

ฉันรู้ว่าarr[x]เป็นเช่นเดียวกับ*(arr + x). นั่นarr[-2]คือ*(arr - 2)ซึ่งดูเหมือนว่าตกลง คุณคิดอย่างไร?

คำตอบ:


168

ถูกต้อง. จาก C99 §6.5.2.1 / 2:

de fi nition ของตัวดำเนินการตัวห้อย [] คือ E1 [E2] เหมือนกับ (* ((E1) + (E2)))

ไม่มีเวทมนตร์ มันเท่ากับ 1-1 เช่นเคยเมื่อต้องการอ้างอิงตัวชี้ (*) คุณต้องแน่ใจว่าชี้ไปยังที่อยู่ที่ถูกต้อง


2
โปรดทราบว่าคุณไม่จำเป็นต้องเปลี่ยนค่าตัวชี้เพื่อรับ UB การคำนวณเพียงอย่างเดียวsomearray-2ไม่ได้กำหนดไว้เว้นแต่ว่าผลลัพธ์จะอยู่ในช่วงจากจุดเริ่มต้นsomearrayถึง 1 หลังจากจุดสิ้นสุด
RBerteig

34
ในหนังสือรุ่นเก่า[]จะอ้างถึงเป็นน้ำตาลไวยากรณ์สำหรับเลขคณิตตัวชี้ วิธีที่ชื่นชอบในการสร้างความสับสนให้กับผู้เริ่มต้นคือการเขียน1[arr]- แทนที่จะเป็นarr[1]- และดูพวกเขาเดาว่ามันหมายถึงอะไร
Dummy00001

4
จะเกิดอะไรขึ้นในระบบ 64 บิต (LP64) เมื่อคุณมีดัชนี int 32 บิตซึ่งเป็นค่าลบ ดัชนีควรได้รับการเลื่อนระดับเป็น int ที่ลงชื่อ 64 บิตก่อนการคำนวณที่อยู่หรือไม่
Paul R

4
@Paul จาก§6.5.6 / 8 (ตัวดำเนินการเพิ่มเติม) "เมื่อนิพจน์ที่มีชนิดจำนวนเต็มถูกเพิ่มหรือลบออกจากตัวชี้ผลลัพธ์จะมีชนิดของตัวถูกดำเนินการตัวชี้หากตัวถูกดำเนินการชี้ไปที่องค์ประกอบ ของออบเจ็กต์อาร์เรย์และอาร์เรย์มีขนาดใหญ่พอผลลัพธ์จะชี้ไปที่องค์ประกอบที่หักล้างจากองค์ประกอบดั้งเดิมเพื่อให้ความแตกต่างของตัวห้อยขององค์ประกอบอาร์เรย์ที่เป็นผลลัพธ์และดั้งเดิมเท่ากับนิพจน์จำนวนเต็ม " ดังนั้นฉันคิดว่ามันจะได้รับการเลื่อนระดับและ((E1)+(E2))จะเป็นตัวชี้ (64 บิต) พร้อมค่าที่คาดหวัง
Matthew Flaschen

@ แมทธิว: ขอบคุณสำหรับสิ่งนั้น - ดูเหมือนว่ามันควรจะทำงานตามที่คาดหวังไว้พอสมควร
Paul R

63

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

int arr[10];

int x = arr[-2]; // invalid; out of range

แต่ก็ไม่เป็นไร:

int arr[10];
int* p = &arr[2];

int x = p[-2]; // valid:  accesses arr[0]

อย่างไรก็ตามการใช้ตัวห้อยเชิงลบเป็นเรื่องผิดปกติ


ฉันจะไม่ไปไกลถึงขนาดที่จะบอกว่ามันไม่ถูกต้องอาจเป็นเรื่องยุ่ง
Matt Joiner

13
@Matt: โค้ดในตัวอย่างแรกให้พฤติกรรมที่ไม่ได้กำหนด
James McNellis

5
มันไม่ถูกต้อง ตามมาตรฐาน C นั้นมีพฤติกรรมที่ไม่ได้กำหนดไว้อย่างชัดเจน ในทางกลับกันหากint arr[10];เป็นส่วนหนึ่งของโครงสร้างที่มีองค์ประกอบอื่น ๆ อยู่ก่อนหน้านั้นarr[-2]อาจมีการกำหนดไว้อย่างดีและคุณสามารถระบุได้ว่ามันขึ้นอยู่กับoffsetofฯลฯ หรือไม่
R .. GitHub STOP HELPING ICE

4
พบได้ใน K&R ตอนที่ 5.3 ตอนใกล้จบ: If one is sure that the elements exist, it is also possible to index backwards in an array; p[-1], p[-2], and so on are syntactically legal, and refer to the elements that immediately precede p[0]. Of course, it is illegal to refer to objects that are not within the array bounds.ยังไงตัวอย่างของคุณก็ดีกว่าช่วยให้ฉันเข้าใจ ขอบคุณ!
Qiang Xu

4
ขออภัยสำหรับความไม่แน่นอนของเธรด แต่ฉันชอบที่ K&R คลุมเครือเกี่ยวกับความหมายที่ "ผิดกฎหมาย" ประโยคสุดท้ายทำให้ดูเหมือนว่าการเข้าถึงนอกขอบเขตทำให้เกิดข้อผิดพลาดในการคอมไพล์ หนังสือเล่มนั้นเป็นยาพิษสำหรับผู้เริ่มต้น
Martin

12

ฟังดูดีสำหรับฉัน มันจะเป็นกรณีที่หายากที่คุณจะต้องใช้มันอย่างถูกต้องตามกฎหมาย


9
มันไม่ได้เป็นที่หายาก - มันมีประโยชน์มากในภาพเช่นการประมวลผลกับผู้ประกอบการย่าน
Paul R

ฉันแค่ต้องการใช้สิ่งนี้เพราะฉันกำลังสร้างพูลหน่วยความจำที่มีสแต็กและฮีป [โครงสร้าง / การออกแบบ] กองซ้อนเพิ่มขึ้นไปสู่ที่อยู่หน่วยความจำที่สูงขึ้นฮีปจะเพิ่มขึ้นไปสู่ที่อยู่หน่วยความจำที่ต่ำกว่า ประชุมตรงกลาง.
JMI MADISON

8

สิ่งที่อาจเป็นarrไปได้คือการชี้ไปที่ตรงกลางของอาร์เรย์ดังนั้นการarr[-2]ชี้ไปที่บางสิ่งในอาร์เรย์ดั้งเดิมโดยไม่ต้องออกนอกขอบเขต


7

ฉันไม่แน่ใจว่ามันน่าเชื่อถือแค่ไหน แต่ฉันเพิ่งอ่านข้อแม้ต่อไปนี้เกี่ยวกับดัชนีอาร์เรย์เชิงลบในระบบ 64 บิต (น่าจะเป็น LP64): http://www.devx.com/tips/Tip/41349

ผู้เขียนดูเหมือนจะบอกว่าดัชนี int อาร์เรย์ 32 บิตที่มีการกำหนดแอดเดรส 64 บิตอาจส่งผลให้การคำนวณแอดเดรสไม่ถูกต้องเว้นแต่ว่าดัชนีอาร์เรย์จะเลื่อนระดับเป็น 64 บิตอย่างชัดเจน (เช่นผ่านการส่ง ptrdiff_t) ฉันเคยเห็นข้อผิดพลาดของธรรมชาติของเขาด้วย gcc 4.1.0 เวอร์ชัน PowerPC แต่ฉันไม่รู้ว่ามันเป็นบั๊กของคอมไพเลอร์ (เช่นควรทำงานตามมาตรฐาน C99) หรือพฤติกรรมที่ถูกต้อง (เช่นดัชนีต้องการแคสต์ถึง 64 บิตสำหรับพฤติกรรมที่ถูกต้อง)?


3
สิ่งนี้ดูเหมือนบั๊กของคอมไพเลอร์
tbleher

2

ฉันรู้ว่าคำถามมีคำตอบ แต่ฉันอดไม่ได้ที่จะแบ่งปันคำอธิบายนี้

ฉันจำหลักการออกแบบคอมไพเลอร์ได้สมมติว่า a คืออาร์เรย์ int และขนาดของ int คือ 2 และที่อยู่ฐานสำหรับ a คือ 1000

วิธีการa[5]ทำงาน ->

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

คำอธิบายนี้ยังเป็นเหตุผลว่าทำไมดัชนีเชิงลบในอาร์เรย์จึงทำงานใน C

คือถ้าฉันเข้าถึงa[-5]มันจะให้ฉัน

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

มันจะส่งคืนวัตถุให้ฉันที่ตำแหน่ง 990 โดยตรรกะนี้เราสามารถเข้าถึงดัชนีเชิงลบใน Array ใน C


2

เกี่ยวกับสาเหตุที่บางคนต้องการใช้ดัชนีเชิงลบฉันได้ใช้ดัชนีเหล่านี้ในสองบริบท:

  1. มีตารางของตัวเลข combinatorial ที่บอกคุณว่าหวี [1] [- 1] = 0; คุณสามารถตรวจสอบดัชนีก่อนเข้าถึงตารางได้เสมอ แต่วิธีนี้ทำให้โค้ดดูสะอาดขึ้นและทำงานได้เร็วขึ้น

  2. วางเซนติเนลไว้ที่จุดเริ่มต้นของตาราง ตัวอย่างเช่นคุณต้องการใช้สิ่งที่ต้องการ

     while (x < a[i]) i--;

แต่คุณควรตรวจสอบด้วยว่าiเป็นบวก
วิธีแก้ไข: ทำให้มันa[-1]เป็น-DBLE_MAXเช่นนั้นมันx&lt;a[-1]จะเป็นเท็จเสมอ


0
#include <stdio.h>

int main() // negative index
{ 
    int i = 1, a[5] = {10, 20, 30, 40, 50};
    int* mid = &a[5]; //legal;address,not element there
    for(; i < 6; ++i)
    printf(" mid[ %d ] = %d;", -i, mid[-i]);
}

1
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีที่รหัสนี้ตอบคำถามช่วยเพิ่มคุณค่าในระยะยาว
β.εηοιτ.βε

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