0
votes

Comment fonctionne ce Strcat basé sur le pointeur en C?

J'ai une certaine expérience de base avec C, mais cela me prend habituellement un peu pour savoir comment mettre en œuvre quelque chose; Utiliser des pointeurs et tel est toujours un mystère pour moi.

Puis je vois un exemple comme strcat Mise en œuvre et je ne peux pas suivre. Est-ce que quelqu'un m'inquiéter de bien vouloir expliquer cela à un nouveau venu C? xxx

Quand je lis cela, je pense "rdest =?, Peut-être une vraie destination". Donc, définissez un pointeur sur la destination d'origine. Alors "alors que (* Dest) Dest ++;", qu'est-ce que cela fait? Même avec la ligne suivante. Je ne suis pas suivi.

utilise-t-il une mémoire supplémentaire pour les deux parties d'origine (SRC et DEST)? Comme dans JS, si vous concatéez 2 chaînes, il crée une mémoire pour une troisième chaîne qui allie les deux, de sorte que vous disposez de la mémoire. Comment cela est-il évité dans la présente implémentation C (si elle est)?


1 commentaires

Tout comme indice: rdest est probablement de " destination de retour ".


6 Réponses :


2
votes
char * my_strcat(char *dest, const char *src)
{
    // Standard dictates strcat() to return dest.
    // That is pretty useless (returning a pointer to the
    // *end* of dest would have been better), but that's
    // the way it is.
    // Since we iterate dest as part of the implementation,
    // we need to "remember" its original value.
    char *rdest = dest;

    // Iterate over the characters pointed to by dest until
    // we found the end (null byte terminator), which is "false"    
    while (*dest)
      dest++;

    // An assignment evaluates to the value assigned. So assigning
    // one character at a time (*dest = *src) will eventually
    // evaluate to false when we assigned the null byte terminator
    // from src (incidentially also terminating dest). Since we
    // postfix-increment both pointers during the assignment, we
    // don't need any actual body for the loop.
    while (*dest++ = *src++)
      ;

    // Return the "remembered" original dest value.
    return rdest;
}

  Is it using any additional memory to the original two parts (src and dest)? Like in JS, if you concatenate 2 strings, it creates memory for a third string that combines the two, so you have double the memory. How is this avoided in this C implementation (if it is)?
A precondition for strcat is that dest must have enough space to hold the end result. So, no, it does not need / assign additional memory. It is up to you to make sure there is enough memory, or realloc more memory before you call strcat.

8 commentaires

Pouvez-vous expliquer tandis que (* devt) en plus de profondeur, même avec tandis que (* dev ++ = * src ++) ? Comment fonctionne le pointeur-ness dans cette situation? Qu'est-ce que (* Dest) obtient, pourquoi pas tandis que (DEST) ? etc.


@LANCEPOLLARD Vous devez dréerference les pointeurs pour copier les valeurs de caractère lui-même. tandis que (DEST) n'évaluerait toujours à true si Dest n'est pas un pointeur null. Lorsque vous utilisez Dest , vous utilisez la valeur du pointeur (en réalité une adresse), pas la valeur à l'intérieur de l'objet référencé.


@LANCEPOLLARD: DEST est l'adresse indiquée sur, *t est la valeur , c'est-à-dire le caractère dans ce cas, à l'adresse indiquée à l'adresse. Une chaîne C est terminée par un octet zéro ( '\ 0' ). Zéro est faux, tout n'est pas zéro est vrai. Donc, tandis que (* devt) sera en boucle jusqu'à la fin de la terminaison de zéro octet. Nous vérifions le caractère , pas l'adresse.


@LancePollard: Commentaires étendus.


