Modifica globale: scusate ragazzi, sono stato tutto eccitato e ho scritto molte sciocchezze. Solo un vecchio geezer ranting.
Volevo credere che C fosse stato risparmiato, ma purtroppo dal C11 è stato portato alla pari con il C ++. Apparentemente, sapere cosa farà il compilatore con gli effetti collaterali nelle espressioni richiede ora di risolvere un piccolo enigma matematico che coinvolge un ordinamento parziale delle sequenze di codici basato su un "si trova prima del punto di sincronizzazione di".
Mi è capitato di aver progettato e implementato alcuni sistemi critici integrati in tempo reale nei giorni di K&R (incluso il controller di un'auto elettrica che potrebbe mandare le persone a schiantarsi contro la parete più vicina se il motore non fosse tenuto sotto controllo, un industriale di 10 tonnellate robot che potrebbe schiacciare le persone in una polpa se non correttamente comandato, e un livello di sistema che, sebbene innocuo, avrebbe alcune decine di processori che risucchiano il loro bus dati a secco con meno dell'1% di sovraccarico del sistema).
Potrei essere troppo senile o stupido per ottenere la differenza tra indefinito e non specificato, ma penso di avere ancora una buona idea di cosa significhino l'esecuzione simultanea e l'accesso ai dati. Secondo la mia opinione probabilmente informata, questa ossessione per il C ++ e ora i ragazzi C con i loro linguaggi da compagnia che si occupano di problemi di sincronizzazione è un sogno costoso. O sai cos'è l'esecuzione simultanea, e non hai bisogno di nessuno di questi aggeggi, oppure non lo fai, e faresti un favore al mondo in generale senza cercare di rovinarlo.
Tutto questo carico di astrazioni di barriera di memoria allettante è semplicemente dovuto a una serie temporanea di limiti dei sistemi cache multi-CPU, che possono essere incapsulati in modo sicuro in oggetti di sincronizzazione del sistema operativo comuni come, ad esempio, i mutex e le variabili di condizione C ++ offerte.
Il costo di questo incapsulamento è solo un piccolo calo delle prestazioni rispetto a ciò che in alcuni casi potrebbe essere utile un uso di istruzioni specifiche della CPU a grana fine.
La volatile
parola chiave (o a#pragma dont-mess-with-that-variable
per quanto mi occupassi, come programmatore di sistema, sarei stato abbastanza per dire al compilatore di smettere di riordinare gli accessi alla memoria. Il codice ottimale può essere facilmente prodotto con direttive ASM dirette per cospargere driver di basso livello e codice OS con istruzioni specifiche della CPU ad hoc. Senza una conoscenza approfondita del funzionamento dell'hardware sottostante (sistema cache o interfaccia bus), sei comunque obbligato a scrivere codice inutile, inefficiente o difettoso.
Un minuto di aggiustamento della volatile
parola chiave e Bob sarebbero stati tutti, tranne lo zio dei programmatori di basso livello più sodo. Invece, la solita banda di maniaci della matematica in C ++ ha avuto una giornata campale progettando l'ennesima incomprensibile astrazione, cedendo alla loro tipica tendenza a progettare soluzioni alla ricerca di problemi inesistenti e confondendo la definizione di un linguaggio di programmazione con le specifiche di un compilatore.
Solo che questa volta il cambiamento ha richiesto di deturpare anche un aspetto fondamentale di C, poiché queste "barriere" dovevano essere generate anche nel codice C di basso livello per funzionare correttamente. Ciò, tra le altre cose, ha provocato il caos nella definizione delle espressioni, senza alcuna spiegazione o giustificazione.
In conclusione, il fatto che un compilatore possa produrre un codice macchina coerente da questo assurdo pezzo di C è solo una lontana conseguenza del modo in cui i ragazzi di C ++ hanno affrontato potenziali incoerenze dei sistemi di cache alla fine degli anni 2000.
Ha creato un terribile pasticcio di un aspetto fondamentale di C (definizione di espressione), così che la stragrande maggioranza dei programmatori C - che non se ne frega niente dei sistemi cache, e giustamente - è ora costretta a fare affidamento sui guru per spiegare differenza tra a = b() + c()
e a = b + c
.
Cercare di indovinare cosa ne sarà di questa sfortunata schiera è comunque una perdita netta di tempo e sforzi. Indipendentemente da cosa ne farà il compilatore, questo codice è patologicamente sbagliato. L'unica cosa responsabile a che fare con esso è inviarlo al cestino.
Concettualmente, gli effetti collaterali possono sempre essere spostati dalle espressioni, con il banale sforzo di lasciare esplicitamente la modifica prima o dopo la valutazione, in una dichiarazione separata.
Questo tipo di codice di merda potrebbe essere stato giustificato negli anni '80, quando non ci si poteva aspettare che un compilatore ottimizzasse qualcosa. Ma ora che i compilatori sono diventati a lungo più intelligenti della maggior parte dei programmatori, tutto ciò che rimane è un pezzo di codice di merda.
Non riesco anche a capire l'importanza di questo dibattito indefinito / non specificato. O puoi fare affidamento sul compilatore per generare codice con un comportamento coerente o non puoi. Se lo chiami indefinito o non specificato sembra un punto controverso.
Secondo la mia opinione probabilmente informata, C è già abbastanza pericoloso nel suo stato di K&R. Un'evoluzione utile sarebbe quella di aggiungere misure di sicurezza di buon senso. Ad esempio, facendo uso di questo strumento avanzato di analisi del codice, le specifiche costringono il compilatore a implementare almeno per generare avvisi sul codice bonkers, invece di generare silenziosamente un codice potenzialmente inaffidabile all'estremo.
Ma invece i ragazzi hanno deciso, ad esempio, di definire un ordine di valutazione fisso in C ++ 17. Ora ogni software imbecille è attivamente incitato a mettere di proposito gli effetti collaterali nel suo codice, crogiolandosi nella certezza che i nuovi compilatori gestiranno con entusiasmo l'offuscamento in modo deterministico.
K&R è stata una delle vere meraviglie del mondo dell'informatica. Per venti dollari hai ottenuto una specifica completa della lingua (ho visto singoli individui scrivere compilatori completi solo usando questo libro), un eccellente manuale di riferimento (il sommario di solito ti indicherà un paio di pagine della risposta alla tua domanda) e un libro di testo che ti insegnerebbe ad usare la lingua in modo sensato. Completa di razionali, esempi e sagge parole di avvertimento sui numerosi modi in cui potresti abusare della lingua per fare cose molto, molto stupide.
Distruggere quell'eredità per così poco guadagno mi sembra uno spreco crudele. Ma di nuovo potrei benissimo non riuscire a vedere il punto completamente. Forse un'anima gentile potrebbe indirizzarmi verso un esempio di nuovo codice C che sfrutta in modo significativo questi effetti collaterali?