c pointer - Con gli array, perché è il caso di un [5] == 5 [a]?




9 Answers

Lo standard C definisce l'operatore [] come segue:

a[b] == *(a + b)

Quindi a[5] valuterà a:

*(a + 5)

e 5[a] valuterà a:

*(5 + a)

a è un puntatore al primo elemento dell'array. a[5] è il valore che è 5 elementi più lontano da a , che è lo stesso di *(a + 5) , e dalla matematica della scuola elementare sappiamo che sono uguali (l'aggiunta è commutative ).

to c++

Come sottolinea Joel in podcast # 34 , in C Programming Language (aka: K & R), c'è menzione di questa proprietà degli array in C: a[5] == 5[a]

Joel dice che è a causa dell'aritmetica del puntatore, ma io ancora non capisco. Perché a[5] == 5[a] ?




Penso che qualcosa venga perso dalle altre risposte.

Sì, p[i] è per definizione equivalente a *(p+i) , che (poiché l'addizione è commutativa) è equivalente a *(i+p) , che (ancora, con la definizione dell'operatore [] ) è equivalente a i[p] .

(E array[i] , il nome dell'array viene convertito implicitamente in un puntatore al primo elemento dell'array.)

Ma la commutatività dell'aggiunta non è così ovvia in questo caso.

Quando entrambi gli operandi sono dello stesso tipo o anche di tipi numerici diversi che vengono promossi a un tipo comune, la commutatività ha perfettamente senso: x + y == y + x .