Tout comme un côté, ce code montre clairement pourquoi il faut faire attention à cette fonction (et d'autres) qui copiez des données d'une adresse source à une adresse de destination. Aucune taille n'est passée, la fonction suppose donc selon C standard 2 choses: 1- src se termine dans un octet zéro, 2- DEST a suffisamment d'espace pour maintenir la copie Les données. Si l'une de ces conditions ne tiennent pas vraie, nous avons un «comportement indéfini»


@DNT: Cela s'appelle "conditions préalables". Par exemple, si src ne se termine pas dans un octet zéro, ce n'est pas une chaîne, et strcat suppose que src et dest faire point sur les chaînes. C'est (par nécessité) une hypothèse fondamentale de C que le programmeur sait ce qu'il fait, a vérifié les conditions préalables et fait toute la comptabilité nécessaire. Il existe d'autres fonctions telles que strncpy , strcpy_s ou strncpy_s qui vérifie plus.


@Devsolar Oui, ce sont des conditions préalables, mais elles ne sont pas appliquées de quelque manière que ce soit dans les fonctions de nombreuses bonnes raisons, à l'exception des versions mises en œuvre dans certaines bibliothèques de débogage. Étant donné qu'une «connaissance de base de C» a été mentionnée, j'ai décidé d'ajouter un petit commentaire latéral pour les indiquer.


@DNT: C'est ce que je voulais souligner aussi. En règle générale, c ne "cocher" pas ou "appliquer" des conditions préalables. La plupart du temps, vous ne pouvez pas vérifier les conditions préalables (du point de vue d'une bibliothèque implémenteur). Ce n'est que la façon dont cette langue roule et une partie de la raison pour laquelle cela fonctionne si bien dans des applications intégrées dans lesquelles chaque octet et chaque cycle d'horloge compte.



1
votes

const char * src code> p>

src code> ne doit pas être modifié par la fonction, utilisez donc conscarness em> pour le marquer comme lecture seule. P>

Char * rdest = Dest; code> p> blockQuote>

Enregistrez la position d'origine jusqu'à plus tard, car il y a une exigence que strcat code> doit renvoyer un pointeur sur le premier élément de la chaîne fusionnée ( renvoyer le rdest; code> ). P>

tandis que (* DEST) code>
devt ++; code> p> BlockQuote>

La boucle tandis que la boucle est implicitement à la recherche du terminateur null. Signification: Trouvez la fin de la première chaîne, de sorte qu'après cette boucle, DEST code> points sur le terminateur NULL de cette chaîne. P>

tandis que (* dev ++ = * src ++) code> p> blockQuote>

Ceci est un idiome commun, bien que cerf confondant, dans C. (il implémente réellement Strcpy code> dans cette ligne.) Prédication de l'opérateur dit Postfix ++ code> prend PRECÉDANCE SUR PREFIX * CODE> ARGISSEMENT = CODE>. P>

Donc, d'abord, chaque pointeur est évalué et ++ est appliqué aux pointeurs, pas à la base des données pointues. Mais puisqu'il s'agit de postfix, l'incrément de l'adresse du pointeur ne se produit pas jusqu'à la fin de l'expression. P>

* code> prend le contenu de chaque pointeur avant cet incrément, puis = code> copie le contenu de * src code> à *t code>. Encore une fois, cela se produit avant que les adresses ne soient incrémentées. P>

Enfin, il existe une vérification implicite contre la résiliation null, car le résultat de l'opérande = code> peut être vérifié - il est équivalent à son opérande gauche, dans ce cas * des code>. Et notez que le terminateur nul est aussi copié, aussi. P>

Vous pouvez réécrire cela tandis que la boucle de manière moins déroutante: p>

*dst = *src;
while(*src != '\0')
{
  dst++;
  src++;
  *dst = *src;
}


2 commentaires

La Constenness de src Lorsque vous renvoyez lancera un avertissement " Retourner le qualificatif" const "de type cible de pointeur [-WERROR = qualificatifs abandonnés] ". Peut-être la peine de noter à côté.


@ROBERTSSSUPORTSMonicAcellio Code avait une faute de frappe, fixe.



1
votes

La chose cruciale à comprendre dans ce code est la manière C traite des chaînes (une gamme de caractères terminés par '\ 0' ). La première chose à faire est de saisir l'analogie à une chaîne comme un mot et de penser à une base de valeur par rapport à la valeur.

L'argument Desser de la fonction représente le pointeur à la premier caractère de la chaîne de destination. Pour ajouter plus de caractères après la chaîne DEST , nous devons accéder à son '\ 0' Terminator, car c'est là que la deuxième chaîne atterrira. C'est le but de cette boucle: xxx

