CES-11 Teoria Cap 1-a

Propaganda
CES-11 ALGORITMOS E
ESTRUTURAS DE DADOS
Capítulo I
Introdução
Capítulo I - Introdução
1.1 – Revisão sobre tipos, comandos e
subprogramação
1.2 – Tipos abstratos de dados
1.3 – Noções de complexidade de algoritmos
1.1 – Revisão sobre Tipos, Comandos
e Subprogramação
1.1.1 – Componentes de um sistema de informações

Um grande sistema de informações pode ser decomposto
em um conjunto de subsistemas menores interligados

Cada um desses subsistemas por sua vez pode ser
decomposto da mesma maneira

E assim por diante, até que se obtenha um conjunto de
informações indivisíveis interligadas
Exemplo: cadastro dos funcionários de uma empresa





Nome, setor de trabalho: strings
Salário: número real
Sexo: um caractere (M ou F)
Data de nascimento: vetor de 3 inteiros
Endereço: logradouro, número, bairro, cidade e estado
Número
inteiro
Strings

Informações escalares: compostas de um único elemento;
indivisíveis; exemplos: Sexo, Salário e Número do Endereço

Informações estruturadas: podem ser subdivididas
(exemplos: Nome, Endereço, Data de Nascimento, Setor de
Trabalho)

Endereço: subdividido em outras informações estruturadas
(Logradouro, Bairro, Cidade e Estado)
1.1.2 – Tipos escalares primitivos

Tipo primitivo: é um tipo escalar cujas variáveis e constantes
são declaradas por meio de palavras reservadas
a) Tipo Inteiro: (int, short, long)
Domínio: números inteiros entre - e +
Operações: + - * / %
=  <  > 
Exemplos: 7 / 3 = 2
7 % 3=1
b) Tipo Real: (float, double)
Domínio: números reais entre - e +
Operações: + - * /
=  <  > 
c) Tipo Caractere: (char)
Domínio:
Dígitos decimais: ‘0’, ‘1’, ... , ‘9’
Letras: ‘A’, ‘B’, ... , ‘Z’, ‘a’, ‘b’, ... , ‘z’
Sinais especiais: ‘.’ , ‘,’ , ‘;’ , ‘+’ , ‘*’ , ‘/’, ‘(’ , ‘)’,‘[’, ‘]’, ...
Caracteres de controle: ‘\n’, ‘\t’, ‘\b’, ‘\r’, ...
Operações: =  ( <  >  + - * / %)
d) Tipo Lógico: (logic – não há em C)
Domínio: Verdade e Falso
Operações: =, ≠, and, or, not, nand, nor e xor
Resultados de comparações são valores lógicos
1.1.3 – Comandos básicos de uma linguagem

Os algoritmos apresentados nesta disciplina serão expressos
num código correspondente a um aplainamento da
Linguagem C

Quase tudo na mesma forma da Linguagem C

Comandos de entrada e saída, comandos de seleção e
outros terão forma mais simples que a da Linguagem C,
para maior clareza

Comando composto:
{ Comando Comando . . . . . Comando }

Bloco:
{ Declarações Comando Comando . . . . . Comando }

Comando de atribuição:
Operadores: =, ++, --, +=, -=, *=, /=, %=

Comandos de entrada e saída:
read (Variável , Variável , . . . . . , Variável);
write (Elemento , Elemento , . . . . . , Elemento)
Elemento pode ser uma expressão ou um string

Comando vazio:
;

Comandos condicionais:
if ( Expressão ) Comando
if ( Expressão ) Comando else Comando

Expressão condicional:
Expr1 ? Expr2 : Expr3

Comandos repetitivos:
while ( Expressão ) Comando
do Comando while Expressão ;
for ( Inicializações ; Expressão ; Atualizações )
Comando

Comando de seleção:
switch ( Expressão inteira ) {
V11, V12, . . ., V1m : Lista de comandos;
V21, V22, . . ., V2n : Lista de comandos;
........
Vi1, Vi2, . . ., Vip : Lista de comandos;
default: Lista de comandos;
}
Para maior clareza, nos algoritmos não serão usados case’s
nem break’s

