2014-08-29 6 views
6

Ho trovato uno strano comportamento da gcc con il seguente codice:Perché un avvertimento del const-qualificatore dipende dal contenuto della variabile, non dal tipo?

#include <stdio.h> 
#include <string.h> 

int main(void) 
{ 
    const char str[] = "This string contains é which is a multi-byte character"; 
    const char search[] = "Ʃ"; 
    char * pos = strstr(str, search); 
    printf("%s\n", pos); 

    return 0; 
} 

Il compilatore produce un avvertimento:

$ gcc toto.c -std=c99 
toto.c: In function ‘main’: 
toto.c:8:18: warning: initialization discards ‘const’ qualifier 
from pointer target type [enabled by default] 

Ma se cambio il contenuto di search:

const char search[] = "é"; 

La stessa compilation non lancia alcun avvertimento: perché?

Nota: Ho lo stesso comportamento esatto se sostituisco Ʃ e é: se il personaggio in search non è presente nel str, ottengo l'avvertimento.

+1

Come nota: 'strstr' restituisce un puntatore nullo se la sottostringa non viene trovata e un puntatore nullo non può essere passato a' printf' con '% s'. – mafso

+0

'void main()' dovrebbe essere 'int main (void)'. Se stai usando un libro di testo che ti dice di usare 'void main()', eliminalo e trova uno migliore; il suo autore non conosce C abbastanza bene da insegnarlo. ('void main()' può essere accettabile per alcune implementazioni, ma nel migliore dei casi è non-portatile.) Puoi dirci dove hai avuto l'idea che 'void main()' è accettabile in C? –

+0

D'accordo, questo è inaccettabile! Di solito non scrivo "main" come questo, questo è il mio file di test 'toto.c' codificato pigro. Lo aggiusterò. – Coconop

risposta

3

Sembra un bug in gcc, corretto in una versione successiva.

Ecco un piccolo programma che ho scritto per illustrare il problema.

#include <stdio.h> 
#include <string.h> 

int main(void) { 
    const char message[] = "hello"; 
#ifdef ASCII_ONLY 
    const char search_for[] = "h"; 
#else 
    const char search_for[] = "Ʃ"; 
#endif 
    char *non_const_message = strstr(message, search_for); 
    if (non_const_message == NULL) { 
     puts("non_const_message == NULL"); 
    } 
    else { 
     puts(non_const_message); 
    } 
} 

Quando compilo questo con

gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c 

(usando gcc 4.8.2 su Linux Mint 17), si compila senza messaggi diagnostici e le conseguenti programma stampa

hello 

(Io uso -pedantic-errors perché ciò induce gcc a (tentare di) di essere un compilatore ISO C conforme.)

Quando lascio cadere l'opzione -DASCII_ONLY, ricevo un messaggio di errore di compilazione:

c.c: In function ‘main’: 
c.c:11:31: error: initialization discards ‘const’ qualifier from pointer target type 
    char *non_const_message = strstr(message, search_for); 

La funzione strstr restituisce un risultato di tipo char*, nonconst char*. Sono necessari due argomenti const char* e con la stringa di ricerca corretta è possibile restituire il valore del primo argomento. Ciò significa che può scartare in silenzio l'argomento const. Ritengo che questo sia un difetto nella libreria standard C, ma probabilmente siamo bloccati. Le implementazioni conformi C non hanno la possibilità di "aggiustare" questo difetto se vogliono rimanere conformi; possono avvisare sugli usi pericolosi di strstr ma non possono rifiutare altrimenti il ​​codice legale.

(Il difetto avrebbe potuto essere evitato suddividendo strstr in due funzioni con nomi diversi, uno prendendo un const char* e restituendo un const char*, e l'altro prendendo un char* e restituire un char*. Il comitato di 1.989 ANSI C non ha preso l'opportunità di farlo, perché non ci pensavano o perché non volevano rompere il codice esistente. Il C++ lo indirizza con due versioni sovraccaricate di strstr, che non era possibile per C.)

La mia prima ipotesi è che gcc sta "magicamente" facendo qualcosa di simile a ciò che fa C++ - ma gli esempi che scaricano const usando solo caratteri ASCII non causano un messaggio diagnostico. Come mostra il mio programma di test, il problema viene attivato dall'uso di un carattere non ASCII in una stringa letterale ("Ʃ" anziché "h").

Quando uso gcc 4.9.1 (che ho installato dai sorgenti), piuttosto che gcc 4.8.2 (la versione di default installata sul mio sistema), il problema scompare:

$ gcc -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c 
hello 
$ gcc -std=c99 -pedantic-errors c.c -o c && ./c 
c.c: In function ‘main’: 
c.c:11:31: error: initialization discards ‘const’ qualifier from pointer target type 
    char *non_const_message = strstr(message, search_for); 
          ^
$ gcc-4.9.1 -DASCII_ONLY -std=c99 -pedantic-errors c.c -o c && ./c 
hello 
$ gcc-4.9.1 -std=c99 -pedantic-errors c.c -o c && ./c 
non_const_message == NULL 
$ 

non ho rintracciato il bug oltre a quello, ma probabilmente lo si poteva trovare in una lista di bug gcc risolti tra 4.8.2 e 4.9.1

Per il codice nella domanda, è possibile evitare il problema definendo const char* come const char* piuttosto che char*. Deve essere const char* in ogni caso, poiché punta a un oggetto che è stato definito come const.

7

Un paio di cose stanno succedendo qui.

I file di intestazione gcc indicano a gcc di utilizzare il suo strstr() ottimizzato, integrato, che il compilatore sa di cosa si tratta. Puramente dal punto di vista della lingua, strstr() è solo una funzione di libreria che, in teoria, non è a conoscenza del compilatore. Ma, lo gcc sa davvero di cosa si tratta.

versione ottimizzata di gcc di strstr() se il parametro stringa è un char *, strstr() restituisce un char *; ma se il parametro stringa è un const char *, strstr() restituisce uno const char *, il che ha senso.

Quindi, nel tuo caso, strstr() restituisce un const char *, che si traduce in un errore evidente, assegnando a un non-const char *.

Ciò che sembra anche accadere è che, nella seconda parte della domanda, gcc rileva che la stringa esiste e ottimizza l'intera cosa; ma in tal caso dovrebbe anche risultare in una conversione da const char * a char * e un avviso. Non sono sicura di questo.

+0

Clang compila questo (una volta modificato il tipo di ritorno di 'main'), quindi sembra probabile. – chris

+0

Vuol dire che al momento della compilazione, 'gcc' sa che la chiamata' strstr' avrà successo, guardando il contenuto del parametro stringa? – Coconop

+2

Sì, gcc è fantastico così. – Quentin

Problemi correlati