estrutura de dados ii carlos oberdan rolim ciência da computação sistemas de informação
TRANSCRIPT
Estrutura de dados II
Carlos Oberdan Rolim
Ciência da ComputaçãoSistemas de Informação
Revisão
Ponteiros, passagem de parâmetros para funções,
alocação dinâmica e estruturas
Ponteiros
Ponteiros em Linguagem C
O Que é uma variável?
É uma área da memória do computador onde é armazenado um valor….
Exemplo 1:
int a = 1; Atribui ao endereço 1000 o valor 1
1000 1001 1002 1003
1
Variável Posição
a 1000
Ponteiros em Linguagem C
O Que É Um Ponteiro?
Um ponteiro é uma variável que aponta para outra variável. Isto significa que um ponteiro mantém o endereço de memória de outra variável. Em outras palavras, o ponteiro não contém um valor no sentido tradicional, mas sim o endereço de outra variável. Um ponteiro "aponta para" esta outra variável mantendo uma cópia de seu endereço
Convém dizer que a expressão “apontar para...” significa “armazenar o endereço de memória de...”
Como um ponteiro contém um endereço, e não um valor, terá duas partes. O ponteiro contém um endereço e o endereço aponta para um valor. Há o ponteiro e o valor para o qual ele aponta. Este fato pode ser um tanto confuso, até você se familiarizar com ele. Superada a etapa da familiarização, ele se torna extremamente eficaz.
Ponteiros em Linguagem C
Operadores relacionados a Ponteiros:
*(asterisco): informa que uma variável irá armazenar o endereço de outra variável;
ou:
Informa ao computador que vc deseja o valor que está no endereço armazenado;
- Pode ser lido como “no endereço”
q = *m; q recebe o valor armazenado no endereço m
& (e comercial): retorna o endereço de uma variável;
- Pode ser lido como “o endereço de”
m = &count; m recebe o endereço de count
Ponteiros em Linguagem C
int main() {
int i,j;
int *p;
p = &i;
*p=5;
j=i;
printf(("%d %d %d\n", i, j, *p);
return 0;
}
==> 5 5 5
Operadores de ponteiros
* (asterisco) indica que a variável é um ponteiro
tipo_dado *nome_ponteiro;
Ex:
int x;
int *pi; /* compilador sabe que pi é ponteiro */
/* pi é um ponteiro para inteiro */
Operadores de ponteiros
o operador “&” quando aplicado sobre uma variável retorna o seu endereço
Ex:
int x = 10, *pi;
pi = &x;
printf(“&x: %p pi: %p”, &x, pi);
=> &x: 0x03062fd8 pi: 0x03062fd8
Operadores de ponteiros
o operador “*” quando aplicado sobre um ponteiro retorna o dado apontado
Ex:
void main () {
int *tmp_ptr;
int x, y;
x = 10;
tmp_ptr = &x;
y = *tmp_ptr; /* (*tmp_ptr) = 10 */
}
tmp_ptr
x
y
0xABA0
0xABA2
0xABA0
10
10
Outro exemplo ilustrado
int i; int *p;
p = &i;
*p=5;
Relembrando...
operador *declara-se com *
int *xacessa-se (alterar, modificar, ler) também com *
*x = 10; // atribui o valor 10 ao local apontado pelo ponteiro ‘x’
printf(“%d”, *x); // imprime o valor armazenado no local apontado por ‘x’
observação: strings e vetores funcionam de forma diferente: um vetor ou string é um ponteiro por definição
operador &acessa (alterar, modificar, ler) o endereço de uma variável (que é um ponteiro)
Exemplo de uso
int a = 1; declara variavel inteiro com valor 1
int *pt_a; declara um ponteiro para um inteiro
pt_a = &a; ponteiro recebe o endereco da variavel a
printf(“%d”, *pt_a); imprime o valor apontado pelo ponteiro
Ponteirosponteiros são variáveis tipadas: (int *) ≠ (float *) ≠ (char *)
As variaveis ponteiro devem sempre apontar para os tipos de dados corretos. Uma variavel ponteiro declarada como apontador de dados inteiros deve sempre apontar para dados deste tipo.
Ex:
main() {
int *ip, x;
float *fp, z;
ip = &x; /* OK */
fp = &z; /* OK */
ip = &z; /* erro */
fp = &x; /* erro */
}
Ponteiros
espaço ocupado pelas variáveis
Ponteiro aponta para o tamanho segundo seu tipo
1 byte
(int *)
1 byte
(float *)
(char *)
Exemplo de uso
Exemplo:
int a = 1;
int *pt_a;
pt_a = &a;
1000 1001 1002 1003
1 1000
Variável Posição
a 1000
pt_a 1001
Ponteiros em Linguagem C
Onde usar isto???Funções!
Alocação Dinâmica
Não sei o tamanho que o vetor precisa ter….!
Não sei o tamanho que cada string precisa ter…
Não sei o tamanho que a matriz precisa ter…
Utilizando Ponteiros
void main() {int x = 10;int *pi;
pi = &x; /* *pi == 10 */(*pi)++; /* *pi == 11 */printf(“%d”, x);
}==> 11
ao alterar *pi estamos alterando o conteúdo de x
Utilizando Ponteiros
void main() {
int x = 10;
int *pi, *pj;
pi = &x; /* *pi == 10 */
pj = pi; /* *pj == 10 */
(*pi)++; /* (*pi, *pj, x) == 11 */
(*pj)++; /* (*pi, *pj, x) == 12 */
printf(“%d”, x); /* ==> 12 */ printf(“%x”, &pj); /* Endereco de x ==> 0x0c220c */
}
Ponteiros e Strings
Quando imprimimos uma cadeia de caracteres constantes (string) com printf o que é passado é o ponteiro para a cadeia.
Printf(“Ola como vai?”);
Dessa forma é possível carregar o endereço da string em um ponteiro do tipo char
char * lista;
lista = "Ola como vai ?";
printf("%s", lista );
Ponteiros e Strings
Na verdade, strings são arrays de caracteres e podem ser acessados através de char *
void main ()
{
char str[]=“abcdef”, *pc;
for (pc = str; *pc != ‘\0’; pc++)
putchar(*pc);
}
==> abcdef
o incremento de pc o posiciona sobre o próximo caracter (byte a byte)
Ponteiros e Strings
Outra forma de mostrar uma string usando laço
char *origem = "testando";
do{
printf("%c ", *origem);
}while (*origem++); /* origem == 0 encerra o laco */
Cuidados com Strings
É comum esquecer de alocar uma área para armazenamento de caracteres
void main() {
char *pc; char str[] = “Um string”;
strcpy(pc, str); /* erro! pc indeterminado */
...
}
Alocação dinâmica
Alocação dinâmica de memóriaAlocação dinâmica x alocação estática
Pode-se alocar dinâmicamente (em tempo de execução) um espaço de memória para uso com arrays, structs, etc...
int main() { int *p;
p = (int *) malloc(sizeof(int)); if (p == 0) { printf("ERRO: Sem memória\n"); return 1; } *p = 5; printf("&d\n", *p); free(p); return 0; }
Aloca de forma dinâmica espaço para um inteiro
Alocação dinâmica de memória
malloc
Utilizamos a função malloc() quando não conseguimos prever a quantidade de memória que nosso programa irá necessitar.
A função malloc() pode ser utilizada em run time para determinar o tamanho de um array.
Exemplochar * p; p = malloc(50);
free
Libera memória alocada previamente
Exemplo
free(p);
Alocação dinâmica de memória
void main() {
int numero = 10;
int *arranjo;
arranjo=(int *)malloc(numero * sizeof(int));
for (i=0; i < numero; i++) {
arranjo[i] = i;
}
free(arranjo);
}
Aloca de forma dinâmica espaço para um array de inteiros de 10 posições
#include <stdio.h> #include <stdlib.h> #include <conio.h>
int main() {
int *a; int i, k, m, min, temp, n;
/* Alocar memória para guardar o array */ printf("Quantos números quer ordenar? "); scanf("%d", &n); a = (int *) malloc( n * sizeof(int) );
if( a == NULL ) { printf("ERRO: nao ha memoria.\n"); exit(1); }
/* Receber os valores a ordenar */ for (i=0; i<n; i++) { printf("%d.º numero -> ",i+1); scanf("%d",&a[i]); }
/* Ordenar o array */ for( k=0; k<=n-1; k++ ) { /* descobre o índice do mínimo */ min = a[k]; m = k; for( i=k; i<=n-1; i++ ) if( a[i] < min ){ min = a[i]; m = i; }
/* troca a[k] com a[m] */ temp = a[k]; a[k] = a[m]; a[m] = temp; }
/* Escrever os elementos ordenados */ for( i=0; i<n; i++ ) printf("%d ", a[i]); printf("\n");
free( a ); /* libertar a memória */
getche();return 0;}
Estrutura de Dados Compostos
(Structs)
Definição
Uma estrutura (struct) ou registro em C é uma coleção de um ou mais valores, agrupados sob um único nome.
Estruturas constituem um recurso importante para organizar os dados utilizados por um programa graças à possibilidade de tratar um grupo de valores como uma única variável
Exemplo de uso
struct ponto { int x; int y;};
struct funcionario { int registro; char nome[30]; char depto[5]; float salario;};
Exemplo de uso
As declarações de ponto e funcionario, definem os respectivos tipos de dados, que podem ser utilizados em declarações de variáveis. Exemplos:
struct ponto p1, p2, p3; struct funcionario Joao;
Na primeira declaração, estão sendo declaradas as variáveis p1, p2 e p3 do tipo ponto. Na segunda declaração, é declarada a variável Joao do tipo funcionário.
Exemplo de uso
Para uma variável do tipo ponto, dizemos que x e y são seus campos ou membros. Os campos de uma variável podem ser acessados individualmente como variáveis usando-se o nome da variável seguido de "." e o nome do campo. Exemplos:
p1.x = 10;p1.y = 20;p2.x = p1.x + 5;p2.y = p2.y + 5;
Além disso, é possível atribuir a uma estrutura o valor de outra estrutura do mesmo tipo. Exemplos:
funcionario f = Joao;p3 = p2;
Estruturas complexas
Os campos de uma estrutura podem ser de qualquer tipo: tipos simples (int, char, float, etc), vetores, ou até mesmo estruturas. Exemplo:
struct retangulo { struct ponto pa; struct ponto pb;}
Uso de estruturas com funções
Uma vez que o tipo de uma estrutura foi declarado, é possível utilizá-lo em outras declarações (variáveis simples, vetores, funções, etc).
...struct ponto poligono[10];...
float dist(ponto p, ponto q) { int dx = p.x - q.x; int dy = p.y - q.y; return sqrt((double)dx*dx + (double)dy*dy);}
Passagem de estrutura por referência
Uma estrutura pode ser passada como parâmetro por referência numa função. Quando se usa uma referência (apontador), o acesso aos campos da mesma é feito através do operador "->" ao invés de ".". Exemplo:
void moveP(ponto* p, int dx, int dy){ p -> x += dx; p -> y += dy;}
...
moveP(&(r -> pa), dx, dy); moveP(&(r -> pb), dx, dy);
Retornando uma estrutura
Uma função pode ter uma estrutura como valor de retorno
struct ponto constroiPonto(int x, int y){ struct ponto temp; temp.x = x; temp.y = y; return temp;}
...struct ponto Origem = constroiPonto(0,0);
Declaração com valores iniciais
Ao declararmos uma estrutura, podemos também definir o seu valor inicial, de forma análoga a aquela utilizada para vetores. Exemplos:
struct ponto origem = {0,0};...struct ponto trapezio[] = { { 5,5}, {5, 10}, {10,5}, {10,13} };...
Usando ponteiros com structs
struct data{int dia;int mês; int ano;};
Definindo uma variável do tipo datastruct data dt;
Definindo um ponteiro para dtstruct data *pdt = &dt;
Fazendo referência a um elemento da estruturadt.dia ou (*pdt).dia ou pdt->diadt.mes ou (*pdt).mes ou pdt->mêsdt.ano ou (*pdt).ano ou pdt->ano
#include <stdio.h>typedef struct estrutura{ int valor; } ESTRUTURA;
void incrementa(ESTRUTURA * e){ printf(" Valor --> %i \n", e->valor); printf(" Valor --> %i \n", (*e).valor); /* outra forma de acessar */ (e->valor)++; /* incrementa valor */}
int main(){ ESTRUTURA d; /* declara variavel struct */ d.valor = 0; /* atribui valor inicial */ printf("Antes da funcao --> %d \n", d.valor); incrementa(&d); /* invoca a funcao */ printf("Apos a funcao --> %d\n", d.valor); return 0;
}
Exemplo de programa demonstrando uso de ponteiro, passagem de parâmetro
para função e estrutura
Alocação dinâmica de estruturas
É preciso armazenar esse endereço retornado pela malloc num ponteiro de tipo apropriado
Para alocar um tipo de dado que ocupa vários bytes, é preciso recorrer ao operador sizeof, que diz quantos bytes o tipo especificado tem
Vamos utilizar outra forma de declaração da struct:
struct tipo_data{
int dia, mes, ano;
};
typedef struct tipo_data DATA;
ATENÇÃO: data é o nome to tipo
Outra forma de declaração
typedef struct tipo_data{
int dia, mes, ano;
} DATA;
DATA *d;
d = malloc (sizeof (DATA));
d->dia = 31;
d->mes = 12;
d->ano = 2008;