FAQ CConsultez toutes les FAQ

Nombre d'auteurs : 27, nombre de questions : 175, dernière mise à jour : 17 décembre 2010  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez devenir rédacteur, lisez ceci .

Sur ce, je vous souhaite une bonne lecture.


SommaireLes entrées/sortiesGestion du clavier et de l'écran en mode console (19)
précédent sommaire suivant
 

Les caractères tapés ne sont pas directement transmis au programme mais placés (par le système) dans un tampon. Le système ne transmettra le contenu de ce tampon que lorsque l'utilisateur aura émis le caractère de validation à savoir '\n'. En langage C, avec un clavier plus ou moins conventionnel, ce caractère est provoqué par la touche Entrée.

Mis à jour le 27 juillet 2008 Melem

En langage C, ce caractère, appelé également Line Feed (en abrégé LF), indique la fin d'une ligne. Aussi bien en lecture (scanf…) qu'en écriture (printf…), le système requiert une ligne complète avant de réellement considérer les données, sinon elles seront tout simplement placées dans un tampon. Si on tente de l'afficher, il provoque le passage à la ligne suivante.

Mis à jour le 27 juillet 2008 Melem

Ce caractère, appelé également Carriage Return (en abrégé CR), si l'on tente de l'afficher, provoque le retour en début de la ligne courante.

Mis à jour le 27 juillet 2008 Melem

