les chaines de caractères en C

25 Aug 2019 25 Aug 2019 11275 vues ESSADDOUKI Mostafa 11 min de lecture

Les chaînes de caractères en langage C

En C, il n'existe pas de type string natif. Une chaîne de caractères est représentée comme un tableau de char terminé par le caractère spécial '\0' (caractère nul), qui marque la fin de la chaîne.

Définition — Chaîne de caractères Une chaîne est un tableau de type char dont le dernier élément est obligatoirement le caractère nul '\0' (code ASCII 0). C'est ce terminateur qui distingue une chaîne d'un simple tableau de caractères et qui permet aux fonctions standard de connaître la fin de la chaîne.

Ainsi, la chaîne "Meknes" occupe 7 octets en mémoire : 6 caractères + 1 terminateur '\0'.

Case[0][1][2][3][4][5][6]
Caractère'M''e''k''n''e''s''\0'

1. Déclarer une chaîne


Syntaxe — Déclaration d'une chaîne C
char nom_chaine[taille];
Attention — Prévoir une case supplémentaire pour '\0' Pour stocker une chaîne de n caractères, il faut déclarer un tableau de taille n + 1afin de laisser de la place au terminateur :
char ville[7];   /* peut stocker "Meknes" (6 car.) + '\0' */

2. Initialiser une chaîne

Il existe quatre façons équivalentes d'initialiser une chaîne en C.

FormeCodeRemarque
Littéral, taille déduitechar ville[] = "Meknes";Taille = 7 (6 + '\0') — recommandée
Littéral, taille fixechar ville[20] = "Meknes";Cases [7]…[19] remplies de '\0'
Caractère par caractèrechar ville[] = {'M','e','k','n','e','s','\0'};'\0' explicite obligatoire
Caractère par caractère, taille fixechar ville[20] = {'M','e','k','n','e','s','\0'};Reste des cases initialisées à '\0'

Exemple n°1 — Déclaration, initialisation et affichage

#include <stdio.h>

int main(void)
{
    char ville[] = "Meknes";

    printf("Chaîne   : %s\n",   ville);
    printf("Longueur : %lu\n",  sizeof(ville) - 1);  /* -1 pour exclure '\0' */
    printf("Taille   : %lu\n",  sizeof(ville));       /* inclut '\0'          */

    return 0;
}
Sortie
Chaîne   : Meknes
Longueur : 6
Taille   : 7

3. Afficher et lire une chaîne

a. printf / scanf

C dispose du spécificateur %s qui permet d'afficher et de lire une chaîne entière en une seule instruction.

Exemple n°2 — Affichage avec printf

#include <stdio.h>

int main(void)
{
    char ville[] = "Meknes";
    printf("%s\n", ville);
    return 0;
}
Sortie
Meknes

Exemple n°3 — Lecture avec scanf

#include <stdio.h>

int main(void)
{
    char ville[20];
    scanf("%s", ville);    /* pas de & car "ville" est déjà une adresse */
    printf("%s\n", ville);
    return 0;
}
Pourquoi pas de & devant ville dans scanf ? Le nom d'un tableau est déjà une adresse (celle du premier élément). Écrire ville dans scanf fournit directement &ville[0] — ajouter & serait redondant.
Limitation de scanf("%s", …) scanf avec %s s'arrête au premier espace. Pour lire une phrase complète (avec espaces), utiliser fgets() à la place.

4. Chaînes et pointeurs de caractères

Une chaîne peut aussi être manipulée via un pointeur char *. Le comportement diffère selon la zone mémoire utilisée.

DéclarationZone mémoireModifiable ?Utilisable hors portée ?
char *p = "Meknes";Segment de données (lecture seule)Non — erreur de segmentationOui — reste après fin de fonction
char t[] = "Meknes";Pile (stack)OuiNon — libérée après fin de fonction
malloc(sizeof(char)*n)Tas (heap)OuiOui — jusqu'à free()

a. Chaîne en lecture seule (segment partagé)

Exemple n°4 — Modification interdite d'un littéral

#include <stdio.h>

int main(void)
{
    char *ville = "Meknes";   /* stockée en lecture seule */
    *(ville + 1) = 'n';       /* tentative d'écriture — erreur */

    return 0;
}
Sortie
Bus error: 10
Danger — Modifier un littéral de chaîne via char * char *ville = "Meknes" pointe vers un littéral stocké en mémoire lecture seule. Toute tentative de modification provoque une erreur fatale (bus error ou segmentation fault). Utiliser char ville[] pour une chaîne modifiable, ou déclarer le pointeur constpour indiquer l'intention :
const char *ville = "Meknes";   /* intention claire : lecture seule */

