2010-09-07 4 views
2

En lisant K & R, je suis tombé sur l'entier à la chaîne fonction. Je lui ai donné une lecture rapide, et j'ai décidé de l'implémenter moi-même, mais au lieu d'imprimer, il met à jour un tableau de caractères.Quelques questions concernant un entier C à la fonction de chaîne

Voici ce que j'ai

void inttostr(int number, char str[]) { 
    static int i; 
    if (number/10) { 
     inttostr(number/10, str); 
    }  
    str[i++] = number % 10 + '0';  
} 

Il semblait fonctionner pour les quelques entiers que je lui ai donné, mais j'ai quelques questions.

  1. Je n'ai pas inclus explicitement l'octet NUL \0 à la fin, alors pourquoi le travail chaîne bien lors de l'impression avec printf("%s\n", str);?
  2. Je ne pense pas que je suis très bon à en pensant récursivement. Quand j'essaie de parcourir le programme dans mon esprit, je perds la trace de ce qui attend encore l'exécution. Y a-t-il une meilleure façon de voir ce qui se passe en interne, pour m'aider à apprendre?
  3. D'autres suggestions sur le code?

J'utilise Xcode.

Ceci est pas devoir. Je suis en train d'apprendre.

Merci!

+0

Ne serait-il pas plus facile d'utiliser itoa()? –

+0

@Alexander Si c'est quelque chose dans une bibliothèque standard, alors vous avez probablement raison. Cependant, j'apprends en faisant, donc appeler la fonction de bibliothèque ne me laissera pas apprendre comment cela peut fonctionner intérieurement. – alex

+2

'itoa' n'est pas standard, et il semble que ce soit un exercice éducatif. –

Répondre

4

Vous avez raison de dire que vous n'écrivez jamais NUL, ce qui est un bug.

En général, vous n'avez pas besoin de penser à toute la solution. Vous devez juste vous assurer que chaque étape est correcte. Donc, dans ce cas, vous dites:

1. inttostr(number/10, str);

prendra soin de tous sauf le dernier chiffre.

2. Ensuite, je prendrai soin du dernier.

Cependant, vous pouvez suivre ce qui se passe. Par exemple 54321 il ressemble:

inttostr(54321, str); // str = ...; 
inttostr(5432, str); // str = ...; 
inttostr(543, str); // str = ...; 
inttostr(54, str); // str = ...; 
inttostr(5, str); // str = ...; 
str[0] = '5'; // str = "5..."; 
str[1] = '4'; // str = "54..."; 
str[2] = '3'; // str = "543..."; 
str[3] = '2'; // str = "5432..."; 
str[4] = '1'; // str = "54321..."; 

Notez que lorsque vous ne retournez pas de l'une des fonctions jusqu'à ce que vous écrivez le premier caractère, puis vous revenez dans l'ordre inverse des appels.

Le signe ... signifie que vous n'avez pas de terminaison NUL. Un autre problème est que vous utilisez une variable statique, donc votre code n'est pas réentrant; cela signifie qu'il casse dans certains scénarios, y compris le multi-threading.

Pour résoudre le problème de réentrée et NUL, vous pouvez faire quelque chose comme le code ci-dessous. Cela crée une fonction d'assistance et transmet l'index en cours à écrire.

void inttostr_helper(int number, char str[], int *i) 
{ 
    if (number/10) { 
     inttostr_helper(number/10, str, i); 
    }  
    str[(*i)++] = number % 10 + '0'; 
    str[*i] = '\0'; 
} 

void inttostr(int number, char str[]) 
{ 
    int i = 0; 
    inttostr_helper(number, str, &i); 
} 

EDIT: solution fixe non statique.

+0

Merci Matthew. Alors, savez-vous pourquoi il se peut que l'impression soit encore correcte bien qu'elle ne soit pas terminée? J'ai depuis ajouté la ligne 'str [i] = '\ 0';'. – alex

+2

@alex, vous avez juste "chanceux" Si vous faites un 'memset' à l'avance (par exemple' memset (str, '-', num_chars); 'il sera plus facile de voir le problème –

2

1, Votre compilateur (en particulier en mode débogage) peut avoir rempli str avec 0. Unix fait cela si vous allouer de la mémoire avec new() - Mais ne comptez pas sur ceci, mettez le premier octet à 0 ou memset tout à 0

2, papier + crayon. Dessinez une table avec chaque variable à travers le haut et le temps sur le côté. 3, écrivez d'abord la version la plus simple et la plus longue, puis soyez intelligent.

+0

Merci Martin. Pour 1, devrais-je inclure '\ 0' à la fin pour être sûr qu'il fonctionnera sur toutes les machines. Pour 2, quand vous dites * time *, voulez-vous dire le niveau de récursivité dans lequel il se trouve? – alex

+0

Je pensais que les unix avaient tendance à effacer la mémoire seulement quand elle est d'abord allouée au processus (ou utilisée comme pile).Si votre programme a déjà libéré de la mémoire (ou a été aussi profond sur la pile), même avec cette mesure en place, vous pouvez allouer de la mémoire qui n'est pas mise à zéro, mais elle contient tout ce que vous (ou la pile d'appels). Oui, vous devriez toujours écrire l'octet 0, ne vous fiez pas au comportement spécifique à l'implémentation. –

0

Avez-vous essayé de dessiner l'exécution étape par étape?

+0

Je l'ai fait, et en quelque sorte * cliqué *. Mais je pensais qu'il pourrait y avoir un meilleur moyen que cela. – alex

1
  • Je n'ai pas inclus explicitement l'octet NUL \ 0 à la fin, alors pourquoi effectue le travail des cordes fines lors de l'impression avec printf ("% s \ n", str) ;?

comment est le tableau de caractères d'origine déclarée lorsque vous appelez votre fonction la première fois? si elle est un tableau de caractères statique alors il sera rempli de 0 et cela expliquerait pourquoi il fonctionne, sinon sa juste « chance »

  • Je ne pense pas que je suis très bon à penser récursive. Lorsque je tente et pas à travers le programme dans mon esprit, je perdre la trace de ce qui est encore en attente d'exécution . Y at-il un meilleur moyen de voir ce qui se passe en interne, pour m'aider à apprendre?

honnêtement, je ne suis pas sûr si quelqu'un est bon de penser récursive :-)

+0

Je l'ai déclaré comme' char str [30] '. Est-ce que cela signifie que chacun des 30 membres aura un octet nul? Quant à la seconde, eh bien vous savez ce que je veux dire! :) – alex

+0

Cela signifie seulement que vous avez alloué de la place pour 30 octets. les valeurs sont NUL uniquement si 'str' est déclaré global ou static, mais pas si c'est un automatique. – RBerteig

2

Je suis impressionné de la créativité à utiliser récursive, malgré qu'il ne soit pas nécessaire. Je pense que le code devrait supprimer la variable i allouée statiquement parce que cette variable persistera à travers les appels. Ainsi, la deuxième fois que vous utilisez cette fonction à partir de votre code, par ex. de main(), il ne sera pas initié et sera la même valeur que celle de l'appel précédent. Je suggère d'utiliser la valeur de retour comme suit:

int inttostr(int number, char *str) { 
    int idx = 0; 
    if (number/10) { 
     idx = inttostr(number/10, str); 
    }  
    str[idx++] = number % 10 + '0'; 
    return idx; 
}