Comandos de desvio:
break ;
goto Rótulo ;
1.1.4 – Tipos estruturados de uma linguagem
a) Vetores:
TipoPrimitivo V[30], W[50], X[200];
ou
typedef TipoPrimitivo vetor[30];
vetor V1, V2, V3;
b) Matrizes:
TipoPrimitivo M1[10][10][10], M2[8][20];
ou
typedef TipoPrimitivo matriz[10][10];
matriz M1, M2, M3;

Há matrizes especiais com poucos elementos não-nulos

Por simplicidade, serão abordadas apenas as matrizes
bidimensionais quadradas

Matriz diagonal:
Para armazená-la:
•Um vetor com os elementos da
diagonal principal
•Um inteiro com sua dimensão
MatDiag
5
n
Elem
5
6
2
8
1
0
3
4
1
2

Matriz tridiagonal:
Para armazená-la:
•Um vetor para armazenar os
elementos das 3 diagonais
relevantes
•Um inteiro com o número de
elementos dessas 3 diagonais
n
19
5 3 7 6 2 4 2 8 5 8 3 1
Elem
1 4 4 8 3 9 1
0
12
1
2
3
4
5
6
7
8
9
10
11
13
14
15
16
17
18
MatTriDiag
Algoritmos especiais são necessários para manipular matrizes
tridiagonais armazenadas em vetores

Matriz triangular:
Para armazená-la:
•Um vetor para
armazenar os elementos
da área não-nula
•Um inteiro com a
dimensão da matriz
MatTriang
5 4 1 3 1 6 2 7 2 2 4 3 8 9 1
Dimensão: 5
•Um flag dizendo se é
superior ou inferior
Tipo: superior
Algoritmos especiais são necessários para manipular matrizes
triangulares armazenadas em vetores

Matriz faixa:
Para armazená-la:
•Um vetor para armazenar os
elementos da área não-nula
•Um inteiro com a dimensão
da matriz
•Dois inteiros com a largura
da faixa acima e abaixo da
diagonal principal
Algoritmos especiais são necessários para manipular matrizes
faixas armazenadas em vetores

Matriz esparsa:
Para armazená-la:
•Matriz bidimensional com 3 linhas:
•Uma com os elementos não-nulos
•Outra com os números das linhas
desses elementos
•Outra com os números de suas colunas
•Um inteiro com o número de elementos
não-nulos
9
4
3
2
8
5
0
2
3
4
5
6
4
1
4
0
5
1
6 elementos
c) Strings

Declarações:
typedef char string[15];
string s1, s2;

Operações:
strcat (s1, s2);
c = strlen (s1);
strcpy (s1, s2);
strcmp (s1, s2);
strcpy (s1, “Cadeia de teste”);
d) Estruturas

Estruturas simples:
typedef struct funcionario funcionario;
struct funcionario {
char Nome[30], Endereço[30], Setor[15];
char Sexo, Estcivil; int Idade, Anosfirma ;
};
funcionario F1, F2, F3, Empregados [200];
.
.
.
.
.
Empregados[1] = F1; F2.Sexo = ‘M’;
strcpy (Empregados[3].Nome, “José da Silva”);

Estruturas de campos alternativos:
Tipo union: define um conjunto alternativo de campos que
podem ser armazenados numa mesma posição de memória.

Exemplo:
typedef union ttt ttt;
union ttt {
char c1; int c2; double c3;
char A[10];
};
ttt t1, t2, t3;
O espaço reservado
para t1 é o do maior
de seus campos
t1
c1 / c2 / c3 / A

