2013-05-16 11 views
5

Mam bloku kodu:Co robi podczas (* p2 ++ = * p1 ++); oznaczać?

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

Ale nie mogę wyjaśnić funkcjonowanie linii chwilę (* = * p2 p1 ++ ++); Czy możesz podać mi kolejność operacji w tej formule?

+1

[Jak działa "while (* s ++ = * t ++)"? (Http://stackoverflow.com/q/810129/4279) – jfs

Odpowiedz

17

To klasyczny kod C, który wygląda niezwykle sprytnie, umieszczając wszystko w jednym wierszu.

while (*p2++ = *p1++); jest równoważna

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

Innymi słowy, to kopiuje łańcuch znaków zakończony zerem, z p1 kończąc wskazując na końcu łańcucha źródłowego i p2 wskazując na końcu łańcucha docelowego.

+6

+1 za komentarz o tym, jak wyglądać sprytnie. –

+5

To tylko * funkcjonalnie * ekwiwalent. Odpowiednik opublikowanego kodu ma dodatkowy narzut. Strlen() wywołuje iterację po ciągach * ponownie *. –

+18

Tak, to nie jest szalone. Heck, pojawia się w KR i nie sądzę, żebyś oskarżył ich o bycie "zbyt sprytnymi". Twoja wersja jest niepotrzebnie droga. –

9

To jest kopia napisowa, ale tracisz oryginalną wartość wskaźnika. Powinieneś zapisać oryginalną wartość wskaźnika.

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

Rzeczywista semantyczne wyjaśnienie pętli while byłoby coś takiego:

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 
} 

kolejności, q1 i q2 są zbawieni, i aby p2 i p1 są zwiększane może być zamienione. Zapisanie *q1 do c może nastąpić w dowolnym momencie po zapisaniu q1. Przypisanie od c do *q2 może nastąpić w dowolnym momencie po zapisaniu c. Z tyłu mojej koperty działa co najmniej 40 różnych interpretacji.

+0

Oryginalny kod również spowodował wyciek pamięci. – Lundin

+0

@Lundin: To nie 'wolna()' pamięć, która została przydzielona, ​​ale program jest zamykany. To tylko przeciek do narzędzi do debugowania pamięci. Ale tak, ogólnie rzecz biorąc, każdej "malloc()" (lub podobnej) powinno towarzyszyć 'free()'. Oryginalny kod utrudnia odzyskanie oryginalnego wskaźnika. – jxh

+1

To nie jest niezdefiniowane zachowanie, ze względu na 'memset'. "Printf" w oryginalnym programie gwarantuje wydrukowanie tylko '\ n'. – user9876

1

Pętla while oblicza wyrażenie: *p2++ = *p1++. Wyrażenie pętli while:
*p2 = *p1 jest oceniane przy użyciu wyniku *p1. Jednak ta wartość jest nadal przypisana do *p2, nawet jeśli wyrażenie jest oceniane jako false lub (0). Przepisanie to:

char c; 

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

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

Można zauważyć, że do odczytu/zapisu nastąpi przynajmniej raz. To jest w porządku, o ile ciąg C p1 jest zakończony nulem, a p2 ma wystarczającą ilość miejsca na ciąg C. To znaczy, malloc powinien przydzielić co najmniej strlen(p1) + 1 bajtów. Podany kod jest prawdziwy.

Jak inni zwrócili uwagę, ostateczna iteracja pozostawi p1 pod adresem jeden-past-the-end, który jest nadal ważny wskaźnik, ale niezdefiniowane wyniki podczas dereferencjonowane. Adres p2 jest prawidłowym wskaźnikiem i prawidłowym dereferencją, ponieważ przydzielasz 20 bajtów. Jednakże, p2 nie wskazuje już na ciąg znaków C.Co chcesz jest odpowiednikiem:

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

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

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

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

większości systemów operacyjnych będzie zwolnić pamięć w p2 na wyjściu z main, ale jest to dobra praktyka, aby rozwijać zasoby z odpowiednim wywołaniu:

free(p2); 

na końcu. W kwestii dobrych praktyk powinieneś również sprawdzić wartość zwracaną przez malloc, aby upewnić się, że alokacja się powiedzie.

+1

'p2' * ma * wskazuje na zakończony znakiem N ciąg C. Wynika to z faktu, że bufor 'p2' został przydzielony jako większy niż' p1' i został wyzerowany za pomocą 'memset' – user9876