b. Chaîne modifiable (tableau)

Exemple n°5 — Modification autorisée sur char[]

#include <stdio.h>

int main(void)
{
    char ville[] = "Meknes";   /* copie sur la pile — modifiable */
    *(ville + 1) = 'n';        /* autorisé */

    puts(ville);
    return 0;
}
Sortie
Mnknes

c. Chaîne allouée dynamiquement (malloc)

Exemple n°6 — Allocation sur le tas avec malloc

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int taille = 7;
    char *ville = (char *)malloc(sizeof(char) * taille);

    if (ville == NULL) { fprintf(stderr, "Allocation échouée\n"); return 1; }

    *(ville + 0) = 'M';
    *(ville + 1) = 'e';
    *(ville + 2) = 'k';
    *(ville + 3) = 'n';
    *(ville + 4) = 'e';
    *(ville + 5) = 's';
    *(ville + 6) = '\0';

    puts(ville);

    free(ville);   /* libération obligatoire */
    return 0;
}
Sortie
Meknes
Astuce — Utiliser strdup pour dupliquer une chaîne sur le tas Au lieu d'allouer manuellement octet par octet, la fonction strdup(POSIX) crée automatiquement une copie allouée dynamiquement :
#include <string.h>
char *copie = strdup("Meknes");   /* alloue + copie en une ligne */
free(copie);

5. Retourner une chaîne depuis une fonction

Le segment mémoire utilisé détermine si une chaîne retournée par une fonction est valide ou non.

Exemple n°7 — Retour valide : pointeur vers littéral (segment partagé)

#include <stdio.h>

char *getVille()
{
    char *ville = "Meknes";   /* segment partagé — persiste après retour */
    return ville;
}

int main(void)
{
    printf("%s\n", getVille());
    return 0;
}
Sortie
Meknes

Exemple n°8 — Retour invalide : adresse d'un tableau local (pile)

#include <stdio.h>

char *getVille()
{
    char ville[] = "Meknes";   /* sur la pile — libérée après retour ! */
    return ville;              /* retourne une adresse invalide         */
}

int main(void)
{
    printf("%s\n", getVille());
    return 0;
}
Sortie
warning: address of stack memory associated with local variable 'ville' returned
return ville;
^~~~~
Danger — Retourner l'adresse d'une variable locale Un tableau local est détruit à la fin de la fonction. Retourner son adresse donne un pointeur dangling : la mémoire pointée peut être écrasée à tout moment. Les solutions sûres pour retourner une chaîne sont de retourner un pointeur vers un littéral (const char *), ou d'allouer avec malloc et de laisser l'appelant libérer la mémoire.

6. Passer une chaîne en paramètre

Comme une chaîne est un tableau, elle est automatiquement convertie en pointeur lors du passage à une fonction.

Exemple n°9 — Passage de chaîne à une fonction

#include <stdio.h>
#include <string.h>

/* Les deux signatures sont équivalentes */
void afficher(char str[])          /* ou : void afficher(char *str) */
{
    printf("Ville : %s (longueur : %lu)\n", str, strlen(str));
}

int main(void)
{
    char ville[20];
    printf("Entrer une ville : ");
    scanf("%s", ville);

    afficher(ville);
    return 0;
}
Remarque — strlen vs sizeof Dans une fonction, sizeof(str) donne la taille du pointeur (8 octets), non la longueur de la chaîne. Utiliser strlen(str) (de string.h) pour obtenir le nombre de caractères hors '\0'.

7. puts() vs printf() pour l'affichage

FonctionSyntaxeSaut de ligneInterprète % ?Cas d'usage
printf("%s", s)printf(format, …)NonOuiAffichage formaté
puts(s)puts(chaine)Oui (automatique)NonAffichage simple, plus sûr
fputs(s, stdout)fputs(chaine, flux)NonNonAffichage sans saut de ligne

Exemple n°10 — puts : chaque appel ajoute un saut de ligne

#include <stdio.h>

int main(void)
{
    puts("Sedoki ");   /* affiche "Sedoki " puis '\n' */
    puts("Mostafa");   /* affiche "Mostafa" puis '\n' */
    return 0;
}
Sortie
Sedoki 
Mostafa

