Con le matrici, perché è un [5] == 5 [a]?


1622

Come sottolinea Joel nel podcast Stack Overflow n. 34 , in C Programming Language (aka: K & R), si fa menzione di questa proprietà degli array in C:a[5] == 5[a]

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


48
qualcosa come un [+] funziona anche come * (a ++) OPPURE * (++ a)?
Egon,

45
@Egon: È molto creativo ma sfortunatamente non è così che funzionano i compilatori. Il compilatore interpreta a[1]una serie di token, non stringhe: * ({posizione intera di} a {operatore} + {numero intero} 1) è uguale a * ({numero intero} 1 {operatore} + {posizione intera di} a) ma non è uguale a * ({posizione intera di} a {operatore} + {operatore} +)
Dinah,

11
Un'interessante variazione composta su questo è illustrata nell'accesso all'array Illogical , dove hai char bar[]; int foo[];ed foo[i][bar]è usata come espressione.
Jonathan Leffler,

5
@EldritchConundrum, perché pensi che "il compilatore non può verificare che la parte sinistra sia un puntatore"? Sì, può. È vero che a[b]= *(a + b)per ogni dato ae b, ma era la libera scelta dei progettisti del linguaggio per +essere definito commutativo per tutti i tipi. Nulla poteva impedire loro di vietare i + pmentre lo permetteva p + i.
ach

13
@Andrey One di solito si aspetta +che sia commutativo, quindi forse il vero problema è scegliere di fare in modo che le operazioni del puntatore assomiglino all'aritmetica, invece di progettare un operatore offset separato.
Eldritch Conundrum,

Risposte:


1926

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

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

Pertanto a[5]valuterà di:

*(a + 5)

e 5[a]valuterà a:

*(5 + a)

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


325
Mi chiedo se non è più come * ((5 * sizeof (a)) + a). Grande spiegazione però.
John MacIntyre,

92
@Dinah: dal punto di vista del compilatore C, hai ragione. Non è necessaria alcuna dimensione e quelle espressioni che ho citato sono LO STESSO. Tuttavia, il compilatore prenderà in considerazione sizeof quando produrrà il codice macchina. Se a è un array int, a[5]verrà compilato in qualcosa di simile mov eax, [ebx+20]invece di[ebx+5]
Mehrdad Afshari,

12
@Dinah: A è un indirizzo, diciamo 0x1230. Se a era in un array int a 32 bit, allora [0] è a 0x1230, a [1] è a 0x1234, a [2] a 0x1238 ... a [5] a x1244 ecc. Se aggiungessimo solo 5 a 0x1230, otteniamo 0x1235, che è sbagliato.
James Curran,

36
@ sr105: questo è un caso speciale per l'operatore +, in cui uno degli operandi è un puntatore e l'altro un numero intero. Lo standard dice che il risultato sarà del tipo di puntatore. Il compilatore / deve essere / abbastanza intelligente.
aib

