11
votes

Comment fonctionne ce code C?

Je cherchais le code suivant dans lequel je suis tombé sur l'impression d'une chaîne dans l'ordre inverse en C Utilisation de la récursion: xxx

Je suis relativement nouveau à C et suis confus confus par la ligne 2. < Code> * STR de ma compréhension est la désérogation du pointeur et doit renvoyer la valeur de la chaîne dans la position actuelle. Mais comment cela est-il utilisé comme argument à une déclaration conditionnelle (qui devrait sauf un droit booléen?)? Dans la ligne 3, le pointeur sera toujours incrémenté au bloc suivant (4 octets depuis son int) ... Donc, ce code n'a donc pas pu échouer s'il s'agit d'être des données dans le prochain bloc de mémoire après la fin de la chaîne?

mise à jour : il n'y a donc pas de types booléens en C correct? Une instruction conditionnelle évalue à «Faux» si la valeur est 0 et «vrai» sinon?


4 commentaires

Garçon, j'espère que ce n'était pas du code de production! :)


Je me demande combien de temps une chaîne devrait être avant d'exploser.


Probablement très long ... mais toujours.


À l'origine, la méthode s'appelait strations4, donc éventuellement qu'elle était destinée aux chaînes d'environ 4 caractères de longueur.


10 Réponses :


38
votes

Ligne 2 vérifie pour voir si le caractère actuel est la terminaison nulle de la chaîne - puisque les chaînes C sont terminés nulle, et le caractère nul est considéré comme une valeur fausse, il va commencer à dérouler la récursion quand il arrive à la fin de la chaîne (au lieu d'essayer d'appeler StrReverse4 sur le caractère après la terminaison nul, ce qui serait au-delà des limites des données valides).

notez également que le pointeur est à un char code>, incrémenter ainsi le pointeur seulement par incréments de 1 octet (depuis char code> est un type à un seul octet) p>

Exemple:. p>

 0  1  2  3
+--+--+--+--+
|f |o |o |\0|
+--+--+--+--+


11 commentaires

merci pour la réponse..mais comme mentionné dans ma mise à jour..Can vous élaborer sur ce que l'on entend par 'NULL est considéré comme une fausse valeur "car c n'a pas de type booléen?


Dans C, il n'y a pas de type booléen, correct - à la place, des instructions logiques traitent simplement tout ce qui évalue à 0 comme false , et tout ce qui évalue à non zéro comme vrai . Étant donné que le "caractère null" est en réalité un octet dont la valeur est 0, elle est considérée comme false .


Critique mineure - Le caractère ASCII 0 est généralement appelé "nul" (avec un L), tandis que le pointeur non valide est généralement appelé "null" (avec deux L). Je pense qu'il est important de faire une distinction ici, car il est facile de se perdre avec des pointeurs.


"NUL" est une forme abrégée, oui, toutefois, lorsque vous parlez de "Terminator NULL" ou "NULL PARTICULIER" Habituellement, l'orthographe correcte (avec deux L) est utilisée. Par exemple: EN.Wikipedia.org/wiki/Null_Character


Wikipedia, par nos termes propres (WP), ne doit pas être utilisé pour des références. Dans le cas de cet article Null_Caracter, il s'agit d'une source de désinformation. Utilisateur: Heathhunnicutt


Je n'ai rien vu sur Wikipedia qui indique que tel est plus que votre propre opinion, Heath.


@Heath - Wikipedia n'est pas d'accord avec vous: en.wikipedia.org/wiki/wikipedia_Talk:Citing_wikipediaIa


@Amber: En C, il y a un type booléen, _bool .


@DeMlax: seulement en C99 et plus tard.


@Amber: C99 est C, tout autant que C ++ 98 (ou peut-être même C ++ 03) est C ++.


@DeMlax: Oui, C99 est C, mais C n'est pas nécessairement C99.



2
votes

à la fin d'une chaîne est typiquement un 0 octet - la ligne si (* str) vérifie l'existence de cet octet et d'arrêter quand il y arrivera.


0 commentaires

3
votes

Le type d'une chaîne C n'est rien d'autre qu'un pointeur à char. La convention est que ce que le pointeur pointe vers un tableau de caractères, terminé par un octet zéro .

* str est donc le premier caractère de la chaîne pointée par str .

Utilisation de * STR dans un état conditionnel est évalué vers FALSE si STR pointe vers l'octet NULL terminant dans la chaîne (vide).


0 commentaires

1
votes

à la fin de la chaîne, il y a un 0 - vous avez donc "test" => [0] 'T' [1] '[2]' [2] [3] 'T' [4] 0

et si (0) -> faux

De cette façon, cela fonctionnera.


0 commentaires

1
votes

