Qual è il punto dei puntatori const?


149

Non sto parlando di puntatori a valori const, ma di puntatori const stessi.

Sto imparando il C e il C ++ oltre le cose basilari e fino ad oggi ho capito che i puntatori vengono passati per valore alle funzioni, il che ha senso. Ciò significa che all'interno di una funzione posso far puntare il puntatore copiato su un altro valore senza influire sul puntatore originale del chiamante.

Quindi qual è il punto di avere un'intestazione di funzione che dice:

void foo(int* const ptr);

All'interno di una tale funzione non puoi fare in modo che ptr indichi qualcos'altro perché è const e non vuoi che venga modificato, ma una funzione come questa:

void foo(int* ptr);

Funziona altrettanto bene! perché il puntatore viene comunque copiato e il puntatore nel chiamante non viene influenzato anche se si modifica la copia. Quindi qual è il vantaggio di const?


29
Cosa succede se si desidera garantire, al momento della compilazione, che il puntatore non può e non deve essere modificato per indicare qualcos'altro?
Platinum Azure

25
Esattamente lo stesso punto di qualsiasi constparametro.
David Heffernan,

2
@PlatinumAzure, ho alcuni anni di esperienza nella programmazione, ma mancano le mie pratiche software. Sono contento di aver fatto questa domanda perché non ho mai sentito la necessità di fare in modo che il compilatore sottolinei i miei errori logici. La mia reazione iniziale al momento in cui ho presentato la domanda è stata "perché dovrei preoccuparmi se il puntatore viene modificato? Non influirà sul chiamante". Fino a quando ho letto tutte le risposte ho capito che mi sarebbe importato perché se provassi a modificarlo, io e la mia logica di codifica sono sbagliati (o c'era un errore di battitura come la mancanza dell'asterisco) e potrei non essermi reso conto se il compilatore non lo avesse fatto non dirmelo
R. Ruiz.