Exemplo: Seja o cadastro dos habitantes de uma cidade
A seguir, informações pertinentes sobre um habitante genérico
A seguir, declarações para
implementar o cadastro
desses habitantes
Habitante
Hab [1000000];
typedef struct Habitante Habitante;
struct Habitante {
char nome [30], ender [30], cid_natal [20];
Data DataNasc: int esc;
Formacao form;
};
typedef union Formacao Formacao;
union Formacao {
PrimGrau pg; SegGrau sg; Superior
};
sp;
typedef struct PrimGrau PrimGrau;
struct PrimGrau {
int ano_term;
};
typedef struct SegGrau SegGrau;
struct Seggrau {
int ano_term; char tipo;
};
typedef struct Superior Superior;
struct Superior {
int ano_term;
char nome_escola[30], tipo[4];
};
typedef struct Data Data;
struct Data {
int dia, mes, ano;
};
e) Tipos enumerativos:
typedef enum Cor Cor;
enum Cor {Branca, Amarela, Verde, Azul, Preta};
typedef enum Diasemana Diasemana;
enum Diasemana {Dom, Seg, Ter, Qua, Qui, Sex, Sab};
Diasemana Ontem, Hoje, Amanha;
Cor c1, c2, c3;
1.1.5 – Subprogramação

Chamada de função:
NomeFunção (ListaArgumentos)

ListaArgumentos pode ser ou não vazia

Uma chamada de função pode aparecer em expressões
maiores

Quando aparecer isolada, seguida de ‘;’, torna-se um
comando de uma lista de comandos

Declaração de funções:
Tipo NomeFunção (ListaParâmetros) {
CorpoFunção
Funções que não retornam
}
valores serão do tipo void

ListaParâmetros pode ser ou não vazia

Parâmetros sempre são alocados quando a função é
chamada para execução

Parâmetros recebem os argumentos da chamada da função

Duas formas de passagem de argumentos: por valor ou por
referência (ou por endereço)
Passagem por valor:
void ff (int a) {
a += 1;
write ("Durante ff: a = ", a);
}
void main ( ) {
int a = 5;
write ("Antes de ff: a = ", a);
ff (a);
write ("Depois de ff: a = ", a);
}
Saída:
Antes de ff: a = 5
Durante ff: a = 6
Depois de ff: a = 5
a
56
a 5
São 2 variáveis
diferentes, de
mesmo nome: a
Passagem por referência ou endereço:
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
Seja sua execução:
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
3
i
8
j
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
3
i
8
j
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
p
q
3
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
p
q
3
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
3
p
q
3
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
3
p
q
3
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
3
p
q
8
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
3
p
q
8
i
8
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
aux
3
p
q
8
i
3
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
Desalocação das variáveis de Trocar:
aux
3
p
q
8
i
3
j
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
Antes de trocar, i = 3; j = 8
Depois de trocar, i = 8; j = 3
Resultado
void trocar (int *p, int *q){
int aux;
aux = *p; *p = *q; *q = aux;
}
8
i
3
j
void main ( ) {
int i = 3, j = 8;
write ("Antes de trocar, i = ", i, "; j = ", j);
trocar(&i, &j);
write ("Depois de trocar, i = ", i, "; j = ", j);
}
Passagem de variáveis indexadas:

O nome de uma variável indexada é o endereço de seu 1º
elemento

Se um dos parâmetros de uma função for uma variável
indexada, na realidade ele será um ponteiro

Caso o argumento correspondente seja o nome de uma
variável indexada:

O endereço correspondente ao seu nome é passado ao
parâmetro

Os elementos da variável-argumento não são copiados
para a função

Então essa passagem de argumento é por referência ou por
endereço

Toda alteração nos elementos da variável-parâmetro terá
efeito sobre aqueles da variável-argumento

Outros possíveis argumentos: ponteiros e endereços

