# Introduction
Les chaînes de caractères sont des séquences de caractères utilisées pour représenter du texte. Bien que cela puisse surprendre, les chaînes de caractères n'ont pas de type dédié en C !
## 1.1 Qu'est-ce qu'une chaîne en C ?
> [!definition] Chaîne de caractères en C
> En C, une chaîne de caractères est un **tableau de caractères** (`char`) terminé par un caractère spécial appelé le **caractère nul** (`\0`). Ce caractère nul indique la fin logique de la chaîne.
Par exemple, la chaîne `"Bonjour"` est stockée en mémoire comme suit :
`| 'B' | 'o' | 'n' | 'j' | 'o' | 'u' | 'r' | '\0' |`
La taille réelle en mémoire d'une chaîne est donc toujours la longueur de la chaîne plus un pour le caractère nul.
> [!note] Chaînes littérales
> Les chaînes écrites entre guillemets doubles (ex: `"Hello"`) sont appelées des **chaînes littérales**. Elles sont stockées dans une zone de mémoire en lecture seule et le compilateur ajoute automatiquement le `\0` final.
## 1.2 Déclaration et initialisation
On déclare une chaîne comme un tableau de caractères :
```c
char maChaine[50]; // Déclare un tableau de 50 caractères
// Peut contenir une chaîne de 49 caractères + '\0'
```
L'initialisation peut se faire de plusieurs manières :
```c
char salutation[] = "Bonjour le monde !"; // Le compilateur calcule la taille nécessaire (18 + 1 = 19)
char nom[20] = "Alice"; // Initialise 'nom' avec "Alice", le reste est rempli de '\0'
char ville[10] = {'P', 'a', 'r', 'i', 's', '\0'}; // Initialisation explicite avec le caractère nul
// char erreur[5] = "Hello World"; // ERREUR : "Hello World" (11 chars) est trop long pour un tableau de 5 !
```
> [!warning] Toujours prévoir la place pour le `\0`
> Lors de la déclaration d'un tableau de caractères pour stocker une chaîne, assurez-vous que la taille du tableau est au moins égale au nombre de caractères de la chaîne **plus un** pour le caractère nul de fin.
# 2. Interagir avec le monde extérieur
Les programmes les plus utiles sont souvent **interactifs**, et échangent donc de l'information ave leur environnement. Cela signifie généralement prendre des informations de l'utilisateur ou de capteurs (entrées) et afficher des résultats ou envoyer des commandes à des actionneurs (sorties). En C, l'interaction par défaut se fait via la **console**, un terminal textuel où le programme peut lire des caractères saisis au clavier et afficher du texte.
Nous explorerons les fonctions standard du C pour gérer les entrées et les sorties, principalement définies dans la bibliothèque `<stdio.h>` (Standard Input/Output), et nous aborderons un concept crucial : le **buffer mémoire tampon**, qui est au cœur de la gestion de ces interactions.
## 2.1 Affichage à l'écran (Sorties)
Les sorties les plus courantes consistent à afficher du texte ou des valeurs de variables sur la console.
### 2.1.1 La fonction `printf()` : L'affichage formaté
`printf()` est la fonction la plus utilisée pour afficher des informations formatées sur la sortie standard (généralement l'écran).
> [!definition] `printf()`
> La fonction `printf()` permet d'afficher une chaîne de caractères formatée sur la console. Elle prend en argument une chaîne de format suivie d'un nombre variable d'arguments correspondant aux valeurs à afficher.
>
> **Syntaxe générale :**
> ```c
> int printf(const char *format, ...);
> ```
> Elle retourne le nombre de caractères affichés en cas de succès, ou une valeur négative en cas d'erreur.
La **chaîne de format** est une chaîne de caractères qui peut contenir :
* Du texte littéral à afficher tel quel.
* Des **spécificateurs de format** (commençant par `%`) qui indiquent le type et le format des variables à afficher.
* Des **caractères d'échappement** (commençant par `\`) pour des caractères spéciaux.
> [!example] Spécificateurs de format courants
> | Spécificateur | Type de donnée | Description |
> | :------------ | :------------ | :------------------------------------------ |
> | `%d` ou `%i` | `int` | Entier signé décimal |
> | `%u` | `unsigned int`| Entier non signé décimal |
> | `%f` | `float`, `double` | Nombre à virgule flottante (décimal) |
> | `%lf` | `double` | Nombre à virgule flottante (pour `double` avec `scanf`, mais `%f` pour `printf` est souvent suffisant) |
> | `%c` | `char` | Caractère unique |
> | `%s` | `char*` | Chaîne de caractères (tableau de `char`) |
> | `%x` ou `%X` | `int` | Entier en format hexadécimal (minuscule/majuscule) |
> | `%p` | `void*` | Adresse d'un pointeur (sera vu plus tard) |
> | `%%` | - | Affiche le caractère `%` |
> [!example] Modificateurs de format
> Les spécificateurs peuvent être enrichis de modificateurs pour contrôler l'affichage :
> * `%Nd` : Affiche un entier sur $N$ caractères (remplit avec des espaces à gauche si moins de $N$).
> * `%0Nd` : Affiche un entier sur $N$ caractères (remplit avec des zéros à gauche).
> * `%.Nf` : Affiche un flottant avec $N$ chiffres après la virgule.
> * `%N.Mf` : Affiche un flottant sur $N$ caractères au total, avec $M$ chiffres après la virgule.
> * `%-Nd` : Aligne à gauche sur $N$ caractères.
>
> **Exemples :**
> ```c
> int age = 30;
> float prix = 12.345;
> printf("Mon age est : %d ans.\n", age); // Mon age est : 30 ans.
> printf("Le prix est de %.2f euros.\n", prix); // Le prix est de 12.35 euros. (arrondi)
> printf("Code : %05d\n", 123); // Code : 00123
> printf("|%-10s|%10d|\n", "Article", 123); // |Article | 123|
> ```
> [!example] Caractères d'échappement
> | Caractère | Description |
> | :-------- | :------------------------------------------ |
> | `\n` | Nouvelle ligne (retour chariot) |
> | `\t` | Tabulation horizontale |
> | `\\` | Caractère `\` |
> | `\"` | Caractère `"` |
> | `\'` | Caractère `'` |
> | `\0` | Caractère nul (fin de chaîne) |
### 2.1.2 La fonction `putchar()` : Afficher un seul caractère
Pour afficher un seul caractère, la fonction `putchar()` est plus simple et parfois plus efficace que `printf()`.
> [!definition] `putchar()`
> La fonction `putchar()` écrit un seul caractère sur la sortie standard.
>
> **Syntaxe générale :**
> ```c
> int putchar(int char_to_write);
> ```
> Elle retourne le caractère écrit ou `EOF` (End Of File) en cas d'erreur.
> [!example] Utilisation de `putchar()`
> ```c
> #include <stdio.h>
>
> int main() {
> char lettre = 'A';
> putchar(lettre);
> putchar('\n'); // Passer à la ligne
> putchar('B');
> putchar('C');
> putchar('\n');
> return 0;
> }
> ```
> **Sortie :**
> ```
> A
> BC
> ```
### 2.1.3 La fonction `puts()` : L'affichage NON-formaté
Pour afficher une chaine de caractères sans formatage, la fonction `puts()` est plus simple (parce qu'elle ajoute automatiquement un retour chariot à la fin) et plus efficace que `printf()` car elle n'a pas à analyser une chaîne de format.
> [!definition] `puts()`
> La fonction `puts()` écrit une chaine de caractères sur la sortie standard, en ajoutant un retour chariot à la fin.
>
> **Syntaxe générale :**
> ```c
> int puts(const char *string);
> ```
> Elle retourne le caractère écrit ou `EOF` (End Of File) en cas d'erreur.
> [!example] Utilisation de `puts()`
> ```c
> #include <stdio.h>
>
> int main() {
> char message[] = "Hello, UniLaSalle !";
> puts(message); // Affiche "Hello, UniLaSalle !" puis un retour chariot
> return 0;
> }
> ```
> **Sortie :**
> ```
> Hello, UniLaSalle !
>
> ```
## 2.1 Acquisition depuis le clavier (Entrées)
Pour appréhender la capture d'information, il vaut mieux avoir conscience de la configuration par défaut:
1. Le périphérique d'entrée par défaut est le clavier (peut être modifié en faveur d'un microphone, un scanner, un capteur de température … )
2. toujours par défaut, il y a un écho des entrées clavier à l'écran (ce qui fait apparaitre dans la console le texte que vous y écrivez, bien que vous ne l'ayez pas explicitement demandé).
### 2.1.1 Saisie d'une chaîne avec `scanf()`
* **`scanf("%s", ...)` :** Lit une chaîne de caractères jusqu'au premier espace blanc (espace, tabulation, retour chariot).
```c
char prenom[20];
printf("Entrez votre prénom (sans espace) : ");
scanf("%s", prenom); // Pas besoin de '&' car 'prenom' est déjà l'adresse du début du tableau
printf("Bonjour, %s !\n", prenom);
```
> [!warning] Dépassement de buffer avec `scanf("%s", ...)`
> Comme mentionné précédemment, `scanf("%s", ...)` ne vérifie pas la taille du tableau. Si l'utilisateur entre une chaîne trop longue, il y aura un dépassement de buffer.
>
> **Pour une saisie plus sûre, limitez la taille :**
> ```c
> char prenom[20]; // Peut contenir 19 caractères + '\0'
> printf("Entrez votre prénom (max 19 caractères) : ");
> scanf("%19s", prenom); // Limite la lecture à 19 caractères
> ```
> Cependant, même avec cette limitation, `scanf("%s", ...)` ne lit qu'un seul mot et peut laisser des caractères dans le buffer si l'utilisateur tape plusieurs mots.
### 2.1.2 Saisie d'une chaîne avec `fgets()`
* **`fgets()` : La méthode préférée pour la saisie de chaînes**
`fgets()` est généralement la fonction préférée pour lire des chaînes de caractères depuis l'entrée standard car elle est plus sûre et gère mieux les espaces.
> [!definition] `fgets()`
> La fonction `fgets()` lit une ligne entière depuis un flux de données, y compris les espaces, et limite la quantité de caractères lus pour éviter les dépassements de buffer.
>
> **Syntaxe générale :**
> ```c
> char *fgets(char *str, int size, FILE *stream);
> ```
> * `str` : Pointeur vers le tableau de caractères où stocker la chaîne lue.
> * `size` : Taille maximale du buffer `str` (y compris le `\0`). `fgets` lira au maximum `size - 1` caractères.
> * `stream` : Le flux de données à lire. Pour l'entrée standard (clavier), utilisez `stdin`.
>
> Elle retourne `str` en cas de succès, ou `NULL` en cas d'erreur ou de fin de fichier.
> [!example] Utilisation de `fgets()`
> ```c
> #include <stdio.h>
> #include <string.h> // Pour strlen()
>
> int main() {
> char phrase[100]; // Peut contenir 99 caractères + '\0'
>
> printf("Entrez une phrase : ");
> fgets(phrase, sizeof(phrase), stdin); // Lit jusqu'à 99 caractères ou un '\n'
>
> // fgets inclut le '\n' si l'utilisateur appuie sur Entrée avant que le buffer ne soit plein.
> // Il est souvent souhaitable de supprimer ce '\n' si présent.
> // On le remplace par '\0' si le dernier caractère est '\n'.
> if (strlen(phrase) > 0 && phrase[strlen(phrase) - 1] == '\n') {
> phrase[strlen(phrase) - 1] = '\0';
> }
>
> printf("Vous avez entré : \"%s\"\n", phrase);
>
> return 0;
> }
> ```
> `fgets()` lit jusqu'au `\n` ou jusqu'à ce que `size-1` caractères aient été lus. Si le `\n` est lu, il est stocké dans le buffer. Si le `\n` n'est pas lu (car la ligne est trop longue), il reste dans le buffer et doit être vidé.
> [!tip] Vider le buffer après `fgets()` (si nécessaire)
> Si `fgets` ne lit pas le `\n` (parce que la ligne saisie était plus longue que la taille du buffer), le `\n` reste dans le buffer. Il faut alors le vider.
>
> ```c
> #include <stdio.h>
> #include <string.h>
>
> void viderBuffer() {
> int c;
> while ((c = getchar()) != '\n' && c != EOF);
> }
>
> int main() {
> char phrase[20]; // Très petit buffer pour illustrer
>
> printf("Entrez une longue phrase (plus de 19 caractères) : ");
> fgets(phrase, sizeof(phrase), stdin);
>
> // Vérifier si le '\n' a été lu et le supprimer si oui
> if (strlen(phrase) > 0 && phrase[strlen(phrase) - 1] == '\n') {
> phrase[strlen(phrase) - 1] = '\0';
> } else {
> // Si le '\n' n'est pas là, c'est que la ligne était trop longue et le buffer n'est pas vide
> viderBuffer();
> }
>
> printf("Vous avez entré : \"%s\"\n", phrase);
>
> return 0;
> }
> ```
---
# Conclusion et perspectives
Ce chapitre vous a introduit aux mécanismes fondamentaux d'interaction avec l'utilisateur en C. Vous avez appris à :
* Afficher des informations formatées avec `printf()` et des caractères avec `putchar()`.
* Saisir des données formatées avec `scanf()` et des caractères avec `getchar()`.
* Comprendre et gérer le **buffer mémoire tampon** pour éviter les problèmes liés aux caractères résiduels, notamment le `\n`.
* Manipuler les **chaînes de caractères** comme des tableaux de `char` terminés par `\0`.
* Utiliser `fgets()` comme alternative plus sûre à `scanf("%s", ...)` pour la saisie de chaînes.
> [!note] Points clés à retenir
> * L'opérateur `&` est crucial avec `scanf()` pour la plupart des types de variables (sauf les tableaux de caractères). C'est un premier aperçu des **pointeurs**, que nous explorerons en profondeur bientôt.
> * La gestion du buffer est essentielle pour des programmes interactifs robustes. Adoptez la bonne pratique de vider le buffer après chaque saisie importante.
> * `fgets()` est la fonction à privilégier pour la saisie de chaînes de caractères afin de prévenir les dépassements de buffer.
Ces compétences sont la base de toute application interactive. Elles seront également précieuses pour les futurs modules, notamment en **robotique**, où les entrées/sorties ne seront plus seulement le clavier/écran, mais des capteurs pour lire l'environnement et des actionneurs pour interagir avec lui. La logique de lecture et d'écriture de données restera la même, seule la source ou la destination changera.