2009-03-19 5 views
0

J'ai du code qui empile des piles lors de l'utilisation de sprintf pour copier un pointeur vers des chaînes. J'essaie de copier le contenu des animaux dans un nouveau tableau de pointeurs appelé sortie. Cependant, j'obtiens un vidage de pile.Copier des chaînes dans un tampon

Quelle devrait être la sortie est la suivante: nouveau lapin animal nouveau cheval animal nouveau âne animal

Un je vais sur ce la bonne façon?

Un grand merci, s

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

void p_init(const char **animals, char **output); 

int main(int argc, char **argv) 
{ 
    char *animals[] = {"rabbit", "horse", "donkey", '\0'}; 
    char **prt_animals = animals; 
    char *output[sizeof(*animals)]; 

     /* print the contents here */ 
    while(*prt_animals) 
    { 
     printf("Animal: %s\n", *prt_animals++); 
    } 

     /* copy and update in the output buffer */ 
    p_init(*&animals, *&output); 

    getchar(); 

    return 0; 


void p_init(const char **animals, char **output) 
{ 
    while(*animals) 
    { 
     sprintf(*output, "new animal %s", *animals); 
     *output++; 
    } 
} 

Répondre

6

Le tableau animals est un tableau de pointeurs. Ce n'est pas un tableau de tampons de quelque taille. Par conséquent, si vous le faites

sizeof(*animals) 

Vous obtiendrez le sizeof du premier élément de ce tableau. Équivalent à

sizeof(char*) 

Parce que votre tableau stocke des pointeurs. Ainsi, dans la ligne qui lit

char *output[sizeof(*animals)]; 

vous allouez 4 ou 8 pointeurs dans un tableau (dépend de la largeur d'un pointeur sur votre plate-forme est. Habituellement, il est 4 ou 8). Mais ce n'est bien sûr pas sensé! Ce que vous vouliez faire est de créer un tableau de pointeurs de la même taille que animals. Vous devrez d'abord obtenir la taille totale du tableau des animaux, puis diviser par la taille d'un élément

char *output[sizeof(animals)/sizeof(*animals)]; 

Maintenant, c'est-ce que vous voulez. Mais les pointeurs auront encore des valeurs indéterminées ... Ensuite, vous passez le tableau en utilisant *&animals (même chose pour l'autre). Pourquoi ça? Vous pouvez passer directement au animals. Prendre son adresse, puis déréférencer est la même chose que de ne rien faire en premier lieu.

Puis dans la fonction que vous appelez, vous copiez les chaînes pointées par des éléments en animal vers une destination indéterminée (rappelez-vous les éléments du tableau output - les pointeurs -.! Ont des valeurs encore indéterminées Nous ne les ont pas encore assignés) . Vous devez d'abord allouer la bonne quantité de mémoire et faire pointer les éléments vers cela.

while(*animals) { 
     // now after this line, the pointer points to something sensible 
     *output = malloc(sizeof("new animal ") + strlen(*animals)); 
     sprintf(*output, "new animal %s", *animals); 
     output++; // no need to dereference the result 
     animals++; // don't forget to increment animals too! 
} 

addition, sur le sizeof ci-dessus

Il y a une chose importante que vous devez être sûr. C'est la façon dont nous calculons la taille. Quoi que vous fassiez, assurez-vous de toujours avoir assez de place pour votre chaîne! Une chaîne C se compose de caractères et d'un caractère nul final, qui marque la fin de la chaîne. Donc, *output doit pointer vers un tampon qui est au moins aussi grand afin qu'il contienne de l'espace pour "new animal " et *animals. Le premier contient 11 caractères. La seconde dépend de ce que nous copions réellement - sa longueur est ce que strlen renvoie. Donc, au total, nous avons besoin

12 + strlen(*animals) 

espace pour tous les caractères, y compris la valeur nulle. Maintenant, ce n'est pas un bon style de coder en dur ce numéro dans votre code.Le préfixe pourrait changer et vous pourriez oublier de mettre à jour le nombre ou un compte approximatif d'un ou deux caractères. C'est pourquoi nous utilisons sizeof, que nous fournissons avec le littéral de chaîne que nous voulons avoir pré-ajouté. Rappelons qu'une expression sizeof évalue à la taille de son opérande. Vous l'utilisez dans main pour obtenir la taille totale de votre tableau avant. Maintenant vous l'utilisez pour le littéral de chaîne. Tous les littéraux de chaîne sont des tableaux de caractères. Les littéraux de chaîne se composent des caractères que vous tapez en plus au caractère nul. Ainsi, la condition suivante est, parce que strlen compte de la longueur d'une chaîne de C, et ne comprend pas le caractère nul final à sa longueur

// "abc" would have the type char[4] (array of 4 characters) 
sizeof "..." == strlen("...") + 1 

Nous ne devons pas diviser par la taille d'un élément, parce que le sizeof char n'en est pas moins un, donc ça ne fera pas de différence. Pourquoi utilisons-nous sizeof au lieu de strlen? Parce qu'il prend déjà en compte le caractère nul final et qu'il évalue au moment de la compilation. Le compilateur peut littéralement substituer la taille renvoyée par l'expression sizeof.

+0

Vous avez besoin de +1 pour la longueur du terminateur null. – dirkgently

+0

le sizeof d'un littéral de chaîne inclut déjà le terminateur null :) so sizeof "" == 1 –

1

Tout d'abord, quel est le point de

p_init(*&animals, *&output); 

par opposition à

p_init(animals, *&output); 

? Deuxièmement, il est illégal de convertir un caractère ** en un caractère ** pour les raisons expliquées here.

Enfin, votre principal problème est que le test

while (*animals) 

que vous attendez à l'échec lorsque vous avez atteint la chaîne vide à la fin du tableau des animaux, est erroné. Cette instruction vérifie réellement si le pointeur qui pointe vers la chaîne est NULL, ne vérifiant pas si la chaîne est VIDE. Une chaîne vide (une chaîne contenant le seul caractère '\ 0') n'est pas la même chose qu'un pointeur nul. En d'autres termes, une fois que vous avez atteint le dernier élément du tableau des animaux, * animaux évalue un pointeur non-NULL qui pointe vers une chaîne vide. Par conséquent, le test se passe, et votre boucle continue pour toujours (bien, elle continue jusqu'à ce que vous couriez assez loin après la fin du tableau des animaux qu'il provoque une segfault). Vous pouvez résoudre ce problème en remplaçant '\ 0' par NULL lorsque vous créez le tableau des animaux, ou vous pouvez modifier la commande while pour vérifier strlen (* animals) == 0 (ou tout autre moyen de vérifier un chaîne vide au lieu d'un pointeur nULL

Edit:... Notez que d'autres ont souligné des problèmes tout aussi graves que je ratées

+0

De plus, * les animaux ne sont pas incrémentés, alors que (* animaux) est une boucle infinie. – mch

+0

Bon point, mais même si elle était incrémentée, ce serait toujours une boucle infinie (ou au moins indéfiniment longue) pour la raison que j'ai mentionnée. –

+0

Um ... '0' est la constante entière de zéro qui équivaut à NULL si vous ignorez la substance void *. Pas la meilleure idée pour le style, mais assez sûr dans la pratique. –

2
char *output[sizeof(*animals)]; 

Crée un tableau de taille 4 de pointeurs vers char Il n'a pas, Toutefois, allouez de la mémoire à ces pointeurs. pointez sur la mémoire que vous ne possédez pas). Essayer d'écrire dans cette mémoire appelle UB. Dans votre cas, UB se manifeste par un vidage de pile. Un autre problème avec votre fonction p_init est indiquée ci-dessous:

void p_init(const char **animals, char **output) 
{ 
    /* runs an infinite loop -- since *animals is never incremented */ 
    /* need to pass size of *animals so that you can terminate your loop */ 
    while(*animals) 
    { 
     /* allocate some memory */ 
     sprintf(*output, "new animal %s", *animals); 
     *output++; 
    } 
} 

Un code fixe en sera quelque chose comme ceci:

void p_init(const char ** animals, const size_t nanimals, char **output) 
{ 
    const char **w = animals; 
    size_t len = 0; 
    while (w < animals + nanimals) 
    { 
     len = strlen(*w); 
     *output = malloc(len + sizeof "new animal " + 1); 
     sprintf(*output, "new animal %s", *w); 
     output++;   
     w++; 
    } 
} 

int main(int argc, char **argv) 
{ 
    char *a[] = { "rat", "dog", "lion" }; 
    char *o[ sizeof a/ sizeof *a ]; 
    p_init((const char**)a, sizeof a/sizeof *a, o); 
    for (size_t i = 0; i < sizeof a/sizeof *a; ++i) printf("%s\n", o[ i ]); 
    for (size_t i = 0; i < sizeof a/sizeof *a; ++i) free(o[ i ]); 
    return 0; 
} 

Ne hésitez pas à jeter dans les en-têtes nécessaires.

2

Vous n'avez alloué aucun espace dans votre tableau de sortie pour y placer la copie.Vous devrez utiliser malloc pour allouer de l'espace avant d'utiliser sprintf pour copier dans ce tampon. N'oubliez pas d'appeler free() sur cette mémoire allouée lorsque vous avez terminé.

1

Vous ne créez pas un très grand tampon - sizeof(*animals) est sizeof(char*) qui est de 4 octets sur un système 32 bits; vous ne créez aucun endroit pour mettre les chaînes de sortie; et vous n'utilisez pas un mécanisme de sécurité tel que snprintf pour écrire dans le tampon, de sorte que vous vous plantez plutôt que d'échouer en toute sécurité.

Corrections pour ces suivre, en gardant à l'utilisation du tableau zéro terminé:

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

void p_init (const char **animals, char **output); 

int main(int argc, char **argv) { 
    // no reason to use char 0 rather than int 0 to mark end here 
    const char* animals[] = {"rabbit", "horse", "donkey", "pig", 0}; 

    printf("sizeof(*animals) = %zd\n", sizeof(*animals)); 
    printf("number of elements in animals = %zd\n", sizeof(animals)/sizeof(*animals)); 

    char *output[sizeof(animals)/sizeof(*animals)]; 

    // print animals 
    for (const char**p = animals; *p; ++p) 
     printf ("Animal: %s\n", *p); 

    // format animals to output 
    p_init (animals, output); 

    // print output 
    for (char**p = output; *p; ++p) 
     printf ("Animal: %s\n", *p); 

    // free output 
    for (char**p = output; *p; ++p) 
     free(*p); 

    return 0; 
} 

void p_init (const char **animals, char **output) { 
    while (*animals) { 
     size_t len = strlen (*animals); 
     char* buf = malloc (len + 13); 

     snprintf (buf, len + 13, "new animal %s", *animals); 

     *output = buf; 

     ++animals; 
     ++output; 
    } 

    *output = 0; 
} 
Questions connexes