Ma in questo caso stiamo parlando specificamente dell'aritmetica del puntatore, in cui un operando è un puntatore e l'altro è un numero intero. (Il numero intero + intero è un'operazione diversa e il puntatore + puntatore non ha senso).

La descrizione dello standard C dell'operatore + ( N1570 6.5.6) dice:

Inoltre, entrambi gli operandi devono avere un tipo aritmetico, oppure un operando deve essere un puntatore a un tipo di oggetto completo e l'altro deve avere un tipo intero.

Avrebbe potuto facilmente dire:

Inoltre, entrambi gli operandi devono avere un tipo aritmetico, oppure l' operando di sinistra deve essere un puntatore a un tipo di oggetto completo e l' operando di destra deve avere un tipo intero.

nel qual caso entrambi i + p e i[p] sarebbero illegali.

In termini C ++, abbiamo davvero due gruppi di operatori + sovraccaricati, che possono essere genericamente descritti come:

pointer operator+(pointer p, integer i);

e

pointer operator+(integer i, pointer p);

di cui solo il primo è veramente necessario.

Allora, perché è così?

C ++ ha ereditato questa definizione da C, che l'ha ottenuta da B (la commutatività dell'indicizzazione dell'array è esplicitamente menzionata nel Riferimento degli utenti del 1972 a B ), che lo ha ottenuto da BCPL (manuale del 1967), che potrebbe averlo ottenuto anche da lingue precedenti (CPL? Algol?).

Quindi l'idea che l'indicizzazione dell'array sia definita in termini di addizione e quell'aggiunta, anche di un puntatore e di un intero, è commutativa, risale a molti decenni, alle lingue degli antenati di C.

Quelle lingue erano molto meno tipizzate rispetto alla moderna C. In particolare, la distinzione tra puntatori e numeri interi veniva spesso ignorata. (I primi programmatori C a volte usavano i puntatori come numeri interi senza segno, prima che la parola chiave unsigned venisse aggiunta alla lingua.) Quindi l'idea di rendere l'aggiunta non commutativa perché gli operandi sono di tipi diversi probabilmente non si sarebbero verificati per i progettisti di quelle lingue . Se un utente volesse aggiungere due "cose", se quelle "cose" sono numeri interi, puntatori o qualcos'altro, non era compito della lingua impedirlo.

E nel corso degli anni, qualsiasi modifica a tale regola avrebbe infranto il codice esistente (sebbene lo standard ANSI C del 1989 potesse essere una buona opportunità).

Cambiare C e / o C ++ richiedendo di posizionare il puntatore sulla sinistra e il numero intero sulla destra potrebbe rompere qualche codice esistente, ma non ci sarebbe alcuna perdita di reale potenza espressiva.

Così ora abbiamo arr[3] e 3[arr] significano esattamente la stessa cosa, anche se quest'ultima forma non dovrebbe mai apparire al di fuori della IOCCC .




Una cosa che nessuno sembra aver menzionato sul problema di Dinah con sizeof :

È possibile aggiungere un intero solo a un puntatore, non è possibile aggiungere due puntatori insieme. In questo modo quando si aggiunge un puntatore a un intero o un intero a un puntatore, il compilatore sa sempre quale bit ha una dimensione che deve essere presa in considerazione.




Bella domanda / risposte.

Voglio solo sottolineare che i puntatori e gli array C non sono gli stessi , sebbene in questo caso la differenza non sia essenziale.

Considera le seguenti dichiarazioni:

int a[10];
int* p = a;

In aout , il simbolo a è in corrispondenza di un indirizzo che è l'inizio dell'array e il simbolo p si trova in un indirizzo in cui è memorizzato un puntatore e il valore del puntatore in quella posizione di memoria è l'inizio dell'array.




Per i puntatori in C, abbiamo

a[5] == *(a + 5)

e anche

5[a] == *(5 + a)

Quindi è vero che a[5] == 5[a].




Ha un'ottima spiegazione in A TUTORIAL SU POINTERS E ARRAY IN C di Ted Jensen.

Ted Jensen lo ha spiegato come:

In effetti, questo è vero, cioè ovunque si scriva a[i] può essere sostituito con *(a + i) senza problemi. In effetti, il compilatore creerà lo stesso codice in entrambi i casi. Quindi vediamo che l'aritmetica del puntatore è la stessa cosa dell'indicizzazione dell'array. In entrambi i casi la sintassi produce lo stesso risultato.

Questo NON sta dicendo che i puntatori e gli array sono la stessa cosa, non lo sono. Stiamo solo dicendo che per identificare un dato elemento di un array abbiamo la scelta di due sintassi, una che usa l'indicizzazione dell'array e l'altra che usa l'aritmetica del puntatore, che produce risultati identici.

Ora, guardando quest'ultima espressione, parte di esso .. (a + i) , è una semplice aggiunta usando l'operatore + e le regole di stato C che tale espressione è commutativa. Questo è (a + i) è identico a (i + a) . Quindi potremmo scrivere *(i + a) con la stessa facilità con cui *(a + i) . Ma *(i + a) potrebbe provenire da i[a] ! Da tutto ciò deriva la curiosa verità che se:

char a[20];

scrittura

a[3] = 'x';

è lo stesso della scrittura

3[a] = 'x';



In C array , arr[3] e 3[arr] sono uguali e le loro notazioni di puntatore equivalenti sono *(arr + 3) a *(3 + arr) . Ma al contrario [arr]3 o [3]arr non è corretto e si tradurrà in errore di sintassi, in quanto (arr + 3)* e (3 + arr)* non sono espressioni valide. Il motivo è che l'operatore di dereferenziazione deve essere posizionato prima dell'indirizzo generato dall'espressione, non dopo l'indirizzo.




In C

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

Il puntatore è una "variabile"

il nome dell'array è un "mnemonico" o "sinonimo"

p++; è valido ma a++ non è valido

a[2] è uguale a 2 [a] perché l'operazione interna su entrambi è

"Aritmetica puntatore" calcolata internamente come

*(a+3) uguale a *(3+a)




tipi di puntatore

1) puntatore ai dati

int *ptr;

2) puntatore const ai dati

int const *ptr;

3) puntatore const per i dati const

int const *const ptr;

e gli array sono di tipo (2) dalla nostra lista
Quando si definisce una matrice alla volta, un indirizzo viene inizializzato in quel puntatore
Poiché sappiamo che non possiamo modificare o modificare il valore const nel nostro programma, poiché genera un ERRORE in fase di compilazione

La principale differenza che ho trovato è ...

Possiamo re-inizializzare il puntatore con un indirizzo ma non lo stesso caso con un array.

======
e torna alla tua domanda ...
a [5] non è altro che * (a + 5)
puoi capire facilmente da
a - contenente l'indirizzo (le persone lo chiamano come indirizzo base) proprio come un (2) tipo di puntatore nella nostra lista
[] - quell'operatore può essere sostituibile con il puntatore *.

quindi finalmente ...

a[5] == *(a +5) == *(5 + a) == 5[a] 



Related