Baixe o app para aproveitar ainda mais
Prévia do material em texto
Programação II Monitor: André Vicente Pessanha 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áveis 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) compatíveis 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 início 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 Estrutura 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! :O ************************************************************************************************ É 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áveis 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 resumo. *********************************************************************************************** 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! ------------------------------------------------------- // ------------------------------------------------------------
Compartilhar