Buscar

Resumo_Struct

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

Resumo Struct:
***********************************************************************************
OBS: Essa matéria cai "somente" na P1, P2 e P3! :D
***********************************************************************************
- Definição:
Struct é uma nova forma de armazenar um conjunto de dados (variáveis) de modo muito mais organizado e prático.
A grande vantagem é que em struct podemos armazenar variávies de tipos diferentes!
OBS: Lembrando que vetor e matrizes também permitem armazenar um conjunto de dados, mas somente de >> um único << tipo! (int, float, etc)
------------------------------------------//---------------------------------------
- Como se cria uma struct? (Parte 1: Protótipo)
Antes de tudo precisamos criar um protótipo para a struct que é onde definimos o nome da struct e suas variáveis.
São sempre informações (variáveis) compativéis com o que queremos representar! Por exemplo, um ponto no plano cartesiano é representado pela coordenada x e y. Um aluno pode ser representado pelo seu nome e matrícula, etc.
OBS: Não precisa se preocupar em criar o protótipo do zero! O próprio enunciado já te dá o protótipo pronto ou no mínimo do que se trata a struct(Aluno, ponto, pombo, etc) assim como uma breve descrição do que precisa ser armazenado.(Variáveis)
OBS2: Risque "pombo" dos exemplos de struct. Pois é, eles invadem quando você menos espera. Haha
Exemplo: Protótipo da struct ponto:
struct ponto{
 float x;
 float y;
};
 
 
ponto é o nome da struct e float x, y são as variáveis que pertencem a essa struct.
OBS: Notou o >> ; << no fim né? :) 
Coloque todos os protótipos de struct >> SEMPRE << no inicio do programa! Logo abaixo dos #include, pois dessa forma, todas as funções do seu programa vão poder usar a struct! 
OBS: Lembra que uma função só pode chamar outra função, se ela estiver definida acima da que chamou? 
Na dúvida é só lembrar da main, ela fica sempre lá embaixo, no final do programa, por isso ela pode chamar qualquer função do programa e com struct é o mesmo esquema! As funções só podem usar uma struct que esteja definida acima dela!
--------------------------------------//-------------------------------------------
- Como se cria uma struct? (Parte 2: Variável da struct)
Após criar o protótipo da struct:
struct ponto{
 float x;
 float y;
}; 
int main (void){
 struct ponto p1; 
 p1.x = 5.0;
 p1.y = 2.0;
}
Na main, "struct ponto" é o >>Tipo<< (Como se fosse int, float, etc) 
"p1" é o nome da variável da struct! (Como se fosse "float p1")
Muito cuidado pra não confundir!
struct ponto p1;
Então sempre que precisamos criar uma variável de qualquer struct temos que escrever "struct >>nome da struct<< >>nome da variável<<" (Pelo menos por enquanto, já vou falar sobre typedef!)
p1.x = 5.0;
Pra alterar ou acessar o conteúdo da variável x temos que usar o conector > . < pra fazer a ligação entre a variável p1 e a variável x da struct.
OBS: Essa parte é mais fácil de entender no Visual Studio, pois assim que você digita > p1. < aparece uma lista com as variáveis que pertencem a struct ponto, no caso, x e y! 
Então se você planeja usar o Visual Studio só uma vez no semestre...Aproveite e pelo menos veja esse detalhe! :D
--------------------------------------//-------------------------------------------
- Como se cria uma struct? (Parte 3: Typedef)
Typedef é como se fosse um atalho, um nome que é usado pra representar alguma struct. 
Imagina ter que escrever "Struct ponto" sempre que quiser criar alguma variável struct desse tipo? 
É nisso que o typedef salva! Pois criamos uma palavra pra substituir e representar o "struct ponto"!
Tem várias formas de se usar o typedef, mas eu vou escrever aqui a forma que eu acho a ideal e é a mais curta também:
typedef struct ponto{
 float x;
 float y;
}Ponto;
Em outras palavras: typedef (substitua ou represente) o tipo "struct ponto" pela palavra > Ponto < ! Agora no resto do programa, vc pode usar sempre Ponto ao invés de struct ponto!
OBS: >>SEMPRE<< coloque a primeira letra maiúscula em todos os typedef! Isso é justamente pra facilitar a visualização e diferenciar de uma variável comum!
Ex: typedef struct aluno Aluno; typedef struct ponto Ponto; typedef struct notas Notas;
OBS: Assim como nas macros (#define) que o recomendado é > sempre < usar palavras em maiúsculo! Isso é justamente pra diferenciar Macro de variável comum e de typedef de structs!
Ex: #define MAX 10 #define NOTAS 3 
Dessa forma o programa fica mais organizado e se alguém de fora olhar o seu código, assim que ver uma variável começando com letra maiúscula, já vai sacar que se trata de uma struct! :)
-------------------------------------//--------------------------------------------
- Leitura e impressão de struct:
typedef struct ponto{
 float x;
 float y;
}Ponto; 
int main (void){
 Ponto p1, p2;
 puts("Digite as coordendas do primeiro ponto:");
 scanf("%f%f", &p1.x, &p1.y);
 puts("Digite as coordendas do segundo ponto:");
 scanf("%f%f", &p2.x, &p2.y);
Se p1 e p2 são structs, então temos que usar o > . < pra acessar o conteúdo das variáveis x e y de cada uma delas.
E como precisamos armazenar um valor, usamos o ' & ' . (Assim como em qualquer variável comum!)
OBS: Apesar de ter o mesmo nome (x e y), são variáveis completamente diferentes para p1 e p2! E não precisa se preocupar, o compilador sabe qual é a variável x e y de cada uma!
 printf("Coordenadas de p1: (%.1f;%.1f)\n", p1.x, p1.y);
 printf("Coordenadas de p2: (%.1f;%.1f)\n", p2.x, p2.y);
 return 0;
}
A impressão é o mesmo esquema da leitura, mas sem o &! Sem mistério nenhum né? :)
--------------------------------------//-----------------------------------------
- Enviar/ Receber struct como parâmetro: (Sem usar ponteiro!)
typedef struct ponto{
 float x, y;
}Ponto;
Ponto lePonto (){
 Ponto novo;
 puts("Digite as coordendas do ponto:");
 scanf("%f%f", &p1.x, &p1.y);
 return novo;
}
void imprimePonto (Ponto p1){
 printf("Coordenadas do ponto: (%.1f;%.1f)\n", p1.x, p1.y);
}
int main (void){
 Ponto p1;
 p1 = lePonto();
 imprimePonto(p1);
 return 0;
}
OBS: Essa forma de solução é a mais básica de todas, usada >>Somente<< nos casos em que o enunciado disser que uma função retorna uma >>Estrutura<< ou uma função envia como parâmetro uma >>Estrutura<< (Observe que não estamos lidando com ponteiro ainda!)
Vamos analisar por partes: 
Ponto lePonto ();
Essa função retorna uma struct ponto, mas como já definimos o typedef lá em cima, podemos só escrever > Ponto < no tipo de retorno da função! Essa função lê as coordenadas de um novo ponto e o retorna.
Se a função retorna uma struct ponto, precisamos armazenar numa variável de um tipo que seja compatível! p1 = lePonto();
OBS: Eu sempre recomendo nomear qualquer nova struct como "novo", justamente pra facilitar o entendimento que você tá criando uma nova variável de struct. Mas isso é completamente opcional.
imprimePonto(p1);
A função imprimePonto recebe uma struct ponto como parâmetro e você envia como se fosse uma variável comum! 
OBS: Assim como em Prog 1, se eu declarei uma variável chamada p1, enviei como parâmetro, o nome dela na função >>NÃO<< precisa ser o mesmo, pois se tratam de variáveis diferentes! 
void imprimePonto (Ponto p1)
Ponto p1 é uma variável local da função imprimePonto (Nesse caso foi feito uma cópia do seu conteúdo e enviado para essa função), mas podíamos ter chamado de Ponto t1 por exemplo pra deixar claro que são variáveis diferentes! 
Mas pra não confundir, eu recomendo sempre usar sempre o mesmo nome! E isso vale pra passagem por referência também! (Ponteiro)
--------------------------------------//-------------------------------------------
- Enviar/ Receber struct
como parâmetro: (Usando ponteiro e alocação dinâmica!)
typedef struct ponto{
 float x, y;
}Ponto;
Ponto * lePonto (){
 Ponto * novo;
 novo = (Ponto *) malloc (sizeof(Ponto));
 if(!novo){
 puts("Erro! Sem memoria!");
 return NULL;
 }
 puts("Digite as coordendas do ponto:");
 scanf("%f%f", &p1->x, &p1->y);
 return novo;
}
void imprimePonto (Ponto * p1){
 printf("Coordenadas do ponto: (%.1f;%.1f)\n", p1->x, p1->y);
}
int main (void){
 Ponto * p1;
 p1 = lePonto();
 if(!p1) exit(1);
 imprimePonto(p1);
 free(p1);
 return 0;
}
Analisando por partes:
Ponto * lePonto ()
Dessa vez a função lePonto retorna um ponteiro para Ponto! Então o primeiro passo é alocar dinamicamente um novo Ponto. Lembrando que se caso não conseguir alocar, retornamos NULL pra representar um ponteiro Nulo!
É na main (A função que fez a chamada) que verificamos se o ponteiro é Nulo ou não. Se for Nulo, não faz sentido continuar o programa e podemos encerrar com a função exit.
if(!p1) exit(1);
OBS: if(!p1) é o mesmo que if(p1 == NULL)
E >>Nunca<< esqueça de devolver a memória alocada através da função free!
free(p1);
Percebe que enviamos o parâmetro da função imprimePonto como se fosse uma variável comum também? 
imprimePonto(p1);
OBS: Agora em Prog 2, a partir daqui, vamos sempre criar variáveis que são ponteiros para algo! Lembra que em Prog 1 o único ponteiro que era criado dessa forma era o FILE *in de arquivo? Tirando essa exceção, a única forma de manipular ponteiro era declarando uma variável comum e enviando como parâmetro seu endereço para alguma função.
Ex: (Ponteiro em Prog 1) Declaração: int lidos; 
Chamada de função: leArquivo(&lidos);
OBS: Em alguns exercícios é necessário resolver dessa forma (Enviando o endereço de uma variável), mas no geral, principalmente em provas o que será cobrado é a nova forma de manipular ponteiros.
Como declaramos Ponto *p1 que já é um ponteiro (Endereço de uma variável), basta enviar como parâmetro como se fosse uma variável comum também! Não precisa do '&'!
imprimePonto(p1);
OBS: Arquivo >>NÃO<< será cobrado em Prog 2! Sim, pois é! Muito triste né? D:
novo = (Ponto *) malloc (sizeof(Ponto));
Queremos converter o retorno da função malloc para o tipo que precisamos, no caso, Ponteiro para Ponto. (Ponto *)
E qual será o tamanho recebido como parâmetro pela malloc? Justamente o tamanho da própria struct! (sizeof (Ponto))
OBS: Se quiser uma explicação mais detalhada sobre como se usa a função malloc, assim como a sua prevenção de erro, é só dar uma lida no bloco "Resumo_AlocaçãoDinâmica". :)
A grande diferença com ponteiro é o uso do conector setinha ( -> ) que será explicado logo adiante!
*********************************************************************************
Detalhe importantíssimo: (Recomendo nem piscar nessa parte! =D )
A partir de agora (Sim, exatamente agora!), antes de acessar o conteúdo de qualquer variável de qualquer struct, primeiro você vai se perguntar: 
"Isso é uma Estrututa ou um ponteiro para estrutura!?"
Ponto p1 é uma Estrutura, então para acessar o conteúdo de x ou y, basta usar o conector > . < ! 
EX: p1.x ou p1.y 
Ponto *p1 é um Ponteiro para uma Estrutura!!! Então após alocar dinamicamente, acessamos o conteúdo de x ou y através do conector -> (Setinha!) 
EX: p1->x ou p1->y 
********************************************************************************
OBS: Esse tipo de solução será usado e cobrado em 99,57% das provas! >>SOMENTE<< nos casos em que o enunciado disser que uma função retorna ou envia como parâmetro um >> Ponteiro para uma Estrutura << (Nesse caso, um Ponteiro para Ponto!)
No mesmo segundo que vc ler a palavra >>Ponteiro<< você já vai sacar que é pra usar a setinha, né? :)
--------------------------------------//------------------------------------------
- Struct definida a partir de outra struct: 
**********************************************************************************
OBS: Essa parte é importantíssima e com certeza será cobrado na P1! =D
**********************************************************************************
É basicamente uma struct que em seu protótipo, possui variáveis de outras structs!
Por exemplo: Vamor criar o protótipo de uma struct círculo que é composta pela coordenada do seu centro (Struct Ponto), um raio e os valores RGB que representam a cor do círculo (Struct Cor).
typedef struct ponto{
 float x, y;
}Ponto;
typedef struct cor{
 int r, g, b;
}Cor;
typedef struct circulo{
 Ponto * centro;
 float raio;
 Cor * cor;
}Circulo;
OBS: Percebe que é >> obrigatório << definir o protótipo da struct ponto e cor antes da struct círculo? Pois ela depende da definição das outras!
Lembra que só podemos chamar uma função que está definida acima da que fez a chamada? É exatamente a mesma ideia! 
Exemplo: Função que cria e retorna um novo círculo alocado dinamicamente e uma função que exibe as informações de um círculo:
Circulo * criaCirculo (){
	Circulo *novo;
	Ponto *p;
	Cor *c;
	p = (Ponto *) malloc (sizeof(Ponto));
	if(!p){
		puts("Erro! Nao conseguiu alocar o Ponto!");
		return NULL;
	}
	c = (Cor *) malloc (sizeof(Cor));
	if(!c){
		puts("Erro! Nao conseguiu alocar a cor!");
		return NULL;
	}
	novo = (Circulo*) malloc (sizeof(Circulo));
	if(!novo){
		puts("Erro! Nao conseguiu alocar o Circulo!");
		return NULL;
	}
OBS: Essa é a forma mais correta de se alocar várias coisas em sequência!
**********************************************************************************
Mas existe uma forma mais simples:
p = (Ponto *) malloc (sizeof(Ponto));
c = (Cor *) malloc (sizeof(Cor));
novo = (Circulo*) malloc (sizeof(Circulo));
if(!p || !c || !novo){
 puts("Erro! Sem memoria!");
 return NULL;
}
OBS: Primeiro aloca todos e depois só faz um único teste, se um deles for igual a NULL, encerra a função. O problema dessa solução é que imagina se não conseguimos alocar o ponto (primeira chamada do malloc), faz sentido tentar alocar os outros?
Pois é, então apesar dessa solução ser bem menor que a outra, é um pouco arriscada. (Vai depender do critério de correção de cada professor)
**********************************************************************************
	
	puts("Coordenadas do Centro: ");
	scanf("%f%f",&p->x, &p->y);
	puts("Cores (RGB):");
	scanf("%d%d%d", &c->r, &c->g, &c->b);
	puts("Raio: ");
	scanf("%f", &novo->raio);
	novo->centro = p;
	novo->cor = c;
	
	return novo;
}
OBS: Percebe que novo->centro é um ponteiro para Ponto? Por isso podemos atribuir ele a variável p que também é um ponteiro para Ponto! Mesmo esquema na atribuição da cor.
void exibeCirculo (Circulo *c){
	printf("Centro: (%.1f ; %.1f)\n",c->centro->x, c->centro->y);
	printf("Raio: %.1f\n", c->raio);
	printf("R: %d G: %d B: %d\n",c->cor->r, c->cor->g, c->cor->b);
}
***********************************************************************************
OBS: Notou que a quantidade de setinhas aumentou? :)
c é um ponteiro para uma estrutura círculo, então a >> única << forma de acessar o conteúdo das suas variáveis é através da setinha. Mas centro é um ponteiro para Ponto, então como vamos fazer pra acessar as variáves x e y? 
É só parar pra analisar, centro é um ponteiro para Ponto! Então vamos seguir a >>MESMA<< lógica e usar uma setinha pra acessar o conteúdo de suas variáveis! Por isso são duas setinhas, uma para cada ponteiro, até chegar onde queremos, o conteúdo de x ou y! 
OBS: Ficou em dúvida quando se usa setinha ou conector ponto? 
Sabia! Eu avisei pra não piscar, não foi? :D 
Só dar uma lida na parte "Detalhe importantíssimo!" na metade desse bloco. 
***********************************************************************************
int main(void) {
	Circulo *c;
	c = criaCirculo();
	if(!c)
exit(1);
	exibeCirculo (c);
	free(c->centro);
	free(c->cor);
	free(c);
	return 0;
}
OBS: Muito cuidado pra não esquecer a prevenção de erro! Logo após chamar a função que cria e retorna algo alocado dinamicamente!
OBS2: Percebe que primeiro dei free no c->centro que é o ponteiro para um ponto, depois free na c->cor que é o ponteiro para uma cor e >>SOMENTE NO FIM<< dei free no ponteiro do círculo! Lembra que um círculo depende de um ponto e uma cor? 
Então se der free no círculo antes, vamos perder as informações do ponto e da cor!!! Ou seja, dar free no círculo não significa dar free em tudo! Erro mais que fatal, muito cuidado!
--------------------------------------//--------------------------------------------

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Continue navegando