La touche Entrée (Retour Chariot) envoie le caractère '\r'. Cependant, cette touche sert en réalité à terminer la ligne plutôt qu'à retourner au début de celle-ci. Or, selon le système, il est possible qu'une ligne doit être terminée non pas par un '\r' mais par un '\n' (à l'exemple d'UNIX) ou un '\r' suivi d'un '\n' (DOS/Windows) par exemple. Ainsi, avant que ce caractère n'atteigne le programme, il aura déjà été converti en « caractère » de fin de ligne (qui peut être '\n', '\r' suivi de '\n', etc.). Cette conversion se faisant de manière totalement opaque pour le programme, on a bien l'impression, dans le programme, que la touche Entrée provoque l'émission du caractère de fin de ligne. Par contre, si le programme effectue une lecture directe au clavier (à l'aide de la fonction _getch() de DOS/Windows par exemple) plutôt qu'une lecture de haut niveau (getchar()), on obtient bien le caractère '\r' et non le caractère de fin de ligne quand l'utilisateur appuie sur Entrée.

D'autre part, en langage C, les entrées/sorties se font par défaut en mode texte. Dans ce mode, le caractère de fin de ligne est toujours représenté par le caractère '\n'. Plus précisément, quand une fonction de lecture rencontre une fin de ligne, elle la convertit en '\n' avant de la retourner au programme. Quand le programme demande l'écriture de '\n', il sera tout d'abord converti en caractère de fin de ligne avant d'être effectivement écrit. En mode binaire, ces conversions n'ont pas lieu.

À noter que pour l'écran, bien qu'il faut normalement écrire '\r' (Retour Chariot) suivi de '\n' (Nouvelle ligne) pour commencer une nouvelle ligne, il est possible, toujours en fonction du système utilisé, que '\n' seulement suffise pour y arriver. Mais dans tout le cas, l'affichage de '\r' seulement provoque le retour au début de la ligne courante.

Mis à jour le 27 juillet 2008 Melem

Cette fonction sert à forcer l'écriture physique des données se trouvant dans le tampon associé à un flux sortant. Par exemple :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h> 
  
int main(void) 
{ 
    int n; 
  
    printf("Entrez un nombre entier : "); 
  
    /* En langage C, une ligne doit être terminée par le caractère '\n'. Tant que       */ 
    /* la ligne n'est pas terminée et que le tampon associé au fichier n'est pas plein, */ 
    /* les caractères transmis ne seront pas effectivement écrits mais tout simplement  */ 
    /* placés dans le tampon. On peut cependant forcer le vidage de ce tampon à l'aide  */ 
    /* de la fonction fflush.                                                           */ 
  
    fflush(stdout); 
  
    scanf("%d", &n); 
  
    printf("Merci pour : %d\n", n); 
  
    return  0; 
}
Cependant, la norme ajoute que le tampon associé à un flux sortant doit être également vidé lorsqu'une opération de lecture nécessite l'émission de ces caractères. Dans de nombreuses implémentations, une demande de lecture au clavier provoque le vidage du tampon associé à l'écran. Le fflush(stdout); juste après notre printf serait donc automatiquement appelé au moment du scanf et dans ce cas, on peut tout simplement l'omettre.

Mis à jour le 27 juillet 2008 Melem

En utilisant fgets().

Code c : Sélectionner tout
fgets(char * s, int n, FILE * flux);
fgets stoppe la lecture si la ligne a été lue dans son intégralité (c'est-à-dire si le caractère '\n' a été lu) ou si elle a lu n - 1 caractères (le n-ième étant réservé pour le caractère de fin de chaîne, '\0'). Lorsque fgets() lit une ligne complète, le caractère '\n' est présent dans le tampon, il faut donc prévoir sa suppression.

Voici une fonction effectuant la lecture d'une ligne en limitant la taille et en supprimant le caractère '\n' s'il est présent :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h> 
#include <string.h> 
  
char * read_stdin(char * buffer, size_t taille) 
{ 
    char * result = fgets(buffer, taille, stdin); 
  
    if (result != NULL) 
    { 
        char * lf = strchr(buffer, '\n'); /* On cherche le caractère '\n'. */ 
  
        if (lf != NULL) /* S'il est présent, ... */ 
            *lf = '\0'; /* ... on le supprime    */ 
        else 
        { 
            /* 
            * Le '\n' n'est pas présent. Ça signifie qu'il reste au moins un 
            * caractère dans stdin. On peut choisir de les ignorer et de vider 
            * stdin ou d'agrandir le buffer si c'est possible (realloc()) et de 
            * rappeler fgets() autant de fois que nécessaire... 
            * 
            * Si on ne fait rien, la prochaine lecture sur stdin se fera sans attente 
            * et récupèrera ce qui n'a pas été lu ... 
            */ 
        } 
    } 
  
    return result; 
}
N.B. : le caractère '\n' n'est pas présent dans deux cas :

  • La saisie est plus longue que taille demandée, dans ce cas les caractères supplémentaires restent présents.
  • La fin de fichier est rencontrée.

Mis à jour le 29 novembre 2004 Anomaly Emmanuel Delahaye gl

Comment vider le buffer clavier ?
Pourquoi gets est-elle dépréciée en faveur de fgets ?

La méthode la plus sûre pour vider le buffer clavier consiste à consommer tout les caractères présents dans ce buffer jusqu'à ce qu'il soit vide :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
#include <stdio.h> 
  
void clean_stdin(void) 
{ 
    int c; 
  
    do { 
        c = getchar(); 
    } while (c != '\n' && c != EOF); 
}

Mis à jour le 2 mars 2003 Emmanuel Delahaye gl haypo LFE

Cela dépend du système. Par exemple, il suffit de taper, en début de ligne, Ctrl + Z sous DOS/Windows et Ctrl + D sous UNIX, puis de valider par Entrée.

Mis à jour le 27 juillet 2008 Melem

Dans certaines implémentations, lorsque stdin est attaché au clavier, fflush(stdin) supprime tous les caractères encore présents dans le buffer du clavier. Appliquer fflush sur un flux entrant, comme stdin, n'est cependant pas portable car la norme ne précise l'effet de fflush que sur un flux sortant.

Mis à jour le 11 septembre 2006 gl Melem

À quoi sert la fonction fflush ?
Comment vider le buffer clavier ?

Un terminal a deux modes de fonctionnement. Dans le mode normal (cooked), on saisit la ligne tranquillement, avec possibilité de l'éditer avec Backspace, avant de l'envoyer en enfonçant Entrée. Le programme n'a pas du tout connaissance de la ligne avant que l'utilisateur enfonce Entrée.

Dans le mode brut (raw), aucune interprétation n'est faite. Les effets sont notamment les suivants :

  • getchar() lit réellement un caractère sans attente la frappe d'Entrée.
  • fgets() lit exactement le nombre de caractères demandés.
  • Il n'y a pas d'écho local des caractères tapés.
  • Si on tape Backspace, le programme reçoit '\b' (code 8) ou DEL (code 127), selon le système.
  • Ctrl-C (interruption), Ctrl-D (EOF), Ctrl-Z (suspension) et les autres combinaisons de contrôle ne sont pas interprétées, mais le code ASCII correspondant est retourné.
  • Si on tape Entrée, le programme reçoit '\r' (au lieu de '\n' en mode cooked).
  • Si on tente d'afficher '\n', le programme saute une ligne sans retourner au début de la ligne (il faut donc utiliser '\r' suivi de '\n' pour revenir en début de ligne puis aller à la ligne suivante).

Mis à jour le 29 novembre 2004 Anomaly

Voici une fonction qui permet de passer d'un mode à l'autre. On appelle mode_raw(1) pour activer le mode raw et mode_raw(0) pour revenir en mode cooked. Pensez à toujours revenir en mode normal à la fin du programme.

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <termios.h> 
#include <unistd.h> 
  
void mode_raw(int activer) 
{ 
    static struct termios cooked; 
    static int raw_actif = 0; 
  
    if (raw_actif == activer) 
        return; 
  
    if (activer) 
    { 
        struct termios raw; 
  
        tcgetattr(STDIN_FILENO, &cooked); 
  
        raw = cooked; 
        cfmakeraw(&raw); 
        tcsetattr(STDIN_FILENO, TCSANOW, &raw); 
    } 
    else 
        tcsetattr(STDIN_FILENO, TCSANOW, &cooked); 
  
    raw_actif = activer; 
}

Mis à jour le 29 novembre 2004 Anomaly

Sous DOS/Windows, avec la fonction _getch(), déclarée dans conio.h. Cette fonction n'émet pas d'écho. Si vous voulez que le caractère tapé soit également affiché, utilisez la fonction getche(). getch() et getche() retournent toutes le code du caractère tapé. Avec un compilateur C/C++ moderne, préférez les noms _getch et _getche aux anciens noms getch et getche.

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h> 
#include <conio.h> 
  
int main(void) 
{ 
    printf("Hello, world.\n"); 
  
    printf("Appuyez sur une touche pour continuer ..."); 
    fflush(stdout); 
    _getch(); 
  
    return 0; 
}
Sous UNIX, il faut passer le terminal en mode brut (raw).

Mis à jour le 29 novembre 2004 Anomaly Melem

Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?
Comment faire passer un terminal en mode brut (UNIX) ?

Il arrive des fois qu'on ait besoin de savoir si une touche est présente, mais sans bloquer le déroulement du programme. La fonction kbhit() permet de savoir si une touche est disponible ou non dans le buffer du clavier. Si une touche est présente, alors une lecture par la fonction getch() suffit. Avec un compilateur C/C++ moderne, préférez les noms _kbhit et _getch aux anciens noms kbhit et getch.

Voici une source intégrant la lecture de touches simple et double code.

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h> 
#include <conio.h> 
  
int main(void) 
{ 
    int touche; 
  
    while (!_kbhit()) /* On boucle jusqu'à ce que l'utilisateur appuie sur une touche */ 
    { 
        printf("Appuyez sur n'importe quelle touche !\n"); 
    } 
  
    /* Une touche a enfin été frappée. Lire son code. */ 
  
    touche = _getch(); 
  
    if (touche >= 32) /* code d'un caractère imprimable */     
        printf("Vous avez appuye sur '%c'.\n", touche); 
    else 
    { 
        /* C'est un caractere special. Ça peut être un caractère de contrôle        */ 
        /* (ECHAP, ENTREE, etc.) ou encore une touche étendue (F1 .. F12, Flèches). */ 
  
        printf("Vous avez appuye sur une touche.\n"); 
    } 
  
    return 0; 
}

Mis à jour le 20 septembre 2004 Elijha gl

Comment faire pour lire un caractère sans attendre la frappe d'Entrée ?
Comment gérer les touches étendues (F1..F12, flèches) ?
Que signifie l'underscore (_) en début du nom d'une fonction ou d'une macro, etc. ?

La fonction _kbhit() permet, sous DOS et Windows, de savoir si une touche est disponible ou non dans le buffer du clavier. La fonction ci dessous fournit le même service sous un environnement de type UNIX.

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <unistd.h> 
#include <sys/time.h> 
  
int unix_text_kbhit(void) 
{ 
    struct timeval tv = { 0, 0 }; 
    fd_set readfds; 
  
    FD_ZERO(&readfds); 
    FD_SET(STDIN_FILENO, &readfds); 
  
    return select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv) == 1; 
}
Les caractères détectés peuvent être lus ensuite par fgets() ou getchar().
Attention, cette fonction ne détectera des caractères tapés que s'ils ont été suivis par Entrée. Pour détecter l'arrivée de caractères dès leur frappe, comme avec _kbhit, il faut que le terminal fonctionne en mode brut (raw).

Mis à jour le 29 novembre 2004 Anomaly

Comment faire passer un terminal en mode brut (UNIX) ?

Les touches F1 à F12 et les touches fléchées sont des touches qui, lorsque l'on appuie dessus, renvoient deux codes l'un à la suite de l'autre. Ce sont ce que l'on appelle des touches étendues.

Pour gérer ce type de touches, il faut lire un premier caractère du buffer clavier, détecter qu'il s'agit d'un code de touche étendue, et relire un second caractère pour identifier la touche. Ce premier « caractère » lu dépend du système et peut également varier selon les touches étendues (généralement il a la valeur 0 ou 224). A noter que les codes étendus, c'est-à-dire celui qui vient en seconde position, est différent suivant les plateformes.

Voici un extrait de code qui fonctionne sous DOS/Windows, la fonction getch n'étant définie que par ces systèmes.

Code C : Sélectionner tout
1
2
3
4
5
6
7
int c = getch(); 
  
if (c == 0 || c == 224) /* Si c'est une touche étendue */ 
{ 
    c = getch();    
    /* c contient maintenant le code de la touche étendue */ 
}

Mis à jour le 12 avril 2003 LFE

Comment faire pour lire un caractère sans attendre la frappe d'Entrée ?

Si la console accepte les séquences d'échappement ANSI (il s'agit d'une spécification de combinaisons de caractères qu'un terminal doit interpréter comme une commande et non comme de simples caractères à imprimer…), il est possible de les utiliser pour positionner le curseur au point de coordonnées (x, y) dans cette console.

