To jest błąd nazwy.
node
== struct data
dzięki typedef
.
Ale node
! = struct node
. Są to 2 różne typy. I struct node
nie został zdefiniowany.
Szkoda, że taka dezorientująca konstrukcja jest dozwolona przez standard C, a nawet nie jest oznaczona jako ostrzeżenie przez kompilatory. Ale tak właśnie dzisiaj definiujemy C, więc musimy z tym żyć.
Moja rekomendacja: nie używaj tych samych nazw dla struct
i typedef
. Stwórz własną konwencję, na przykład struct thing_s
i typedef struct thing_s thing_t;
. Pozwoli to uniknąć nazewnictwa takiego jak ten.
Rozwiązanie jest teraz dość oczywiste. Wymienić:
typedef struct data { int num; struct node *link; } node;
przez
typedef struct data { int num; struct data *link; } node;
i problematyczne printf
będzie teraz pracować.
Ale twoje pytanie brzmi: dlaczego twój program działał w ogóle bez tego printf
?
Teraz jest interesujący.
Powróćmy do początkowej definicji. Tak jak powiedzieliśmy, struct node
nie istnieje i dlatego jest niekompletny. Aby lepiej śledzić to, co mamy zamiar wyjaśnić, nazwijmy to: struct incomplete_s
. Teraz node
staje:
typedef struct data { int num; struct incomplete_s *link; } node;
To będzie nadal działać bez problematycznego printf
.
Powód jest taki, że node
jest prawidłowo zdefiniowany. Jest to struktura o znanych rozmiarach i typach. Ponieważ nie ma znaczenia, że struct incomplete_s
jest niekompletne, ponieważ link
jest zdefiniowany jako wskaźnik do niego. Równie dobrze mógłbyś zdefiniować void * link;
i nadal by działało.
void*
lub struct incomplete_s *
lub whatever *
mają ten sam rozmiar: wszystkie są wskaźnikami. Tak więc struktura je obsługująca może być prawidłowo utworzona.
W głównej pętli, program robi:
while(e!=NULL)
{
printf("%d\n",e->num);
e=e->link;
}
czyli e
, który jest wskaźnikiem node*
, przyjmuje wartość e->link
, która jest wskaźnikiem struct incomplete_s *
.
Należy zauważyć, że oba wskaźniki powinny wskazywać na różne typy. Ale oba są wskaźnikami, więc tak, to zadanie jest technicznie możliwe i autoryzowane przez standard.
Teraz bardziej ostrożny kompilator prawdopodobnie wyda ostrzeżenie tutaj, ponieważ nie powinno się mieszać wskaźników różnych typów. Jest to cichy typ odlewania, który jest receptą na przyszłe błędy. Nie wiem, którego kompilatora używasz, ale możesz zwiększyć jego poziom ostrzeżenia (użyj "ostrzeżenia 4" dla Visual lub -Wall -Wextra
dla gcc), a najprawdopodobniej nie spodoba się ta operacja =
.
Bardziej wyraźny typ odlewania rozwiązałoby że (*): e = (node*)(e->link);
Teraz to już nie jest cichy, programista jest odpowiedzialny za ostrzeżenia zniknie.
e->link
zdecydowanie istnieje, więc kompilator może pobrać tę wartość. Ale s->link->num
nie istnieje, ponieważ s->link
jest struct incomplete_s*
, a my nie wiemy, co to jest, więc nie wiemy, czy ma on element num
.
(*) Uzupełnienie uzupełniające: Przy pewnym wyższym poziomie optymalizacji nadal nie będzie dobrze: strict aliasing może przeszkadzać. Tak więc wyłuskiwanie typu wskaźnika na inny typ wskaźnika pozostaje niebezpieczną operacją.
Udało mi się skompilować i uruchomić twój kod bez ostrzeżenia i błędów po prostu zmieniając 'typedef struct data' na' typedef struct node'. –
Podstawowe rzeczy: nie możesz użyć nazwy 'typedef' wewnątrz struktury, której używasz' typedef'. typ to także 'węzeł', a nie' węzeł struktury '. Ponieważ użyłeś 'struct node', sugerujesz, że gdzieś jest gdzieś' struct node'. –
Ten kod działa. Ale jeśli dołączę tę skomentowaną instrukcję printf, generuje błąd – Sharon