Il C ha pochissima sintassi da imparare: circa 32 parole chiave e una manciata di regole di punteggiatura. Quasi ogni programma che scriverai ha la stessa forma esterna, quindi vale la pena smontarla una volta, con attenzione.
Il programma completo più piccolo
Questo compila, viene eseguito e non fa nulla:
Nessun include, nessun output, nessuna classe o modulo di contorno. main è il punto di ingresso: quando il sistema operativo avvia il tuo programma, chiama main. Quando main restituisce un valore, il programma termina e quel valore è lo stato di uscita.
Aggiungi un output e ti serve un include:
Direttive del preprocessore
Le righe che iniziano con # vengono gestite prima della compilazione vera e propria, dal preprocessore. Non sono istruzioni C e non vogliono il punto e virgola.
#include <stdio.h> /* header della libreria standard: parentesi angolari */
#include "myutils.h" /* il tuo header: virgolette, cercato prima in locale */
#define MAX 100 /* una macro: MAX viene sostituito da 100 ovunque più sotto */
#include è un copia e incolla letterale di testo: l'intero contenuto del file indicato viene inserito in quel punto. stdio.h contiene la dichiarazione di printf, ed è così che il compilatore sa quali argomenti riceve printf. Se ometti l'include ottieni implicit declaration of function 'printf'.
La distinzione tra parentesi angolari e virgolette riguarda dove cerca il compilatore: <...> indica le directory di include di sistema, "..." significa cerca prima accanto a questo file.
La funzione main
int main(void) {
return 0;
}
Quattro parti, ognuna con un significato:
int: il tipo di ritorno di main. Il valore arriva al sistema operativo come stato di uscita, dove 0 significa successo.main: il nome riservato che il runtime chiama. Ogni programma C in ambiente hosted ne ha esattamente uno.(void): la lista dei parametri. Quivoidsignifica "non riceve argomenti". Scriveremain()è valido ma più debole: in C le parentesi vuote significano "parametri non specificati", il che disattiva il controllo degli argomenti.{ ... }: il corpo della funzione.
L'altra forma standard riceve gli argomenti da riga di comando:
int main(int argc, char *argv[]) { ... }
Usa int main(void) finché non ti servono.
Istruzioni e punto e virgola
Un'istruzione è una singola operazione e termina con un punto e virgola.
Quattro istruzioni, quattro punti e virgola. Il punto e virgola è un terminatore, non un separatore: anche l'ultima istruzione di un blocco ne ha bisogno.
Cosa non vuole il punto e virgola:
#include <stdio.h> /* riga del preprocessore: niente punto e virgola */
int add(int a, int b) { /* intestazione di una definizione di funzione: niente punto e virgola */
return a + b;
} /* graffa di chiusura di una funzione: niente punto e virgola */
if (x > 0) {
...
} /* graffa di chiusura di un blocco di controllo: niente punto e virgola */
L'unico caso che frega tutti è un punto e virgola al posto del corpo:
if (score > 90); /* BUG: questo ; è l'intero corpo dell'if */
printf("Eccellente!\n"); /* viene sempre eseguito, qualunque sia score */
È C perfettamente valido. L'if controlla un'istruzione vuota e la printf è semplicemente la riga successiva. -Wall ti avvisa; il compilatore non ti fermerà.
Blocchi e parentesi graffe
Le parentesi graffe raggruppano le istruzioni in un blocco, che conta come una sola istruzione ovunque ne sia prevista una.
Le graffe sono facoltative quando il corpo è una sola istruzione, ma ometterle è il modo in cui nascono i bug famosi: aggiungi una seconda riga in seguito e questa finisce fuori dall'if senza avvisi. Usa sempre le graffe.
I blocchi creano anche uno scope: una variabile dichiarata dentro { ... } smette di esistere alla graffa di chiusura.
Maiuscole, minuscole e spazi
Il C distingue maiuscole e minuscole ovunque. total, Total e TOTAL sono tre nomi separati. Tutte le parole chiave sono in minuscolo, quindi Int, Return e Printf sono tutti errori, e il messaggio di errore per Printf confonde, perché il compilatore pensa che tu intenda una funzione che non ha mai visto.
Gli spazi, invece, sono quasi del tutto liberi. Il compilatore tratta qualsiasi sequenza di spazi, tabulazioni e a capo come un unico separatore. Questo è valido:
int main ( void )
{
printf
( "Ciao\n" ) ;
return 0 ;
}
Anche questo:
int main(void){printf("Ciao\n");return 0;}
Entrambi compilano allo stesso modo. Visto che al compilatore non importa, la formattazione è interamente per le persone, e conviene seguire il layout convenzionale (un'istruzione per riga, quattro spazi o una tabulazione per livello di annidamento, graffa di apertura sulla stessa riga) perché ogni codebase C che leggerai userà qualcosa di simile.
Due punti in cui gli spazi contano davvero: non puoi spezzare una parola chiave o un identificatore (in t non è int) e non puoi mettere un a capo dentro una normale stringa letterale.
Identificatori: regole per i nomi
Un identificatore è qualsiasi nome che scegli tu: variabili, funzioni, tipi. Le regole:
- È fatto di lettere, cifre e underscore.
- Non deve iniziare con una cifra.
total2va bene,2totalno. - Non può essere una parola chiave: nessuna variabile chiamata
int,fororeturn. - Maiuscole e minuscole contano.
- I nomi che iniziano con un underscore, e qualsiasi nome con due underscore di fila, sono riservati all'implementazione: non inventarli.
int score; /* va bene */
int total_items; /* va bene: lo snake_case è la convenzione del C */
int _hidden; /* valido ma riservato: evitalo */
int 2fast; /* errore */
int float; /* errore: parola chiave */
Un programma completamente commentato
Tutto quanto visto sopra, in un unico posto:
L'ordine di lettura qui conta: with_tax è definita prima che main la usi. Il C legge il file dall'alto verso il basso, quindi una funzione deve essere dichiarata prima di essere chiamata. Definiscila prima, oppure scrivi un prototipo di funzione in cima e definiscila più avanti.
Domande frequenti
Qual è la struttura di base di un programma C?
Prima le direttive del preprocessore (#include <stdio.h>), poi le definizioni delle funzioni, una delle quali deve essere main. Dentro una funzione le istruzioni terminano con il punto e virgola e i blocchi sono racchiusi tra parentesi graffe. Un programma minimo è #include <stdio.h> più int main(void) { return 0; }.
Perché si scrive int main(void) e non solo main()?
int è il tipo di ritorno di main: lo stato di uscita che il programma restituisce al sistema operativo. (void) dice esplicitamente che main non riceve argomenti. Scrivere main() con le parentesi vuote è uno stile più vecchio che non dice nulla al compilatore sui parametri, quindi i controlli sono minori.
Tutte le istruzioni C hanno bisogno del punto e virgola?
Ogni istruzione sì, ma non ogni riga. Le direttive del preprocessore (#include, #define), le definizioni di funzione e la graffa di chiusura di un blocco o di if/for/while non lo vogliono. Un punto e virgola di troppo dopo if (x) è C valido e fa eseguire il corpo sempre, senza avvisare.
C distingue tra maiuscole e minuscole?
Sì, completamente. count, Count e COUNT sono tre identificatori diversi, e Int non è la parola chiave int. Tutte le parole chiave del C sono in minuscolo, quindi una maiuscola in Return o Printf produce un errore.