Академический Документы
Профессиональный Документы
Культура Документы
HISTÓRICO DA LINGUAGEM C
A linguagem C foi criada na década de 70, por Dennis Ritchie, nos laboratórios Bell. Para tanto, ele
utilizou o sistema operacional Unix e a linguagem BCPL. Baseado nessa linguagem, um outro pesquisador,
chamado Ken Thompson (que também trabalhou na criação de C) havia criado a linguagem B, que por sua vez
foi influenciada pela linguagem BCPL criada por Martin Richards. Como a linguagem de Richie foi posterior à
linguagem B, recebeu o nome de C.
A linguagem C é estruturada, e considerada de nível médio, possui portabilidade do código fonte e um
programa objeto muito eficiente, rápido e compacto.
Alguns Softwares escritos em C: Unix e Linux, Parte do Windows e seus Aplicativos, Borland Delphi, Turbo
Pascal, C++Builder, etc.
2 FORMA GERAL
• A função main( ) tem que existir em algum lugar do programa marca o início da execução.
• Programa mínimo em C:
main( )
{
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Exemplo:
// Programa: prog1.cpp
// Autor: Ivo Mathias e Jeferson Quimelli
#include <stdio.h>
#include <conio.h>
void main( )
{
printf (“Meu primeiro programa em Linguagem C”);
getch();
}
• Toda instrução deve ser encerrada por < ; > (ponto e vírgula);
• printf é uma função, note um ‘( )’ após o nome, o que é regra básica para todos os comandos em linguagem
C;
3 FUNÇÃO PRINTF( )
• Função de E/S
• Não faz parte da definição de C – necessita inclusão da biblioteca stdio.h
SINTAXE:
1)
main( )
{
printf (“Esta e a linha numero um \n”);
printf (“Esta e a linha numero dois”);
}
2)
main( )
{
printf (“Esta e a linha numero um \nEsta e a linha numero dois”);
}
3)
main( )
{
printf (“o número 2”);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
4)
main( )
{
printf (“o número %d”, 2);
}
5)
main( )
{
printf (“Meu nome eh %s, \nEu tenho %d anos.”, “Mario”, 18);
}
Saída:
obs: \n é um código especial que produz uma mudança de linha, como veremos logo à frente, item 3.1.
6)
main( )
{
printf (“ A letra %c”, ‘a’);
printf (“ vem antes de %c”, ‘b’);
}
Saída:
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
1)
main( )
{
printf (“A\nB\nC”);
}
2)
main( )
{
printf (“A\tB\tC”);
}
3)
main( )
{
printf (“A\nB\rC”);
}
4)
main( )
{
printf (“AB\bC”);
}
5)
main( )
{
printf (“.\n.1\n..2\n...3\n”);
}
6)
main( )
{
printf (“Comandos DOS residem no C:\\DOS”);
}
7)
main( )
{
printf (“\”Fique alerta!\”, gritou o veterano.”);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
1)
main( )
{
printf (“Este e o capitulo %d”, 3);
}
2)
main( )
{
printf (“%d+%d = %d”, 3, 4, 3+4);
}
3)
main( )
{
printf (“%d = %i”, 3, 3);
}
4)
main( ) // cuidado!! Este programa gerará erro (imprimirá:1717986918)
{
printf (“O valor %d e um numero em ponto flutuante”, 3.3);
}
5)
main( ) // programa corrigido:
{
printf (“O valor %f e um numero em ponto flutuante”, 3.3);
}
6)
main( ) // o código %f gera saída com 7 algarismos significativos
{
printf (“%f + %f = %f”, 3.3, 4.4, 3.3 + 4.4);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
7)
main( ) // %e permite impressão de pot. de 10 em formato científico
{
printf (“%e + %e = %E”, 8.2e+002, 3.36e+005, 8.2e+002 + 3.36e+005);
}
8)
main( ) // Um mesmo número, 16, escrito com formatações diferentes
{
printf (“%d %o %x”, 16, 16, 16);
}
Saída:
16 20 10
Explicação:
1)
main( )
{
printf (“os alunos sao %2d \n”, 350);
printf (“os alunos sao %4d \n”, 350);
printf (“os alunos sao %5d \n”, 350);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
2)
main( )
{
printf (“%d \n %2d \n %3d”, 7, 8, 9);
}
3)
main( ) // especificador de formato %f, float
{
printf (“%f %f\n”, 535.45, 73745.66);
printf (“%16f %16f\n”, 535.45, 73745.66);
}
4)
main( )
{
printf (“ %3.1f \ n”, 3456.78);
printf (“ %10.3f \ n”, 3456.78);
}
Saída:
3456.8
3456.780
5)
main( ) // especificador %f, com espaco para os decimais
{
printf (“%6.2f %8.4f”, 12.3456, 12.3456);
}
6)
main( ) // especificador %f, com mais de 19 digitos significativos
{
printf (“o numero %f”, 7493752094857203945087405.3453458);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
4 CONSTANTES
Uso:
1)
main( ) //exemplo de constante inteira/decimal
{
printf (“ o numero %d”, 2);
}
2)
main( )
{
printf (“ o numero 2”);
}
Embora os dois programas gerem a mesma saída, no primeiro o “2” é tratado como constante e no
segundo o “2” é tratado como parte (caracter) da expressão (string) de controle.
3)
main( ) //exemplo de constante de ponto flutuante ou real
{
printf (“ o número %f”, 3.141593);
}
Obs: Em C não existem as constantes lógicas (.T., .F., true ou false) que são definidas em outras
linguagens. Um valor falso é representado pelo número 0 (zero), enquanto que qualquer outro valor decimal é
interpretado como verdadeiro. Isto será visto á frente, no assunto expressões lógicas.
5 VARIÁVEIS
• Conceito:
• “Objeto” que pode assumir diferentes valores.
• Espaço de memória de um certo tipo de dado associado a um nome para referenciar seu conteúdo.
• Recurso de armazenamento que guarda valores num formato específico (int ou ponto flutuante, p. ex.)
main( )
{
int idade;
idade = 30;
Neste caso, reservou-se – retirou-se dos endereços de memória disponíveis – um espaço suficiente para
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Sintaxe:
tipo nome-da-variável;
ou
tipo nome1, nome2, ... nomen;
1)
int a;
int b;
2)
int a, b;
3)
char letra;
int número, idade=20; //inicializando variável na declaração
Programas-exemplo:
1)
main( )
{
int x;
float y;
x = 3;
y = 3 * 4.5;
printf (“ %d * 4.5 = %f”, x, y);
}
2)
main( )
{
int soma;
soma = 3 + 5;
Obs: As variáveis podem ser inicializadas quando de sua declaração, como nos exemplos:
int i,contador = 0;
float a, b, media = 3.3;
Esta possibilidade ajuda a melhorar a clareza e qualidade do programa, assim como sua manutenção,
por definir um ponto claro no início do programa para inicialização das variáveis. Qualquer consulta ao valor ou
alteração ficam assim facilitadas.
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
O tamanho, em bytes, de cada tipo de variável pode ser obtido por um programa como o abaixo:
#include <stdio.h>
#include <conio.h>
main( )
{
bool b;
printf (“Tamanho em bytes da variável tipo bool: ”);
printf (“%d”, sizeof(b));
getch();
}
• short – aplicado a um tipo, cria um novo tipo que ocupa metade dos bytes do tipo original. Ex: short int.
• long - aplicado a um tipo, cria um novo tipo que ocupa o dobro dos bytes do tipo original. Ex: long int.
• unsigned – permite que todo o domínio, que antes se aplicava a números negativos e positivos, se
aplique somente a números positivos..
main( )
{
bool a;
char b;
unsigned char c;
short int x;
unsigned short x1;
int z;
unsigned int z1;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
long z2;
unsigned long z3;
float r;
double r1;
a = 1;
b = 'i';
c = 'I';
x = 32767;
x1 = 65535;
z = 2147483647;
z1 = 4294967295;
z2 = 2147483647;
z3 = 4294967295;
r = 2147483647;
r1 = 1.7e+308;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Exemplo:
main( )
{
int evento = 5;
char corrida = ‘c’;
float tempo = 27.25;
obs:
• Não pode-se definir um identificador com o mesmo nome que uma palavra chave.
Palavras Chave:
6 FUNÇÃO SCANF( )
Sintaxe:
scanf(“expressão de controle”, lista de argumentos)
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
%c caracter
%d inteiro
%e número ou notação científica
%f ponto flutuante
%o octal
%x hexadecimal
%s string (cadeia de caracteres)
%lf double
Exemplos:
1)
main( )
{
int num;
char letra;
2)
main( )
{
char a ;
Para este último exemplo, se você digitar a tecla “m”, o programa ecoará:
Se você colocar um asterisco (*) no especificador de formato scanf, ele ignora na leitura o valor digitado
para o campo e continua a leitura com o próximo especificador de formato.
Este especificador tem maior utilidade em leitura de arquivos, para que não sejam efetivamente lidas
informações não relevantes à pesquisa em curso.
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Exemplo:
main( )
{
int valor;
• A função scanf obriga que a tecla <enter> seja pressionada após a entrada dos dados.
• A biblioteca de C oferece funções que lêem dados sem esperar <enter>.
Exemplos:
main( )
{
char ch;
Executando:
digite um caracter: a
todos ja sabiam que você digitou a
main( )
{
char ch;
ch = getch( );
printf (“ \ n somente agora saberemos”);
printf (“que você digitou %c”, ch);
}
Executando:
Digite um caracter:
somente agora saberemos que você digitou b
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
8 OPERADORES
Binários: = + - * / %
Unário: - (aplicado sobre um operador somente)
Exemplos:
int a, b;
b = 3;
a = b + 2;
b = a * b;
b = 7 % 2 resto da divisão entre inteiros. No caso, o resultado seria 1.
main()
{
int a, b;
a = 700 / 2;
b = 700 % 2;
printf("divisao = %d resto = %d", a, b);
)
A atribuição em ‘C’ é uma expressão. Logo você pode executar do mesmo modo estas duas instruções abaixo:
a = 5;
a = b = 4 * a;
Como no programa:
main( )
{
int a, b;
a = 5;
a = b = 4*a;
a := b := 4 * a; <- inválido
b := 4 * a;
a := b;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Atenção! Em C, as instruções:
a = 2000; válido
2000 = a; inválido. Não se pode atribuir valor a uma constante, só a uma variável.
Exemplos:
1)
main( )
{
float nota1, nota2, media;
2)
main( )
{
float nota1, nota2;
3)
main( )
{
int nota, conceito;
Exercício proposto: Expanda este último exemplo, (3), de modo a ficar mais clara a digitação dos dados durante
a execução e a leitura do programa
4)
main( )
{
int resto, divisor, dividendo;
Saída:
entre com 2 números 10 4
resto da divisão inteira de 10 por 4 = 2
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Exemplos:
int n;
n = 0;
n++; -> n = n + 1; -> n = 1
++n; -> n = n + 1; -> n = 2
Se o operador é usado em uma expressão, o valor da variável que é incrementada ou decrementada depende se o
operador é pré-fixado ou pós-fixado:
Exemplos:
1)
n = 5;
x = n++; x = 5 (usa a variável e depois incrementa) n = 6
2)
n = 5;
x = n++ * 3; x = 15 (usa a variável e depois incrementa) n = 6
3)
n = 5;
x = ++n * 3; x = 18 (incrementa e depois usa a variável) n = 6
4)
n = 6;
x = n-- / 2; x = 3 (usa a variável e depois decrementa) n = 5
5)
n = 5;
x = --n / 2; x = 2 (decrementa e depois usa a variável) n = 4
6)
main( )
{
int num = 0;
printf (“ %d”, num);
printf (“ %d”, num++);
printf (“ %d”, num);
}
Saída:
0 0 1
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
7)
main( )
{
int num = 0;
Saída:
0 1 1
Desafio: E se no lugar de num++ e ++num tivéssemos num-- e --num, qual seria a saída?
8)
main( )
{
int z=199, w=100;
++z;
w--;
printf(“z = %d”, z);
printf(“\nw = %d”, w);
}
9)
main( )
{
int valor1=0, valor2=0;
10)
main( ) // Neste programa, a ordem - pré ou pós - faz diferença
{
int x, z=199, w=100;
x = (++z) – (w--);
printf(“x = %d z = %d w = %d”, x, z, w);
}
Exercício: Refaça este último exemplo, retirando os parênteses e alterando a ordem do operador.
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Ou seja:
x = 3 * a - b -> x = (3 * a) - b
x = y = 5 % 2 -> x = (y = (5 % 2))
A precedência dos operadores de incremento e decremento é maior que a dos operadores aritméticos simples:
Primeiramente: ++ e --
Depois: - (unário)
Depois: * / % + -
Finalmente: = (atribuição binário)
Ou seja:
x = 3 * a++ - b -> (3 * (a++)) - b
y = 3 * --a - b -> (3 * (--a)) - b
z = a * b++ -> a * (b++)
Obs: os operadores de incremento e decremento, ++ e -- só podem ser usados com variáveis. Assim, estas
expressões gerarão erro:
(a * b)++;
5++;
Assim, as instruções:
n = 5;
printf (“ %d %d %d \n”, n, n + 1, n++);
terão a saída abaixo se a avaliação for feita da direita para a esquerda (primeiro o n++) :
6 7 5
caso a avaliação dos argumentos da lista fosse feita da esquerda para a direita, a saída seria:
5 6 5
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Outros exemplos:
1)
main( )
{
int n, i = 3;
n = i * (i + 1) + (++i);
printf (“n = %d”, n);
}
2)
i = 3;
printf(“%d %d %d”, i = i + 1, i = i + 1, i = i + 1);
• Operadores:
+=, -=, *=, /=, %=
• Justificativa: Obs: o uso destes operadores produz código de máquina mais eficiente
• Sintaxe:
Exemplos:
i += 2 i = i + 2;
x *= y + 1 x = x * (y + 1)
t /= 4 t = t / 4
p %= 6 p = p % 6
h -= 3 h = h - 3;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
> maior
>= maior ou igual
< menor
<= menor ou igual
== igualdade
!= diferença
• Obs: em C não existe a constante dita “booleana” (false, .F., .T. true):
Comparações verdadeiras retornam o resultado um (1) (muito embora qualquer valor diferente de zero
seja interpretado pelo C como verdadeiro).
Exemplo:
main( )
{
int verdadeiro, falso;
Saída:
Verdadeiro = 1 falso = 0
-(unário)
++ --
* / % + -(binário)
< > <= >= == !=
= += -= *= /= %=
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
1)
(a > 10) || (a < 100) condição final verdadeira somente se a>10 e a<100
2)
(a > 10) && (x < 10) condição final verdadeira se a>10 ou x<10
3)
(x && y) condição final verdadeira se x <>0 ou y<>0 (lembre-se: zero representa falso)
4)
! (x > 0) condição final verdadeira se x <=0
5)
if ((10 < a) && (a < 100)) // verdadeiro se 10 < a < 100
6)
if ((10 < a) || (a == -1)) // verdadeiro se a>10 ou se a= = -1
7)
if((10 <= a) <= 100) é equivalente a:
if ((10 <= a) && (a < 100)) // verdadeiro se (10 <= a <= 100)
8) O programa:
main( )
{
char ch;
printf (“ digite uma letra entre A e Z”);
ch = getche( );
if ((ch >= ‘A’) && (ch < = ‘Z’))
printf (“ Você acertou”)
}
main( )
{
char ch;
printf (“ digite uma letra entre A e Z”);
ch = getche( );
if (ch >= ‘A’)
if (ch < = ‘Z’)
printf (“ você acertou”)
}
• Precedência:
(maior) (! - ++ --), (* / %), (+ -) aritméticos, (< > <= >= <>), (== !=)
relacionais, (&&), (||) lógicos, (= += -= *= /= %=) atribuição (menor)
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
9 COMENTÁRIOS
Exemplo:
10 ESTRUTURA SEQÜENCIAL
• É a mais simples estrutura: a execução seqüencial das instruções do início até o final. Também conhecida
como “por gravidade”.
Programa-exemplo:
main( )
{
int x;
float y;
x = 3;
y = 3 * 4.5;
printf (“ %d * 4.5 = %f”, x, y);
TAREFAS:
2) Dados dois números, informar a divisão do primeiro pelo segundo e o resto da divisão (dica: operados
MOD - %);
3) Calcule uma idade, sendo dados o ano do nascimento e o ano do último aniversário;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
11 ESTRUTURAS DE DECISÃO
• Inserem “inteligência” ao programa, permitindo-o tomar decisões, com ações alternativas, a partir de testes,
conhecidos como “condições”.
• Estas condições lógicas ou testes consistem avaliação do estado de variáveis ou de expressões, avaliações
estas sempre terão resultado “verdadeiro” ou ´falso´.
se <condição>
então
<comando_composto_1>;
senão
<comando_composto_2>;
fim_do_se
Exemplo:
if (<condicao>) if (a<b)
{ {
<comando_composto_1>; a=b;
} c=2;
}
if (<condicao>)
<instrução_1>;
else
<instrução_2>;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Observe que a <instrução 1> é finalizada com o “;” (ponto e vírgula). Isto não significa o final da estrutura de
decisão, mas sim da instrução.
Em Pascal, este ponto e vírgula no comando intermediário não existe.
1)
if (a<b)
a=b;
else
b=a;
2)
main( )
{
if (getche ( ) == ‘p’)
printf (“ você digitou p”);
else
printf (“ você não digitou p”);
}
if (<condicao>)
<instrução_1>;
exemplo:
if (a<b)
a=b;
outros exemplos:
1)
if (i==j)
printf(“ I e j sao iguais”);
2)
if (i!==j)
printf(“ I e j sao diferentes”);
3)
main( )
{
char ch;
ch = getche ( );
if (ch == ‘p’)
printf (“você pressionou a tecla p”);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
4)
main( )
{
if (getche()) == ‘p’ )
{
printf (“ você digitou p”);
printf (“ pressione outra tecla ”);
getche( );
} //fim do if
} //fim do programa
if (<condicao>)
{
<comando_composto_1>;
}
else if (<condicao>)
{
<comando_composto_2>;
}
else
{
<comando_composto_3>;
}
Exemplo:
main( )
{
char ch;
printf (“ digite uma letra entre A e Z”);
ch = getche ( );
if (ch >= ‘A’)
if (ch < = ‘Z’)
printf (“ você acertou”)
}
• PROBLEMA: Na execução do programa abaixo, quando a instrução associada ao else, z=b, será
executada, quando n não for igual a zero, ou quando a não for maior que b?
if (n > 0)
if (a > b)
z = a;
else
z = b;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
Para que o else esteja associado ao if mais externo (o de cima), a estrutura deveria ficar:
if (n > 0)
{
if (a > b)
z = a;
}
else
z = b;
if (n < 100)
if ( n < 10)
printf (“n eh menor que 10”);
else
printf (“n eh menor que 100”); // ativada para 10<=n<100
if (n < 100)
if ( n < 10)
printf (“n eh menor que 10”);
else
printf (“n eh menor que 10”); // mensagem corrigida
ou
if (n < 100)
{
if ( n < 10)
printf (“n eh menor que 10”);
}
else //agora sim o else se refere ao primeiro if
printf (“n eh menor que 100”);
main( )
{
int a, b, c;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
APLICAÇÃO: Elaborar um algoritmo para, dados três números, determinar o menor deles.
Algoritmo
Defina variáveis
Leia os números
Determine o menor número
Escreva o menor número
Fim do algoritmo
Algoritmo
Declare a, b, c numéricos
Leia a, b, c
Se a<b e a<c
Então menor a
Senão se b < c
Então menor b
Senão menor c
Fim do algoritmo
Comportamento: Se condição for verdadeira, será executada a expressão 1. Caso contrário, será
executada a expressão 2.
Exemplo 1:
Max = (num1 > num2) ? num1 : num2;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
12 ESTRUTURAS DE REPETIÇÃO
Embora as estruturas seqüencial e de repetição sejam essenciais em programação, são as estruturas de repetição
que permitem que permitem acesso ao potencial dos computadores e executar cálculos muito rápidos.
Poder-se-ia conseguir a impressão dos números inteiros, de 1 a 5, utilizando a estrutura seqüencial, neste
programa com cinco linhas de instruções:
main( )
{
printf (“ 1 ”);
printf (“ 2 ”);
printf (“ 3 ”);
printf (“ 4 ”);
printf (“ 5 ”);
}
Saída: 1 2 3 4 5
Entretanto, não seria viável utilizar o mesmo método para imprimir os 1000 primeiros números a partir de 1,
porque este exigiria pelo menos 1.000 linhas de código:
main( )
{
printf (“1”);
printf (“2”);
: : :
printf (“1000”);
}
while(num<=1000)
{
printf (“ % d”, num);
num++;
}
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
• Caso o programa apresente indícios de travamento, verifique a hipótese de que ele esteja em repetição
infinita (ou loop infinito). Para verificar (e corrigir) a causa deste comportamento anômalo, determine
com exatidão como a estrutura executa cada um dos quatro elementos fundamentais descritos:
inicialização, teste, instruções e modificação.
O programa anterior poderia ser alterado para chegar à sua forma simplificada, mostrando que a estrutura while
não exige o uso das chaves para delimitação das instruções a serem executadas repetidamente. Observe também
que esta estrutura tem sintaticamente a característica de não possuir “;” ao seu final (característica esta
apresentada pela estrutura do-while):
main( )
{
int num=1;
while(num<=1000)
printf (“ % d”, num++);
}
Desafio: Tente descobrir por que os programas abaixo entram em loop infinito e escreva a versão corrigida.
Dica: teste os quatro elementos do while (que não são terra, água, fogo e ar...).
Programa 1:
main( )
{
int num=1;
while(num<=1000)
printf (“ % d”, num-=5);
}
Programa 2:
main( )
{
int cont=20;
while(cont<=800);
printf (“ % d”, cont++);
}
Programa 3:
main( )
{
int num=1;
while(num<=1000)
printf (“ % d”, num);
}
• Seja muito criterioso ao alterar o valor da variável de controle dentro da estrutura for. De preferência,
nunca faça isso, a não ser que não haja outro jeito. Você pode literalmente perder o controle da
execução da estrutura.
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
• Forma geral:
for (inicialização; teste; incremento)
{
<bloco de instruções>;
}
• Comportamento: Esta estrutura inicializa a variável de controle, testa o seu valor desta variável e, após,
modifica o seu valor, incrementando ou decrementando. Se o teste tiver resultado positivo, a estrutura
permite a execução das instruções associadas. Se não, o controle é passado à próxima instrução após a
estrutura. Em outras palavras:
o Inicialização: Expressão de atribuição que é executada uma única vez;
o Teste: Condição que controla a execução do laço. É sempre avaliada a cada execução: se o
resultado é verdadeiro, executa-se novamente as instruções associadas; se falso, interrompe-se
a execução;
o Incremento: Define como a variável de controle será alterada; Efetuado após execução do
corpo do laço, define o valor da variável de controle para a próxima iteração.
o Bloco de instruções: se houver somente uma instrução a executar, dispensa-se o uso das
chaves.
Exemplo:
main( )
{
for (num=1; num<=1000; num++)
printf (“ % d”, num);
}
• Perceba que a estrutura for apresenta os mesmos quatro elementos fundamentais da estrutura while:
inicialização, teste, modificação e instruções.
• Identificando estes quatro elementos em uma estrutura while, você poderá facilmente transformá-la em
uma estrutura for. O que pode ser muito útil se você estiver com dificuldades em construir sua estrutura
for.
Outros exemplos:
1) Inicialização e incremento diferentes de um. Programa que imprime os números pares menores
que 10:
main( )
{
int número;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
main( )
{
int x, y;
main( )
{
char ch;
int i;
main( )
{
char maiusc, minusc;
printf(“Maiúsculas \t Minusculas”);
for (maiusc=’Á’, minusc=’a’; maiusc<’F’; maiusc++, minusc++)
printf(“%c \t %c \n”, maiusc, minusc);
}
main( )
{
char ch;
6) O programa do exemplo anterior, reescrito omitindo expressões. Permanece apenas o “;”, marcando
a posição do elemento omitido:
main( )
{
char ch;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
7) Programa com omissão da expressão de teste. Neste caso, ela é considerada sempre verdadeira:
main( )
{
for ( ; ; )
printf ( “\n estou em loop infinito...”);
}
main( )
{
int n, codigo, num=60;
float nota1, nota2;
9) Programa que utiliza como condição para parada o valor nulo, como valor falso:
#include <stdio.h>
#include <conio.h>
main()
{
int x,y, z;
getch();
}
10) Programa que escreve a tabela ASCII na tela, uma tela por vez:
void main()
{
int cont, linha=1;
printf("Decimal\t\tOctal\t\tHexadecimal\n");
for (cont=0; cont<=255; cont++)
{
printf("%d \t\t %o \t\t %c \t\t\n", cont, cont, cont);
linha++;
if (linha==24)
{
linha=1;
getch();
}
}
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
main( )
{
int i;
• Quando um laço for, while ou do-while está dentro de outro, dizemos que o laço interior está aninhado.
Exemplo:
main( )
{
int linha, coluna;
Ou seja,
quando não se tem como determinar o número de repetições do laço, usa-se estrutura while
quando se pode determinar o número de repetições do laço, usa-se estrutura for
Exemplo do uso do while:Contar o número de caracteres de uma frase até que <Enter> seja digitado.
main( )
{
int cont = 0;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
12.3 DO-WHILE
• Implementação em C da estrutura em algoritmo repita..até;
• Cria um ciclo repetitivo até que a expressão de teste seja falsa (=0).
• Similar ao laço while, entretanto a condição é avaliada no final da estrutura.
Forma Geral:
do
{
<bloco de instruções>
}
while (expressão de teste);
main( )
{
int n=1;
do
{
printf (“n= %d”, n);
n++
}
while (n<=100);
}
main( )
{
int n=1;
do
printf (“n= %d”, n++);
while (n<=100);
}
3) Programa que testa a capacidade de adivinhar uma letra, utilizando estruturas while e do-while:
main( )
{
char ch;
int tentativas;
do
{
printf (“digite uma letra”);
tentativas = 1;
while ((ch = getch( )) != ‘t’)
{
printf (“%c é incorreto \n”, c);
tentativas++;
printf (“tente novamente \n”);
}
printf (“%c é correto”, c);
printf (“acertou em %d vezes”, tentativas);
printf (“continua? (s / n):”);
}
while (getche( ) == ‘s’);
}
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
13 SWITCH
• Forma de substituir a estrutura if–else if-else ao se executar vários testes com maior flexibilidade e
formato limpo;
Forma geral:
switch (expressão)
{
case constante1:
<instruções;>
break;
case constante2:
<instruções>
break;
default:
<instruções>
}
• As instruções colocadas após cada case e a instrução break associada são opcionais e podem ser
omitidas, conforme podem ser ver nos exemplos colocados a seguir.
• A expressão de controle do switch deve resultar em um valor inteiro ou caracter.
• É obrigatório o uso de constantes após a palavra reservada case. Expressões ou variáveis serão rejeitadas
pelo compilador se inseridas neste local.
• Caso a condição deva ser expressada por uma expressão ou por um domínio/extensão (2 a 20, p. ex.), deve
se usar a estrutura if – else if – else.
• A instrução break, que pode ser usada em qualquer estrutura de repetição em C, causa a saída imediata do
laço. Quando estiver presente em laços aninhados afetará somente o laço que o contém (e os internos,
obviamente).
• Comportamento: O switch avalia o resultado da expressão e procura este valor na lista de opções. Se
encontrar, executa a lista de opções associada a esta opção. Se não encontrar, passa-se a executar a primeira
instrução após a estrutura switch.
• Caso não haja um comando break após a lista de instruções associada à opção encontrada, o programa
continuará na execução das instruções associadas às outras opções abaixo. Veja no exemplo:
main( )
{
char letra;
Este resultado indesejado ocorre porque cada vez que o switch recebe para teste uma letra para a qual
existe uma opção, esta letra é impressa no printf correspondente e em todos abaixo.
Para que o programa funcione como desejado, é necessário colocar uma instrução break após cada
opção case, do seguinte modo:
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
main( )
{
char letra;
Em algumas vezes pode ser útil a utilização da técnica de encadeamento em um switch, ou seja,
permitir que uma instrução (ou mais) possa ser ativada por vários case, pela não utilização da instrução break,
mas isto pode resultar em erros de difícil detecção. Veja uma boa utilização da técnica no exemplo:
main( )
{
char letra;
• O rótulo default em uma estrutura switch tem o mesmo efeito que uma cláusula else em um if, ou seja,
permite especificar instruções alternativas caso nenhuma opção do switch seja ativada. Veja o exemplo que
imprime o número de vogais e consoantes do alfabeto:
main( )
{
char letra;
int vogais = 0;
int consoantes = 0;
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática
O programa calculadora da p. 26 seria substituído com vantagem pelo abaixo, que utiliza a estrutura switch:
main( )
{
int a, b, c;
printf("digite o primeiro operando: ");
scanf("%d", &a);
printf("digite o segundo operando: ");
scanf("%d", &b);
printf("\n1 - soma: ");
printf("\n2 - subtracao: ");
printf("\n3 - multiplicacao: ");
printf("\n4 - divisao: ");
printf("\ninforme a operacao: ");
scanf("%d", &c);
switch(c)
{
case 1: printf("\n\n soma: %d", a+b);
break;
case 2: printf("\n\n subtracao: %d", a-b);
break;
case 3: printf("\n\n multiplicacao: %d", a*b);
break;
case 4: printf"\n\n divisao: %d", a/b);
break;
default:
printf("\n\n Voce não digitou nenhum valor valido");
}
getch();
}
14 BREAK
Exemplo:
main( )
{
int num;
while (1)
{
printf ( “\n digite um número, zero para encerrar: ”);
scanf (“%d”, &num);
printf (“ 2 * %d = %d”, num, 2*num);
if (num == 0)
break;
}
}
15 CONTINUE
• Faz que seja executada a próxima interação do laço (ignorando o código que estiver abaixo).
• No caso de while ou do-while, o comando continue desvia o controle para o teste condicional.
• No caso de um laço for, primeiro o incremento é executado e depois o teste condicional.
Obs: Deve-se evitar o uso do comando continue, pois dificulta a manutenção de um programa.
_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Estrutura de Programação Departamento de Informática
15 CONTINUE
2. Dados os valores de a, b e c, determinar os valores das raízes da equação do segundo grau que tenha estes fato-
res.
3. Fazer um programa que calcule e imprima a soma dos números pares desde 100 até 200, inclusive. Utilize es-
trutura while.
5. Fazer um programa que calcule N! (fatorial de N), sendo N um valor inteiro e positivo.
6. Leia um número previamente indeterminado de valores representando idades de indivíduos. O último valor,
que não entrará nos cálculos, deverá conter o valor zero. Calcule e escreva a média das idades deste grupo de in-
divíduos.
7. Fazer um programa que calcule e escreva o número de grãos de milho que se pode colocar num tabuleiro de
xadrez, colocando 1 no primeiro quadro e nos quadros seguintes o dobro do quadro anterior.
8. A conversão de graus Fahrenheit para centígrados é obtida por C = (F-32)*5/9. Fazer um programa que calcule
e escreva uma tabela em graus centígrados em função de graus Fahrenheit, que variam de 50 a 150, de 1 em 1.
9. Supondo que a população de um país A seja da ordem de 90.000.000 de habitantes com uma taxa anual de
crescimento de 3% e que a população de um país B seja, aproximadamente, de 200.000.000 de habitantes com
uma taxa anual de crescimento de 1,5%, fazer um programa que calcule e escreva o número de anos necessários
para que a população do país A ultrapasse ou se iguale à população do país B, mantidas essas taxas de cresci-
mento.
10. Um determinado material radioativo perde metade de sua massa a cada 50 segundos. Dada a massa inicial,
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 38
Estrutura de Programação Departamento de Informática
em gramas, fazer um programa que determine o tempo necessário para que essa massa se torne menor que 0,5
grama. Escreva a massa inicial, a massa final e o tempo calculado em horas, minutos e segundos.
11. Uma companhia de teatro planeja dar uma série de espetáculos. A direção calcula que a R$50,00 o ingresso
serão vendidos 120 ingressos, e as despesas montarão em R$ 2.000,00 (valor fixo). A uma diminuição de R$
5,00 no preço dos ingressos, espera-se que haja um aumento de 26 ingressos vendidos.
Fazer um programa que escreva uma tabela de valores do lucro esperado em função do preço do ingres-
so, fazendo-se variar este preço de R$ 50,00 a R$ 10,00, de R$ 5,00 em R$ 5,00. Escreva ainda o lucro máximo
esperado , o preço e o número de ingressos correspondentes.
13. Fazer um programa para calcular e escrever o valor do número PI, com precisão de 0,0001, usando a série
abaixo. Para obter a precisão desejada, adicionar apenas os termos cujo valor absoluto seja maior ou igual a
0,0001.
4 4 4 4 4
PI = 4 - --- + --- - --- + --- - ---- + ...
3 5 7 9 11
14. Elaborar um programa que calcule e escreva o valor da série abaixo com erro menor que um décimo de mili-
onésimo (0,0000001) e indique quantos termos foram usados.
61 59 57 55 53
S = 63 + ----- + ----- + ----- + ----- + ----- + .....
1! 2! 3! 4! 5!
15. Fazer um programa que calcule o valor do co-seno de um ângulo x, fornecido como entrada, através de 20
termos da série:
x2 x4 x6 x8
co-seno(x) = 1 - ---- + ---- - ---- + ---- - ...
2! 4! 6! 8!
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 39
Algoritmos e Programação Departamento de Informática
b) Compostas
Homogêneas vetores/matrizes (arrays/arranjos)
elementos do mesmo tipo
unidimensionais vetores
multidimensionais matrizes
Heterogêneas registros
elementos podem ser (e geralmente são) de tipos diferentes
estrutura própria para armazenar informações em arquivos
16.3.1 Declaração
Sintaxe:
tipo nome[<tamanho máximo>]
Exemplos:
float nota[60];
int vetor[100];
• Importante: O primeiro elemento de um vetor tem índice zero (0). O segundo elemento é o de índice um
(1). Se um vetor tem “m” elementos, o índice do último elemento será m-1.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 39
Algoritmos e Programação Departamento de Informática
Exemplo de aplicação: Se tivéssemos que ler as notas de 3 alunos e calcular a média destas notas utilizando
variáveis simples, o faríamos com o seguinte código:
main()
{
float nota0, nota1, nota2;
Imagine agora que tivéssemos que calcular a média entre as notas de 300 alunos utilizando variáveis
simples. Nosso programa teria pelo menos 600 linha de código, o que seria inviável:
main()
{
float nota0, nota1, nota2;
A solução é utilizar um array/arranjo unidimensional (vetor) para guardar as notas e utilizar uma estrutura de
repetição while ou for para gerar o índice, do primeiro (zero) ao último.
main()
{
int i;
float notas[300], media=0.0;
i = 0;
while (i<300)
{
printf (“entre com a nota %d”, i+1);
scanf (“%f”, ¬as[i]);
media = media + notas[i];
i++;
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 40
Algoritmos e Programação Departamento de Informática
O mesmo programa implementado com estrutura for seria (com pequenos melhoramentos):
main()
{
int i;
float notas[300], media=0.0;
Proposta: Altere este programa, de forma que imprima o vetor em ordem reversa (do último ao primeiro).
Dica:
for (cont=tamanho-1; cont>=0; cont--)
Segundo exemplo: Leitura de um vetor de números, de tamanho informado informado pelo usuário,
determinando o seu menor elemento:
#include <stdio.h>
#include <conio.h>
#define TAM 100
void main ()
{
int vetor[TAM];
int cont, tamanho, menor;
menor = vetor[0];
for (cont=1; cont<tamanho; cont++)
if(vetor[cont]<menor)
menor=vetor[cont];
getch();
Tarefa proposta: Altere o programa acima de forma que ele informe também a posição do menor elemento
dentro do vetor original.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 41
Algoritmos e Programação Departamento de Informática
Terceiro exemplo: Leitura das três notas de um grupo de alunos, com o cálculo da média de cada um e
determinação de seus critérios de aprovação. Impressão dos valores em forma de tabela (código, notas,
médias e critério).
#include <stdio.h>
#include <conio.h>
void main()
{
float nota1[100], nota2[100], nota3[100], media[100];
int cod[100];
int i, n;
getch();
}
int numero = 0;
Isto equivaleria a:
int notas[5];
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 42
Algoritmos e Programação Departamento de Informática
• A linguagem C aceita a omissão do tamanho do vetor quando da sua inicialização. Neste caso, ele
assume o tamanho do vetor como o número de valores relacionados. Ou seja, Indicando-se os
inicializadores, o compilador fixará a dimensão do arranjo. Como no exemplo:
• Pode se inicializar apenas os primeiros elementos do vetor. Porém não se pode inicializar um elemento
sem se inicializar todos os anteriores. Assim, são válidas as inicializações como as do primeiro
exemplo:
que equivale a:
int notas [5] = {1,2,0,0,0}
do
{
printf (“Informe numero de alunos (max = 100): ”);
scanf(“%d”, &quantidade);
if (quantidade >100)
printf (“Numero excessivo de alunos (>100).”);
}
while(quantidade>TAM);
Isto é, esta estrutura de leitura do tamanho do vetor não permitirá a leitura de um valor além do limite (100, no
caso).
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 43
Algoritmos e Programação Departamento de Informática
16.6.1 HISTÓRICO
O estudo e a aplicação de algoritmos de ordenação sempre estiveram ligados à história da informática de
forma muito marcante:
1880 – Hermann Holerith – Censo decenal americano – a grande massa de dados a ordenar levou à invenção
da máquina de ordenar, que utilizava cartões perfurados, utilizados para leitura de dados até a
década de 80;
1940 – Primeiro programa significativo do primeiro computador digital – rotina de ordenação;
1952 – Primeira análise de algoritmos: Análise do tempo de vários métodos de ordenação.
16.6.2 APLICAÇÃO
Onde existirem dados, sendo mais clara a sua relevância em:
- Listas telefônicas;
- Cadastros;
- Dicionários.
B) Quanto à complexidade
Diretos Avançados
Seleção direta Heapsort
Troca direta (+ lento) Quick sort (+ rápido)
Inserção direta Shell sort
16.6.4.1 SELEÇÃO DIRETA – Para cada posição do vetor, coloca nele o menor elemento do sub-vetor que
começa nesta posição e vai até a última. O primeiro for fixa posições incrementalmente crescentes
para nelas colocar os menores elementos dos sub-vetores. O segundo for varre o sub-vetor à procura
do seus menor elemento. Selecionado este menor elemento, realiza a troca deste elemento com o da
posição fixada no primeiro for.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 44
Algoritmos e Programação Departamento de Informática
16.6.4.2 INSERÇÃO DIRETA – Analisa cada elemento do vetor, da segunda posição (índice 1) até a última
(posição n-1), e o insere na sua posição ordenada, abaixo no vetor. O primeiro for fixa posições
incrementalmente crescentes para analisar os elementos nestas posições. Salva-se este elemento na
variável aux. A estrutura while faz que enquanto existirem, abaixo no vetor, valores maiores que o
elemento, estes últimos sejam alçados a uma posição acima. Quando for encontrada uma posição [j-
1] com valor menor que o elemento em questão (quando vetor[j-1]<aux), a execução da estrutura
while é interrompida e na posição [j] (cujo valor já foi alçado a uma posição superior na iteração
anterior) é inserido o elemento em análise. A ênfase é colocar cada elemento na posição relativa
correta.
• A expressão (j>0) dentro da condição de continuação do while é necessária para o caso do elemento em
análise ser o menor do vetor, para que não seja testada uma posição menor que a [0]. Capice?
16.6.4.3 TROCA DIRETA – Também coloca em cada posição do vetor, a partir do primeiro, o menor
elemento do sub-vetor que começa nesta posição e vai até a última, como no método da seleção direta,
porém traz este menor elemento realizando trocas entre os elementos. O primeiro for fixa posições
incrementalmente crescentes para nelas colocar os menores elementos dos sub-vetores. Com o
segundo for começa comparando se o penúltimo elemento é maior que o último. Se for, efetua a
troca, utilizando a variável auxiliar aux. E continua com as comparações e trocas em posições
incrementalmente descrescentes, até comparar o elemento da posição fixada no primeiro for e seu
subseqüente. Como este método vai trazendo os menores elementos para cima, à semelhança de
bolhas que sobem à superfície, este método também é chamado de bubblesort.
Tarefa: Implementar os algoritmos propostos em programas, testar com conjunto de elementos pré-determinados
e analisar os seus modos de operação.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 45
Algoritmos e Programação Departamento de Informática
Solução da implementação proposta para ordenação pelo método da troca direta (bolha):
#include <stdio.h>
#include <conio.h>
void main ()
{
int vetor[100];
int i, j, n, aux, menor;
getch();
• ATENÇÃO: Se você ficou com alguma dúvida em vetores, estude bem o assunto de novo, porque
iremos ver à frente como passar vetores a funções, quando estas dúvidas não podem mais existir.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 46
Algoritmos e Programação Departamento de Informática
Exemplos:
float distancia[100][80][120];
int tabela[10][10];
• Em ‘C’ interpreta-se que uma matriz é um vetor de vetor(es), ou seja, um vetor em que cada
posição temos um outro vetor (ou um vetor de vetores).
• Por exemplo, o array multidimensional int matéria[4][40] pode ser interpretado como
sendo a representação de 4 matérias, cada uma com 40 alunos. Uma rotina para leitura deste
array seria:
Uma matriz bidimensional pode ter seus valores inicializados já em sua declaração, como no
exemplo:
int nota[3][4];
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 47
Algoritmos e Programação Departamento de Informática
int nota[3][4];
nota[0][0] = 0;
nota[0][1] = 0;
nota[0][2] = 0;
nota[0][3] = 0;
nota[1][0] = 0;
nota[1][1] = 0;
nota[1][2] = 0;
nota[1][3] = 0;
nota[2][0] = 0;
nota[2][1] = 0;
nota[2][2] = 0;
nota[2][3] = 0;
A) Inicializar os elementos de uma matriz com valores crescentes e imprimir os seus elementos:
main()
{
int tabela[10][10], linha, coluna, i=0;
B) Ler uma matriz com dimensões fornecidas pelo teclado e depois imprimir os seus elementos:
main()
{
int tabela[10][10],linhas,colunas,i,j,lm,cm,menor;
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 48
Algoritmos e Programação Departamento de Informática
menor=tabela[0][0];
lm=0;
cm=0;
for (i=0;i<linhas;i++)
for(j=0;j<colunas;j++)
if(tabela[i][j]<menor)
{
menor=tabela[i][j];
lm=i;
cm=j;
}
main()
{
int m1[10][10], m2[10][10], msoma[10][10], linhas, colunas, i, j;
printf("entrada da matriz1:”);
for(i=0;i<linhas;i++)
for(j=0;j<colunas;j++)
{
printf("elemento [%d,%d]:",i+1,j+1);
scanf("%d",&m1[i][j]);
}
printf("entrada da matriz2:”);
for(i=0;i<linhas;i++)
for(j=0;j<colunas;j++)
{
printf("elemento [%d,%d]:",i+1,j+1);
scanf("%d",&m2[i][j]);
msoma[i][j] = m1[i][j] + m2[i][j] ;
}
for(i=0;i<linhas;i++)
{
for(j=0;j<colunas;j++)
printf("%d ",matsoma[i][j]);
printf("\n");
}
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 49
Algoritmos e Programação Departamento de Informática
Condição necessária: o número de colunas da primeira matriz precisa ser igual ao número de
linhas da segunda matriz.
main()
{
int ma[10][10], mb[10][10], mc[10][10], lina, colb, calb, i, j, k;
// lina = numero de linhas da matriz A
// colb = numero de colunas da matriz B
// calb = colunas da matriz A = linhas da matriz B
// Leitura da matriz A
printf("entrada da matriz A:\n");
for(i=0;i<lina;i++)
for(j=0;j<calb;j++)
{
printf("elemento [%d,%d]:",i+1,j+1);
scanf("%d",&ma[i][j]);
}
// Leitura da matriz B
printf("entrada da matriz B:\n");
for(i=0;i<calb;i++)
for(j=0;j<colb;j++)
{
printf("elemento [%d,%d]:",i+1,j+1);
scanf("%d",&mb[i][j]);
}
for(i=0;i<lina;i++)
{
for(j=0;j<colb;j++)
printf("%d ",mc[i][j]);
printf("\n");
}
getch();
}
DICA: A mesma dica quanto aos vetores. Se você ficou com alguma dúvida quanto às matrizes, volte
e tire esta dúvida, para que esta dúvida não atrapalhe o entendimento de passagem de matrizes como
argumento de funções.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 50
Algoritmos e Programação Departamento de Informática
17 STRINGS/CADEIAS DE CARACTERES
#include <stdio.h>
#include <conio.h>
main()
{
printf ("Teste de impressao de string de caracteres, ");
printf ("%s", "tambem chamada de cadeia de caracteres");
getch();
}
• Lembrando que a forma geral da função printf é: printf (“expressão de controle”, lista de
argumentos), percebe-se que
o No primeiro printf a string de caracteres compõe a expressão de controle, visto que não existe
argumentos na lista, enquanto que
o o segundo printf trata a string como argumento a ser impresso com código de formatação %s.
As strings também podem ser inicializadas quando de sua declaração, como no exemplo:
main()
{
char cadeia1[10]= “exemplo1”;
char cadeia2[10]= {“exemplo2”};
char cadeia3[10]= {’e’,’x’,’e’,’m’,’p’,’l’,’o’,’3’,’\0’;
getch();
}
Note que cadeia3 é inicializada como vetor de caracteres, incluindo o caracter null ao final.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 51
Algoritmos e Programação Departamento de Informática
Utiliza o código de formatação %s para leitura e escrita. O scanf lê a string digitada até que um caracter branco
seja encontrado. Como no exemplo:
#include <stdio.h>
#include <conio.h>
main()
{
char frase[128];
printf ("Digite uma frase e encerre com <Enter>: ");
scanf ("%s", &frase);
printf ("%s", frase);
getch();
}
• Atividade proposta: Teste este exemplo e verifique a sua limitação para leitura de frases (palavras
separadas por espaços).
• O programa abaixo também é válido. Ao invés de passar para a função scanf o endereço da variável,
&frase, passa o endereço do seu primeiro elemento, &frase[0]. Veremos a razão disto quando
estudarmos passagem de parâmetros por referência para funções.
#include <stdio.h>
#include <conio.h>
main()
{
char frase[128];
printf ("Digite uma frase e encerre com <Enter>: ");
scanf ("%s", &frase[0]);
printf ("frase = %s", frase);
getch();
}
Outra limitação do uso de scanf para leitura de strings de caracteres é exemplificado abaixo.
Este programa, junção dos dois últimos, não funciona adequadamente – o segundo scanf não lê nenhum
valor digitado pelo teclado. Isto acontece porque o <Enter> digitado para a leitura da primeira frase permanece
no buffer de leitura e será interpretado como o valor a ser lido pelo segundo scanf.
A diferença entre as duas implementações da função scanf (&frase e &frase[0])) não influi em nada
neste resultado.
main()
{
char frase[128];
printf ("Digite uma frase e encerre com <Enter>: ");
scanf ("%s", &frase);
printf ("frase = %s", frase);
getch();
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 52
Algoritmos e Programação Departamento de Informática
A linguagem C provê as funções gets e puts para leitura mais facilitada de strings.
• A função gets (get string) lê os caracteres digitados até encontrar um caracter ’\n’. Então atribui
estes caracteres à variável string, substituindo o ’\n’ por ’\0’.
• A função puts (put string) imprime no dispositivo de saída padrão (no caso, o vídeo) o valor do seu
argumento. Só aceita um argumento.
#include <stdio.h>
#include <conio.h>
main()
{
char frase[128];
printf ("Digite uma frase e encerre com <Enter>: ");
gets (frase); // Esta instrucao funciona
puts (frase);
• O caracter ‘\n’ (ASCII 10, LF, Line Feed, Avanço de Linha), é o caracter introduzido quando se digita o
<Enter>. A digitação do <Enter> também introduz outro caracter, o ASCII 13, CR, Carriage Return, ou
Retorno de Carro de Impressão.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 53
Algoritmos e Programação Departamento de Informática
• Tarefa: Incremente neste último programa-exemplo a impressão da string caracter a caracter, utilizando
a função putchar().
Exemplo 2: Programa que conta o número de vezes que um dado caractere aparece em um texto:
#include <stdio.h>
#include <conio.h>
#define TAM 256
main( )
{
int i=0, c, ocorrencias=0;
char frase[TAM], caracter;
getch();
}
Exemplo 3: Programa que demonstra que uma string pode ser tratada como um vetor de caracteres no printf,
imprimindo elementos isolados(caracteres) ou a string toda:
#include <stdio.h>
#include <conio.h>
main()
{
char nome[40];
printf ("Digite seu nome: ");
gets(nome);
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 54
Algoritmos e Programação Departamento de Informática
17.6.1 strlen(str)
Definição: Retorna o tamanho do string, ou seja, o número de caracteres que a compõe. Não inclui na contagem
o caracter de controle final null, ‘\0’. Requer a inclusão da biblioteca <string.h>.
Exemplo 1:
#include <stdio.h>
#include <conio.h>
#include <string.h>
main( )
{
char nome[40];
Exemplo 2:
main()
{
char nome[40]="Joao", sobrenome[20]=" da Silva";
int tamanho;
getch();
}
Definição: Concatena a string str2 ao final da string str1. O tamanho de str1 deve ser suficiente para
comportar a string final, concatenação das duas.
Exemplo1:
main( )
{
char nome[40] = "James ",
sobrenome[30] = "Bond";
strcat(nome, sobrenome);
puts (sobrenome);
puts (nome);
getch();
}
Saida:
Bond
James Bond
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 55
Algoritmos e Programação Departamento de Informática
Exemplo2:
#include <stdio.h>
#include <conio.h>
#include <string.h>
main()
{
char nome[40]="Joao", sobrenome[20]=" da Silva";
strcat(nome, sobrenome);
printf ("\nSeu nome completo = %s", nome);
// ou
puts ("\nSeu nome completo = ");
puts (nome);
getch();
}
Compara dois strings retornando um valor inteiro que indica o status da comparação, que é feita por ordem
alfabética:
A armadilha no uso do strcmp é que a comparação entre duas cadeias iguais retorna valor zero, o que
para uma instrução if é considerado valor falso.
Cuidado, portanto, na montagem do if.
Exemplo 1:
main( )
{
char nome1[40] = "Jose", nome2[30] = "Pedro";
if (strcmp(nome,sobrenome))
puts ("os nomes são diferentes");
else
puts ("os nomes são identicos");
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 56
Algoritmos e Programação Departamento de Informática
Exemplo 2:
main()
{
char senhai[40]="abcdefg", senhaf[40];
int result;
getch();
}
Copia o valor de str2, que pode ser uma variável string ou uma constante string, para str1, que DEVE ser uma
variável string.
A implementação ISO da linguagem C não permite que se faça atribuição para uma variável string do conteúdo
de outra variável string ou de uma constante string.
Exemplo:
main()
{
char sobrenome[20] = "da Silva", nome[20]="Paulo",
nome2[20]="Jose";
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 57
Algoritmos e Programação Departamento de Informática
À medida que aumenta a complexidade de um programa, torna-se cada vez mais imperativo “quebrar” o
programa em pedaços menores, isolando em cada um destes pedaços as instruções logicamente relacionadas. A
esta tarefa se dá o nome de modularização.
Todo programa em C tem pelo menos uma função, denominada main(), que é sempre a primeira a ser
executada.
Ao encontrar a chamada a uma função, o controle do programa é desviado para esta função. Após
executadas as instruções desta, o controle volta ao ponto da função que fez a chamada.
Vantagens:
• Simplificação na programação: Os módulos são menores, e mais simples;
• Qualidade: Projeto, implementação e teste por etapas controladas;
• Reusabilidade do código – pode-se usar as mesmas funções em outros programas. Se uma função está
bem implementada e testada, não há por que desenvolver de novo;
• Legibilidade do programa – cada função tem uma tarefa específica e facilmente reconhecível;
• Particionamento do trabalho em equipes – divisão das tarefas por grupos de pessoas.
• Encapsulamento da funcionalidade - determina-se O QUE a função deve fazer e QUAL a interface dela
com o restante do programa. COMO é feita a implementação pode ser oculto. Caso se deseje alterar esta
implementação, mantendo-se a funcionalidade e a interface, o programa não será afetado, o que influi
em todos as vantagens anteriores.
18.1 PROCEDIMENTOS
As funções podem ou não retornar valores para o ponto de onde são chamadas. Na linguagem C, quando não
retornam valores recebem o nome especial de procedimentos1. Como não retornam valores, devem ter a palavra
void colocada antes do seu nome.
Exemplo:
#include <stdio.h>
#include <conio.h>
void desenha( )
{
int i;
for (i =0; i<=10; i++)
printf (“-”);
}
main( )
{
desenha( );
printf (" tracejado efetuado pela funcao desenha() ");
desenha( );
getch();
}
Cada vez que é executada uma chamada à função desenha() são impressos 10 traços na tela.
Observe que a função não retornou nenhum valor e que também, neste caso, não foi passado à função nenhum
valor.
1
Em Algoritmos e em Pascal, procedimentos são módulos que retornam nenhum ou vários valores. Funções retornam somente
um valor.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 58
Algoritmos e Programação Departamento de Informática
Exemplo 1: Programa com procedimento que imprime valor na tela. Não há envio de valores do main() para o
procedimento e nem retorno de valores para o main().
void mostra_numero()
{
printf("33\n");
}
main()
{
mostra_numero();
getch();
}
Exemplo 2: O mesmo programa do exemplo anterior, com a exceção de que a função/procedimento é colocada
após o main().Neste, para que não ocorra erro no momento da compilação, é colocado o cabeçalho da função
no início do main(). Isto permite que o compilador, no momento da tradução da instrução de chamada à função
verifique se esta chamada está correta quanto ao nome, número e tipo de argumentos/parâmetros, se existirem.
main()
{
void mostra_numero();
printf("33\n");
mostra_numero();
getch();
}
void mostra_numero()
{
printf("33\n");
}
Exemplo 3: Impressão de mensagens no main() antes e depois da chamada à função e na função, antes e depois
da impressão do valor numérico.
void mostra_numero()
{
printf("Imprimindo no inicio da funcao \n");
printf("66\n");
printf("imprimindo no final da funcao\n");
}
main()
{
printf("\nImprimindo do main(),antes de chamar a funcao \n");
mostra_numero();
printf("imprimindo do main(), apos a chamada aa funcao\n");
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 59
Algoritmos e Programação Departamento de Informática
Exemplo 4: Programa que chama 5 vezes a função/procedimento que imprime o alfabeto. Note que a variável i,
apesar de ter sido declarada como do tipo inteiro, recebe caracteres, sem nenhum problema. Poderia, sem
nenhuma alteração para o comportamento do programa, ter sido declarada do tipo char.
void mostra_alfabeto()
{
int i;
for (i='A';i<='Z';++i)
putchar(i);
putchar('\n');
}
main()
{
int i;
for(i=1;i<=5;i++)
mostra_alfabeto();
getch();
}
Quando o procedimento ou função precisa de um ou vários valores para executar a sua funcionalidade,
no seu cabeçalho (primeira linha) são colocadas as variáveis que receberão cópia dos valores enviados nas
chamadas, precedidas de suas declarações de tipo.
Estas variáveis são chamadas de parâmetros da função/procedimento e devem combinar em número e
tipo com os valores ou variáveis colocados nas chamadas, que são chamados de argumentos do procedimento ou
função.
Algumas bibliografias usam os termos parâmetro formal e parâmetro real para os parâmetros e
argumentos, respectivamente. Veja os exemplos:
Exemplo 1: Programa com procedimento que recebe e imprime um valor inteiro. O recebimento é
efetuado através da variável int digito, que receberá cópia do valor do argumento do tipo int, constante ou
variável, na chamada ao procedimento.
A primeira chamada à função usa como argumento a constante inteira 5. A segunda chamada usa como
argumento a variável inteira i.
A declaração do parâmetro digito no cabeçalho da função tem efeito de declaração de variável local.
main()
{
int i=10;
mostra_digito(5);
mostra_digito(i);
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 60
Algoritmos e Programação Departamento de Informática
Exemplo 2: Programa com as mesmas funções do exemplo anterior, porém com a função colocada após
o main().
Para que a compilação deste programa seja possível, é necessário colocar o cabeçalho da função no
início do main(), com a colocação dos tipos dos parâmetros da função (não é preciso colocar o parâmetro). Isto
possibilita que o compilador possa conferir se a chamada à função está correta, incluindo nome da função,
número e tipo dos parâmetros.
main()
{
void mostra_digito(int);
mostra_digito(5);
getch();
}
Exemplo 3: Este programa executa 10 chamadas à função mostra_digito, cada chamada passando um valor
diferente.
main()
{
int i;
for (i=1;i<=10;i++)
mostra_digito(i);
getch();
}
Exemplo 4: Utilização de parâmetros tipo float (reais). Observe que um dos argumentos é inteiro. Isto não causa
erro nem de compilação nem de execução. Caso ocorresse o contrário – parâmetro do tipo int e
argumento do tipo float – somente a parte inteira do argumento seria passado para a função.
Experimente.
main()
{
mostra_soma(45,53.7);
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 61
Algoritmos e Programação Departamento de Informática
• A passagem de parâmetros por valor funciona do mesmo modo para funções, genericamente, da mesma
forma como funciona para os procedimentos, de forma específica.
• O que já não acontece com passagem por referência, haja vista que este a maneira para retorno de
valores da função para o ponto de chamada, o que não se aplica aos procedimentos. Veremos passagem
de parâmetros por referência um pouco mais à frente.
As variáveis declaradas dentro de uma função ou procedimento são denominadas locais e somente são
visíveis, ou seja, podem ser usadas, dentro do próprio bloco, ou seja.
Elas são criadas na entrada do bloco e destruídas na saída (automáticas).
As variáveis globais são as declaradas fora das funções, geralmente antes do main()e podem ser usadas
em qualquer parte do programa, por qualquer função e também ser alterada. O uso de variáveis globais é
fortemente desaconselhado, por permitir a comunicação entre os módulos adicional à interface de entrada e saída
das funções, determinada pelos parâmetros e comando de retorno. Um uso desapercebido do mesmo nome de
variável dentro de um módulo pode ter conseqüências imprevisíveis e geralmente desastrosas.
void mostra_digito(int i)
{
i++;
printf("%d\n",i);
}
main()
{
int i;
getch();
}
Exemplo de programa utilizando variável global. O incremento da variável i dentro da função faz com
que sejam feitas somente 5 chamadas à esta função e não 10, como se esperaria:
int i;
void mostra_digito(int digito)
{
i++;
printf("i= %d digito= %d\n",i, digito);
}
main()
{
for (i=0; i<10; i++)
mostra_digito(i);
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 62
Algoritmos e Programação Departamento de Informática
18.2 FUNÇÕES
Módulos que retornam um ou mais valores ao retornar ao ponto de onde foram chamados.
Exemplos:
return a
return a+b
return (a+b)
A passagem de parâmetros por valor para uma função tem o mesmo comportamento descrito
anteriormente para os procedimentos.
O retorno de valor pode ser feito utilizando passagem de parâmetros por referência, a ser visto mais à
frente, ou através do comando return. O valor retornará no ponto da chamada à função. Veja os exemplos:
Exemplo 1: Programa com função que recebe dois valores inteiros e retorna a sua soma. Na primeira
chamada são utilizados como argumentos duas constantes inteiras, cujas cópias são passadas (por valor) aos
parâmetros a e b, deste modo declaradas variáveis locais da função. A execução do comando return força o
retorno do controle ao local de chamada, levando consigo o resultado do valor colocado à direita do return, no
caso a avaliação da expressão (a+b). Este valor substitui a chamada à função e é atribuído à variável à esquerda.
main()
{
int res1, res2, c=7, d=8;
res1=soma_valores(10,53);
printf("Resultado=%d\n",res1);
res2=soma_valores(c,d);
printf("Resultado=%d\n",res2);
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 63
Algoritmos e Programação Departamento de Informática
main()
{
float c=7.3, d=8.7;
printf("Resultado=%d\n", soma_valores(10,53.7));
printf("Resultado=%d\n", soma_valores(c,d));
getch();
}
Exemplo 3: Programa com função que recebe dois valores e retorna a sua média:
main()
{
float media(float,float);
printf("Media=%f\n",media(7.88,8.37));
getch();
}
Exemplo 4: Programa com função que retorna o fatorial de um numero inteiro passado como
argumento:
main( )
{
printf (“ o fatorial de 4 = %d”, fatorial(4) );
printf (“ o fatorial de 3 = %d”, fatorial(3) );
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 64
Algoritmos e Programação Departamento de Informática
Exemplo 5: Programa que utiliza uma função para ler um caracter e o transformar o caracter em
minúscula, caso ele não o seja:
char minúsculo( )
{
char ch;
ch = getche( );
if ((ch >= ‘A’) && (ch <= ‘Z’))
return (ch + ‘a’ - ‘A’);
else
return (ch);
}
main( )
{
char letra;
Tarefas propostas:
1) Programa com função que receba a altura e a base de um retângulo e devolva a sua área;
2) Modifique a função deste último programa para que ela receba uma letra e a transforme em minúscula,
se ainda não o for.
3) Desenvolva uma função que retorne o absoluto de um número passado como argumento.
4) Função que receba base e expoente, inteiros, e calcule a sua potenciação, ou seja, a base elevada ao
expoente.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 65
Algoritmos e Programação Departamento de Informática
2 3 n
Problema - calcular a seguinte seqüência: S(x, n) = x/1! + x /2! + x /3! + ... + x / n!
Solução:
int fat(int n)
{
int i, resultado= 1;
if (expoente == 0)
return 1;
for(i=1; i<=expoente; i++)
resultado *= base;
return resultado;
}
void main( )
{
float x;
int termos;
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 66
Algoritmos e Programação Departamento de Informática
18.4 RECURSIVIDADE
Recursividade é a chamada de uma função por si mesma.
É muito utilizada em estruturas de dados avançadas como árvores e métodos de ordenação avançados.
Seu uso pode gerar códigos extremamente eficientes e concisos para as necessidades, muito embora possa trazer
como efeito colateral a complexidade.
A maior dificuldade no uso da recursividade é estabelecer um critério exato e controlado para a parada
das chamadas.
Tente, antes de digitar o programa, descobrir como funciona o programa e qual será a sua saída.
Dica: Sacuda a preguiça, use o caderno e lápis e faça as cópias necessárias do texto da função para
visualizar melhor a execução do programa.
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
if (n<3)
repete (n);
main()
{
int m;
m=0;
repete(m);
getch();
}
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 67
Algoritmos e Programação Departamento de Informática
2) Função que recebe uma letra e a transforme em minúscula, se ainda não o for.
3) Desenvolva uma função que retorne o absoluto de um número passado como argumento.
main( )
{
int num, b;
b = abs (-3);
printf (“Valor absoluto de -3 = %d”, b);
4) Função que receba base e expoente, inteiros, e calcule a sua potenciação, ou seja, a base elevada ao
expoente.
if (expoente == 0)
return 1;
for (i = 1; i <= expoente; i++)
resultado *= base
return resultado;
}
• Falaremos sobre passagem de argumentos por referência após abordarmos o próximo tópico,
ponteiros.
_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 68
Algoritmos e Programação Departamento de Informática
19 PONTEIROS
Já utilizamos o operador de endereço & em leituras com scanf, como mostrado abaixo. Neste caso, o
endereço da variável numero é passado como argumento para a função, para que esta coloque neste endereço o
valor digitado.
Exemplo 1:
#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
Exemplo 2:
#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
float numero = 3.3;
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 68
Algoritmos e Programação Departamento de Informática
•Este último exemplo demonstra que endereços do tipo int tem o mesmo formato de endereços de
valor (variáveis) do tipo float, apesar destes dois tipos de dados ocuparem tamanhos diferentes de
espaço de armazenamento.
Por exemplo, a instrução abaixo cria a variável ponteiro ptr, capaz de armazenar o endereço de uma
variável simples do tipo int:
Exemplo completo:
#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
int *ptr;
ptr = &contador;
Contador = 1;
ptr = &contador; /* o endereço de contador é atribuído a ptr */
/* ptr aponta para contador */
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 69
Algoritmos e Programação Departamento de Informática
•Fixe bem: Leia a expressão *ptr como valor da variável cujo endereço está em ptr.
Esta forma de acesso indireto a uma variável, através da variável que armazena o seu endereço
deve ser perfeitamente assimilada, porque é uma das bases para criação dinâmica de variáveis.
*ptr = 8 tem o mesmo efeito que contador = 8, porque ptr aponta para contador.
Exemplo completo 1:
#include <stdio.h>
#include <conio.h>
main()
{
int a, b;
int *pa, *pb; // pa e pb são variáveis do tipo ponteiro para int
a = 2;
b = 5;
getch();
}
Exemplo completo 2:
#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
int *ptr;
ptr = &contador;
printf ("Valor de ptr = %04X ", ptr); // endereço de contador, que está em ptr
printf ("Valor de *ptr = %d Valor de &ptr = %04X", *ptr, &ptr);
getch();
}
•No exemplo:
optr endereço de outra variável, contador, armazenado na variável ponteiro ptr;
o*ptr conteúdo da variável (contador) que tem o seu endereço em ptr, para onde
“ptr aponta”;
o&ptr endereço da variável ponteiro (raramente utilizado).
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 70
Algoritmos e Programação Departamento de Informática
Por exemplo, no programa abaixo, o conteúdo da variável contador é passado à função muda_valor, o valor
da variável a, que recebe cópia deste valor é alterado, porém o conteúdo da variável contador não é alterado:
void muda_valor(int a)
{
a+=4;
printf("Valor dentro da funcao = %d \n", a);
}
main()
{
int contador = 5;
getch();
}
Valor inicial = 5
Valor dentro da funcao =9
Valor final =5
Se quisermos que o conteúdo da variável contador seja alterado pela função muda_valor, devemos passar o
endereço da variável. No cabeçalho da função, a variável que receberá este valor deve ser uma variável ponteiro.
Deste modo, o programa anterior fica alterado para:
main()
{
int contador = 5;
getch();
}
Valor inicial = 5
Valor dentro da funcao =9
Valor final =9
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 71
Algoritmos e Programação Departamento de Informática
Perceba bem as diferenças no cabeçalho da função, na manipulação da variável dentro da função e na chamada
da função:
void muda_valor(int *a); no cabeçalho da função, a variável a, que recebe o endereço da variável
argumento é do tipo ponteiro
Se imprimirmos também o endereço das variáveis utilizadas pelo programa fica mais fácil entender o que
acontece:
main()
{
int contador = 5;
muda_valor(&contador);
printf ("Valor final = %d \n", contador);
getch();
}
Valor inicial = 5
Endereco de contador = 22FF74 /* este valor pode mudar, */
/* dependendo do uso da memória */
Isto confirma que realmente a função alterou o mesmo endereço de memória da variável utilizada no main().
A instrução *a+=4; alterou, indiretamente, o conteúdo da variável contador, porque o conteúdo de a era o
próprio endereço de contador, passado na chamada.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 72
Algoritmos e Programação Departamento de Informática
Atividade proposta 1: Fazer programa que utilize uma função que dobre o valor de uma variável.
Resolução:
main()
{
int contador = 5;
getch();
}
Atividade proposta 2: Fazer programa que utilize uma função para trocar o conteúdo de duas variáveis.
Resolução:
temp = *pa;
*pa = *pb;
*pb = temp;
}
main()
{
int a = 2, b = 3;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 73
Algoritmos e Programação Departamento de Informática
A passagem de vetores e matrizes como argumentos para função sempre ocorre por referência.
Como esperado, isto implica em que as alterações que forem efetuadas no vetor passado como argumento
dentro da função serão mantidas no módulo que chamar esta função.
Mesmo sem explicitamente utilizar endereços na chamada (&variável) ou ponteiros para receber estes
valores no cabeçalho da função (tipo *pvariavel), o que é passado para a função chamada é o endereço do
primeiro elemento do vetor/matriz, valor este assumido pelo elemento do vetor/matriz declarada no cabeçalho
da função.
Note, entretanto, que quando se passam elementos individuais do vetor/matriz, isto é feito por valor (ex:
vetor[2]).
a) Na chamada da função: é colocado o nome da variável composta, do mesmo modo como são colocadas
as variáveis simples. Por exemplo:
imprime_vetor(vetor,quant);
Exemplo completo 1: Programa que lê um vetor e o passa como argumento, para função que o imprime:
main()
{
float vetor[100];
int quant, tamanho;
imprime_vetor(vetor,quant);
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 74
Algoritmos e Programação Departamento de Informática
Exemplo 2: programa que passe um vetor para uma função e obtenha de retorno o seu menor elemento:
#include <stdio.h>
#include <conio.h>
main()
{
float vetor[100];
int cont, tamanho;
float menor_elemento(float[],int);
Tarefa proposta 1: programa que passe um vetor para uma função e obtenha de retorno a soma de seus
elementos.
Resolução:
menor=0;
return soma;
}
e a chamada à função:
soma_elementos (vetor,cont));
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 75
Algoritmos e Programação Departamento de Informática
Exemplo 3: Programa que passe um vetor para uma função e o receba duplicado.
#include <stdio.h>
#include <conio.h>
main()
{
int vetor[100];
int cont, tamanho;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 76
Algoritmos e Programação Departamento de Informática
Exemplo 4: Programa que passe um vetor de inteiros para uma função e o receba ordenado.
#include <stdio.h>
#include <conio.h>
main()
{
int vetor[100];
int cont, tamanho;
getch();
}
Tarefa proposta 2: Refaça o programa utilizando outro método de ordenação (item 16.6, p. 49).
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 77
Algoritmos e Programação Departamento de Informática
Exemplo 5: Programa que passe um vetor de caracteres para uma função e o receba ordenado.
#include <stdio.h>
#include <conio.h>
#define END '\0'
main()
{
char vetor[100];
int cont, tamanho=0;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 78
Algoritmos e Programação Departamento de Informática
A diferença entre a passagem por valor de matrizes em relação à passagem de vetores é que no
cabeçalho da função deve se colocar pelo menos os índices da colunas, como no exemplo:
Exemplo completo 1: fazer um programa que passe uma matriz a uma função e obtenha de retorno o seu
menor elemento:
#include <stdio.h>
#include <conio.h>
main()
{
int matriz[100][100];
int i, j, colunas, linhas;
getch();
}
Tarefa proposta: Programa que passe uma matriz para uma função e obtenha de retorno a soma de seus
elementos.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 79
Algoritmos e Programação Departamento de Informática
Exemplo 2: Programa que passe uma matriz para uma função e a receba duplicada.
#include <stdio.h>
#include <conio.h>
main()
{
int matriz[100][100];
int i, j, colunas, linhas;
DESAFIO: Fazer um programa que leia um conjunto de nomes, passe estes nomes a uma função, que os
devolva ordenados.
Dicas:
• Um nome nada mais é que uma string de caracteres;
• Uma string de caracteres é tratada pelo compilador C como vetor de char;
• Um vetor de nomes é, então, um vetor de vetor, ou seja, uma matriz de caracteres, onde cada linha
da matriz é um nome.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 80
Algoritmos e Programação Departamento de Informática
Uma cadeia de caracteres pode ser declarada, inicializada e impressa utilizando as instruções do programa:
Programa-exemplo 1:
main()
{
int i;
char cadeia[20]= {'M','e','n','s','a','g','e','m'};
for (i=0; cadeia[i] != '\0'; ++i)
putchar(cadeia[i]);
getch();
}
Se imprimirmos também os endereços que cada caracter ocupa na memória, como no programa abixo, teremos
como resultado algo interessante:
Programa-exemplo 2:
main()
{
int i;
char cadeia[20]= {'M','e','n','s','a','g','e','m'};
for (i=0; cadeia[i] != '\0'; ++i)
printf("%X %c\n", &cadeia[i], cadeia[i]);
getch();
}
A saída deste novo programa será, dependendo do uso atual da memória do computador, algo como:
22FF40 M
22FF41 e
22FF42 n
22FF43 s
22FF44 a
22FF45 g
22FF46 e
22FF47 m
Isto quer dizer que os caracteres desta string ocupam endereços de memória sucessivos.
Este fato também vale, do mesmo modo para vetores e matrizes numéricos. Mesmo as matrizes
multidimensionais tem os seus valores armazenados seqüencialmente.
Então, se soubermos o endereço do primeiro caracter, poderemos imprimir os demais, incrementando o valor do
próprio endereço.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 81
Algoritmos e Programação Departamento de Informática
Programa-exemplo 3:
main()
{
int i;
char *p;
char cadeia[20]= {'M','e','n','s','a','g','e','m'};
p= &cadeia[0];
while (*p != '\0')
{
putchar(*p); p++;
}
getch();
}
Programa-exemplo 4:
main()
{
int i;
char *p;
char cadeia[20]= {'M','e','n','s','a','g','e','m'};
p= &cadeia[0];
while (*p != '\0')
putchar(*p++);
getch();
}
Tarefas propostas: Implementar os programas anteriores, utilizando funções para efetuar as impressões das
cadeias a elas transmitidas.
Resolução programa 1:
#include <stdio.h>
#include <conio.h>
main()
{
char nome[50];
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 82
Algoritmos e Programação Departamento de Informática
Resolução programa 2:
#include <stdio.h>
#include <conio.h>
main()
{
mostra_cadeia("Mensagem a exibir");
getch();
}
Resolução programa 3:
#include <stdio.h>
#include <conio.h>
main()
{
mostra_cadeia("Mensagem a exibir");
getch();
}
Resolução programa 4:
#include <stdio.h>
#include <conio.h>
main()
{
mostra_cadeia("Mensagem a exibir");
getch();
}
Lembre-se de que:
- O caracter '\0' é tratado pelo C da mesma forma como trata o 0 (zero);
- O 0 (zero) é tratado como falso nas condições lógicas (qualquer outro número é tratado como
verdadeiro);
- quando *pcadeia tiver o conteúdo \0, será considerado resultado falso e interromperá a
execução do while.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 83
Algoritmos e Programação Departamento de Informática
Uma string de caracteres não pode ser copiada para outra utilizando uma instrução de atribuição porque as
strings não são variáveis simples. São, na verdade, consideradas pelo compilador C como vetores de strings.
Para copiar o conteúdo de uma string de caracteres em uma variável string, pode-se utilizar a função
strcpy, mostrada no item 17.6.4, ou desenvolver que faça esta cópia, caracter a caracter.
Abaixo, duas maneiras de implementar esta cópia: a primeira utilizando vetores e a segunda, ponteiros.
#include <stdio.h>
#include <conio.h>
main()
{
char destino[128];
getch();
}
#include <stdio.h>
#include <conio.h>
main()
{
char destino[128];
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 84
Algoritmos e Programação Departamento de Informática
Os mesmos conceitos aplicados às strings - que são tratadas pelo compilador C como vetores de
caracteres - são também válidos para outros vetores numéricos, como demonstrado no programa abaixo, que
utiliza ponteiros para exibir os valores de um vetor com 5 valores numéricos:
void mostra_valores(float *pvalor, int tamanho)
{
int i=1;
main()
{
float valores[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
mostra_valores(valores,5);
getch();
}
Note-se, novamente, que na passagem de vetores/matrizes como argumento, o que é passado é o endereço
do primeiro elemento, e que o incremento no ponteiro faz com que ele aponte para o próximo elemento.
Alterando-se o programa para que ele imprima também o endereço de cada elemento, guardado em
pvalor, temos o código:
main()
{
float valores[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
mostra_valores(valores,5);
getch();
}
E o resultado será:
Valor 1.100000 Endereco 22FF50
Valor 2.200000 Endereco 22FF54
Valor 3.300000 Endereco 22FF58
Valor 4.400000 Endereco 22FF5C
Valor 5.500000 Endereco 22FF60
Isto quer dizer que se incrementamos em uma unidade uma variável ponteiro, não é feita simplesmente a
soma do valor 1 a esta variável, mas, sim, é acrescentado o valor necessário para que ele aponte para o próximo
elemento.
No exemplo anterior, incrementando a variável ponteiro em uma unidade, o valor efetivamente somado
foi 4, o tamanho em bytes da variável cujo endereço a variável ponteiro pode armazenar.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 85
Algoritmos e Programação Departamento de Informática
20 ESTRUTURAS E UNIÕES
main()
{
struct exemplo { // declaração do tipo de dado struct exemplo
int num; // as variáveis do tipo exemplo terão campo num
char ch; // as variáveis do tipo exemplo terão campo ch
};
20.1 DECLARAÇÃO
1ª forma:
struct pessoa { // declaração do tipo estrutura pessoa
char[50] nome;
int idade;
float salario;
};
• Uma estrutura tem três componentes principais: 1) a palavra reservada struct, 2) o identificador da
estrutura e 3) a declaração dos campos.
• Inobstante algumas referências indicarem que cliente, neste último exemplo, seria a declaração de
uma variável, testes nos compiladores C indicam que ele (cliente) funciona como um alias (apelido) do
nome do identificador, podendo substituí-lo.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 86
Algoritmos e Programação Departamento de Informática
Identifique: a) a definição do tipo; b) a declaração da variável; c) a definição dos valores dos campos através de
atribuição e leitura e d) a impressão dos valores dos campos.
main()
{
typedef struct pessoa {
int idade;
float salario;
}cliente;
cliente maria;
maria.idade = 30;
printf("Salario: "); scanf("%f", &maria.salario);
getch();
}
main()
{
struct pessoa {
int idade;
float salario;
};
maria.idade = 30;
maria.salario = 500.89;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 87
Algoritmos e Programação Departamento de Informática
Exemplo de aplicação 3: declaração e manipulação dados estrutura, incluindo campo do tipo string de
caracteres.
Demonstra-se, também, que o identificador cliente não é uma variável (e sim um alias do identificador do
tipo) ao se tentar atribuir o valor 33 ao hipotético campo cliente.idade.
E como pode ser “atribuído” um valor a um campo string (via função strcpy).
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
main()
{
typedef struct pessoa {
char nome[50];
int idade;
float salario;
}cliente;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 88
Algoritmos e Programação Departamento de Informática
Estas duas formas de acesso indireto são equivalentes e podem ser interpretadas como:
“Campo da variável para a qual varp aponta”. Ou:
“Campo da variável cujo endereço está em varp”.
triangulo isosceles;
isosceles.lado1=6;
isosceles.lado2=6;
isosceles.lado3=6;
printf("perimetro = %d ",isosceles.perimetro);
getch();
}
triangulo isosceles;
triangulo *p;
p=&isosceles;
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 89
Algoritmos e Programação Departamento de Informática
Tarefa proposta 1: Desenvolver programa que implemente o programa de aplicação imediata (item 20, p. 86) de
forma que permita o acesso indireto aos campos das variáveis do tipo estrutura, utilizando ponteiros.
Tarefa proposta 2: Implementar o programa anterior utilizando uma função para imprimir os elementos. Na
chamada à função deverá ser passado como argumento a variável estrutura e não seus campos individualmente.
Tarefa proposta 3: Altere o programa anterior para que sejam incrementados em uma unidade os valores dos
campos. Obs: Ao se incrementar uma variável caracter, a ela será atribuído o próximo caracter da tabela ASCII.
Resolução da tarefa 1:
main()
{
struct exemplo {
int num;
char ch;
};
exemplo varx;
exemplo *pv;
pv = &varx;
(*pv).num = 2;
(*pv).ch = ‘Z’;
printf(“%d”, (*pv).num);
printf(“%c”, (*pv).ch);
}
Implementação alternativa: substitua as ocorrências de (*pv) por pv->, conforme exposto no item 20.2:
main()
{
struct exemplo {
int num;
char ch;
};
exemplo varx;
exemplo *pv;
pv = &varx;
pv->num = 2;
pv->ch = ‘Z’;
printf(“%d”, pv->num);
printf(“%c”, pv->ch);
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 90
Algoritmos e Programação Departamento de Informática
Resolução da tarefa 2:
struct exemplo {
int num;
char ch;
};
main()
{
exemplo varx;
exemplo *pv;
pv = &varx;
pv->num = 2;
pv->ch = 'Z';
imprime_struct(pv);
}
Resolução da tarefa 3:
struct exemplo {
int num;
char ch;
};
main()
{
exemplo varx;
exemplo *pv;
pv = &varx;
pv->num = 2;
pv->ch = 'A';
incrementa_struct(pv);
printf("%d", pv->num);
printf("%c", pv->ch);
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 91
Algoritmos e Programação Departamento de Informática
cliente clientela[100];
int i, n;
getch();
}
Tarefa proposta 2: programa que defina vetor de estrutura, que possuam campos nome, idade e salário. Utilize
uma função para leitura do vetor e outra para sua impressão.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 92
Algoritmos e Programação Departamento de Informática
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int idade;
float salario;
char nome[10];
} cliente;
main()
{
cliente clientela[50];
int m;
m=insere_clientes(clientela);
mostra_clientes(clientela,m);
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 93
Algoritmos e Programação Departamento de Informática
20.5 UNIÕES
A uniões são declaradas e manipuladas de maneira muito parecida com as estruturas. A diferença entre elas é que
os campos de uma variável declarada de tipo união ocupam um só espaço na memória. Ou seja: pode-se
armazenar diferentes tipos de dados, porém um só deles por vez.
A maior vantagem do uso das uniões é a economia de memória, visto que não são declarados campos que não
são utilizados.
Veja o exemplo:
main()
{
union tipouniao {
int inteiro;
float real;
char caracter;
};
tipouniao varuniao;
getch();
}
Observa-se que deve se manter estrito controle sobre qual dado está armazenado no momento na união.
O grupo de instruções abaixo geraria um resultado errôneo em tempo de execução:
varuniao.inteiro = 2;
printf("Valor atual da uniao = %f \n", varuniao.real);
O espaço que é reservado pelo compilador para uma variável união é o suficiente para armazenar o maior campo
da união.
main()
{
struct stru{
int inteiro;
float real;
char caracter;
};
union uni {
int inteiro;
float real;
char caracter;
};
uni uniao;
stru estrutura;
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 94
Algoritmos e Programação Departamento de Informática
21 ALOCAÇÃO DINÂMICA
A alocação estática de memória, que é a que temos implementado até o momento resolve muitas, porém não
todas as necessidades de programação.
int a;
o compilador estabelece que será alocado, em tempo de execução, o espaço de memória necessário para o
armazenamento de um int e permitirá colocar e retirar valores deste espaço atribuindo a ele um nome, no caso,
a.
Por outro lado, a seguinte declaração reservará espaço suficiente para o armazenamento de 100 variáveis do
tipo int, dando o nome a para apontar para o primeiro elemento:
int a[100];
Entretanto, o que se pode fazer se o espaço reservado não for suficiente, ou seja, se precisarmos utilizar
mais espaço do que o originalmente previsto?
A solução é implementarmos a alocação dinâmica de variáveis, através de ponteiros e das funções
malloc() e sizeof().
A função sizeof() retorna o tamanho, em bytes, do argumento passado para a função. Este argumento
pode ser uma constante, uma variável ou um tipo. Veja o exemplo:
#include <stdio.h>
#include <conio.h>
main()
{
bool b;
printf("sizeof(2)= %d\n", sizeof(2)); // 2
printf("sizeof('A')= %d\n", sizeof('A')); // 1
printf("sizeof(char)= %d\n", sizeof(char)); // 1
printf("sizeof(3.41592)= %d\n", sizeof(3.141592)); // 8
printf("sizeof(bool)= %d", sizeof(b)); // 1
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 95
Algoritmos e Programação Departamento de Informática
A função malloc() aloca o número de bytes passados a ela como argumento e retorna o endereço do
início desta área de memória alocada. Por exemplo, a instrução abaixo aloca 4 bytes de memória e coloca o
endereço inicial deste espaço na variável ponteiro, p:
p = malloc(4);
O endereço retornado é de formato padrão, não formatado para nenhum tipo de ponteiro (em C, diz-se
que é retornado um ponteiro para void).
Oportunamente vimos que no momento da criação de uma variável ponteiro deve-se especificar o tipo de
dado (int, float, char, struct,...) para o qual o ponteiro pode apontar (item 19.20).
Vimos tabém que quando incrementamos uma variável ponteiro, o próximo endereço apontado depende
do tipo de dados apontado (itens 19.6.1 e 19.6.2). Ou seja, quando incrementamos um ponteiro (p. ex: p++) que
aponta para o início de um vetor ou string, esta variável ponteiro apontará para o próximo elemento do vetor ou
para o próximo caracter da string, conforme o caso.
Portanto, para que possamos armazenar o endereço retornado em formato padrão, deve-se transformá-lo
para o tipo específico do ponteiro que irá receber este endereço.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
main()
{
int *p;
*p = 11;
printf ("*p= %d", *p); // *p = 11
getch();
}
Para que o endereço retornado pela função malloc() possa ser armazenado na variável ponteiro p,
que, neste caso, pode armazenar o endereço de um inteiro, utiliza-se uma conversão de tipo (type cast),
colocando-se antes da função malloc() o tipo para o qual se quer converter o endereço. No exemplo, o tipo é
(int *), ponteiro para int, ou seja, o endereço será transformado para o tipo endereço de uma variável
int.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 96
Algoritmos e Programação Departamento de Informática
Estruturas que possuem um campo ponteiro que pode armazenar um endereço de uma outra variável
estrutura do mesmo tipo são especialmente úteis para criar listas encadeadas.
A declaração da estrutura dos nós desta lista deverá conter um campo que permita armazenar o endereço
do próximo nó. Veja o exemplo abaixo:
main()
{
struct elemento {
int valor;
elemento *proximo;
};
primeiro = &noh;
Neste caso, a variável primeiro poderá guardar o endereço de (apontar para) um nó da lista.
Observe que cada nó que for criado utilizando este tipo estrutura elemento terá também um campo
próximo, que poderá guardar o endereço do próximo nó. E assim por diante.
Neste caso foi criada (estaticamente) a variável noh, do tipo estrutura elemento que, portanto, terá os
campos valor e próximo.
O campo valor desta variável noh, declarada do tipo elemento, poderá ser acessado como abaixo:
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 97
Algoritmos e Programação Departamento de Informática
Exemplo de aplicação 1:
O programa abaixo demonstrar de maneira simplificada o tipo de operações que são efetuadas para
listas simplesmente encadeadas:
- Inicialmente declara o tipo struct Tficha, com o campo ponteiro prox.
- Cria, também, um tipo ponteiro Pficha, ponteiro para Tficha.
- Após mostrar o tamanho em bytes do tipo Tficha, aloca uma variável do tipo Tficha
dinamicamente, colocando o seu endereço inicial em novo.
- Este endereço é atribuído a corrente, que também pasa a apontar para a nova variável.
- Atribui-se e imprime-se valor para o campo nome desta nova variável.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
corrente = novo;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 98
Algoritmos e Programação Departamento de Informática
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
main()
{
Pficha primeiro=NULL, corrente, novo;
getch();
}
Obs: Note a criação, neste exemplo, do tipo Pficha, ponteiro para Tficha, tipo ao qual pertencem as variáveis
ponteiro primeiro, corrente e novo. A criação deste tipo torna mais simples e inteligível o uso da função
malloc na instrução: novo = (Pficha) malloc(sizeof(Tficha));
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 99
Algoritmos e Programação Departamento de Informática
A impressão dos nomes armazenados na lista poderia ser implementada no exemplo anterior de maneira
genérica para qualquer número de nomes incluídos através do algoritmo:
Tarefa proposta 1: Implementar um programa que crie, insira nomes e os liste, através de uma lista
simplesmente encadeada, utilizando funções para inserção e listagem dos nomes.
Dica: Você pode utilizar os cabeçalhos para as funções e respectivas chamadas, como mostrado:
insere(&prim, &corr);
lista(prim);
Tarefa proposta 2: Reescrever o programa proposto na tarefa 1, inserindo também uma função para a
exclusão de um nome desejado.
A diferença em relação à lista simplesmente encadeada é que os nós de uma lista duplamente encadeada
precisam de dois campos do tipo ponteiro, um para armazenar o endereço do nó anterior além do campo para
armazenar o endereço do próximo nó.
A ligação de cada nó com o nó anterior e com o posterior, além de permitir a eliminação de nós com
maior facilidade, possibilita percorrer a lista no modo inverso, do último ao primeiro nó, para manipular ou
imprimir os dados.
struct Tficha
{
char nome[40];
char email[30];
char telefone[6];
Tficha *prox;
Tficha *ant;
};
Tficha *primeiro,*novo,*ultimo,*corrente;
Programas que manipulam listas duplamente encadeadas necessitam, além das variáveis tipo estrutura
com dois campos ponteiro (no caso, Tficha), também de variáveis ponteiro que possam armazenar as posições
do primeiro, do ultimo nó, assim como para armazenar os valores temporários do novo nó e do nó
corrente, necessários para a criação e manipulação da lista.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 100
Algoritmos e Programação Departamento de Informática
Exemplo de aplicação 1: O programa abaixo cria dinamicamente três nós de uma lista duplamente
encadeada e lista os nomes na ordem de inserção e na ordem inversa.
main()
{
Pficha primeiro=NULL, corrente, novo, ultimo;
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 101
Algoritmos e Programação Departamento de Informática
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
main()
{
Pficha primeiro=NULL, corrente, novo, ultimo;
int cont=1;
while(cont)
{
novo = (Pficha) malloc(sizeof(Tficha));
if (primeiro== NULL) {
primeiro=novo;
novo->ant=NULL;
}
else {
novo->ant=corrente;
corrente->prox=novo;
}
corrente = novo;
printf("Nome: ");
scanf("%s", &(*corrente).nome);
corrente->prox = NULL;
ultimo = novo;
printf("Digite (0) para listar ou (1) para continuar insercao:");
scanf("%d",&cont);
}
getch();
}
Tarefa proposta: Implementar um programa que crie, insira nomes e os liste, através de uma lista duplamente
encadeada, utilizando funções para inserção, listagem e exclusão de nomes.
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 102
Algoritmos e Programação Departamento de Informática
Uma das possíveis soluções para a tarefa proposta nº 1 sobre listas simplesmente encadeadas:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
main ()
{
PFicha prim=NULL, corr;
int cont=1;
while (cont)
{
insere(&prim, &corr);
printf("Digite (0) para encerrar ou (1) para continuar:");
scanf("%d",&cont);
}
lista(prim);
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 103
Algoritmos e Programação Departamento de Informática
Uma das possíveis soluções para a tarefa proposta sobre o assunto listas duplamente encadeadas:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 104
Algoritmos e Programação Departamento de Informática
main()
{
Pficha primeiro=NULL, ultimo=NULL;
int escolha=1;
while (escolha) //cont==1
{
printf("Escolha: (0)Fim, (1)Inserir, (2)Listar, (3)Lista
inversa, (4)Excluir no': \n");
scanf("%d", &escolha);
if (escolha==0)
printf ("Fim do programa - Obrigado pelo uso!\n");
else if (escolha==1)
insere(&primeiro, &ultimo);
else if (escolha==2)
listadireta(primeiro);
else if (escolha==3)
listainversa(ultimo);
else if (escolha==4)
deleta(&primeiro,&ultimo);
else
printf ("Opcao invalida!\n");
}
getch();
}
_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 105
Algoritmos e Programação Departamento de Informática
22.1.CLASSE
Uma classe é uma estrutura de dados, equivalente à struct da linguagem C.
As classes declaram quais os atributos (variáveis) e quais métodos (funções) terão os objetos que forem
declarados desta classe. Podemos dizer que a classe define a composição do objeto, o seu tipo.
Os atributos e os métodos são os membros da classe. Além desses, temos ainda os construtores e os
destrutores (ver seção 22.11).
22.2.OBJETO
Objetos são instâncias de classes.
Um objeto é um elemento que terá todas as propriedades definidas na classe, do mesmo modo como
uma variável i, declarada como do tipo int, possui todas as propriedades deste tipo. Deste modo, o objeto
possuirá todos os atributos e métodos definidos na classe.
22.3.ENCAPSULAÇÃO
Encerrar, como em cápsula, as propriedades de um objeto. Os componentes do objeto, atributos e
métodos, não podem ser diretamente acessados ou manipulados por outros objetos, a não ser quando isto for
expressamente permitido (label de permissão public:).
22.4.MÉTODO
Cada uma das funções membros da classe e, por conseqüência, dos seus objetos.
22.5.MENSAGEM
Maneira de ativação dos métodos do objeto e de acessar ou manipular os seus atributos, se permitido.
22.6.ABSTRAÇÃO
Quando se cria uma classe de objetos, representação de um objeto do mundo real, apenas alguns de seus
elementos são implementados, tendo em vista que não é possível considerar a sua totalidade. De acordo com o
dicionário: “Ato de separar mentalmente um ou mais elementos de uma totalidade complexa, os quais só
mentalmente podem subsistir fora desta totalidade.”
Assim, quando criamos uma classe clientes, implementamos nesta classe apenas alguns dos seus
dados cadastrais, abstraindo-nos das outras informações, não necessárias para a implementação.
1
GOLDBERG,A.; ROBSON, D. Smalltalk – 80: the language and its implementation. Reading: Addison-
Wesley, 1985.
____________________________________________________________________________ 104
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
#include<conio.h>
#include<iostream.h>
class A
{
public:
void f();
int i, j;
};
void A::f()
{
i = 2;
j = 1;
}
main()
{
A ObjA;
ObjA.f();
cout << ObjA.i;
cout << ObjA.j;
ObjA.i = 3;
ObjA.j = 5;
cout << “Novo valor de i: ” << ObjA.i;
cout << “Novo valor de j: ” << ObjA.j;
getch();
}
Comentários:
1. Cria-se a classe A, que possui dois elementos membros públicos: a função método f() e os atributos
i e j, ambos do tipo int. Isto quer dizer que o método f() e os atributos i e j dos objetos da classe A podem
são acessíveis em qualquer ponto do programa.
2. A implementação do método f() é feita fora da definição da classe (não-inline), pela utilização do
operador de escopo ::. Deste modo, a linha void A::f() quer dizer que ali se inicia a implementação a função
f() da classe A. Dentro da classe fica apenas o protótipo do método, void f();.
O que o método f() faz, ao ser ativado, é inicializar os atributos i e j com os valores 2 e 1,
respectivamente.
3. No main()é criado o objeto ObjA, instância da classe A. Deste modo este objeto possui os atributos
i e j e o método f().
4. A instrução ObjA.f(); é mensagem que ativa o método f() de ObjA. Este inicializa os atributos i
e j de ObjA com os valores 2 e 1. Estes valores são mostrados na unidade de saída padrão pela utilização da
variável cout, disponível com o uso da biblioteca iostream.h, e com o operador <<.
5. As instruções ObjA.i = 3; e ObjA.j = 5; são mensagens que atribuem valores aos atributos i e
j, alterando os valores inicializados pelo método f(). Percebe-se que o acesso direto aos atributos, como o feito
neste caso, pode levar ao descontrole dos valores atuais destes atributos, contrariando o paradigma dos objetos
que é exatamente proteger estes atributos de alterações acidentais ou não desejadas, permitindo acesso a eles
somente pelo método do objeto.
6. Os novos valores dos atributos são exibidos, com o uso da variável cout (lê-se c-out).
____________________________________________________________________________ 105
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
Note que como os métodos e os atributos da classe foram criadas dentro do label de permissão public,
elas podem ser acessadas ou manipuladas por qualquer função externa ao objeto.
22.8.LABELS DE PERMISSÃO
Existem três labels de permisssão, private, public e protected, que indicam o nível de permissão
de acesso que tem os membros declarados dentro deles.
Os membros public podem ser acessados de qualquer função ou objeto, de onde a classe e o objeto
são visíveis.
Os membros protected podem ser acessados somente através dos membros de objetos da mesma
classe, de classes declaradas friend (amigas) e também de membros de objetos de classes derivadas (ver
Herança).
Os membros private são acessíveis comente por membros de objetos da mesma classe ou por
membros de objetos de classes friend.
Veja o exemplo 1:
#include<conio.h>
#include<iostream.h>
class X
{
public:
void f1();
int i1;
private:
void f2();
int i2;
};
void X::f1()
{
i2 = 0;
f2();
i1=1;
}
void X::f2()
{
i1 = 0;
i2 = 0;
f1();
}
main()
{
X x1;
x1.i1 = 0;
x1.f1();
// x1.i2 = 0; se compiladas, estas duas linhas gerarão erro.
// x1.f2(); i2 e f2() são private, somente acessíveis por f1()
Neste exemplo, cria-se o objeto x1 que, por conseqüência passa a possuir os métodos f1() e f2() e os atributos
int i1 e i2.
____________________________________________________________________________ 106
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
A função void f2() e o atributo int i2 são private, portanto somente acessíveis pelo método f1().
Tentativa de acesso por outro modo gerará erro em tempo de compilação.
Exemplo 2:
#include <iostream.h>
class CRetangulo
{
int base, altura;
public:
void define_valores (int,int);
int area (void) {return (base*altura);}
};
int main () {
CRetangulo Ret;
Ret.define_valores (3,4);
cout << "area: " << Ret.area();
}
Comentários:
1. Os atributos base e altura são considerados private, porque esta é a permissão padrão que os
membros de uma classe recebem.
2. A classe possui dois métodos public, as funções void define_valores e int area (void).
3. Como o código da função método int area (void) é implementado na própria definição da classe,
esta é dita inline, ou “em linha” (Isto significa que o compilador seguirá a ordem natural das linhas do código e
não precisará procurar a implementação do método mais à frente).
Tarefa proposta 1: Refazer o exemplo anterior, criando dois objetos de retângulos, Ret1 e Ret2.
22.10.SOBRECARGA
____________________________________________________________________________ 107
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
Exemplo 1:
#include <iostream.h>
#include <conio.h>
void mostra_mensagem()
{
cout << " meu livro" << endl;
}
main()
{
mostra_mensagem("o melhor livro eh??? ");
// chama o parametro do 1 º void
mostra_mensagem();
// chama o parametro do 2º void
getche();
}
Exemplo 2:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void inicializa()
{
x1=x2=x3=0;
}
void mostra()
{
printf("x1= %d\n", x1);
printf("x2= %d\n"),x2;
printf("x3= %d\n"),x3;
}
main()
{
inicializa();
mostra();
inicializa(3);
mostra();
getch();
}
____________________________________________________________________________ 108
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
22.11.CONSTRUTORES E DESTRUTORES
Como comentamos na seção 22.1, Classes, além dos métodos e dos atributos, as classes podem ter
Construtores e Destrutores. São funções específicas para criar e destruir objetos e não tem tipo nem são do tipo
void.
Caso não sejam definidos na classe, no momento da criação e destruição dos objetos são ativados o
construtor e o destrutor padrões (default).
O destrutor padrão é ativado quando o é encerrado o escopo no qual o objeto foi criado, seja ao
final do bloco (função) onde foi criado ou, mesmo, ao final do programa.
O construtor padrão cria o objeto, com seus métodos, porém não inicializa os atributos. Isto pode
ser um problema, caso o programa utilize os valores dos atributos antes destes serem inicializados. O resultado
será um erro.
Para se criar uma função construtora, declara-se dentro da classe uma função com o mesmo nome
da classe. Esta função será chamada automaticamente quando um novo objeto da classe for criado.
// exemplo de classes
#include <iostream.h>
class CRetangulo
{
int base, altura;
public:
CRetangulo (int,int);
int area (void) {return (base*altura);}
};
int main () {
Area de Ret: 12
Area de Ret2: 30
Comentários:
1 – As funções construtoras são facilmente identificáveis porque possuem o mesmo nome da classe
e não tem tipo (int, float,char), nem mesmo especificado como void. Isto porque elas não retornam valor
2 – Perceba que a instanciação (criação) dos objetos Ret e Ret2 inclui a passagem dos parâmetros
com os quais os atributos dos objetos devem ser inicializados.
3 - Não temos mais a função método define_valores, porque o construtor já provê esta
funcionalidade.
____________________________________________________________________________ 109
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
A função destrutora é chamada automaticamente um objeto deixa de existir. Isto pode ocorrer ao
final do programa, ao final do bloco onde o objeto foi criado ou quando um objeto criado dinamicamente deixa
de existir. A não ser no último caso, da destruição de um objeto dinâmico, a destruição é feita sem maiores
problemas, à semelhança das funções e variáveis comuns, que deixam de existir quando o escopo onde são
criados deixa de existir.
Consideraremos, portanto, em exemplo, a implementação de uma função destrutora de objetos
dinâmicos. Objetos dinâmicos tem atribuído a si espaços de memória que precisam ser liberados no momento de
sua destruição e a implementação de um uma função destrutora que cuide destes detalhes é muito útil.
O nome da função destrutora é o mesmo nome da classe, precedido por um ~ (til). À semelhança
das funções construtoras, um destrutor não deve possuir tipo nem ser void.
class CRetangulo
{
int *base, *altura;
public:
CRetangulo (int,int);
~CRetangulo ();
CRetangulo::~CRetangulo () {
delete base;
delete altura;
}
int main () {
return 0;
}
Area de Ret: 12
Area de Ret2: 30
Comentários:
1 – A classe CRetangulo possui um construtor que cria duas variáveis dinâmicas do tipo int e
armazena seus endereços nas variáveis ponteiro base e altura. O valor para base e altura, recebidos através
dos parâmetros a e b do método construtor são colocados nestas variáveis dinâmicas através das variáveis
ponteiro base e altura.
3 – Quando o programa é encerrado os dois objetos ativam suas funções destrutoras, que por sua
vez liberam os espaços de memória alocados pelos construtores de Ret e Ret2.
____________________________________________________________________________ 110
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
22.11.1.Sobrecarga de construtores
Os construtores também podem ser sobrecarregados, ou seja, podem-se ter dois ou mais
construtores com o mesmo nome em uma classe, desde que o número e tipo de parâmetros - a assinatura do
cabeçalho – sejam diferentes.
Como exemplo de sobregarga de construtores, , temos o seguinte programa, cuja classe Cretangulo
declara dois construtores, um que possui dois parâmetros do tipo int. e outro que não possui nenhum argumento.
Este último, ao ser ativado, cria os atributos base e altura, inicializando-os com os valores padrão 5 e 6. O outro
construtor irá inicializar os atributos base e altura com os valores passados como argumento.
O que irá determinar qual dos dois construtores será ativado é o número e o tipo de argumentos
colocados instrução de criação do objeto (instanciação).
class CRetangulo
{
int base, altura;
public:
CRetangulo (); // Construtor – sem parametros
CRetangulo (int,int); // Outro construtor – com parâmetros
CRetangulo::CRetangulo () {
base = 5;
altura = 6;
}
int main () {
CRetangulo Ret(3,4);
CRetangulo Ret2;
Area de Ret: 12
Area de Ret2: 30
Comentários:
1 - O objeto Ret foi criado passando-se como argumentos os valores 3 e 4. O construtor ativado foi
o primeiro, que inicializa os atributos base e altura com os valores passados como argumento s. No caso, 3 e 4.
2 – Já o objeto Ret2 foi declarado (instanciado) sem nenhum argumento, e sem parênteses. Foi
então inicializado com o segundo construtor que não possui nenhum parâmetro, que declara base e altura com os
valores 5 e 6.
3 - É importante observar que se declararmos um objeto novo e não quisermos passar argumentos
para ele, nós não incluímos parênteses (). Ou seja, na ativação do segundo construtor, que não recebe
argumentos, não se devem colocar os parênteses. Assim:
____________________________________________________________________________ 111
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
22.12.HERANÇA
Em uma linguagem Orientada a Objetos, Herança é a capacidade de construir classes, ditas
derivadas, que recebem, em sua definição, os elementos visíveis definidos em outras classes, ditas classes-base.
Deste modo, a classe derivada possuirá, por herança, antes mesmo de serem definidos seus membros próprios, os
membros visíveis que foram definidos na classe base, como públicos.
A definição de uma classe derivada é feita nos seguintes moldes:
Poderiam, ainda, ser utilizados os labels protected ou private, o que determinaria que os
elementos visíveis na classe base tornar-se-ão protegidos ou privados na classe derivada.
A propriedade da herança é muito útil quando temos várias classes com membros em comum.
Assim, para não termos que reescrever esta parte do código em cada uma das classes, cria-se uma classe base que
contenha esta parte em comum e se criam classes derivadas que herdam este código compartilhado.
Deste modo tem-se economia de código, facilidade de manutenção, pois as implementações e
alterações serão feitas somente uma vez, em um só lugar, que se reflete em maior garantia de qualidade.
Como exemplo, podemos estudar as classes que permitiriam criar objetos retângulo e triângulo.
Vemos que ambas possuem os atributos base e largura, e ambas possuem os métodos
define_valores(int, int) e área( ). Como somente este último método, area( ), possui
implementação diferente, os outros membros compartilhados poderão compor uma classe base, chamada
CPoligono, e poderão ser herdados pelas classes CRetangulo e Ctriangulo.
Deste modo, o único membro a ser declarado nas classes derivadas é o método área().
class CPoligono {
protected:
int base, altura;
public:
void define_valores (int a, int b)
{ base=a; altura=b;}
};
int main () {
CRetangulo Ret;
CTriangulo trgl;
Ret.define_valores (4,5);
trgl.define_valores (4,5);
cout << Ret.area() << endl;
cout << trgl.area() << endl;
return 0;
}
20
10
____________________________________________________________________________ 112
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
Em princípio todo membro da classe base é herdado por uma classe derivada com exceção de:
Construtor e destrutor
Membro operator=()
friends
Embora o construtor e destrutor da classe base não sejam herdados, o construtor padrão (ex:
construtor sem parâmetros) e o destrutor da classe base sempre são chamados quando um novo objeto da classe
derivada é criado ou destruído.
22.12.1.HERANÇA MÚLTIPLA
Em C++ é perfeitamente possível que uma classe herde campos e métodos de mais de uma classe
simplesmente separando as classes base diferentes com vírgulas na declaração da classe derivada.
Por exemplo, se tivéssemos uma classe específica para imprimir na tela ( CSaida) e quiséssemos
que nossas classes CRetangulo e CTriangulo também herdassem seus membros além dos de CPoligono,
poderíamos ter escrito:
// herança múltipla
#include <iostream.h>
class CPoligono {
protected:
int base, altura;
public:
void define_valores (int a, int b)
{ base=a; altura=b;}
};
class CSaida {
public:
void saida (int i);
};
int main () {
CRetangulo Ret;
CTriangulo trgl;
Ret.define_valores (4,5);
trgl.define_valores (4,5);
____________________________________________________________________________ 113
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
Ret.saida (Ret.area());
trgl.saida (trgl.area());
return 0;
}
22.13.POLIMORFISMO
É a existência de funções com o mesmo nome em classes diferentes. Apesar de terem o mesmo
nome, elas executam tarefas peculiares a cada classe.
#include <iostream.h>
#include <conio.h>
class cao
{
public:
char nome[10];
float tamanho;
private:
void imprime();
//função imprime tbem pode ser utilizada nas outras classe
};
class gato
{
public:
char nome[10];
float tamanho;
private:
void imprime();
// tem a mesma função da 1º classe mas parametros diferentes
};
void cao::imprime()
{
strcpy(cao::nome,nome);
printf("%s",nome);
}
void gato::imprime()
{
strcpy(gato::nome,nome);
printf("%s",nome);
}
Comentários:
2 – Embora tenham o mesmo nome e assinatura, cada método é intrínseco a cada classe (e portanto
aos seus objetos). Quando for ativado, cada objeto ativará o seu método específico.
____________________________________________________________________________ 114
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática
int main () {
CRetangulo Ret1, Ret2;
Ret1.define_valores (3,4);
cout << "area: " << Ret1.area();
Ret2.define_valores (3,4);
cout << "area: " << Ret2.area();
Obs: Este resumo de POO em C++ teve como referência básica o tutorial: “Básico de C++”, de
Juan Soulié, cujo original está em: http://www.cplusplus.com, cuja tradução, feita por Márcio Franco, está
disponível a partir de: http://www.linhadecodigo.com.br/Artigo.aspx?id=1268
Nele poderão ser vistos alguns itens básicos ainda não incluídos neste resumo, como:
Ponteiros para classes,
A palavra chave this,
Membros estáticos,
Classes friend,
Membros virtuais,
Classes base abstratas.
____________________________________________________________________________ 115
Professor Jeferson Antonio Quimelli