3
@ R.Ruiz. Senza dubbio, anche il più esperto di noi potrebbe fare con ulteriori garanzie di correttezza. Poiché l'ingegneria del software è una forma di ingegneria in cui può essere accettabile un po 'più di margine di manovra (HTTP 502 casuali, connessioni lente, il mancato caricamento dell'immagine occasionale non sono occasioni straordinarie, ma un guasto al motore in un aeroplano è inaccettabile e probabilmente grave), a volte le persone programmano con fretta indebita. Lo stesso argomento che giustifica la scrittura di unit test spiegherà l'uso delle constgaranzie di correttezza. Ci fa solo sentire più sicuri che il nostro codice è indubbiamente corretto.
Platinum Azure,

Risposte:


207

const è uno strumento che dovresti usare per perseguire un concetto C ++ molto importante:

Trova i bug in fase di compilazione, anziché in fase di esecuzione, facendo in modo che il compilatore imponga ciò che intendi.

Anche se non cambia la funzionalità, l'aggiunta constgenera un errore del compilatore quando fai cose che non volevi fare. Immagina il seguente errore di battitura:

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

Se lo usi int* const, questo genererebbe un errore del compilatore perché stai cambiando il valore in ptr. L'aggiunta di restrizioni tramite la sintassi è una buona cosa in generale. Non esagerare: l'esempio che hai dato è un caso in cui la maggior parte delle persone non si preoccupa di usarlo const.


8
Grazie, questa è una risposta che mi convince. Metti const in modo che il compilatore ti avverta dei tuoi errori di assegnazione. Il tuo esempio è perfetto per illustrare questo concetto perché è un errore comune con i puntatori. Di te!
R. Ruiz.

25
"Aiuta il compilatore ad aiutarti" è il mantra che normalmente canto per questo.
Flexo

1
+1, ma qui è un caso interessante in cui è argueable: stackoverflow.com/questions/6305906/...
Raedwald

3
quindi è la stessa risposta che daresti a "Perché rendere le variabili private, quando non puoi semplicemente usarle al di fuori della classe".
Lee Louviere,

questo potrebbe essere un po 'fuori tema, ma dove si trova questo puntatore const nella memoria? So che tutte le const si trovano nella parte globale della memoria, ma non sono sicuro al 100% di una const in cui vieni passato come una funzione. grazie e scusa se questo è fuori tema
Tomer

77

Mi preme usare solo const argomenti perché questo abilita più controlli del compilatore: se riassegno accidentalmente un valore di argomento all'interno della funzione, il compilatore mi morde.

Raramente riutilizzo variabili, è più pulito creare nuove variabili per contenere nuovi valori, quindi essenzialmente tutte le mie dichiarazioni di variabili sono const(tranne per alcuni casi come le variabili di ciclo in cui constimpedirebbe il funzionamento del codice).

Si noti che ciò ha senso solo nella definizione di una funzione. Non appartiene alla dichiarazione , che è ciò che l'utente vede. E all'utente non importa se utilizzo constparametri all'interno della funzione.

Esempio:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

Nota come sono l'argomento e la variabile locale const. Né è necessario ma con funzioni anche leggermente più grandi, questo mi ha ripetutamente salvato da errori.


17
+1da un altro constfanatico. Tuttavia, preferisco che i miei compilatori mi abbaiano. Faccio troppi errori e soffrirei molto se mordessero.
sbi,

2
+1, consiglio vivamente la conststrategia "a meno che non ci sia una buona ragione". Vi sono tuttavia alcune buone eccezioni, ad esempio Copia e scambia
Flexo

2
Se stavo progettando un nuovo linguaggio, gli oggetti dichiarati sarebbero di sola lettura ("const") per impostazione predefinita. Avresti bisogno di una sintassi speciale, forse una parola chiave "var", per rendere scrivibile un oggetto.
Keith Thompson,

@Keith sono tentato di dire "certo". Tutto il resto è stupido a questo punto. Ma sfortunatamente la maggior parte dei progettisti di lingue sembra non essere d'accordo con noi ... In effetti, ho già pubblicato altrettanto (in una domanda ora cancellata, "Qual è la tua opinione di programmazione più controversa?").
Konrad Rudolph,

1
@KubaOber Non vedo affatto come sia una pessimizzazione. Se in seguito scopri che devi modificare ciò che è stato passato nel corpo della funzione, lascia cadere constl'argomento e preferibilmente commenta il perché. Quel po 'di lavoro extra non giustifica la marcatura di tutti gli argomenti come non constpredefiniti e l'apertura a tutti i potenziali errori che crea.
underscore_d

20

La tua domanda tocca qualcosa di più generale: gli argomenti di funzione dovrebbero essere costanti?

Gli argomenti di costanza di valore (come il puntatore) sono dettagli di implementazione e non fanno parte della dichiarazione di funzione. Ciò significa che la tua funzione è sempre questa:

void foo(T);

Spetta interamente all'implementatore della funzione se desidera utilizzare la variabile dell'argomento scope-scope in modo mutabile o in modo costante:

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

Quindi, seguire la semplice regola per non inserire mai constla dichiarazione (intestazione) e inserirla nella definizione (implementazione) se non si desidera o è necessario modificare la variabile.


5
Sono d'accordo con questo, ma mi sento un po 'a disagio con l'idea di rendere diversa la dichiarazione e la definizione. Per cose diverse da const, la dichiarazione e la (parte prototipo della) definizione saranno generalmente identiche.
Keith Thompson,

@KeithThompson: Beh, non devi farlo se non vuoi. Basta non inserire constla dichiarazione. Dipende interamente da te se vuoi aggiungere il qualificatore nell'implementazione.
Kerrek SB,

1
Come ho già detto, sono d'accordo constsul fatto che mettere la dichiarazione ma non la definizione abbia senso. Sembra solo un difetto nella lingua che questo è l'unico caso in cui ha senso rendere la dichiarazione e la definizione non identiche. (Ovviamente non è l'unico inconveniente di C.)
Keith Thompson,

16

Il qualificatore const di livello superiore viene scartato nelle dichiarazioni, quindi le dichiarazioni nella domanda dichiarano esattamente la stessa funzione. D'altra parte, nella definizione (implementazione) il compilatore verificherà che se si contrassegna il puntatore come const, non verrà modificato all'interno del corpo della funzione.


Non lo sapevo. Quindi se provo a sovraccaricare void foo (int * const ptr) con void foo (int * t ptr) otterrò un errore del compilatore. Grazie!
R. Ruiz.

@ R.Ruiz. Se la decelerazione è void foo (int * t ptr) e la definizione è void foo (int * const ptr), NON si verificherà un errore del compilatore.
Trevor Hickey,

1
@TrevorHickey Lo faranno ... ma non per il motivo che pensano. int* t ptrè un errore di sintassi. Senza quello, i due sono identici per scopi di sovraccarico.
underscore_d

14

Hai ragione, per il chiamante non fa assolutamente alcuna differenza. Ma per chi scrive la funzione può essere una rete di sicurezza "okay, devo assicurarmi di non fare questo punto sulla cosa sbagliata". Non molto utile ma neanche inutile.

È praticamente lo stesso di averne uno int const the_answer = 42nel tuo programma.


1
Cosa importa dove lo puntano, è un puntatore allocato nello stack? La funzione non può rovinare tutto. Ha molto più senso usare i puntatori di sola lettura quando si ha a che fare con i puntatori a puntatori. E ' non è la stessa cosa di int const! Voterò questo solo per quella dichiarazione fuorviante. const inte int constsono equivalenti, mentre const int*e int* consthanno due significati diversi!
Lundin,

1
@lundin Supponiamo che la funzione sia grande e contenga altre cose. Accidentalmente si può far puntare a qualcos'altro (nello stack della funzione). Questo non è un problema per il chiamante, ma sicuramente potrebbe essere per la persona chiamata. Non vedo nulla di fuorviante nelle mie dichiarazioni: " per chi scrive la funzione può essere una rete di sicurezza".
cnicutar,

1
@Lundin Informazioni sulla int constparte; Ho messo il tipo prima di const (sembra innaturale) da qualche tempo e sono consapevole delle differenze. Questo articolo potrebbe rivelarsi utile. Personalmente ho avuto motivi leggermente diversi per passare a questo stile.
cnicutar,

1
Va bene. Ho appena scritto una mia risposta a lungo termine nel caso in cui qualcun altro ma io e te stiamo leggendo questo post. Ho anche incluso una logica per cui int const è cattivo stile e const int è buono stile.
Lundin,

Lundin, sto anche leggendo! Sono d'accordo che const int è lo stile migliore però. @cnicutar, la tua prima risposta a Lundin è ciò che stavo cercando quando ho inviato questa domanda. Quindi il problema non sta nel chiamante (come pensavo senza pensarci), ma const è una garanzia per la chiamata. Mi piace questa idea, grazie.
R. Ruiz.

14

C'è molto nella constparola chiave, è piuttosto complessa. Generalmente, aggiungere un sacco di const al tuo programma è considerato una buona pratica di programmazione, cerca nel web "const correttezza" e troverai molte informazioni a riguardo.

La parola chiave const è un cosiddetto "qualificatore di tipo", altri lo sono volatilee restrict. Almeno volatile segue le stesse (confuse) regole di const.


Prima di tutto, la parola chiave const ha due scopi. Il più ovvio è proteggere i dati (e i puntatori) da abusi intenzionali o accidentali rendendoli di sola lettura. Qualsiasi tentativo di modificare una variabile const verrà individuato dal compilatore in fase di compilazione.

Ma c'è anche un altro scopo in qualsiasi sistema con memoria di sola lettura, vale a dire garantire che una determinata variabile sia allocata all'interno di tale memoria - potrebbe essere EEPROM o flash, ad esempio. Questi sono noti come memorie non volatili, NVM. Una variabile allocata in NVM ovviamente seguirà comunque tutte le regole di una variabile const.

Esistono diversi modi per utilizzare la constparola chiave:

Dichiarare una variabile costante.

Questo può essere fatto sia come

const int X=1; or
int const X=1;

Queste due forme sono completamente equivalenti . Quest'ultimo stile è considerato cattivo stile e non dovrebbe essere usato.

Il motivo per cui la seconda riga è considerata stile errato è probabilmente perché "identificatori della classe di archiviazione" come static ed extern possono anche essere dichiarati dopo il tipo effettivo, int staticecc. Ma farlo per gli identificatori della classe di archiviazione è etichettato come una funzionalità obsoleta dal comitato C (bozza ISO 9899 N1539, 6.11.5). Pertanto, per motivi di coerenza, non si dovrebbe nemmeno scrivere qualificatori di tipo in quel modo. Non serve altro che confondere il lettore comunque.

Dichiarare un puntatore a una variabile costante.

const int* ptr = &X;

Ciò significa che il contenuto di 'X' non può essere modificato. Questo è il modo normale di dichiarare i puntatori come questo, principalmente come parte dei parametri di funzione per "const correttezza". Poiché "X" non deve in realtà essere dichiarato come const, potrebbe essere qualsiasi variabile. In altre parole, puoi sempre "aggiornare" una variabile in const. Tecnicamente, C consente anche il downgrade da const a una variabile semplice da parte di comandi espliciti, ma farlo è considerato una cattiva programmazione e i compilatori di solito danno avvisi contro di essa.

Dichiarare un puntatore costante

int* const ptr = &X;

Ciò significa che il puntatore stesso è costante. È possibile modificare ciò a cui punta, ma non è possibile modificare il puntatore stesso. Questo non ha molti usi, ce ne sono alcuni, come garantire che un pointer-pointer-at (pointer-to-pointer) non abbia il suo indirizzo cambiato mentre passato come parametro a una funzione. Dovrai scrivere qualcosa di non troppo leggibile in questo modo:

void func (int*const* ptrptr)

Dubito che molti programmatori C possano ottenere la const e * proprio lì. So che non posso - ho dovuto verificare con GCC. Penso che sia per questo che raramente vedi quella sintassi per puntatore a puntatore, anche se è considerata una buona pratica di programmazione.

I puntatori costanti possono anche essere utilizzati per garantire che la variabile puntatore stessa sia dichiarata nella memoria di sola lettura, ad esempio si potrebbe voler dichiarare una sorta di tabella di ricerca basata su puntatore e allocarla in NVM.

E, naturalmente, come indicato da altre risposte, è possibile utilizzare anche puntatori costanti per imporre la "correttezza const".

Dichiarare un puntatore costante a dati costanti

const int* const ptr=&X;

Questi sono i due tipi di puntatori sopra descritti combinati, con tutti gli attributi di entrambi.

Dichiarare una funzione membro di sola lettura (C ++)

Dato che questo è etichettato C ++, dovrei anche menzionare che puoi dichiarare le funzioni membro di una classe come const. Ciò significa che la funzione non è autorizzata a modificare nessun altro membro della classe quando viene chiamata, il che impedisce sia al programmatore della classe di errori accidentali, ma informa anche il chiamante della funzione membro che non rovinerà nulla chiamandolo. La sintassi è:

void MyClass::func (void) const;

8

... oggi ho capito che i puntatori vengono passati per valore alle funzioni, il che ha senso.

(imo) non ha davvero senso come predefinito. il valore predefinito più ragionevole è passare come puntatore non riassegnabile ( int* const arg). vale a dire, avrei preferito che i puntatori passassero poiché gli argomenti venivano implicitamente dichiarati const.

Quindi qual è il vantaggio di const?

il vantaggio è che è abbastanza facile e talvolta poco chiaro quando si modifica l'indirizzo a cui punta l'argomento, in modo tale da poter introdurre un bug quando non è piuttosto semplice. la modifica dell'indirizzo è atipica. è più chiaro creare una variabile locale se si intende modificare l'indirizzo. inoltre, la manipolazione dei puntatori non elaborati è un modo semplice per introdurre bug.

quindi è più chiaro passare per indirizzo immutabile e creare una copia (in quei casi atipici) quando si desidera modificare l'indirizzo indicato dall'argomento:

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

aggiungendo che local è praticamente gratuito e riduce la possibilità di errori, migliorando al contempo la leggibilità.

a un livello superiore: se si sta manipolando l'argomento come un array, in genere è più chiaro e meno soggetto a errori il client dichiarare l'argomento come contenitore / raccolta.

in generale, aggiungere const a valori, argomenti e indirizzi è una buona idea perché non si realizzano sempre gli effetti collaterali, che il compilatore applica felicemente. pertanto, è utile come const come in altri casi (ad esempio la domanda è simile a "Perché dovrei dichiarare valori const?"). fortunatamente, abbiamo anche riferimenti che non possono essere riassegnati.


4
+1 per questo è l'impostazione predefinita. Il C ++, come la maggior parte delle lingue, ha la direzione sbagliata. Invece di avere una constparola chiave, dovrebbe avere una mutableparola chiave (beh, ce l' ha, ma con la semantica sbagliata).
Konrad Rudolph,

2
Idea interessante con const come default. Grazie per la tua risposta!
R. Ruiz.

6

Se si esegue la programmazione di sistemi embedded o driver di dispositivo in cui sono presenti dispositivi mappati in memoria, vengono spesso utilizzate entrambe le forme di "const", una per impedire la riassegnazione del puntatore (poiché punta a un indirizzo hardware fisso) e, se la periferica il registro a cui punta è un registro hardware di sola lettura, quindi un'altra const rileverà molti errori in fase di compilazione anziché in fase di esecuzione.

Un registro di chip periferico a sola lettura a 16 bit potrebbe assomigliare a:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

Quindi puoi facilmente leggere il registro hardware senza dover ricorrere al linguaggio assembly:

input_word = *peripheral;


5

int iVal = 10; int * const ipPtr = & iVal;

Proprio come una normale variabile const, un puntatore const deve essere inizializzato su un valore al momento della dichiarazione e il suo valore non può essere modificato.

Ciò significa che un puntatore const punterà sempre allo stesso valore. Nel caso sopra, ipPtr punterà sempre all'indirizzo di iVal. Tuttavia, poiché il valore a cui si punta è ancora non const, è possibile modificare il valore a cui si punta tramite dereferenziazione del puntatore:

* ipPtr = 6; // consentito, poiché pnPtr indica un int non const


5

La stessa domanda può essere posta su qualsiasi altro tipo (non solo puntatori):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}