A seguir um exemplo ilustrativo
void Alterar (int B[]) {
B[1] = B[3] = 7;
}
O parâmetro B de
Alterar é um ponteiro
void main () {
int i, j, A[10] = {0};
Não é necessário
write ("Vetor inicial: ");
colocar a dimensão
for (i = 0; i <= 9; i++)
write (A[i]);
Poderia ser int *B
Alterar (A);
write ("Vetor intermediario: ");
for (i = 0; i <= 9; i++) write (A[i]);
Alterar (&A[4]);
write ("Vetor final: ");
for (i = 0; i <= 9; i++) write (A[i]);
}
B
void Alterar (int B[]) {
B[1] = B[3] = 7;
}
A
0
07
0
70
0
70
0
70
0
0
A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9]
void main () {
int i, j, A[10] = {0};
B[0] B[1] B[2] B[3] B[0]
B[4] B[5]
B[1] B[2]
B[6] B[7]
B[3] B[4]
B[8] B[9]
B[5]
write ("Vetor inicial: ");
for (i = 0; i <= 9; i++)
write (A[i]);
Alterar (A);
write ("Vetor intermediario: ");
for (i = 0; i <= 9; i++) write (A[i]);
Alterar (&A[4]);
write ("Vetor final: ");
for (i = 0; i <= 9; i++) write (A[i]);
}
Vetor inicial
void Alterar (int B[]) {
B[1] = B[3] = 7;
}
:
0
0
0
0
0
0
0
0
0
0
Vetor intermediario:
0
7
0
7
0
0
0
0
0
0
Vetor final
0
7
0
7
0
7
0
7
0
0
:
void main () {
int i, j, A[10] = {0};
write ("Vetor inicial: ");
for (i = 0; i <= 9; i++)
write (A[i]);
Alterar (A);
write ("Vetor intermediario: ");
for (i = 0; i <= 9; i++) write (A[i]);
Alterar (&A[4]);
write ("Vetor final: ");
for (i = 0; i <= 9; i++) write (A[i]);
}
Resultado
Passagem de estruturas:

A passagem de uma estrutura como argumento pode ser por
valor ou por referência (endereço)

A passagem de uma grande estrutura por valor provoca a
movimentação de considerável massa de dados

Então uma grande estrutura só deve ser passada por valor,
se o subprograma alterar esse valor e se o argumento não
puder sofrer essa alteração

Caso o subprograma não altere o parâmetro correspondente, é
recomendável que tal estrutura seja passada por endereço:

Obtém-se economia de memória e melhor desempenho
1.1.6 – Ponteiros
a) Conceitos, declarações e atribuições

Ponteiros são endereços

Existem constantes e variáveis do tipo ponteiro

O endereço de uma variável a declarada num bloco qualquer
permanece o mesmo durante a execução do bloco


Então &a é uma constante do tipo ponteiro
Variável do tipo ponteiro armazena um endereço que pode
ser alterado durante a execução do programa
Exemplo: seja trecho de programa:
int c = 237, b = 15, *q;
double a = 13.5, *p;
p = &a; q = &b;
write ("Endereco(p) = ",
write ("p = ", p);
write ("Endereco(a) = ",
write ("Endereco(q) = ",
write ("q = ", q);
write ("Endereco(b) = ",
write ("Endereco(c) = ",
write ("a = ", a);
write ("b = ", b);
write ("c = ", c);
&p);
&a);
&q);
&b);
&c);
Resultado
Endereco(p)
p
Endereco(a)
Endereco(q)
q
Endereco(b)
Endereco(c)
a
b
c
=
=
=
=
=
=
=
=
=
=
1638204
1638208
1638208
1638216
1638220
1638220
1638224
13.5
15
237
Com este resultado,
pode-se desenhar o
mapa da memória a
seguir
int c = 237, b = 15, *q;
double a = 13.5, *p;
p = &a; q = &b;
1638204
1638208
p
13.5
a
1638220
q
15
b
237
c
1638208
Endereco(p)
p
Endereco(a)
Endereco(q)
q
Endereco(b)
Endereco(c)
a
b
c
Resultado
=
=
=
=
=
=
=
=
=
=
1638204
1638208
1638208
1638216
1638220
1638220
1638224
13.5
15
237
1638216
1638220
1638224
int c = 237, b = 15, *q;
double a = 13.5, *p;
p = &a; q = &b;
1638204
1638208
p
13.5
a
1638220
q
15
b
237
c
1638208
&a é uma constante-ponteiro
para double
&b é uma constante-ponteiro
para int
1638216
1638220
1638224
int c = 237, b = 15, *q;
double a = 13.5, *p;
p = &a; q = &b;
1638204
1638208
p
13.5
a
1638220
q
15
b
237
c
1638208
q é um ponteiro para o tipo int
p é um ponteiro para o tipo
double
p recebe o endereço de a
q recebe o endereço de b
1638216
p aponta para a
q aponta para b
1638220
a é apontada por p
b é apontada por q
1638224
int c = 237, b = 15, *q;
double a = 13.5, *p;
p = &a; q = &b;
1638204
1638208
p
13.5
a
1638220
q
15
b
237
c
1638208
Duas formas de representação
gráfica:
p
13.5
a
1638216
q
15
b
ou simplesmente:
p
q
1638220
13.5
a
15
b
1638224

