const char * const rispetto a const char *?


110

Sto eseguendo alcuni programmi di esempio per familiarizzare con il C ++ e mi sono imbattuto nella seguente domanda. Innanzitutto, ecco il codice di esempio:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

Nel codice sopra, il parametro per print_string avrebbe potuto invece essere const char * const the_string. Quale sarebbe più corretto per questo?

Capisco che la differenza è che uno è un puntatore a un carattere costante, mentre l'altro è un puntatore costante a un carattere costante. Ma perché funzionano entrambi? Quando sarebbe rilevante?

Risposte:


244

Quest'ultimo ti impedisce di modificare the_stringall'interno print_string. In realtà sarebbe appropriato qui, ma forse la verbosità ha scoraggiato lo sviluppatore.

char* the_string: Posso cambiare i punti in charcui the_stringe posso modificare i charpunti in cui punta.

const char* the_string: Posso cambiare i punti in charcui the_string, ma non posso modificare i charpunti in cui punta.

char* const the_string: Non posso cambiare i punti in charcui the_string, ma posso modificare i charpunti in cui punta.

const char* const the_string: Non posso cambiare i punti in charcui the_string, né posso modificare i charpunti in cui punta.


11
+1 per l'ultima frase. La correttezza della costanza è prolissa, ma ne vale la pena.
mskfisher

6
@Xeo: la tua forma è ancora più confusa perché è a una trasposizione dal cambiare completamente il suo significato. const char *è molto meglio perché constè completamente opposto.
R .. GitHub SMETTA DI

7
@R ..: beh, almeno per me non lo è. Leggendo da destra a sinistra, ottengo "pointer to const char". Per me, è solo che è meglio così.
Xeo

6
Beh, ti stai prendendo in giro perché i tipi C vengono letti dall'interno verso l'esterno, non da sinistra a destra. :-)
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

11
Sono un po 'imbarazzato a quanto pare sono l'unico che non lo capisce ... ma qual è la differenza tra "il carattere a cui punta" e "il carattere a cui punta"?
Mancanza

138
  1. Puntatore mutevole a un personaggio mutevole

    char *p;
  2. Puntatore mutevole a un carattere costante

    const char *p;
  3. Puntatore costante a un carattere mutevole

    char * const p; 
  4. Puntatore costante a un carattere costante

    const char * const p;

Non dovrebbe essere: const char* p; --> constant pointer to mutable charactere char *const p; --> mutable pointer to constant character
PnotNP

2
@NulledPointer No. Le dichiarazioni C ++ vengono formate da destra a sinistra. Puoi quindi leggere const char * pcome: "p è un puntatore a una costante di carattere" o un puntatore mutabile a un carattere costante come afferma correttamente James. lo stesso con il secondo :).
Samidamaru

28

const char * constsignifica che il puntatore così come i dati a cui punta il puntatore, sono entrambi const!

const char *significa che solo i dati a cui punta il puntatore sono const. il puntatore stesso tuttavia non è const.

Esempio.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 

20

(So ​​che questo è vecchio ma volevo condividerlo comunque.)

Volevo solo approfondire la risposta di Thomas Matthews. La regola destra-sinistra delle dichiarazioni di tipo C dice più o meno: quando si legge una dichiarazione di tipo C, inizia dall'identificatore e vai a destra quando puoi ea sinistra quando non puoi.

Questo è spiegato al meglio con un paio di esempi:

Esempio 1

  • Inizia dall'identificatore, non possiamo andare a destra, quindi andiamo a sinistra

    const char* const foo
                ^^^^^

    foo è una costante ...

  • Continua a sinistra

    const char* const foo
              ^

    foo è un puntatore costante a ...

  • Continua a sinistra

    const char* const foo
          ^^^^

    foo è un puntatore costante a char ...

  • Continua a sinistra

    const char* const foo
    ^^^^^

    foo è un puntatore costante alla costante char (Complete!)

Esempio 2

  • Inizia dall'identificatore, non possiamo andare a destra, quindi andiamo a sinistra

    char* const foo
          ^^^^^

    foo è una costante ...

  • Continua a sinistra

    char* const foo
        ^

    foo è un puntatore costante a ...

  • Continua a sinistra

    char* const foo
    ^^^^

    foo è un puntatore costante a char (Complete!)

Esempio 1337

  • Inizia dall'identificatore, ma ora possiamo andare a destra!

    const char* const* (*foo[8])()
                            ^^^

    foo è un array di 8 ...

  • Premi le parentesi così non puoi più andare a destra, vai a sinistra

    const char* const* (*foo[8])()
                        ^

    foo è un array di 8 puntatori a ...

  • Finito tra parentesi, ora può andare a destra

    const char* const* (*foo[8])()
                                ^^

    foo è un array di 8 puntatori a una funzione che restituisce ...

  • Niente di più a destra, vai a sinistra

    const char* const* (*foo[8])()
                     ^

    foo è un array di 8 puntatori a una funzione che restituisce un puntatore a un ...

  • Continua a sinistra

    const char* const* (*foo[8])()
                ^^^^^

    foo è un array di 8 puntatori a funzioni che restituisce un puntatore a una costante ...

  • Continua a sinistra

    const char* const* (*foo[8])()
              ^

    foo è un array di 8 puntatori a funzioni che restituisce un puntatore a un puntatore costante a un ...

  • Continua a sinistra

    const char* const* (*foo[8])()
          ^^^^

    foo è un array di 8 puntatori a funzioni che restituisce un puntatore a un puntatore costante a un carattere ...

  • Continua a sinistra

    const char* const* (*foo[8])()
    ^^^^^

    foo è un array di 8 puntatori a funzioni che restituisce un puntatore a una costante puntatore a una costante char (Complete!)

