Sind negative Array-Indizes in C zulässig?

115

Ich habe gerade einen Code gelesen und festgestellt, dass die Person verwendet hat, arr[-2]um auf das zweite Element vor dem zuzugreifen arr, wie folgt:

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

Ist das erlaubt

Ich weiß, das arr[x]ist das gleiche wie *(arr + x). So arr[-2]ist es *(arr - 2), was in Ordnung zu sein scheint. Was denken Sie?

bodacydo
quelle

Antworten:

168

Das ist richtig. Ab C99 §6.5.2.1 / 2:

Die Definition des Indexoperators [] ist, dass E1 [E2] identisch ist mit (* ((E1) + (E2))).

Es gibt keine Magie. Es ist eine 1: 1-Äquivalenz. Wie immer, wenn Sie einen Zeiger (*) dereferenzieren, müssen Sie sicherstellen, dass er auf eine gültige Adresse zeigt.

Matthew Flaschen
quelle
2
Beachten Sie auch, dass Sie den Zeiger nicht dereferenzieren müssen, um UB zu erhalten. Das bloße Rechnen somearray-2ist undefiniert, es sei denn, das Ergebnis liegt im Bereich von Anfang somearraybis 1 nach seinem Ende.
RBerteig
34
In älteren Büchern []wurden sie als Syntaxzucker für die Zeigerarithmetik bezeichnet. Die beliebteste Art, Anfänger zu verwirren, besteht darin, zu schreiben 1[arr]- anstatt arr[1]- und ihnen zuzusehen, wie sie raten, was das bedeuten soll.
Dummy00001
4
Was passiert auf 64-Bit-Systemen (LP64), wenn Sie einen negativen 32-Bit-Int-Index haben? Sollte der Index vor der Adressberechnung auf ein 64-Bit-Int mit Vorzeichen hochgestuft werden?
Paul R
4
@Paul, aus §6.5.6 / 8 (Additive Operatoren): "Wenn ein Ausdruck mit einem ganzzahligen Typ zu einem Zeiger hinzugefügt oder von diesem subtrahiert wird, hat das Ergebnis den Typ des Zeigeroperanden. Wenn der Zeigeroperand auf ein Element zeigt eines Array-Objekts, und das Array ist groß genug, zeigt das Ergebnis auf ein Element, das vom ursprünglichen Element versetzt ist, so dass die Differenz der Indizes der resultierenden und ursprünglichen Array-Elemente gleich dem ganzzahligen Ausdruck ist. " Ich denke also, dass es gefördert wird und ((E1)+(E2))ein (64-Bit-) Zeiger mit dem erwarteten Wert sein wird.
Matthew Flaschen
@ Matthew: Danke dafür - es klingt so, als ob es funktionieren sollte , wie man es vernünftigerweise erwarten könnte.
Paul R
63

Dies ist nur gültig, wenn arres sich um einen Zeiger handelt, der auf das zweite Element in einem Array oder einem späteren Element zeigt. Andernfalls ist es nicht gültig, da Sie außerhalb der Grenzen des Arrays auf Speicher zugreifen würden. Das wäre zum Beispiel falsch:

int arr[10];

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

Aber das wäre okay:

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

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

Es ist jedoch ungewöhnlich, einen negativen Index zu verwenden.

James McNellis
quelle
Ich würde nicht so weit gehen zu sagen, dass es ungültig ist, nur potenziell chaotisch
Matt Joiner
13
@Matt: Der Code im ersten Beispiel liefert undefiniertes Verhalten.
James McNellis
5
Es ist ungültig. Nach dem C-Standard weist es explizit ein undefiniertes Verhalten auf. Auf der anderen Seite könnte int arr[10];ein Teil einer Struktur mit anderen Elementen davor arr[-2]möglicherweise genau definiert sein, und Sie könnten feststellen, ob er auf offsetofusw. basiert .
R .. GitHub STOP HELPING ICE
4
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.Ich habe es gegen Ende in K & R Abschnitt 5.3 gefunden: Ihr Beispiel hilft mir jedoch besser, es zu verstehen. Vielen Dank!
Qiang Xu
4
Entschuldigen Sie die Thread-Nekromantie, aber ich finde es einfach toll, wie zweideutig K & R ist, was "illegal" bedeutet. Der letzte Satz klingt so, als würden Zugriffe außerhalb der Grenzen einen Kompilierungsfehler auslösen. Dieses Buch ist Gift für Anfänger.
Martin
12

