2013-07-29 9 views
10

czytałem nowy (maj 2013), O'Reilly książki „zrozumieniu i wykorzystaniu C Wskaźniki” Richard Reese, i mam pytanie dotyczące jakiegoś kodu w nim, na stronie 87.Używanie oryginalnego wskaźnika po ponownym przydziale?

if (++length > maximumLength) { 
    char *newBuffer = realloc (buffer, maximumLength += sizeIncrement); 

    if (newBuffer == NULL) { 
     free (buffer); 
     return NULL; 
    } 

    currentPosition = newBuffer + (currentPosition - buffer); 
    buffer = newBuffer; 
} 

Mam nadzieję nazwy zmienne są oczywiste; jeśli potrzebny jest kontekst, będę edytować, aby dostarczyć cały fragment kodu, a nie tylko ten fragment.

Moje pytanie dotyczy linii currentPosition = newBuffer + (currentPosition - buffer);. Moje rozumienie realloc() jest takie, że gdy nowa alokacja się powiedzie, pierwotnie przydzielona pamięć zostanie zwolniona. Jeśli to prawda, to linia, o której mowa, używa wskaźników zwisających, innit? Zarówno buffer, jak i currentPosition na RHS tego wyrażenia są wskaźnikami do pamięci, która została zwolniona.

Moim instynktem byłoby przepisanie tego, aby uniknąć używania zwisających wskaźników za pomocą length, który przecież już jest w pobliżu. Chcę zastąpić te dwie ostatnie linie z:

buffer = newBuffer; 
currentPosition = buffer + length; 

Niemniej jednak przypuszczalnie kod jako prac pisemnych, ponieważ oba wskaźniki nadal posiadają adresy (aczkolwiek śmieci), a przesunięcie między tymi dwoma adresami można jeszcze obliczony jako sposób zmiany przydziału currentPosition. Czy jestem po prostu personicką w tym, że czuję się z tym nieswojo?

Aby uogólnić pytanie: gdy wskaźnik jest zwisający, czy można bezpiecznie używać adresu zawartego w wskaźniku w dowolnym celu, na przykład do obliczania przesunięć? Dzięki.

+0

W czasie realokacji "długość" jest o jeden większa od wielkości bufora ("maksymalna długość" przed korektą). Powinieneś używać 'currentPosition = buffer + length - 1', jeśli poprawnie interpretuję znaczenia. – Casey

+0

Sprawdziłem to przed faktycznym opublikowaniem pytania. Kod książki inicjalizuje zarówno "długość" jak i "aktualną pozycję" do zera. "length" jest inkrementowane w pierwszym warunkowym, więc zawsze jest o jeden za indeksem ostatnio dodanego elementu. 'currentPosition' to miejsce, w którym nowy element ma zostać dodany, a po dodaniu zwiększa się. Nie tak powinienem napisać kod na początek, ale biorąc podany kod, 'buffer + length' jest poprawny. – verbose

+0

Więc 'currentPosition' to wstępnie skomponowany' buffer + length'? Stoję poprawiony (i lekko zaskoczony przez redundancję). – Casey

Odpowiedz

9

kiedy wskaźnik jest zwisający, czy można bezpiecznie używać adresu zawartego w wskaźniku w dowolnym celu, na przykład do obliczania przesunięć?

Nie, to nie jest bezpieczne. Po free wartość wskaźnika jest niepoprawnym adresem, a niepoprawny adres nie może być używany do arytmetyki wskaźnika bez wywoływania niezdefiniowanego zachowania.

+0

Źródło? Nie spodziewałem się tego. –

+2

@CoryNelson C11, arytmetyka wskaźnika 6.5.6p8. Jeśli wynik nie znajduje się w obiekcie tablicowym lub po ostatnim elemencie => niezdefiniowanym zachowaniu. – ouah

+1

@ Ouah, myślę, że odnosi się to do próby dereferencji wyniku. – ROTOGG

0

Bezpieczne jest użycie wskaźnika zwisającego (np. Dla "arytmetyki wskaźnika"), o ile nie próbujesz wyłuskać wskaźnika (tzn. Zastosować operator *).

+3

Jest to błędne, ponieważ standard C nie gwarantuje, że arytmetyczna na poprzednich wskaźnikach do obiektów, które już nie istnieją działa. Można przypuszczać, że wskaźnik jest zaimplementowany jako adres pamięci, a arytmetyczna na adresy oczywiście działa, ale wskaźniki niekoniecznie są implementowane w ten sposób. Dodatkowo, optymalizator może dokonywać dedukcji na podstawie reguł C, co może prowadzić do zaskakujących zachowań. –

Powiązane problemy