LOL, hai ragione. Mi è venuto in mente il caso dei puntatori perché pensavo che fossero speciali, ma sono proprio come qualsiasi altra variabile passata per valore.
R. Ruiz.

5

La tua domanda è davvero di più sul perché definire una variabile come const non solo come parametro puntatore const a una funzione. Le stesse regole si applicano qui come quando si definisce una variabile come costante, se è un parametro per funzionare o una variabile membro o una variabile locale.

Nel tuo caso particolare, funzionalmente non fa differenza come in molti altri casi quando dichiari una variabile locale come const ma pone una limitazione che non puoi modificare questa variabile.


il tuo commento rende conto di quanto sia inutile la mia domanda perché hai ragione: è la stessa cosa di avere qualsiasi altro parametro come const. Può sembrare inutile, ma è lì per aiutare il programmatore ad avere questa limitazione ed evitare bug
R. Ruiz.

4

Passare un puntatore const a una funzione non ha molto senso, poiché verrà comunque passato per valore. È solo una di quelle cose consentite dal design del linguaggio generale. Proibirlo solo perché non ha senso renderebbe le specifiche della lingua. più grandi.

Se sei all'interno di una funzione, è ovviamente un altro caso. Avere un puntatore che non può cambiare ciò a cui punta è un'asserzione che rende il codice più chiaro.