Code C : Sélectionner tout
1
2
3
4
5
6
#include <stdio.h> 
  
void goto_x_y(unisgned y, unsigned x) 
{ 
    printf("\033[%u;%uH", y, x); 
}
Cela fonctionne sous DOS et de nombreux environnements de type UNIX mais malheureusement pas sous Windows.

Sous Windows, la solution la plus naturelle consiste à appeler la fonction SetConsoleCursorPosition() en lui fournissant un handle sur la console et les coordonnées de la position souhaitée.

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <windows.h> 
  
void GotoXY(SHORT x, SHORT y) 
{ 
    /* STD_OUTPUT_HANDLE fait référence à la sortie standard du programme qui est par défaut la console */ 
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 
    COORD  Pos; 
  
    Pos.X = x; 
    Pos.Y = y; 
  
    SetConsoleCursorPosition(hConsole, Pos); 
}

Mis à jour le 20 septembre 2004 gl haypo

Normalement, cela peut simplement se faire en appelant une commande système comme cls sous DOS/Windows et clear sous UNIX. Cette technique n'est cependant pas la plus efficace à cause des limitations connues de la fonction system. Il n'existe pas non plus hélas de fonction standard qui permette de le faire. Voici donc quelques fonctions que vous pouvez utiliser pour arriver à vos fins.