Considerando-se as declarações:
float a; int c; float *p; int *q;
são possíveis as seguintes atribuições:
p = &a; q = &c;
p = NULL;
p
q
a
c
p
p = (float*) 1776;
p
1776
Acesso ao local apontado por um ponteiro:

Seja a declaração:
int a, b, *p;
p
a
*p

O local apontado por p é referenciado por *p
b
*p

Executando-se p = &a; então *p passa a coincidir com a

Depois, executando-se p = &b;
coincidir com b
então *p passa a

Exemplo: Seja o seguinte trecho de programa:
int a, b = 2, *p;
p = &a;
*p = 1;
b = *p;
a
1
Inicialmente
Prosseguindo
b
21
p

Exemplo: Seja a seguinte declaração:
int a = 2, b = 5, *p = &a, *q = &b;
Inicialmente:
Voltando
Fazendo
pà =
*p
situação
=q;*q; inicial:
a
52
p
b
5
q
Apesar de *p e *q serem iguais, p é diferente de q

As principais utilidades dos ponteiros são:



Alocação dinâmica de variáveis indexadas
Passagem de parâmetros por referência
Encadeamento de estruturas

O encadeamento de estruturas foi a principal razão para a
criação de ponteiros

Ele é usado intensamente em CES-11 Algoritmos e
Estruturas de Dados
b) Relação entre Ponteiros e Variáveis Indexadas:

O nome de uma variável indexada é o endereço do
primeiro de seus elementos


É uma constante-ponteiro apontando para seu elemento
zero
Não tem um local exclusivo na memória

Seja a declaração int A[8];

Representação gráfica de A:
O nome de uma
variável indexada
pode ser atribuído a
um ponteiro de
mesmo tipo
Exemplo: seja o seguinte trecho de programa:
int i, A[8], B[5], *p;
p = A;
p = B;
Inicialmente:
Após
p = B;
A;
A
Proibidos:
p tem localAexclusivo
= p;
B = p;
A e B não têm
A = B;
B = A;
A = &i;
B = &i;
A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7]
p ?
B
p tornou-se equivalente a B:
A:
B[0] B[1] B[2] B[3] B[4]
p[0]  B[0],
A[0], p[1]  A[1],
B[1], ... ,
p[7]  B[4]
p[4]
A[7]
*A  A[0] e *B  B[0]
p se tornou variável indexada

Ponteiros podem ter subscritos e variáveis indexadas
admitem o operador unário ‘*’

Exemplo: supondo as declarações:
int i, A[50], *p;
A[i]
*(p+i)
é equivalente a
é equivalente a
*(A+i)
p[i]
Sabe-se que A contém o endereço de A[0], logo
p=A
equivale a
p = &A[0]
p = A+i
equivale a
p = &A[i]
a[1] tem sempre o mesmo endereço
p[1] pode variar de endereço
c) Alocação dinâmica de memória

Muitas vezes é útil reservar espaço para uma variável
indexada, em tempo de execução

Sabendo-se o seu número de elementos, reserva-se espaço
necessário e suficiente para essa variável

Em primeiro lugar, tal variável deve ser declarada como
ponteiro

Esse tipo de reserva em
tempo de execução
denomina-se alocação
dinâmica

Há uma região da memória
ocupada pelo programa
denominada heap, destinada
a essas alocações dinâmicas

A alocação pode ser feita
pela função malloc

malloc recebe como
argumento o número de
bytes a ser alocado

malloc então reserva na heap
esse número de bytes de
forma contígua

O valor retornado por malloc
é o endereço do primeiro
desses bytes (um ponteiro)

Esses bytes ficam
indisponíveis para novas
alocações

