Eu tenho este código:
#include<stdio.h>
#include<stdlib.h>
typedef struct{
int data;
struct node *next;
} NODE;
NODE *head = NULL;
void display_ll(NODE *head){
if (head == NULL){
printf("\nList is empty");
} else{
for(NODE *curr=head; curr!=NULL; curr=curr->next){
printf("%d ->", curr->data);
}
printf("\b\b ");
}
}
int main(void){
NODE *temp = (NODE *)malloc(sizeof(NODE));
temp->data = 5;
temp->next = NULL;
head = temp;
display_ll(head);
return 0;
}
Na linha 15, onde faço curr=curr->next
o for
loop, estou recebendo um aviso se compilado usando clang, e um erro se compilado usando gcc:
- barulho:
warning: incompatible pointer types assigning to 'NODE *' from 'struct node *' [-Wincompatible-pointer-types]
- gcc:
error: assignment to ‘NODE *’ from incompatible pointer type ‘struct node *’ [-Wincompatible-pointer-types]`
Eu imaginei uma solução alternativa onde escrevemos a estrutura como typedef struct node {...} NODE;
e funciona. Mas eu quero saber se há alguma correção para isso sem essa redefinição de struct.
O nome da sua struct não é
node
.Na verdade, ele não tem nome (
struct{ ... }
), eNODE
é um alias para aquela struct sem nome.Portanto nesta linha:
next
não é um ponteiro para a estrutura atual como você parece esperar (é um ponteiro para alguma outra suposiçãostruct node
que é declarada aqui).Para consertar isso, basta dar o nome à sua estrutura:
Isso criará
next
um ponteiro para a estrutura atual, conforme você espera.Live demo
Uma observação lateral:
Você deve sempre habilitar e anotar os avisos do compilador. No
display_ll
parâmetrohead
está escondendo o global. Mesmo que seja isso que você queria que acontecesse, é recomendado mudar o nome do parâmetro para evitar confusão.struct node
eNODE
são algo diferentes.node
é uma tag eNODE
é um alias .Você precisa adicionar a tag à sua declaração de struct:
Você não tem um
struct node
, você tem uma struct sem nome.Estruturas em C são bem confusas, pois podem ser usadas de muitas maneiras diferentes.
Para definir uma estrutura em C você escreveria
Onde
node
está uma tag struct , um tipo especial de identificador que vive em um namespace diferente de variáveis comuns. Para declarar uma variável com base nessa tag struct, temos que digitarstruct node x;
com astruct
palavra-chave presente.Se escrevermos
então essa é uma struct anônima sem uma tag struct. Essa definição de struct só é visível aqui - não podemos declarar variáveis dessa mesma struct anônima em outro lugar. O que significa que é inútil - a menos que adicionemos um nome de variável no final:
Agora poderíamos definir uma struct anônima e ao mesmo tempo criar um alias para ela com um
typedef
, para tornar possível declarar variáveis dessa struct em qualquer lugar:Aqui
node_t
não está o nome da variável, mas o nome de um tipo. O acima é talvez a maneira mais comum de definir structs em C.Se usarmos esse estilo, teremos problemas quando fizermos uma struct de autorreferência. Porque não importa qual estilo usemos, a definição da struct não estará completa até chegarmos ao
};
no final. O que significa que não podemos nos referir a ela de dentro da definição da struct - porque a definição ainda não está completa.Para resolver isso, podemos declarar a estrutura:
Este é um tipo incompleto do qual ainda não podemos criar nenhuma instância - não até chegarmos à
};
definiçãostruct node { ... };
mais tarde, onde a definição do tipo é concluída.No entanto, podemos criar um ponteiro para um tipo incompleto - mesmo que ainda não possamos declarar um objeto dele:
E seria por isso que às vezes obtemos erros estranhos do compilador reclamando sobre "o tipo está incompleto" quando esquecemos de incluir algum cabeçalho que torna a definição de struct visível. Uma vez que o compilador vê a definição de struct dentro da mesma unidade de tradução, somos capazes de declarar objetos desse tipo e acessar os membros.
Usando o mesmo truque acima, podemos definir uma struct como:
Aqui
struct node *next
está um ponteiro para um tipo incompleto, prestes a ser concluído na próxima linha.No entanto, se escrevermos
Então o compilador declara um ponteiro para um tipo incompleto
struct node
, mas esse não é o struct que estamos definindo agora. Esse struct é anônimo sem uma tag struct e, além disso, nomeamos o tiponode_t
- nenhum deles tem uma conexão comstruct node
. Então o compilador pensastruct node
que é algo definido em outro lugar.Portanto,
struct node*
não é compatível,node_t*
pois, até onde o compilador pode perceber, são duas estruturas diferentes.Como antes, podemos resolver isso fornecendo uma tag struct:
Agora
struct node* next
é um ponteiro para o tipo incompletostruct node
e também temos um alias para esse tipo chamadonode_t
. O que significa que uma vez que a definição e typedef estão no lugar, podemos usarstruct node
enode_t
de forma intercambiável.Além disso, se desejarmos, podemos dar
typedef
o mesmo nome à tag struct e à tag struct, já que elas estão em espaços de nomes diferentes:Leitura adicional:
O tipo incompleto é muito útil para implementar encapsulamento privado:
Como fazer encapsulamento privado em C?
Há uma pequena guerra de estilos acontecendo sobre qual estilo de struct usar. No entanto, nem tudo é subjetivo:
há razões técnicas para escolher um estilo de codificação de struct em vez do outro?