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から:

添字演算子[]の定義は、E1 [E2]が(*((E1)+(E2)))と同一であることです。

魔法はありません。1-1相当です。いつものようにポインター(*)を逆参照するときは、それが有効なアドレスを指していることを確認する必要があります。


2
UBを取得するためにポインターを逆参照する必要がないことにも注意してください。somearray-2結果が最初からsomearray最後まで1の範囲にない限り、計算は未定義です。
RBerteig 2010

34
古い本で[]は、ポインタ演算の構文糖として参照されていました。初心者を混乱させるお気に入りの方法は、1[arr]-の代わりにarr[1]- 書いて、それが何を意味するのかを推測することです。
Dummy00001 2010

4
負の32ビットintインデックスがある場合、64ビットシステム(LP64)で何が起こりますか?インデックスはアドレス計算の前に64ビットの符号付き整数に昇格する必要がありますか?
ポールR

4
@ Paul、§6.5.6/ 8(加算演算子)から、「整数型の式がポインタに加算または減算されると、結果はポインタオペランドの型になります。ポインタオペランドが要素を指す場合配列オブジェクトの場合、配列が十分に大きい場合、結果は元の要素からの要素オフセットを指し、結果の要素と元の配列要素の添え字の差が整数式と等しくなるようにします。」だから私はそれがプロモート((E1)+(E2))され、期待値を持つ(64ビット)ポインターになると思います。
Matthew Flaschen

@Matthew:そのためのおかげで-それはのように聞こえるはずです 1が合理的に予想されるとして働いています。
ポールR

63

これはarr、が配列の2番目の要素または後の要素を指すポインターである場合にのみ有効です。それ以外の場合は、配列の境界外のメモリにアクセスするため、無効です。したがって、たとえば、これは間違っています。

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]

ただし、負の添え字を使用することはまれです。


私はそれが無効であると言うほどには行きません、単に潜在的に乱雑です
マットジョイナー8/10

13
@Matt:最初の例のコードは未定義の動作をもたらします。
James McNellis、2013

5
無効です。C標準では、明示的に未定義の動作があります。一方、int arr[10];その前に他の要素と構造体の一部で、arr[-2]潜在的に明確に定義された可能性があり、それが基づいている場合は、決定することができるoffsetof等、
R .. GitHubのSTOPが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が「違法」の意味について曖昧であることを気に入っています。最後の文は、範囲外のアクセスがコンパイルエラーをスローするように聞こえます。その本は初心者にとって毒です。
マーティン

12

私にはいいですね。ただし、正当にそれを必要とすることはまれなケースです。


9
そうではありませんということ、それは近所のオペレーターと例えば画像処理に非常に便利です-珍しいです。
ポールR

スタックとヒープ[構造/デザイン]を使用してメモリプールを作成しているので、これを使用する必要がありました。スタックはより高いメモリアドレスに向かって成長し、ヒープはより低いメモリアドレスに向かって成長します。真ん中の会議。
JMIマディソン

8

それarrはおそらく配列の中央を指していたのでarr[-2]、境界を超えずに元の配列の何かを指しています。


7

これがどれほど信頼できるかはわかりませんが、64ビットシステム(LP64と思われる)での負の配列インデックスに関する次の警告を読んだだけです。http://www.devx.com/tips/Tip/41349

著者は、配列インデックスが明示的に64ビットに昇格されない限り(たとえば、ptrdiff_tキャストによって)、32ビットint配列インデックスと64ビットアドレス指定が不適切なアドレス計算を引き起こす可能性があると言っているようです。私は実際に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にあるオブジェクトを返します。このロジックにより、Cの配列の負のインデックスにアクセスできます。


2

誰かが負のインデックスを使用する理由について、私は2つのコンテキストでそれらを使用しました。

  1. あなたにcomb [1] [-1] = 0を伝える組み合わせ番号の表を持っている; テーブルにアクセスする前にいつでもインデックスをチェックできますが、これにより、コードがすっきりして高速に実行されます。

  2. テーブルの最初にセンチネルを置く。たとえば、次のようなものを使用したい場合

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

しかし、あなたはそれiがポジティブであることも確認すべきです。
解決策:そうすることa[-1]-DBLE_MAXx&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
このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。
β.εηοιτ.βε

Pythonグルーヴィー...それらがあります。単純なユースケースは、配列のサイズを知らなくても配列の最後の要素にアクセスできることです。これは、多くのProjectの状況で非常に現実的な要件です。また、多くのDSLはこれから利益を得ます。
Rathinavelu Muthaliar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.