ProgrammazioneSenior C Developer, System Programmer

Come vengono riprodotti gli effetti collaterali nel calcolo degli argomenti delle funzioni nel linguaggio C? Qual è l'ordine di calcolo degli argomenti e quali sorprese possono verificarsi?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Nel linguaggio C l'ordine di calcolo degli argomenti di una funzione non è definito dagli standard (fino a C99 incluso). Gli argomenti possono essere calcolati da sinistra a destra, da destra a sinistra o in qualsiasi altro ordine (a discrezione del compilatore o dell'architettura).

  • Tutte le espressioni/effetti collaterali negli argomenti devono essere completati prima della chiamata alla funzione, ma non c'è garanzia che vengano eseguiti in un ordine specifico.
  • Ciò significa che l'uso di variabili con modifica di valore in più argomenti è una potenziale fonte di comportamento indefinito (undefined behavior) o semplicemente di differenze tra architetture.

Esempio

void fn(int a, int b) { /* ... */ } int x = 1; fn(x++, x++); // l'ordine di calcolo di x++ e x++ non è definito!

Domanda ingannevole

"In quale sequenza vengono calcolati gli argomenti di una funzione in C e ci si può fidare di questo quando si scrive codice?"

Un errore comune è pensare che gli argomenti vengano calcolati da sinistra a destra (per analogia con le espressioni). In pratica, ogni chiamata di funzione viene compilata a discrezione del compilatore (e della piattaforma).

void foo(int a, int b, int c); int x = 1; foo(x++, x++, x++); // il risultato dipende dall'ordine di calcolo degli argomenti

La vera risposta è: non ci si può fidare — il comportamento non è definito!

Esempi di errori reali dovuti alla mancanza di conoscenza delle sfumature dell'argomento


Storia

In un prodotto multi-piattaforma, un programmatore ha scritto push(stack, stack->size++, data);. Su la maggior parte delle piattaforme tutto funzionava, ma su una il size dello stack aumentava prima della trasmissione dei dati, mentre su un'altra dopo. I dati "andavano persi" o venivano indirizzati in modo errato, l'errore si manifestava raramente ed era molto difficile da debug.


Storia

In una libreria di protocollo di rete, la funzione di logging veniva chiamata con espressioni-argomenti che incrementavano i contatori statistici. I report statistici venivano generati in modo errato: diversi client avevano indici di contatori diversi, poiché venivano eseguiti in un ordine non previsto.


Storia

Nella gestione dell'hardware, la funzione di inizializzazione passava puntatori e offset con incremento all'interno degli argomenti (tipo init(ptr++, cnt++);). Su alcune CPU, l'hardware veniva inizializzato correttamente, mentre su altre si verificava un crash, la causa è stata a lungo cercata nell'hardware, anche se il problema era nel codice C non corretto.