Programmation C source/chaînes
int main(void)
 /*
 Les fonctions permettant de manipuler les chaînes de caractères
 1. Comparaison de chaînes
 2. Longueur d'une chaîne
 3. Concaténation et copie de chaînes
 4. Recherche dans une chaîne
 5. Traitement des blocs mémoire
 printf("----------------------------------------\n");
 printf(" Comparaison de chaînes\n");
 printf("----------------------------------------\n");
 #if 0
 int strcmp(const char * chaine1, const char * chaine2);
 int strncmp(const char * chaine1, const char * chaine2, size_t longueur);
 #endif
 /* La fonction strcmp() compare les deux chaînes
 La fonction strncmp() est identique sauf qu’elle ne compare
 que les n (au plus) premiers caractères
 négatif, si chaine1 est inférieure à chaine2
 nul, si chaine1 est égale à chaine2
 positif, si chaine1 est supérieur à chaine2
 Pour tester l'égalité entre deux chaînes, il faut donc écrire
 if (strcmp(chaine1, chaine2) == 0) ...
 ou if (!strcmp(chaine1, chaine2)) ...
 if (strcmp(chaine1, chaine2)) ... teste si deux chaînes sont différentes
 Noter chaine1 == chaine2, si chaine1 et chaine2 sont des char *
 revient à tester si les deux chaînes pointent sur la même zone mémoire */
 #if 0
 int strcasecmp(const char * chaine1, const char * chaine2);
 int strncasecmp(const char * chaine1, const char * chaine2, size_t longueur);
 #endif
 /* La fonction strcasecmp() compare deux chaînes,
 en ignorant les différences entre majuscules et minuscules.
 La fonction strncasecmp() est similaire, à la différence qu’elle ne
 prend en compte que les n premiers caractères */
 // exemples :
 if (!strcmp("Jean", "Jean-pierre"))
 printf("chaînes identiques\n");
 else
 printf("chaînes différentes\n");
 if (!strncmp("Jean", "Jean-pierre", 4))
 printf("4 premiers caractères identiques\n");
 else
 printf("4 premiers caractères différents\n");
 if (!strcasecmp("pierre", "PIERRE"))
 printf("chaînes identiques\n");
 else
 printf("chaînes différentes\n");
 printf("\n----------------------------------------\n");
 printf(" Longueur d'une chaîne\n");
 printf("----------------------------------------\n");
 #if 0
 size_t strlen(const char * chaine);
 #endif
 // Renvoie la longueur de la chaine sans compter le caractère '\0'.
 char chaine1[] = { 'B', 'o', 'n', 'j', 'o', 'u', 'r', '\0' };
 printf("Longueur de chaine1(Bonjour) = %zu\n", strlen(chaine1));
 printf("\n----------------------------------------\n");
 printf(" Concaténation et copie de chaînes\n");
 printf("----------------------------------------\n");
 #if 0
 char * strcpy (char *destination, const char *source);
 char * strncpy (char *destination, const char *source, size_t n);
 char * strcat (char *destination, const char *source);
 char * strncat (char *destination, const char *source, size_t n);
 #endif
 /* strcpy copie le contenu de source à l'adresse mémoire pointé par destination,
 incluant le caractère nul final
 strcat concatène la chaîne source à la fin de la chaîne destination,
 en y rajoutant aussi le caractère nul final
 Toutes ces fonctions renvoient le pointeur sur la chaîne destination.
 Pour strncpy, on notera que
 si la chaîne source a moins de n caractères non nuls,
 strncpy rajoutera n - strlen(source) caractères nuls à la suite, pour compléter
 si la chaîne source fait au moins n caractères, la fonction n'insèrera pas de
 caractère nul en fin de chaine (i.e. destination ne sera pas une chaîne de
 caractères valide) */
 #if 0
 char * copie_chaine(const char * source)
 char * chaine = malloc(strlen(source));
 if (chaine != NULL)
 strcpy(chaine, source);
 return chaine;
 #endif
 /* Ce code a priori correct provoque pourtant l'écriture d'un caractère dans une
 zone non allouée. Les conséquences de ce genre d'action sont totalement
 imprévisibles, pouvant au mieux passer inaperçue, ou au pire écraser des
 structures de données critiques dans la gestion des blocs mémoires et engendrer
 des accès mémoire illégaux lors des prochaines tentatives d'allocation ou de
 libération de bloc, i.e. le cauchemar de tout programmeur. Dans cet exemple il
 aurait bien évidemment fallu écrire : */
 #if 0
 char * chaine = malloc( strlen(source) + 1 );
 #endif
 /* Une autre erreur, beaucoup plus fréquente hélas, est de copier une chaîne dans
 un tableau de caractères local, sans se soucier de savoir si ce tableau est
 capable d'accueillir la nouvelle chaîne. Les conséquences peuvent ici être
 beaucoup plus graves qu'un simple accès illégal à une zone mémoire globale.
 Un écrasement mémoire (buffer overflow) est considéré comme un défaut de
 sécurité relativement grave, puisque, sur un grand nombre d'architectures,
 un « attaquant » bien renseigné sur la structure du programme peut effectivement
 lui faire exécuter du code arbitraire.
 Ce problème vient de la manière dont les variables locales aux fonctions et
 certaines données internes sont stockées en mémoire. Comme le C n'interdit pas
 d'accéder à un tableau en dehors de ses limites, on pourrait donc, suivant la
 qualité de l'implémentation, accéder aux valeurs stockées au-delà des
 déclarations de variables locales. En fait, sur un grand nombre d'architectures,
 les variables locales sont placées dans un espace mémoire appelé pile, avec
 d'autres informations internes au système, comme l'adresse de retour de la
 fonction, c'est-à-dire l'adresse de la prochaine instruction à exécuter après la
 fin de la fonction.
 En s'y prenant bien, on peut donc écraser cette valeur pour la remplacer par
 l'adresse d'un autre bout de code, qui donnerait l'ordre d'effacer le disque dur,
 par exemple ! Si en plus le programme possède des privilèges, les résultats
 peuvent être assez catastrophiques. Ainsi décrit, le problème semble complexe :
 il faudrait que l'attaquant puisse insérer dans une zone mémoire de l'application
 un bout de code qu'il a lui-même écrit, et qu'il arrive à écrire l'adresse de ce
 bout de code là où le système s'attend à retrouver l'adresse de retour de la
 fonction. Cependant, ce genre d'attaque est aujourd'hui très courant, et les
 applications présentant ce genre d'erreur deviennent très rapidement la cible
 d'attaques.
 Voici un exemple très classique, où ce genre d' exploit peut arriver : */
 #if 0
 int traite_chaine(const char *ma_chaine)
 char tmp[512];
 strcpy(tmp, ma_chaine);
 #endif
 /* Ce code, hélas plus fréquent qu'on ne le pense, est à bannir. Parmi différentes
 méthodes, on peut éviter ce problème en ne copiant qu'un certain nombre des
 premiers caractères de la chaîne, avec la fonction strncpy : */
 #if 0
 char tmp[512];
 strncpy(tmp, ma_chaine, sizeof(tmp));
 tmp[sizeof(tmp) - 1] = '\0';
 #endif
 /* On notera l'ajout explicite du caractère nul, si ma_chaine est plus grande que
 la zone mémoire tmp. La fonction strncpy ne rajoutant hélas pas, dans ce cas,
 de caractère nul. C'est un problème tellement classique que toute application C
 reprogramme en général la fonction strncpy pour prendre en compte ce cas de
 figure (voir la fonction POSIX strdup, ou strlcopy utilisée par le système
 d'exploitation OpenBSD, par exemple)
 Le langage n'offrant que très peu d'aide, la gestion correcte des chaînes de
 caractères est un problème à ne pas sous-estimer en C. */
 char chaineSource[] = { 'P', 'i', 'e', 'r', 'r', 'e', '\0' };
 char * chaineDestination = malloc( strlen(chaineSource) + 1 );
 size_t longueurDestination = strlen(chaineSource) + 1;
 strcpy(chaineDestination, chaineSource);
 printf("%s\n", chaineSource);
 printf("(strcpy) %s\n", chaineDestination);
 int n = 3;
 strncpy(chaineDestination, chaineSource, n);
 if (n > 0)
 chaineDestination[n - 1]= '\0';
 printf("\n%s\n", chaineSource);
 printf("(strncpy) %s\n", chaineDestination);
 const char * chaineCat = "err";
 if (strlen(chaineDestination)+strlen(chaineCat)+1 <= longueurDestination)
 strcat(chaineDestination, chaineCat);
 printf("\n%s\n", chaineCat);
 printf("(strcat) %s\n", chaineDestination);
 if (strlen(chaineDestination)+1+1 <= longueurDestination)
 strncat(chaineDestination, chaineCat, 1);
 printf("(strncat 1) %s\n", chaineDestination);
 char * chaineCopie = strdup(chaineSource);
 printf("\nchaineSource %s\n", chaineSource);
 printf("(strdup) chaineCopie %s\n",chaineCopie);
 free(chaineCopie);
 //strlcopy(); OpenBSD
 free(chaineDestination);
 printf("\n----------------------------------------\n");
 printf(" Recherche dans une chaîne\n");
 printf("----------------------------------------\n");
 #if 0
 char * strchr(const char * chaine, int caractère);
 char * strrchr(const char * chaine, int caractère);
 #endif
 /* Recherche le caractère dans la chaine et renvoie la position de la première
 occurrence dans le cas de strchr et la position de la dernière occurrence dans le
 cas de strrchr. */
 #if 0
 char * strstr(const char * meule_de_foin, const char * aiguille);
 #endif
 /* Recherche l'aiguille dans la meule de foin et renvoie la position de la première
 occurrence. */
 const char * chaineRecherche = "Recherche";
 char * position = strchr(chaineRecherche, 'e');
 printf("*position = %c\n", *position);
 printf("position = %p\n", position);
 position = strrchr(chaineRecherche, 'e');
 printf("\n*position = %c\n", *position);
 printf("position = %p\n", position);
 char * positionAiguille = strstr(chaineRecherche, "che");
 printf("\npostitionAiguille = %s\n", positionAiguille);
 printf("\n----------------------------------------\n");
 printf(" Traitement des blocs mémoire\n");
 printf("----------------------------------------\n");
 #if 0
 void * memcpy( void * destination, const void * source, size_t longueur );
 #endif
 /* Copie 'longueur' octet(s) de la zone mémoire 'source' dans 'destination'.
 Vous devez bien sûr vous assurer que la chaine destination ait suffisament de
 place, ce qui est en général plus simple dans la mesure où l'on connait la
 longueur.
 Attention au récouvrement des zones :
 si destination + longueur < source alors source >= destination. */
 #if 0
 void * memmove( void * destination, const void * source, size_t longueur );
 #endif
 /* Identique à la fonction memcpy(), mais permet de s'affranchir totalement
 de la limitation de recouvrement. */
 #if 0
 void * memset( void * memoire, int caractere, size_t longueur );
 #endif
 /* Initialise les 'longueur' premiers octets du bloc 'memoire', par la valeur
 convertie en type char de 'caractere'. Cette fonction est souvent employée pour
 mettre à zéro tous les champs d'une structure ou d'un tableau : */
 #if 0
 struct MonType_t mem;
 memset( &mem, 0, sizeof mem );
 #endif
 #if 0
 int memcmp( const void * mem1, const void * mem2, size_t longueur );
 #endif
 /* Compare les 'longueur' premiers octets des blocs 'mem1' et 'mem2'. Renvoie les
 codes suivants :
 < 0 : mem1 < mem2
 = 0 : mem1 == mem2
 > 0 : mem1 > mem2 */
 #if 0
 void * memchr( const void * memoire, int caractere, size_t longueur );
 #endif
 /* Recherche dans les 'longueur' premiers octets du bloc 'memoire', la valeur
 convertie en type char de 'caractere'. Renvoie un pointeur sur l'emplacement où
 le caractère a été trouvé, ou NULL si rien n'a été trouvé. */
 #if 0
 void * memrchr( const void * memoire, int caractere, size_t longueur );
 #endif
 /* Pareil que memchr(), mais commence par la fin du bloc 'memoire'. */
 const char * maSource1 = "abcDEFghi";
 int longueur1 = strlen(maSource1) + 1;
 char * maDestination1 = malloc(longueur1);
 if (maDestination1 + longueur1 < maSource1 || maSource1 + longueur1 < maDestination1)
 memcpy(maDestination1, maSource1, longueur1);
 printf("(memcpy) maDestination1 = %s\n", maDestination1);
 else
 printf("Les deux zones ne doivent pas se chevaucher.");
 free(maDestination1);
 char maDestination2[] = "abcDEFghi";
 char * maSource2 = &maDestination2[3];
 memmove(maDestination2, maSource2, 7);
 printf("\n(memmove) maDestination2 = %s\n", maDestination2);
 memset(maDestination2, '*', 6);
 printf("\n(memset) maDestination2 = %s\n", maDestination2);
 char * chaineA = "bonjour";
 char * chaineB = "Bonjour";
 if (memcmp(chaineA, chaineB, 7))
 printf("\n(memcmp) les 7 premiers octets sont différents.\n");
 char * caractere = memchr(chaineB, 'o', 3);
 printf("\n(memchr) caractere %c = %p\n", *caractere, caractere);
 caractere = memrchr(chaineB, 'o', 7);
 printf("\n(memrchr) caractere %c = %p\n", *caractere, caractere);
 return 0;