C에서 음의 배열 인덱스가 허용됩니까?


115

나는 방금 일부 코드를 읽고 그 사람이 arr[-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에서 :

아래 첨자 연산자 []의 정의는 E1 [E2]가 (* ((E1) + (E2)))와 동일하다는 것입니다.

마법은 없습니다. 1-1 등가입니다. 포인터 (*)를 역 참조 할 때 항상 그렇듯이 유효한 주소를 가리키고 있는지 확인해야합니다.


2
UB를 얻기 위해 포인터를 역 참조 할 필요가 없습니다. somearray-2결과가 시작부터 somearray끝까지 1 까지의 범위에 있지 않는 한 계산 은 정의되지 않습니다 .
RBerteig 2010 년

34
오래된 책에서는 포인터 산술을위한 구문 설탕[] 으로 참조되었습니다 . 초보자를 혼동 하는 가장 좋은 방법은 글을 쓰는 것 대신에 글을 쓰고 그것이 의미하는 바를 추측하는 것을 보는 것입니다. 1[arr]arr[1]
Dummy00001 2010-08-13

4
음수 인 32 비트 정수 인덱스가있는 경우 64 비트 시스템 (LP64)에서는 어떻게됩니까? 인덱스는 주소 계산 전에 64 비트 부호있는 정수로 승격되어야합니까?
Paul R

4
@Paul, §6.5.6 / 8 (더하기 연산자), "포인터에서 정수 유형의 표현식을 더하거나 빼면 결과는 포인터 피연산자의 유형이됩니다. 포인터 피연산자가 요소를 가리키는 경우 배열 객체의 크기이고 배열이 충분히 크면 결과는 결과와 원래 배열 요소의 첨자의 차이가 정수 표현식과 같도록 원래 요소에서 오프셋 된 요소를 가리 킵니다. " 그래서 나는 그것이 승격 될 것이라고 생각 ((E1)+(E2))하고 예상 값을 가진 (64 비트) 포인터가 될 것입니다.
Matthew Flaschen

@Matthew : 감사합니다- 합리적으로 예상 한대로 작동 해야하는 것 같습니다.
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]

그러나 음의 아래 첨자를 사용하는 것은 드문 경우입니다.


난 그냥 잠재적으로 혼란 무효의 말대로 지금까지 안 갈거야
매트 소목

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

저자는 64 비트 주소 지정이있는 32 비트 int 배열 색인이 배열 색인이 명시 적으로 64 비트로 승격되지 않는 한 잘못된 주소 계산을 초래할 수 있다고 말하는 것 같습니다 (예 : ptrdiff_t 캐스트를 통해). 나는 실제로 gcc 4.1.0의 PowerPC 버전에서 그의 성격의 버그를 보았지만 그것이 컴파일러 버그인지 (즉, C99 표준에 따라 작동해야하는지) 또는 올바른 동작인지 (즉, 인덱스가 64로 캐스트가 필요한지 모르겠습니다.) 올바른 행동을위한 비트)?


3
이것은 컴파일러 버그처럼 들립니다.
tbleher 2013-09-13

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에서 객체를 반환합니다.이 논리에 따라 C의 Array에있는 음수 인덱스에 액세스 할 수 있습니다.


2

누군가 음수 인덱스를 사용하려는 이유에 대해 두 가지 컨텍스트에서 사용했습니다.

  1. comb [1] [-1] = 0을 알려주는 조합 번호 테이블을 가지고 있습니다. 테이블에 액세스하기 전에 항상 인덱스를 확인할 수 있지만 이렇게하면 코드가 더 깔끔해 보이고 더 빠르게 실행됩니다.

  2. 테이블의 시작 부분에 센티넬을 넣습니다. 예를 들어, 다음과 같은 것을 사용하고 싶습니다.

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

하지만 i긍정적 인지도 확인해야합니다 .
해결 방법 : 그 있도록 할 a[-1]것입니다 -DBLE_MAX그 때문에, x&lt;a[-1]항상 false가 될 것입니다.


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
이 코드가 질문에 답할 수 있지만이 코드가 질문에 대한 이유 및 / 또는 답변 방법에 대한 추가 컨텍스트를 제공하면 장기적인 가치가 향상됩니다.
β.εηοιτ.βε

파이썬 그루비 ... 가지고 있습니다. 간단한 사용 사례는 배열 크기를 모르고 배열의 마지막 요소에 액세스 할 수 있다는 것입니다. 이는 많은 프로젝트 상황에서 매우 실제적인 요구 사항입니다. 또한 많은 DSL이 이로부터 혜택을받습니다.
Rathinavelu Muthaliar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.