Exemplo: para alocar um vetor de 7 elementos do tipo int:
int *V;
V = (int *) malloc (7 * sizeof (int));
28 bytes
V
int
4 bytes
V pode ser usada
como vetor
heap
Tamanho dos vetores:
typedef int *vetor;
void main () {
Vetor A: 28 39 84 27
int m, i; vetor A, B, C;
Vetor B: 94 27 68 17
write ("Tamanho dos vetores: ");
Vetor C:
94
39
read (m);
A = (int *) malloc (m*sizeof(int));
B = (int *) malloc (m*sizeof(int));
C = (int *) malloc (m*sizeof(int));
write (“\nVetor A: ");
for (i = 0; i < m; i++) read (A[i]);
write (“\nVetor B: ");
for (i = 0; i < m; i++) read (B[i]);
write (“\nVetor C: ");
for (i = 0; i < m; i++)
C[i] = (A[i] > B[i])? A[i]: B[i];
for (i = 0; i < m; i++) write (C[i]);
}
7
Exemplo: seja
o programa à
72 39
esquerda
82 49 10
83
84
27
83
72
39
No vídeo

Quando um espaço assim alocado não for mais usado, ele
pode ser novamente disponibilizado para outras alocações,
mediante o comando
free (A);

Quando um programa faz muitas alocações dinâmicas, ele
pode esgotar a capacidade dessa área especial

Se, durante a execução, algumas dessas alocações perderem
sua utilidade, é bom que elas sejam novamente
disponibilizadas

Isso pode evitar tal esgotamento
d) Alocação dinâmica de matrizes:
Pode feita por
vetores de
ponteiros
Seja A uma
matriz (m x n):
typedef int *vetor;
Exemplo: leitura
typedef vetor *matriz;
uma matriz
void main () {
int m, n, i, j; matriz A;
write ("Dimensoes de uma matriz: "); read (m, n);
A = (vetor *) malloc (m * sizeof(vetor));
for (i = 0; i < m; i++)
A[i] = (int *) malloc (n * sizeof(int));
A
?
?
?
de
5
m
4
n
Dimensões
da matriz
?
?
?
A partir daqui, o ponteiro A
pode ser usado como matriz
typedef int *vetor;
Exemplo: leitura
typedef vetor *matriz;
uma matriz
void main () {
int m, n, i, j; matriz A;
write ("Dimensoes de uma matriz: "); read (m, n);
A = (vetor *) malloc (m * sizeof(vetor));
for (i = 0; i < m; i++)
A[i] = (int *) malloc (n * sizeof(int));
write ("Elementos da matriz:");
for (i = 0; i < m; i++) {
write ("Linha ", i);
for (j = 0; j < n; j++) read (A[i][j]);
}
write ("Confirmacao:");
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++) write (A[i][j]);
write ("\n");
}
}
de
e) Alocação dinâmica e encadeamento de estruturas

Seja o seguinte código:
typedef struct st st;
struct st {int a; float b;};
st *p;
p = (st *) malloc (sizeof(st));
p

a
5
b
17.3
Atribuindo:
(*p).a = 5;
(*p).b = 17.3;
Outra forma de referenciar os campos de uma estrutura
apontada:
p->a = 5;
p->b = 17.3;
equivalem a (*p).a = 5;
(*p).b = 17.3;

Exemplo: sejam as seguintes declarações:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
Um dos campos da
estrutura st é um ponteiro
para a própria st
e os seguintes comandos:
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
a
5
prox

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
a
2
p
prox
a
3
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
a
5
prox

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
a
2
p
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
a
2
p
q
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2
3

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
q
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2
3

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2
3

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
Vídeo
2
3
5

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
for (q = p; q != NULL; q = q->prox)
write (q->a);
prox
q
q == NULL:
Fim!!!
Vídeo
2
3
5

Seja a execução dos comandos:
typedef struct st st;
struct st {int a; st *prox;};
st *p, *q;
p
a
2
prox
a
3
prox
a
5
prox
q
p = (st *) malloc (sizeof(st));
p->a = 2;
p->prox = (st *) malloc (sizeof(st));
p->prox->a = 3;
p->prox->prox = (st *) malloc (sizeof(st));
p->prox->prox->a = 5;
p->prox->prox->prox = NULL;
A constante NULL
for (q = p; q != NULL; q = q->prox)
é muito usada para
write (q->a);
teste de final de
percurso
p
a
2
prox
a
3
prox
a
5
prox
q
Duas formas
de referenciar
este local
p->prox->prox->a
(*(*(*p).prox).prox).a
Qual delas é mais cômoda???

