web-dev-qa-db-fra.com

Pointeurs de caractères du tableau C sprintf

Quelqu'un pourrait-il me dire ce que je fais mal ici? Pourquoi mon programme se bloque-t-il? J'essaie d'insérer une troisième chaîne entre string1 Et string2.

#include <stdio.h>

int main (void) 
{
char *string1 = "HELLO";
char *string2 = "WORLD";
char *stringX  = "++++";
char *string3;
printf ("%s,%s\n",string1,string2);
sprintf(string3,"%s%s%s",string1,stringX,string2);
printf ("NewVar: %s",string3);
}

Pourquoi sprintf ne stocke-t-il pas la valeur résultante à l'adresse mémoire pointée par string3? Cela fonctionne quand je déclare string3 Comme un tableau ordinaire mais pas quand c'est un pointeur vers char tableau.

Je pensais que string3 Ne pointait vers aucun emplacement de mémoire, mais il semble quand je le fais printf("%p",string3);

Production:

# ./concat
HELLO,WORLD,0x40042
12
user2953313

Imaginez que vous avez un tas d'argent que vous souhaitez mettre dans une mallette. De quoi avez-vous besoin? Vous devez mesurer la taille de l'argent pour savoir quelle taille de mallette utiliser, et vous avez besoin d'une poignée pour transporter l'argent facilement.

L'argent est vos cordes. La mallette est un espace mémoire. La poignée de la mallette est le pointeur.

  1. Mesurez votre argent: strlen(string1) + strlen(string2) + strlen(stringX). Appelez cela "total".
  2. Obtenez maintenant une mallette assez grande: malloc(total+1)
  3. Et mettez une poignée dessus: string3

Bricoler tout ça ensemble ...

char *string3 = malloc(strlen(string1)+strlen(stringX)+strlen(string2)+1);
sprintf(string3, "%s%s%s", string1, stringX, string2);

Alors qu'est-ce qui n'allait pas avec la première tentative? Vous n'aviez pas de mallette. Vous avez de l'argent et vous avez une poignée, mais pas de mallette au milieu. Cela a semblé fonctionner, d'une manière aléatoire, parce que le compilateur vous a donné une poubelle sale pour contenir l'argent. Parfois, la benne à ordures a de la place, parfois non. Quand ce n'est pas le cas, nous appelons cela un "défaut de segmentation".

Chaque fois que vous avez des données, vous devez allouer de l'espace pour ces données. Le compilateur alloue de l'espace pour vos chaînes constantes, comme "HELLO". Mais vous devez allouer de l'espace pour les chaînes construites au moment de l'exécution.

26
bishop

sprintf y stocke la valeur. Le problème est que le pointeur string3 a une valeur non initialisée, donc vous écrasez simplement la mémoire aléatoire.

Une option que vous avez consiste à utiliser un tampon de chaîne statique:

char string3[20];
snprintf(string3, sizeof(string3), "Hello!");

Ou, vous pouvez utiliser asprintf sur GNU systèmes basés sur libc pour allouer automatiquement l'espace approprié:

char * string3;
asprintf(&string3, "Hello!");
// ... after use
free(string3); // free the allocated memory
9
che

sprintf n'alloue pas de mémoire à la chaîne qu'il écrit. Vous devez fournir une chaîne valide pour qu'elle puisse être écrite, mais vous lui passez actuellement un pointeur non initialisé.

La solution la plus simple consiste à changer

char *string3;
sprintf(string3,"%s%s%s",string1,stringX,string2);

à

char string3[200];
sprintf(string3,"%s%s%s",string1,stringX,string2);

Vous voudrez peut-être vous prémunir contre les dépassements de mémoire tampon dans ce cas en utilisant snprintf à la place

char string3[200];
snprintf(string3,sizeof(string3),"%s%s%s",string1,stringX,string2);

Vous pouvez également gérer des longueurs de chaîne source plus importantes en déterminant la taille de string3 au moment de l'exécution, en prenant soin de free cette mémoire lorsque vous en avez terminé.

char* string3 = malloc(strlen(string1) + strlen(stringX) + strlen(string2) + 1);
if (string3 == NULL) {
    // handle out of memory
}
sprintf(string3,"%s%s%s",string1,stringX,string2);
...
free(string3);
7
simonc

vous devez allouer de l'espace pour string3 soit avec malloc si vous avez besoin qu'il soit sur le tas, soit déclarez-le comme un tableau de caractères si ce n'est pas le cas.

4
Sam I am