Prévia do material em texto
DEPARTAMENTO DE INFORMÁTICA
Programação
Estruturada
Notas de aula
Profa. Beatriz Lux
Prof. Rolf Fredi Molz
Atualizada em fevereiro 2017
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
1
Sumário
2.0 Bibliografia recomendada
2
3.0 Conceitos Gerais
3
4.0 Comandos de controle de fluxo
17
5.0 Tipo Ponteiro
24
6.0 Modularização
33
7.0 Tipo estruturado homogêneo - matrizes
47
8.0 Strings em C
57
9.0 Tipo estruturado heterogêneo - registros
62
10.0 Métodos internos de ordenação
70
11.0 Métodos internos de pesquisa
77
12.0 Recursividade
79
13.0 Operações em meio magnético - arquivos
82
14.0 Estruturas e alocação dinâmica de memória – exemplo com
Lista Encadeada
94
15.0 Anexo – Como adicionar um arquivo Header a um projeto
16.0 Anexo – Tabela ascii
99
101
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
2
2.0 Bibliografia Recomendada
Recomenda-se ao aluno não basear seu estudo apenas no conteúdo destas notas
de aula, sendo importante que busque mais informações através da leitura de livros
dedicados ao estudo de programação estruturada e da linguagem C. Abaixo cita-
mos alguns títulos que consideramos apropriados e que nortearam a produção des-
tas notas de aula. Ambos podem ser encontrados na nossa biblioteca:
SCHILDT, Herbert. C : completo e total. 3. ed., rev. e atual. São Paulo: Makron,
c1997.
MIZRAHI, Victorine Viviane. Treinamento em linguagem C. Volume 1. São
Paulo: Makron, 1990.
MIZRAHI, Victorine Viviane. Treinamento em linguagem C. Volume 2. São
Paulo: Makron, 1990.
ZIVIANI, Nivio. Projeto de algoritmos com implementações em Pascal e C. São
Paulo: Pioneira, 1999.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
3
3.0 Conceitos Gerais
3.1 Objetivos e Caracterização da Linguagem
A linguagem de programação C foi criada por Dennis Richtie,no início da década
de 70, nos laboratórios Bell para ser usada na implementação de sistemas opera-
cionais (UNIX) e outras tarefas de programação de baixo nível.
A linguagem era fornecida junto com o UNIX e manteve sua sintaxe padrão inalte-
rada por cerca de dez anos. A principal documentação deste padrão encontra-se
na publicação "The C Programming Language", de Brian Kernighan e Dennis
Ritchie (K&R), tida como a "bíblia da linguagem C".
Em 1985, ANSI (American National Standards Institute) estabeleceu um padrão
oficial de C, o chamado "C ANSI". Um dos objetivos do processo de padronização
C ANSI foi o de produzir um sobreconjunto do C K&R, incorporando muitas das
características não-oficiais subseqüentemente introduzidas.
C caracteriza-se por ser uma linguagem estruturada, pois possibilita dividir a infor-
mação em blocos estanques, o que é feito através de subprogramas (funções), que
são os blocos fundamentais de um programa em C. Desta maneira, determinada
tarefa pode ser definida e escrita em separado, numa função, utilizando suas pró-
prias variáveis (chamadas locais) de cuja existência, não precisará tomar conheci-
mento o resto do programa ou os demais subprogramas. Este conceito é um dos
tópicos que estudaremos com profundidade em nossa disciplina.
Outra característica de C é sua portabilidade, ou seja, um código escrito em
linguagem C poderá ser executado em diferentes máquinas, independentemente
da sua configuração física e do sistema operacional residente, sendo apenas
necessário sua re-compilação para cada arquitetura diferente
A Linguagem C tornou-se ao longo do tempo uma das linguagens de programação
mais usadas, pois, afora as qualidades acima citadas, possibilita produzir códigos
concisos e de rápido processamento por ser uma linguagem de nível médio, onde
estão combinados elementos de linguagens de alto nível e as funcionalidades de
assembly. Foi utilizada na criação e desenvolvimento de softwares e sistemas ope-
racionais que se tornaram famosos em todo mundo, como por exemplo o Sistema
Operacional Windows.
3.2 Elementos Básicos da Linguagem
3.1 Símbolos usados
Letras de A a Z tanto maiúsculas como minúsculas.
O símbolo _ (caracter de sublinha) é considerado letra.
Digitos de 0 a 9.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
4
Especiais + - * / = ^ # & , : ; ‘ $ e outros
Símbolos especiais usados em conjunto:
= atribuição
!= diferente
<= menor ou igual
>= maior ou igual
{ } delimitadores de inicio e fim de um bloco de comandos
/* inicio de comentário
*/ fim de comentário
// inicio de comentário de aoenas 1 linha (não é necessário fechá-lo)
O tamanho máximo de uma linha é de 127 caracteres.
3.2 Tipos Básicos de Dados
Todas as vaiáveis em C possuem um identificador (nome) e um tipo, que especifica
os valores que a variável poderá assumir. A tabela 1 apresenta o s cinco tipos bá-
sicos, o número de bits que cada um utiliza e a faixa e valores que alcança.
Tipo Bits Faixa de valores
char 8 -128 à 127
int 16 -32768 à 32767
float 32 -3.4E-38 à 3.4E+38
double 64 -1.7E-308 à 1.7E+308
void 0 Sem valor
Tabela 1
3.3 Identificadores
Os identificadores, que servem para nomear programas, variáveis, constantes, pro-
cedimentos, etc., seguem a seguinte regra:
Devem iniciar por uma letra e a esta podem seguir letras, números ou sinal
de sublinha. Não pode conter espaços. Tamanho: até 127 caracteres, sendo
significativos os 32 primeiros. Obs.: atentar para as palavras reservadas
da linguagem que não podem ser usadas.
Em C os primeiros 32 caracteres de um nome de identificador são significan-
tes. Isso quer dizer que duas variáveis com os 32 primeiros caracteres em
comum, diferindo somente no 33º , são consideradas iguais.
A linguagem C é case-sensitive , o que significa que letras maiúsculas e mi-
núsculas são tratadas como diferentes umas das outras. Por isso, media, Me-
dia e MEDia são distintos.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
5
3.4 Palavras reservadas da linguagem
A tabela 2 apresenta as palavras reservadas da linguagem C, que não poderão ser
utilizadas como identificadores.
auto double if static
break else int struct
case entry long switch
char extern register typedef
continue float return union
default for sizeof unsigned
do goto short while
Tabela 2
3.5 Estrutura básica de um Programa em C
Os programas em C são formados por uma ou mais funções, porém sempre haverá
uma (e só uma) função denominada main, onde a execução do programa neces-
sariamente irá começar. Além da main poderá haver outras funções, dependendo
apenas de como o programador dividir as tarefas que seu programa deverá realizar.
Exemplo de um programa simples em C:
#include<stdio.h> //inclusão de biblioteca
#include<math.h> //inclusão de biblioteca
#define v 3.0 // definição de contante
int main()
{ float a,r,q; // declaração de variáveis
printf("Informe um real\n"); //apresentação de dados na tela
scanf("%f",&a); //entrada de dados via teclado
printf("O no.lido foi: %.2f\n", a);
r=sqrt(a);
printf("raiz quadrada de %.2f: %.2f\n",a,r);
q=pow(a,2);
printf("%.2f ao quadrado: %.2f\n",a,q);
printf("%.2f ao quadrado: %.2f",v,pow(v,2));
return 0; //retorno da função main
}
Tela de entrada e saída do programa quando executado (F9):
3.5.1 Diretiva #include
Toda a diretiva, em C, começa com o símbolo # no início da linha.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
6
A diretiva #include inclui o conteúdo de um outro arquivo dentro do programa
atual, ou seja, a linha que contêm a diretiva é substituída pelo conteúdo do ar-
quivo especificado.
No programa exemplo acima necessitamos incluir alguns arquivos que contêm a
declaração de funções de bibliotecas padrão. As bibliotecas, normalmente, pos-
suem a extensão .h e se encontram em algum diretório pré-definido pelo compila-
dor. Sempre que o programa utilizar alguma função da biblioteca-padrão deve ser
incluído o arquivo correspondente.
Apresenta-se a seguir alguns dos principais .h da linguagem C: Descrição
stdio.h - Funções de entrada e saída (I/O)
string.h - Funções de tratamento de strings
math.h - Funções matemáticas
ctype.h - Funções de teste e tratamento de caracteres
stdlib.h - Funções de uso genérico
É importante saber que cada diretiva deve estar em sua própria linha.
3.5.2 – Definição de Constantes
O conceito de constantes em linguagens de programação é atribuir um certo valor
constante (que não pode ser alterado) a um nome, e quando este nome for refe-
renciado dentro do código do programa, será utilizado nas operações o valor atri-
buído a este nome.
Uma das formas de definirmos constantes em C é utilizar a diretiva #define que
é conhecida como diretiva de macro-substituição.
Conforme o tipo da constante, será sua representação:
Constantes de caractere são envolvidas por aspas simples (‘’);
Ex; ‘a’
Constantes formadas por cadeia de caracteres são envolvidas por aspas du-
plas;
Ex: “Tecle algo para sair do programa”
Constantes com valor inteiro são representadas por um número inteiro;
Ex: 10, -100
Constantes com valor real (float) devem apresentar o ponto decimal seguido
da parte fracionária do número;
Ex: 19.55
# define nome “João”
# define pi 3.1416
# define letra ´a´
O compilador substitui o identificador pelo valor cada vez que aquele for encontrado
no programa fonte antes da compilação do programa.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
7
É recomendável o uso de identificadores como sinônimos de constantes pois au-
menta a legibilidade do programa e auxilia sua documentação, além disto, o agru-
pamento das constantes no início do programa torna mais fácil a sua modificação,
caso necessário.
3.5.3 Comentários
É bastante útil e indicado colocar comentários nos códigos de programas para fa-
cilitar sua leitura e elucidar seu objetivo e funcionamento .
Utiliza-se caracteres especiais para indicar comentários:
comentários de uma linha podem ser iniciados com duas barras ( // )
comentários de mais de uma linha iniciam com uma sequencia dos carac-
teres barra asterisco ( /* ) e encerrados com asterisco barra ( */ )
3.5.4 Declaração e variáveis
Todas as variáveis de um programa deverão ser declaradas.
Para declarar uma variável devemos identificador inicialmente seu tipo, a seguir
o nome da variável e encerrar a declaração com ponto e vírgula.
float raio;
int idade;
Se tivermos mais de uma variável com o mesmo tipo, podem ser declaradas na
mesma linha, separadas por vírgula.
float raio, área;
As variáveis são classificadas em variáveis locais e globais.
Variáveis globais são aquelas declaradas fora do escopo das funções.
Variáveis locais são aquelas declaradas no início de um bloco e seus escopos
estão restritos aos blocos em que foram declaradas. A tabela 2 exemplifica as duas
formas de declaração, local e global.
#include<stdio.h>
#include<math.h>
#define v 3.0
float a,r,q;
int main()
{
printf("Informe um real\n");
scanf("%f",&a);
printf("O no.lido foi: %.2f\n ",
a);
r=sqrt(a);
printf("raiz quadrada de %.2f:
%.2f\n", a,r);
return 0;
}
#include<stdio.h>
#include<math.h>
#define v 3.0
int main()
{float a,r,q;
printf("Informe um real\n");
scanf("%f",&a);
printf("O no.lido foi: %.2f\n ",
a);
r=sqrt(a);
printf("raiz quadrada de %.2f:
%.2f\n", a,r);
return 0;
}
Exemplo variável global Exemplo variável local
Tabela 2
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
8
Observações:
É uma prática tradicional do C, usar letras minúsculas para nomes de
variáveis e maiúsculas para nomes de constantes. Isto facilita na hora
da leitura do código;
Quando se escreve código usando nomes de variáveis em português,
evita-se possíveis conflitos com nomes de rotinas encontrados nas di-
versas bibliotecas, que são em sua maioria absoluta, palavras em in-
glês.
3.5.4 A função principal
A linha int main() indica que estamos definindo uma função de nome main. To-
dos os programas em C devem ter uma função main, pois é esta função que será
chamada quando o programa for executado.
O conteúdo da função é delimitado por chaves { }, obrigatoriamente.
Isso significa que { (abre chaves) substitui a palavra inicio que utilizamos em algo-
ritmos e } (fecha chaves) substitui a palavra fim.
O código que estiver dentro das chaves será executado seqüencialmente quando
a função for chamada.
Em C, sempre que necessitarmos delimitar um bloco de comandos que deve ser
executado sequencialmente, utilizamos as chaves, isto se tornará novamente re-
levante quando estudarmos o uso dos comando condicionais e de repetição.
3.5.5 O uso do ponto e vírgula
É importante notar no exemplo que todas as linhas de comando são separadas
por ponto e vírgula, exceto as declarações #include, #define, o cabeçalho da fun-
ção e os demarcadores de blocos (chaves) de instruções.
Durante a compilação do programa, o ponto-e-vírgula mostra ao compilador quando
uma linha de comando termina e quando outra linha de comando se inicia.
Assim, o compilador acusará um erro sempre que verificar a falta de um ponto-e-
vírgula, pois ele não saberá quando termina ou começa um determinado comando
dentro do código digitado.
3.5.6 Operador de atribuição
O operador “=” atribui um valor ou resultado de uma expressão contida a sua direita
para a variável (ou constante) especificada a sua esquerda.
Exemplos:
a=V*2;
b=c*valor+sqrt(x);
É possível fazer atribuição sucessiva de valores: a=b=c=1;
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
9
É possível atribuir um valor a uma variável ao mesmo tempo em que ela é decla-
rada.
int a=5,b=2;
float r=8.6;
3.5.7 Expressões, Operadores e funções aritméticas
Expressões são algoritmos especificados para a computação de valores, consis-
tem em operações com variáveis, constantes e funções combinadas com opera-
dores.
a=v*2; c=a+b; c=a/d;
3.5.7.1 Operadores Aritméticos
Veja na tabela 3 os operadores aritméticos
Operador Ação
+ Soma (inteira e ponto flutuante)
- Subtração ou Troca de sinal (inteira e ponto flutuante)
* Multiplicação (inteira e ponto flutuante)
/ Divisão (inteira e ponto flutuante)
% Resto de divisão (de inteiros)
-- Decremento
++ Incremento
Tabela 3O operador / (divisão)
quando aplicado a variáveis inteiras, fornece o resultado da divisão inteira;
quando aplicado a variáveis em ponto flutuante fornece o resultado da divi-
são "real".
O operador % fornece o resto da divisão inteira entre dois inteiros.
Exemplo:
int a ,b, x, y;
float z , z1, z2;
{ a=17;
b=3;
z=17.0
x = a / b;
y = a % b;
z1 = z / b;
z2 = a/b;
}
ao final da execução destas linhas, os valores calculados seriam
x = 5 y = 2 z1 = 5.666666 z2 = 5.0 .
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
10
Note que, na linha correspondente a z2, primeiramente é feita uma divisão inteira
(pois os dois operandos são inteiros). Somente após efetuada a divisão é que o
resultado é atribuído a uma variável float.
3.5.7.2- Funções Aritméticas Pré-Definidas
São funções implementadas na biblioteca math.h.
Na tabela 4 são apresentadas algumas funções matemáticas em C
resultado função tipo do pa-
râmetro
tipo do re-
sultado
Logaritmo neperiano log(x) real real
Logaritmo base 10 log10(x)
real real
e elevado à potencia x exp(x) real real
Valor absoluto de x fabs(x) real real
abs(x) int int
Decompõe um real x em duas partes:
y recebe a parte inteira e a função devolve
a parte fracionária, ambas como real (dou-
ble)
modf(x,y) real, real real
Arredonda o valor de x para cima ceil (x) real real
Arredonda o valor de x para baixo floor(x) real real
Retorna x elevado a potência y pow(x,y) real real
Raiz quadrada de x sqrt(x ) real real
Retorna o resto da divisão de x por y
Ex: fmod (25.55 , 2) = 1.55
fmod(x,y) real, real real
Seno de x sin(x ) real real
Cosseno de x cos( x) real real
Arco-tangente de x atan( x) real real
Tabela 4
A ativação da função no programa é feita através do identificador, nome da função
e da lista de argumentos (parâmetros), entre parênteses e separados por vírgula.
Obs. : x é o argumento da função, pode ser uma expressão aritmética.
3.5.7.3 Operadores Relacionais
Uma relação é uma comparação realizada entre valores do mesmo tipo. Estes va-
lores são representados na relação por constantes, variáveis ou expressões. O re-
sultado será sempre True ou False. A natureza da relação é indicada por um ope-
rador relacional, descrito na tabela 5.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
11
Operador Ação
> Maior do que
>= Maior ou igual a
< Menor do que
<= Menor ou igual a
== Igual a
!= Diferente de
Tabela 5
3.5.7.4 Operador ternário
Em C podemos expressar relações de uma forma bastante compacta, através do
uso do operador ternário, que serve para substitui o comando if-else.
Como exemplo, na coluna 1 da tabela 6 é apresentada uma construção condicional
com o comando if–else e na coluna 2 a sua correspondente com comando ternário.
If (n1>n2)
maior= n1
else
maior=n2
maior=(n1>n2) ? n1 : n2
Tabela 6
Fica definido na construção que aparece na segunda coluna que haverá uma ava-
liação da expressão lógica e que se resultar verdadeira deverá ser atribuido o valor
de n1 à variavel maior, se resultar falsa, a variável maior receberá o valor de n2.
Forma geral do operador ternário:
condição ? expressão_1 : expressão_2
3.5.7.5 Operadores Lógicos
Os operadores lógicos em C são descritos na tabela 7
Operador Ação
&& E
|| OU
! NÃO
Tabela 7
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
12
3.5.7.6 Operadores de Atribuição Composta
Em C qualquer expressão da forma:
variável = variável operador expressão
pode ser escrita como:
variável operador = expressão
Exemplos:
raiz = raiz * 4; raiz *= 4;
ano = ano + 10; ano += 10;
soma = soma / ( a + b); soma /= (a + b);
i = i % 2; i %= 2;
A tabela 8 mostra que diferentes posicionamentos dos operadores resultados origi-
nam diferentes resultados.
x=10;
y=++x; y recebe 11 e x recebe 11
x=10;
y=x++; y recebe 10 e x recebe 11
int a,b,c,i=3; a:? b:? c:? i:3
a=i++; a:3 b:? c:? i:4
b=++i; a:3 b:5 c:? i:5
c=--i; a:3 b:5 c:4 i:4
Tabela 8
3.5.7.7 Conversão entre tipos de variáveis - type casting
O C permite a mudança de tipo das suas variáveis através do operador (cast), onde
cast é o novo tipo pretendido.
O casting pode ser usado com qualquer um dos tipos simples.
Exemplo:
int k;
char ch = 'A';
k = (int) ch;
Aqui o valor de k será 65 (o código ascii do carácter 'A').
Um uso freqüente do casting é assegurar que a divisão entre inteiros não seja uma
divisão inteira. Basta para isso converter para real o numerador ou denominador.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
13
O outro operando é assim também automaticamente convertido, antes de se efe-
tuar a operação.
3.5.7.8 Operador unário sizeof()
O operador sizeof()retorna o tamanho em bytes da expressão ou tipo fornecido
entre parênteses Por exemplo, suponha que o tipo float tenha quatro bytes então
o operador sizeof(float) retorna o valor 4.
3.6 Funções de entrada e saída de dados
3.6.1 Função de saída de dados formatada
Forma Geral:
printf (“expressão de controle”, lista de argumentos);
Esta função permite escrever na tela e é um comando da biblioteca stdio.h
A expressão de controle pode conter caracteres que serão exibidos na tela e os
códigos de formatação (ou controle) que indicam o formato em que os argumentos
deverão ser impressos.
Cada argumento deve ser separado por vírgula. Os códigos de controle usam a
notação % para formatar variáveis e \ para formatar a disposição na tela (quebra
de linha, tabulação, etc). Para cada formatação de variável na expressão de con-
trole deve haver uma variável na lista de argumentos.
Exemplo:
printf("O MDC entre %d e %d eh: %d\n",m,n,x);
A tabela 9 apresenta códigos de controle para formatar variáveis:
Código Significado
%d inteiro
%f float
%lf double
%c 1 caractere
%s String
%p ponteiro
%% Coloca na tela um %
Tabela 9
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
14
A tabela 10 apresenta os códigos de controle para disposição na tela
Código Significado
\b Retrocesso ("back")
\f Alimentação de formulário ("form feed")
\n Nova linha ("new line")
\t Tabulação horizontal ("tab")
\" Aspas
\' Apóstrofo
\0 Nulo (0 em decimal)
\\ Barra invertida
\v Tabulação vertical
\a Sinal sonoro ("beep")
\N Constante octal (N é o valor da constante)
\xN Constante hexadecimal (N é o valor da constante)
Tabela 10
Na lista de argumentos pode haver variáveis e/ou constantes, sempre separadas
por vírgula, veja o exemplo do trecho de programa abaixo:
int p
p= 65487 * 236597 ;
printf("O Produto entre %d e %d eh: %d\n",65487, 236597 ,p);
A função printf():
Imprime a partir do último caracter impresso.•
Não muda de linha até que a linha acabe ou que encontre um \n.
Alguns exemplos de printf() e o que eles exibem:
printf ("Teste %% %%") -> "Teste % %"
printf ("%f",40.345) -> "40.345"
printf ("Um caractere %c e um inteiro %d",'D',120) -> "Um carac-
tere D e um inteiro 120"
printf ("%s e um exemplo","Este")-> "Este e um exemplo"
printf ("%s%d%%","Juros de ",10) -> "Juros de 10%"
Ao apresentar na tela valores com ponto flutuante, estes serão colocados em nota-
ção científica. Se quisermos apresentar os valores com casas decimais é necessá-
rio acrescentar um formato, como no exemplo abaixo:
#include <stdio.h>
#define V 3
int a;
float b;
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
15
int main()
{ a=V*2;
b=sqrt(a);
printf(" %d %5.2f ",a,b);
return 0;
}
Através desta especificação de formato ( printf(" %d %5.2f ",a,b), o valor de b será
escrito com 5 caracteres no total (incluindo o ponto da casa decimal) e duas ca-
sas depois do ponto decimal.
A tabela 11 apresenta mais exemplos:
Código Descrição
%6f Ponto flutuante com pelo menos seis caracteres
%.2f Ponto flutuante com dois caracteres após o ponto decimal
%6.2f Ponto flutuante com pelo menos seis caracteres e dois após o ponto
decimal
%6d Inteiro com pelo menos seis caracteres
Tabela 11
3.6.2 Função de entrada de dados formatada
Permitem ler dados pelo teclado. Sua sintaxe é semelhante ao printf:
Forma Geral : scanf(“expressão de controle”, lista de argumentos);
A expressão de controle pode conter códigos de formatação, precedidos por %.
A lista de argumentos:
Depende da String de Formato.
Separados por ‘,’.
Sempre variáveis.
As variáveis devem ser precedidas por ‘&’
Exemplo:
int mat;
float media;
int main ()
{ printf(“Entre com a matrícula do aluno e sua media: ");
scanf(“%d %f“, &mat, &media);
-------;
--------;
}
%f indica que será lido um valor do tipo float, atribuído a uma variável na
sequencia indicada na lista de argumentos (&media).
A lista de argumentos consiste então no endereço das variáveis.
C oferece um operador para tipos básicos chamado operador de endereço
&, que retorna o endereço do operando.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
16
Exemplo para o operador de endereços:
int main()
{
int num;
num = 2;
printf (“Valor %d, endereço = %u”, num, &num);
return 0;
}
O programa acima imprime o valor e o endereço de memória da variável num.
%u é usado pois endereço é visto como inteiro sem sinal.
Ex. de saída: Valor=2, endereço = 1370
3.6.3 Funções de Entrada e Saída de dados para caracteres
3.6.3.1 Funções de Entrada e Saída para caracteres (com enter)
getchar()
Biblioteca: stdio.h
Declaração: int getchar(void);
Propósito: A função getchar() (get character) lê um caracter individual da entrada
padrão (em geral, o teclado).
Esta função é dita line buffered, isto é, não retorna valores até que o caracter de
controle line feed (\n) seja lido. Este caracter, normalmente, é enviado pelo teclado
quando a tecla [enter] é pressionada. Se forem digitados vários caracteres, estes
ficarão armazenados no buffer de entrada até que a tecla [enter] seja pressionada.
Então, cada chamada da função getchar() lerá um caracter armazenado no buffer.
putchar()
Biblioteca: stdio.h
Declaração: int putchar(int c);
Propósito: Esta função putchar() (put character) imprime um caracter individual c
na saída padrão (em geral o monitor de vídeo).
getchar retorna inteiro mas é possível atribuir a uma variável char (o que
geralmente é feito).
putchar é declarada para entrada de inteiro mas geralmente é usada com
um argumento caracter.
3.6.3.2 Funções de Entrada e Saída para caracteres (sem enter)
getch(), getche()
Alguns compiladores nào comportam estas funções
Declaração: int getch(void);
int getche(void);
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
17
Ao ser executada, a função getch() (get character) aguarda que uma tecla (ou com-
binação de teclas) seja pressionada, recebe do teclado o código correspondente e
retorna este valor.
A função getche() (get character and echoe) tem o mesmo funcionamento porém
escreve na tela, quando possível, o caracter correspondente.
Exemplo
// Uso das funcoes getchar() e putchar()
#include <stdio.h>
char c;
void main(void)
{
printf("\nDigite uma frase:\n");
do
{
c = getchar(); // leitura do 'buffer'
if(c >= 97 && c <= 122) // se c e' minúsculo...
{
c -= 32; // c=c-32 transforma em maiúsculo
}
putchar(c); // impressao dos caracteres maiúsculos
}while (c != '\n'); // ...enquanto nao e' [enter]
}
4.0 Comandos de controle de fluxo
4.1 Comandos Condicionais
Um comando condicional é usado para selecionar um único comando de seus com-
ponentes para a execução.
4.1.1 Comando if- else
O comando IF especifica que um comando deve ser executado somente se o re-
sultado de uma expressão lógica for verdadeira. Se for falsa, então outro comando
deve ser executado, ou nenhum comando da sua estrutura deve ser executado.
Formas do comando:
A) if (condição)
comando ;
Nesta forma, o comando somente será executado se a condição for TRUE. Caso
contrário, se a condição for FALSE, nenhuma ação será realizada.
O comando a ser executado pode ser simples ou composto.
Um comando será simples quando for apenas uma linha de instrução (seguida de
ponto e vírgula) e será composto quando houver mais de uma linha de instrução,
originando um bloco que deverá ser delimitado por chaves
Ex: if (a = = b )
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
18
{
x = 1.5 ;
y = 2.5 ;
}
B) if (condição)
comando 1; (ou bloco)
else
comando 2; (ou bloco)
Neste caso, se a condição for verdadeira será executado o comando 1 e caso for
falsa será executado o comando 2. Tanto o comando 1 como o comando 2 po-
dem ser simples ou compostos.
Exemplos:
If (a = = b)
x = 1.5;
else
x = 2.5 ;
if (a ==
b)
{ x =
1.5;
x := 1.5 ;
y = 2.5 ;
else
{ x = -1.5 ;
y = -2.5 ;
}
Exemplo: Faça um programa que leia um número e informe se é igual a 10, maior
ou menor que 10.
#include <stdio.h>
int num;
void main (void)
{ printf ("Digite um numero: ");
scanf ("%d",&num);
if (num==10)
{
printf ("\n\nVoce acertou!\n");
printf ("O numero e igual a 10.\n");
}
else
if (num>10)
printf ("O numero e maior que 10.");
else
printf ("O numero e menor que 10.");
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
19
4.1.2 Comando condicional switch
O comando switch é próprio para testar uma variável em relação a diversos valo-
res pré-estabelecidos. É básicamente usado na implementação de menus.
Sua forma geral é:
switch (variável)
{
case constante_1: declaração_1;
break;
case constante_2: declaração_2;
break;
case constante_n: declaração_n;
break;
default declaração_default;
}
A estrutura switch não aceitacondições, apenas constantes. O switch testa
a variável e executa a declaração cujo case corresponda ao valor atual da variável.
A declaração default é opcional e será executada apenas se a variável, que está
sendo testada, não for igual a nenhuma das constantes. Não há necessidade de
chaves envolvendo as instruções de cada case, pois estas instruções não são con-
sideradas um bloco, mas deve haver um conjunto de chaves envolvendo todo o
corpo de cases, incluindo default.
O comando break, faz com que o switch seja interrompido assim que uma
das declarações seja executada. Mas ele não é essencial ao comando switch. Se
após a execução da declaração não houver um break, o programa continuará exe-
cutando os cases do switch, na ordem seqüencial, até o fim do switch.
Exemplo: Faça um programa que leia um número e informe se é igual ou diferente
de 9, 10 e 11 .
#include <stdio.h>
int num;
int main (void)
{ printf ("Digite um numero: ");
scanf ("%d",&num);
switch (num)
{
case 9:
printf ("\n\nO numero e igual a 9.\n");
break;
case 10:
printf ("\n\nO numero e igual a 10.\n");
break;
case 11:
printf ("\n\nO numero e igual a 11.\n");
break;
default:
printf ("\n\nO numero não e nem 9 nem 10 nem
11.\n");
}
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
20
4.2 Comandos de Repetição
4.2.1 Comando for
Forma geral:
for (inicialização;teste;incremento)
comando ou bloco;
Havendo um bloco de comandos, este deverá ser envolvido por chaves.
O comando for é uma instrução de múltiplos usos, não precisando essas três partes
separadas pelo operador ; (ponto-e-vírgula). O que deve ser sempre lembrado é a
interpretação da condição de teste. Tal deve ser pensada com a seguinte frase:
“faça o laço enquanto o teste for verdadeiro”.
Exemplo 1: Faça um programa que imprima os 100 primeiros números naturais,
exceto o zero
#include <stdio.h>
int n;
int main ()
{
for (n=1; n<=100; n++)
printf ("%d ",n);
return 0;
}
Exemplo 2: Faça um programa que imprima os primeiros 20 números pares.
#include <stdio.h>
int main ()
{
int n=2;
for (; n<=40; n+=2)
printf ("%d ",n);
}
No exemplo acima, a parte de inicialização foi omitida para exemplificar que não é
necessária, desde que essa parte seja realizada em alguma outra parte do código.
Expandindo esse conceito, pode-se realizar um loop infinito com a instrução for da
seguinte forma:
#include <stdio.h>
int main ()
{ int n=2;
for (;;)
{ printf ("%d ",n);
n=n*2;
}
return 0;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
21
O exemplo exposto implementa um laço (loop) infinito. Observe que não há a parte
de condição de saída da instrução for. Essa condição de saída, nesse caso, deve
ser colocada dentro do bloco de instrução do loop, por meio de uma instrução con-
dicional if, por exemplo.
4.2.2 Comando while
Forma Geral :
while (condição)
comando ou bloco;
Exemplo:
Faça um programa que imprima os 100 primeiro números naturais, exceto o
zero
#include <stdio.h>
int n;
int main()
{
n= 1;
while (n<=100)
{ printf(“%d”,n);
n++;
}
return 0;
}
4.2.3 Comando do - while
Forma geral:
do
{
instrução;
} while (condição);
Mesmo que a declaração seja apenas um comando é uma boa prática deixar as
chaves. O ponto-e- vírgula final é obrigatório.
A estrutura do-while executa a instrução, testa a condição e, se esta for verdadeira,
volta para a instrução. Este comando, ao contrário do for e do while, garante que a
instrução será executada pelo menos uma vez.
4.3 Listas de Exercícios dos Comandos Condicionais e de Repetição
1- Faça um programa que, dado um par de valores, que representa as coordena-
das de um ponto no plano, determine o quadrante ao qual pertence o ponto, ou
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
22
então se está na origem ou sobre um dos eixos cartesianos. Permitir a repetição
da leitura e do cálculo até que o usuário deseje encerrar o programa.
2- Uma empresa decidiu dar uma gratificação especial a seus funcionários, base-
ada no número de horas extras e no número de horas que o empregado faltou
ao trabalho. O valor do prêmio é obtido pela consulta à tabela abaixo, em que
H é o número de horas extras subtraído do número de horas faltas.
H( horas) Prêmio (R$)
[0,10] 50,00
(10,20] 80,00
(20,30] 110,00
(30,40] 180,00
(40,100] 250,00
Considere que o prêmio deverá ser acrescentado ao salário bruto do funcionário
e sobre este total deve ocorrer um desconto de 8.5% relativo a impostos.
Dados o salário bruto do funcionário, o número de horas que ele faltou e o nú-
mero de horas-extras que fez (considerar horas e minutos do tipo real ex: 8.30),
imprimir o salário bruto, o prêmio obtido pelo funcionário e seu salário líquido.
3- Faça um programa que lendo idade em anos e sexo de um associado de um
clube, conceda desconto na mensalidade a ser paga, observando:
sexo feminino, até 30 anos desconto de 20%
sexo “ 31 a 40 anos desconto de 30%
sexo “ acima de 41 anos desconto de35%
sexo masculino até 25 anos sem desconto
sexo masculino acima de 25 anos desconto de 25%.
Forneça idade e mensalidade a pagar.
O programa deverá ser encerrando quando o usuário digitar ‘F’ respondendo a
pergunta: Repetir o cálculo (s/n)?
4- Faça um programa que, tendo como dados de entrada o custo de 5 produtos e
seus códigos, escreva os seus preços finais (com acréscimo de imposto) e suas
origens, conforme tabela abaixo:
Código Origem Imposto
1 Sul 10%
2 Sudeste 12%
3 Leste 11%
5- Faça um programa que escreva de quantas maneiras diferentes pode-se obter
cada um dos diferentes totais de pontos resultantes do lançamento de dois da-
dos.
6- Faça um programa que leia um valor n, inteiro e positivo, repetindo a leitura caso
o valor esteja fora do estabelecido, calcula e escreve o valor de E, sendo
7- Faça um programa para imprimir na tela os 25 primeiros múltiplos de um número
dado
!
1
...
!3
1
!2
1
!1
1
1
n
E
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
23
8- Faça um programa que simula o lançamento de um dado por 10 vezes e fornece
o número de vezes que saiu cada face do dado. Utilize para simular o lança-
mento as funções rand( ) e srand().
9- Faça um programa para calcular a soma:
S = 1 -1/2 + ¼ - 1/6 + 1/8 + ... + 1/200
10- Faça um programa que lê uma seqüência indeterminada de dois valores inteiros
positivos e forneça o máximo divisor comum pelo processo das divisões suces-
sivas dos dois valores. A leitura encerra quando um dos valores (ou os dois)
for igual a zero.
Cálculo do M.D.C. pelo processo das divisões sucessivas:
Nesse processo efetuamos várias divisões até chegar a uma divisão exata. O
divisor desta divisão é o m.d.c. Acompanhe o cálculo do m.d.c.(48,30).
Regra prática:
1º) dividimos o número maior pelo número menor;
48 / 30 = 1 (com resto 18)
2º) dividimos o divisor 30, que é divisor da divisão anterior, por 18, que é o resto
da divisão anterior, e assim sucessivamente;30 / 18 = 1 (com resto 12)
18 / 12 = 1 (com resto 6)
12 / 6 = 2 (com resto zero - divisão exata)
3º) O divisor da divisão exata é 6. Então m.d.c.(48,30) = 6.
4.4 Exercícios Resolvidos
// Exercicio 5
#include <stdio.h>
int t,d1,d2,m;
int main()
{
for(t=2;t<13;t++)
{
m=0;
for(d1=1;d1<7;d1++)
for(d2=1;d2<7;d2++)
if (d1+d2==t)
m++;
printf("total %2d = %2d maneiras\n",t,m);
}
return 0;
}
// Exercício 8
#include <stdio.h>
#include <stdlib.h>
int r, x, s1,s2,s2,s3,s4,s5,s6;
int main()
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
24
{
s1=s2=s3=s4=s5=s6=0;
srand( time(NULL) );
/* a função time() retorna a hora corrente,
ou -1 se ocorrer um erro. Se for passado 'time'como argumento
a hora corrente é armazenada em 'time'.*/
/* srand( ) estabelece um ponto de partida para a sequencia
gerada por rand() e utiliza a hora do sistema como semente.
*/
for(x=1;x<11;x++)
{
r=1+rand()%6;
/* A função rand() retorna um valor inteiro aleatório entre
0 e 32767, usamos o resto da divisão por seis mais 1 para
garantir no. entre 1 e 6 */
printf("gerou %d\n",r);
switch (r)
{
case 1: s1++; break;
case 2: s2++; break;
case 3: s3++; break;
case 4: s4++; break;
case 5: s5++; break;
case 6: s6++; break;
}
}
printf("1 saiu %d vezes\n",s1);
printf("2 saiu %d vezes\n",s2);
printf("3 saiu %d vezes\n",s3);
printf("4 saiu %d vezes\n",s4);
printf("5 saiu %d vezes\n",s5);
printf("6 saiu %d vezes\n",s6);
return 0;
}
5.0 Tipo Ponteiro
Um endereço de memória é como se fosse um número inteiro que serve para es-
pecificar um byte específico de memória. Podemos imaginar a memória do compu-
tador como sendo um enorme vetor onde cada elemento é um byte, acessível atra-
vés de um número inteiro que é seu “endereço”.
Uma diferença básica entre um inteiro qualquer e um inteiro que representa um
endereço está nas operações que podem ser efetuadas com eles. Enquanto intei-
ros comuns podem ser adicionados, multiplicados, etc., os endereços servem ape-
nas para referenciar posições específicas da memória.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
25
Normalmente, usamos variáveis para armazenar dados (caracteres, reais, inteiros,
etc.). Se um endereço é como se fosse um número inteiro, nada impede que pos-
samos criar também variáveis para armazenar endereços de memória. Estas vari-
áveis especiais são denominadas ponteiros.
“Uma variável do tipo ponteiro serve para armazenar um endereço de memória”.
5.1 Declaração de variáveis do tipo ponteiro
Variáveis Estáticas - O seu valor é referido através do nome da variável, ou seja,
“o nome da variável é considerado uma expressão designatória do seu valor”.
Sua vida útil é:
Variáveis globais: durante toda execução do programa.
Variáveis Locais: durante a execução do subprograma.
Variáveis Dinâmicas - Uma variável dinâmica pode ser criada ou destruída dina-
micamente em qualquer ponto durante a execução de um programa.
O valor de uma variável dinâmica não é referido através de seu nome, mas sim
através de uma ligação ou ponteiro para outra variável em que este valor está
armazenado.
Ao passo que com uma variável estática estamos interessados no valor da variável,
com uma variável do tipo ponteiro estamos interessados no valor para o qual ela
aponta.
Variável Estática :
num 5
Variável Ponteiro:
p 27
Antes de criarmos uma variável ponteiro, é preciso definir que tipo de ponteiro va-
mos usar. Note que, para acessar uma informação na memória, ter apenas o seu
Variável Anônima - aquela cujo
valor não é obtido diretamente
através do seu nome, mas sim
através de uma variável do tipo
ponteiro.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
26
endereço não é suficiente pois é necessário conhecer também o seu tamanho
(quantos bytes devemos acessar a partir do endereço).
Sintaxe da declaração de um ponteiro:
É preciso definir para que tipo de dado o ponteiro vai apontar:
tipo * identificador_da variável;
Exemplos:
uma variável denominada p e que aponta para inteiro:
int *p
dois ponteiros para char:
char *temp,*pt2
Para armazenar o endereço de uma variável ponteiro o compilador reservará 2
bytes (dependendo da máquina).
O valor de uma variável ponteiro não é referido através de seu nome, mas sim
através de uma ligação ou ponteiro para outra variável em que este valor está ar-
mazenado.
5.2 Operadores de ponteiros
5. 2.1 Existem dois operadores especiais para ponteiros:
& operador unário que devolve o endereço do seu operando
* operador unário que devolve o valor da variável localizada no
endereço apontado (é o complemento de &)
Tanto & como * têm precedência maior que qualquer operador aritmético, ex-
ceto o menos unário.
Exemplos de declaração, atribuição e operadores em ponteiros:
int *p, c, q, *px, *py;
c=100;
p=&c;
q=*p;
*px=5;
*py=*py
/* as variáveis p, px e py são do tipo pon-
teiro para inteiros, as variáveis c e q são
do tipo inteiro*/
//c recebe 100
/* p armazena o endereço que a variável c
ocupa na memória, logo p aponta para c*/
/*q armazena o valor que esta armazenado no
// ende-
reço para o qual p aponta (100)
endereço para onde p aponta*/
/* armazena o valor 5 na variável apontada
pelo ponteiro px */
*/armazena na variável apontada por py o
mesmo valor que está na variável apontada
por px (5) */
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
27
Observação: Na declaração, o símbolo * indica o “tipo apontado” em outras
instruções indica “a variável apontada por”.
Um exemplo:
#include <stdio.h>
int main ()
{ int a, *pa;
double b, *pb;
char c, *pc;
// atribuições de endereços
pa = &a; pb = &b; pc = &c;
// atribuição de valores
a = 1; b = 2.34; c = '@';
printf("\n valores:%5d %5.2lf %c", a, b,
c);
printf("\n ponteiros:%5d %5.2lf %c", *pa,
*pb, *pc);
printf("\n enderecos:%p %p %p", pa, pb,
pc);
/* mais atribuições de valores usando os pontei-
ros*/
*pa = 77; *pb = 0.33; *pc = '#';
printf("\n valores :%5d %5.2lf %c", a, b,
c);
printf("\n ponteiros:%5d %5.2lf %c", *pa,
*pb, *pc);
printf("\n enderecos:%p %p %p\n", pa, pb,
pc);
return 0;
}
Ponteiros também são variáveis e, portanto, ocupam posições na memória:
#include <stdio.h>
int main()
{ int a, *pa;
double b, *pb;
char c, *pc;
// atribuições de endereços
pa = &a; pb = &b; pc = &c;
// mostra o endereço das variáveis
printf("\nendereço de a, b e c: %p %p %p", pa, pb, pc);
// mostra o endereço das variáveis de outra forma
printf("\nendereço de a, b e c (outra forma): %p %p %p", &a,
&b, &c);
// mostra o endereço dos ponteiros
printf("\nendereço dos ponteiros: %p %p %p\n", &pa, &pb,
&pc);
return 0;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
28
5.2.2 Operações com Ponteiros
5.2.2.1 Atribuição entre ponteiros
Se temos dois ponteiros, p1 e p2 podemos fazer
p1=p2.
Repareque estamos fazendo com que p1 aponte para o mesmo lugar
que p2 aponta.
Se quisermos que a variável apontada por p1 tenha o mesmo conteúdo
da variável apontada por p2 devemos fazer *p1=*p2.
5.2.2.2 Operações aritméticas com Ponteiros: apenas duas
Incremento de um ponteiro
Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do
mesmo tipo para o qual o ponteiro aponta. Isto é, se temos um ponteiro para um
inteiro e o incrementamos, ele passa a apontar para o próximo inteiro.
Esta é mais uma razão pela qual o compilador precisa saber o tipo de um pon-
teiro: se você incrementa um ponteiro char* ele anda 1 byte na memória e se você
incrementa um ponteiro double* ele anda 8 bytes na memória.
Considere ptr um ponteiro para inteiro que contém o valor atual 2000 e 2 bytes o
tamanho necessário para um inteiro.
Após a expressão:
ptr++
ptr conterá 2002 (e não 2001)
Cada vez que ptr for incrementado, ele apontará para a posição do próximo
inteiro.
Em outras palavras, os ponteiros são incrementados relativamente ao tamanho do
tipo base.
Decremento de um ponteiro
O decremento funciona de forma semelhante ao incremento
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
29
Supondo que p, p1 e p2 sejam ponteiros para inteiros:
p++; // p aponta para o próximo inteiro
p--; // p aponta para o inteiro anterior
p1=p2+3; // p1 aponta para o terceiro inteiro após o inteiro apontado por p2
5.2.2.3 Operadores relacionais
Considerando ponteiros de mesmo tipo, é possível utiliza-los em expressões que
contenha operadores relacionais:
podemos saber se dois ponteiros são iguais ou diferentes (== e !=).
no caso de operações do tipo >, <, >= e <= estamos comparando qual pon-
teiro aponta para uma posição mais alta na memória. Então uma compara-
ção entre ponteiros pode nos dizer qual dos dois está "mais adiante" na me-
mória.
A comparação entre dois ponteiros se escreve como a comparação entre ou-
tras duas variáveis quaisquer:
If (p1<p2)
printf(“p1 aponta para uma memória mais baixa que p2\n”);
5.2.2.4 Operações não permitidas com ponteiros
Não é possível:
dividir ou multiplicar ponteiros,
adicionar dois ponteiros,
adicionar ou subtrair floats ou doubles de ponteiros.
5.3 Ponteiros para Ponteiros
Um ponteiro para ponteiro pode ser imaginado com a situação de anotarmos em
um papel o local onde está guardado o endereço da casa de um amigo.
Declaração de um ponteiro para um ponteiro:
tipo_da_variável **nome_da_variável;
onde:
**nome_da_variável é o conteúdo final da variável apontada;
*nome_da_variável é o conteúdo do ponteiro intermediário.
Um exemplo:
#include <stdio.h>
int main()
{
float fpi = 3.1416, *pf, **ppf;
pf = &fpi; /* pf armazena o endereco de fpi */
ppf = &pf; /* ppf armazena o endereco de pf */
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
30
/* imprime o valor armazenado no endereço apontado por pf */
printf("valor armazenado no endereço apontado por pf: %f\n",
*pf);
/* Imprime o valor armazenado no endereço apontado pelo ponteiro
(pf) para o qual ppf aponta */
printf("Valor armazenado no endereço apontado por ppf: %f\n",
**ppf);
return 0;
}
5.4 Inicialização de ponteiros – a constante “NULL”
A biblioteca stdio.h define uma macro especialmente para que façamos a iniciali-
zação de um ponteiro, informando que ele não tem um endereço associado
É muito importante que saibamos para onde o ponteiro está apontando, ou seja:
nunca use um ponteiro que não foi inicializado.
O exemplo abaixo ilustra esta situação:
int main ()
/* Nao Execute!!! */
{ int n,*p;
n=22;
*p=n;
Return 0;
}
Este programa compilará e rodará. Mas o ponteiro p pode estar apontando para
qualquer lugar.
Estamos gravando o número 22 em um lugar desconhecido.
Com um número apenas, não vamos ver nenhum defeito. Porém se tivermos por
ábito gravar números em posições aleatórias no computador, o micro poderá tra-
var.
!! Inicialize sempre os seus ponteiros com o valor NULL !!
5.5 Alocação dinâmica de memória
Considerando a declaração abaixo:
int *p
identificamos que p é uma variável que aponta para valores inteiros (ou seja, arma-
zena seu endereço).
No início da execução de um bloco em que esta variável é declarada, a variável p
é criada (tal como acontece com as varáveis estáticas). Depois de sua criação p
existe, embora não exista ainda a variável do tipo int para onde ela aponta,
situação que pode ser representa como no esquema abaixo:
p ?
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
31
Para que p possua um endereço (aponte para um endereço), é preciso que faça-
mos uma alocação dinâmica de memória (um pedido de memória do tamanho cor-
reto para armzenar um valor inteiro).
A alocação dinâmica permite ao programador alocar memória para variáveis
quando o programa está sendo executado. Assim, poderemos definir, por exemplo,
um vetor ou uma matriz cujo tamanho descobriremos em tempo de execução. A
memória alocada pelas funções de alocação dinâmica é obtida do heap.
O heap é a região de memória livre que
se encontra entre o programa (com a
área de armazenamento permanente)
e a pilha (stack).
O tamanho do heap é, a princípio, des-
conhecido do programa.
Existe uma função especial em C para alocarmos memória no heap, a função
malloc().
5.5.1 Função malloc
A função malloc() serve para alocar memória de forma dinâmica.
Sintaxe: void malloc (int size);
Biblioteca: stdlib.h
A função recebe o número de bytes que queremos alocar (size), aloca na memó-
ria e retorna um ponteiro void * para o primeiro byte alocado.
O ponteiro void * pode ser atribuído a qualquer tipo de ponteiro por isso, deve ser
utilizado sempre um typecasting (conversão para o tipo apropriado).
Se a memória for alocada no topo do heap, o heapPointer é atualizado (incremen-
tado de size).
Ex: sendo p um ponteiro para inteiro fazemos o typecasting para inteiro
p= (int *) malloc( sizeof(int) );
como não sabemos necessariamente o comprimento de um inteiro (2 ou 4
bytes dependendo do compilador), usamos como parâmetro sizeof(int).
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
32
Se não houver memória suficiente para alocar a memória requisitada a função
malloc() retorna NULL.
Após realizar a alocação de um endereço, nosso ponteiro p já possuirá uma variá-
vel anônima a ele associada:
p ?
Porém, a variável anônima associada a p não está armazenando ainda nenhum
valor. Para isso fazemos uma atribuição (ou uma leitura):
*p = 97;
o que pode ser graficamente representado como:
p 97
Exemplo e esquema representativo da memória durante a alocação dinâmica de
memória:
#include <stdlib.h>
int *pi; (1)
int main ()
{
pi = (int *) malloc (sizeof(int)); (2)
*pi=97; (3)
return 0;
}
(1) (2) (3)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
33
5.5.2 Função free()
A função free(p) libera a região de memória apontada por p para uso.
O tamanho liberado está implícito, isto é, é igual ao que foi alocado anterior-
mente por malloc.
Caso p seja um ponteironulo nenhuma ação é executada.
Declaração: void free(void *p);
Biblioteca: stdlib.h
O trecho de código abaixo aloca dinâmicamente um inteiro e depois o libera:
#include <stdlib.h>
int main(int)
{
int *pi;
pi = (int *) malloc (sizeof(int));
...
…
free(pi);
}
6.0 Modularização (subprogramas)
6.1 Introdução ao conceito de modularização
Exemplos de modularização, i.e., sistemas que são compostos por módulos com
funções bem definidas e tão independentes quanto possível, são bem conheci-
dos. Um destes exemplos que pode ser citado, são os sistemas (domésticos ou
não) de som, onde há um módulo responsável pela amplificação, outro para sinto-
nizar rádio, outro para amplificar o som, ler CDs e assim por diante.
A divisão de um sistema em módulos tem várias vantagens. Para o fabricante, a
modularização tem a vantagem de reduzir a complexidade do problema, dividindo-
o em subproblemas mais simples, que podem inclusive ser resolvidos por equipes
independentes. Sob o ponto de vista da fabricação, é mais simples alterar a com-
posição de um módulo, por exemplo, porque se desenvolveram melhores circuitos
para o amplificador, do que alterar a composição de um sistema integrado. Por
outro lado, é mais fácil detectar problemas e resolvê-los, pois os módulos são, em
princípio, razoavelmente independentes. Claro que os módulos muitas vezes não
são totalmente independentes. Por exemplo, o sistema de controle à distância de
uma aparelhagem implica interação com todos os módulos simultaneamente.
A arte da modularização está em identificar claramente que módulos devem existir
no sistema, de modo a garantir que as ligações entre os módulos sejam minimiza-
das e que a sua coesão interna seja máxima. Isto significa que, no caso de um
bom sistema de alta fidelidade, os cabos entre os módulos são simplificados ao
máximo e que os módulos contenham apenas os circuitos que garantem que o mó-
dulo faça sua função. A coesão tem, portanto, a ver com as ligações internas a
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
34
um módulo, que idealmente devem ser maximizadas. Normalmente, um módulo é
coeso se tiver uma única função, bem definida.
Para quem utiliza, por outro lado, a modularização tem como vantagem principal
permitir a alteração de um único módulo sem ter de comprar um sistema
novo. Claro que para isso acontecer o novo módulo tem de (1) ter a mesma função
do módulo substituido e (2) possuir uma interface idêntica (os mesmo tipo de cabos
com o mesmo tipo de sinal eléctrico). Isto é, os módulos, do ponto de vista do
utilizador, funcionam como "caixas pretas" com uma função bem definida e com
interfaces bem conhecidas. Para o utilizador o interior de um módulo é irrelevante.
Mas a modularização tem outras vantagens: o amplificador pode no futuro ser reu-
tilizado num sistema de vídeo, por exemplo, evitando a duplicação de circuitos com
a mesma função.
As vantagens da modularização são muitas e foi introduzida na programação a
partir das linguagens como o C e o Pascal. É um dos métodos usados em progra-
mação para desenvolvimento de programas de grande escala mas é útil mesmo
para pequenos programas, quanto mais não seja pelo treino que proporciona.
Vantagens da modularização para a programação:
1. Facilita a detecção de erros, pois é em princípio simples verificar qual é o
módulo responsável pelo erro.
2. É mais fácil testar os módulos individualmente do que o programa com-
pleto.
3. É mais fácil fazer a manutenção (correcção de erros, melhoramentos, etc.)
módulo por módulo do que no programa total. Além disso, a modulariza-
ção aumenta a probabilidade dessa manutenção não ter consequências
nefastas nos outros módulos do programa.
4. Permite o desenvolvimento independente dos módulos. Isto simplifica o
trabalho em equipe, pois cada elemento, ou cada sub-equipe, tem a seu
cargo apenas alguns módulos do programa.
5. A mais evidente vantagem da modularização em programas de pequena
escala, mas também nos de grande escala, é a possibilidade de reutiliza-
ção do código desenvolvido.
Um programador assume, ao longo do desenvolvimento de um programa, os dois
papeis descritos acima: por um lado é fabricante, pois é sua responsabilidade de-
senvolver módulos; por outro é utilizador, pois fará com certeza uso de outros mó-
dulos, desenvolvidos por outrem ou por ele próprio no passado. Esta é uma noção
muito importante. É conveniente que um programador possa ser um mero utilizador
dos módulos já desenvolvidos, sem se preocupar com o seu funcionamento interno:
ele sabe qual a interface do módulo e qual a sua função, e usa-o. Isto permite
reduzir substancialmente a complexidade da informação que o programador tem
de ter presente na sua memória, conduzindo por isso a substanciais ganhos de
produtividade e a uma menor taxa de erros.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
35
Em nosso estudo, até agora, já entramos em contato com o uso de módulos pron-
tos. São as funções que utilizamos para limpar a tela, escrever na tela, fazer en-
trada de dados e outras.
Existem basicamente duas formas de criar subprogramas: utilizar procedimentos
ou funções. A linguagem C só permite a utilização de funções , por isso enfocare-
mos basicamente funções em nossas aulas.
Um procedimento é um conjunto de instruções, com interface bem definida, que
pode receber e enviar dados para quem o acionou, normalmente o programa prin-
cipal.
Uma função por sua vez, também segue a definição acima, com a diferença de que
devem ter obrigatóriamente um tipo definido para o valor que a função retorna. Em
outras palavras, funções só não retornam um valor se as definirmos como void.
Uma vez definida ("fabricada"), uma função, ou um procedimento, podem ser utili-
zados sem que se precise conhecer o seu funcionamento interno, da mesma forma
que o usuário da aparelhagem de som não está muito interessado nos circuitos
dentro do amplificador, mas simplesmente nas suas características. Claro, para que
funcuione adequadamente, é preciso saber conectar o equipamento ao sistema. O
mesmo acontece com as funções e os procedimentos.
Tomemos como exemplo uma função que é utilizada para gerar automáticamente
números aleatórios, a função rand. Para poder utilizá-la devemos conhecer primeiro
seu protótipo (cabeçalho da função). O protótipo irá nos mostrar qual o tipo e os
parâmetros de rand. Onde procurar esta informação? Tenha sempre a mão um bom
livro de C ou utilize o help do turbo C. O protótipo é:
int rand(void)
int informa que a função retorna um valor inteiro
rand é seu nome
e (void) indica que ela não requer parâmetros.
Outro exemplo é a função matemática sqrt. Esta função, como já estudamos, in-
forma como resultado (retorna) a raiz quadrada de um número. Para fazer este
cálculo devemos colocar entre parênteses (como parâmetro) o valor ou a variável
que servirá para fazer o cálculo da raiz quadrada e devemos declarar uma variável
apropiada para armazenar o resultado. Veja na tabela 4.
Exemplo:
#include math.h
float r,n;
int main ()
{ r=sqrt(16); // r armazena o retorno da função cujo parâmetro
é 16*/
------;
------;
return 0;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
36
Para usarmos funções prontas, portanto, não necessitamos conhecer o seu código,
porém é importante conhecer seu protótipo (tipo que retorna, nome da função e
parâmetros que requer).
Mas nem sempre utilizaremos apenas funções prontas, muitas vezes “fabricare-
mos” nós mesmos nossas funções.
6.2 Definiçãode funções em C
Outra observação: Se não declaramos o tipo de uma função ela será considerada
int.
6.3 Escopo de uma variável (região de validade) tipos de parâmetros e retorno
de funções
Variáveis Globais (Estáticas): são declaradas fora das funções e são reco-
nhecidas em todo o programa (em main e inclusive nos demais subprogra-
mas).
Variáveis Locais: são declaradas no bloco da função e são válidas apenas
na função. São alocadas através da requisição de espaço na pilha (stack)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
37
e permanecem na memória somente durante a execução da função, deixando
de existir quando a função é encerrada.
Exemplo2: Faça um programa que leia três valores reais que representam a base
maior, base menor e altura de um trapézio, calcule e informe a área do trapézio.
#include <stdio.h>
int main ()
{
float bmai,bmen,h,area;
printf("Digite a base
maior\n");
printf("Digite a base me-
nor\n");
printf("Digite altura do tra-
pezio\n");
scanf("%f%f%f",&bmai,&bmen,&h);
area=(bmai+bmen)/2*h;
printf("area do trapezio:
5.2f\n",area);
return 0;
}
#include <stdio.h>
void calculo (float ma, float me,
float altura);
int main ()
{
float bmai,bmen,h;
printf("Digite a base maior\n");
printf("Digite a base menor\n");
printf("Digite altura do trape
zio\n");
scanf("%f%f%f",&bmai,&bmen,&h);
calculo(bmai,bmen,h);
}
void calculo (float ma, float me,
float altura)
{
float area;
area=(ma+me)/2*altura;
printf("Area: %5.2f",area);
}
Ex2: sem função Ex2: com função e parâmetros, sem retorno
É importante observar que, antes da função main, foi feita a declaração do protótipo
da função, que consiste em colocar seu cabeçalho seguido de ponto e vírgula. Isto
é feito para informar ao compilador a existência do código da função após main.
Para evitar a declaração do protótipo, a função poderia ser implementada antes de
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
38
main, colocando exatamente as mesmas instruções que foram colocadas após
main, sem o ponto e vírgula ao final da linha do cabeçalho.
Portanto, sempre que implementarmos o código de funções após main, deveremos
fazer a declaração de seu protótipo antes de main. Apenas as funções que tiverem
como retorno o tipo int não precisarão seguir esta regra.
A função calculo foi implementada com 4 variáveis locais:
bma, bme, alt: são declaradas entre parênteses, pois são parâmetros da função.
area: declarada no corpo da função porque não é parâmetro
A função calculo não precisa retornar nada para quem a chamou, logo seu tipo é
void.
Ao ser acionada uma função com parâmetros, devemos informar os valores, entre
parênteses e estes ficarão armazrnados nas variáveis locais declaradas dentro dos
parênteses, na mesma ordem em que são colocados.
No exemplo teremos a variável bma passando seu valor para o parâmetro ma, te-
remos a variável bme passando seu valor para o parâmetro me e a variável h pas-
sando seu valor para o parâmetro altura.
Assim, existirá uma comunicação de informações entre main e a função calculo que
é a passagem destes valores, lidos em main e sem os quais a função não poderia
efetuar o calculo.
Esta comunicação se dá somente no sentido de main para função (e não vice-
versa) e é conhecida como passagem de parâmetros por valor.
Neste exemplo a função nada retorna, visto que ela mesma informa o resultado do
cálculo, mas poderíamos entender que seria melhor se a função retornasse a área
calculada para quem a acionou.
O exemplo abaixo apresenta uma forma de resolução onde a área calculada é o
retorno da função .
#include <stdio.h>
float calculo (float ma, float me, float altura);
int main ()
{
float bmai,bmen,h,a;
printf("Digite a base maior\n");
printf("Digite a base menor\n");
printf("Digite altura do trapezio\n");
scanf("%f%f%f",&bmai,&bmen,&h);
a=calculo(bmai,bmen,h);
printf("Area: %5.2f",area);
return 0;
}
float calculo (float ma, float me, float altura)
{ float area;
area=(ma+me)/2*altura;
return(area)
}
Ex3: resolução com retorno da função
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
39
Observe autilização do comando return antes de encerrar a função. Através deste
comando estamos informando qual o retorno desta função, no caso o valor arma-
zenado na variável local área, que por isso deve ser do mesmo tipo da função.
Outra forma de passarmos valores para “fora” de uma função é através dos parâ-
metros. Podemos declarar parâmetros de forma que eles não só recebam um valor
de quem aciona a função mas ao mesmo tempo retornem um valor para a variável
que se corresponde com o parâmetro.
Esta forma de passagem de parâmetros é chamada de passagem por referência
e consiste em informarmos o endereço da variável no acionamento da função. Por
sua vez, o parâmetro correspondente deverá ser declarado como tipo ponteiro,
para poder armazenar este endereço.
Exemplo: Faça um programa que leia 3 valores inteiros (a,b e c) e escreva-os or-
denados, de forma que em a esteja o menor valor e em c o maior.
#include <stdio.h>
int main ()
{
int a,b,c,aux;
printf("Informe os tres inteiros\n");
scanf("%d %d %d",&a,&b,&c);
if (a>b)
{aux=a;
a=b;
b=aux;
}
if (a>c)
{aux=a;
a=c;
c=aux;
}
if (b>c)
{aux=b;
b=c;
c=aux;
}
printf("a=%d b=%d c=%d",a,b,c);
return 0;
}
Percebe-se que a cada vez que o teste entre as variáveis for verdadeiro será reali-
zado um mesmo bloco de instruções, que troca as variáveis envolvidas no teste.
Este trecho de código pode ser implementado em uma função que receba os valo-
res e devolva-os trocados, como é mostrado abaixo, realizando uma passagem de
parâmetros por referência.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
40
#include <stdio.h>
void troca(int *x, int *y);
int main ()
{
int a,b,c;
printf("Informe os tres inteiros\n");
scanf("%d %d %d",&a,&b,&c);
if (a>b)
troca(&a,&b);
if (a>c)
troca(&a,&c);
if (b>c)
troca(&b,&c);
printf("a=%d b=%d c=%d\n",a,b,c);
return 0;
}
void troca (int *x, int *y)
{
int aux;
aux=*x;
*x=*y;
*y=aux;
}
Variáveis Locais Estáticas
É possível, quando da declaração de variáveis prefixá-las com o qualificador static.
As variáveis locais estáticas são inicializadas uma só vez e não desapare-
cem quando a função a que pertencem termina, podendo no entanto ser
acessadas apenas dentro da função.
As variáveis locais estáticas também mantêm o seu valor de umas chama-
das para as outras, como se vê nos exemplos seguinte:
Exemplo:
#include <stdio.h>
void func (int x);
int main () // função principal
{ func (10);
func (20);
func (30);
return 0;
}
void func (int x)
{ /* declaração de 1variável local, porém estática,
e por isso, permanece com o valor da chamada anterior*/
static int s = 0;
printf("\n valor de s no inicio da funcao = %5d", s);
s = s + x;
printf("\n valor de s no final da funcao = %5d", s);
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
41
6.4Inclusão de arquivos de cabeçalho com funções criadas pelo usuário
Podemos criar um arquivo cabeçalho (*.h) com as funções que acharmos apropri-
adas, geralmente aquelas que são muito utilizadas. Abaixo apresenta-se o mesmo
exemplo utilizado para demonstrar passagem de parâmetros por referência, visto
anteriormente. Agora, porém, a função que faz a troca entre os valores está imple-
mentada em um arquivo externo, chamado uteis. h. Veja abaixo:
#include <stdio.h>
#include"d:\tc\bin\A09_01\
Ex_incl\uteis.h"
int main ()
{
int a,b,c,aux;
printf("Informe os
tres inteiros\n");
scanf("%d %d
%d",&a,&b,&c);
if (a>b)
troca(&a,&b);
if (a>c)
troca(&a,&c);
if (b>c)
troca(&b,&c);
printf("a=%d b=%d
c=%d",a,b,c);
return 0;
}
//arquivo .h que possui uma funcao
para trocar dois inteiros
//-----------------------------------
//cabeçalho da função
void troca(int *x, int *y);
//-----------------------------------
// desenvolvimento da função
void troca(int *x, int *y)
{ int aux;
aux=*x;
*x=*y;
*y=aux;
}
OBS: quando delimitamos o arquivo cabeçalh < > estamos direcionando sua
busca no diretorio padrao de inclusao (a pasta include do diretorio tc), quando uti-
lizamos " " significa o diretorio corrente, ou podemos especificar um caminho.
Para criar um arquivo header acione o menu File - New e depois File outra vez.
Escolha header:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
42
6.5 Exercícios propostos
1. Escreva uma função que dadas as notas de prova p1 e p2 e as notas dos exer-
cícios-programa ep1, ep2 e ep3 de um aluno, devolve o valor da média final
deste aluno. A média final do aluno é dada por (3p+ep)/4, onde p = (p1+2p2)/3
e ep = (ep1+2ep2+3ep3)/6.
2. Idem, acrescentando se p<5 ou ep<6 a média final é o mínimo entre 4.5, p e
ep.
3. Fazer um programa que possibilite várias opções de cálculos a partir de um
menu. O programa chamará a função correspondente a cada cálculo.
a- S = 1/1 + 3/2 + 5/3+........+ 99/50
b- S = 1/1 - 2/2 + 3/3 -..........- 10/10
c- S = 1000/1 - 997/2 + 994/3.........
d- S = 480/10 - 475/11 + 470/12 - .......
e- Sair
Obs.: Nas opções c e d fazer os cálculos para os 20 primeiros termos.
O menu deverá ficar disponível até ser escolhida a opção e.
4. Faça uma função que receba como argumento os valores dos lados de um tri-
ângulo, a função deverá retornar 0 se triângulo for equilátero, 1 se for isósce-
les ou 2 se for escaleno.
5. Fazer um programa em C que solicita o total gasto pelo cliente de uma loja,
imprime as opções de pagamento, solicita a opção desejada e imprime o valor
total das prestações (se houverem).
a - Opção a vista com 10% de desconto
b- Opção em duas vezes (preço da etiqueta)
c- Opção de 3 até 10 vezes com 3% de juros ao mês (somente para compras
acima de R$ 100,00).
OBS: fazer uma função que imprime as opções, solicita a opção desejada e
retorna a opção escolhida.
No programa principal, testar a opção escolhida e ativar a função correspon-
dente (uma função para cada opção).
6. Faça um programa para ler valores inteiros até ser digitado o valor -1 (tarefa 1)
e fornecer o fatorial de cada valor lido (tarefa2).
7. Faça um programa que verifica quais dentre os 1000 primeiros números natu-
rais são número perfeito. Número perfeito é todo número que, somando seus
divisores, esta soma resulta nele mesmo. Utilize uma fção para a verificação
de cada um dos números. A função main deve informar o resultado.
Ex: 6 é divisível por 1 6 é divisível por 2 6 é divisível por 3
1+2+3 = 6 é quadrado perfeito.
8. Faça um programa que lê os seguintes dados relativos a 30 alunos de uma
escola de futebol: altura; data de nascimento (tarefa de main). O programa de-
verá fornecer como saída: a idade média dos alunos (tarefa2); a altura média
dos alunos (tarefa 3).
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
43
9. A prefeitura de uma cidade fez uma pesquisa entre seus habitantes, coletando
dados sobre o salário e o número de filhos. A prefeitura deseja saber:
média do salário da população (utilize 1 função);
média do número de filhos ((utilize 1 função);
maior salário (utilize 1 função);
percentual de pessoas com salário até R$920,00 (utilize 1 função).
Encerrar a leitura dos dados com salário negativo.
10. Faça um programa onde main imprima na tela os ‘n’ primeiros números primos,
onde ‘n’ será fornecido pelo usuário. Utilize uma função para verificar se o nú-
mero é primo.
11. Dado n e p inteiros, n,p >= 0, calcular as combinações de n elementos p a p,
isto é: n! / (p! * (n-p)! ). Utilize funções.
12. A sequência de Fibonacci é a seguinte: 1, 1, 2, 3, 5, 8, 13, 21, ...
os dois primeiros termos são iguais a 1. Cada termo seguinte é igual `a soma
dos dois anteriores. Escreva um programa onde main solicita ao usuário o nú-
mero do termo e calcule através de uma função o valor do termo. Main deverá
escrever o valor do termo. Por exemplo: se o número fornecido pelo usuário for
igual a 7, main deverá imprimir 13.
13. Escreva um programa em que main solicite ao usuário três números inteiros a,
b, e c onde a é maior que 1. Uma outra função deve somar todos os inteiros
entre b e c que sejam divisíveis por a. Main deve informar a soma.
6.6 Exercícios Resolvidos
Exercício 1
#include <stdio.h>
#include <stdlib.h>
float media(float p1,float p2,float p3,float ep1,float ep2,float ep3);
int main()
{ float p1,p2,p3,ep1,ep2,ep3;
printf("Informe as notas das tres provas:\n");
scanf("%f%f%f",&p1,&p2,&p3);
printf("Informe as notas dos tres exercícios:\n");
scanf("%f%f%f",&ep1,&ep2,&ep3);
printf("A media eh: %.2f",media(p1,p2,p3,ep1,ep2,ep3));
return 0;
}
float media(float p1,float p2,float p3,float ep1,float ep2,float ep3)
{ float p,ep,m;
p=(p1+2*p2)/3;
ep=(ep1+2*ep2+3*ep3)/6;
m=(3*p+ep)/4;
return(m);
}
Exercício 4
#include <stdio.h>
#include <stdlib.h>
int triangulo (float a, float b, float c);
int main()
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
44
{ float a,b,c;
int tipo;
printf("Informe os lados do triangulo\n");
scanf("%f%f%f",&a,&b,&c);
tipo=triangulo(a,b,c);
switch(tipo)
{ case 0: printf("\n Equilatero");
break;
case 1: printf("\n Isosceles");
break;
case 2: printf("\n Escaleno");
break;
case 3: printf("\n Não formam um triangulo");
break;
}
return 0;
}
int triangulo (float a, float b, float c)
{ int r;
if(a<=b+c && b<=a+c && c<= a+b) // teste para saber se podem formar
triangulo
if(a==b && b==c)
r=0;
else
if(a!=b && b!=c && a!=c)
r=2;
else
r=1;
else
r=3;
return (r);
}
Exercício 8
#include <stdio.h>
#include <time.h>
int idade(int day, int month, int year,int d,int m,int a);
float acum_altura(float a);
int acum_idade(int i);
int main()
{ const max= 3;
char nome[15];
int x,dia,mes,ano,d,m,a,i;
float alt,sh,si;
struct tm *local;
time_t t;
t= time(NULL);
local = localtime(&t);ano = 1900 + local->tm_year;
mes = 1 + local->tm_mon;
dia = local->tm_mday;
printf("%d\\%d\\%d\n", dia,mes, ano );
for (x=1; x<=max; x++)
{ printf("Digite nome, altura e data de nascimento\n");
scanf("%s",nome);
scanf("%f%d%d%d",&alt,&d,&m,&a);
i=idade(dia,mes,ano,d,m,a);
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
45
si=acum_idade(i);
sh=acum_altura(alt);
printf("\nidade=%d,idade_acumulada=%0.2f,altura_acumulada=
%0.2f\n",i,si,sh);
}
printf("\nIdade media: %0.2f",si/(x-1));
printf("\nAltura media: %0.2f",sh/(x-1));
return 0;
}
int idade(int day, int month, int year,int d,int m,int a)
{ int i;
if (year>a)
{ if (month>m)
i=year-a;
else
if (month==m)
{ if (day>=d)
i=year-a;
else
i=year-a-1;
}
else
i=year-a-1;
}
else
i=0;
return (i);
}
int acum_idade(int i)
{
static int si=0;
si+=i;
return si;
}
float acum_altura(float a)
{
static float sa=0;
sa+=a;
return(sa);
}
Exercício 9
#include <stdio.h>
#define MIN 420.00
float acum_real(float sal);
int acum_inteiro(int n);
float maior_real(float sal, float maior);
int conta_se(float sal, float valor);
int main()
{ float s, ms, maior=0;
int nf,c1=0,c2=0 ,mf;
printf("Informe o salario:\n");
scanf("%f",&s);
while (s>=0)
{ printf("Informe o no. de filhos\n");
scanf("%i",&nf);
c1++;
ms=acum_real(s);
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
46
mf=acum_inteiro(nf);
maior=maior_real(s,maior);
c2=conta_se(s,MIN);
printf("Informe o salario:\n");
scanf("%f",&s);
}
printf("media salarial: %5.2f\n",ms/c1);
printf("media de filhos: %5.2f\n",(float)mf/c1);
printf("maior salario: %5.2f\n",maior);
printf("percent. de pessoas com salario ate %5.5f: \n",MIN,
c2*100/(float)c1);
return 0;
}
float acum_real(float sal)
{ static float ar=0;
ar=ar+sal;
return(ar);
}
int acum_inteiro(int n)
{ static int ai=0;
ai=ai+n;
return (ai);
}
float maior_real(float sal, float maior)
{ if(sal>maior)
maior=sal;
return(maior);
}
int conta_se(float sal, float valor)
{ static int c=0;
if(sal<=valor)
c++;
return(c);
}
Exercício 13
#include <stdio.h>
int soma(int a, int b, int c);
//-----------principal------------------------------------
int main(void)
{
int a,b,c;
printf("Informe 3 valores inteiros positivos, a,b e c\n");
printf("Digite a>1 e b<c \n");
scanf("%d%d%d",&a,&b,&c);
if ((a>1) && (c>b))
printf("a soma dos multiplos de %d entre %d e %d eh:
%d",a,b,c,soma(a,b,c));
else
printf("fora do solicitado");
return 0;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
47
//------------calcula soma dos multiplos---------------------
int soma(int a, int b, int c)
{
int x,s=0;
for (x=b; x<=c; x++)
if (x%a==0)
s+=x;
return s;
}
7.0 Tipo estruturado homogêneo - matrizes
Consiste em uma seqüência ordenada e estruturada de itens de dados (componen-
tes), organizados em dimensões e identificados através de índices (subscritos). Ou
seja, um array é uma estrutura com um número fixo de componentes, todos do
mesmo tipo, representados por um único identificador de variável.
Devido às suas características, muitos autores o denominam tipo estruturado ho-
mogêneo.
Cada elemento de um array é distinguido pelo índice que referencia sua po-
sição dentro da estrutura.
7.1.- Matriz Unidimensional : estrutura do tipo lista, vetor, que é referenciado
por uma variável com um único índice.
Forma Geral da declaração:
tipo_da_variável nome_da_variável [tamanho];
Ex:
float valores[8];
Esta declaração cria um espaço na memória para armazenar sequencialmente 8
valores do tipo float
O primeiro destes valores estará na posição de índice zero e o último na posição
7.
Valores[3] = 13
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
48
7.2 Matriz bidimensional - estrutura do tipo matriz, tabela, que é referencia atra-
vés de dois índices, o primeiro para indicar a linha e o segundo para indicar a co-
luna onde o elemento se encontra.
Forma Geral da declaração:
tipo_da_variável nome_da_variável [no. Linhas][no. colunas];
Exemplo:
int tabela [5] [6];
Esta declaração cria um espaço na memória para armazenar sequencialmente 30
valores do tipo float
O primeiro destes valores estará na posição [0][0] ou seja, na linha zero e coluna
zero e o último estará na posição [4][5].
Tabela[2][3]=85
7.3 Inicialização de matrizes
Ao declarar uma variável simples é possível definir um valor inicial para a mesma,
como abaixo:
int k = 21;
Matrizes também podem ser inicializadas e nesse caso, a sequência de valores
aparece entre '{' e '}', separados por vírgula.
Exemplo de inicialização de matriz unidimensional:
int dias[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
Exemplo e inicialização de matriz bidimensional:
float valores[2][3] = { {1.6, 4.0, 5.1}, {12.9, 6.1, 3.5} };
7.4 O uso de constantes para definir o tamanho de matrizes
O uso de constantes para definir o tamanho de matrizes é uma boa opção que fa-
cilita a alteração de tamanho o e o teste de programas com matrizes muito gran-
des.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
49
#include <stdio.h>
#include <conio.h>
#define TAM_MATRIZ 5
void main(void)
{
int valores[TAM_MATRIZ], i;
for (i = 0; i < TAM_MATRIZ; i++)
sacnff(" %d", &valores[i]);
}
7.5 Matrizes como argumento de funções
Ao passarmos uma matriz para uma função, não podemos passar todos os seus
elementos, podemos passar apenas o endereçco de seu primeiro elemento. Isto
significa que não é feita uma cópia, elemento a elemento da matriz, mas que a
função usa o endereço para acessar a matriz real usada na chamada da função.
Isto faz com que qualquer alteração no valor dos elementos da matriz dentro da
função, seja efetuada na própria matriz, ou seja, será sempre uma passagem por
referência.
É possível gerar um ponteiro para uma matriz e isto é feito utilizando apenas o
nome da matriz, sem índices.
Considerando a declaração de matriz:
int mat[10] que se refere a uma matiz com 10 elementos do tipo inteiro
e a declaração de um ponteiro para inteiro:
int *p
podemos fazer a atribuição
p=mat;
e com isso armazenamos o endereço do primeiro elemento de mat em p.
Logo, o nome de uma matriz contém o endereço de seu primeiro elemento.
Um exemplo:
int v[100];
*v = 33; // é o mesmo que v[0] = 33;
*(v+2) = 44; // é o mesmo que v[2] = 44;
for (i=0; i<100; i++)
*(v+i) = 0; //zera o vetor v
Outro exemplo:
int v[100];
int *pv;
pv = v; // também pode ser pv = &v[0];
Notas de aulade Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
50
for (i = 0; i < 100; i++)
*pv[i] = 0;
7.5.1 Passagem de matrizes unidimensionais como parâmetro
Consideremos o vetor
int vet [10];
que deve ser passado como argumento a uma função func().
Na chamada da função utilizamos apenas o nome da função e o vetor como argu-
mento:
func(vet);
A função func() poderá ser declarada através de uma das três maneiras seguintes:
1. void func (int *p); // um ponteiro
2. void func (int v[10]); // matriz dimensionada
3. void func (int v[ ]); // matriz não dimensionada
Nos três casos, teremos dentro de func() um ponteiro para o endereço do vetor.
A primeira declaração usa mesmo um ponteiro
A segunda usa a declaração padrão de uma matriz
E a última não especifica o tamanho da matriz
Pode-se concluir, portanto, que não importa para a função o tamanho do vetor
pois o compilador gera um código que instrui func() a receber um ponteiro, ele
não cria realmente uma matriz com 10 elementos inteiros.
Exemplo:
#include <stdio.h>
void exibe_matriz(int valores[], int num_de_elementos)
{
int i;
printf("Prestes a exibir %d valores\n",num_de_elementos);
for (i = 0; i < num_de_elementos; i++)
printf("%d\n", valores[i]);
}
int main()
{
int notas[5] = {70, 80, 90, 100, 90};
int conta[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int pequeno[2] = {-33, -44};
exibe_matriz(notas, 5);
exibe_matriz(conta, 10);
exibe_matriz(pequeno, 2);
}
7.5.2 Passagem de matrizes bidimensionais como parâmetro
Vimos que ao passar como parâmetro uma matriz unidimensional para uma função,
não é necessário especificar o número de elementos na matriz.
Em matrizes bidimensionais, por sua vez, não será necessário especificar o número
de linhas na matriz, mas, sim, especificar o número de colunas.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
51
Matrizes bidimensionai são armazenadas como um vetor, onde as linhas (come-
çando na linha zero) são armazenadas uma seguida à outra (formando um vetor).
Assim, para acessar qualquer elemento a partir do primeiro é o compilador executa
um calculo onde é necessário saber o número de colunas (ou comprimento de cada
linha).
Exemplo:
include <stdio.h>
void exibe_2d_matriz(int matriz[ ][10], int linhas)
{
int i, j;
for (i = 0; i < linhas; i++)
for (j = 0; j < 10; j++)
printf("matriz[%d][%d] = %d\n", i, j, matriz[i][j]);
}
int main()
{
int a[1][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}};
int b[2][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19,20}};
int c[3][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30}};
exibe_2d_matriz(a, 1);
exibe_2d_matriz(b, 2);
exibe_2d_matriz(c, 3);
}
7.5.3 Tratando uma matriz bidimensional como se tivesse uma só dimensão
Quando for necessário trabalhar com os elementos de uma matriz bidimensional,
mas sem precisar acessar os elementos em suas posições de linha ou coluna, as
funções poderão tratar a matriz bidimensional como se ela tivesse uma dimensão.
Exemplo:
#include <stdio.h>
int soma_matriz(int matriz[], int elementos)
{
Int soma = 0;
int i;
for (i = 0; i < elementos; i++)
soma += matriz[i];
return(soma);
}
int main()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[2][10]={{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19,20}};
printf("Soma dos elementos da primeira matriz %d\n",
soma_matriz(a, 10));
printf("Soma dos elementos da segunda matriz %d\n",
soma_matriz(b, 20));
return 0;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
52
7.5.4 Exercícios sobre matrizes
1.0 Escrever um programa em que uma função determine o maior valor em uma
matriz de valores inteiros com n>0 linhas e m>0 colunas, cujos valores te-
nham sido lidos em main. Main escreve o resultado.
2.0 Alterar o programa anterior utilizando uma função para ler a matriz e outra
função para indicar todas as posições da matriz em que se encontra tal valor
máximo.
3. 0 Escrever uma função que determine se uma matriz quadrada de n>0 linhas e
colunas é uma matriz permutação. Uma matriz quadrada é chamada de ma-
triz permutação se seus elementos são apenas 0’s e 1’s e se em cada linha
e coluna da matriz existe apenas um único valor. Main lê a matriz e informa
o resultado.
Exemplo de matriz permutação:
4.0 Escrever uma função (e um programa que exercite tal função) que altere os
valores dos elementos da matriz de valores reais com n>0 linhas e m>0 co-
lunas de tal forma que o valor alterado de um elemento corresponda ao valor
original daquele elemento dividido pelo maior valor original na coluna em que
se encontra o elemento em questão.
5.0 Faça um programa que lê uma lista de 5 números inteiros, não aceitando
valores repetidos. Faça a verificação de repetição através de uma função.
Main escreve os valores lidos.
6.0 Escrever uma função (e um programa que exercite tal função) que deter-
mine o índice da coluna de uma matriz de inteiros (composta por n>0 linhas
e m>0 colunas) com o maior valor de soma de elementos por coluna.
No exemplo acima a função deve retornar o valor 2, pois a soma dos ele-
mentos da terceira coluna (20) é maior que os valores da soma dos elemen-
tos de cada uma das demais colunas (3, 13 e 18 nas colunas 0, 1 e 3, res-
pectivamente).
7.0 Faça um algoritmo que calcule a multiplicação de duas matrizes A e B,
sendo inicialmente lidos os valores que correspondem ao número de linhas e
colunas destas matrizes. Deverá então ser feito o teste para verificar se
pode ocorrer multiplicação entre A e B e caso seja possível, o algoritmo de-
verá ler as duas matrizes e informar a sua multiplicação.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
53
8.0 Faça um programa no qual:
Uma função lê os valores de uma lista com nove elementos cujos valores
devem ser maiores que 1 e menores que 10, chama outra função para o
qual passa os valores lidos e este identifica os que são ímpares, formando
com eles uma nova lista, cujos elementos são escritos na tela.
A função que leu os valores chama uma terceira função passando para ele
os 9 valores da lista e esta terceira transforma a lista em uma matriz qua-
drada, a qual escreve na tela. Utilize passagem de parâmetros.
7.5.5 Exercícios resolvidos
Exercício 2
#include <stdio.h>
#define lmax 10
#define cmax 10
int fmaior(int mat[][cmax],int l, int c);
void posicoes(int mat[][cmax],int l, int c, int maior);
void le_mat_int(int mat[][cmax], int l, int c);
//-----------principal------------------------------------
int main()
{
int mat[lmax][cmax],x,y,l,c,maior;
printf("Informe o no. de linhas e depois de colunas\n");
scanf("%i%i",&l,&c);
printf("Digite os valores da matriz");
le_mat_int(mat,l,c);
maior=fmaior(mat,l,c);
printf("O maior valor eh %i\n",maior);
posicoes(mat,l,c,maior);
return 0;}
void le_mat_int(int mat[][cmax], int l, int c)
{int x,y;
for (x=0;x<l;x++)
for (y=0;y<c;y++)
scanf("%i",&mat[x][y]);
}
int fmaior (int mat[][cmax], int l, int c)
{
int x,y,ma;
ma=mat[0][0];
for (x=0;x<l;x++)
for (y=0;y<c;y++)
if (mat[x][y]>ma)
ma=mat[x][y];
return ma;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
54
void posicoes(int mat[][cmax],int l, int c, int maior)
{
int x,y;
for (x=0;x<l;x++)
for (y=0; y<c; y++)
if (mat[x][y] == maior)
printf("maior encontrado na linha %i coluna %i\n",x,y);
}
Exercício 7
#include <stdio.h>
#define lmax 5
#define cmax 5
int teste(int ca,int lb);
void leitura(int ma[][cmax], int mb[][cmax],int la,int ca,int
lb,int cb);
void multip(int ma[lmax][cmax], int mb[lmax][cmax],int la,int
ca,int lb,int cb);
int main()
{ int la,ca,lb,cb,t=0;
int ma[lmax][cmax], mb[lmax][cmax];
printf("Informe Linhas e Colunas da matriz A\n");
scanf("%d %d",&la,&ca);
printf("Informe Linhas e Colunas da matriz B\n");
scanf("%d %d",&lb,&cb);
t=teste(ca,lb);
if(t)
{ leitura(ma,mb,la,ca,lb,cb);
multip(ma,mb,la,ca,lb,cb);
MULTIPLICAÇÃO DE MATRIZES
Dadas as matrizes A (m x n) e B (n x p), chama-se produto de A por B a
matriz C=A.B, de ordem mxp, tal que cada elemento c
ij
de C é obtido multi-
plicando-se, ordenadamente, os elementos da linha i de A pelos elemen-
tos da coluna j de B, e somando-se os produtos assim obtidos.
Para que duas matrizes possam ser multiplicadas é necessário que o número
de colunas da primeira seja igual ao número de linhas da segunda
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
55
}
else
printf("Impossivel multiplicar");
}
int teste(int ca,int lb)
{ int r=0;
if(ca==lb)
r=1;
return r;
}
void leitura(int ma[][cmax], int mb[][cmax],int la,int ca,int
lb,int cb)
{ int x,y;
printf("Matriz A:\n");
for(x=0;x<la;x++)
for(y=0;y<ca;y++)
scanf("%d",&ma[x][y]);
printf("Matriz B:\n");
for(x=0;x<lb;x++)
for(y=0;y<cb;y++)
scanf("%d",&mb[x][y]);
for (x=0;x<la;x++)
for(y=0;y<ca;y++)
printf("%d",ma[x][y]);
for (x=0;x<lb;x++)
for(y=0;y<cb;y++)
printf("%d",mb[x][y]);
}
void multip(int ma[lmax][cmax], int mb[lmax][cmax],int la,int
ca,int lb,int cb)
{ int x,y,z,s,mc[lmax][cmax],t;
for(x=0;x<la;x++)
for(y=0;y<cb;y++)
{ s=0;
for(z=0;z<ca;z++)
s=s+ ma[x][z] * mb[z][y];
mc[x][y]= s;
}
printf("Matriz resultante da multiplicacao");
for (x=0;x<la;x++)
for(y=0;y<cb;y++)
printf("%d",mc[x][y]);
}
Exercício 8
#include <stdio.h>
#define max 9
void impares(int v, int x);
void leitura(int l[]);
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
56
void matriz(int l[]);
int main ()
{ int l[max];
leitura(l);
return 0;
}
void leitura(int l[])
{int x,v,i[max],xi=0;
for(x=0;x<max;x++)
{ printf("Informe o valor (maior que 1 e menor que 10):");
scanf("%d",&v);
while(v<=1 || v>=10)
{ printf("fora do limite, Informe novamente o valor:");
scanf("%d",&v);
}
l[x]=v;
impares(v,x);
}
matriz(v);
}
void impares(int v, int x)
{ static int i[max]={0,0,0,0,0,0,0,0,0};
int y;
static int xi=-1;
if (v%2!=0)
{ xi++;
i[xi]=v; }
if (x==max-1)
{printf("foram lidos %d pares\n",xi+1);
if(xi>0)
for(y=0;y<xi+1;y++)
printf("%d ",i[y]);
}}
void matriz(int l[])
{ int m[3][3],x,y,z=-1;
for(x=0;x<3;x++)
for(y=0;y<3;y++)
{ z++;
m[x][y]=l[z]; }
printf("matriz quadrada:");
for (x=0;x<3;x++)
for (y=0;y<3;y++)
printf("%i",m[x][y]);
}
8.0 Strings em C
Em C, uma string consiste em um vetor de caracteres finalizado por um zero. Um
zero é representado como ' \0 ' ou NULL e pode armazenar qualquer tipo de
caractere válido da tabela ASCII.
8.1 Declaração de strings
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
57
Nâo é necessário que nós mesmos coloquemos o zero ao informar uma string, pois
o compilador faz isso mas ao declararmos uma string é importante lembrar de dei-
xar um caractere a mais para o valor zero.
Desta forma, se precisamos de uma string para guardar nomes com 20 caracteres
a declaração será:
char nome[21];
É possível declarar uma string e inicializá-la sem informar o número de caracte-
res, veja abaixo:
char string[] = “alo”;
O compilador C interpretará que o tamanho da string é a quantidade de caracteres
da inicialização (3) mais um (1) para o zero, totalizando neste caso, quatro
elementos. Isto porém será um problema se quisermos (mais tarde) acrescentar
mais caracteres na variável string, pois não foi reservado espaço préviamente para
estes novos caracteres.
8.2 Declaração de um vetor de string
Para criar um vetor de strings, deve-se utilizar uma matriz bidimensional de
caracteres, onde o tamanho do índice esquerdo determina o número de strings
(elementos) do vetor e o tamanho do índice direito especifica o comprimento
máximo de cada string.
Exemplo:
char nome[3][8];
A declaração acima cria um vetor com 3 strings com 7 caracteres + '\0' (NULL)
cada uma.
Para acessar uma string particular deve-se especificar apenas o índice esquerdo,
ou seja, nome[0], nome[1] ou nome[2].
Pode-se acessar também qualquer caractere de qualquer uma das strings, isto é
feito utilizando os dois índices, como por exemplo, nome[2][0] é o caracter ‘F’.
8.3 Algumas funções para manipular strings
9.3.1 Função scanf na leitura de strings
A função scanf permite a leitura de strings através do formato %s, porém tem um
funcionamento limitado, pois considera encerrada a leitura da string assim que en-
contrar um espaço em branco.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
58
Se, por exemplo, o nome for composto por duas ou mais partes (Ana Maria da
Silva) a função scanf irá considerar apenas a seqüência de caracteres “Ana” e co-
locará automaticamente o caracter \0 (NULL) para encerrar a string.
8.3.1.1 Outros problemas no uso do scanf com caracteres:
Quando você utiliza, em um mesmo programa C a função scanf, junto com as
funções gets ou getch, encontra alguns problemas com a aparente "não execu-
ção" de uma ou mais das chamadas destas funções.
Para o caso do ambientes DOS, usando o BorlandC, basta acrescentar, após o
uso da scanf, uma chamada à função fflush, na seguinte maneira:
int main()
{ char ch;
int a;
..........
.........
........
.........
scanf(“%i “, &a);
fflush(stdin);
scanf(“ %c”, ch)
.......
.........
........
}
Outra solução é não utilizar o scanf nas leituras, lendo variáveis do tipo integer ou
real em uma variável auxiliar. Esta variável auxiliar será um vetor de char e será
lida com gets. Depois será feita a conversão para o tipo adequado e armazenado
na variável original. Veja abaixo:
int main ()
{ char aux[20];
float r;
int i;
gets(aux);
r=atof(aux);
gets(aux);
i=atoi(aux);
}
Quando utilizamos o scanf para ler caracteresdentro de uma repetição, podemos
ter o mesmo problema, pois, também neste caso, o scanf requer a tecla enter
para efetuar a entrada de dados e esta tecla, que fica no buffer sinaliza que já
existe um valor para a variável caractere a ser lida. O compilador C guarda então
este valor na variável e limpa o buffer. Veja abaixo um exemplo deste problema e
as duas soluções indicadas.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
59
#include <stdio.h>
// falha na leitura repetida do caracter ch.
int main()
{
int i;
char ch;
for (i=0; i<5; i++)
{
printf(" %i Caractere: ",i+1);
scanf("%c", &ch);
}
}
Uma solução:
#include <stdio.h>
int main()
{ int i;
char ch;
for (i=0; i<5; i++)
{
printf("%i Caractere: ",i+1);
scanf("%c%*c", &ch);
fflush(stdin);
}
return 0;
}
Outra solução:
#include <stdio.h>
int main()
{
int i;
char ch;
for (i=0; i<5; i++)
{
printf("%i Caractere: ", i+1);
scanf("%c%*c", &ch);
}
return 0;
}
O formato %*c indica que o segundo caractere lido (o enter) deve ser excluído.
8.3.2 Função gets() - pertence à biblioteca stdio.h
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
60
Permite ler uma string através do teclado. Considera encerrada a entrada da
string quando for digitado o caractere de nova linha (\n) que é representado pela
tecla <enter>.
8.3.3 Função puts() - pertence à biblioteca stdio.h
A função puts() é o complemento da função gets() e tem a finalidade de imprimir
uma string por vez, levando o cursor para próxima linha após imprimir.
Se for necessário imprimir mais de uma string por vez, deve-se usar a função
printf.
Considerando a declaração:
char nome[]=“Maria do Carmo”;
As duas instruções abaixo resultam iguais:
printf(“%s\n”,nome);
puts(nome);
8.3.4 Função strlen() – pertence à bibioteca string.h
Esta função retorna o número de caracteres de uma string passada como parâ-
metro
Exemplo:
int c;
char nome[]=“Maria do Carmo”;
c=strlen(nome);
c armazenará 14
8.3.5 Função strcat() - pertence à bibioteca string.h
Esta função une duas funções juntando uma string ao final da outra.
Exemplo:
char nome1[] = “Santa ”, nome2[] = “Cruz”;
strcat (nome1, nome2);
puts(nome1)
Será apresentado Santa Cruz
8.3.6 Função strcmp() - pertence à bibioteca string.h
Esta função compara duas strings, caracter a caracter e retorna zero se elas fo-
rem iguais.
Exemplo:
char nome1[] = “Santa ”, nome2[] = “Cruz”;
int r;
r=strcmp(nome1, nome2);
if (r == 0)
printf(“São iguais”)
else
printf(“São diferentes”);
No trecho de código do exemplo acima a saída será: São diferentes
8.3.7 Funções para conversão de uma string em valor numérico
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
61
8.3.7.1 Função atof()
Sintaxe: double atof(const char *str)
Biblioteca: stdlib.h
Converte a string apontada por str em um valor double e retorna o resultado.
8.3.7.2 Função atoi()
Sintaxe: int atoi(const char *str)
Biblioteca: stdlib.h
Converte a string apontada por str em um valor inteiro e retorna o resultado.
Programa exemplo que demonstra algumas funções para string e outras
para caracteres:
/* Faça um programa que lendo idade em anos e sexo de um associado de um
clube, conceda desconto na mensalidade a ser paga, observando:
sexo feminino, até 30 anos desconto de 20%
sexo “ 31 a 40 anos desconto de 30%
sexo “ acima de 41 anos desconto de35%
sexo masculino até 25 anos sem desconto
sexo masculino acima de 25 anos desconto de 25%.
Forneça idade e mensalidade a pagar.
O programa deverá ser encerrando quando o usuário digitar ‘Fim’ no lugar do
nome. */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
const
m=60;
char nome[15],sexo;
int idade,t,x,r;
float mens;
//-----------principal------------------------------------
int main()
{
printf("Informe nome, para encerrar digite fim:\n");
gets(nome);
t=strlen(nome);
for(x=0;x<t;x++) //cada caractere é transformado em maiusculo
nome[x]=toupper(nome[x]);
r=strcmp(nome,"FIM"); //strcmp compara duas strings
while(r != 0)
{ printf("informe idade e sexo\n");
scanf("%i",&idade);
//scanf("%c",&sexo); /*a funcao getche() eh mais apropriada
para ler caracteres pertence a biblioteca conio.h */
sexo=getche();
sexo=toupper(sexo); /*toupper transforma em maiusculo o ca-
ractere pertence a biblioteca ctype*/
switch (sexo)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
62
{ case'F' : if (idade<=30)
mens=m*0.8;
else
if (idade>30 && idade<=40)
mens=m*0.7;
else
mens=m*0.65;
break;
case 'M' : if (idade<=25)
mens=m;
else
mens=m*0.75;
break;
default:printf("/nSexo invalido\n");
}
printf("\nIdade: %i Mensalidade: %f",idade,mens);
printf("\nInforme nome, para encerrar digite fim:\n");
fflush(stdin); /*libera a memoria do periférico de entrada pa
drao, pertence à biblioteca stdio.h */
gets(nome);
t=strlen(nome);
for(x=0;x<t;x++)
nome[x]=toupper(nome[x]);
r=strcmp(nome,"FIM");
}
return 0;
}
8.3.8 Exercício proposto
1.0 Faça um programa que lê os seguintes dados relativos a 30 alunos de uma
escola de futebol: nome; altura; data de nascimento (tarefa 1). O programa deverá
fornecer como saída: a idade média dos alunos; a altura média dos alunos (tarefa
2).
9.0 Tipo Estruturado Heterogêneo (Registros ou Estruturas)
Um registro é uma estrutura composta por um número fixo de componentes, cha-
mados campos. É um grupo de informações relativas a uma mesma entidade.
Os campos do registro podem ser de diferentes tipos e a cada campo é dado um
nome, (identificador do campo), o qual é utilizado para seleciona-lo.
As estruturas são utilizadas para representar um conjunto de informações que es-
tão logicamente relacionadas;
Por exemplo:
Uma ficha de cadastro de uma empresa que tem todo tipo de infor-
mações sobre um funcionário, necessárias para a empresa.
Uma ficha de um consultório médico que possui alguns dados e ca-
racterísticas do estado de saúde dos pacientes.
9.1 Declaração
Quando for necessário utilizar um registro é preciso definir um novo tipo:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
63
struct nome_do_novo_tipo
{ tipo1 campo11, campo12, ..., campo1N;
tipo2 campo21, campo22, ..., campo2N;
...
tipoM campoM1, campoM2, ... campo MN; };
Onde:
nome_do_novo_tipo é o identificador que especifica o nome do registro criado
(novo tipo de dados). A partir desse nome podem ser criadas variáveis desse tipo;
tipo1, tipo2, tipo3, ... tipoM são os tipos de cada um dos campos do registro;
campo11, campo12, ..., campoMN são os nomes dos campos do registro.
Exemplos:
struct fichaCadastral // declaração do tipo struct
{ char cidade[41], estado[31], pais[21], dataNasc[11],
nome[51],profissao[51], dataAdmissao[11], escolari-
dade[21];
int qtdeDependentes, numero, fumante;
double salario;
};
// declaração das variáveis do tipo criado
struct fichaCadastral funcionario1, funcionario2;
struct aluno
{ char nome[20];
char curso [25];
float nota;
};
Após a declaração da struct aluno é possível declarar variáveis do tipo criado:
struct aluno a1, a2;
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
64
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).
Exemplo:
Struct aluno turma[55]
Também é possível declarar logo ao final da declaração da estrutura as suas vari-
áveis, conforme mostra-se a seguir:
struct reg_cliente // nome_da_estrutura
{char nome[30]; //campos
float salario;
int idade;
} cliente, fornecedor; //lista_de_variáveis
Os campos de uma estrutura podem ser de qualquer tipo: tipos simples (int, char,
float, etc), vetores, ou até mesmo estruturas:
struct ponto
{ int x;
int y;
};
struct linha
{
struct ponto pa;
struct ponto pb;
}
struct linha l1,l2;
9.2 Acesso aos campos da estrutura
O acesso aos campos é feito através do operador de associação – o ponto ( . )
a1.nome=”José Carlos dos Santos”;
a1.curso=” Ciência da Computação”;
a1.nota= 10;
l1.pa.x=3.0;
l1.pa.y=5.0;
l1.pb.x=15.0
l1.pb.y=5.0
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
65
9.3 Atribuição entre variáveis do tipo struct
É possível fazer atribuições entre variáveis de registro, porém elas devem ser do
mesmo tipo struct declarado.
a2=a1;
l2 = l1;
9.4 Matriz de estrutura
É possível definir uma matriz do tipo estrutura:
#include <stdio.h>
struct conta {
int num ;
float saldo ;
} ;
void testeStruct(struct conta x[2]);
int main() {
struct conta c[2];
testeStruct(c) ;
printf ("Main: Valor de c.num: %d\n", c[0].num) ;
printf ("main: Valor de c.saldo: %.1f\n",c[0].saldo) ;
printf ("Main: Valor de c.num: %d\n", c[1].num) ;
printf ("main: Valor de c.saldo: %.1f\n",c[1].saldo) ;
return 0;
}
void testeStruct (struct conta x[2]) {
x[0].num = 2 ;
x[0].saldo = 20.0 ;
x[1].num = 3 ;
x[1].saldo = 30.0 ;
}
9.5 Struct como retorno de uma função
Uma função pode ter uma estrutura como valor de retorno:
#include stdio.h
struct ponto
{ int x; int y; };
struct ponto constroiPonto(int x, int y;
int main()
{ struct ponto origem;
origem=constroiPonto(0,0);
}
struct ponto constroiPonto(int x, int y)
{ struct ponto temp;
temp.x = x;
temp.y = y;
return temp;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
66
9.6 Struct como parâmetro de função
Uma struct pode ser passada como parâmetro para uma função, tanto na passa-
gem por valor, como por referencia.
Na passagem por referencia devemos observar o acesso aos campos da mesma
é feito através do operador "->" ao invés de "."
Exemplo de passagem por referência de uma struct para uma função:
#include <stdio.h>
struct ponto
{ int x; int y;
};
void movep(struct ponto *p, int dx, int dy);
int main()
{
struct ponto p1;
int i;
printf("Informe as coordenadas x e y do ponto:\n");
scanf("%d%d",&p1.x,&p1.y);
movep(&p1,2,3);
printf("nova coordenada x: %d nova coordenada y:
%d",p1.x,p1.y);
return 0;
}
void movep(struct ponto *p1, int dx, int dy)
{ p1 -> x += dx;
p1 -> y += dy;
}
9.7 Inicialização de uma variável do tipo struct
Ao declararmos uma variável do tipo estrutura, podemos também definir o seu valor
inicial, de forma análoga àquela utilizada para vetores.
struct ponto origem = {0,0};
9.8 Ponteiros para structs
A declaração é similar à declaração de um ponteiro para outros tipos de dados.
Na implementação de estruturas de dados compostas por nodos ou células pon-
teiros para struct são bastante utilizados. Veja um exemplo no capítulo 16.
9.9 Exercícios do tipo struct
1. Fazer um programa que cria uma estrutura livro, que contém os elementos isbn
(código internacional, um inteiro), ano de edição, número de páginas e preço.
Criar uma variável desta estrutura que é um vetor de 5 elementos. Ler os valores
para a estrutura e imprimir através de uma função a média do número de páginas
do livros.
2. Foi realizada uma pesquisa entre 500 habitantes de uma certa região. De cada
habitante foram coletados os dados: idade, sexo, salário e número de filhos.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
67
Construa um programa C que armazene as informações da pesquisa em um
vetor de struct e utilize 1 função para realizar cada um dos itens:
Calcular a média salarial dos habitantes
Calcular a média de filhos.
Calcular o número de habitantes com salário inferior a média.
Listar os habitantes com mais de 3 filhos.
3. Faça um programa para ler as seguintes informações relativas a 20 alunos da
disciplina Programação Estruturada de um curso de Ciência da Computação:
Matrícula (inteiro)
Nota Final (real)
O programa deverá utilizar um vetor de struct para armazenar estas informações
e, após lê-las, utilizar uma função para fornecer cada uma das informação
abaixo:
A média da turma;
O total de alunos aprovados, considerando que estão aprovados os alunos
com nota final >=7;
A matrícula do(s) alunos com maior Nota Final.
4. Considerando o registro de um produto de uma loja contendo as seguintes in-
formações: descrição, valor, fornecedor, quantidade em estoque, fazer um pro-
grama que, considerando 50 produtos, utilize funções para:
1. Ler as informações;
2. Exibir as informações na ordem inversa em que foram digitados;
3. Informar os produtos que têm menos de 2 unidades em estoque;
4. Fornecer o total em reais (R$) que a empresa mantém com estes produtos.
5. Elabore um programa para armazenar em um vetor de struct os dados de 80
pessoas digitadas pelo usuário. Cada pessoa é caracterizada pelas seguintes
informações: o seu nome (máximo de 30 caracteres), o seu peso e a sua idade.
Utilize funções para :
1. Cadastar as pessoas;
2. Exibir o nome dos adolescentes, ou seja, pessoas com idade de 12 a 18
anos;
3. Exibir o(s) nome(s) da(s) pessoa(s) com o menor peso.
6. Considerando a declaração da struct abaixo
struct reg_cliente {
char nome[30];
float salario;
int idade;
};
faça um programa para ler as informações de 30 clientes através de uma função e
utilize outra função para retornar a main quantos clientes ganham mais de
R$5.000,00 e não têm mais de 27 anos.
7. Seja uma estrutura para descrever os carros de uma determinada revendedora,
contendo os seguintes campos:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
68
marca: string de tamanho 15
ano: inteiro
cor: string de tamanho 10
preço: real
Faça um programa onde em main seja definido um menu para:
a) acionar uma função para ler um vetor com no máximo 60 carros.
b) acionar uma função que receba um preço e imprima os carros (marca, cor
e ano) que tenham preço igual ou menor ao preço recebido.
c) acionar uma função que leia a marca de um carro e imprima as informa-
ções de todos os carros dessa marca (preço, ano e cor).9.10 Exercícios resolvidos
// exercício 2
#include <stdio.h>
#include <stdlib.h>
#define MAX 3
struct info
{ int idade, nfilhos;
float salario;
};
float media_sal(struct info pesquisa[MAX]);
float media_filhos(struct info pesquisa[MAX]);
int inferior_media (struct info pesquisa[MAX], float m_s);
int mais_filhos (struct info pesquisa[MAX]);
int main()
{
int x;
float m_s;
struct info pesquisa[MAX];
char aux[20];
printf("Informe os dados\n");
for(x=0;x<MAX;x++)
{ printf("Idade do habitante %d: ", x+1);
/*scanf("%i",&pesquisa[x].idade); ler com gets e converter para
inteiro, */
gets(aux);
pesquisa[x].idade=atoi(aux); //converte char para int
fflush(stdin); //libera a memória de entrada de dados
printf("No. de filhos do habitante %d: ", x+1);
//scanf("%i",&pesquisa[x].nfilhos);
gets(aux);
pesquisa[x].nfilhos=atoi(aux);
fflush(stdin);
printf("Salario do habitante %d: ", x+1);
fflush(stdin);
//scanf("%f",&pesquisa[x].salario);
gets(aux);
pesquisa[x].salario=atof(aux); //converte char para float
}
m_s = media_sal(pesquisa);
printf("media salarial: %.2f \n",m_s);
printf("media de filhos: %.2f \n", media_filhos(pesquisa));
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
69
printf("numero de habitantes com salario abaixo da media sala-
rial: %d \n", inferior_media(pesquisa,m_s));
printf("numero de habitantes com mais de 3 filhos: %d \n",
mais_filhos(pesquisa));
return 0;
}
// a média salarial dos habitantes
float media_sal(struct info pesquisa[MAX])
{int x;
float m_sal;
m_sal=0;
for(x=0;x<MAX;x++)
m_sal+= pesquisa[x].salario;
m_sal=m_sal/MAX;
return (m_sal);
}
// a media de filhos.
float media_filhos(struct info pesquisa[MAX])
{int x;
float m_fil;
m_fil=0;
for(x=0;x<MAX;x++)
m_fil+= pesquisa[x].nfilhos;
m_fil=m_fil/MAX;
return (m_fil);}
// o numero de habitantes com salario inferior a media.
int inferior_media (struct info pesquisa[MAX],float m_s)
{int inferior , x;
inferior=0;
for(x=0;x<MAX;x++)
if (pesquisa[x].salario< m_s)
inferior++;
return(inferior);
}
// o total de habitantes com mais de 3 filhos.
int mais_filhos (struct info pesquisa[MAX])
{int c=0,x;
for(x=0;x<MAX;x++)
if (pesquisa[x].nfilhos>3)
c++;
return(c);
}
/*Exercício 3 (demonstra passagem de struct por referência) */
#include <stdio.h>
#include <stdlib.h>
#define Max 3
typedef struct {
int mat;
float nf;
int aprov;
}aluno;
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
70
int aprovado(aluno *al);
int main ()
{ aluno v[Max],al;
char aux[15];
int x,a,c=0;
fflush(stdin);
printf("Informe a matricula e a nota final dos %i alu-
nos\n",Max);
for(x=0;x<Max;x++)
{ gets(aux);
al.mat=atoi(aux);
fflush(stdin);
gets(aux);
al.nf=atof(aux);
fflush(stdin);
al.aprov=0;
a=aprovado(&al);
v[x]=al;
if(a)
c++;
}
printf("\ntotal de alunos aprovados: %i",c);
return 0;
}
int aprovado(aluno *al)
{ int a=0;
if (al -> nf>=7)
{ a=1;
al -> aprov=1;
}
return(a);
}
10.0 Métodos internos de ordenação
Em processamento de dados lidamos com dois tipos de ordenação: interna e ex-
terna. Se a ordenação deve ser efetuada em um conjunto de elementos na memória
do computador, dizemos tratar-se de ordenação interna. Quando os elementos a
sererm ordenados, entretanto, está armazenados em arquivo, a ordenação é cha-
mada de externa.
10.1 Método da Bolha (Bubble Sort )
É um dos métodos mais simples e conhecidos e baseia-se na troca de elementos.
Consiste básicamente em :
a)varrer a lista comparando cada elemento do vetor com todos os seus elementos
seguintes;
b)em cada comparação, verificar se estão invertidos, caso positivo, trocá-los de
lugar;
c)repetir os itens a e b até comparar os dois últimos elementos da lista.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
71
Abaixo uma função que exemplifica a implementação do método:
void bolha(int v2[ ])
{ int x,y,aux;
for(x=0;x<max-1;x++)
for (y=x+1;y<max;y++)
if (v2[x]>v2[y])
{ aux =v2[x];
v2[x]=v2[y];
v2[y]=aux; }
}
10.2 Método de ordenação por Seleção Direta
É um pouco mais eficiente que o método Bubblesort. Em uma ordenação ascen-
dente, por exemplo, consiste em descobrir, a cada iteração, qual o menor elemento
do vetor e colocá-lo na posição definitiva, iniciando o processo no primeiro ele-
mento. Com isto, a cada etapa teremos um segmento já ordenado e outro não or-
denado.
Descrição do método:
Inicialmente considera-se o segmento já ordenado como sendo formado pelo pri-
meiro elemento do vetor. Os demais elementos formam o segmento não orde-
nado.Pega-se, um a um, os elementos não ordenados, e por busca seqüencial da
direita para a esquerda no segmento ordenado, localiza-se a sua posição correta,
movendo-se os valores necessários.
Continua-se este processo avançando no vetor da esquerda para a direita e a cada
vez aumentando o segmento ordenado.
vetor original:
divisão inicial:
ordenado não ordenado
primeira iteração:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
72
Exemplo de Implementação :
#include <stdio.h>
#define Max 7
int main()
{ int v[Max], i, a, b, t;
printf("Digite os valores: ");
for(i=0;i<Max;i++)
scanf("%i",&v[i]);
for(a=1; a<Max; a++)
{ t = v[a];
for(b=a-1; b>=0 && t<v[b]; b--)
v[b+1] = v[b];
v[b+1] = t; }
printf("Os valores ordenados:\n");
for(i=0;i<Max;i++)
printf("%d ",v[i]);
return 0;
}
10.3- Método Shell
Método criado por Donald Shell, consiste em comparar e trocar não elementos
adjacentes, mas sim elementos separados por um certo intervalo.
É derivado da ordenação por inserção e é baseado em iniciar a comparação entre
elementos afastados por um intervalo maior que 1 e ir diminuindo progressiva-
mente este intervalo até atingir 1, que será p intervalo final.
Como intervalo inicial pode considera-se a metade do número de elementos a or-
denar, mas não necessariamente.
Referência: Martins, J. Pavão. Introdução à programação usando o Pascal. Portu-
gal: McGraw Hill , 1994.
vetor original:
divisão inicial:
ordenado não ordenado
primeira iteração:
segunda iteração:
terceira iteração:
quarta iteração:
quinta iteração:
sexta iteração:
Inclui elemento 15 no
segmento ordenado
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
73
Exemplo:
Ordenar o vetor abaixo utilizando o método Shell, utilizando os intevalos 4, 2, 1:
18 15 7 9 23 16 14 2 20
Ordenação pelo 1º. Intervalo (4)
18 15 7 9 23 16 14 2 20
18
23
20
9 15 7 14 20 16 18 2 23
15
16
9 15 7 14 20 16 18 2 23
7
18
9 15 7 14 20 16 18 2 23
14
2
9 15 7 2 20 16 18 14 23
20
23
9 15 7 2 20 16 18 14 23
Ordenação pelo 2º. Intervalo (2):
9 15 7 2 20 16 18 14 23
97
20
18
23
7 15 9 2 18 16 20 14 23
15
2
16
14
7 15 2 9 18 14 20 16 23
2
18
20
23
7 15 2 9 18 14 20 16 23
9
14
16
7 15 2 9 18 14 20 16 23
18
20
23
7 15 2 9 18 14 20 16 23
14
16
7 15 2 9 18 14 20 16 23
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
74
Ordenação final (intervalo igual a 1):
7 15 2 9 18 14 20 16 23
7 15
7 15 2 9 18 14 20 16 23
15 2
2 7 15 9 18 14 20 16 23
15 9
2 7 9 15 18 14 20 16 23
15 18
2 7 9 15 18 14 20 16 23
18 14
2 7 9 14 15 18 20 16 23
18 20
2 7 9 14 15 18 20 16 23
20 16
2 7 9 14 15 16 18 20 23
20 23
2 7 9 14 15 16 18 20 23
A eficácia deste algoritmo deriva do comportamento natural do método de Inserção.
Os primeiros passos irão “preparar o terreno”, ou seja tornarão o vetor muito pró-
ximo do estado final, restando poucas comparações e trocas para o último passo.
A seqüência exata para divisão em segmentos decrescentes pode variar, a única
regra é que o último deve ser 1.
Por exemplo, a seqüência pode ser : h=9, h=5, h=3, h=2, h=1
Exemplo de implementação do método Shell:
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void shell(char *item, int count);
int main()
{ char s[80];
int l,i;
printf("Digite uma string: ");
gets(s);
l=strlen(s);
for(i=0;i<l;i++)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
75
s[i]=toupper(s[i]);
shell(s, strlen(s));
printf("A string ordenada: %s\n", s);
return 0;
}
void shell(char *item, int count)
{ int i, j, gap, k;
char x, a[5];
a[0]=9; a[1]=5; a[2]=3; a[3]=2; a[4]=1;
for(k=0; k<5; k++)
{
gap = a[k];
for(i=gap; i<count; ++i)
{
x = item[i];
for(j=i-gap; x<item[j] && j>=0; j=j-gap)
item[j+gap] = item[j];
item[j+gap] = x;
}
}
}
10.4 Método Quicksort
Algoritmo de alta eficiência criado por C. A. R. Hoare em 1960 e conhecido tam-
bém como método de ordenação por partição.
Sua idéia central é dividir o conjunto inicial de valores a ordenar em dois conjuntos
menores que são ordenados independentemente. O método consiste basicamente
de duas etapas: partição e ordenação.
1- Partição: nesta fase é feito o rearranjo do conjunto de valores através da
escolha arbitrária de um dos seus itens (chamado de pivô, ou mediana) de tal forma
que ao final desta etapa todos os elementos menores ou iguais ao pivô estejam a
sua esquerda e todos os maiores (ou iguais) a sua direita, estando então o pivô
posicionado de forma adequada.
No exemplo abaixo foi escolhido para pivo um elemento central ao conjunto de va-
lores.
Após a determinação do pivô o vetor começa a ser percorrido pela esquerda e pela
direita por dois apontadores, (i e j), sendo trocados de posição os elementos que
estejam fora da posição relativamente ao valor do pivô;
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
76
2- A próxima etapa (ordenação) consiste em aplicar o mesmo processo para
ambas as partições e, em seguida para cada uma das partições obtidas, até que
cada uma possua apenas um elemento.
No algoritmo apresentado abaixo, que é uma solução iterativa do método quick-
sort, é mantida uma lista de pedidos de particionamentos que ainda devem ser
executados. Após cada passo surgem duas tarefaa de particionamento e somente
uma poderá ser resolvida na iteração que segue ficando a outra empilhada na lista.
Utiliza-se uma pilha porque as tarefas que ficam aguardadndo resolução devem ser
atendidas na ordem inversa.
Programa QuickSort;
Const
n=7;
estrutura marcadores{
esq, dir: inteiro; };
estrutura marcadores pilha[n];
inteiro v[n];
inteiro i,j,x,y,w,l,r,s;
Inicio
Para i←0 a n faça
leia(v[i]);
s ← 0;
pilha[s].esq ← 0;
pilha[s].dir ← n-1;
Repita
l ← pilha[s].esq;
r ← pilha[s]. dir;
s ← s -1;
Repita
i ← l;
j ← r;
x ← v[ (l+r) / 2];
Repita
Enquanto v[i]<x faça
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
77
i ← i+1;
Enquanto v[j]>x faça
j ← j-1;
Se i<=j então
Inicio
w ← v[i];
v[i] ← v[j];
v[j] ← w;
i ← I+1;
j ← j-1;
Fim;
Até i>j;
Se i<r então
Inicio
s ← s+1;
pilha[s].esq ← i;
pilha[s].dir ← r;
Fim;
r ← j;
Até l >= r;
Até s <0;
O método quicksort usualmente é implementado na forma recursiva, o que será
visto dentro do assunto recursividade.
Referências:
WIRTH, Niklaus. Algoritmos e estruturas de dados. Rio de Janeiro: Prentice-
Hall,1989.
ZIVIANI, Nivio. Projeto de Algoritmos com implementações em Pascal e C. São
Paulo: Pioneira, 1999.
11.0 Métodos internos de Pesquisa
11.1 Pesquisa Seqüencial: Verifica cada elemento do vetor, seqüencialmente, até
que o valor seja encontrado, ou que todos os elementos do vetor tenham sido
verificados.
11.2 Pesquisa Binária
É mais rápida que a pesquisa seqüencial e iIdeal para listas com grande número
de elementos.
Descrição do Método:
1- Os elementos da lista devem estar em ordem crescente;
2- Localizar o elemento do meio;
3- Se ele for igual ao elemento procurado, a pesquisa é dita bem sucedida e inter-
rompida;
4- No caso de ser maior repete-se o processo (2-3) na primeira metade do vetor;
5- Caso seja menor, repete-se o processo na segunda metade do vetor.
6- Este procedimento é continuado até que o elemento seja localizado ou então,
até que não reste mais um trecho do vetor a ser pesquisado.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
78
#include <stdio.h>
#define max 10
void le_vetor(int v[]);
int le_valor();
int pesq_bin(int v[],int num);
void ordena(int v[]);
int main()
{
int v[max], num,p;
le_vetor(v);
num=le_valor();
ordena(v);
p=pesq_bin(v,num);
if (p>=0)
printf("Encontrado na posição %i
\n",p);
else
printf("Nao foi encontrado\n");
return 0;
}
void le_vetor(int v[])
{ int x;
for (x=0;x<max;x++)
{ printf("Digite o elem %d: ",x+1);
scanf("%d",&v[x]);
}
}
int le_valor()
{ int n;
printf("Digite o valor a procurar:");
scanf("%d",&n);
return n;
}
void ordena(int v2[ ])
{
int x,y,aux;
for(x=0;x<max-1;x++)
for (y=x+1;y<max;y++)
if (v2[x]>v2[y])
{ aux=v2[x];
v2[x]=v2[y];
v2[y]=aux;
}
}
int pesq_bin(int v1[],int n)
{ int comeco,meio,fim,x=-1;
comeco=0;
fim=max-1;
while (comeco<=fim)
{ meio=(comeco+fim)/2;
if (n==v1[meio])
{ x=meio;
comeco=fim+1;
}
else
{ if (n<v1[meio])
fim=meio-1;
else
comeco=meio+1;}
}
return (x);
}
11.3 Exercício proposto
1. Faça um programa quelê os seguintes campos de uma estruct, relativos a
uma turma com 60 alunos: nome, altura,data de nascimento. O programa deverá
apresentar um menu que permita fornecer:
a) a média das idades,
b) o nome do aluno mais alto;
c) listagem apresentando idades em ordem decrescente e os respectivos nomes
dos alunos.
d) pesquina de aluno por nome.
Utilize uma função para obter cada uma das saídas desejadas.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
79
12.0 Recursividade
A recursão é uma técnica pela qual uma rotina estruturada faz chamadas a ela
mesma, com o objetivo de completar uma tarefa repetitiva e que pode ser identifi-
cada como uma tarefa recursiva, pois nem todas as tarefas repetitivas podem ser
resolvidas através de recursão.
12.1 Definições
Dizemos que a solução de uma tarefa é recursiva quando pode ser definida em
termos de si mesma, definindo uma tarefa mínima e uma condição de parada.
Um exemplo bastante intuitivo é a tarefa “subir uma escada”.
Observe que, a tarefa “subir a escada”pode ser decomposta em uma repeti-
ção finita da tarefa “subir um degrau”, até atingir a condição de parada
Um outro exemplo típico de problema que pode ser resolvido de forma recursiva é
o cálculo do fatorial de um número, onde, por definição teremos: n ! = n * (n-1)!
Sendo N um inteiro, temos:
0 ! = 1
1 ! = 1 * 0 ! = 1*1
2 ! = 2 * 1 ! = 2 * 1 * 1
3 ! = 3* 2 ! = 3 * 2 * 1 * 1
n ! = n * (n-1)! = n*(n-1)*(n-2)* …* 1
Que pode ser formulado como uma
função:
1 se N = 0;
N ! =
N * ( N-1 ) ! se N > 0
Esta definição nos possibilita formular uma função recursiva para cálculo do fato-
rial de um dado número n, inteiro:
int fat (int n)
{ if (n==0 )
return 1;
else
return n*fat(n-1); }
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
80
A condição de parada e a tarefa mínima, neste caso, foram encontradas na própria
definição da função fatorial:
Condição de parada (caso base) :
0 ! = 1
Tarefa mínima (caso recursivo) :
N! = N*(N -1)!
É importante observar que a cada chamada feita à função Fat, a dimensão do pro-
blema (N) diminui, até atingir a condição de parada (N=0), o que é indispensável
para limitar a execução revursiva da função.
Pode-se então definir como Regras da Recursividade:
Um subprograma recursivo deve ter pelo menos uma condição de pa-
rada;
Cada chamada recursiva deve ocorrer de modo que sua execução ca-
minhe em direção ao caso base.
Para controlar o retorno às linhas de instrução (e os valores dos parâmetros da
função) que deixaram de ser executadas, devido ao desvio provocado pela cha-
mada recursiva, o compilador mantém uma pilha de execução.
Esta pilha guarda o endereço e o valor da variável antes da nova chamada. Estes
são os chamados “processos suspensos” que ficam aguardando até poderem se
completar.
A figura a seguir mostra um exemplo, considerando N=3
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
81
Outro exemplo de solução recursiva: Soma de n números naturais a partir de 1
#include <stdio.h>
#include <stdlib.h>
/*Faça um programa para calcular a soma de todos os números pertencentes
ao conjunto dos naturais, menos o zero, menores ou iguais a um dado
valor n, também natural */
int soma(int n);
int main()
{ int n,s,x;
printf("Digite um valor inteiro posit e diferente de zero \n");
scanf("%i",&n);
/* s=n;
* for (x=1;x<n;x++)
* s=s+x; */
printf("A soma é: %i \n", soma(n));
return 0;
}
int soma(int n)
{ if(n==1)
return n;
else
return n+soma(n-1);
}
Um algoritmo que é resolvido classicamente através de recursividade é o método
de ordenação quick sort, abaixo um exemplo de sua implementação:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void quick(char *item, int count);
void qs(char *item, int left, int right);
int main()
{ char s[80];
printf("Digite uma string: ");
gets(s);
quick(s, strlen(s));
printf("A string ordenada: %s\n", s);
return 0;
}
void quick(char *item, int count)
{ qs(item, 0, count-1);
}
void qs(char *item, int left, int right)
{ int i, j;
char x, y;
i = left; j = right;
x = item[(left+right)/2];
do
{ while(item[i]<x && i<right) i++;
while(x<item[j] && j>left) j--;
if(i<=j)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
82
{ y = item[i];
item[i] = item[j];
item[j] = y;
i++;
j--;
}
} while(i<=j);
if(left<j) qs(item, left, j);
if(i<right) qs(item, i, right);
}
12.2 Exercícios propostos
Faça um programa que calcula, através de uma função recursiva, a potência
de um número, sendo lidos no programa principal a base e a potência.
Faça um programa para calcular recursivamente o valor de um termo N da
Série de Fibonatti,sabendo que o primeiro termo da série é 1, o segundo termo da
série também é 1 e partir destes todos os termos seguintes são formados pela soma
dos dois anteriores:
13.0 Operações em meio magnético - Arquivos
Os arquivos são usados para armazenar, de forma permanente, grandes quantida-
des de dados. O armazenamento é feito em memória secundária.
O sistema de arquivos em C é projetado para trabalhar com diversos dispositivos
(terminais, acionadores de disco, de fita, etc) e para tratar a informação de forma
semelhante, apesar das diferenças entre os dispositivos, o sistema com buffer
adota um dispositivo lógico, chamado stream.
Assim, streams são independentes dos dispositivos e a mesma função poderá es-
crever em um arquivo em disco ou em outro dispositivo de saída.
Existem dois tipos de streams: Binárias e Texto
Streams de Texto:
Uma seqüência de caracteres, organizada em linhas.
As linhas são separadas por um único caractere, chamado caractere de
nova linha (ou LF) cujo código ASCII decimal é 10.
Números são guardados como cadeias de caracteres.
Streams Binárias:
Uma seqüência de bytes.
Não há indicador de fim de arquivo.
Números são guardados como na memória (2 bytes para inteiro, 4 bytes
para float, etc.)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
83
A linguagem C possibilita duas diferentes categorias de acesso a disco:
Alto Nível – acesso a arquivos através de um pacote de funções permitindo 4 mo-
dos diferentes:
1. Dados lidos e escritos um caractere por vez;
2. Dados lidos e escritos como “strings”;
3. Dados lidos e escritos de modo formatado;
4. Dados lidos e escritos em formato de registro (ou bloco) que
possui um tamanho fixo, como matrizes e estruturas. Tem-se
assim uma arquivo formado por uma sequencia de itens com
mesmo tamanho.
Baixo Nível – apenas as funções read() e write()
Em nosso curso abordaremos somente o acesso de alto nível.
13.1 Definição do tipo file
Programas criados para trabalhar com arquivos envolvem a implementação de,
pelo menos, três etapas: criação ou abertura do arquivo, gravação ou leitura de
informações no arquivo e fechamento do arquivo.
Na primeira etapa, caso o arquivo nunca tenha sido criado, isto deverá ser feito,
caso contrário, deverá ser aberto para inclusões, leituras, alterações, exclusões,etc.
Já na segunda etapa utilizam-se funções definidas na linguagem para trabalhar
com os dados existentes no arquivo e/ou anexar novos dados.
Na terceira etapa é feito o fechamento do arquivo e aí garantimos que todas as
operações implementadas na segunda etapa sejam efetivamente realizadas.
Um arquivo possue sempre um nome que o identifica no meio externo, porém no
código do programa necessitamos uma variável específica que represente a estru-
tura do arquivo. A biblioteca stdio.h contém a declaração de uma estrutura cha-
mada FILE que guarda as informações necessárias para que possamos trabalhar
com arquivos nos nossos programas em C.
As variáveis do tipo FILE diferem de todas as outras pelo fato de estarem associa-
das a arquivos em disco.
Enquanto uma variável do tipo FILE só existe durante a execução de um pro-
grama, o arquivo que lhe está associado pode existir antes da execução de um
programa e manter a sua existência em disco após o término da sua execução.
Quando trabalharmos com arquivos, portanto, haverá sempre a necessidade de ser
feita a declaração de uma variavel ponteiro para a estrutura FILE, como mostra o
exemplo a seguir. Muitas funções que fazem parte do sistema de arquivos do C
ANSI retornam um ponteiro desta variável para relacionar uma stream com um ar-
quivo.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
84
#include <stdio.h>
int main()
{
FILE *fptr ;
fptr = fopen("exemplo.dat", "w") ;
. . .
. . .
fclose(fptr) ;
}
No trecho de código do exemplo, são mostradas as duas funções sempre neces-
sárias ao implementarmos arquivos, que correspondem à primeira e última etapas,
citadas no inicio deste tópico. A função fopen (utilizada para abrir um arquivo),
retorna um ponteiro para o arquivo aberto que fica armazenado em fptr. Ao finalizar
o arquivo é necessário fechá-lo e é utilizada a função fclose, sendo seu parâmetro
a mesma variável, associada ao tipo File.
13.2 Estrutura básica de um arquivo:
Um arquivo consiste em uma seqüência de componentes de um mesmo tipo. O
número de componentes (registros) de um arquivo não é determinado na definição
do arquivo, sendo que o compilador C vai alocando espaço conforme for necessá-
rio, através de um apontador de registros no arquivo.
Cada vez que um componente é escrito em um arquivo ou lido de um arquivo, o
apontador do arquivo (indicador de posição de registro) avança para o próximo
componente, ficando assim já prosicionado para uma próxima inserção ou leitura
no arquivo, de forma sequencial.
Em C os arquivos são tratados como de organização seqüencial, porém é permitido
que arquivos binários sejam acessados tanto na forma seqüencial como na forma
randômica (acesso direto), o que fazemos utilizando procedimentos e funções exis-
tentes na linguagem.
Se todos os registros de um arquivo são de mesmo comprimento, a posição cor-
respondente a cada registro pode ser calculada e o indicador de registro movimen-
tado para esta posição, obtendo-se desta maneira o acesso direto.
13.3 Funções para tratamento de arquivos em alto nível
13.3.1 Abertura e fechamento de arquivo
Função fopen - abertura de arquivo
Antes de podermos realizar operações em um arquivo, (por exemplo, inserir um
registro, consultar, alterar, excluir) é necessário a abertura de uma stream e sua
associação com um arquivo.
Isto é feito com o comando fopen que tem o seguinte protótipo:
Declaração: FILE *fopen (char *nomearq, char *modo);
Biblioteca: stdio.h
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
85
O parâmetro nomearq é uma string de caracteres que contém o nome do arquivo
desejado. O parâmetro modo especifica como será usado o arquivo - para ler, gra-
var ou anexar.
A função fopen() executa duas tarefas:
1) preenche a estrutura FILE com as informações necessárias para o programa
e para o sistema operacional, assim eles podem se comunicar;
2) retorna um ponteiro do tipo FILE que aponta para a localização na me-
mória da estrutura FILE.
São possíveis três tipos de abertura:
“r” para leitura
“w”para gravação
“a” para adicionar dados
Existem modificadores para os tipos de abertura:
1- acrescenta-se a letra “b” para indicar que o tipo do arquivo é binário
2- acrescenta-se o sinal “+” quando o arquivo já existe e queremos atualizá-lo
A tabela abaixo mostra os modos válidos e seus modificadores:
Se o arquivo não puder ser aberto, fopen() devolve NULL. É usual utilizar o retorno
para encerrar o procedimento evitando erros de abertura, como mostra o trecho de
código a seguir:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
86
#include <stdio.h>
#include <conio.h>
int main()
{
FILE *fptr;
int ch;
if ((fptr=fopen("arqtext.txt","r"))==NULL)
{ printf("Impossivel abrir o arquivo");
exit(); //encerra o programa e fecha todos os arqui-
vos
}
}
Devemos sempre considerar que ao realizar a abertura de um arquivo o localizador
de registros se posiciona no seu início.
Função fclose() - fechamento de arquivo
Sintaxe: int fclose (FILE *fp);
Biblioteca: stdio.h
Observação: Retorna 0 se a operação foi bem sucedida.
Ao finalizar as operações em um arquivo este deve ser fechado, pois com isto al-
guma informação que tenha ficado no “buffer”, associada ao fluxo de saída, é gra-
vada.
Mas, o que é este "buffer"?
Quando enviamos caracteres para serem gravados em um arquivo, estes caracte-
res são armazenados temporariamente em uma área de memória (o "buffer") em
vez de serem escritos em disco imediatamente. Quando o "buffer" estiver cheio,
seu conteúdo é escrito no disco de uma vez. A razão para se fazer isto tem a ver
com a eficiência nas leituras e gravações de arquivos. Se, para cada caracter que
fossemos gravar, tivéssemos que posicionar a cabeça de gravação em um ponto
específico do disco, apenas para gravar aquele caracter, as gravações seriam
muito lentas. Assim estas gravações só serão efetuadas quando houver um volume
razoável de informações a serem gravadas ou quando o arquivo for fechado.
O fechamento libera também, para reaproveitamento, a área utilizada pela estrutura
FILE.
13.3.2 Função sinalizadora de final de arquivo – EOF()
Sintaxe: int feof (FILE *fp);
Biblioteca: stdio.h
A função feof() informa se o fim de arquivo foi encontrado. Devolve 0 se não chegou
ao fim do arquivo.
14.3.3 Função rewind()
Sintaxe: void rewind (FILE *fp);
Biblioteca: stdio.h
Movimenta o localizador de registros para o inicio do arquivo.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
87
13.3.4 Escrita e leitura de um caractere em arquivo texto
putc() : grava um caractere por vez no arquivo.
Sintaxe: int putc (int ch, FILE *fp);
Biblioteca: stdio.h
Se uma gravação for bem sucedida putc() devolverá o caracter gravado, caso con-
trário devolverá um EOF (fim de arquivo).
getc() : Lê no arquivo um caractere de cada vez.
Sintaxe: int getc (FILE *fp);
Biblioteca: stdio.h
Sempre, após a escrita de um caractere ou a leitura de um caractere o sinalizador
de registros ficará posicionado para nova gravação ou leitura em sequencia.
ch é o caracter a ser gravado
fp é o ponteiro do arquivo aberto pela função fopen()
Exemplo para leitura e gravação de um caractere em arquivo texto:
#include <stdio.h>
int main()
{ FILE *fptr;
int ch;
if ((fptr=fopen("arqtext.txt","w+"))==NULL)
{ printf("Impossivel abrir o arquivo");
exit(); // encerra o programae fecha todos os arquivos
}
printf("Digite o texto (<enter> encerra)\n");
while ((ch=getche()) != '\r') //enquanto ch diferente de enter
putc(ch,fptr); //grava no arquivo
rewind(); //reposiciona o localizador de posição de arquivo
no inicio do arquivo
printf("Este é o texto gravado no arquivo:\n");
while((ch=getc(fptr)) != EOF)
printf("%c",ch);
fclose(fptr);
return 0;
}
Arquivos são uma sequencia de informações e é possível deslocar-se dentro desta
sequencia de dados. Sempre que abrimos um arquivo (para leitura ou gravação)
ficamos posicionados no seu inicio e sempre após a realização de cada operação
nos movemos uma posição à frente, o que caracteriza um acesso seqüencial aos
dados. Para ler as informações gravadas no arquivo texto, precisamos retornar ao
inicio do arquivo. Este é o motivo de usar o comando rewind() no exemplo acima,
ele coloca o localizador de registros no início do arquivo.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
88
13.3.5 Escrita e leitura de uma string em um arquivo texto
fputs(): função utilizada quando é preciso gravar uma sequencia de caracteres em
um arquivo.
Sintaxe: char *fputs (char *str, FILE *fp);
Biblioteca: stdio.h
fgets(): usada para ler uma seqüencia de caracteres de um arquivo.
Sintaxe: char *fgets (char *str, int comprimento, FILE *fp);
Biblioteca: stdio.h
A função fgets identifica o fim de uma string através do '\n'
Com estes comandos sempre, após a gravação ou a leitura de uma string, o
sinalizador de registros fica posicionado para nova gravação ou leitura de outra
string, em sequencia.
Exemplo:
//exemplifica leitura e gravacao de strings
#include <stdio.h>
int main ()
{ FILE *fptr;
char linha[81];
if ((fptr = fopen("teste.txt","w+"))==NULL)
{ printf("Impossivel abrir o arquivo");
exit(); // encerra o programa e fecha todos os arquivos
}
printf("Digite o texto (tecle <enter> em nova linha p/ encer-
rar)\n");
while (strlen(gets(linha))>0)
{fputs(linha,fptr); //grava a linha lida no arquivo
fputs("\n", fptr); // grava o sinal de fim de linha
}
rewind(fptr);
printf("Este eh o texto gravado no arquivo:\n");
while((fgets(linha,80,fptr)) != NULL)
printf("%s",linha); //escreve na tela
fclose(fptr);
return 0;
}
13.3.6 Escrita e leitura de dados formatados
Estes comandos são utilizados quando necessitamos gravar dados numéricos em
arquivos.
fprintf() - função para gravação de dados formatados
Sintaxe: int fprintf (FILE *fp, char *formato, lista argumentos);
Biblioteca: stdio.h
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
89
fscanf() – função para leitura de dados formatados.
Sintaxe: int fscanf (FILE *fp, char *formato, lista argumentos);
Biblioteca: stdio.h
A função fscanf devolve o valor EOF se chegou ao fim do arquivo antes de ler
uma variável solicitada.
Podemos relacionar fprintf com printf e também fscanf com scanf, pois todas se
comportam de forma semelhante, exceto pelo fato de que fprintf e scanf operam
com arquivos em disco.
Exemplo para gravação e leitura formatada:
/* faca um programa que grava o nome, o no. de registro e o preco de livros em um
arquivo até o usuario digitar fim para nome. A seguir apresenta os dados gravados*/
#include <stdio.h>
int main()
{ FILE *fptr;
char titulo[30];
int regnum;
float preco;
if ((fptr=fopen("text.txt","w+"))==NULL)
{ printf("Impossivel abrir o arquivo");
exit();
}
printf("\nDigite o titulo do livro (fim encerra):");
scanf("%s",titulo);
while(strcmp(titulo, "fim")!=0)
{ printf("\nDigite o no. do registro:");
scanf("%d",®num);
printf("\nDigite o preco:");
scanf("%f",&preco);
fprintf(fptr,"%s %d %f",titulo, regnum, preco);
printf("\nDigite o titulo do livro (fim encerra):");
scanf("%s",titulo);
}
rewind(fptr);
while(fscanf(fptr,"%s %d %f",titulo,®num,&preco)!=EOF)
printf("%s %3d %f\n", titulo, regnum, preco);
fclose(fptr);
return 0;
}
13.3.7 Funções para leitura e gravação de blocos de dados e implementação
de arquivos binários.
Existem funções apropriadas para realizar leitura e gravação de estruturas (regis-
tros e matrizes) que se caracterizam por ser um bloco de dados, sempre com o
mesmo tamanho. Arquivos destas estruturas podem ser implementados com for-
mato binário, ao invés de texto. A vantagem está em que um arquivo binário ocupa
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
90
bem menos espaço em disco, já que armazena os dados em forma binária, ou seja,
como eles estão armazenados na memória do computador.
Em C os arquivos binários são tratados como de organização seqüencial, sendo
que cada vez que um componente é escrito em um arquivo ou lido de um arquivo,
o apontador do arquivo (indicador de registro) avança para o próximo componente,
ficando assim já prosicionado para uma próxima inserção ou leitura no arquivo, de
forma sequencial. É permitido, porém que sejam criados arquivos com acesso tanto
na forma seqüencial como na forma randômica (acesso direto).
No acesso seqüencial os registros vão sendo gravados sequencialmente, um após
o outro a partir da primeira posição, que é a poisição zero(0). Para acessar um
determinado registro devemos nos posicionar no inicio do arquivo e fazer uma
busca seqüencial no mesmo.
Considerando que todos os registros de um arquivo de bloco de dados são de
mesmo comprimento, a posição correspondente a cada registro pode ser calculada
e o indicador de registro movimentado para esta posição, obtendo-se desta maneira
o acesso direto. A função que permite deslocar o apontador de registros no arquivo
é a função fseek(), que será vista a seguir.
No acesso direto, os registros são gravados também sequencialmente porém im-
plementa-se de tal forma que, antes de gravar cada registro, o apontador (ou loca-
lizador de registros) é levado para uma determinada posição no arquivo (geral-
mente definida por um campo de tipo inteiro da struct e que que seja uma chave
primária das informações).
Para fazer o acesso direto a um registro bastará, então, posicionar o localizador
segundo a chave primária com que foi posicionado na gravação e ler o registro.
O acesso é mais rápido, porém, durante a gravação, a cada vez que o apontador
de registros é deslocado, “saltando posições” no arquivo, estas são ocupadas com
“lixo” o que dificulta um posterior aproveitamento destes espaços no arquivo.
É usual então que se faça uma inicialização dos registros no arquivo colocando em
determinado campo um controle, para indicar que aquela posição do arquivo está
“livre”.
13.3.7.1 Função fseek() - permite deslocar o apontador de registro no arquivo
para uma posição específica.
Sintaxe: int fseek (FILE *fptr, long int num_bytes, int origem);
Biblioteca: stdio.h
num_bytes: É o número de bytes desde a origem até chegar à posição desejada.
A tabela a seguir mostra os três valores possíveis para origem:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
91
Origem Identificador
Início do arquivo SEEK_SET
Posição corrente SEEK_CUR
Fim do arquivo SEEK_END
Para mover o apontador “num_bytes” a partir da origem utiliza-se SEEK_SET,
para movê-lo a partir da posição que se encontra utiliza-se SEEK_CUR e para mo-
ver-lo a partir do final doarquivo usa-se SEEK_END.
A função fseek() retorna zero se for bem sucedida e um valor diferente de zero se
houver erro.
Exemplo:
struct l
{ char titulo[30];
int regnum;
double preco;
};
FILE * fptr;
struct l livro;
fseek((fptr, 9*sizeof(struct l), SEEK_SET);
O comando fseek do trecho de código acima, irá posicionar o apontador de registros
na 9ª. posição no arquivo
13.3.7.2 Função ftell() – retorna a posição do apontador de registros em rela-
ção ao início do arquivo
Sintaxe: long ftell (FILE *fptr);
Biblioteca: stdio.h
13.3.7.3 Funções fread() e fwrite() – leitura e gravação de blocos de dados
As funções fread e fwrite são utilizadas para ler e gravar blocos de dados, usual-
mente uma struct.
fread(): Leitura de blocos
Sintaxe: int fread (void *buffer, int num_bytes, int cont, FILE *fptr);
Biblioteca: stdio.h
Devolve o número de itens (blocos) lidos, o valor de cont portanto. Se encontrar o
fim do arquivo ou ocorrer um erro devolverá um valor menor que cont.
fwrite():Gravação de blocos
Sintaxe: int fwrite (void *buffer, int num_bytes, int cont, FILE *fptr);
Biblioteca: stdio.h
Devolve o número de itens (blocos) gravados, o valor de cont portanto. Se encon-
trar o fim do arquivo ou ocorrer um erro devolverá um valor menor que cont.
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
92
buffer: É um ponteiro para a região da memória ou o endereço de uma variável
que receberá os dados lidos do arquivo pela função fread ou que será gravada no
arquivo pela função fwrite.
num_bytes: Especifica a quantidade de bytes a serem lidos ou gravados.
cont: Determina quantos blocos (cada um com comprimento de num_bytes) serão
lidos ou gravados.
fptr: É o ponteiro para o arquivo.
Este modo grava números no formato binário
Permite gravar qualquer quantidade de dados de uma vez (mais de 1 bloco
por vez);
Uma simples instrução pode gravar matrizes, estruturas, matrizes de estrutu-
ras e outras.
Exemplo de gravação de 01 bloco de dados:
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct
{ char titulo[30];
int regnum;
double preco;
} livro;
FILE * fptr;
fptr = fopen ("livros.rec","wb");
if (fptr == NULL)
{ printf("\nErro na abertura do
arquivo");
exit (1);
}
do
{ printf("\n\nDigite o titulo: ");
gets(livro.titulo);
printf("Digite o registro: ");
gets(numstr);
livro.regnum=atoi(numstr);
printf("Digite o preco: ");
gets(numstr);
livro.preco=atof(numstr);
fwrite(&livro, sizeof(livro), 1, fptr);
printf("Adicionar outro livro(s/n) ?");
} while (getche() == 's');
fclose(fptr);
return 0;
}
fwrite(&livro, sizeof(livro), 1, fptr)
ponteiro para o local da memória
onde os dados lidos ficarão armaz.
quantidade de bytes a serem lidos
quantid de itens a serem lidos
a cada chamada
ponteiro para FILE
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
93
Exemplo de leitura de 01 bloco de dados:
#include <stdio.h>
#include <stdlib.h>
int main()
{ struct
{ char titulo[30];
int regnum;
double preco;
} livro;
FILE * fptr;
fptr=fopen("livros.rec","rb");
if (fptr == NULL)
{ printf("\nErro na abertura do arquivo
livros.rec");
exit (1);
}
while (fread &livro, sizeof(livro), 1, fptr)==1)
{ printf("\nTitulo: %s",livro.titulo);
printf("\nRegistro: 3d",livro.regnum);
printf("\nPreco: %.2f\n",livro.preco);
}
fclose(fptr);
return 0;
}
13.3.8 Teste para identificar a criação ou abertura de um arquivo
Não é usual termos um programa para criar (abrir pela primeira vez) e outro para
abrir sempre que precisarmos fazer manutenção no arquivo (acresentar dados,
pesquisar, excluir, alterar etc.).
Torna-se então necessário implementar um teste que faça o compilador avaliar a
situação do arquivo (ele já existe?).
Abaixo mostra-se uma solução possível, testando o retorno da abertura de arquivo
existente:
int main()
{ FILE *arq;
char nomearq[25];
printw("informe o nome do arquivo\n");
scanw("%s", nomearq);
arq=fopen(nomearq,"rb+"); //abre para leitura ou gravação
if(arq==NULL)
{ arq=fopen( nomearq,"wb+"); //cria para leitura ou gravação
if(arq==NULL)
{ printw(" O arquivo não pode ser aberto");
exit(1);
}
else
{ printw("arquivo aberto com sucesso! (tecle enter)");
getch();
}
return 0;
}
fread(&livro, sizeof(livro), 1, fptr) == 1
ponteiro para o local da memória
onde os dados lidos ficarão armaz.
quantidade de bytes a serem lidos
quantid de itens a serem lidos a cada chamada
ponteiro para FILE
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
94
13.4.9 exemplo de uma função para devolver o tamanho de um arquivo
// função criada para informar o tamanho de um arquivo
long calcularTamanhoArquivo(FILE *arquivo)
{ // guarda o estado antes de chamar a função fseek
long posicaoAtual = ftell(arquivo);
// guarda tamanho do arquivo
long tamanho;
// calcula o tamanho
fseek(arquivo, 0, SEEK_END);
tamanho = ftell(arquivo);
// recupera o estado antigo do arquivo
fseek(arquivo, posicaoAtual, SEEK_SET);
return tamanho;
}
14.0 Estruturas e alocação dinâmica de memória – exemplo com
Lista Encadeada
Uma lista encadeada é um conjunto de itens (células, nodos), onde cada
elemento contém uma informação e um ponteiro para o próximo item.
Possui também um ponteiro para o seu início e o ponteiro do último elemento
tem o valor NULL.
Para colocar um novo item , basta criar uma nova célula, apontar o campo
prox da célula anterior para a nova célula, ler os dados e fazer o seu campo
prox igual a NULL.
Para percorrer a lista inicia-se pelo endereço apontado pela celula cabeça e
segue-se o “caminho” definido pelos endereços dos campos prox de cada
item.
Supondo que os objetos armazenados nas células são do tipo int, a estrutura de
cada célula da lista pode ser definida assim:
struct cel {
int conteudo;
struct cel *prox;
};
conteudo prox
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
95
14.1 Comando typedef
O comando typedef permite ao programador definir um novo nome para um deter-
minado tipo. Sua forma geral é:
typedef antigo_nome novo_nome;
É conveniente tratar as células como um novo tipo de dados e atribuir um nome a
esse novo tipo:
typedef struct cel celula;
Após a designação typedef, uma célula c e um ponteiro p para uma célula po-
dem ser declarados assim:
celula c;
celula *p;
Outro exemplo:
#include <stdio.h>
typedef struct tipo_endereco
{
char rua [50];
int numero;
char bairro [20];
char cidade [30];
char sigla_estado [3];
long int CEP;
} TEndereco;
typedef struct ficha_pessoal
{
char nome [50];
long int telefone;TEndereco endereco;
}TFicha;
int main()
{
TFicha *ex;
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
96
14.2 Lista Encadeada
Declaração de ponteiro para a estrutura:
struct nodo *prim;
No momento em que o programa precisa de espaço na mémória para um nodo ele
o aloca:
prim=(struct nodo*)malloc(sizeof(struct nodo))
A seguir, é possível armazenar valores no nodo:
prim.info = 5;
prim.prox = NULL;
Para criar uma lista encadeada, com inserção no final, necessitamos um nú-
mero maior de ponteiros:
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
97
struct nodo
{ int info;
struct nodo *prox;
};
struct nodo *prim, *novo,*atual;
• prim permanecerá apontando o pri-
meiro
• novo será usado sempre para alo-
car novo espaço
• atual fará o campo prox apontar
para o novo espaço
• depois atual assumirá o endereço
de novo, e permitirá entrar com info
e fazer prox apontar para null
#include <stdio.h>
#include <stdlib.h>
typedef struct nodo /* Estrutura que será usada para criar
os nodos da lista */
{ int info;
struct nodo *prox; //ponteiro para próximo elem. da lista
} Tnodo;
void inserir(Tnodo **cabeca); /* Protótipos das funcoes para in-
serir e listar elementos da lista */
void listar (Tnodo *cabeca);
int main()
{ Tnodo *cabeca = NULL; /* Ponteiro para a cabeça da lista */
Tnodo *noatual; /* Ponteiro para percorrer a
lista no momento de desalocar seus ele-
mentos*/
char q; /* Caractere para receber a opção do usuario*/
do {
printf("\n\nOpcoes: \nI - inserir novo nodo \nl – listar
todos os nodos \nS - sair \nInforme sua opcao: ");
scanf("%c", &q); // Le a opcao do usuario
switch(q) {
case 'i': case 'I': inserir(&cabeca);
break;
case 'l': case 'L': listar(cabeca);
break;
case 's': case 'S': break;
default: printf("\n\n Opcao nao valida");
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
98
}
getchar();
while ((q != 's') && (q != 'S') );
noatual = cabeca;
while (noatual != NULL)
{ cabeca = noatual->prox;
free(noatual); /* Desaloca a memoria alocada para os
elementos da lista*/
noatual = cabeca;
}
return 0;
}
void listar (Tnodo *noatual) /* Lista todos os nodos presentes na
lista encadeada*/
{ int i=0;
while( noatual != NULL) /*Enquanto nao atinge o fim da lista*/
{ i++;
printf("\n Informacao %d = %d ", i, noatual->info);
noatual = noatual->prox; /* Faz noatual apontar para o
proximo nodo*/
}
}
void inserir (Tnodo **cabeca) /* Funcao para inserir um novo nodo,
ao final da lista */
{ Tnodo *noatual, *novono;
int cod;
printf("\n Informacao: ");
scanf("%d", &cod);
if (*cabeca == NULL) /* Se ainda nao existe nenhum produto na
lista*/
{ *cabeca = malloc(sizeof(Tnodo)); // cria o nodo cabeca
(*cabeca)->info = cod;
(*cabeca)->prox = NULL;
}
else
{ noatual = *cabeca; /* Se ja existem elementos na lista,
deve percorre-la ate o seu final e inserir o
novo elemento */
while(noatual->prox != NULL)
noatual = noatual->prox; /* Ao final do while, noatual
aponta p/ o último nodo */
novono = malloc(sizeof(Tnodo)); /* Aloca memoria para o
novo nodo*/
novono->info = cod;
novono->prox = NULL;
noatual->prox = novono; /* Faz o último nodo apontar
para o novo nodo*/
}
}
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
99
15.00 Como criar um arquivo “header” em C no Code::Blocks v13.12
A partir de um projeto já iniciado, que no exemplo (figura 1) é chamado de teste_he-
ader:
Figura 1
1- Entre no menu File – New – File (figura 2):
Figura 2
2- Escolha C/C++ Header – Go (figura 3):
Figura 3
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
100
3- O próximo passo é dar um nome definindo também o diretório para salvar o
arquivo (figura 4):
Figura 4
4- Aperte Finish e sua tela deverá estar como mostra a figura 5:
Figura 5
5- Para finalizar, implemente as funções da biblioteca:
Figura 6
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
101
16. 0 Tabela ASCII
As tabelas mostradas neste anexo representam os 256 códigos usados nos computa-
dores da família IBM. Esta tabela refere-se ao American Standard Code for Information
Interchange (código padrão americano para troca de informações), que é um conjunto
de números representando caracteres ou instruções de controle usados para troca de
informações entre computadores entre si, entre periféricos (teclado, monitor, impres-
sora) e outros dispositivos. Estes códigos tem tamanho de 1 byte com valores de 00h
a FFh (0 a 255 decimal). Podemos dividir estes códigos em três conjuntos: controle,
padrão e estendido.
Os primeiros 32 códigos de 00h até 1Fh (0 a 31 decimal), formam o conjunto de con-
trole ASCII. Estes códigos são usados para controlar dispositivos, por exemplo uma
impressora ou o monitor de vídeo. O código 0Ch (form feed) recebido por ima impres-
sora gera um avanço de uma página. O código 0Dh (carriage return) é enviado pelo
teclado quando a tecla ENTER é pressionada. Embora exista um padrão, alguns pou-
cos dispositivos tratam diferentemente estes códigos e é necessário consultar o manual
para saber exatamente como o equipamento lida com o código. Em alguns casos o
código também pode representar um caracter imprimível. Por exemplo o código 01h
representa o caracter (happy face).
Os 96 códigos seguintes de 20h a 7Fh (32 a 127 decimal) formam o conjunto padrão
ASCII. Todos os computadores lidam da mesma forma com estes códigos. Eles repre-
sentam os caracteres usados na manipulação de textos: códigos-fonte, documentos,
mensagens de correio eletrônico, etc. São constituídos das letras do alfabeto latino (mi-
núsculo e maiúsculo) e alguns símbolos usuais.
Os restantes 128 códigos de 80h até FFh (128 a 255 decimal) formam o conjunto
estendido ASCII. Estes códigos também representam caracteres imprimíveis porem
cada fabricante decide como e quais símbolos usar. Nesta parte do código estão defi-
nidas os caracteres especiais: é, ç, ã, ü ...
Dec. Hex. Controle
0 00h NUL (Null)
1 01h SOH (Start of Heading)
2 02h STX (Start of Text)
3 03h ETX (End of Text)
4 04h EOT (End of Transmision)
5 05h ENQ (Enquiry)
6 06h ACK (Acknowledge)
7 07h BEL (Bell)
8 08h BS (Backspace)
9 09h HT (Horizontal Tab)
10 0Ah LF (Line Feed)
11 0Bh VT (Vertical Tab)
12 0Ch FF (Form Feed)
13 0Dh CR (Carriage Return)
14 0Eh SO (Shift Out)
15 0Fh SI (Shift In)
16 10h DLE (Data Link Escape)
17 11h DC1 (Device control 1)
18 12h DC2 (Device control 2)
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
102
19 13h DC3 (Device control 3)
20 14h DC4 (Device control 4)
21 15h NAK (Negative Acknowledge)22 16h SYN (Synchronous Idle)
23 17h ETB (End Transmission Block)
24 18h CAN (Cancel)
25 19h EM (End of Media)
26 1Ah SUB (Substitute)
27 1Bh ESC (Escape)
28 1Ch FS (File Separator)
29 1Dh GS (Group Separator)
30 1Eh RS (Record Separator)
31 1Fh US (Unit Separator)
Caracter Dec. Hex.
<espaço> 32 20h
! 33 21h
" 34 22h
# 35 23h
$ 36 24h
% 37 25h
& 38 26h
' 39 27h
( 40 28h
) 41 29h
* 42 2Ah
+ 43 2Bh
, 44 2Ch
- 45 2Dh
. 46 2Eh
/ 47 2Fh
0 48 30h
1 49 31h
2 50 32h
3 51 33h
4 52 34h
5 53 35h
6 54 36h
7 55 37h
8 56 38h
9 57 39h
: 58 3Ah
; 59 3Bh
< 60 3Ch
= 61 3Dh
> 62 3Eh
? 63 3Fh
@ 64 40h
A 65 41h
B 66 42h
C 67 43h
Caracter Dec. Hex.
D 68 44h
E 69 45h
F 70 46h
G 71 47h
H 72 48h
I 73 49h
J 74 4Ah
K 75 4Bh
L 76 4Ch
M 77 4Dh
N 78 4Eh
O 79 4Fh
P 80 50h
Q 81 51h
R 82 52h
S 83 53h
T 84 54h
U 85 55h
V 86 56h
W 87 57h
X 88 58h
Y 89 59h
Z 90 5Ah
[ 91 5Bh
\ 92 5Ch
] 93 5Dh
^ 94 5Eh
_ 95 5Fh
` 96 60h
a 97 61h
b 98 62h
c 99 63h
d 100 64h
e 101 65h
f 102 66h
g 103 67h
Caracter Dec. Hex.
h 104 68h
i 105 69h
j 106 6Ah
k 107 6Bh
l 108 6Ch
m 109 6Dh
n 110 6Eh
o 111 6Fh
p 112 70h
q 113 71h
r 114 72h
s 115 73h
t 116 74h
u 117 75h
v 118 76h
w 119 77h
x 120 78h
y 121 79h
z 122 7Ah
{ 123 7Bh
| 124 7Ch
} 125 7Dh
~ 126 7Eh
<delete> 127 7Fh
Ç 128 80h
ü 129 81h
é 130 82h
â 131 83h
ä 132 84h
à 133 85h
å 134 86h
ç 135 87h
ê 136 88h
ë 137 89h
è 138 8Ah
ï 139 8Bh
Caracter Dec. Hex.
î 140 8Ch
ì 141 8Dh
Ä 142 8Eh
Å 143 8Fh
É 144 90h
Æ 145 91h
Æ 146 92h
ô 147 93h
ö 148 94h
ò 149 95h
û 150 96h
ù 151 97h
ÿ 152 98h
Ö 153 99h
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
104
Ü 154 9Ah
¢ 155 9Bh
£ 156 9Ch
¥ 157 9Dh
₧ 158 9Eh
ƒ 159 9Fh
ááááá 160 A0h
í 161 A1h
ó 162 A2h
ú 163 A3h
ñ 164 A4h
Ñ 165 A5h
ª 166 A6h
º 167 A7h
¿ 168 A8h
⌐ 169 A9h
¬ 170 AAh
½ 171 ABh
¼ 172 ACh
¡ 173 ADh
« 174 AEh
» 175 AFh
░ 176 B0h
Caracter Dec. Hex.
▒ 177 B1h
▓ 178 B2h
│ 179 B3h
┤ 180 B4h
╡ 181 B5h
╢ 182 B6h
╖ 183 B7h
╕ 184 B8h
╣ 185 B9h
║ 186 BAh
╗ 187 BBh
╝ 188 BCh
╜ 189 BDh
╛ 190 BEh
┐ 191 BFh
└ 192 C0h
┴ 193 C1h
┬ 194 C2h
├ 195 C3h
─ 196 C4h
┼ 197 C5h
╞ 198 C6h
╟ 199 C7h
╚ 200 C8h
╔ 201 C9h
╩ 202 CAh
╦ 203 CBh
204 CCh
═ 205 CDh
╬ 206 CEh
╧ 207 CFh
╨ 208 DOh
╤ 209 D1h
╥ 210 D2h
╙ 211 D3h
╘ 212 D4h
╒ 213 D5h
Caracter Dec. Hex.
╓ 214 D6h
╫ 215 D7h
╪ 216 D8h
┘ 217 D9h
┌ 218 DAh
█ 219 DBh
▄ 220 DCh
▌ 221 DDh
▐ 222 DEh
▀ 223 DFh
Α 224 E0h
ß 225 E1h
Γ 226 E2h
π 227 E3h
Σ 228 E4h
σ 229 E5h
µ 230 E6h
Τ 231 E7h
Φ 232 E8h
Θ 233 E9h
Ω 234 EAh
δ 235 EBh
∞ 236 ECh
φ 237 EDh
238 EEh
∩ 239 EFh
≡ 240 F0h
± 241 F1h
≥ 242 F2h
≤ 243 F3h
⌠ 244 F4h
⌡ 245 F5h
÷ 246 F6h
≈ 247 F7h
° 248 F8h
∙ 249 F9h
· 250 FAh
Caracter Dec. Hex.
√ 251 FBh
ⁿ 252 FCh
² 253 FDh
· 254 FEh
255 FFh
Notas de aula de Programação Estruturada
Professoes Beatriz Lux e Rolf F. Molz
105
Entre os caracteres da tabela ASCII estendidos, os mais úteis são, talvez, os caracteres
de desenho de quadro em linhas simples e duplas: os caracteres de B3h até DAh (179 a
218 decimal). Como a visualização deste conjunto é difícil, o desenho abaixo pode auxiliar
nesta tarefa:
196 194 205 203
218 ┌ ─ ┬ ┐ 191 201 ╔ ═ ╦ ╗ 187
179 │ 186 ║
195 ├ ┼ ┤ 180 204 ╠ ╬ ╣ 185
197 206
192 └ ┴ ┘ 217 200 ╚ ╩ ╝ 188
193 202
209 210
213 ╒ ╤ ╕ 184 214 ╓ ╥ ╖ 183
198 ╞ ╪ ╡ 181 199 ╟ ╫ ╢ 182
216 215
212 ╘ ╧ ╛ 190 211 ╙ ╨ ╜ 189
207 208
Material retirado de:
http://cpd-srv03.unifra.br/~walkiria/alg_prog2/exe-nteriores/tabela%20ASCII.doc