2017-05-23 4 views
-2

Contexte:Mystère de la mystérieuse P

Je suis en train de créer un programme qui prend un nom d'utilisateur (en supposant que l'entrée est propre), et imprime les initiales du nom.

Objectif:

  • Essayer ma main à la programmation C avec CS50
  • moi-même Se familiariser avec malloc & realloc

code:

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

string prompt(void); 
char *getInitials(string input); 
char *appendArray(char *output,char c,int count); 

//Tracks # of initials 
int counter = 0; 

int main(void){ 

    string input = prompt(); 
    char *output = getInitials(input); 
    for(int i = 0; i < counter ; i++){ 

     printf("%c",toupper(output[i])); 
    } 


} 

string prompt(void){ 

    string input; 

    do{ 
     printf("Please enter your name: "); 
     input = get_string(); 
    }while(input == NULL); 

    return input; 

} 

char *getInitials(string input){ 

    bool initials = true; 
    char *output; 
    output = malloc(sizeof(char) * counter); 
    for(int i = 0, n = strlen(input); i < n ; i++){ 
     //32 -> ASCII code for spacebar 
     //9 -> ASCII code for tab 
     if(input[i] == 32 || input[i] == 9){ 
      //Next char after spaces/tab will be initial 
      initials = true; 


     }else{//Not space/tab 
      if(initials == true){ 
       counter++; 
       output = appendArray(output,input[i],counter); 

       initials = false; 


      }   

     } 
     // eprintf("Input[i] is : %c\n",input[i]); 
     // eprintf("Counter is : %i\n",counter); 
     // eprintf("i is : %i\n",i); 
     // eprintf("n is : %i\n",n); 



    } 

    return output; 
} 

char *appendArray(char *output,char c,int count){ 

    // allocate an array of some initial (fairly small) size; 
    // read into this array, keeping track of how many elements you've read; 
    // once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents; 
    // repeat until done. 


    //pointer to memory 
    char *data = malloc(0); 
    //Increase array size by 1 
    data = realloc(output,sizeof(char) * count); 
    //append the latest initial 
    strcat(data,&c); 
    printf("Value of c is :%c\n",c); 
    printf("Value of &c is :%s\n",&c); 
    for(int i = 0; i< count ; i++){ 
     printf("Output: %c\n",data[i]); 
    } 
    return data; 

} 

Problème:

La sortie n'est pas ce que je m'attendais car il y a un mystérieux P apparaissant dans la sortie.

Par exemple, quand je rentre le nom de Barack Obama, au lieu d'obtenir le résultat: BO, je reçois le résultat BP et la même chose se produit pour quelque nom que je choisis d'entrer, avec la dernière initiale étant toujours P.

sortie:

Please enter your name: Barack Obama 
Value of c is :B 
Value of &c is :BP 
Output: B 
Value of c is :O 
Value of &c is :OP 
Output: B 
Output: P 
BP 

ce que je l'ai fait:

J'ai tracé le problème à la fonction appendArray, et plus précisément à la valeur de & c (adresse de c) si je ne sais pas ce qui est faisant apparaître le P, ce que cela signifie, pourquoi il apparaît et comment je peux m'en débarrasser.

La valeur de P s'affiche peu importe quand je l'entrée.

Un aperçu de ce qui se passe et ce que je peux faire pour le résoudre sera très apprécié.

Merci!

+1