4

Immagino che un vantaggio sarebbe che il compilatore può eseguire ottimizzazioni più aggressive all'interno della funzione sapendo che questo puntatore non può cambiare.

Evita anche ad es. passando questo puntatore a una sottofunzione che accetta un riferimento puntatore non const (e potrebbe quindi cambiare il puntatore come void f(int *&p)), ma concordo sul fatto che l'utilità è piuttosto limitata in questo caso.


+1 per le ottimizzazioni. Almeno i libri dicono che è vero. Quello che vorrei è capire esattamente quali sono queste ottimizzazioni
R. Ruiz.

4

Un esempio di dove un puntatore const è altamente applicabile può essere dimostrato in questo modo. Considera di avere una classe con un array dinamico al suo interno e desideri passare all'utente l'accesso all'array ma senza concedere loro i diritti per modificare il puntatore. Tener conto di:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

Che produce:

I dati di input
mettono i dati

Ma se proviamo questo:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

Noi abbiamo:

errore: lvalue richiesto come operando di sinistra dell'assegnazione // Drat nuovamente sventato!

Quindi chiaramente possiamo modificare il contenuto dell'array, ma non il puntatore dell'array. Buono se vuoi assicurarti che il puntatore abbia uno stato coerente quando lo restituisci all'utente. C'è un problema, però:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

