2016-06-29 45 views
5

Rozważmy:Dlaczego * curr i curr-> val mają tę samą wartość?

#include<stdlib.h> 
#include<stdio.h> 

struct node { 
    int val; 
    struct node *next; 
}; 

typedef struct node item; 

void main() { 
    item * curr, *head; 
    int i; 

    head = NULL; 
    for (i = 1; i <= 10; i++) { 
     curr = (item *)malloc(sizeof(item)); 

     curr->val = i; 
     curr->next = head; 
     head = curr; 
    } 

    curr = head; 
    while (curr) { 
     printf("%d\n", curr->val); 
     printf("%d\n", *curr); 
     curr = curr->next; 
    } 

    getchar(); 
} 

Jak wydrukować, *curr i curr->val są takie same. Czemu?

Jestem zdezorientowany około *curr. Myślę, że *curr jest wartością &curr punktów. (Jestem nowicjuszem w C.)

+0

Jeśli kompilujesz z ostrzeżeniami, linia "printf (" % d \ n "* Curr);" powinien wydrukować ostrzeżenie, ponieważ węzeł struct nie jest liczbą całkowitą. Chociaż istnieje powód, dla którego widzisz to zachowanie, jest to nieco skomplikowane i ma związek z obsługą varargs w C. Prawdopodobnie powinieneś po prostu unikać tego, aby zachowanie było bardziej zrozumiałe. –

+3

'printf ("% d \ n ", * curr);' jest niezdefiniowanym zachowaniem, ponieważ '* curr' nie jest int. – immibis

+1

Oprócz tego, co powyższe komentarze poprawnie wskazano, dla odniesienia, dostęp do elementów z '(* curr) .val' jest taki sam jak' curr-> val' – wolfsgang

Odpowiedz

-4

Tutaj

curr->val = i; 
curr->next = head; 
head = curr; 

najpierw ustawić (*curr).val do i. Następnie zmieniasz curr->next na head. W tej chwili nadal NULL. Następnie ustawiasz head na curr. I tutaj interesująca część, curr wskazuje na jej początek i *curr odrzuca to, co jest w istocie curr->val, ponieważ od tego zaczyna się struktura curr.

Zgodnie z normą, w tym answer

(C1X §6.7.2.1.13 „a wskaźnik do obiektu struktury, odpowiednio przekształcone, wskazuje początkowej członka ... i vice versa Może. być bezimienny wyściółka wewnątrz jako struktury obiektu, ale nie na jego początku. ")

co oznacza, że ​​nie ma żadnego miejsca (wyściółka) (lub też np pewnym HEAD informacji) między faktycznym rozpoczęciem struktury i rozpoczęciem pierwszego elementu.

EDYCJA: Nie mówię nic o UB, nie dlatego, że go nie widzę. OP poprosił o coś innego. Należy podkreślić, ale nie być odpowiedzią! Właśnie dlatego mamy Sekcję komentarzy!

+1

'curr' wskazuje na' & (curr-> val) ', ale' * curr' to ** nie ** 'curr-> val' po prostu zaczyna się od' curr-> val', ale jest dłuższy. Zobacz moją odpowiedź, aby uzyskać więcej informacji –

0

To jest niezdefiniowane zachowanie i da ci ostrzeżenia.

printf pobiera typ argumentu przekazywanego do niego za pomocą specyfikatorów formatu. W twoim przypadku pierwszym elementem struktury jest int, a więc na szczycie stosu znajduje się int. Stąd, gdy printf szuka int, wyszukuje jeden i daje prawidłowe wyniki, mimo że przeszedłeś całą strukturę.

Jeśli był inny typ danych niż int jako pierwszy element twojej struktury, dałby on niezdefiniowany wynik. Tak więc, jak wspomniałem w komentarzach, poprawne sposoby dostępu do elementów struktury są z (*curr).value lub jak już użyłeś curr->value.

+1

Mówisz, że kod jest niezdefiniowanym zachowaniem, ale później mówisz, że "jeśli był inny typ danych niż" int "... dałby on niezdefiniowane wyjście". Z pewnością, jeśli jest to niezdefiniowane zachowanie, wynik jest już niezdefiniowany, bez względu na typ? –

+0

@PaulHankin To prawda, napisałem to tylko po to, aby wyjaśnić poprawne wyniki w tym przypadku i jak nie będzie to miało miejsca w innych przypadkach. – wolfsgang

9

Po pierwsze jako początkujący powinieneś spróbować napisać poprawny kod, który nie ostrzega przed próbą zrozumienia, co dzieje się pod maską i dlaczego niektóre nieprawidłowe wyrażenia wciąż dają poprawne wyniki.

C jest bardzo permisywnym językiem, tak długo, jak kompilator może tłumaczyć to, co czyta, ogólnie zakłada, że ​​programista wie, dlaczego je napisał. To się dzieje tutaj.

W języku C adres struct można przesłać na adres do pierwszego elementu. Tak więc (void *) &curr jest taki sam jak (void *) &(curr->val). Tutaj rzutuję wszystko na void *, aby mieć kompatybilne typy wskaźników.

Oznacza to, że *((int *) curr) jest doskonale zdefiniowanym wyrażeniem C, a jego wartość jest rzeczywiście curr->val. Ale to prawda, ponieważ napisałem wyraźny rzut wskaźnika.

To, co napisałeś działa tylko przez przypadek, ponieważ jako curr jest typem złożonym; przekazujesz int i item * do printf i używasz tylko jego pierwszej wartości - to jest już nie standard C i mogę tylko przyjąć klasyczną implementację, aby to odgadnąć. Ale po prostu zastąpić polecenia drukowania przez że:

printf("%d - %d\n", curr->val, 12); 
printf("%d - %d\n", *curr, 15); 

Powinieneś zobaczyć śmieci w drugim printf, ponieważ z %d wyrażają Państwo zdać prosty int i faktycznie zdać kruszywo. Również tutaj nic nie jest wyraźnie powierzone na standard, ale wspólne wdrożenie powinno dać taki wynik.

TL, DR: jako *curr nie jest int, printf("%d\n", *curr); jest niezdefiniowany zachowanie. Oznacza to, że wszystko może się zdarzyć, nawet oczekiwany wynik, ale inna wersja tego samego kompilatora lub innego kompilatora, a nawet zmiana parametrów kompilacji może dać inny wynik, podobnie jak drobna zmiana w kodzie.

A C pozwala na automatyczny rzut wskaźnika do iz void * i nigdy nie powinieneś używać wartości zwracanej malloc, ponieważ może ona ukrywać błędy. Proszę napisać:

curr = malloc(sizeof(item)); 

To już nie jest C++ zgodne, ale jest to prawidłowe C.