MS-DOS :

Code c : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
#include <dos.h> 
  
void dos_clear_screen(void) 
{ 
    /* Il suffit de réinitialiser le mode vidéo (appeler la fonction 00h de l'interruption 10h) */ 
    union REGS inregs, outregs; 
  
    inregs.h.ah = 0x00;   /* Fonction 00h : Change de mode vidéo */ 
    inregs.h.al = 0x03;   /* Mode = 03h : 80x25 caractères, 16 couleurs */ 
  
    int86(0x10, &inregs, &outregs); /* INT 10h */ 
}

Windows :

Code c : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <windows.h> 
  
void windows_clear_screen(void) 
{ 
    HANDLE hConsole; 
    CONSOLE_CONS_BUFFER_INFO Info; 
    DWORD NbOctetsEcrits; /* Requis par FillConsoleOutputCharacter */ 
    COORD Debut = {0, 0}; 
  
    /* STD_OUTPUT_HANDLE fait référence a la sortie standard du programme qui est par défaut la console */ 
    hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 
  
    /* Lit les infos sur le buffer de l'écran */ 
    GetConsoleScreenBufferInfo(hConsole, &Info); 
  
    /* Remplit l'écran avec le caractère espace */ 
    FillConsoleOutputCharacter(hConsole, ' ', Info.dwSize.X*Info.dwSize.Y, Debut, &NbOctetsEcrits); 
  
    /* Remet le curseur au début de l'écran */ 
    SetConsoleCursorPosition(hConsole, Debut); 
}