Possiamo ancora eliminare il riferimento di memoria del puntatore, anche se non possiamo modificare il puntatore stesso.

Quindi, se si desidera che il riferimento di memoria punti sempre a qualcosa (IE non viene mai modificato, simile a come funziona attualmente un riferimento), allora è altamente applicabile. Se vuoi che l'utente abbia pieno accesso e modificarlo, allora non-const fa per te.

Modificare:

Dopo aver notato il commento di okorz001 di non essere in grado di assegnare a causa del fatto che GetArray () è un operando di valore corretto, il suo commento è del tutto corretto, ma quanto sopra vale ancora se si dovesse restituire un riferimento al puntatore (suppongo di aver presupposto che GetArray fosse riferimento a un riferimento), ad esempio:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

Restituirà nel primo con conseguente errore:

errore: assegnazione della posizione di sola lettura 'Temp.TestA :: GetArray ()'

Ma il secondo si verificherà allegramente nonostante le potenziali conseguenze sul lato inferiore.

Ovviamente, verrà sollevata la domanda "perché dovresti voler restituire un riferimento a un puntatore"? Ci sono rari casi in cui è necessario assegnare memoria (o dati) direttamente al puntatore originale in questione (ad esempio, costruendo il proprio front-end malloc / free o new / free), ma in questi casi è un riferimento non const . Un riferimento a un puntatore const Non mi sono imbattuto in una situazione che lo giustifichi (a meno che non sia forse come variabili di riferimento const dichiarate piuttosto che tipi di ritorno?).

