2013-05-16 3 views
5

J'ai un bloc de code:Que fait while (* p2 ++ = * p1 ++); signifier?

int main() 
{ 
    char *p1 = "Hello"; 
    char *p2; 
    p2 = (char*)malloc (20); 
    memset (p2, 0, 20); 
    while (*p2++ = *p1++); 
    printf ("%s\n", p2); 
} 

Mais je ne peux pas expliquer le fonctionnement de la ligne while (* p2 ++ = * p1 ++); Pourriez-vous me faire connaître l'ordre d'opération dans cette formule?

+1

[Comment fonctionne "while (* s ++ = * t ++)"?] (Http://stackoverflow.com/q/810129/4279) – jfs

Répondre

17

C'est du code C classique qui essaie de paraître extrêmement intelligent en mettant tout en une ligne.

while (*p2++ = *p1++); est équivalent à

strcpy(p2, p1); 
p1 += strlen(p1) + 1; 
p2 += strlen(p2) + 1; 

En d'autres termes, il copie une chaîne à zéro terminal, avec p1 se retrouvent pointant vers la fin de la chaîne de source et p2 pointant vers la fin de la chaîne de destination.

+6

+1 pour le commentaire sur la tentative de recherche intelligente. –

+5

C'est seulement * fonctionnellement * équivalent. Le code équivalent affiché a un surcoût supplémentaire. strlen() appelle itérer sur les chaînes * à nouveau *. –

+18

Ouais, ce n'est pas fou. Heck, il apparaît dans KR, et je ne pense pas que vous les accuseriez d'essayer d'être "trop ​​intelligent". Votre version est inutilement coûteuse. –

9

Ceci est une copie de chaîne, mais vous perdez la valeur du pointeur d'origine. Vous devez enregistrer la valeur du pointeur d'origine.

int main() 
{ 
    char *p1 = "Hello"; 
    char *p2 = malloc(20); 
    char *p3 = p2; 
    memset (p2, 0, 20); 
    while (*p2++ = *p1++); 
    printf ("%s\n", p3); 
} 

L'explication sémantique réelle de la boucle while serait quelque chose comme:

for (;;) { 
    char *q2 = p2;    // original p2 in q2 
    char *q1 = p1;    // original p1 in q1 
    char c = *q1;    // original *p1 in c 
    p2 += 1;     // complete post increment of p2 
    p1 += 1;     // complete post increment of p1 
    *q2 = c;     // copy character *q1 into *q2 
    if (c) continue;   // continue if c is not 0 
    break;      // otherwise loop ends 
} 

L'ordre q1 et q2 sont sauvés, et l'ordre p2 et p1 sont incrémentée peuvent être interchangés. La sauvegarde de *q1 à c peut se produire à tout moment après l'enregistrement de q1. L'affectation de c à *q2 peut se produire à tout moment après l'enregistrement de c. Au dos de mon enveloppe, cela donne au moins 40 interprétations différentes.

+0

Le code original a également créé une fuite de mémoire. – Lundin

+0

@Lundin: Il n'a pas 'free()' la mémoire allouée, mais le programme est en cours de fermeture. Ce n'est qu'une fuite dans les outils de débogage de la mémoire. Mais, oui, généralement, chaque 'malloc()' (ou similaire) devrait être accompagné d'un 'free()'. Le code d'origine rend difficile la récupération du pointeur d'origine. – jxh

+1

Ce comportement n'est pas indéfini, à cause du 'memset'. Le 'printf' dans le programme d'origine est garanti pour imprimer juste' \ n'. – user9876

1

La boucle while évalue l'expression: *p2++ = *p1++. L'expression de boucle while:
*p2 = *p1 est évaluée en utilisant le résultat de *p1. Toutefois, cette valeur est toujours affectée à *p2 même si l'expression est évaluée comme false ou (0). Réécriture ceci:

char c; 

do 
{ 
    c = *p1; /* read the src byte */ 
    *p2 = c; /* write to dst byte */ 

    p2++, p1++; /* increment src, dst pointers */ 
} 
while (c != 0); 

Vous remarquerez qu'une lecture/écriture se produira au moins une fois. C'est OK, tant que la chaîne C p1 est terminée par zéro, et p2 a suffisamment de stockage pour la chaîne C. Cela signifie que malloc doit allouer au moins strlen(p1) + 1 octets. Dans ce code fourni, c'est vrai.

Comme d'autres l'ont souligné, la dernière itération quittera p1 à une adresse un past-the-end, qui est encore un pointeur valide, mais a non défini des résultats quand déréférencé. L'adresse de p2 est à la fois un pointeur valide et un déréférencement valide, puisque vous allouez 20 octets. Cependant, p2 ne pointe plus vers la copie de chaîne C.Ce que vous voulez est un équivalent à:

char *p1 = "Hello"; 
char *p2, *tmp; 

p2 = (char*)malloc (20); 
memset (p2, 0, 20); 

tmp = p2; 
while (*tmp++ = *p1++); 

printf ("%s\n", p2); 

La plupart des systèmes d'exploitation libérera la mémoire à p2 à la sortie de main, mais il est bon de se détendre les ressources avec un appel correspondant à:

free(p2); 

à la fin. Alors que sur le sujet de la bonne pratique, vous devriez également vérifier la valeur de retour de malloc pour s'assurer que l'allocation a réussi.

+1

'p2' * fait * pointer vers une chaîne C terminée par nul. C'est parce que le tampon 'p2' a été alloué plus grand que' p1', et a été mis à zéro avec 'memset' – user9876

Questions connexes