Je suis un peu nouveau dans C
et je m'interroge sur certaines choses concernant l'allocation de mémoire. Ma fonction est la suivante:
size_t count_nwords(const char* str) { //char* copied_str = strdup(str); // because 'strtok()' alters the string it passes through char copied_str[strlen(str)]; strcpy(copied_str, str); size_t count = 1; strtok(copied_str, " "); while(strtok(NULL, " ") != 0) { count++; } //free(copied_str); return count; }
Cette fonction compte le nombre de mots dans une chaîne (le délimiteur est un espace, c'est-à-dire "
"). Je ne veux pas que la chaîne passée en argument soit modifiée.
J'ai deux questions:
strdup ()
(qui est la partie commentée dans le code) doit-elle être préférée à la méthode strcpy ()
? Je crois comprendre que strcpy ()
est suffisant et plus rapide, mais je ne suis pas certain. size_t
à renvoyer (c'est une variable locale), faut-il le faire pour s'assurer que la fonction est robuste? Ou est-ce que l'utilisation de size_t nwords = count_nwords (copied_input);
est totalement sûre et obtiendra toujours correctement la valeur renvoyée? Merci!
EDIT: J'ai accepté la seule réponse qui concernait précisément mes questions, mais je vous conseille de lire les autres réponses car elles fournissent de bonnes informations sur les erreurs que j'ai faites dans mon code.
3 Réponses :
- La méthode strdup () (qui est la partie commentée dans le code) doit-elle être préférée à celle de strcpy ()? Ma compréhension est que strcpy () est suffisant et plus rapide, mais je ne suis pas certain.
Votre solution est propre et fonctionne bien, alors ne vous inquiétez pas. Le seul point est que vous utilisez VLA qui est maintenant facultatif, alors utiliser strdup
serait moins courant. En ce qui concerne les performances, comme il n'est pas spécifié comment les VLA sont implémentés, les performances peuvent varier d'un compilateur / plateforme à un compilateur / plateforme (gcc est connu pour utiliser stack pour les VLA mais tout autre compilateur peut utiliser le tas). Nous savons seulement que strdup
alloue sur le tas, c'est tout. Je doute que le problème de performances provienne d'un tel choix.
Remarque: la taille de votre allocation est erronée et doit être au moins strlen (str) +1
.
- Puisqu'aucune mémoire n'est allouée pour la valeur size_t à renvoyer (c'est une variable locale), si cela doit être fait afin de garantir le la fonction est robuste? Ou utilise size_t nwords = count_nwords (copied_input); complètement sûr et sera toujours correctement obtenir la valeur renvoyée?
La gestion des valeurs de retour et de la mémoire appropriée est une préoccupation du compilateur. Habituellement, ces valeurs sont transférées sur / depuis la pile (avoir une certaine lecture sur "pile frame"). Comme vous pouvez vous en douter, de l'espace est alloué sur la pile juste avant l'appel et est désalloué après l'appel (dès que vous supprimez ou copiez la valeur retournée).
Comme vous vous en doutez, de l'espace est alloué sur la pile juste avant l'appel et est désalloué après l'appel (dès que vous supprimez ou copiez la valeur retournée).
: J'ai du mal à comprendre cette. Vous dites que size_t nwords = count_nwords (copied_input);
conservera correctement la référence à la valeur puisque l'appel à la fonction est immédiatement affecté à une autre variable locale?
Il est difficile de tout expliquer ici, mais à peu près oui (vous avez raison). Renseignez-vous sur l'appel de fonction et le cadre de pile pour le comprendre en détail.
Auriez-vous une ressource / un lien préféré vers lequel me diriger? :)
L'entrée @payne "Call stack" de wikipedia est une bonne introduction.
Impossible de tenir compte du caractère nul
size_t count_nwords(const char* str) { size_t count = 0; while (*str) { while (isspace((unsigned char) *str)) { str++; } if (*str) { count++; while (!isspace((unsigned char) *str) && *str) { str++; } } } return count; }
Mauvais algorithme
Même avec le correctif ci-dessus, le code renvoie 1 avec count_nwords ("")
Copie inutile de la chaîne
strtok ()
n'est pas nécessaire ici. Une copie de la chaîne n'est pas nécessaire.
Alternative: parcourir la chaîne.
// char copied_str[strlen(str)]; char copied_str[strlen(str) + 1]; strcpy(copied_str, str);
Une autre option est l'approche state-loop où vous bouclez continuellement sur chaque caractère en gardant une trace de l'état de votre compte avec un simple drapeau. (vous êtes soit dans un mot lisant des caractères, soit vous lisez des espaces). L'avantage étant que vous n'avez qu'une seule boucle impliquée. Un petit exemple serait:
size_t count_words (const char *str) { size_t words = 0; int in_word = 0; while (*str) { if (isspace ((unsigned char)*str)) in_word = 0; else { if (!in_word) words++; in_word = 1; } str++; } return words; }
Il vaut la peine de comprendre toutes les techniques. isspace
nécessite l'inclusion de ctype.h
.
char copied_str [strlen (str)];
doit êtrechar copied_str [strlen (str) +1];
puisque vous avez également besoin d'espace pour le nul-terminator.Notez que vous pouvez également simplement itérer directement la chaîne et compter le nombre d'espaces sans utiliser
strtok
, ce qui sera plus rapide que l'une ou l'autre option.Brève comparaison:
strcpy (s_string, f_string)
==while (* s_string ++ = * f_string ++)
, etstrdup ()
==s_string = malloc (strlen (f_string) +1); strcpy (s_string, f_string);
Le code est incorrect sur le plan algorithmique.
count_nwords ("")
tente de renvoyer 1, mais il n'y a pas de "mots".