2013-05-16 12 views
5

Ho un blocco di codice:Cosa succede mentre (* p2 ++ = * p1 ++); significare?

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

ma non riesco a spiegare il funzionamento della linea while (* p2 ++ = * p1 ++); Potrebbe farmi sapere l'ordine di funzionamento in questa formula?

+1

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

risposta

17

È il classico codice C che cerca di sembrare estremamente intelligente mettendo tutto in una riga.

while (*p2++ = *p1++); è equivalente a

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

In altre parole, esso copia una stringa con terminazione null, con p1 finiscono indicando la fine della stringa sorgente e p2 punta alla fine della stringa di destinazione.

+6

+1 per il commento sul tentativo di sembrare intelligente. –

+5

È solo * funzionalmente * equivalente. Il codice equivalente pubblicato ha un sovraccarico aggiuntivo. strlen() chiama iterate sulle stringhe * di nuovo *. –

+18

Sì, questo non è niente di pazzo. Diamine, appare in KR, e non credo che le accuseresti di cercare di essere "troppo intelligente". La tua versione è inutilmente costosa. –

9

Questa è una copia di stringa, ma si sta perdendo il valore del puntatore originale. Dovresti salvare il valore del puntatore originale.

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

La spiegazione reale semantica del ciclo while sarebbe qualcosa di simile:

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'ordine che q1 e q2 sono salvati, e l'ordine che p2 e p1 vengono incrementati possono essere scambiati. Il salvataggio di *q1 a c può verificarsi in qualsiasi momento dopo il salvataggio di q1. L'assegnazione di c a *q2 può verificarsi in qualsiasi momento dopo il salvataggio di c. Sul retro della mia busta, questo risolve almeno 40 diverse interpretazioni.

+0

Il codice originale ha anche creato una perdita di memoria. – Lundin

+0

@Lundin: non ha '' libero() 'la memoria che è stata allocata, ma il programma sta uscendo. È solo una perdita di strumenti di debug della memoria. Ma, sì, in generale, ogni 'malloc()' (o simile) dovrebbe essere accompagnato da un 'free()'. Il codice originale rende difficile il recupero del puntatore originale. – jxh

+1

Non è un comportamento non definito, a causa di 'memset'. Il 'printf' nel programma originale è garantito per stampare solo' \ n'. – user9876

1

Il ciclo while sta valutando l'espressione: *p2++ = *p1++. L'espressione del ciclo while:
*p2 = *p1 viene valutata utilizzando il risultato di *p1. Tuttavia, questo valore è ancora assegnato a *p2 anche se l'espressione viene valutata come false o (0). Riscrittura questo:

char c; 

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

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

si noterà che una lettura/scrittura avverrà almeno una volta. Va bene, purché la stringa C p1 non sia terminata e p2 disponga di spazio sufficiente per la stringa C. Cioè, malloc dovrebbe allocare almeno strlen(p1) + 1 byte. In questo codice fornito, questo è vero.

Come altri hanno sottolineato, l'iterazione finale lascerà p1 ad un indirizzo uno past the end, che è ancora un puntatore valido, ma è indefinito risultati quando dereferenziati. L'indirizzo di p2 è sia un puntatore valido, sia un dereferenziamento valido, dal momento che stai allocando 20 byte. Tuttavia, p2 non punta più alla copia della stringa C.Ciò che si vuole è un equivalente a:

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

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

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

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

maggior parte dei sistemi operativi rilascerà la memoria a p2 all'uscita dal main, ma è buona pratica per rilassarsi le risorse con una corrispondente chiamata a:

free(p2); 

alla fine. In merito alle buone pratiche, dovresti anche controllare il valore di ritorno di malloc per assicurarti che l'assegnazione abbia avuto successo.

+1

'p2' * fa * punta a una stringa C nul-terminata. Questo perché il buffer 'p2' è stato allocato più grande di' p1', ed è stato azzerato con 'memset' – user9876

Problemi correlati