2010-05-05 22 views
29

Alcuni vecchio codice che ho appena incontrato:Perché questo codice funziona ancora?

MLIST * new_mlist_link() 
{ 
    MLIST *new_link = (MLIST *) malloc(sizeof(MLIST)); 
    new_link->next = NULL; 
    new_link->mapi = NULL; 
    new_link->result = 0; 
} 

Questo è stato chiamato a costruire una lista collegata, tuttavia ho notato non v'è alcuna dichiarazione:

return new_link; 

Anche senza l'istruzione return lì, la lista è ancora stata costruita correttamente. Perché è successo?

Edit: Piattaforma: Mandriva 2009 a 64 bit Linux 2.6.24.7-server di GCC 4.2.3-6mnb1

Edit: divertente ... questo codice anche eseguito con successo su circa 5 diverse installazioni di Linux, tutta versioni/sapori differenti, così come un Mac.

+2

Non c'è risposta possibile a parte la fortuna muto, a meno che (o forse anche se) si facci sapere quale piattaforma. – Potatoswatter

+1

Questo rende un buon caso per l'utilizzo di un correttore di codice statico. – semaj

+3

Interessante che funzionasse su così tante piattaforme. Ha solo una variabile locale, quindi deve essere abbastanza standard per GCC su Intel per memorizzare una singola variabile locale nell'EAX (o il suo equivalente a 64 bit), e anche per usare quel registro per i valori di ritorno. –

risposta

35

Su Windows a 32 bit, la maggior parte delle volte, il valore restituito da una funzione viene lasciato nel registro EAX. Configurazioni simili sono usate in altri sistemi operativi, anche se ovviamente è specifico del compilatore. Questa particolare funzione memorizzava presumibilmente la variabile new_link nella stessa posizione, quindi quando si ritornava senza un ritorno, la variabile in quella posizione veniva considerata come valore di ritorno dal chiamante.

Questo è non-portatile e molto pericoloso da fare in realtà, ma è anche una delle piccole cose che rendono la programmazione in C così divertente.

+4

+1, con l'aggiunta che è l'ABI, non il sistema operativo che definisce la convenzione di chiamata. – danben

+1

Questo spiega perché è stato eseguito, ma il motivo per cui è stato compilato è la domanda successiva. : P Penseresti che il compilatore controllasse una dichiarazione di ritorno (so che tutto il mio fare). Ad ogni modo, mentre funziona, può sicuramente essere etichettato come una cosa cattiva. – ssube

+1

Penso che questo stile di codifica fosse più comune 30 anni fa quando C stava diventando popolare. Il progetto da cui proveniva questo codice era probabilmente scritto da un vecchio coder (o solo molto tempo fa) e da allora aveva disabilitato gli avvisi del compilatore in modo che questo si compilasse correttamente. –

5

In pratica è fortuna; apparentemente, il compilatore capita di attaccare new_link nello stesso posto in cui si incollerebbe un valore restituito.

-1

Probabilmente una coincidenza:

Lo spazio per il valore di ritorno della funzione viene assegnato prima del tempo. Dal momento che quel valore non è inizializzato, potrebbe indicare lo stesso spazio sull'heap come la memoria allocata per la struttura.

+1

Uhh, no, i valori restituiti vengono di solito restituiti nei registri o, suppongo, nello stack. Non è un puntatore casuale essere misteriosamente corretto. I valori di ritorno non sono assegnati. – WhirlWind

+1

Come indicato sopra, i valori restituiti vengono restituiti in un registro se si adattano, come nel caso di un puntatore. – Joel

+4

@Whirl: su alcune architetture, lo spazio viene riservato nello stack per i valori restituiti quando viene richiamata una procedura. Su altri, il risultato viene restituito in un registro. È ** diverso **. –

1

Questo funziona per coincidenza. Non dovresti fare affidamento su questo.

7

Possibile utilizza semplicemente il registro EAX che normalmente memorizza il valore di ritorno dell'ultima funzione chiamata. Questa non è affatto una buona pratica! Il comportamento per questo genere di cose è indefinito .. Ma è bello vedere il lavoro ;-)

1

Molto probabilmente è che produrrà un bug molto difficile da trovare. Non sono sicuro di dove lo leggo, ma ricordo che se si dimentica di inserire una dichiarazione di ritorno, la maggior parte dei compilatori imposterà di default void.

Ecco un breve esempio:

#include <iostream> 

using namespace std; 

int* getVal(); 

int main() 
{ 
     int *v = getVal(); 
     cout << "Value is: " << *v << endl; 
     return 0; 
} 

int* getVal() 
{ 
     // return nothing here 
} 

Per me, questo funziona pure. Tuttavia, quando lo eseguo, ottengo un errore di segmento. Quindi è davvero indefinito. Solo perché compila, non significa che funzionerà.

+1

Watson presumo? –

0

Funziona perché negli anni '40, quando fu creato il linguaggio C, non esisteva la parola chiave return.Se si guarda nella sezione "funzioni" dello standard C43 Minsi, ha questo da dire in materia (tra le altre cose):

16.4.3b For backwards compatibility the EAX register MUST be used to return 
     the address of the first chunk of memory allocated by malloc. 

</humour>

+4

No, non lo è. Non esiste uno standard C43 MINSI, sta solo inventandosi. –

+0

Ora sappiamo tutti l'umorismo di James. perché giù votato? – tristan

5

Per evitare questo problema, utilizzare:

-Wreturn-type:

Warn whenever a function is defined with a return-type that defaults to int. Also warn about any return statement with no return-value in a function whose return-type is not void (falling off the end of the function body is considered returning without a value), and about a return statement with an expression in a function whose return-type is void.

-Werror=return-type per accendere la sopra in un errore:

Make the specified warning into an error. The specifier for a warning is appended, for example -Werror=switch turns the warnings controlled by -Wswitch into errors. This switch takes a negative form, to be used to negate -Werror for specific warnings, for example -Wno-error=switch makes -Wswitch warnings not be errors, even when -Werror is in effect. You can use the -fdiagnostics-show-option option to have each controllable warning amended with the option which controls it, to determine what to use with this option.

(da GCCs warning options)

Problemi correlati