Considera se abbiamo una funzione che accetta un puntatore const (rispetto a uno che non lo fa):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

L'errore nel const produce quindi il messaggio:

errore: decremento del parametro di sola lettura 'Dati'

Il che è positivo perché probabilmente non vogliamo farlo, a meno che non vogliamo causare i problemi indicati nei commenti. Se modifichiamo il decremento nella funzione const, si verifica quanto segue:

NonConst: A
Const: B

Chiaramente, anche se A è 'Data [1]', viene trattato come 'Data [0]' perché il puntatore NonConst ha permesso l'operazione di decremento. Con la const implementata, come scrive un'altra persona, rileviamo il potenziale bug prima che si verifichi.

Un'altra considerazione principale è che un puntatore const può essere usato come pseudo riferimento, in quanto la cosa a cui punta il riferimento non può essere cambiata (ci si chiede, se forse è così che è stato implementato). Tener conto di:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

Quando si tenta di compilare, produce il seguente errore:

errore: assegnazione della variabile di sola lettura 'B'

Il che è probabilmente una brutta cosa se si volesse un riferimento costante ad A. Se B = NULLviene commentato, il compilatore ci lascerà felicemente modificare *Be quindi A. Questo potrebbe non sembrare utile con ints, ma considera se hai una singola posizione di un'applicazione grafica in cui desideri un puntatore non modificabile che ti fa riferimento che potresti passare in giro.

Il suo utilizzo è variabile (scusate il gioco di parole non intenzionale), ma usato correttamente, è un altro strumento nella scatola per aiutare con la programmazione.


2
Temp.GetArray() = NULLfallisce perché Temp.GetArray()è un valore, non perché è qualificato con cost. Inoltre, credo che il qualificatore const venga rimosso da tutti i tipi di ritorno.
Oscar Korz,

@ okorz001: dopo il test, hai davvero ragione. Tuttavia, quanto sopra si applica se si restituisce un riferimento al puntatore stesso. Modificherò il mio post di conseguenza.
SSight3,

3

Non c'è niente di speciale nei puntatori in cui non vorresti mai che fossero costanti. Così come puoi avere intvalori costanti per i membri della classe , puoi anche avere puntatori costanti per ragioni simili: vuoi assicurarti che nessuno cambi mai ciò che viene indicato. Riferimenti C ++ in qualche modo risolvono questo problema, ma il comportamento del puntatore è ereditato da C.


3

Credo che ciò impedirebbe al codice di incrementare o decrementare il puntatore all'interno del corpo della funzione.


3

Tipi di dichiarazione di qualsiasi variabile come-
(1) Dichiarazione di una variabile costante.
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) Dichiarare un puntatore a una variabile costante
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified


Forniamo una variabile puntatore come const in una funzione come argomento quando non vogliamo cambiare il suo valore reale
Pyadav,
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.