2012-04-03 17 views
13

Disclaimer: Questa domanda è strettamente accademica. L'esempio che sto per dare è probabilmente di cattivo gusto.Dereferenziazione di un puntatore a dati statici esterni all'intervallo C

Supponiamo che in CI scrivere una subroutine di questa forma:

char *foo(int x) 
{ 
    static char bar[9]; 

    if(x == 0) 
     strcpy(bar, "zero"); 
    else 
     strcpy(bar, "not zero"), 

    return bar; 
} 

Poi, altrove, io uso foo come segue:

printf("%i is %s\n", 5, foo(5)); 

Il mio modello mentale di puntatori e variabili statiche prevede che, in pratica, l'output di questo printf sarà

5 non è zero

... ma questo è effettivamente richiesto dallo standard C o sono nel territorio del demone nasale?

per rendere le cose ancora peggiori, che dire qualcosa di simile

strcpy(foo(5), "five"); 

mio modello mentale dice che questo dovrebbe "lavoro" a meno che non sia esplicitamente illegale, anche se è un po 'inutile in quanto non influenza l'uscita del foo. Ma ancora, questo è effettivamente definito dallo standard?

+2

Per il tuo ultimo esempio, è "legale", ma non sono sicuro che sia "morale". Potrebbe anche avere un semplice vecchio globale se tutti possono modificarlo. (E fai attenzione ai fili). – Mat

risposta

12

Quello che hai scritto è OK; non ci sono demoni nasali che ti aspettano. Anche l'esempio strcpy() è 'OK' perché non si sta calpestando oltre i limiti dell'array. Quel 'OK' è tra virgolette perché non è una buona idea, ma come scritto, non c'è accesso alla memoria fuori limite e quindi nessun comportamento indefinito. I dati static nella funzione sono presenti per tutta la durata del programma, contenente l'ultimo valore che è stato scritto su di esso.

Ci potrebbero essere problemi se si tenta:

printf("%i is %s but %i is %s\n", 5, foo(5), 0, foo(0)); 

Si otterrà una risposta sbagliata per uno dei due numeri, ma non è definito quale sarà la risposta sbagliata.

+0

Perché sarebbe diverso? Non lo vedo ... –

+2

La variabile nella funzione può contenere "non zero" o "zero" nel momento in cui 'printf()' viene chiamato (dopo che le due chiamate a 'foo()' sono completare). Quindi la stessa stringa verrà stampata sia per 0 che per 5, e poiché uno di essi è zero e l'altro no, non importa se la stringa nella funzione contiene "zero" o "non zero"; è sbagliato per uno dei due numeri. Se vuoi essere elegante e averli entrambi corretti, dovresti rivedere la funzione: 'static const char bar [9] =" not zero "; if (x == 0) return & bar [4]; else return & bar [0]; 'e improvvisamente entrambi andranno bene. –

+1

@ 0A0D Perché quella funzione ha un effetto collaterale. – cnicutar

1

Non vedo nulla di sbagliato con questo codice.

Il puntatore che restituisce foo() è un puntatore valido e all'utente è consentito il dereferenziamento.

Modifica: Per "niente di sbagliato" intendo che tutto è sintatticamente ok, ma sono d'accordo con altre risposte che questo codice non è buono in alcun senso della parola per una serie di motivi. È semplicemente corretto secondo lo standard C.

5

Ebbene, dal momento che si desidera una citazione dalla norma:

La durata di un oggetto è la parte di esecuzione del programma durante il quale lo stoccaggio è garantita da assegnare per esso.

Un oggetto le cui identi fi catore viene dichiarata senza classe di archiviazione specifico er _Thread_local, e sia con esterno o interno tiranteria o con la classe di archiviazione specifico er statico, ha una durata di stoccaggio statico.La sua durata è l'intera esecuzione del programma e il suo valore memorizzato viene inizializzato solo una volta, prima dell'avvio del programma.

2

In qualunque condizione, foo() restituisce un puntatore a una variabile statica, la cui durata è permanente (cioè vive da quando il controllo passa sopra per la prima volta fino alla fine del programma). Questo è perfettamente a posto.

La logica del codice è meno soddisfacente; per esempio, la funzione non è rientranti e lontana da thread-safe, e ovviamente l'operazione di stringa incustodita è completamente suicida.

+0

Bene, l'operazione di stringa incustodita è un po 'cattiva in quanto rende più facile rompere le cose in seguito, ma non dovrebbe essere a posto dato che lo uso solo per stringhe letterali? Anche dall'esterno della funzione, se già so che 'foo' restituisce un puntatore a una stringa che non devo liberare, e che quella stringa è talvolta' "non zero" ', quindi posso dedurre che la stringa è assegnato staticamente e posso inserire '" five "' in esso. – Ian

+0

Devi assicurarti di non scrivere mai una stringa più lunga di sette caratteri nella stringa. Forse non "suicidio" (penso di aver letto male il tuo codice come 'strcat' piuttosto che' strcpy'), ma comunque inutilmente pericoloso. Puoi mettere 'strncpy' lì dentro con' sizeof bar'. –

0

Niente di sbagliato. Meglio stile sarà

const char *foo(int x); 

allora non si può eseguire la strcpy in esso senza gettando via il const. Se vuoi veramente romperlo, niente può fermarti.

se vuoi farlo rientrare.

const char *foo(int x) 
{ 
return (x? "not zero" : "zero"); 
} 
Problemi correlati