Klingt gut für mich. Es wäre jedoch ein seltener Fall, dass Sie es jedoch zu Recht benötigen würden.

Matt Joiner
quelle
9
Es ist nicht so selten - es ist sehr nützlich, z. B. bei der Bildverarbeitung mit Nachbarschaftsbetreibern.
Paul R
Ich musste dies nur verwenden, weil ich einen Speicherpool mit einem Stapel und einem Heap [Struktur / Design] erstelle. Der Stapel wächst in Richtung höherer Speicheradressen, der Heap wächst in Richtung niedrigerer Speicheradressen. Treffen in der Mitte.
JMI MADISON
8

Was wahrscheinlich war, arrzeigte auf die Mitte des Arrays, wodurch arr[-2]auf etwas im ursprünglichen Array gezeigt wurde, ohne die Grenzen zu überschreiten.

Igor Zevaka
quelle
7

Ich bin mir nicht sicher, wie zuverlässig dies ist, aber ich habe gerade die folgende Einschränkung zu negativen Array-Indizes auf 64-Bit-Systemen (vermutlich LP64) gelesen: http://www.devx.com/tips/Tip/41349

Der Autor scheint zu sagen, dass 32-Bit-Int-Array-Indizes mit 64-Bit-Adressierung zu fehlerhaften Adressberechnungen führen können, es sei denn, der Array-Index wird explizit auf 64 Bit hochgestuft (z. B. über eine ptrdiff_t-Umwandlung). Ich habe tatsächlich einen Fehler seiner Art mit der PowerPC-Version von gcc 4.1.0 gesehen, aber ich weiß nicht, ob es sich um einen Compiler-Fehler handelt (dh sollte gemäß C99-Standard funktionieren) oder um ein korrektes Verhalten (dh der Index benötigt eine Umwandlung in 64) Bits für korrektes Verhalten)?

Paul R.
quelle
3
Das klingt nach einem Compiler-Fehler.
Tbleher
2

Ich weiß, dass die Frage beantwortet ist, aber ich konnte nicht widerstehen, diese Erklärung zu teilen.

Ich erinnere mich an die Prinzipien des Compiler-Designs. Nehmen wir an, a ist ein int-Array und die Größe von int ist 2, und die Basisadresse für a ist 1000.

Wie a[5]wird es funktionieren ->

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

Diese Erklärung ist auch der Grund, warum negative Indizes in Arrays in C funktionieren.

dh wenn ich darauf zugreife a[-5], gebe ich es

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

Es wird mir ein Objekt an Position 990 zurückgeben. Durch diese Logik können wir auf negative Indizes in Array in C zugreifen.

Ajinkya Patil
quelle
2

Warum jemand negative Indizes verwenden möchte, habe ich in zwei Zusammenhängen verwendet:

  1. Eine Tabelle mit kombinatorischen Zahlen, aus der hervorgeht, dass Sie [1] [- 1] = 0 kämmen; Sie können Indizes immer überprüfen, bevor Sie auf die Tabelle zugreifen. Auf diese Weise sieht der Code jedoch sauberer aus und wird schneller ausgeführt.

  2. Einen Centinel an den Anfang eines Tisches setzen. Zum Beispiel möchten Sie so etwas wie verwenden

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

aber dann solltest du auch überprüfen, ob das ipositiv ist.
Lösung: macht es so , dass a[-1]ist -DBLE_MAX, so dass x&lt;a[-1]immer falsch sein wird.

Santiago Egido Arteaga
quelle
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]);
}
Rathinavelu Muthaliar
quelle
1
Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
β.εηοιτ.βε
Python groovig ... haben sie. Ein einfacher Anwendungsfall ist, dass man auf das letzte Element eines Arrays zugreifen kann, ohne die Arraygröße zu kennen, was in vielen Projektsituationen eine sehr reale Anforderung ist. Davon profitieren auch viele DSLs.
Rathinavelu Muthaliar