Exemple n°11 — fputs : sans saut de ligne automatique

#include <stdio.h>

int main(void)
{
    fputs("Sedoki ",  stdout);
    fputs("Mostafa",  stdout);
    printf("\n");
    return 0;
}
Sortie
Sedoki Mostafa
Danger — printf(str) avec une chaîne utilisateur Passer directement une chaîne en premier argument de printf est une faille de sécurité connue (format string attack). Si la chaîne contient %s, %d… le comportement est indéfini :
/* Incorrect — faille de sécurité */
printf(str);

/* Correct — toujours spécifier le format */
printf("%s", str);

Exemple n°12 — printf interprète le % dans la chaîne

#include <stdio.h>

int main(void)
{
    printf("Sedokimo%stafa");   /* % interprété comme format — warning */
    return 0;
}
Sortie
warning: more '%' conversions than data arguments [-Wformat]

Exemple n°13 — puts affiche le % tel quel

#include <stdio.h>

int main(void)
{
    puts("Sedokimo%stafa");   /* % n'est pas interprété */
    return 0;
}
Sortie
Sedokimo%stafa

8. Lire une chaîne avec espaces : fgets et gets

scanf("%s", …) s'arrête au premier espace. Pour lire une ligne complète contenant des espaces, deux fonctions sont disponibles — avec des niveaux de sécurité très différents.

FonctionSyntaxeLimite de tailleSécuritéRecommandation
fgetsfgets(buf, n, stdin)Oui — lit au plus n−1 caractèresSûreÀ utiliser
getsgets(buf)Non — aucune limiteDangereuseÀ éviter — supprimée en C11

a. fgets() — lecture sécurisée


Syntaxe — fgets C
char *fgets(char *buf, int n, FILE *flux);
/* Lit au plus n-1 caractères depuis flux (stdin, fichier…)
   S'arrête à '\n', à EOF, ou après n-1 caractères
   Ajoute automatiquement '\0' en fin de chaîne */

Exemple n°14 — fgets avec limite de taille

#include <stdio.h>
#define MAX 15

int main(void)
{
    char buf[MAX];
    printf("Entrer une phrase : ");
    fgets(buf, MAX, stdin);
    printf("Chaîne lue : %s\n", buf);
    return 0;
}
Sortie
Entrer une phrase : mostafa est un professeur d'informatique
Chaîne lue : mostafa est un
Remarque — fgets conserve le '\n' Contrairement à gets, fgets inclut le caractère '\n'final dans la chaîne lue. Pour le supprimer :
#include <string.h>
buf[strcspn(buf, "\n")] = '\0';   /* supprime le '\n' s'il est présent */

b. gets() — à éviter absolument

Exemple n°15 — gets : débordement de tampon

#include <stdio.h>
#define MAX 6

int main(void)
{
    char buf[MAX];
    printf("Saisir une chaîne : ");
    gets(buf);   /* dangereux — aucune limite de taille ! */
    printf("Chaîne : %s\n", buf);
    return 0;
}
Sortie
warning: this program uses gets(), which is unsafe.
Saisir une chaîne : mostafa est un professeur
Chaîne : mostafa est un professeur
Segmentation fault: 11
Danger — gets() supprimée en C11 gets() n'effectue aucune vérification de taille. Une saisie plus longue que le buffer écrase d'autres zones mémoire — c'est une faille de type buffer overflow, responsable de nombreuses failles de sécurité historiques. La fonction a été retirée du standard C11. Toujours utiliser fgets(buf, taille, stdin) à la place.

Récapitulatif

OpérationFonction / SyntaxePoint clé
Déclarationchar s[n];Prévoir n = longueur + 1 pour '\0'
Initialisationchar s[] = "texte";Forme la plus concise — recommandée
Affichageprintf("%s", s) / puts(s)puts ajoute '\n' ; préférer printf("%s", s)
Lecture sans espacescanf("%s", s)S'arrête au premier espace — pas de &
Lecture avec espacesfgets(s, n, stdin)Sûre — limite la saisie à n−1 caractères
Longueurstrlen(s)Hors '\0' — nécessite string.h
Pointeur constconst char *s = "texte";Lecture seule — modification interdite
Retour de fonctionmalloc ou littéral const char *Ne jamais retourner l'adresse d'un tableau local

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.