Le variabili automatiche locali (teoricamente dichiarate con la parola chiave auto, ma nessuno la usa visto che basta fare una definizione "normale" all'interno della funzione) nascono e muoiono all'interno del blocco in cui sono dichiarate. Nel momento in cui si esce dal loro blocco, esse non esistono più.
Le variabili globali si definiscono senza keyword particolari fuori da ogni funzione. Esse esistono per tutta la durata del programma.
static ed extern sono dei modificatori, modificano cioè le definizioni/dichiarazioni di variabili.
Se static è applicato ad una variabile locale, la variabile diventa "statica", ossia di fatto diventa una globale con scope limitato alla funzione. Ciò significa che essa è visibile solamente all'interno della funzione, ma che il suo valore persiste tra una chiamata e l'altra alla funzione; non viene cioè distrutta e ricreata ogni volta che si esce e si rientra dal suo scope. In generale l'uso di variabili locali static non è consigliato, dato che con essa risulta facile creare funzioni non rientranti.

static applicato a variabili globali ed extern hanno a che fare con il linkage della variabile in questione.
In C di rado si scrivono programmi che stanno interamente in un singolo file sorgente, ed è spesso necessario scambiare informazioni tra i vari file sorgenti; spesso e volentieri, quindi, è necessario condividere variabili globali a cavallo di più sorgenti.
Per fare sì che questo sia possibile, le definizioni di variabili globali effettuate senza il modificatore static fanno sì che il compilatore inserisca una "nota" al linker dentro al file oggetto generato, che dice "guarda che qui sono definite una serie di variabili globali che si chiamano così e cosà". Negli altri sorgenti che vogliono utilizzarle, invece, si effettuano delle dichiarazioni extern relative alle variabili da "importare" dagli altri header (non si tratta di definizioni, dato che il compilatore non alloca spazio per esse nel modulo oggetto corrente). La dichiarazione in questione consente al compilatore di fare tutti i controlli sintattici del caso, e di lasciare, in tutti i punti in cui la variabile è usata, una nota al linker del tipo "guarda che qui viene usata una variabile extern che si chiama in questa maniera".
Il linker, quando prende tutti i moduli oggetto e li collega per creare l'eseguibile finale, guarda le "note" generate dal compilatore riguardo alle variabili disponibili nei vari moduli oggetto e le usa per ricollegare le variabili extern alle variabili "vere" definite negli altri moduli oggetto.

static applicato alle variabili globali, invece, fa sì che la variabile globale a cui è applicato non venga "esportata" dal modulo oggetto, risultando così invisibile dall'esterno. Questo se non erro dovrebbe consentire al compilatore qualche ottimizzazione in più e dovrebbe rendere i moduli oggetto più piccoli, ma soprattutto consente di definire in diversi moduli oggetto delle variabili globali con lo stesso nome senza che questo dia adito a problemi. Questo può essere utile se i vari file sono scritti da persone diverse e le variabili in questione sono dei dettagli implementativi, nascosti dall'interfaccia "pubblica" della libreria.