( (* doss) La condition est équivalente à (* "DEST! = '\ 0' ) , parce que '\ 0' a une valeur numérique de 0 , ce qui est équivalent à false ) < P> Après avoir obtenu la position où la deuxième chaîne doit commencer, nous commençons à la copier du caractère par caractère: xxx

Notez que (* devt ++ = * src ++) a un seul caractère "=", ce qui signifie qu'il s'agit d'une affectation , pas une comparaison. La valeur testée à l'intérieur des parenthèses est la chose à laquelle vous obtenez assigné, c'est-à-dire * src . Donc, il continuera tant que (* src! = '\ 0') , ce qui se trouve où la deuxième chaîne se termine. Notez également que le caractère '\ 0' est également copié dans ces affectations, ce qui est un must absolu, car sans cela, la chaîne résultante ne serait pas terminée (ainsi, techniquement, cela ne serait même pas possible. Soyez une chaîne valide).

Great, maintenant que nous avons copié la chaîne où elle doit être, nous devons retourner le pointeur sur le premier personnage. Ah, mais nous avons déplacé le pointeur dans la première boucle! C'est là que rdest est entré en place, sauvegarde la position initiale avant les boucles afin que nous puissions le retourner à la fin. J'espère que cela aide.


0 commentaires

1
votes

Commençons par la déclaration de fonction:

return rdest;


0 commentaires

0
votes

Si vous changez un peu le rôle du RDest sera plus clair

char * my_strcat_s(char *dest, const char *src)
{
    size_t destlen = strlen(dest);
    char *workdest = malloc(destlen + strlen(src) + 1);

    if(workdest)
    {
        strcpy(workdest, dest);
        strcpy(workdest + destlen, src);
    }
    return workdest;
}


0 commentaires

0
votes

chaîne code> est juste un tableau (tampon) de char code> s. Fondamentalement, un tableau de 8 bits non signé INT code> s. Et le dernier élément de la matrice est '\ 0' code>. La matrice réelle peut être beaucoup plus grande que la chaîne l'occupant, et Strcat exige en effet que le Dest le code> soit suffisamment important pour contenir à la fois la chaîne DEST code> et la source cordes ensemble. Strcat code> n'est pas une méthode prête à utiliser comme dans les langages de niveau supérieur. C'est un cas d'utilisation ressemble à ceci:

  1. Char * tampon = MALLOC (Strlen (Stren1) + Strlen (String2) +1) Code> Créer un tampon suffisamment grand pour contenir les deux chaînes. LI>
  2. STRY (tampon, string1) code> copie la première chaîne au tampon li>
  3. strcat (tampon, stress2) code> Ajoute la deuxième chaîne sur le tampon où la première chaîne se termine. Li> ol>

    ++ code> et - code> Les opérateurs permettent à un pointeur de servir d'énumérateur. Pensez à ceux comme .nSuext () code> et .prev () code>. La mise en garde ici est qu'elles reviennent (ou acceptent) la valeur avant de déplacer l'énumérateur. Ceci est essentiel ici, c'est essentiellement ce qui permet à C d'être si difficile;) Si vous souhaitez recréer cela en niveau supérieur, ce sera getandnext () code> et SetAndreNext () Code> P>

    * code> est un accesseur, qui travaille à la fois, il s'agit donc d'un getvalue () d'énumérateur () code> et setValue () code>. P>

    Le premier bloc passe simplement le tampon Dest code> jusqu'à ce qu'il atteigne la fin de la chaîne dedans - mais pas la fin du tampon. p> xxx Pré>

    en pseudo-code est: p> xxx pré>

    c'est parce que \ 0 code> est un véritable zéro dans le int code > Signification, et INT zéro est faux code> au sens booléen. Tout ce qui n'est pas zéro est vrai code>. Cela signifie -1, 42 et 'a' code> sont tout aussi true code> comme 1. donc en C, nous ne pouvons pas passer le ! = 0 code> qui est ce qui est Aussi inutile que l'écriture ! = faux code> dans une langue qui a de vrais booléens. p> xxx pré>

    peut être retraité comme suit: p>

    char value;
    do
    {
        dest.set(src.get());
        value = src.get();
        src.next();
        dst.next();
    }
    while (value != '\0');
    


0 commentaires