2016-02-24 9 views
5
#include<stdio.h> 
typedef struct data 
{ 

    int num; 
    struct node *link; 
}node; 
main() 
{ 
    node *s=NULL,*e=NULL,*new; 
    new=malloc(sizeof(node)); 
    s=malloc(sizeof(node)); 
    s->num=10; 
    s->link=new; 
    new->num=8; 
    new->link=NULL; 
// printf("%d\n",s->link->num); //// error: dereferencing pointer to incomplete type 
    e=s; 
    while(e!=NULL) 
    { 
     printf("%d",e->num); 
     e=e->link; 
    } 

} 

Dla powyższego programu C otrzymałem poprawne dane wyjściowe. Ale jeśli dołączę skomentowaną linię, generuje błąd niepełnego przekierowania. Czy ktoś może wyjaśnić przyczynę?Wskaźnik odwołania do błędu niekompletnego przy użyciu struktury typedef w C

+2

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'. –

+0

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'. –

+0

Ten kod działa. Ale jeśli dołączę tę skomentowaną instrukcję printf, generuje błąd – Sharon

Odpowiedz

1

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ą.

+0

Świetnie! To kompletna odpowiedź. –

+1

Dziękuję bardzo Cyan – Sharon

0

struct node jest niezarejestrowany w tym kodzie, więc struct node* jest niekompletnym typem.

Domyślam się, że struct node *link; w deklaracji struct powinna być struct data *link;.

+0

co z tym; 's-> link = new; nowy-> num = 8; new-> link = NULL; ' – Samer

+0

Ale otrzymuję wyjście, jeśli printf ("% d \ n ", s-> link-> num); jest komentowany. Czy możesz mi pomóc z powodu? s-> link-> num tworzy problem. – Sharon

2

Przede wszystkim należy zmodyfikować struct, a błąd z komentowanym części powinien być rozwiązany:

typedef struct data 
{ 

    int num; 
    struct data *link; 

}node; 

Przy definiowaniu S i nowego w następujących liniach, nie ma potrzeby, aby je umieścić równe NULL, to działa dobrze główną funkcją:

void main(){ 
    node *s,*e,*new; 
    e=NULL; 

    new=malloc(sizeof(node)); 
    s=malloc(sizeof(node)); 
    s->num=10; 
    new->num=8; 
    new->link=NULL; 
    s->link=new; 
    printf("%d\n",s->link->num); /// works fine without any error 
    e=s; 
    while(e!=NULL) 
    { 
     printf("%d\n",e->num); 
     e=e->link; 
    } 
return; 
} 
0

najkrótsza odpowiedź brzmi: zdefiniowany typ, który potrzebuje jedną strukturę, która nie jest zadeklarowany.

Powód, dla którego wyświetlany jest błąd w skomentowanej linii, jest taki, że jest to unikalna linia, w której użyto elementu członka łącza w strukturze węzła. W dowolnej innej części kodu nie uderzaj tak jak robisz, na przykład, e = new->link; kompilator nie musi wiedzieć o elementach struktury w tym czasie, ponieważ powiedziałeś, że e jest węzłem typu, węzeł jest struktura i new->link jest zgodną strukturą, więc wszystko jest w porządku.

Jest to również powód, dla którego zmieniasz typedef struct data { …}; na typedef struct node { …}; i wszystko działa, ponieważ gdy kompilator potrzebuje informacji o węźle struktury, znajduje strukturę nazywaną węzłem i jest członkiem o nazwie link, który ma element o nazwie num. Jak widać, typedef jest tylko rodzajem aliasingu. Mam nadzieję, że pomaga ci to zrozumieć.

Powiązane problemy