48
"dalla matematica della scuola elementare sappiamo che sono uguali" - Capisco che stai semplificando, ma sono con quelli che pensano che questo sia troppo semplice. Non è elementare *(10 + (int *)13) != *((int *)10 + 13). In altre parole, qui c'è molto di più dell'aritmetica della scuola elementare. La commutatività si basa in modo critico sul compilatore che riconosce quale operando è un puntatore (e a quale dimensione dell'oggetto). Per dirla in altro modo (1 apple + 2 oranges) = (2 oranges + 1 apple), ma (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH,

288

Perché l'accesso alla matrice è definito in termini di puntatori. a[i]è definito come medio *(a + i), che è commutativo.


42
Le matrici non sono definite in termini di puntatori, ma l' accesso ad esse è.
Razze di leggerezza in orbita,

5
Vorrei aggiungere "quindi è uguale a *(i + a), che può essere scritto come i[a]".
Jim Balter,

4
Vorrei suggerire di includere la citazione dallo standard, che è la seguente: 6.5.2.1: 2 Un'espressione postfissa seguita da un'espressione tra parentesi quadre [] è una designazione sottoscritta di un elemento di un oggetto array. La definizione dell'operatore di sottoscrizione [] è che E1 [E2] è identico a (* ((E1) + (E2))). A causa delle regole di conversione che si applicano all'operatore binario +, se E1 è un oggetto array (equivalentemente, un puntatore all'elemento iniziale di un oggetto array) ed E2 è un numero intero, E1 [E2] designa l'elemento E2-th di E1 (contando da zero).
Validità

Per essere più corretti: le matrici decadono in puntatori quando vi si accede.
12431234123412341234123

Nitpick: non ha senso dire che " *(a + i)è commutativo". Tuttavia, *(a + i) = *(i + a) = i[a]poiché l' aggiunta è commutativa.
Andreas Rejbrand,

231

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 (di nuovo, per definizione []dell'operatore) è equivalente a i[p].

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

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

Quando entrambi gli operandi sono dello stesso tipo, o anche di diversi tipi numerici che sono promosso un tipo comune, commutatività rende 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. (Numero intero + intero è un'operazione diversa e puntatore + puntatore è senza senso.)

La descrizione dello standard C del + 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 altrettanto 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 il operando di destra deve avere un tipo intero.

nel qual caso entrambi i + p e i[p]sarebbe illegale.

In termini di C ++, abbiamo davvero due serie di +operatori sovraccarichi , che possono essere liberamente 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ì?

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

Quindi l'idea che l'indicizzazione di array sia definita in termini di addizione e che l'addizione, anche di un puntatore e di un numero intero, sia commutativa, risale a molti decenni fa ai linguaggi antenati di C.

Quelle lingue erano tipizzate in modo molto meno marcato di quanto non lo sia la C moderna. In particolare, la distinzione tra puntatori e numeri interi è stata spesso ignorata. (I primi programmatori C a volte usavano i puntatori come numeri interi senza segno, prima delunsigned parola chiave fosse aggiunta al linguaggio.) Quindi l'idea di rendere l'aggiunta non commutativa perché gli operandi sono di tipi diversi probabilmente non sarebbe venuta ai progettisti di quei linguaggi. Se un utente voleva aggiungere due "cose", indipendentemente dal fatto che quelle "cose" fossero numeri interi, puntatori o qualcos'altro, non era nella lingua per impedirlo.

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

La modifica di C e / o C ++ per richiedere il posizionamento del puntatore a sinistra e il numero intero a destra potrebbero rompere un po 'di codice esistente, ma non ci sarebbe perdita di reale potere espressivo.

Quindi ora abbiamo arr[3]e intendiamo 3[arr]esattamente la stessa cosa, sebbene quest'ultima forma non dovrebbe mai apparire al di fuori dell'IOCCC .


12
Fantastica descrizione di questa proprietà. Da un punto di vista di alto livello, penso che 3[arr]sia un manufatto interessante ma dovrebbe essere usato raramente. La risposta accettata a questa domanda (< stackoverflow.com/q/1390365/356> ) che ho chiesto qualche tempo fa ha cambiato il modo in cui ho pensato alla sintassi. Sebbene non ci sia spesso tecnicamente un modo giusto e sbagliato di fare queste cose, questo tipo di funzionalità ti fa iniziare a pensare in un modo separato dai dettagli di implementazione. C'è un vantaggio in questo diverso modo di pensare che è in parte perso quando si fissano i dettagli dell'implementazione.
Dinah,

3
L'aggiunta è commutativa. Per lo standard C definirlo altrimenti sarebbe strano. Ecco perché non si potrebbe dire altrettanto facilmente "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 essere di tipo intero". - Non avrebbe senso per la maggior parte delle persone che aggiungono cose.
iheanyi,

9
@iheanyi: l'aggiunta è solitamente commutativa - e di solito ci vogliono due operandi dello stesso tipo. L'aggiunta del puntatore consente di aggiungere un puntatore e un numero intero, ma non due puntatori. IMHO è già un caso speciale abbastanza strano che richiedere al puntatore di essere l'operando di sinistra non sarebbe un onere significativo. (Alcune lingue usano "+" per la concatenazione di stringhe; questo non è certamente commutativo.)
Keith Thompson,

3
@supercat, Peggio ancora. Ciò significherebbe che a volte x + 1! = 1 + x. Ciò violerebbe completamente la proprietà associativa dell'aggiunta.
iheanyi,

3
@iheanyi: penso che intendessi proprietà commutativa; l'addizione non è già associativa, poiché sulla maggior parte delle implementazioni (1LL + 1U) -2! = 1LL + (1U-2). In effetti, la modifica renderebbe associative alcune situazioni che attualmente non lo sono, ad esempio 3U + (UINT_MAX-2L) sarebbe uguale (3U + UINT_MAX) -2. Ciò che sarebbe meglio, tuttavia, è che la lingua abbia aggiunto nuovi tipi distinti per numeri interi promotibili e "avvolgimenti" di anelli algebrici, in modo che l'aggiunta di 2 a un ring16_tche detiene 65535 produrrebbe un ring16_tvalore 1, indipendentemente dalla dimensione diint .
supercat,

196

E naturalmente

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

La ragione principale di ciò era che negli anni '70, quando fu progettato C, i computer non avevano molta memoria (64 KB era molto), quindi il compilatore C non faceva molto controllo della sintassi. Quindi " X[Y]" è stato tradotto alla cieca in " *(X+Y)"

Questo spiega anche le sintassi " +=" e " ++". Tutto nella forma " A = B + C" aveva la stessa forma compilata. Ma se B era lo stesso oggetto di A, allora era disponibile un'ottimizzazione a livello di assieme. Ma il compilatore non era abbastanza brillante da riconoscerlo, quindi lo sviluppatore ha dovuto ( A += C). Allo stesso modo, se lo Cfosse 1, era disponibile una diversa ottimizzazione a livello di assieme e lo sviluppatore doveva renderlo esplicito, poiché il compilatore non lo riconosceva. (Più recentemente i compilatori lo fanno, quindi queste sintassi sono in gran parte inutili in questi giorni)


127
In realtà, ciò viene valutato come falso; il primo termine "ABCD" [2] == 2 ["ABCD"] restituisce true, oppure 1, e 1! = 'C': D
Jonathan Leffler

8
@Jonathan: la stessa ambiguità porta alla modifica del titolo originale di questo post. Siamo uguali segni di equivalenza matematica, sintassi del codice o pseudo-codice. Sostengo l'equivalenza matematica, ma dal momento che stiamo parlando di codice, non possiamo sfuggire al fatto che stiamo visualizzando tutto in termini di sintassi del codice.
Dinah,

19
Non è questo un mito? Voglio dire che gli operatori + = e ++ sono stati creati per semplificare il compilatore? Alcuni codici diventano più chiari, ed è utile avere la sintassi, indipendentemente da ciò che il compilatore fa con esso.
Thomas Padron-McCarthy,

6
+ = e ++ ha un altro vantaggio significativo. se il lato sinistro cambia alcune variabili durante la valutazione, la modifica verrà eseguita una sola volta. a = a + ...; lo farà due volte.
Johannes Schaub - litb

8
No - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Dereferenziare una stringa ti dà un carattere, non una sottostringa
MSalters,

55

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

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


1
C'è una conversazione abbastanza esaustiva su questo nei commenti della risposta accettata. Ho fatto riferimento a detta conversazione nella modifica alla domanda originale ma non ho affrontato direttamente la tua preoccupazione molto valida di sizeof. Non sono sicuro di come farlo nel modo migliore. Dovrei apportare un'altra modifica a orig. domanda?
Dinah,

49

Per rispondere alla domanda alla lettera. Non è sempre verox == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

stampe

false

27
In realtà un "nan" non è uguale a se stesso: lo cout << (a[5] == a[5] ? "true" : "false") << endl;è false.
Vero

9
@TrueY: Lo ha affermato specificamente per il caso NaN (e nello specifico x == xnon è sempre vero). Penso che fosse la sua intenzione. Quindi è tecnicamente corretto (e forse, come si suol dire, il miglior tipo di corretto!).
Tim Čas,

4
La domanda riguarda C, il tuo codice non è il codice C. C'è anche un NANin <math.h>, che è meglio di 0.0/0.0, perché 0.0/0.0è UB quando __STDC_IEC_559__non è definito (la maggior parte delle implementazioni non definisce __STDC_IEC_559__, ma sulla maggior parte delle implementazioni 0.0/0.0continuerà a funzionare)
12431234123412341234123

26

Ho appena scoperto che questa brutta sintassi potrebbe essere "utile", o almeno molto divertente con cui giocare quando si desidera gestire una matrice di indici che si riferiscono a posizioni nella stessa matrice. Può sostituire parentesi quadre nidificate e rendere il codice più leggibile!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Certo, sono abbastanza sicuro che non ci sia un caso d'uso per questo nel codice reale, ma l'ho trovato comunque interessante :)


Quando vedi i[a][a][a]che pensi che io sia un puntatore a un array o un array di un puntatore a un array o un array ... ed aè un indice. Quando vedi a[a[a[i]]], pensi che a sia un puntatore a un array o un array ed iè un indice.
12431234123412341234123

1
Wow! È un ottimo uso di questa funzionalità "stupida". Potrebbe essere utile in
contesti

26

Bella domanda / risposte.

Voglio solo sottolineare che i puntatori e le matrici C non sono gli stessi , anche se in questo caso la differenza non è essenziale.

Considera le seguenti dichiarazioni:

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

In a.out, il simbolo si atrova su un indirizzo che è l'inizio dell'array e il simbolo si ptrova su un indirizzo in cui è memorizzato un puntatore e il valore del puntatore in quella posizione di memoria è l'inizio dell'array.


2
No, tecnicamente non sono gli stessi. Se si definisce b come int * const e lo si fa riferimento a un array, si tratta comunque di un puntatore, il che significa che nella tabella dei simboli, b si riferisce a una posizione di memoria che memorizza un indirizzo, che a sua volta indica dove si trova l'array .
PolyThinker il

4
Ottimo punto Ricordo di aver avuto un bug molto brutto quando ho definito un simbolo globale come char s [100] in un modulo, dichiarandolo come char esterno *; in un altro modulo. Dopo aver collegato tutto insieme, il programma si è comportato in modo molto strano. Perché il modulo che utilizzava la dichiarazione extern utilizzava i byte iniziali dell'array come puntatore a char.
Giorgio,

1
In origine, nel nonno BCPL di C., un array era un puntatore. Cioè, quello che hai ottenuto quando hai scritto (ho traslitterato in C) int a[10]era un puntatore chiamato 'a', che indicava abbastanza spazio per 10 numeri interi, altrove. Quindi a + i e j + i avevano la stessa forma: aggiungi il contenuto di un paio di posizioni di memoria. In effetti, penso che BCPL fosse senza nome, quindi erano identici. E il ridimensionamento di tipo size non si applicava, poiché BCPL era puramente orientato alle parole (anche su macchine indirizzate a parole).
Dave,

Penso che il modo migliore per capire la differenza sia confrontarsi int*p = a;con int b = 5; In quest'ultimo, "b" e "5" sono entrambi numeri interi, ma "b" è una variabile, mentre "5" è un valore fisso. Allo stesso modo, "p" e "a" sono entrambi indirizzi di un carattere, ma "a" è un valore fisso.
James Curran,

20

Per i puntatori in C, abbiamo

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

e anche

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

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


15

Non una risposta, ma solo uno spunto di riflessione. Se la classe presenta un operatore indice / indice sovraccarico, l'espressione 0[x]non funzionerà:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Dal momento che non abbiamo accesso alla classe int , questo non può essere fatto:

class int
{
   int operator[](const Sub&);
};

2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
Ben Voigt,

1
Hai davvero provato a compilarlo? Esistono gruppi di operatori che non possono essere implementati al di fuori della classe (ovvero come funzioni non statiche)!
Ajay,

3
oops, hai ragione. " operator[]deve essere una funzione membro non statica con esattamente un parametro." Conoscevo quella limitazione operator=, non pensavo che fosse applicabile [].
Ben Voigt,

1
Certo, se cambiate la definizione di []operatore, non sarebbe mai più equivalente ... se a[b]è uguale *(a + b)e lo cambiate, dovrete sovraccaricare anche int::operator[](const Sub&);e intnon è una classe ...
Luis Colorado

7
Questo ... non è ... C.
MD XF,

11

Ha un'ottima spiegazione in TUTORIAL SUI PUNTATORI E SUGLI ARRAY IN C di Ted Jensen.

Ted Jensen lo ha spiegato come:

In realtà, questo è vero, cioè ovunque si scriva a[i]può essere sostituito*(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 di array. Entrambe le sintassi producono lo stesso risultato.

Questo NON sta dicendo che i puntatori e le matrici siano 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 producono risultati identici.

Ora, guardando quest'ultima espressione, parte di essa .. (a + i), è una semplice aggiunta che utilizza l'operatore + e le regole di C affermano che tale espressione è commutativa. Cioè (a + i) è identico a (i + a). Così potremmo scrivere *(i + a)con la stessa facilità *(a + i). Ma *(i + a)potrebbe venire 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';

4
a + i NON è un'aggiunta semplice, perché è l'aritmetica del puntatore. se la dimensione dell'elemento di a è 1 (carattere), allora sì, è proprio come intero +. Ma se è (es.) Un numero intero, allora potrebbe essere equivalente a un + 4 * i.
Alex Brown,

@AlexBrown Sì, è l'aritmetica del puntatore, che è esattamente il motivo per cui l'ultima frase è sbagliata, a meno che tu non abbia prima lanciato 'a' per essere un (carattere *) (supponendo che un int sia 4 caratteri). Davvero non capisco perché così tante persone siano bloccate dal risultato di valore reale dell'aritmetica del puntatore. L'intero scopo dell'aritmetica del puntatore è quello di sottrarre i valori del puntatore sottostante e lasciare che il programmatore pensi agli oggetti manipolati piuttosto che indirizzare i valori.
jschultz410,

8

So che la domanda ha una risposta, ma non ho resistito alla condivisione di questa spiegazione.

Ricordo i principi della progettazione del compilatore, supponiamo che asia un intarray e la dimensione intsia di 2 byte e l'indirizzo di base per asia 1000.

Come a[5]funzionerà ->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Così,

Allo stesso modo, quando il codice c è suddiviso in un codice a 3 indirizzi, 5[a]diventerà ->

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Quindi, in sostanza, entrambe le istruzioni puntano alla stessa posizione in memoria e quindi, a[5] = 5[a] .

Questa spiegazione è anche il motivo per cui gli indici negativi negli array funzionano in C.

cioè se a[-5]accedo mi darà

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Mi restituirà l'oggetto nella posizione 990.


6

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


6

nel compilatore c

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

sono diversi modi per fare riferimento a un elemento in un array! (NON A TUTTI I WEIRD)


5

Un po 'di storia ora. Tra le altre lingue, BCPL ha avuto un'influenza abbastanza importante sullo sviluppo iniziale di C. Se hai dichiarato un array in BCPL con qualcosa del tipo:

let V = vec 10

che in realtà allocava 11 parole di memoria, non 10. Tipicamente V era la prima e conteneva l'indirizzo della parola immediatamente successiva. Quindi, diversamente da C, la denominazione di V è andata in quella posizione e ha preso l'indirizzo dell'ennesimo elemento dell'array. Pertanto indiretta dell'array in BCPL, espresso come

let J = V!5

doveva davvero fare J = !(V + 5)(usando la sintassi BCPL) poiché era necessario recuperare V per ottenere l'indirizzo di base dell'array. Così V!5e5!V erano sinonimi. Come osservazione aneddotica, WAFL (Warwick Functional Language) è stato scritto in BCPL, e al meglio della mia memoria tendeva a usare quest'ultima sintassi anziché la prima per accedere ai nodi usati come memoria dei dati. Concesso questo proviene da qualche parte tra 35 e 40 anni fa, quindi la mia memoria è un po 'arrugginita. :)

L'innovazione di dispensare con la parola di archiviazione aggiuntiva e di avere il compilatore inserire l'indirizzo di base dell'array quando è stato nominato è venuto dopo. Secondo il documento di storia C, ciò accadde all'incirca nel momento in cui le strutture furono aggiunte a C.

Si noti che !in BCPL era sia un operatore con prefisso unario che un operatore con infissione binaria, in entrambi i casi facendo riferimento indiretto. solo che il modulo binario includeva un'aggiunta dei due operandi prima di eseguire la direzione indiretta. Data la natura orientata alle parole di BCPL (e B), questo in realtà aveva molto senso. La restrizione di "puntatore e numero intero" è stata resa necessaria in C quando ha acquisito i tipi di dati ed è sizeofdiventata una cosa.


1

Bene, questa è una funzione che è possibile solo grazie al supporto linguistico.

Il compilatore interpreta a[i]come *(a+i)e l'espressione 5[a]valuta *(5+a). Poiché l'addizione è commutativa, si scopre che entrambi sono uguali. Quindi l'espressione valuta true.


Sebbene ridondante, questo è chiaro, conciso e breve.
Bill K,

0

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 è "mnemonico" o "sinonimo"

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

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

"Puntatore aritmetico" calcolato internamente come

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


-4

tipi di puntatore

1) puntatore ai dati

int *ptr;

2) const puntatore ai dati

int const *ptr;

3) puntatore const a const const

int const *const ptr;

e gli array sono del tipo di (2) dal nostro elenco
Quando si definisce un array alla volta un indirizzo viene inizializzato in quel puntatore.
Come sappiamo che non possiamo cambiare o modificare il valore const nel nostro programma perché genera un ERRORE alla compilazione tempo

La principale differenza che ho trovato è ...

È possibile inizializzare nuovamente il puntatore tramite un indirizzo ma non lo stesso caso con un array.

======
e torna alla tua domanda ...
a[5]non è altro *(a + 5)
che puoi capire facilmente
a - contenendo l'indirizzo (la gente lo chiama come indirizzo di base) proprio come un (2) tipo di puntatore nel nostro elenco
[]- quell'operatore può essere sostituibile con puntatore *.

così finalmente ...

a[5] == *(a +5) == *(5 + a) == 5[a] 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.