Il y a un [CS50 site d'échange de pile] (http://cs50.stackexchange.com/) dans le cas où vous êtes intéressé . – pmg

+0

@pmg Je ne le savais pas. Merci de l'avoir signalé! –

+0

'output = malloc (compteur sizeof (char) *);' -> 'output = malloc (++ compteur); * output = 0; ' – BLUEPIXY

Répondre

3

Plusieurs questions, en ordre décroissant d'importance ...

Première question - c à appendArray est pas une chaîne - il n'est pas une séquence des valeurs de caractères terminées par un 0. c est un objet unique char, stockant une seule valeur char.

Lorsque vous essayez d'imprimer ccomme une chaîne, comme dans

printf("Value of &c is :%s\n",&c); 

printf écrit la séquence des valeurs de caractères à partir de l'adresse de c jusqu'à ce qu'il voit un octet 0-valeur. Pour une raison quelconque, l'octet suivant immédiatement c contient la valeur 80, qui est le code ASCII (ou UTF-8) pour le caractère 'P'. L'octet suivant contient un 0 (ou une séquence d'octets contenant des caractères non imprimables, suivie d'un octet de valeur 0).

De même, en utilisant &c comme argument de strcat est inappropriée, car cn'est pas une chaîne. Au lieu de cela, vous devez faire quelque chose comme

data[count-1] = c; 

En second lieu, si vous voulez traiter le tableau data comme une chaîne, vous devez vous assurer de la taille au moins 1 plus que le nombre d'initiales et d'écrire un 0 à la dernier élément:

data[count-1] = 0; // after all initials have been stored to data 

Troisièmement,

char *data = malloc(0); 

ne sert à rien, le comportement est défini par l'implémentation, et vous immédiatement écraser le résultat de malloc(0) avec un appel à realloc:

data = realloc(output,sizeof(char) * count); 

Alors, se débarrasser de l'appel tout à fait malloc(0); soit juste initialiser data à NULL, ou l'initialiser avec le realloc appel:

char *data = realloc(output, sizeof(char) * count); 

Quatrièmement, évitez d'utiliser des « nombres magiques » - des constantes numériques de sens au-delà de leur valeur littérale immédiate. Lorsque vous voulez comparer valeurs de caractères, utilisez caractère constantes. OIEau, changer

if(input[i] == 32 || input[i] == 9){ 

à

if (input[i] == ' ' || input[i] == '\t') 

De cette façon, vous n'avez pas à vous soucier de savoir si le codage de caractères ASCII est, UTF-8, EBCDIC, ou un autre système. ' ' signifie espace partout, '\t' signifie onglet partout.

Enfin ...

Je sais qu'une partie de votre motivation pour cet exercice est de se familiariser avec malloc et realloc, mais je veux vous mettre en garde sur certaines choses:

realloc est potentiellement une opération coûteuse , peut déplacer les données vers un nouvel emplacement, et peut échouer. Vous ne voulez vraiment pas realloc un tampon un octet à la fois. Au lieu de cela, il est préférable de realloc en morceaux. Une stratégie typique est de multiplier la taille actuelle de la mémoire tampon par un facteur> 1 (doublement en général):

char *tmp = realloc(data, current_size * 2); 
if (tmp) 
{ 
    current_size *= 2; 
    data = tmp; 
} 

Vous devriez toujours vérifier le résultat d'une malloc, calloc ou realloc appel pour vous assurer qu'il a réussi avant tenter d'accéder à cette mémoire.

notes stylistiques mineures:

Évitez les variables globales où vous pouvez. Il n'y a aucune raison que counter soit global, d'autant plus que vous le passez comme argument à appendArray. Le déclarer local main et le transmettre comme argument (par référence) à getInput:

int main(void) 
{ 
    int counter = 0; 
    ... 
    char *output = getInitials(input, &counter); 
    for(int i = 0; i < counter ; i++) 
    { 
    printf("%c",toupper(output[i])); 
    } 
    ... 
} 

/** 
* The "string" typedef is an abomination that *will* lead you astray, 
* and I want to have words with whoever created the CS50 header. 
* 
* They're trying to abstract away the concept of a "string" in C, but 
* they've done it in such a way that the abstraction is "leaky" - 
* in order to use and access the input object correctly, you *need to know* 
* the representation behind the typedef, which in this case is `char *`. 
* 
* Secondly, not every `char *` object points to the beginning of a 
* *string*.  
* 
* Hiding pointer types behind typedefs is almost always bad juju. 
*/ 
char *getInitials(const char *input, int *counter) 
{ 
    ... 
    (*counter)++;         // parens are necessary here 
    output = appendArray(output,input[i],*counter); // need leading * here 
    ... 
} 
+0

Bien méritant le mot * "éducation" *. –