Ulteriore spiegazione: http://www.unixwiz.net/techtips/reading-cdecl.html


CMIIW, const char * const foo dovrebbe essere equivalente a char const * const foo?
luochenhuan

@luochenhuan Sì, in effetti lo sono.
Garrett

12

Molte persone suggeriscono di leggere l'identificatore del tipo da destra a sinistra.

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

In entrambe le forme, il puntatore punta a dati costanti o di sola lettura.

Nella seconda forma, il puntatore non può essere modificato; il puntatore punterà sempre nella stessa posizione.


3

La differenza è che senza l'extra constil programmatore potrebbe cambiare, all'interno del metodo, dove punta il puntatore; per esempio:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

Sarebbe invece illegale se la firma lo fosse void print_string(const char * const the_string)

Molti programmatori ritengono troppo prolissa (nella maggior parte degli scenari) la constparola chiave extra e la omettono, anche se sarebbe semanticamente corretta.


2

In quest'ultimo stai garantendo di non modificare sia il puntatore che il carattere nel primo ti garantisci solo che il contenuto non cambierà ma puoi spostare il puntatore


Ah, quindi senza la const finale, potrei effettivamente impostare il puntatore in modo che punti a una stringa completamente diversa?
fig

Sì, senza quella const finale puoi usare il puntatore del parametro per fare qualche iterazione con l'aritmetica del puntatore, dove se c'era quella const dovevi creare il tuo puntatore che è una copia di quel parametro.
Jesus Ramos

2

Non c'è motivo per cui nessuno dei due funzioni. Tutto print_string()non fa altro che stampare il valore. Non cerca di modificarlo.

È una buona idea creare una funzione che non modifichi gli argomenti dei segni come const. Il vantaggio è che le variabili che non possono cambiare (o che non si desidera modificare) possono essere passate a queste funzioni senza errori.

Per quanto riguarda la sintassi esatta, si desidera indicare quale tipo di argomenti è "sicuro" per essere passati alla funzione.


2

Penso che sia raramente rilevante, perché la tua funzione non viene chiamata con argomenti come & * the_string o ** the_string. Il puntatore stesso è un argomento di tipo valore, quindi anche se lo modifichi non cambierai la copia che è stata usata per chiamare la tua funzione. La versione che stai mostrando assicura che la stringa non cambierà e penso che sia sufficiente in questo caso.


2

const char *significa che non puoi usare il puntatore per cambiare ciò a cui punta. Puoi cambiare il puntatore in modo che punti a qualcos'altro, però.

Tener conto di:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

Il parametro è un puntatore non const a const char, quindi può essere modificato in un altro const char *valore (come una stringa costante). Se, tuttavia, scrivessimo per errore *text = '\0', otterremmo un errore di compilazione.

Probabilmente, se non si intende modificare ciò a cui punta il parametro, è possibile creare il parametro const char * const text, ma non è comune farlo. Di solito permettiamo alle funzioni di modificare i valori passati ai parametri (poiché passiamo i parametri per valore, qualsiasi modifica non influisce sul chiamante).

BTW: è buona norma evitare char const * perché spesso viene letto male - significa lo stesso di const char *, ma troppe persone lo leggono come significato char * const.


Wow! Stavo cercando di capire la differenza tra la mia const char *e la firma char const *- il modo in cui hai formulato il tuo BTW ha davvero aiutato!
saggio

1

Quasi tutte le altre risposte sono corrette, ma mancano di un aspetto: quando si utilizza l'extra constsu un parametro in una dichiarazione di funzione, il compilatore lo ignorerà essenzialmente. Per un momento, ignoriamo la complessità del tuo esempio come puntatore e usiamo semplicemente un file int.

void foo(const int x);

dichiara la stessa funzione di

void foo(int x);

Solo nella definizione della funzione è più constsignificativo:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

Questa definizione è compatibile con una delle dichiarazioni precedenti. Al chiamante non interessa che xsia const- questo è un dettaglio di implementazione che non è rilevante nel sito della chiamata.

Se hai un constpuntatore ai constdati, si applicano le stesse regole:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

Pochi programmatori C ++ si preoccupano di creare parametri const, anche quando potrebbero esserlo, indipendentemente dal fatto che quei parametri siano puntatori.


"il compilatore lo ignorerà essenzialmente" non sempre è vero, Visual C ++ 2015 produrrà un avviso se si aggiunge l'extra constal parametro della funzione nella definizione ma non nella dichiarazione.
raymai97

@ raymai97: Penso che sia un bug nel compilatore 2015, ma non ho il 2015 a portata di mano da testare. Lavoro come ho descritto nel 2017, che, secondo le mie conversazioni con alcuni esperti di standard, è il comportamento previsto.
Adrian McCarthy

-1

La differenza tra i due è che char * può puntare a qualsiasi puntatore arbitrario. Const char * al contrario, punta alle costanti definite nella sezione DATA dell'eseguibile. Di conseguenza, non è possibile modificare i valori dei caratteri di una stringa const char *.


Non sto chiedendo la differenza tra char * e const char *. Sto chiedendo tra const char * e const char * const
fig

Questa risposta non è corretta. A const char*può indicare ovunque voglia.
cubo
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.