Programação II Tipos Estruturados Parte 1: struct Parte 2: Ponteiros para Estrutura Estruturas e Memória Bruno Feijó Dept. de Informática, PUC-Rio
struct
Dados Compostos Até agora tipos simples: char, int, float,. Necessidade por dados compostos, por tipos estruturados Ex.: pontos no espaço 2D como um objeto (ou tipo) único Ponto X Y Ex.: registros de conta bancária Cliente Nome NoContaCorrente End Rua No Compl
Struct struct ponto float x; float y; typedef struct ponto Ponto; Ponto X Y struct ponto p; ou Ponto p; se definirmos "Ponto" como "struct ponto" operador de acesso (.): p.x = 10.0; p.y = 5.0; ou lendo do teclado: scanf("%f%f",&p.x,&p.y); // ou: &(p.x)
Estruturas Aninhadas struct ponto float x; float y; typedef struct ponto Ponto; y pt1 (1,1) pt2 (4,3) x struct retangulo Ponto pt1; Ponto pt2; typedef struct retangulo Retangulo; ou: struct retangulo struct ponto pt1; struct ponto pt2; Defina um objeto chamado tela (como sendo um retângulo) e estabeleça os valores do primeiro ponto da tela (pt1), conforme figura acima: Retangulo tela; tela.pt1.x = 1.0; tela.pt1.y = 1.0;
Exemplo Ponto dentro de Retângulo Sejam p um ponto e r um retângulo. Defina uma função que retorna 1 se p r e 0 caso contrário. Considere os tipos definidos no slide anterior. 3 2 r p 1 pt1 (1,1) 0 0 1 2 3 4 #include <stdio.h> pt2 (4,3) struct ponto float x; float y; typedef struct ponto Ponto; struct retangulo Ponto pt1; Ponto pt2; typedef struct retangulo Retangulo; int pemret(ponto p, Retangulo r) return p.x >= r.pt1.x && p.x <= r.pt2.x && p.y >= r.pt1.y && p.y <= r.pt2.y; int main(void) Ponto p1 = 1, 1 Ponto p2 = 4, 3 Ponto p = 3.5, 1.5 Retangulo r; r.pt1 = p1; r.pt2 = p2; printf("%d\n", pemret(p, r)); return 0; }
typedef e struct em único comando evitar! Podemos ter definição simultânea: typedef struct ponto float x; float y; } Ponto; Mas, recomendamos definir typedef após (ou antes) struct. É bom ficarmos com a liberdade de definir struct em um módulo e o typedef em outro. Faremos isto, mais tarde no curso, em TAD (Tipo Abstrato de Dados).
Ponteiros para Estrutura
Ponteiro para Estrutura Se uma estrutura grande deve ser passada para uma função, geralmente é mais eficiente passar um ponteiro do que copiar a estrutura inteira. struct ponto * pp; Uma primeira forma de usar ponteiro para estrutura e pegar o endereço. Por exemplo: struct ponto a; struct ponto * pp; pp = &a; (*pp).x = 10.0 //. tem maior precedencia que * Alternativa de acesso: operador de acesso -> pp->x = 10.0; Exemplo: struct retangulo r, * rp; rp = &r; então, são equivalentes: r.pt1.x rp->pt1.x (r.pt1).x (rp->pt1).x Os valores dos componentes de uma estrutura podem ser definidos na inicialização ou através de scanf. Há várias maneiras de trabalhar com o ponteiro. Por exemplo: struct ponto a = 4.0,3.0 struct ponto a; struct ponto * pp; ou struct ponto * pp; pp = &a; pp = &a; printf("%f %f\n",pp->x,pp->y); scanf("%f%f",&pp->x,&pp->y); printf("%f %f\n",pp->x,pp->y);
Exemplo ponteiro como argumento de função Escreva função que imprime os valores da estrutura ponto através de seu ponteiro #include <stdio.h> struct ponto float x; float y; void imprime(struct ponto *); int main(void) struct ponto p; p.x = 4.0; p.y = 3.0; imprime(&p); return 0; } void imprime(struct ponto * pp) printf("%f %f\n", pp->x, pp->y); }
Exemplo Área Escreva função que retorna área de um retângulo. Defina um dos pontos diretamente a1 = } e o outro com o operador (.) #include <stdio.h> struct ponto float x; float y; struct retangulo struct ponto pt1; struct ponto pt2; float area(struct retangulo * pr); int main(void) struct ponto a1 = 1.0,1.0 struct ponto a2; struct retangulo r; } a2.x = 4.0; a2.y = 3.0; r.pt1 = a1; r.pt2 = a2; printf("%f\n", area(&r)); return 0; float area(struct retangulo * pr) return (pr->pt2.x - pr->pt1.x) *(pr->pt2.y - pr->pt1.y); }
Alocação Dinâmica struct ponto float x; float y; typedef struct ponto Ponto; typedef struct ponto * PPonto; Ponto p1; p1.x = 10.0; PPonto pp; pp->x = 10.0; Outra forma (com alocação dinâmica): Ponto * p; p = (Ponto *)malloc(sizeof(ponto)); p->x = 10.0;
Exemplo Estrutura Aluno usando Ponteiros Escreva um programa que primeiro lê o nome de um aluno e as suas duas notas P1 e P2 via teclado, colocando-os numa estrutura que contém um string e um vetor notas com dois floats. Depois escreva uma função que recebe um ponteiro para esta estrutura e retorna a média do aluno. Por fim, imprima nome e média no programa principal. Use três módulos (aluno.h, aluno.c e prog1.c) e decida onde colocar as definições de estrutura. Use typedef. Faça alocação dinâmica.
prog1.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include "aluno.h" int main(void) Aluno * p; Exemplo Estrutura Aluno usando Ponteiros - Solução struct aluno char nome[81]; float notas[2]; typedef struct aluno Aluno; float media(aluno * p); p = (Aluno *)malloc(sizeof(aluno)); // alternativa estatica: Aluno a; p = &a; printf("entre nome aluno: "); scanf(" %80[^\n]s",p->nome); printf("entre notas P1 e P2: "); scanf("%f%f",&p->notas[0],&p->notas[1]); printf("aluno= %s Media= %.2f\n",p->nome,media(p)); aluno.h } return 0; #include "aluno.h" aluno.c outra alternativa estática (inicializando): Aluno a = "Ana Paula",8,5} p = &a; float media(aluno * p) return (p->notas[0] + p->notas[1])/2; }
Estruturas e Memória
Definir, Declarar e Acessar uma Estrutura Primeiro nós definimos um novo tipo para o computador, e.g.: struct ex1 int a; int b; char c; Isto informa ao compilador o quão grande é a nossa struct e como os diferentes itens de dados (os membros da estrutura) são colocados na memória. mas isto NÃO ALOCA memória! Para alocar memória, nós temos que declarar uma variável usando o nosso novo tipo de dados, e.g. struct ex1 x; Depois disto, podemos acessar membros específicos desta variável: x.b = 5 x 5 x.c x.b x.a Os membros de uma estrutura são colocados na ordem especificada pela definição Ao passar uma estrutura como argumento de uma função, há uma cópia temporária custosa (porque é de toda a estrutura). Torna-se muito menos custoso passar um ponteiro (pois haverá a cópia de um simples endereço), e.g.: argumento é uma estrutura: float media(aluno p) argumento é um ponteiro para estrutura: float media(aluno * p) Lembre que p é uma variável temporária que vai morrer quando a função acabar.
Structure Padding O tamanho em bytes de uma estrutura nem sempre é a soma dos tamanhos isolados dos seus membros. Por exemplo, struct ex1 int a; int b; char c; char d; float e; ocupará 16 bytes, ao invés de 4 + 4 + 1 + 1 + 4 = 14 bytes. Se mudarmos a ordem dos membros da estrutura, o gasto de bytes pode até aumentar: Por exemplo, struct ex1 int a; char c; int b; char d; float e; ocupará 20 bytes. teste com sizeof(struct ex1)! A arquitetura de um computador é otimizada para a leitura de 4 bytes por vez (em computadores de 32 bits) ou 8 bytes por vez (em máquinas de 64 bits). Desta maneira, o compilador coloca bytes vazios para completar este empacotamento dos membros de uma estrutura. Isto é chamado de structure padding. struct ex1 int a; int b; char c; char d; float e; a b c d e pad struct ex1 int a; int b; char c; char d; float e; a c b d e Em alguns compiladores (e.g. Visual Studio), você pode impor empacotamento diferente, usando diretivas #pragma Mas não as use neste curso pad1 pad2