Encadeamento de estruturas é a principal razão para
incorporação de variáveis-ponteiros nas linguagens de
programação

Nesta disciplina será vista grande variedade de alternativas de
armazenamento de informações oferecida por encadeamentos
desse gênero

Modelos como listas lineares, árvores, grafos, estruturas
multi-ligadas muito se beneficiam da inclusão de um ou mais
ponteiros em variáveis do tipo struct
1.1.7 – Recursividade
Formulas recursivas escalares simples:
Fatorial:
n! =
Potenciação:
-1, para n < 0
1, para 0 ≤ n ≤ 1
n * (n-1)!, para n > 1
1, para n = 0
An =
A * An-1, para n > 0
Máximo divisor comum:
mdc (m, n) =
∞, p/ m = 0 e n = 0
m, p/ m > 0 e n = 0
n, p/ m = 0 e n > 0
mdc (n, m%n), p/ m e n > 0
int fat (int
int f;
if (n < 0)
else if (n
else f = n
return f;
}
n) {
f = -1;
<= 1) f = 1;
* fat(n-1);
void main() {
char c; int n;
write ("Calculo do fatorial de n");
write ("Digite n: "); read (n);
write ("Fat(", n, ") = ", fat(n));
}
Exemplo: execução de
um programa com
uma função recursiva
para o cálculo de
fatorial
int fat (int
int f;
if (n < 0)
else if (n
else f = n
return f;
}
n) {
f = -1;
<= 1) f = 1;
* fat(n-1);
void main() {
char c; int n;
write ("Calculo do fatorial de n");
write ("Digite n: "); read (n);
write ("Fat(", n, ") = ", fat(n));
}
5
n
Valor digitado: 5
int fat (int
int f;
if (n < 0)
else if (n
else f = n
return f;
}
n) {
f = -1;
<= 1) f = 1;
* fat(n-1);
fat – v1
fat – v2
fat – v3
n
n
4
n
3
f
24
f
6
5
f 120
f = 5*fat(4)
void main() {
char c; int n;
write ("Calculo do fatorial de n");
write ("Digite n: "); read (n);
write ("Fat(", n, ") = ", fat(n));
}
f = 4*fat(3)
f = 3*fat(2)
fat – v5
fat – v4
n
1
n
2
f
1
f
2
f =1
f = 2*fat(1)
5
n
Valor digitado: 5
Recursividade com variáveis indexadas

Exemplo: formulação recursiva para a procura binária
Vet
-5
-1
4
7
10
14
15
17
21
23
24
30
32
38
45
50
53
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
inf
x
med
sup
ProcBinaria (x, inf, sup, Vet) =
FALSO se (x < Vet[inf] ou x > Vet[sup])
VERDADE se (x == Vet[med])
ProcBinaria (x, inf, med-1, Vet) se (x < Vet[med])
ProcBinaria (x, med+1, sup, Vet) se (x > Vet[med])
- - - - typedef int *vetor;
- - - - logic ProcBinaria (int, int, int, vetor);
A
A[0]
void main () {
int - - - -, n, num; vetor A;
logic estah; - - - - Alocacao, leitura e ordenacao do vetor A
estah = ProcBinaria (num, 0, n-1, A);
- - - - }
logic ProcBinaria (int x, int inf, int sup, vetor Vet) {
int med; logic r;
if (x < Vet[inf] || x > Vet[sup]) r = FALSE;
Vet
else {
med = (inf + sup) / 2;
if (x == Vet[med]) r = TRUE;
else if (x < Vet[med]) r = ProcBinaria (x, inf, med-1, Vet);
else r = ProcBinaria (x, med+1, sup, Vet);
}
return r;
Os ponteiros Vet de todas as chamadas
}
recursivas apontarão para A[0]
Download