2009-05-25 15 views

Antwort

24

wird der Compiler

index[array] 

in

*(index + array) 

Mit der normalen Syntax drehen

array[index] 

in

*(array + index) 
machen würde

und so sehen Sie, dass beide Ausdrücke auf den gleichen Wert auswerten. Dies gilt sowohl für C als auch für C++.

+13

Diese 'Transformations'-Illustration gilt nur, wenn eine der Variablen ein Zeiger oder ein Array ist und die andere ein ganzzahliger Typ ist, d. H. Der übliche C-Fall. In C++ verliert a [b] seine Symmetrie, da operator [] eine überladene Elementfunktion sein kann und der Compiler nicht stattdessen * (a + b) verwenden darf. * (a + b) hat möglicherweise einen anderen Typ und einen anderen Wert oder ist möglicherweise kein gültiger Ausdruck. –

+0

+1. Cool. Ich habe es nie so gedacht. – ralphtheninja

+0

@Charles: guter Punkt! –

6

Von den frühesten Tagen von C an war der Ausdruck a[i] einfach die Adresse von a [0], die zu i hinzugefügt wurde (um die Größe von a [0] vergrößert) und dann dereferenziert wurde. In der Tat, alle diese waren äquivalent:

a[i] 
i[a] 
*(a+i) 

====

Das einzige, was ich besorgt wäre über die tatsächliche de-Referenzierung. Während sie alle die gleiche Adresse produzieren, kann die De-Referenzierung ein Problem sein, wenn die Typen a und i unterschiedlich sind.

Zum Beispiel:

int i = 4; 
    long a[9]; 
    long x = a[i]; //get the long at memory location X. 
    long x = i[a]; //get the int at memory location X? 

Ich habe nicht wirklich, dass das Verhalten getestet, aber es ist etwas, was Sie wollen, können achten. Wenn es das ändert, was de-referenziert wird, verursacht es wahrscheinlich auch alle möglichen Probleme mit Arrays von Objekten.

====

Update:

Sie können sich wahrscheinlich das Bit oben zwischen den ===== Linien bedenkenlos ignorieren. Ich habe es unter Cygwin mit einem kurzen und einem langen Test getestet und es scheint in Ordnung zu sein, also waren meine Befürchtungen zumindest in den grundlegenden Fällen unbegründet. Ich habe immer noch keine Ahnung, was mit komplizierteren passiert, weil ich es nie tun werde.

+0

Wie ist es heute anders ?? – Ben

+2

Ich glaube nicht, dass es anders ist. Ich meinte es stammt aus den frühesten Tagen, nicht, dass es nur so in den frühen Tagen war. – paxdiablo

+0

Das ist falsch. Wie dem auch sei, der Compiler weiß, dass das Hinzufügen eines int zu einem langen * (in beliebiger Reihenfolge) zu einem long * führt, das dann zu lange dereferenziert wird. Und das gilt für jeden beliebigen Zeigertyp, einschließlich benutzerdefinierter. –

5

als Matthew Wilson diskutiert in Imperfect C++, kann diese verwendet wird, die Typsicherheit in C++, zu erzwingen durch Verwendung von DIMENSION_OF() -ähnlichen Makros mit Instanzen von Typen zu verhindern, die den Index-Operator definiert werden, wie in:

#define DIMENSION_OF_UNSAFE(x) (sizeof(x)/sizeof((x)[0])) 

#define DIMENSION_OF_SAFER(x) (sizeof(x)/sizeof(0[(x)])) 

int ints[4]; 

DIMENSION_OF_UNSAFE(ints); // 4 
DIMENSION_OF_SAFER(ints); // 4 

std::vector v(4); 

DIMENSION_OF_UNSAFE(v); // gives impl-defined value; v likely wrong 
DIMENSION_OF_SAFER(v); // does not compile 

Es gibt mehr dazu, um mit Zeigern umzugehen, aber das erfordert einige zusätzliche Template-Smarts. Überprüfen Sie die Implementierung von STLSOFT_NUM_ELEMENTS() in den STLSoft Bibliotheken, und lesen Sie alles darüber in Kapitel 14 von Imperfect C++.

edit: Einige der Kommentatoren schlagen vor, dass die Implementierung keine Zeiger zurückweist. Es tut (sowie benutzerdefinierte Typen), wie durch das folgende Programm veranschaulicht. Sie können dies durch die unkommentierten Zeilen 16 und 18 überprüfen. (Ich habe das gerade auf Mac/GCC4 getan, und es lehnt beide Formulare ab).

1 
2 #include <stlsoft/stlsoft.h> 
3 
4 #include <vector> 
5 
6 #include <stdio.h> 
7 
8 int main() 
9 { 
10   int  ar[1]; 
11   int* p = ar; 
12   std::vector<int>  v(1); 
13 
14   printf("ar: %lu\n", STLSOFT_NUM_ELEMENTS(ar)); 
15 
16 //  printf("p: %lu\n", STLSOFT_NUM_ELEMENTS(p)); 
17 
18 //  printf("v: %lu\n", STLSOFT_NUM_ELEMENTS(v)); 
19 
20   return 0; 
21 } 
22 
+0

Eine bessere 'Dimension'-Implementierung kann mit Vorlagen erreicht werden: Vorlage Std :: Größe_T Dimension (T [N] & x) {return N; }. Das ist sicherer als die Größe von (a)/sizeof (a [0]) mit Arrays (die nicht zu Zeigern geworden sind). –

+1

Dribeas: Das Problem damit ist, dass es nicht länger ein Kompilierzeitausdruck ist. Die von Wilson beschriebene Technik veranschaulicht eine Kompilierungsversion. Wie gesagt, es ist in den STLSoft-Bibliotheken verfügbar. – dcw

+0

Es gibt auch eine Möglichkeit, einen Kompilierungszeitausdruck zu erhalten: template char (& an_array (T (&) [N])) [N]; Dies wird zu einem Bezug auf ein Char-Array mit der gleichen Größe ausgewertet.Verwenden Sie es wie folgt: int v [sizeof ein_array (ein anderes_array)]; –

0

in C und C++ (mit Array ein Zeiger oder Array ist) ist es eine Sprache-Funktion: Zeigerarithmetik. Die Operation a [b], bei der entweder a oder b ein Zeiger ist, wird in Zeigerarithmetik umgewandelt: * (a + b). Da die Addition symmetrisch ist, ändert die Neuordnung die Bedeutung nicht.

Jetzt gibt es Unterschiede für Nicht-Zeiger. Bei einem Typ A mit überladenem Operator [] ist a [4] ein gültiger Methodenaufruf (wird A :: operator aufrufen), aber das Gegenteil wird nicht kompiliert.