Linux :

Code C : Sélectionner tout
1
2
3
4
5
6
7
#include <ncurses.h> 
  
void unix_clear_screen(void) 
{ 
    clear(); 
    move(0, 0); 
}

Mis à jour le 12 avril 2003 haypo

Que fait la fonction system ?

Le matériel étant une notion complètement inconnue du langage C, il faut normalement toujours faire appel aux fonctions du système pour y avoir accès. CONIO (conio.h) sous DOS et Windows et ncurses sous UNIX mettent à disposition du programmeur des routines permettant de gérer le clavier et l'écran en mode console à un niveau inférieur à celui que permet le C standard mais comme nous venons de le dire, aucune des deux bibliothèques n'est portable (il peut cependant exister des portages…).

PDCurses, un sous-ensemble de ncurses, est une bibliothèque portable de gestion du clavier et de l'écran en mode console.

Mis à jour le 12 avril 2003 LFE Melem

La création d'interfaces graphiques est un domaine non couvert par le C. Pour développer des applications graphiques (c'est-à-dire des fenêtres, des boutons, etc.), vous pouvez soit utiliser directement les fonctions de votre système (c'est-à-dire l'API Windows sous Windows, le protocole X sous UNIX, etc.), soit faire appel à une bibliothèque éventuellement portable comme GTK+ .

Si vous voulez par contre faire du graphisme (jeux 2D ou 3D…), vous devriez plutôt vous tournez vers des bibliothèques spécialisées comme allegro , SDL ou encore OpenGL à moins que vous ayez envie de réinventer la roue en utilisant les fonctions de très très bas niveau du système.

Mis à jour le 12 avril 2003 LFE Melem

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2016 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Contacter le responsable de la rubrique C