Ultima modifica:
Qualsiasi programmatore sa bene che il C e il C++ sono linguaggi nativi unsafe. In questi linguaggi non c'è nessun controllo su quello che il programmatore ha scritto. Questo permette al programmatore di non essere limitato entro strette regole di programmazione. Però programmatori inesperti di questi linguaggi possono inciampare in "piccoli errori" che possono compromettere diversi aspetti del programma (e, a volte, anche del sistema operativo in cui è eseguito):
Per questi motivi, e per tanti altri, è importante che chiunque utilizzi il C o il C++ conosca bene le regole che spiegherò in questa guida. In tal modo potranno essere evitati gli errori più comuni dei programmatori novizi che a volte possono anche portare a gravi danni: piccoli programmini scritti da programmatori alle prime armi possono facilmente trasformarsi in pericolose falle di sicurezza utilizzabili dagli hacker.
1) void main()
Può compromettere: il corretto funzionamento di altri programmi; la stabilità del programma; la stabilità di altri programmi; la stabilità del sistema operativo.
Spesso si trovano programmi che utilizzano void main() per definire l'entry-point del proprio programma. In realtà main() è una funzione di tipo int, e deve essere definita così (lo chiede anche lo Standard ANSI C). Vediamo perché e cosa succede se invece definiamo main() come void.
Lasciamo un attimo stare il main (lo riprendiamo dopo) e prendiamo ad esempio questo codice (che ci servirà a capire):
Questo codice fa una cosa banale: cerca di calcolare la radice quadrata di 144. Però, se provate ad eseguire il codice, il risultato sarà senz'altro qualcosa di sbagliato:
Perché questo? perché la funzione sqrt ritorna normalmente un valore double. Noi ci stiamo comportando invece come se ritornasse un valore int: infatti abbiamo definito sqrt come int e vogliamo stampare su schermo il suo valore di ritorno utilizzando %d che si aspetta di trovare un valore int. Questo provoca un piccolo effetto collaterale: le macchine utilizzano un particolare registro per contenere i valori di ritorno delle funzioni intere (EAX) e un particolare registro per contenere i valori di ritorno delle funzioni che ritornano valori in virgola mobile. Quindi, possono succedere diverse cose: sqrt scrive il suo valore double nel registro per i valori in virgola mobile, però il nostro codice legge il valore contenuto nel registro EAX (quello dei valori int) e quindi legge un numero sbagliato; oppure, anche se non si verificasse questo problema (ad esempio, se sia i numeri interi che quelli in virgola mobile vengono passati utilizzando lo stesso registro), il nostro codice andrebbe ad interpretare un valore double come int, fornendo così un risultato sbagliato.
Inoltre ci sono altri effetti negativi: questi errori possono anche sbilanciare lo stack (se una viene inserito un valore di dimensione double nello stack e poi viene tolto un valore di dimensione int alcuni byte resteranno nello stack).
Con il main è la stessa cosa. Si può immaginare che colui che avvia il programma (un altro programma o il sistema operativo) utilizzi un codice tipo questo:
Questo codice si aspetta che il main ritorni un valore int, invece main è definito come void! Per questo motivo potrebbe rendere il sistema instabile sbilanciando lo stack e portandolo al crash.
Oppure con void la funzione main ritornerà un valore random. Di solito questo non è un problema ma potrebbe diventarlo: se ad esempio il valore di ritorno di main supera il valore di Sys$RCLimit (che è il valore massimo che può avere un valore di ritorno) il sistema operativo lancerà un errore; oppure se il valore di ritorno di main a un valore diverso da 0, un programma o uno script che fa uso di quel programma potrebbe interpretare quel valore di ritorno come un codice errore e quindi comportarsi in modo anomalo.
Quindi, in sostanza, void main() è da evitare:
2) system("pause")
system("pause") è largamente utilizzato per mettere in pausa il programma in attesa della pressione di un tasto da parte dell'utente. Funziona benissimo, ma ha un difetto: costringe a fare una chiamata alle funzioni interne del sistema operativo.
Quando system("pause") viene chiamato, in breve, succede questo:
Inoltre, per utilizzare system("pause"), deve essere inclusa la libreria stdlib.h che magari si poteva evitare.
Ancora: system("pause") può funzionare solo su sistemi Windows (e anche DOS) mentre non funziona su Linux.
Quindi, in sostanza, system("pause") è da evitare:
Come alternativa a system("pause") possiamo utilizzare qualsiasi funzione che prende informazioni da stdin.
Ad esempio c'è getch(). Questa funzione è però definita in conio.h e per questo motivo è presente solo in alcuni compilatori (Borland, MSVC++, ...). Perciò, di solito, si preferisce evitare anche getch().
Di gran lunga preferibile è invece getchar(). Questa funzione si trova in tutte le implementazioni di ANSI C. Una volta che il programma è compilato, si tratta semplicemente di un salto a una funzione in quanto getchar() viene compilata all'interno del programma (a differenza di system("pause") che è sempre esterna).
In C++ invece è preferibile utilizzare cin.get(). Questa funzione è l'equivalente C++ di getchar().
L'alternativa finale, se si vuole mettere in pausa il programma solo perché non si chiuda alla fine dell'esecuzione, è quella di avviare il programma da console invece che facendo doppio click sull'eseguibile (di fatto, le applicazioni console, sono state ideate proprio affinché vengano avviate da console).
3) ................................
99) Fonti
Steve Summit, Articolo del 23 Agosto 1996, http://www.eskimo.com/~scs/readings/voidmain.960823.html
TOPIC IN AGGIORNAMENTO!!! .....
- Il suo corretto funzionamento o quello di un altro programma;
- Le sue prestazioni e il corretto utilizzo delle risorse;
- La sua stabilità, quella di un altro programma o del sistema operativo;
- La sicurezza del sistema operativo.
Per questi motivi, e per tanti altri, è importante che chiunque utilizzi il C o il C++ conosca bene le regole che spiegherò in questa guida. In tal modo potranno essere evitati gli errori più comuni dei programmatori novizi che a volte possono anche portare a gravi danni: piccoli programmini scritti da programmatori alle prime armi possono facilmente trasformarsi in pericolose falle di sicurezza utilizzabili dagli hacker.
1) void main()
Può compromettere: il corretto funzionamento di altri programmi; la stabilità del programma; la stabilità di altri programmi; la stabilità del sistema operativo.
Spesso si trovano programmi che utilizzano void main() per definire l'entry-point del proprio programma. In realtà main() è una funzione di tipo int, e deve essere definita così (lo chiede anche lo Standard ANSI C). Vediamo perché e cosa succede se invece definiamo main() come void.
Lasciamo un attimo stare il main (lo riprendiamo dopo) e prendiamo ad esempio questo codice (che ci servirà a capire):
Questo codice fa una cosa banale: cerca di calcolare la radice quadrata di 144. Però, se provate ad eseguire il codice, il risultato sarà senz'altro qualcosa di sbagliato:
Perché questo? perché la funzione sqrt ritorna normalmente un valore double. Noi ci stiamo comportando invece come se ritornasse un valore int: infatti abbiamo definito sqrt come int e vogliamo stampare su schermo il suo valore di ritorno utilizzando %d che si aspetta di trovare un valore int. Questo provoca un piccolo effetto collaterale: le macchine utilizzano un particolare registro per contenere i valori di ritorno delle funzioni intere (EAX) e un particolare registro per contenere i valori di ritorno delle funzioni che ritornano valori in virgola mobile. Quindi, possono succedere diverse cose: sqrt scrive il suo valore double nel registro per i valori in virgola mobile, però il nostro codice legge il valore contenuto nel registro EAX (quello dei valori int) e quindi legge un numero sbagliato; oppure, anche se non si verificasse questo problema (ad esempio, se sia i numeri interi che quelli in virgola mobile vengono passati utilizzando lo stesso registro), il nostro codice andrebbe ad interpretare un valore double come int, fornendo così un risultato sbagliato.
Inoltre ci sono altri effetti negativi: questi errori possono anche sbilanciare lo stack (se una viene inserito un valore di dimensione double nello stack e poi viene tolto un valore di dimensione int alcuni byte resteranno nello stack).
Con il main è la stessa cosa. Si può immaginare che colui che avvia il programma (un altro programma o il sistema operativo) utilizzi un codice tipo questo:
Questo codice si aspetta che il main ritorni un valore int, invece main è definito come void! Per questo motivo potrebbe rendere il sistema instabile sbilanciando lo stack e portandolo al crash.
Oppure con void la funzione main ritornerà un valore random. Di solito questo non è un problema ma potrebbe diventarlo: se ad esempio il valore di ritorno di main supera il valore di Sys$RCLimit (che è il valore massimo che può avere un valore di ritorno) il sistema operativo lancerà un errore; oppure se il valore di ritorno di main a un valore diverso da 0, un programma o uno script che fa uso di quel programma potrebbe interpretare quel valore di ritorno come un codice errore e quindi comportarsi in modo anomalo.
Quindi, in sostanza, void main() è da evitare:
- Perché lo Standard ANSI C dice che non va usato;
- Perché può sbilanciare lo stack minando la stabilità del sistema operativo o di altri programmi;
- Perché può compromettere il corretto funzionamento di altri programmi ritornando valori random che possono assumere significati non previsti.
int main()
oppure:int main(void)
oppure:int main(int argc, char **argv)
2) system("pause")
system("pause") è largamente utilizzato per mettere in pausa il programma in attesa della pressione di un tasto da parte dell'utente. Funziona benissimo, ma ha un difetto: costringe a fare una chiamata alle funzioni interne del sistema operativo.
Quando system("pause") viene chiamato, in breve, succede questo:
- Il processo del programma viene sospeso
- Viene chiamato il sistema operativo
- Il sistema operativo deve cercare il comando pause
- Il sistema operativo deve allocare la memoria necessaria ad utilizzare il comando
- Il comando viene eseguito (e il programma si mette in attesa della pressione di un tasto)
- La memoria viene deallocata
- Il sistema operativo restituisce il controllo al processo del programma
- Il processo del programma riparte
Inoltre, per utilizzare system("pause"), deve essere inclusa la libreria stdlib.h che magari si poteva evitare.
Ancora: system("pause") può funzionare solo su sistemi Windows (e anche DOS) mentre non funziona su Linux.
Quindi, in sostanza, system("pause") è da evitare:
- Perché aggiunge lavoro inutile al programma rendendolo più pesante in esecuzione;
- Perché obbliga a includere la libreria stdlib.h che spesso si può evitare;
- Perché funziona solo su sistemi Microsoft Corporation.
Come alternativa a system("pause") possiamo utilizzare qualsiasi funzione che prende informazioni da stdin.
Ad esempio c'è getch(). Questa funzione è però definita in conio.h e per questo motivo è presente solo in alcuni compilatori (Borland, MSVC++, ...). Perciò, di solito, si preferisce evitare anche getch().
Di gran lunga preferibile è invece getchar(). Questa funzione si trova in tutte le implementazioni di ANSI C. Una volta che il programma è compilato, si tratta semplicemente di un salto a una funzione in quanto getchar() viene compilata all'interno del programma (a differenza di system("pause") che è sempre esterna).
In C++ invece è preferibile utilizzare cin.get(). Questa funzione è l'equivalente C++ di getchar().
L'alternativa finale, se si vuole mettere in pausa il programma solo perché non si chiuda alla fine dell'esecuzione, è quella di avviare il programma da console invece che facendo doppio click sull'eseguibile (di fatto, le applicazioni console, sono state ideate proprio affinché vengano avviate da console).
3) ................................
99) Fonti
Steve Summit, Articolo del 23 Agosto 1996, http://www.eskimo.com/~scs/readings/voidmain.960823.html
TOPIC IN AGGIORNAMENTO!!! .....
Se avete altre cose da aggiungere a questa lista, potete scriverle in topic. Sono già in programma:
-scanf()
-gets()
-feof()
-le varie funzioni su stringa con i possibili buffer overflow
-malloc() senza free()
Se avete in mente qualcosa che non compare in questa lista, fatemelo sapere e lo aggiungerò in futuro.
-scanf()
-gets()
-feof()
-le varie funzioni su stringa con i possibili buffer overflow
-malloc() senza free()
Se avete in mente qualcosa che non compare in questa lista, fatemelo sapere e lo aggiungerò in futuro.