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
.
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
'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? –
D'accordo, questo è inaccettabile! Di solito non scrivo "main" come questo, questo è il mio file di test 'toto.c' codificato pigro. Lo aggiusterò. – Coconop