Nulla potrebbe essere più utile ad un programmatore della conoscenza dell'intero processo di acquisizione di un codice tasto!

Il buffering è solo l'ultima banalità della catena. Se l'input è "buffered", come nel caso generico della getchar(), il tasto o i tasti premuti vengono messi a disposizione del programma solo dopo la pressione di ENTER (invio). Questo implica che la funzione in oggetto:
1) Sospende il programma, in linea di principio, fino alla pressione di ENTER;
2) Tutto ciò che viene digitato prima di ENTER viene salvato internamente in un buffer, ovvero un array gestito dalla libreria e/o dal SO.
3) Quando la funzione termina, essa restituisce il primo valore prelevato dal buffer.
L'aspetto più importante è che è obbligatorio premere ENTER per terminare l'input.

Altro aspetto importante, la pulizia del buffer. Lo standard, fin dal C'89, è estremamente chiaro: si tratta di una operazione opzionale. I compilatori non hanno l'obbligo di implementare la fflush() sugli stream di input.
Per la pulizia del buffer occorre quindi un idioma specifico, come il seguente.

codice:
void kbd_clean(void)
{
    register char ch;
    do 
    {
        ch = getchar();
    } while (('\n' != ch) && (EOF != ch));
    /* 
    ** Alternativamente, piu' in sintesi: 
    ** while ((ch = getchar() != '\n') && (ch != EOF));
    */
}
Le funzioni unbuffered, come getch(), non hanno questo genere di problemi e restituiscono direttamente il primo tasto premuto.
Non accedono ad alcun buffer interno e non richiedono la pressione di tasti specifici. Nulla di particolarmente esotico.