dans la ligne 3, le pointeur sera toujours incrémenté au bloc suivant (4 octets depuis son int) ... p>

thats faux, c'est char *, il ne sera incrémenté que par 1. Parce que le caractère est de 1 octet seulement. P>

Mais comment cela est-il utilisé comme argument à une déclaration conditionnelle (qui devrait sauf un droit booléen?)? p> BlockQuote>

Vous pouvez utiliser n'importe quelle valeur si ($$) à $$, et il ne vérifiera que si son non-zéro ou non, Bool est également implémenté comme simple 1 = true et 0 = faux. P>

Dans un autre niveau supérieur, une langue fermement tapé, vous ne pouvez pas utiliser de telles choses si, mais en C, tout se résume à des chiffres. Et vous pouvez utiliser n'importe quoi. P>

if(1) // evaluates to true 
if("string")  // evaluates to true
if(0) // evaulates to false

0 commentaires

0
votes

Déclarations conditionnelles ( si , pour , tandis que , etc.) attend une expression booléenne. Si vous fournissez une valeur entière, l'évaluation revient à 0 == false ou non-0 == true . Comme mentionné déjà, le caractère de terminaison d'une chaîne C est un octet nul (valeur entière 0). Donc le si échouera à la fin de la chaîne (ou premier octet nul dans la chaîne).

comme un de côté, si vous faites * str sur un pointeur NULL, vous invoquez un comportement indéfini; Vous devez toujours vérifier qu'un pointeur est valide avant la déséroférance.


0 commentaires

0
votes

1.

str est un pointeur à un char. L'incrémentation str rendra le point de pointeur sur le second caractère de la chaîne (car il s'agit d'un tableau de caractères). Remarque: L'incrémentation des pointeurs augmentera par le type de données que le pointeur pointe sur.

pour ex: xxx

2. xxx

L'expression est évaluée et si la valeur résultante est zéro ( null , \ 0 , 0 ), les déclarations ne sont pas exécutés. Comme chaque chaîne se termine par \ 0 la récursion devra terminer un certain temps.


3 commentaires

Toutes les chaînes ne sont pas garanties dans un octet nul. En effet, la plupart des temps c'est; Mais il n'y a rien qui nécessite que cela soit le cas.


Se mettre d'accord. Je viens de supposer cela dans ce cas.


@ezpz: une c-string est par définition une séquence de caractères terminée par un caractère null; S'il n'y a pas de null de terminaison, ce n'est tout simplement pas une c-string;)



0
votes

c n'a pas de concept de valeurs booléennes: en C, chaque type scalaire (c.-à-d. Types d'arithmétique et de pointeur) peut être utilisé dans des contextes booléens où 0 signifie false et non -zero true .

Comme les chaînes sont désormais terminées, le terminateur sera interprété comme faux , alors que tous les autres caractères (avec une valeur non nulle!) vrai . Cela signifie qu'il existe un moyen facile d'itération sur les caractères d'une chaîne: xxx

stréscript4 () fait la même chose, mais par récursivité à la place d'itération.


0 commentaires

0
votes

Essayez ce code, qui est aussi simple que celui que vous utilisez: xxx


0 commentaires

0
votes

C'est une sorte de sujet hors sujet, mais quand j'ai vu la question que je me demandais immédiatement si cela était réellement plus rapide que de faire une linge et de faire partner à l'arrière.

Donc, j'ai fait un petit test. P>

#include <string.h>

void reverse1(const char* str)
{
    int total = 0;
    if (*str) {
            reverse1(str+1);
            total += *str;
    }
}

void reverse2(const char* str)
{
    int total = 0;
    size_t t = strlen(str);
    while (t > 0) {
            total += str[--t];
    }
}

int main()
{
    const char* str = "here I put a very long string ...";

    int i=99999;

    while (--i > 0) reverseX(str);
}


2 commentaires

C'est probablement parce que dans votre système la surcharge des appels de fonction récursive est grand par rapport au temps de la solution itérative, ce qui rend un appel de fonction à Strlen. Mon point principal consiste à faire des généralisations d'un test peut être dangereux, si vous êtes intéressant. Il vaut mieux écrire clairement le code maintenu et l'optimiser ultérieurement, uniquement lorsque les tests de performance réels révèlent des goulots d'étranglement.


Je suis d'accord. Bien que, comme Strlen scanne linéairement (je suppose) toute la chaîne, la comparaison doit être comprise entre une seule fonction (la récursion) et une itération de la mise en œuvre de SHLEN plus 1 / N de l'appel de la fonction Strlen. Mais encore une fois, je suis d'accord pour dire qu'il est préférable d'écrire clair, puis d'optimiser si nécessaire. Une question, dans votre système, il est plus rapide de la solution de récursion plutôt que la solution itérative?