2014-12-21 9 views
20

L'uso di const con un puntatore può rendere il puntatore non modificabile mediante il dereferenziamento utilizzando il puntatore in questione. Ma perché non posso modificare ciò a cui il puntatore non punta direttamente?const e puntatori in C

Ad esempio:

int a = 3; 
const int* ptr = &a; 
*ptr = 5; 

non verrà compilato. Ma perché

*(ptr + 2) = 5; 

anche non compilare? Non sto cambiando ciò a cui punta il puntatore.
Quindi dobbiamo dire che l'uso di const con un puntatore in questo modo non solo rende non modificabile ciò a cui punta il puntatore (dereferenziando il puntatore) ma anche qualcos'altro, a cui l'indirizzo si ottiene usando il puntatore?

So che nell'esempio sto tentando di accedere alla memoria non allocata, ma questo è solo per motivi di discussione.

+0

Quindi ti aspettavi che il compilatore assumesse che solo i primi 'sizeof (int)' byte sono 'const'? Ha più senso che il compilatore assumerà che qualsiasi indirizzo di memoria a cui si accede con 'ptr' è un indirizzo di sola lettura (" fisicamente "di sola lettura, o almeno di sola lettura nella" prospettiva "della funzione che usa' ptr '). –

risposta

25

ptr +2 ha semplicemente lo stesso tipo di ptr ovvero è un puntatore a un oggetto const.

L'aritmetica del puntatore presuppone che l'oggetto puntato sia un array di tutto lo stesso tipo di base. Questo tipo include la qualifica const.

+0

Quindi è come int + float = float, quindi const [tipo] * + qualche valore = const [tipo] *, giusto? – Root149

+0

@ Root149, qualcosa di simile, sì. L'aggiunta di qualsiasi tipo intero a un tipo di puntatore restituisce lo stesso tipo di puntatore. –

+0

Solo con tipi di puntatori decadono (tutto viene appiattito sui puntatori quando sono coinvolti i puntatori, inclusi gli array), dove i tipi di interi normali vengono promossi fino a un tipo che può rappresentare tutto. – Blindy

13

La non modificabilità introdotta da const dipende da dove è scritto const.

Se si scrive

const int * ptr = &a; 

(o int const * ptr = &a;), il const si riferisce alla pointee, in modo da utilizzare il puntatore per la scrittura alla destinazione è vietato.

(OTOH, se Wou scritto

int * const ptr = &a; 

non si poteva modificare ptr.)

Nel tuo caso, tutto ciò che coinvolge la scrittura per la destinazione è proibito.

Questo include *ptr e ptr[0] (che sono equivalenti), ma anche tutto ciò che coinvolge una modifica dell'indirizzo di destinazione, ad esempio *(ptr + 2) o ptr[2].

+5

(e nota che 'const int *' e 'int const *' sono la stessa cosa - puntatore a const, non const pointer.) – Mat

+0

nota anche che dato "int * const ptr = & a;' ", puoi scrivere "' * ptr = 5; '" ma non "' ++ ptr' ". La buona copertura delle regole è nella risposta a http://stackoverflow.com/questions/1143262/what-is-the-difference-between-const-int-const-int-const-int-const - la prima riga di la risposta - "Leggi indietro" - riassume splendidamente. – frasnian

+0

@frasnian Questo è essenzialmente ciò che ho detto - '++ ptr' è coperto da" non puoi modificare 'ptr'". – glglgl

4

Poiché un puntatore può essere utilizzato anche come matrice (si pensi a argv), il compilatore limita ogni accesso in cui è coinvolto il puntatore. In questo modo l'intero array è di sola lettura.

+0

Bene, argv è solo un puntatore, poiché scrivere char * argv [] equivale a char ** argv. creiamo solo l'illusione di un array. – Root149

+0

@ Root149 L'array o non fa alcuna differenza quando si accede ad esso, poiché l'accesso funziona comunque tramite puntatori. – glglgl

+1

@ Root149: giusto. E a causa di questa illusione, il compilatore non sa se il tuo puntatore è anche una tale "illusione". Può essere usato come un array, come 'argv', e quindi ogni accesso in scrittura ad esso deve essere limitato (non importa se' ptr [0] 'o' ptr [100] '). –

3

Pensiamo al tipo di espressioni.

const int* ptr = &a; 

Il tipo di ptr è const int*.

Quindi il tipo di *ptr è const int. Non modificabile

Il tipo di (ptr + 2) è ancora const int* quindi il tipo di *(ptr + 2) è const int che non è ancora modificabile.

9

Sebbene altre risposte spieghino gli aspetti tecnici del perché non funziona, vorrei offrire un motivo più generale: è l'unica cosa che ha senso.

Il problema è che non esiste un modo generale per il compilatore di decidere se p + something è lo stesso di p o no, perché something può essere arbitrariamente complessa. Una regola come "Valori puntata da p e p + 0 sono immodificabile, ma altri valori possono ancora essere modificato" non può essere verificata in fase di compilazione: immaginate se hai scritto:

*(p + very complex expression) = ... 

dovrebbe il compilatore in grado di capire se very complex expression è zero? Che dire di

int s; 
scanf("%d", &s); 
*(p + s) = ... 

Cosa dovrebbe fare il compilatore in questo caso?

L'unica scelta ragionevole era quella di rendere qualsiasi valore accessibile tramite p non modificabile.

+0

Questo comportamento non è causato da alcuna ambiguità nel processo decisionale del compilatore. È una conseguenza semplice e corretta delle regole del sistema di tipi. –

+0

@ JamesT.Huggett: Non sono sicuro di cosa intenda per "ambiguità nel processo decisionale del compilatore"; è vero che il comportamento è una conseguenza di una regola di digitazione, e la mia risposta cerca solo di spiegare perché le regole del sistema di tipi sono state progettate in questo modo. –