[1] In effetti questo è proprio lo scopo, più o meno manifesto, di gran parte degli esempi presentati nel testo: dal loro esame appare evidente come spesso la portabilità sia stata sacrificata, a fronte di altri vantaggi.
[2] In tal modo esso è particolarmente adatto ad essere elaborato nei registri interni dei coprocessori matematici 80x07. Il float e il double occupano invece, ripettivamente, 32 e 64 bit.
[3] Quando si abbiano esigenze di portabilità è dunque preferibile evitare il ricorso diffuse prassi quali inizializzare a 1 un char o int per utilizzarlo come maschera avente tutti i bit a 1.
[4] Vediamo un esempietto. Su una macchina equipaggiata con un Intel 80x86 si può validamente utilizzare il costrutto seguente:
union farptr {
void far *pointer;
struct ptrparts {
unsigned offset;
unsigned segment;
} ptr;
} point;
Esso consente di referenziare un puntatore far (point.pointer) oppure la sua word segmento (point.ptr.segment), oppure ancora la word offset (point.ptr.offset) semplicemente utilizzando l'opportuno elemento della union: su macchine equipaggiate con il processore Z8000 tale costrutto maniene la propria validità sintattica, ma point.ptr.offset contiene il segmento del puntatore e, viceversa, point.ptr.segment ne contiene l'offset, in quanto lo Z8000 non utilizza la tecnica backwords.
[5] Ovviamente ogni compilatore C è tenuto al rispetto delle priorità algebriche; tuttavia la valutazione di un'espressione può procedere, a parità di priorità algebrica tra gli elementi valutati, indifferentemente da sinistra a destra o viceversa (per alcuni operatori non sono definiti vincoli di associatività), a tutto vantaggio dell'efficienza complessiva del programma.
[6] Esempio, tratto dalla realtà. In tutte le implementazioni standard, toupper(char c) converte in maiuscolo, se minuscolo, il carattere contenuto in c. Se la libreria utilizzata implementa toupper() come funzione, la riga di codice:
a = toupper(++c);
può dare luogo alle ambiguità concernenti il momento in cui c è incrementata. Ma portando il sorgente ad un compilatore che implementi toupper() come macro:
#define toupper(c) (islower(c) ? c - 'a' + 'A' : c)
la riga di codice viene espansa dal preprocessore nel modo seguente:
a = (islower(++c) ? ++c - 'a' + 'A' : ++c);
con le immaginabili conseguenze sui valori finali di c e di a.
[7] In STDDEF.H.
[8] High Performance File System.
[9] Dispositivi hardware pilotati da apposite interfaccia software (device driver), che comunicano con le periferiche attraverso un protocollo dedicato e con il sistema mediante flussi (stream) di dati; i programmi applicativi risultano così, in larga misura, indipendenti dal dispositivo fisico (stampante, disco, console) dal quale provengono (o al quale sono diretti) i dati.
[10] "Quasi" significa che comunque vi sono alcune differenze tra il comportamento dei due sistemi operativi in questione. Ad esempio, Unix consente di redirigere lo standard error, al contrario del DOS.
[11] Ogni versione di DOS affianca i nuovi servizi a quelli preesistenti, anche quando questi ultimi siano resi obsoleti dai primi: ne deriva una sostanziale compatibilità verso il basso.
[12] Inoltre è lecito ipotizzare che le nuove versioni del DOS abbiano, nel tempo, rimpiazzato quelle originariamente installate.
[13] Inoltre, qualora esse siano presenti in altre
versioni del DOS, nulla assicura che il contenuto dei registri
della CPU o la struttura dei dati gestiti rimangano invariati.
E' un vero peccato: di solito si rivelano utilissime.