Вы находитесь на странице: 1из 120

Algoritmos e Programação Departamento de Informática

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.

ESTRUTURA BÁSICA DE UM PROGRAMA C

1 DICAS ÚTEIS NA EDIÇÃO DE PROGRAMAS-FONTE

• Salvar em modo texto (não DOC/WORD, RTF);


• Salvar SEMPRE o documento antes de compilar;
• A execução do programa sempre começa na linha main(). Os parênteses são obrigatórios;
• O C é caso sensitivo;
• Use SEMPRE a endentação, porque ela permite maior clareza de código;
• É necessário o ponto-e-vírgula (;) ao final de cada comando.

2 FORMA GERAL

• Consiste em uma coleção de funções, de acordo com a estrutura básica:

main( ) //primeira função a ser executada


{ início da função
corpo da função;
} fim da função

• 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:

printf (“expressão de controle”, lista de argumentos);

Exemplos somente com string na expressão de controle, sem lista de argumentos:

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

Exemplo c/ número decimal como argumento:

4)
main( )
{
printf (“o número %d”, 2);
}

Exemplo c/números decimais e cadeia de caracteres (string):

5)
main( )
{
printf (“Meu nome eh %s, \nEu tenho %d anos.”, “Mario”, 18);
}

Saída:

Meu nome é Mario,


Eu tenho 18 anos.

obs: \n é um código especial que produz uma mudança de linha, como veremos logo à frente, item 3.1.

Exemplo com caracteres:

6)
main( )
{
printf (“ A letra %c”, ‘a’);
printf (“ vem antes de %c”, ‘b’);
}

Saída:

A letra a vem antes de b

3.1 CARACTERES DE CONTROLE (SEQÜÊNCIAS DE ESCAPE)


\n nova linha
\r <enter> (= retorno de carro = <cr> = return)
\t tabulação (tab)
\b retrocesso (volta posição do cursor, permitindo sobrescrever caracter)
\“ aspas (para escrever aspas, sem fechar a expressão de controle)
\\ barra (para escrever barra, sem interpretar como caracter de controle)

_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática

Exemplos utilizando caracteres de controle/ seqüências de escape:

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

3.2 CÓDIGOS DE FORMATAÇÃO


%c caracter
%d decimal
%e notação científica
%f ponto flutuante
%o octal
%s cadeia de caracteres (string)
%x hexadecimal
%lf double
%ld longint

Exemplos utilizando códigos de formatação:

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);
}

Obs: 8.2e+002 = 8.2x102 = 8.2x100 = 820


3.36e+005 = 3.36x105 = 3.36x100000 = 336000

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:

Em decimal: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16


Em octal: 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20
Em hexa: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, A, B, C, D, E, 10

3.2.1 TAMANHO DE CAMPO

• Tamanho de campos: é possível estabelecer o tamanho mínimo para a impressão de um campo.


• Para números inteiros (código %d), o número colocado entre o “%” e o “d” estabelece o número de casas
reservadas para a impressão do argumento.
• Para números em formato de ponto flutuante (reais), pode-se especificar além do tamanho total, o número de
casas decimais que se deseja sejam mostradas. Ex: o código %8.4f significa que serão reservadas oito casas
para o número total, das quais quatro serão reservadas para a parte decimal.

Exemplos de estabelecimento de tamanhos de campo para inteiros:

1)
main( )
{
printf (“os alunos sao %2d \n”, 350);
printf (“os alunos sao %4d \n”, 350);
printf (“os alunos sao %5d \n”, 350);
}

Saída: os alunos são 350


os alunos são 350
os alunos são 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);
}

Exemplos de estabelecimento de tamanhos de campo para reais / ponto flutuante:

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

• “Objeto” que tem valor fixo e inalterável ao longo da execução do programa.

Exemplos: ‘c’, 8, “primeiro programa”, 2.876.

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.)

5.1 DECLARAÇÃO DE VARIÁVEIS


• Objetivo: reservar uma quantidade de memória para um certo tipo de dado, indicando o nome pelo qual a
área será referenciada. Como no exemplo:

main( )
{
int idade;
idade = 30;

printf (“ mínima idade é : %d”, idade);


}

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

armazenar constantes inteiras e se deu a este espaço o nome de idade.

Sintaxe:

No modo mais simples a declaração de variáveis tem a forma:

tipo nome-da-variável;
ou
tipo nome1, nome2, ... nomen;

Exemplos de declarações de variáveis:

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;

printf (“ soma : %d”, soma);


}

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

5.2 TIPOS BÁSICOS

Tipos Características Bytes Escala


bool Booleano 1 0 a 1
char Caracteres 1 -128 a 127
unsigned char caracteres s/sinal 1 0 a 255
short int Inteiros 2 -32768 a 32767
unsigned short inteiros s/sinal 2 0 a 65535
int inteiros c/sinal 4 -2.147.483.648 a 2.147.483.647
unsigned int inteiros s/sinal 4 0 a 4.294.967.295
long inteiros c/sinal 4 -2.147.483.648 a 2.147.483.647
unsigned long inteiros s/sinal 4 0 a 4.294.967.295
float precisão simples 7 dígitos 4 -2.147.483.648 a 2.147.483.647
double precisão dupla 15 dígitos 8 1.7e-308 a 1.7e+308

• Obs: o tipo int tem sempre o tamanho da palavra da máquina.

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();
}

5.2.1.1 MODIFICADORES DE TIPOS

• 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..

Exemplo de declaração, atribuição e impressão de variáveis:

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;

printf ("bool -> %d ", a);


printf ("\n\nchar -> %c - %d", b,b);
printf ("\n\nunsigned char -> %c - %d ", c,c);
printf ("\n\nshort -> %d ", x);
printf ("\n\nunsigned short -> %d ", x1);
printf ("\n\nint -> %d ", z);
printf ("\n\nunsigned int -> %u ", z1);
printf ("\n\nlong -> %d ", z2);
printf ("\n\nunsigned long -> %u ", z3);
printf ("\n\nfloat -> %f ", r);
printf ("\n\ndouble -> %e ", r1);
getch();
}

_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática

5.3 INICIALIZAÇÃO DE VARIÁVEIS

• É a combinação de uma declaração de variáveis com o operador de atribuição e um valor.


• Tem a vantagem de:
• Tornar mais claro o valor inicial da variável
• Facilitar a troca de valores (sem ter que procurar a linha de inicialização)

Exemplo:

main( )
{
int evento = 5;
char corrida = ‘c’;
float tempo = 27.25;

printf (“ o melhor tempo da eliminatória % c”, corrida);


printf (“ \ n do evento %d foi % f”, evento, tempo);
}

5.4 NOMES DE VARIÁVEIS

• Quantos caracteres quiser (32).


• Comece com letras ou sublinhado: seguidos de letras, números ou sublinhados

obs:

‘C’ é sensível ao caso (maiúsculos e minúsculos):

peso <> Peso <> pEso

• Não pode-se definir um identificador com o mesmo nome que uma palavra chave.

Palavras Chave:

Auto static extern int long if


if do default while do e outras...

6 FUNÇÃO SCANF( )

Sintaxe:
scanf(“expressão de controle”, lista de argumentos)

• Função de E / S - complemento de printf( ).


• A expressão de controle utiliza códigos de formato que iniciam com: % ou %*
• A lista de argumentos é composta por variáveis precedidas por &: &variável
• Quando usamos & precedendo uma variável, estamos falando do endereço da mesma na memória.
• O que é passado para a função SCANF não é o conteúdo da variável, mas sim o endereço da variável, para
que a função coloque neste endereço o valor digitado. Isto será visto com maior detalhes ao estudarmos
como o C efetua o retorno de valores de funções.

_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática

6.1 CÓDIGOS DE FORMATAÇÃO SCANF( )

%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;

scanf(“ %d”, &num);


scanf (“ %c”, &letra);

printf(“numero digitado: %d’, num“);


printf(“\nletra digitada: %c’, letra“);
}

2)
main( )
{
char a ;

printf ( “digite um caracter” );


scanf ( “ % c”, &a );
printf (“ \n %c = %d em decimal”, a, a);
printf (“%o em octal, %x em hexadecimal”, a, a);
}

Para este último exemplo, se você digitar a tecla “m”, o programa ecoará:

m = 109 em decimal, 155 em octal, 6d em hexadecimal

6.1.1 CÓDIGO DE FORMATAÇÃO COM ASTERISCO (*)

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;

printf ( “digite um valor ponto flutuante e um inteiro”);


scanf(“ %*f %d”, valor);
printf ( “\n Valor inteiro digitado %d”, valor);
}

7 FUNÇÕES GETCHE( ) E GETCH( )

• 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>.

getche( ) lê um caracter do teclado ecoando-o na tela.

getch( ) lê um caracter do teclado sem ecoá-lo na tela.

Exemplos:

1) USANDO getche() ...

main( )
{
char ch;

printf ( “digite um caracter”);


ch = getche( );
printf ( “\n todos sabem que você digitou %c”, ch);
}

Executando:

digite um caracter: a
todos ja sabiam que você digitou a

2) USANDO getch() ...

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

8.1 OPERADORES ARITMÉTICOS SIMPLES

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;

printf("a= %d, ", a);


printf("\nb= %d, ", b);
getch();
}

• Obs: Em Pascal isto não seria permitido:

a := b := 4 * a; <- inválido

Devendo esta instrução ser implementada em duas etapas:

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;

printf ( “digite a primeira nota: ”);


scanf ( “ %f ”, &nota1);
printf ( “digite a segunda nota: ”);
scanf ( “ %f ”, &nota2);
media = (nota1+nota2)/2;
printf ( “sua media final é %f”, media);
}

2)
main( )
{
float nota1, nota2;

printf ( “digite as notas: ”);


scanf ( “ %f %f”, &nota1, &nota2);
printf ( “sua media final é %f”, (nota1+nota2)/2);
}

3)
main( )
{
int nota, conceito;

printf ( “entre com a nota e o conceito”);


scanf ( “ %d %d”, &nota, &conceito);
printf ( “sua nota final é %d”, 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;

printf(“entre com 2 números”);


scanf(“ %d %d , &dividendo, &divisor);
resto = dividendo % divisor;
printf(“\nresto da divisão inteira de %d”, dividendo);
printf(“por %d = %d”, divisor, resto);
}

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

8.2 OPERADORES INCREMENTO (++) E DECREMENTO (--)


• Incrementam / decrementam uma unidade de seu operando.
• Modos distintos  pré-fixado / pós-fixado.

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;

printf (“ %d”, num);


printf (“ %d”, ++num);
printf (“ %d”, num);
}

Saída:
0 1 1

Desafio: E se no lugar de num++ e ++num tivéssemos num-- e --num, qual seria a saída?

Exemplo 6: ___ ___ ___

Exemplo 7: ___ ___ ___

8)
main( )
{
int z=199, w=100;

++z;
w--;
printf(“z = %d”, z);
printf(“\nw = %d”, w);
}

9)
main( )
{
int valor1=0, valor2=0;

printf(“valor inicial = %d ”, valor1);


printf(“\nvalor com incremento pre-fixo = %d ”, ++valor1);
printf(“\nvalor com incremento pos-fixo = %d ”, valor2++);
}

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

8.3 PRECEDÊNCIA ENTRE OS OPERADORES ARITMÉTICOS


A precedência entre os operadores aritméticos simples é a seguinte:
Primeiramente: - (unário)
Depois: * / % + -
Finalmente: = (atribuição  binário)

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++;

8.4 USO DE OPERADORES ++ E – COM PRINTF( )


O uso de operadores de incremento e decremento, ++ e -- em argumentos da função printf pode gerar
resultados inesperados e indesejados, tendo em vista que
1) Alteram o valor da variável
2) São avaliados da direita para a esquerda ou da esquerda para a direita dentro da lista de argumentos,
dependendo de uma versão do compilador C para outra:

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);
}

Saída para avaliação à direita:


n = 24

2)
i = 3;
printf(“%d %d %d”, i = i + 1, i = i + 1, i = i + 1);

Saída para avaliação à direita:


6 5 4

Exercício proposto: determine quais seriam as saídas para avaliação à esquerda

8.5 OPERADORES ARITMÉTICOS DE ATRIBUIÇÃO

• Operadores:
+=, -=, *=, /=, %=

• Funcionalidade: atribui um novo valor à variável dependendo do operador e da expressão a direita.

• Justificativa: Obs: o uso destes operadores produz código de máquina mais eficiente

• Sintaxe:

x op = exp é equivalente a x = (x) op (exp)

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

8.6 OPERADORES RELACIONAIS


• Operadores relacionais são aqueles utilizados para comparações:

> 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):

O valor falso é representado pelo valor zero (0);

falso = (15 == 20);  será atribuído o valor zero(0)para “falso”

Comparações verdadeiras retornam o resultado um (1) (muito embora qualquer valor diferente de zero
seja interpretado pelo C como verdadeiro).

verdadeiro = (15 < 20);  será atribuído o valor um(1) p/ “verdadeiro”

if (4==4), if(1), if(33) terão o mesmo resultado.

Exemplo:

main( )
{
int verdadeiro, falso;

verdadeiro = (15 < 20);


falso = (15 == 20);
printf (“Verdadeiro = %d, falso = %d”, verdadeiro, falso);
}

Saída:
Verdadeiro = 1 falso = 0

• A precedência dos operadores, incluindo agora os relacionais, fica:

-(unário)
++ --
* / % + -(binário)
< > <= >= == !=
= += -= *= /= %=

8.7 OPERADORES LÓGICOS


• Muitas vezes é necessário colocar uma condição composta para teste de um if. Isto é conseguido com os
operadores lógicos, && (e), II (ou) e ! (negação/não).

Estude bem os exemplos abaixo de uso de condições com operadores lógicos:

_________________________________________________________________________________
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”)
}

substitui, com vantagem de clareza e economia de código, a estrutura if do programa abaixo:

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

• Informação acrescentada ao código para facilitar sua compreensão.


• É ignorado pelo compilador (não faz parte do código objeto).
• Começa com /* terminando com */.
• Ou pode ser // no inicio da linha.

Exemplo:

/* isto é um exemplo de comentário */


main( )
{
// isto é outro comentário
printf (“ apenas um exemplo”);
}

É inválido utilizar /* ou */ dentro de um comentário:


/* isto não é um /* comentário */

Entretanto, é válida a seguinte instrução:


/ * comentário em
mais de uma linha */

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:

Elaborar em algoritmo e em C os programas-solução para os problemas:

1) Informar o dobro de um numero;

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´.

11.1 FORMA GERAL EM C:


exemplo:
if (<condicao>) if (a<b)
{ {
<comando_composto_1>; a=b;
} c=2;
else }
{ else
<comando_composto_2>; {
} b=a;
c=4;
}

Comportamento: O comando if só executa o <comando_composto_1> caso a condição de teste seja


verdadeira, nada fazendo se a expressão for falsa.
Caso a a expressão de teste for falsa será executado o <comando_composto_2>.

Em algoritmo/pseudo-linguagem/ português estruturado, a forma geral seria:

se <condição>
então
<comando_composto_1>;
senão
<comando_composto_2>;
fim_do_se

11.2 ESTRUTURA SIMPLIFICADA 1


• Estrutura de decisão sem a opção do else/ “senão”:

Exemplo:
if (<condicao>) if (a<b)
{ {
<comando_composto_1>; a=b;
} c=2;
}

11.3 ESTRUTURA SIMPLIFICADA 2


• Se os comandos não são compostos, ou seja, são instruções isoladas, as implementações em C ficam:

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.

Exemplos da estrutura simplificada 2:

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”);
}

11.4 ESTRUTURA SIMPLIFICADA 3


• A mais simples: uma só instrução, sem a opção else:

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

11.5 ESTRUTURAS DE DECISÃO ANINHADAS


• Utilizadas quando se quer efetuar um segundo ou terceiro teste dentro da estrutura.

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”)
}

11.5.1 EXECUÇÃO DA OPÇÃO ELSE EM ESTRUTURAS IF ANINHADAS

• 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;

• RESPOSTA: O else, em estruturas if-else-if é sempre associado ao if mais interno (mais


próximo). No caso, o else estará associado ao segundo if. Isto quer dizer que para a execução das
instruções sob o else somente serão executadas se as condições de todos os ifs forem falsas.

_________________________________________________________________________________
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;

Este outro exemplo está errado:

if (n < 100)
if ( n < 10)
printf (“n eh menor que 10”);
else
printf (“n eh menor que 100”); // ativada para 10<=n<100

A versão corrigida seria:

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”);

Mais um exemplo correto de utilização da estrutura if - else if – else:

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);
if (c==1)
printf("\n\n soma: %d", a+b);
else if (c==2)
printf("\n\n subtracao: %d", a-b);
else if (c==3)
printf("\n\n multiplicacao: %d", a*b);
else if (c==4)
printf("\n\n divisao: %d", a/b);
else
printf("\n\n Voce não digitou nenhum valor valido");
getch();
}

_________________________________________________________________________________
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.

Implementação menos refinada, dentro da arquitetura TOP-DOWN:

Algoritmo
Defina variáveis
Leia os números
Determine o menor número
Escreva o menor número
Fim do algoritmo

Implementação mais refinada:

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

Implementar este algoritmo em C:


Dica: Se a<b e a<c  if((a<b)&&(a<c))

11.6 OPERADOR CONDICIONAL TERNÁRIO “?”:


• Forma compacta de expressar uma instrução if - else. Sua forma geral é:

(condição) ? expressão 1 : expressão 2

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;

Esta instrução equivale a:

if (num1 > num2)


Max = num1;
else
Max = num2;

Exercício proposto: construa a estrutura if equivalente a:

ABS = (num < 0) ? - num : num;

_________________________________________________________________________________
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”);
}

A resolução deste problema (e de outros) é a utilização de estruturas de repetição:

• Em algoritmo: Enquanto..faça, repita..até e para..faça.


• Equivalentes em C: estruturas: while, do-while e for.
• Cada execução do conjunto (bloco) de instruções subordinadas a estas estruturas chama-se de iteração
ou loop.

12.1 ESTRUTURA WHILE (ENQUANTO..FAÇA)


• Comportamento: A estrutura while examina uma expressão de teste. Se e enquanto esta condição for
verdadeira (!=0), o grupo de instruções da estrutura será executado repetidamente, até que a condição
seja avaliada como falsa (=0).

Exemplo: impressão dos mil primeiros números inteiros:


main( )
{
int num=1;

while(num<=1000)
{
printf (“ % d”, num);
num++;
}
}

Saída: 1 2 3 ... 1000

_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática

• A estrutura while possui quatro elementos fundamentais, perfeitamente identificáveis no exemplo


dado:
o a inicialização da variável de controle,
o o teste para execução das instruções,
o as próprias instruções e
o a modificação da variável de controle.

• 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

12.2 ESTRUTURA FOR (PARA..FAÇA)


• A estrutura de repetição for substitui, na maioria dos casos, e com vantagens, a estrutura while.

• 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;

for (número=2; número<10; número += 2)


printf (“ %d”, número);
}

• Os exemplos 2 , 3 e 4, a seguir, utilizam mais de uma variável na estrutura de controle, ou


seja, a inicialização e o incremento com mais de uma variável, separadas por vírgulas.
Atenção: A estrutura for admite somente uma condição de teste.

_________________________________________________________________________________
Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias
Algoritmos e Programação Departamento de Informática

2) Programa que imprime simultaneamente os números de 1 a 100 e de 101 a 200:

main( )
{
int x, y;

for (x=1, y=101; x<=100; x++, y++)


printf (“%d \t %d”, x, y);
}

3) Programa que imprime as letras do alfabeto e a sua ordem

main( )
{
char ch;
int i;

for (i=1, ch=‘a’; ch <= ‘z’; ch++, i++)


printf (“%d.a letra = %c \n”, i, ch);
}

4) Programa que imprime as letras de A a F, em maiúsculas e minúsculas:

main( )
{
char maiusc, minusc;

printf(“Maiúsculas \t Minusculas”);
for (maiusc=’Á’, minusc=’a’; maiusc<’F’; maiusc++, minusc++)
printf(“%c \t %c \n”, maiusc, minusc);
}

5) Exemplo de programa com uso de funções nas expressões do laço:

main( )
{
char ch;

for (ch=getch( ); ch!=‘x’; ch=getch( ))


printf ( “%c”, ch + 1);
}

6) O programa do exemplo anterior, reescrito omitindo expressões. Permanece apenas o “;”, marcando
a posição do elemento omitido:

main( )
{
char ch;

for ( ;(ch=getch( )) != ‘x’; )


printf (“ %c”, ch+1);
}

_________________________________________________________________________________
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...”);
}

8) Programa na forma geral, com múltiplas instruções no for:

main( )
{
int n, codigo, num=60;
float nota1, nota2;

for (n=1, n<=num; n++)


{
printf(“Codigo do aluno:”); scanf(“%d”, codigo)
printf(“Nota 1:”); scanf(“%f”, nota1);
printf(“Nota 2:”); scanf(“%f”, nota2);
printf(“Media: %f”, (nota1+nota2)/2.0)
}

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;

for (x=0, y=4, z=1000; z; z/=10)


printf ("x= %d \t y= %d \t z= %f \n", 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

Desafio: Descubra o erro do programa abaixo:

main( )
{
int i;

for ( i = 0; i < 100; i++ ) ;


printf ( i );
}

Saída desejada: 1 2 ... 99 100 Saída obtida: 100

12.2.1 LAÇOS ANINHADOS

• 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;

for (linha=1; linha<=2; linha++)


{
for (coluna=1; coluna<=3; coluna++)
printf (“linha %d coluna %d \t”, linha, coluna);
printf(“\n”);
}
}

Saída: linha 1 coluna 1 linha 1 coluna 2 linha 1 coluna 3


linha 2 coluna 1 linha 2 coluna 2 linha 2 coluna 3

12.2.2 while VERSUS for

estrutura for  Sabe-se a princípio o número de interações;


estrutura while  Não se sabe a princípio o número de interações.

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;

printf (“digite uma frase: \n”);


while (getche( ) != ‘\r’)
cont++;
printf (“\n Número de caracteres: %d”, cont);
}

_________________________________________________________________________________
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);

1) Exemplo de aplicação imediata com bloco de instruções:

main( )
{
int n=1;
do
{
printf (“n= %d”, n);
n++
}
while (n<=100);
}

2) Simplificação do programa anterior, uma única instrução (chaves opcionais):

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;

for (letra=’A’; letra <= ‘Z’; letra++)


switch(letra)
{
case ‘A’: printf("%c”, letra);
case ‘E’: printf("%c”, letra);
case ‘I’: printf("%c”, letra);
case ‘O’: printf("%c”, letra);
case ‘U’: printf("%c”, letra);
}
}

a saída será: AAAAAEEEIIIOOU

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;

for (letra=’A’; letra <= ‘Z’; letra++)


switch(letra)
{
case ‘A’: printf("%c”, letra);
break;
case ‘E’: printf("%c”, letra);
break;
case ‘I’: printf("%c”, letra);
break;
case ‘O’: printf("%c”, letra);
break;
case ‘U’: printf("%c”, letra);
break;
}
}

agora, sim, a saída será: AEIOU

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;

for (letra=’A’; letra <= ‘Z’; letra++)


switch(letra)
{
case ‘A’:
case ‘E’:
case ‘I’:
case ‘O’:
case ‘U’: printf("%c”, letra);
}
}

cuja saída será: AEIOU

• 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;

for (letra=’A’; letra <= ‘Z’; letra++)


switch(letra)
{
case ‘A’:
case ‘E’:
case ‘I’:
case ‘O’:
case ‘U’: vogais++;
break;
default : consoantes++;
}
printf("Vogais: %d Consoantes: %d”, vogais, consoantes);
}

_________________________________________________________________________________
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

• Pode ser usado em qualquer estrutura de laço em C.


• Causa a saída imediata do laço.
• Quando presente em laços aninhados afetará somente o laço que o contém (e os internos, obviamente).

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

• Força a próxima interação do laço (ignorando o código que estiver abaixo).


• No caso de while, do-while, o comando continue faz com que o controle vá direto 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 comando continue, pois dificulta a manutenção de um programa.

16 EXERCÍCIOS PROPOSTOS – ESTRUTURAS DE SELEÇÃO


E REPETIÇÃO
1. Dados três valores distintos, escrevê-los em ordem crescente.

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.

4. Fazer o mesmo exercício anterior utilizando estrutura For.

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.

12. Fazer um programa que calcule e escreva o valor de S:


1 3 5 7 9 99
S = --- + --- + --- + --- + --- + ... + ----
1 2 3 4 5 50

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

16 VARIÁVEIS COMPOSTAS HOMOGÊNEAS – VETORES E


MATRIZES

16.1 TIPOS DE VARIÁVEIS


a) Simples

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.2 DEFINIÇÃO DE VARIÁVEIS HOMOGÊNEAS:


• Estruturas de dados utilizadas para representar uma coleção de variáveis de um mesmo tipo.
• Maneira de armazenar na memória uma série de informações do mesmo tipo, referenciando-as por um único
nome. Cada informação isolada é diferenciada e acessada por um índice;

16.3 VETORES - DECLARAÇÃO DE TIPO E MANIPULAÇÃO

16.3.1 Declaração
Sintaxe:
tipo nome[<tamanho máximo>]

Exemplos:
float nota[60];
int vetor[100];

16.3.2 Acesso e manipulação:

Atribuição de um valor a um elemento (o quarto) do vetor:


nota[3] = 9.5;

Impressão/leitura de um elemento de um vetor:


printf(“%d”, exemplo[3]); // impressão do quarto elemento do vetor
scanf(“%d”, &vetor[5]); // leitura do sexto elemento do vetor
printf(“%d”, exemplo[0]); // impressão do primeiro elemento do vetor

• 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;

printf(“entre com a 1a. nota”);


scanf(“%f”, &nota0);
printf(“entre com a 2a. nota”);
scanf(“%f”, &nota1);
printf(“entre com a 3a. nota”);
scanf(“%f”, &nota2);

printf(“media = %f”, (nota0 + nota1 + nota2) / 3));


}

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;

printf(“entre com a 1a. nota”);


scanf(“%f”, &nota0);
printf(“entre com a 2a. nota”);
scanf(“%f”, &nota1);
. . .
. . .
. . .
printf(“entre com a 300a. nota”);
scanf(“%f”, &nota299);

printf(“media = %f”, (nota0 + nota1 + ... + nota299) / 300));


}

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.

A implementação utilizando estrutura while seria:

main()
{
int i;
float notas[300], media=0.0;

i = 0;
while (i<300)
{
printf (“entre com a nota %d”, i+1);
scanf (“%f”, &notas[i]);
media = media + notas[i];
i++;
}

printf (“Media= %f \n”, media/300);


i = 0;
while (i<300)
{
printf (“\n Nota do aluno %d= ”, i+1);
printf (“%f \n”, 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;

for (i=0; i<300; i++)


{
printf (“entre com a nota %d”, i+1);
scanf (“%f”, &notas[i]);
media += notas[i];
}

printf (“Media= %f \n”, media/300);


for (i=0; i<300; i++)
{
printf (“\n Nota do aluno %d= ”, i+1);
printf (“%f \n”, notas[i]);
}
}

Ficou bem mais simples, não?


É só imaginar que cada laço de repetição while ou for será executado 300 vezes (neste exemplo), cada
um com um índice maior (incrementalmente crescente), até que todos os elementos do vetor sejam considerados.
Seja na leitura, no cálculo da média ou na impressão.

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;

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &tamanho);

printf("\nDigite os elementos do vetor: \n");

for (cont=0; cont<tamanho; cont++)


scanf("%d",&vetor[cont]);

menor = vetor[0];
for (cont=1; cont<tamanho; cont++)
if(vetor[cont]<menor)
menor=vetor[cont];

printf("\nMenor elemento= %d", menor);

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;

printf("\n Num. alunos (max=100): ");


scanf("%d", &n);

printf("\nDigite os codigos e notas dos alunos: \n");


for (i=0; i<n; i++)
{
printf("codigo: "); scanf("%d",&cod[i]);
printf("nota 1: "); scanf("%f", &nota1[i]);
printf("nota 2: "); scanf("%f", &nota2[i]);
printf("nota 3: "); scanf("%f", &nota3[i]);
media[i] = (nota1[i] + nota2[i] + nota3[i])/3.0;
}

printf("\nImpressao dos codigos, media e condicao: \n");

printf("Codigo Nota 1 Nota 2 Nota 3 Media Criterio\n");

for (i=0; i<n; i++)


{
printf("\n%d %f %f %f %f", cod[i], nota1[i], nota2[i], nota3[i], media[i]);
if (media[i]>=5)
printf("Aprovado");
else
printf("Reprovado");
}

getch();
}

16.4 INICIALIZANDO VETORES


Vimos anteriormente que as variáveis simples poderiam ser inicializadas, isto é, poderíamos atribuir
valor a elas quando da sua declaração. Considere, por exemplo, como a variável inteira número
poderia ser inicializada com o valor 0 (zero) quando da sua declaração:

int numero = 0;

Do mesmo modo podemos inicializar um vetor, como no exemplo:

int notas[5] = {0,0,0,0,0};

Isto equivaleria a:

int notas[5];

for (i=0; i<5; i++)


notas[I] = 0;

_________________________________________________________________________________
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:

int notas[ ] = {0,0,1,3};

que tem o mesmo efeito de:


int notas[4] = {0,0,1,3}

• 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:

int notas [5] = {1,2}

que equivale a:
int notas [5] = {1,2,0,0,0}

Por outro lado, são inválidas as inicializações:


int notas [5] = { , ,0, , } // ERRO
int notas [ ] = { , ,0, , } // ERRO

16.5 VARIÁVEIS HOMOGÊNEAS - VERIFICANDO LIMITES


A linguagem C não realiza verificação de limites em vetores e matrizes. Isto é, nada impede que uma instrução C
efetue o acesso além do limite do vetor, o que gerará resultados imprevisíveis. Por este motivo, é sempre muito
importante garantir os corretos valores dos índices e, de preferência, efetuar a verificação dos limites, como no
exemplo:

#define TAM 100

int alunos[TAM], quantidade;


// equivalente a: int alunos[100], quantidade;

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 VETORES – ORDENAÇÃO

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.

16.6.3 CLASSIFICAÇÃO DOS MÉTODOS DE ORDENAÇÃO


A) Quanto ao local da ordenação
Memória
Arquivo – grandes arquivos / processamento em batch (Merge Sorts)

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 MÉTODOS DIRETOS

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.

for (i=0; i<=n-2; i++)


{
k = i;
menor = vetor[i];
for (j=i+1; j<=n-1; j++)
if(vetor[j]<menor)
{
k = j;
menor = vetor[k];
{
vetor[k] = vetor[i];
vetor[i] = menor;
}

_________________________________________________________________________________
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.

for (i=1; i<=n-1; i++)


{
aux = vetor[i];
j = i;
while ((j>0)&&(aux<vetor[j-1]))
{
vetor[j]= vetor[j-1];
j=j-1;
}
vetor[j] = aux;
}

• 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.

for (i=0; i<=n-1; i++)


for (j=n-2; j>=i; j--)
if(vetor[j]>vetor[j+1])
{
aux = vetor[j];
vetor[j] = vetor[j+1];
vetor[j+1] = aux;
}

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;

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &n);

printf("\nDigite os elementos do vetor: \n");


for (i=0; i<=n-1; i++)
scanf("%d",&vetor[i]);

for (i=0; i<=n-1; i++)


for (j=n-2; j>=i; j--)
if (vetor[j] > vetor[j+1])
{
aux = vetor[j];
vetor[j] = vetor[j+1];
vetor[j+1] = aux;
}

printf("\nImpressao vetor ordenado: ");


for (i=0; i<n; i++)
printf("\n%d", vetor[i]);

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

16.7 ARRANJOS MULTIDIMENSIONAIS – MATRIZES

16.7.1 MATRIZES – DECLARAÇÃO/MANIPULAÇÃO


Sintaxe:
tipo nome[tamanho1][tamanho2]...[tamanhon], onde:

tamanho1, tamanho2, ..., tamanhon  número máximo de elementos da dimensão.

Exemplos:
float distancia[100][80][120];

int tabela[10][10];

• A manipulação de uma matriz bidimensional é efetuada utilizando-se duas estruturas for


aninhadas, uma para gerar os números das linhas e a outra para gerar os índices das colunas. Do
mesmo modo, uma matriz com n dimensões precisará de n estruturas for para ser manipulada,
ou seja, para que cada elemento possa ser referenciado individualmente.

• 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:

int i,j, matéria[4][40];

for (i=0; i<4; i++)


{
printf (“\nEntre com as notas da matéria %d”, i+1);

for (j=0; j<40; j++)


{
printf (“\nEntre com a nota do aluno %d: ”, j+1);
scanf (“%d”, &materia [i][j]);
}
}

16.7.2 INICIALIZANDO MATRIZES

Uma matriz bidimensional pode ter seus valores inicializados já em sua declaração, como no
exemplo:

int nota[3][4] = {{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}};

Esta instrução substitui com vantagem o seguinte grupo de instruções:

int nota[3][4];

for (i=0; i<linhas; i++)


for (j=0; j<colunas; j++ )
nota[i][j] = 0;

Que por sua vez substitui as instruções:

_________________________________________________________________________________
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;

16.7.3 MATRIZES – PROGRAMAS-EXEMPLO DE APLICAÇÃO

A) Inicializar os elementos de uma matriz com valores crescentes e imprimir os seus elementos:

main()
{
int tabela[10][10], linha, coluna, i=0;

for (linha=0; linha<10; linha++)


for (coluna=0; coluna<10; coluna++)
tabela[linha][coluna] = i++;

for (linha=0; linha<10; ++linha)


{
for (coluna=0; coluna<10; ++coluna)
printf("%4d", tabela[linha][coluna]);
printf("\n");
}
}

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;

printf("digite o numero de linhas: ");


scanf("%d",&linhas);
printf("digite o numero de colunas: ");
scanf("%d",&colunas);
for(i=0;i<linhas;i++)
for(j=0;j<colunas;j++)
{
printf("elemento [%d,%d]:",i+1,j+1);
scanf("%d",&tabela[i][j]);
}
for(i=0;i<linhas;i++)
{
for(j=0;j<colunas;j++)
printf("%0.2d ",tabela[i][j]);
printf("\n");
}
}

_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 48
Algoritmos e Programação Departamento de Informática

C) Buscar e imprimir o menor elemento de uma matriz:

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;
}

printf("menor elemento e %d na posicao [%d][%d]",menor,lm+1,cm+1);

D) Ler duas matrizes e fornecer a matriz soma:

Condição necessária: as matrizes precisam ter as mesmas dimensões.

main()
{
int m1[10][10], m2[10][10], msoma[10][10], linhas, colunas, i, j;

printf("digite o numero de linhas das matrizes: ");


scanf("%d",&linhas);
printf("digite o numero de colunas das matrizes: ");
scanf("%d",&colunas);

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] ;
}

printf("impressao da matriz soma:”);

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

E) Ler duas matrizes e fornecer a matriz resultado (multiplicação):

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

printf("digite o numero de linhas da matriz A: ");


scanf("%d",&lina);
printf("colunas da matriz A (= linhas da matriz B): ");
scanf("%d",&calb);
printf("digite o numero de colunas da matriz B: ");
scanf("%d",&colb);

// 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]);
}

// Calculo da matriz resultado


for(i=0;i<lina; i++)
for(j=0;j<colb;j++)
{
mc[i][j] = 0;
for(k=0;k<calb; k++)
mc[i][j] = mc[i][j] + ma[i][k]*mb[k][j];
}

// Saida da matriz resultado


printf("impressao da matriz resultado:\n");

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

17.1 DEFINIÇAO DE STRING - IMPRESSÃO


Uma string de caracteres ou cadeia de caracteres é uma seqüência de caracteres delimitada por aspas
duplas.
Exemplo de impressão de strings:

#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.

• No momento da compilação, o computador coloca ao final da string o caracter ‘null’, o ASCII 0,


inserindo a seqüência de escape ‘\0’. Este caracter null indica, para as funções de manipulação, o fim
da string. Este caracter, frise-se, não tem nada a ver com o caracter zero, ‘0’.

17.2 VARIÁVEL STRING – DECLARAÇÃO E INICIALIZAÇÃO


As strings de caracteres podem ser armazenadas em C em variáveis strings, que nada mais são do que
vetores de caracteres, aos quais as funções de leitura e atribuição adicionam após o último caracter significativo o
caracter null, ’\0’. Por esta razão, strings de caracteres são também chamadas de strings ASCII zero.

Exemplos de declarações de variáveis string:

char nome[20], palavrão[128];

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’;

printf ("cadeia1 = %s", cadeia1);


printf ("cadeia2 = %s", cadeia2);
printf ("cadeia3 = %s", cadeia3);

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

17.3 I/O COM SCANF E PRINTF

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();

printf ("Digite uma frase e encerre com <Enter>: ");


scanf ("%s", &frase[0]);
printf ("frase = %s", frase);
getch();
}

_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 52
Algoritmos e Programação Departamento de Informática

17.4 I/O COM GETS E PUTS

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.

Veja as aplicações no exemplo:

#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);

printf ("Digite uma frase e encerre com <Enter>: ");


gets (&frase[0]); // Esta instrucao também funciona
puts("Frase digitada: ");
puts (frase);

// gets (&frase); Esta instrucao NAO funciona


// puts("Frase digitada: ", frase); Também NAO funciona
// A funcao puts só aceita um argumento
getch();
}

• 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.

17.5 MANIPULAÇÃO DE STRINGS COMO VETOR DE CARACTERES


A leitura e a impressão de strings de caracteres pode ser feita caracter a caracter, utilizando as funções
getchar() e putchar(). O primeiro exemplo abaixo lê os caracteres digitados no teclado e ao ser digitado
<Enter>, leitura do caracter '\n', interrompe a leitura e coloca o caracter nulo, '\0', no final da string.
Este caracter sinaliza o final da string para outras as funções de manipulação, como vimos.
#include <conio.h>
#include <stdio.h>
#define FIM '\0'
main()
{
char vetor[128];
int tamanho=0;

printf("\nDigite uma frase e pressione <Enter>: \n");


while ((vetor[tamanho] = getchar()) != '\n')
tamanho++;
vetor[tamanho]=FIM;

printf("A string %s contem %d caracteres. \n",vetor, tamanho);


puts("Impressao da string utilizando puts: "); puts(vetor);
getch();
}

_________________________________________________________________________________
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;

printf("Digite uma frase: "); gets(frase);


printf("Digite o caracter a ser procurado nesta frase: ");
c = getche();

while (frase[i] != '\0')


{
if (frase[i] == c)
ocorrencias++;
i++;
}

printf ( "\nO caracter %c ", c);


printf ( "aparece %d vezes na frase: %s", ocorrencias, frase);

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);

printf ("%c\n", nome[3]);


printf ("%c\n", nome[0]);
printf ("%s\n", nome);
getch();
}

_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 54
Algoritmos e Programação Departamento de Informática

17.6 FUNÇÕES DE MANIPULAÇÃO DE STRINGS

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];

printf("Digite seu nome: " );


gets (nome);
printf(“Tamanho = %d”, strlen(nome));
}

Exemplo 2:

main()
{
char nome[40]="Joao", sobrenome[20]=" da Silva";
int tamanho;

// Demonstra o uso da funcao strlen


printf ("\nTamanho do nome (%s) = %d ", nome, strlen(nome));
tamanho = strlen(&sobrenome[0]);
printf ("\nTamanho do sobrenome (%s)= %d", sobrenome, tamanho);

getch();
}

17.6.2 strcat (str1, str2)

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();
}

17.6.3 strcmp (str1, str2)

Compara dois strings retornando um valor inteiro que indica o status da comparação, que é feita por ordem
alfabética:

Negativo se str1 < str2


0 (zero) se str1 = str2  em um if, é considerado como falso (armadilha do C)
positivo se str1 > str2

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;

printf ("Digite a senha de alta complexidade: ");


gets(senhaf);

result = strcmp(senhai, senhaf);

if (result) // se result for diferente de zero, verdadeiro


puts("Senha invalida"); // result diferente de zero
else
puts("Senha valida"); // result igual a zero,
// interpretado como falso

getch();
}

17.6.4 strcpy (str1, str2)

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";

printf ("\nNome original= %s", nome);


printf ("\nSobrenome original= %s\n\n", sobrenome);

// nome = nome2; Instrução invalida


// sobrenome = "de Souza"; Também invalido
// Um vetor nao pode receber atribuição do desta maneira

strcpy(nome,nome2); // Tambem valido


printf ("\nnome apos o uso de strcpy= %s", nome);

strcpy(sobrenome,"de Souza"); // Instrucao valida


printf ("\nSobrenome apos o uso de strcpy= %s", sobrenome);

getch();
}

_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 57
Algoritmos e Programação Departamento de Informática

18 MODULARIZAÇÃO - FUNÇÕES E PROCEDIMENTOS

À 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

18.1.1 PROCEDIMENTOS SEM PARÂMETROS / QUE NÃO RECEBEM VALORES


Quando o procedimento não precisa de nenhum valor para executar a sua funcionalidade, a chamada a este
procedimento é programada colocando-se na instrução o nome da função/procedimento seguida de (). Veja os
exemplos:

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();
}

18.1.2 PASSAGEM DE PARÂMETROS PARA PROCEDIMENTOS - POR VALOR

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.

void mostra_digito(int digito)


{
printf("Valor passado para a funcao= %d\n",digito);
}

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();
}

void mostra_digito(int digito)


{
printf("%d\n",digito);
}

Exemplo 3: Este programa executa 10 chamadas à função mostra_digito, cada chamada passando um valor
diferente.

void mostra_digito(int digito)


{
printf("%d\n",digito);
}

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.

void mostra_soma(float a, float b)


{
printf("%6.2f+%6.2f=%6.2f\n",a,b,a+b);
}

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.

18.1.3 VARIÁVEIS LOCAIS E GLOBAIS

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.

Exemplo de programa utilizando somente variáveis locais. As variáveis i do main() e da função


mostra_digito são independentes e não tem relação nenhuma uma com a outra. Inclusive o incremento de i
dentro da função não terá repercussão nenhuma no conteúdo da variável i do main:

void mostra_digito(int i)
{
i++;
printf("%d\n",i);
}

main()
{
int i;

for (i=0; i<10; i++)


mostra_digito(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.

18.2.1 O COMANDO return


A execução de um comando return em uma função faz com que uma o controle da execução retorne à
função que a chamou.
Ele pode opcionalmente retornar um valor ao ponto de chamada.

Exemplos:

return a
return a+b
return (a+b)

18.2.2 A PASSAGEM DE VALORES PARA A FUNÇÃO E O RETORNO DE VALORES

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.

int soma_valores(int a,int b)


{
return(a+b);
}

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

Exemplo 2: O mesmo programa anterior, com duas alterações principais:


a) sem os parênteses após o return e
b) utilizando a chamada diretamente como parte do argumento de outra função, no caso, a printf, o
que dispensa a declaração das variáveis res1 e res2, com economia de código:

float soma_valores(float a,float b)


{
return a+b;
}

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:

float media(float a,float b)


{
return (a+b)/2;
}

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:

int fatorial (int n)


{
int i, resultado = 1;

for ( i = 1; i <= n; i ++)


resultado *= i;
return resultado;
}

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;

printf (“ digite uma letra em minúsculo”);


letra = minúsculo ( );
if (letra == ‘a’) // if (minusculo( ) == ‘a’)
printf (“ok”);
}

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.

Resolução ao final do capítulo.


Mas você não vai dar uma de mole e preguiçoso e ver a resposta antes de quebrar a cabeça um pouco,
vai?

_________________________________________________________________________________
Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 65
Algoritmos e Programação Departamento de Informática

18.3 USANDO VÁRIAS FUNÇÕES


Um aplicativo, em sua versão final, pode ter vários módulos, cada um desempenhando uma
funcionalidade do programa. O correto projeto possibilitará que se desfrute todas as vantagens do uso da
modularização. Veja o exemplo de um programa que utiliza três funções:

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;

for(i=1; i<= n; i++)


resultado *= i;
return resultado;
}

float potencia(float base, int expoente)


{
int i;
float resultado = 1;

if (expoente == 0)
return 1;
for(i=1; i<=expoente; i++)
resultado *= base;
return resultado;
}

float serie (float x, int n)


{
int i;
float resultado= 0;

for (i= 1; i<= n; i++)


resultado += potência(x,i)/fat(i);
return resultado;
}

void main( )
{
float x;
int termos;

printf(“entre com o numero de termos: “);


scanf(“%d”, &termos);

printf(“entre com o valor de X: “);


scanf(“%f”, &x);

printf(“O valor de série = %f “, serie(x, 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.

No exemplo abaixo, a função repete() é chamada uma vez pelo main().


Na execução da função, esta escreve a mensagem “Subindo...n”, onde n é o número da chamada à função.
Ao este número chegar a três, a função para de chamar a si mesma e passa a executar o restante da função,
escrevendo na tela a mensagem “Descendo...n”, onde n é o número da chamada à função que está sendo
executada.

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>

int repete (int n)


{
n++;
printf("Subindo...%d\n",n);

if (n<3)
repete (n);

printf("Descendo ... %d\n",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

Resolução das tarefas propostas sobre funções:

1) Função que recebe a altura e a base de um retângulo e devolve a área;

float área_retângulo (float largura, float altura)


{
return largura*altura;
}

2) Função que recebe uma letra e a transforme em minúscula, se ainda não o for.

char minúsculo (char ch)


{
if ((ch >= ‘A’) && (ch <= ‘Z’))
return (ch + ‘a’ - ‘A’);
else
return (ch);
}
main( )
{
printf (“ %c”, minúsculo (‘A’) );
}

3) Desenvolva uma função que retorne o absoluto de um número passado como argumento.

int abs (int x)


{
return ( ( x < 0 ) ? -x : x );
}

main( )
{
int num, b;

b = abs (-3);
printf (“Valor absoluto de -3 = %d”, b);

printf (“entre com um número: “);


scanf (“%d”, &num );
printf (“Valor absoluto de num = %d”, abs(num));

4) Função que receba base e expoente, inteiros, e calcule a sua potenciação, ou seja, a base elevada ao
expoente.

float potência (float base, int expoente)


{
int i; float resultado = 1;

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

Variáveis ponteiro são as que podem armazenar endereços de memória.

19.1 OPERADOR DE ENDEREÇO - &


Como registramos oportunamente, variáveis são localizações da memória que possuem:
a) Um nome pelo qual são referenciadas;
b) Um conteúdo.
c) Um endereço determinado na memória. Para se obter este endereço onde se localiza uma variável,
utiliza-se o operador de endereço &.

Um endereço é o ponto inicial da memória do espaço ocupado pelo valor.

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.

printf ("Numero: ");


scanf ("%d", &numero);
printf ("Numero = %d", numero);

Exemplo 1:

#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;

printf ("Valor = %d ", contador);


printf ("Endereco = %X", &contador);
getch();
}

•Utiliza-se o especificador de formato %X – formato hexadecimal - para imprimir um endereço de


memória.
•Um endereço de memória é expresso em dois termos: um segmento e um deslocamento dentro do
segmento, que é exibido pelo especificador de formato %X.

Exemplo 2:

#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
float numero = 3.3;

printf ("Valor inteiro = %d Endereco inteiro = %04X \n", contador, &contador);


printf ("Valor float = %f Endereco float = %04X", numero, &numero);
getch();
}

_________________________________________________________________________________
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.

19.2 DECLARAÇÕES DE VARIÁVEIS PONTEIRO


Como eu posso guardar/armazenar o valor de um endereço para uso posterior?
 Utilizando um tipo especial de variável, os ponteiros.

Para declarar uma variável ponteiro para um tipo especificado, coloque:
a) O tipo da variável cujo endereço poderá ser armazenado na variável ponteiro;
b) O nome da variável ponteiro a ser criada, precedido de asterisco(*).

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:

int *ptr;  Declaração de ptr

Esta declaração deve ser lida da direita para a esquerda:

ptr é uma variável ponteiro, capaz de armazenar o endereço de um int.

Exemplo completo:

#include <stdio.h>
#include <conio.h>
main()
{
int contador = 1;
int *ptr;

ptr = &contador;

printf ("Valor inteiro = %d ", contador);


printf ("Endereco inteiro = %X \n", &contador);
printf ("Valor de ptr = %X", ptr);
getch();
}

19.3 OPERADOR DE INDIREÇÃO *


Para acessar ou alterar o conteúdo da variável cujo endereço está armazenado em uma variável ponteiro,
utiliza-se o operador de indireção, que é implementado pela colocação de um asterisco antes da variável
ponteiro. Por exemplo:

Contador = 1;
ptr = &contador; /* o endereço de contador é atribuído a ptr */
/* ptr aponta para contador */

numero = *ptr; /* numero recebe 1, o valor de contador */


// acesso indireto a contador

Printf (“%d”, *ptr); /* imprime o conteúdo de contador */


*ptr = 8; /* contador recebe 8 */

_________________________________________________________________________________
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;

pa = &a; /* ou: pa aponta para a variável a ou: pa recebe o endereço de a


pb = &b; /* ou: pa aponta para a variável a ou: pb recebe o endereço de b

*pa = 10; /* ou: a recebe 10 */


*pb = 3; /* ou: b recebe 3 */

printf ("novo valor de a = %d \n", *pa);


printf ("novo valor de b = %d \n", *pb);

getch();
}

Exemplo completo 2:

#include <stdio.h>
#include <conio.h>

main()
{
int contador = 1;
int *ptr;

ptr = &contador;

printf ("Valor contador= %d Endereco contador = %04X \n", contador, &contador);


printf ("Valor de ptr = %04X \n", ptr);
// = a contador
printf ("Conteudo da variavel int cujo endereço esta em ptr = %d \n", *ptr);
printf ("Endereco de ptr = %04X \n", &ptr);

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

19.4 PASSAGEM DE ARGUMENTOS POR REFERÊNCIA ATRAVÉS


DE PONTEIROS
Quando um programa passa argumentos por valor em uma chamada, a função que recebe este valor pode utiliza-
lo, mas o conteúdo da variável colocado na chamada não é alterado, qualquer que seja o tratamento dado ao
valor passado à função.

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;

printf ("Valor inicial = %d \n", contador);


muda_valor(contador);
printf ("Valor final = %d \n", contador);

getch();
}

A saída deste programa será:

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:

void muda_valor(int *a) /* recebe endereco */


{
*a+=4; /* soma 4 a *a, mesmo endereco de memória que contador */
printf("Valor dentro da funcao = %d \n", *a);
}

main()
{
int contador = 5;

printf ("Valor inicial = %d \n", contador);


muda_valor(&contador); /* passando endereco */
printf ("Valor final = %d \n", contador);

getch();
}

A saída deste novo programa será agora:

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

*a+=4;  o conteúdo da variável contador é alterado indiretamente, porque o seu


endereço está armazenada na variável a

muda_valor(&contador);  a chamada à função muda_valor passa o endereço da variável


contador

Se imprimirmos também o endereço das variáveis utilizadas pelo programa fica mais fácil entender o que
acontece:

void muda_valor(int *a)


{
*a+=4;
printf("Valor dentro da funcao = %d \n", *a);
printf("Endereco guardado em a = %X \n", a);

main()
{
int contador = 5;

printf ("Valor inicial = %d \n", contador);


printf ("Endereco de contador = %X \n", &contador);

muda_valor(&contador);
printf ("Valor final = %d \n", contador);

getch();
}

A saída será agora:

Valor inicial = 5
Endereco de contador = 22FF74 /* este valor pode mudar, */
/* dependendo do uso da memória */

Valor dentro da funcao =9


Endereco guardado em a = 22FF74 /* mas será sempre igual */
/* a este valor */
Valor final =9

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:

void dobra_valor(int *v) /* recebe endereco */


{
*v *=2;
/* dobra o valor de *v e, indiretamente, de contador */
printf("Valor dentro da funcao = %d \n", *v);
}

main()
{
int contador = 5;

printf ("Valor inicial = %d \n", contador);


dobra_valor(&contador); /* passando endereco */
printf ("Valor final = %d \n", contador);

getch();
}

Atividade proposta 2: Fazer programa que utilize uma função para trocar o conteúdo de duas variáveis.

Resolução:

void troca_valores(int *pa, int *pb)


{
int temp;

temp = *pa;
*pa = *pb;
*pb = temp;
}

main()
{
int a = 2, b = 3;

printf ("Valor inicial de a = %d e de b= %d\n", a, b);


troca_valores(&a, &b); /* passando enderecos */
printf ("Valor final de a = %d e de b= %d\n", a, b);

getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 73
Algoritmos e Programação Departamento de Informática

19.5 PASSAGEM DE VETORES E MATRIZES PARA FUNÇÕES

19.5.1 PASSAGEM DE VETORES

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 mecânica da passagem de vetores/matrizes como argumentos segue as seguintes regras:

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);

b) No cabeçalho da função é declarada a variável composta sem a especificação de tamanho. Por


exemplo:
void imprime_vetor (float vetor[],int n)

Exemplo completo 1: Programa que lê um vetor e o passa como argumento, para função que o imprime:

void imprime_vetor (float vet[],int n)


{
int i;

for (i=0; i<n; i++)


printf("%f", vet[i]);
}

main()
{
float vetor[100];
int quant, tamanho;

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &tamanho);

printf("\nDigite os elementos do vetor: \n");


quant=0;
while (quant<tamanho)
{ scanf("%f",&vetor[quant]);
quant++;
}

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>

float menor_elemento (float vet[],int n)


{
int i;
float menor;
menor=vet[0];
for (i=0; i<n; i++)
if (vet[i] < menor)
menor = vet[i];
return menor;
}

main()
{
float vetor[100];
int cont, tamanho;

float menor_elemento(float[],int);

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &tamanho);

printf("\nDigite os elementos do vetor: \n");


cont=0;
while (cont<tamanho)
{ scanf("%f",&vetor[cont]);
cont++;
}

printf ("Menor elemento do vetor: \n");


printf("%f \n", menor_elemento(vetor,cont));
getch();
}

Tarefa proposta 1: programa que passe um vetor para uma função e obtenha de retorno a soma de seus
elementos.

Resolução:

float soma_elementos (float vet[],int n)


{
int i;
float soma;

menor=0;

for (i=0; i<n; i++)


soma += vet[i];

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>

void dobra_vetor (int vet[],int n)


{
int i;

for (i=0; i<n; i++)


vet[i] = vet[i]*2;

main()
{
int vetor[100];
int cont, tamanho;

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &tamanho);

printf("\nDigite os elementos do vetor: \n");


for (cont=0; cont<tamanho; cont++)
scanf("%d",&vetor[cont]);

printf ("Chamando a funcao de duplicacao... \n");


dobra_vetor(vetor,tamanho);

printf("\nSaida do vetor duplicado: \n");


for (cont=0; cont<tamanho; cont++)
printf("%d \n",vetor[cont]);

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>

void ordena_vetor (int vet[],int n)


{
int i, k, j, menor;

for (i=0; i<=n-2; i++)


{
k=i;
menor=vet[i];
for(j=i+1; j<=n-1; j++)
if (vet[j] < menor)
{
k=j;
menor = vet[k];
}
vet[k] = vet[i];
vet[i] = menor;
}
}

main()
{
int vetor[100];
int cont, tamanho;

printf("\nDigite o numero de elementos do vetor (max=100): ");


scanf("%d", &tamanho);

printf("\nDigite os elementos do vetor: \n");


cont=0;
while (cont<tamanho)
{ scanf("%d",&vetor[cont]);
cont++;
}

printf ("Chamando a funcao de ordenacao... \n");


ordena_vetor(vetor,tamanho);

printf("\nSaida do vetor ordenado: \n");


cont=0;
while (cont<tamanho)
{ printf("%d",vetor[cont]);
cont++;
}

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'

void ordena_vetor (char vetor[],int n)


{
int i, k, j;
char menor;

for (i=0; i<=n-2; i++)


{
k=i;
menor=vet[i];
for(j=i+1; j<=n-1; j++)
if (vet[j] < menor)
{
k=j;
menor = vet[k];
}
vet[k] = vet[i];
vet[i] = menor;
}
}

main()
{
char vetor[100];
int cont, tamanho=0;

printf("\nDigite os caracteres a ordenar e <Enter>: \n");


while ((vetor[tamanho] = getchar()) != '\n')
tamanho++;
vetor[tamanho]=END;

/* Chamando a funcao de ordenacao... */


ordena_vetor(vetor,tamanho);

printf("\nSaida do vetor ordenado: \n");


cont=0;
while (cont<tamanho)
{ printf("%c",vetor[cont]);
cont++;
}

/* Repetindo saída com função puts, tratando vetor de char como


string:*/
printf("\n");
puts(vetor);

getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 78
Algoritmos e Programação Departamento de Informática

19.5.2 PASSAGEM DE MATRIZES COMO ARGUMENTO PARA FUNÇÕES

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:

menor_elemento (int mat[][100],int m, int n)

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>

menor_elemento (int mat[][100],int m, int n)


{
int i,j, menor;
menor=mat[0][0];
for (i=0; i<m; i++)
for (j=0; j<n; j++)
if (mat[i][j] < menor)
menor = mat[i][j];
return menor;
}

main()
{
int matriz[100][100];
int i, j, colunas, linhas;

printf("\nDigite o numero de linhas da matriz (max=100): ");


scanf("%d", &linhas);

printf("\nDigite o numero de colunas da matriz (max=100): ");


scanf("%d", &colunas);

printf("\nDigite os elementos da matriz: \n");


for (i=0; i<linhas; i++)
for (j=0; j<colunas; j++)
{
printf("\nDigite o elemento[%d][%d] da matriz: \n", i, j);
scanf("%d", &matriz[i][j]);
}

printf ("Menor elemento da matriz: \n");


printf("%d \n", menor_elemento(matriz,linhas, colunas));

getch();
}

Tarefa proposta: Programa que passe uma matriz para uma função e obtenha de retorno a soma de seus
elementos.

soma_elementos (int mat[][100],int m, int n)


{
int i,j, soma;
soma = 0;
for (i=0; i<m; i++)
for (j=0; j<n; j++)
soma += mat[i][j];
return soma;
}

_________________________________________________________________________________
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>

void dobra_matriz(int mat[][100],int m, int n)


{
int i,j;
for (i=0; i<m; i++)
for (j=0; j<n; j++)
mat[i][j] = mat[i][j]*2;
}

main()
{
int matriz[100][100];
int i, j, colunas, linhas;

printf("\nDigite o numero de linhas da matriz (max=100): ");


scanf("%d", &linhas);

printf("\nDigite o numero de colunas da matriz (max=100): ");


scanf("%d", &colunas);

printf("\nDigite os elementos da matriz: \n");


for (i=0; i<linhas; i++)
for (j=0; j<colunas; j++)
{
printf("\nDigite o elemento[%d][%d] da matriz: \n", i+1, j+1);
scanf("%d", &matriz[i][j]);
}

/* Chamada aa funcao que dobra os elementos da matriz */


dobra_matriz(matriz,linhas,colunas);

printf("\nImpressao da matriz dobrada: ");


for (i=0; i<linhas; i++)
{
printf("\n");
for (j=0; j<colunas; j++)
printf("%6d", matriz[i][j]);
}
getch();
}

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

19.6 MANIPULAÇÃO AVANÇADA DE STRINGS UTILIZANDO


PONTEIROS

19.6.1 IMPRESSÃO DE STRINGS UTILIZANDO PONTEIROS

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();
}

Ou, de forma mais compacta,

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>

void mostra_cadeia(char cadeia[])


{
int i;

for (i=0; cadeia[i] != '\0'; ++i)


putchar(cadeia[i]);
}

main()
{
char nome[50];

printf("Digite nome: "); scanf("%s", nome);


mostra_cadeia(nome);
getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 82
Algoritmos e Programação Departamento de Informática

Resolução programa 2:
#include <stdio.h>
#include <conio.h>

void mostra_cadeia(char cadeia[])


{
int i;

for (i=0; cadeia[i] != '\0'; ++i)


printf("%X %c\n", &cadeia[i], cadeia[i]);
}

main()
{
mostra_cadeia("Mensagem a exibir");
getch();
}

Resolução programa 3:
#include <stdio.h>
#include <conio.h>

void mostra_cadeia(char *pcadeia)


{
while (*p != '\0')
{
putchar(*p); p++;
}
}

main()
{
mostra_cadeia("Mensagem a exibir");
getch();
}

Resolução programa 4:
#include <stdio.h>
#include <conio.h>

void mostra_cadeia(char *pcadeia)


{
while (*pcadeia)
putchar (*pcadeia++);
}

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

19.6.2 CÓPIA DE STRINGS UTILIZANDO PONTEIROS

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.

Cópia de uma string de caracteres em uma variável string utilizando vetores:

#include <stdio.h>
#include <conio.h>

void copia_cadeia(char fonte[], char destino[])


{
int i = 0;

while (destino[i] = fonte[i])


++i;
}

main()
{
char destino[128];

copia_cadeia("Cadeia a ser copiada", destino);


puts(destino);

getch();
}

Cópia de uma string de caracteres em uma variável string utilizando ponteiros:

#include <stdio.h>
#include <conio.h>

void copia_cadeia(char *fonte, char *destino)


{
while (*destino++ = *fonte++)
{;}

main()
{
char destino[128];

copia_cadeia("Cadeia a ser copiada", destino);


puts(destino);

getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 84
Algoritmos e Programação Departamento de Informática

19.7 MANIPULAÇÃO AVANÇADA DE VETORES UTILIZANDO


PONTEIROS

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;

while (i++ <= tamanho)


printf("%f \n", *pvalor++);
}

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:

void mostra_valores(float *pvalor, int tamanho)


{
int i=1;

while (i++ <= tamanho) {


printf("Valor %f Endereco %X \n", *pvalor, pvalor);
pvalor = pvalor+1;
}
}

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

Forma de manter unidas variáveis de diferentes tipos.


Facilita passagem de argumentos relacionados a função. Se temos vários dados relacionados a um mesmo
objeto, ao invés de passá-los todos como argumentos para a função, passa-se apenas uma variável do tipo
estrutura.
Também chamadas de variáveis compostas heterogêneas (As variáveis compostas homogêneas são as
matrizes, como vimos). Em outras linguagens de programação como o Pascal, as estruturas são chamadas de
registros.

Exemplo de aplicação imediata:

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
};

exemplo varx; // declaração de uma variável do tipo exemplo


// varx possui dois campos, num e ch
varx.num = 2; // atribuição de valor ao campo num de varx
varx.ch = ‘Z’; // atribuição de constante ao campo ch de varx

printf(“%d”, varx.num); // impressão do campo num


printf(“%c”, varx.ch); // impressão do campo ch
}

20.1 DECLARAÇÃO

1ª forma:
struct pessoa { // declaração do tipo estrutura pessoa
char[50] nome;
int idade;
float salario;
};

struct pessoa maria; // declaração de variáveis estrutura


pessoa marcos, joao = {26, 345.67}; // forma também aceita

2ª forma, utilizando typedef:


typedef struct pessoa {
char[50] nome;
int idade;
float salario;
}cliente; // definição de um alias p o identificador

pessoa marcos, joao = {26, 345.67}; //declaração de variáveis


cliente maria; //declaração utilizando o alias

• 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

Exemplo de aplicação 1: declaração e manipulação de dados estrutura (2 campos numéricos e 1 variável)

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);

printf("Dados da Maria: \n");


printf("Idade = %d ",maria.idade);
printf("Salario = %f \n \n",maria.salario);

getch();
}

Exemplo de aplicação 2: declaração e manipulação de dados estrutura (2 campos numéricos e 3 variáveis)


Neste exemplo, demonstra-se como proceder à definição dos valores dos campos de três modos: a) na
inicialização; b) por atribuição e c) pelo teclado.

main()
{
struct pessoa {
int idade;
float salario;
};

struct pessoa maria, marcos, joao = {26, 345.67};

maria.idade = 30;
maria.salario = 500.89;

printf("Informe os dados do Marcos: \n");


printf("Idade: "); scanf("%d", &marcos.idade);
printf("Salario: "); scanf("%f", &marcos.salario);

printf("\n Dados do Joao: \n");


printf("Idade = %d ",joao.idade);
printf("Salario = %f \n \n",joao.salario);

printf("Dados da Maria: \n");


printf("Idade = %d ",maria.idade);
printf("Salario = %f \n \n",maria.salario);

printf("Dados do Marcos: \n");


printf("Idade = %d ",marcos.idade);
printf("Salario = %f ",marcos.salario);

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;

cliente maria, marcos, joao = {"Joao Silva",26, 345.67};

// cliente.idade = 33 atribuicao indevida - dá erro

// maria.nome = “ Maria Ferreira”; atrib indevida - erro

strcpy(maria.nome, "Maria Ferreira");


maria.idade = 30;
maria.salario = 500.89;

printf("Informe os dados do Marcos: \n");


printf("Nome: "); gets(marcos.nome); // *
printf("Idade: "); scanf("%d", &marcos.idade);
printf("Salario: "); scanf("%f", &marcos.salario);

printf("\n Dados do Joao: \n");


printf("Nome = %s ",joao.nome);
printf("Idade = %d ",joao.idade);
printf("Salario = %f \n \n",joao.salario);

printf("Dados da Maria: \n");


printf("Nome = %s ",maria.nome);
printf("Idade = %d ",maria.idade);
printf("Salario = %f \n \n",maria.salario);

printf("Dados do Marcos: \n");


printf("Nome = %s ",marcos.nome);
printf("Idade = %d ",marcos.idade);
printf("Salario = %f ",marcos.salario);

getch();
}

* O programa não funcionará adequadamente se implementarmos a leitura do campo marcos.nome utilizando


a função scanf. Por esta razão é mais adequado utilizar a função gets.

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 88
Algoritmos e Programação Departamento de Informática

20.2 ACESSANDO ESTRUTURAS ATRAVÉS DE PONTEIROS


Para manipular estruturas através de ponteiros, tem-se que:
a) Declarar uma variável ponteiro que possa apontar para o tipo estrutura criado;
b) Armazenar o endereço da variável estrutura na variável ponteiro.
c) Acessar o conteúdo da variável estrutura indiretamente utilizando:
c.1 (*varp).campo ou // a instrucao *varp.campo = 6; gera erro.
c.2 varp ->campo

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”.

Implementação 1, sem ponteiros:


main()
{
typedef struct {
int lado1;
int lado2;
int lado3;
int perimetro;
} triangulo;

triangulo isosceles;

isosceles.lado1=6;
isosceles.lado2=6;
isosceles.lado3=6;

isosceles.perimetro = isosceles.lado1 + isosceles.lado2 +


isosceles.lado3;

printf("perimetro = %d ",isosceles.perimetro);

getch();
}

Implementação 2, com ponteiros:


main()
{
typedef struct {
int lado1;
int lado2;
int lado3;
int perimetro;
} triangulo;

triangulo isosceles;
triangulo *p;

p=&isosceles;

p->lado1=6; p->lado2=6; p->lado3=6;

p->perimetro = p->lado1 + p->lado2 + p->lado3;


// ou: (*p).perimetro = (*p).lado1 + (*p).lado2 + (*p).lado3;
printf("perimetro = %d ",isosceles.perimetro);
// ou: p->perimetro ou: (*p).perimetro
getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 89
Algoritmos e Programação Departamento de Informática

20.3 TAREFAS PROPOSTAS

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;
};

void imprime_struct(exemplo *pe)


{
printf("%d", pe->num);
printf("%c", pe->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;
};

void incrementa_struct(exemplo *pe)


{
pe->num++; // ou: pe->num = pe->num + 1;
pe->ch++; // ou: pe->ch = pe->ch + 1;
}

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

20.4 VETORES DE ESTRUTURAS

A manipulação de vetores de estruturas segue basicamente as mesmas regras da manipulação de variáveis


simples, com a diferença básica que os campos devem ser acessados separadamente, como nos exemplos:

scanf("%d", &clientela[i].idade);  leitura

printf("Idade: %d : ", clientela[i].idade);  impressão

clientela[i].idade = 22;  atribuição

menor = clientela[i].idade;  atribuição

O programa completo abaixo exemplifica melhor o uso destas instruções:


main()
{
typedef struct pessoa {
int idade;
float salario;
}cliente;

cliente clientela[100];
int i, n;

printf("Informe o número de clientes: ");


scanf("%d", &n);

printf("\n\nEntrada dos dados dos clientes \n\n");


for (i=0; i<n; i++) {
printf("Informe os dados do Cliente %d: \n", i+1);
printf("Idade: "); scanf("%d", &clientela[i].idade);
printf("Salario: "); scanf("%f", &clientela[i].salario);
}

printf("\n\nImpressao dos dados dos clientes \n\n");

for (i=0; i<n; i++) {


printf("Cliente %d Idade: %d : \n", i+1, clientela[i].idade);
printf("Cliente %d Salario: %f: \n", i+1, clientela[i].salario);
}

getch();
}

Tarefa proposta 1: Implementar, no programa anterior, tembém o campo nome.

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

Resolução da tarefa proposta 2:

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct{
int idade;
float salario;
char nome[10];
} cliente;

int insere_clientes(cliente clientela[]) //retorna número de elementos


{
int n,i;
printf("Quantos clientes vc ira inserir:");
scanf("%d",&n);
i=0;
while (i<n) {
printf("\n\nEntrada dos dados dos clientes \n\n");
printf("Informe os dados do cliente %d: \n", i+1);
printf("Nome:");
scanf("%s",&clientela[i].nome);
printf("Idade:");
scanf("%d", &clientela[i].idade);
printf("Salario:");
scanf("%f",&clientela[i].salario);
i++;
}
return n;
}

void mostra_clientes(cliente clientela[],int n)


{
int i;
i=0;
while (i<n) {
printf("\n ******Cliente:[%d]******", i+1);
printf("\nNome: %s \n",clientela[i].nome);
printf("Idade: %d \n",clientela[i].idade);
printf("Salario: %f \n",clientela[i].salario);
i++;
}
}

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;

varuniao.inteiro = 2; // campo recebeu dado int


printf("Valor atual da uniao = %d \n", varuniao.inteiro);

varuniao.real = 3.141592; // campo recebeu dado float


printf("Valor atual da uniao = %f \n", varuniao.real);

varuniao.caracter = 'A'; // campo recebeu dado char


printf("Valor atual da uniao = %c \n", varuniao.caracter);

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;

printf("Tamanho da estrutura= %d bytes\n",sizeof(estrutura)); //12


printf("Tamanho da uniao= %d bytes\n", sizeof(uniao)); // 4
}

_________________________________________________________________________________
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.

Quando se coloca a seguinte declaração em um programa:

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().

21.1 A FUNÇÃO 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

21.2 A FUNÇÃO MALLOC( )

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);

A alocação de memória compreende, então:


- localizar espaço de memória disponível,
- marcar este espaço como em uso (retirar da relação dos espaços/bytes livres) e
- obter o endereço do início desta área, atribuindo-o a uma variável ponteiro.

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).

Quais as conseqüências disto?

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.

A utilização prática da função malloc() é demonstrada abaixo:

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

main()
{
int *p;

p = (int *)malloc(4); // ou: p = (*int)malloc(sizeof(int));

*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

21.3 LISTAS SIMPLESMENTE ENCADEADAS

ESTRUTURA PARA DECLARAÇÃO DE NÓS DE UMA LISTA SIMPLESMENTE ENCADEADA

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;
};

elemento noh, *primeiro;

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:

noh.valor = 2; //acesso direto ou

(*primeiro).valor = 10; // acesso indireto, através de primeiro


//altera o conteúdo de noh.valor para 10
// ou: primeiro->valor = 10;

_________________________________________________________________________________
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>

typedef struct Tficha // ou: struct Tficha


{
char nome[30];
struct Tficha *prox;
};

typedef Tficha *Pficha; // Define tipo ponteiro Pficha


// Suas variáveis poderão apontar para
// variáveis do tipo Tficha
main()
{
char nome2[30]; // ou: Tficha *corrente, *novo
Pficha corrente, novo;
printf("sizeof(Tficha)= %d\n", sizeof(Tficha)); //36

novo = (Tficha *) malloc(sizeof(Tficha));


// ou: novo = (Pficha) malloc(sizeof(Tficha));

corrente = novo;

printf("Digite nome: ");


scanf("%s", &corrente->.nome); // ou:
// scanf("%s", &(*corrente).nome);
corrente->prox = NULL; // ou: (*corrente).prox = NULL;

printf("Nome: %s", corrente->nome);


// ou: printf("Nome: %s", (*corrente).nome);

getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 98
Algoritmos e Programação Departamento de Informática

Exemplo de aplicação 2: O programa abaixo utiliza a estrutura e variáveis definidas no exemplo


anterior, criando três variáveis dinâmicas encadeadas e mostrando os seus conteúdos.

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Tficha


{
char nome[30];
struct Tficha *prox;
};

typedef Tficha *Pficha;

main()
{
Pficha primeiro=NULL, corrente, novo;

novo = (Pficha) malloc(sizeof(Tficha));


primeiro = novo;
corrente = novo;
printf("Nome: ");
scanf("%s", &corrente->nome);
corrente->prox = NULL;

novo = (Pficha) malloc(sizeof(Tficha));


corrente->prox = novo;
corrente = novo;
printf("Nome: ");
scanf("%s", &(*corrente).nome);
corrente->prox = NULL;

novo = (Pficha) malloc(sizeof(Tficha));


corrente->prox = novo;
corrente = novo;
printf("Nome: ");
scanf("%s", &(*corrente).nome);
corrente->prox = NULL;

// impressão dos nomes da lista


printf("\nNomes armazenados na lista: \n");
corrente=primeiro;

printf("Nome: %s\n", corrente->nome);


corrente=corrente->prox;

printf("Nome: %s\n", corrente->nome);


corrente=corrente->prox;

printf("Nome: %s\n", corrente->nome);

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:

printf("\nNomes armazenados na lista: \n");


corrente=primeiro;
while (corrente != NULL)
{
printf("Nome: %s\n", corrente->nome);
corrente=corrente->prox;
}

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:

void insere(Pficha *p, Pficha *c)

void lista(Pficha primeiro) // ou: void lista(Tficha *primeiro)

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.

21.4 LISTAS DUPLAMENTE ENCADEADAS

DECLARAÇÃO DE TIPO ESTRUTURA PARA NÓ DE LISTA DUPLAMENTE ENCADEADA

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.

typedef struct ficha


{
char nome[30];
struct ficha *ant;
struct ficha *prox;
} Tficha;

typedef Tficha *Pficha;

main()
{
Pficha primeiro=NULL, corrente, novo, ultimo;

novo = (Pficha) malloc(sizeof(Tficha));


corrente = novo;
printf("Nome: ");
scanf("%s", &corrente->nome); // ou:
// scanf("%s", &(*corrente).nome);
corrente->prox = NULL;
primeiro = novo;
ultimo = novo;
corrente->ant = NULL;

novo = (Pficha) malloc(sizeof(Tficha));


corrente->prox = novo;
novo->ant = corrente;
corrente = novo;
ultimo = novo;
printf("Nome: ");
scanf("%s", &corrente->nome);
corrente->prox = NULL;

novo = (Pficha) malloc(sizeof(Tficha));


corrente->prox = novo;
novo->ant = corrente;
corrente = novo;
ultimo = novo;
printf("Nome: ");
scanf("%s", &corrente->nome);
corrente->prox = NULL;

printf("\nNomes armazenados na lista: \n");


corrente=primeiro;
printf("Nome: %s\n", corrente->nome);
corrente=corrente->prox;
printf("Nome: %s\n", corrente->nome);
corrente=corrente->prox;
printf("Nome: %s\n", corrente->nome);

printf("\nNomes armazenados na lista, do ultimo ao primeiro: \n");


corrente = ultimo;
printf("Nome: %s\n", corrente->nome);
corrente=corrente->ant;
printf("Nome: %s\n", corrente->nome);
corrente=corrente->ant;
printf("Nome: %s\n", corrente->nome);

getch();
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 101
Algoritmos e Programação Departamento de Informática

Exemplo de aplicação 2: Este programa amplia as funcionalidades do programa-exemplo anterior,


permitindo a criação de um número indefinido de nós e a listagens dos nomes na mesma ordem da inserção e na
ordem inversa.

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

typedef struct ficha {


char nome[30];
struct ficha *ant;
struct ficha *prox;
} Tficha;

typedef Tficha *Pficha;

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);
}

printf("\nNomes armazenados na lista: \n");


corrente=primeiro;
while (corrente != NULL) {
printf("Nome: %s\n", corrente->nome);
corrente=corrente->prox;
}

printf("\nNomes armazenados na lista, do ultimo ao primeiro: \n");


corrente = ultimo;
while (corrente != NULL) {
printf("Nome: %s\n", corrente->nome);
corrente=corrente->ant;
}

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>

typedef struct Tficha


{
char nome[30];
struct Tficha *prox;
};

typedef TFicha *PFicha;

void insere(PFicha *p, PFicha *c)


{
PFicha novo;
novo=(PFicha)malloc(sizeof(TFicha));
if (*p== NULL)
*p=novo;
else
(*c)->prox=novo;
novo->prox=NULL;
printf("Nome:");
scanf(" %s",&novo->nome);
*c=novo;
}

void lista(PFicha primeiro)


{
Pficha corrente;
printf("Nomes armazenados na lista: ");
corrente=primeiro;
while (corrente!=NULL)
{ printf("\n Nome:%s",corrente->nome);
corrente=corrente->prox;
}
}

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:

// LISTA DUPLAMENTE ENCADEADA


// c funções, s var globais, c passagem var p/ referencia, c exclusão

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

typedef struct ficha


{
char nome[30];
struct ficha *ant;
struct ficha *prox;
} Tficha;

typedef Tficha *Pficha;

void insere(Pficha *p, Pficha *u)


{
Pficha novo;
novo = (Pficha) malloc(sizeof(Tficha));
if (*p==NULL)
{
*p=novo;
novo->ant=NULL;
}
else
{
novo->ant=*u;
(*u)->prox=novo;
}
printf("Nome: ");
scanf("%s", &novo->nome);
novo->prox = NULL;
*u = novo;
}

void listadireta(Pficha aux)


{
int cont=1;
printf("\nNomes armazenados na lista, do primeiro ao ultimo: \n");
while (aux != NULL)
{ printf("%d - Nome: %s\n", cont++, aux->nome);
aux=aux->prox;
}
}

void listainversa(Pficha aux)


{
printf("\nNomes armazenados na lista, do ultimo ao primeiro: \n");
while (aux != NULL)
{
printf("Nome: %s\n", aux->nome);
aux=aux->ant;
}
}

_________________________________________________________________________________
Professor Jeferson Antonio Quimelli 104
Algoritmos e Programação Departamento de Informática

void deleta(Pficha *p, Pficha *u)


{
int escolha, x,cont=1;
Pficha aux;
printf("Listagem dos registros: \n");
listadireta(*p);
aux=*p; // aux aponta para o primeiro no
printf("Numero do registro a excluir: \n"); scanf("%d",&x);
while (cont<x)
{
aux = aux->prox;
cont++;
}
printf("Registro a excluir: ");
printf("%s\n",aux->nome);
if (aux==*p) // aux aponta para o primeiro no
{ *p = (*p)->prox;
(*p)->ant=NULL;
}
else if (aux==*u) // aux aponta para o ultimo no
{ *u = aux->ant;
(*u)->prox = NULL;
}
else // aux aponta para no intermediario
{ aux->ant->prox = aux->prox;
// campo prox no anterior aponta p prox no
aux->prox->ant = aux->ant;
// campo ant prox no ponta p no anterior
}
free(aux);
printf("Nova lista apos exclusao: \n");
listadireta(*p);
}

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.PROGRAMAÇÃO ORIENTADA A OBJETOS


Os conceitos da programação orientada a objetos foram desenvolvidos no Centro de Pesquisas da
Xerox em Palo Alto, USA, na década de 70 e aplicados na linguagem Smalltalk, desenvolvida especialmente
para esse fim pelos pesquisadores Goldberg e Robson, descrita e divulgada em 19851.
Os conceitos de orientação a objetos foram incorporados à linguagem C através da biblioteca iostream.h
(ou iostream), gerando a linguagem C++, que manteve, deste modo, o paradigma da programação estruturada em
adição ao novo paradigma de orientação a objetos.
A grande vantagem da utilização dos objetos é que se pode implementar um melhor controle de quais
funções (métodos ou outras funções externas ao objeto) podem ou não manipular determinadas propriedades
(variáveis membros dos objetos).

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

22.7.CONCEITOS BÁSICOS - EXEMPLO DE APLICAÇÃO

#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()

cout << x1.i1;


// cout << x1.i2; o mesmo erro ocorrerá com esta linha
getch();
}

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);}
};

void CRetangulo::define_valores (int a, int b) {


base = a;
altura = b;
}

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).

4. A função main() cria o objeto Ret, instância da classe CRetangulo.

5. Os dois métodos do objeto são ativados através das mensagens no main().

Tarefa proposta 1: Refazer o exemplo anterior, criando dois objetos de retângulos, Ret1 e Ret2.

22.9.DEFINIÇÃO DE CLASSES COM A PALAVRA STRUCT

A linguagem C++ estendeu para a palavra-chave struct de C a mesma funcionalidade da


palavra-chave class de C++, exceto pelo fato de que seus membros são public por padrão ao invés de serem
private.
De qualquer maneira, devido ao fato de class e struct terem quase a mesma funcionalidade
em C++, struct geralmente é usado para estruturas somente com dados e class para classes que possuem
procedimentos e funções membro.

22.10.SOBRECARGA

____________________________________________________________________________ 107
Professor Jeferson Antonio Quimelli
Algoritmos e Programação Departamento de Informática

Em OO, sobrecarga é o ato de declarar dois elementos com o mesmo nome.


Podemos ter sobrecarga de funções, de métodos e até de operadores.
No caso das funções (incluindo funções-membro), isto ocorre quando duas ou mais funções são
definidas com o mesmo nome. Neste caso, é exigido que eles tenham assinaturas diferentes, ou seja, a quantidade
e o tipo dos parâmetros do cabeçalho das funções devem ser diferentes. Será chamada a função cujos parâmetros
(tipo e número) forem iguais aos da chamada. Esta determinação é feita em tempo de compilação.

Exemplo 1:

#include <iostream.h>
#include <conio.h>

void mostra_mensagem()
{
cout << " meu livro" << endl;
}

void mostra_mensagem(char *mensagem)


{
cout << mensagem;
}

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>

int x1, x2, x3;

void inicializa()
{
x1=x2=x3=0;
}

void inicializa(int valor)


{
x1=x2=x3=valor;
}

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.

Vamos agora adaptar o exemplo anterior, incluindo um construtor:

// exemplo de classes
#include <iostream.h>

class CRetangulo
{
int base, altura;
public:
CRetangulo (int,int);
int area (void) {return (base*altura);}
};

CRetangulo::CRetangulo (int a, int b) {


base = a;
altura = b;
}

int main () {

CRetangulo Ret (3,4);


CRetangulo Ret2 (5,6);

cout << "Area de Ret: " << Ret.area() << endl;

cout << "Area de Ret2: " << Ret2.area() << endl;


}

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.

// exemplo de construtores e destrutores


#include <iostream.h>

class CRetangulo
{
int *base, *altura;
public:
CRetangulo (int,int);

~CRetangulo ();

int area (void) {return (*base * *altura);}


};

CRetangulo::CRetangulo (int a, int b) {


base = new int;
altura = new int;
*base = a;
*altura = b;
}

CRetangulo::~CRetangulo () {
delete base;
delete altura;
}

int main () {

CRetangulo Ret(3,4), Ret2 (5,6);

cout << "Area de Ret: " << Ret.area() << endl;


cout << "Area de Ret2: " << Ret2.area() << endl;

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.

2 – O método área() acessa as dimensões do retângulo indiretamente, através das variáveis


ponteiro base e altura e retorna a área.

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).

// sobrecarga de construtores de classe


#include <iostream.h>

class CRetangulo
{
int base, altura;
public:
CRetangulo (); // Construtor – sem parametros
CRetangulo (int,int); // Outro construtor – com parâmetros

int area (void) {return (base*altura);}


};

CRetangulo::CRetangulo (int a, int b) {


base = a;
altura = b;
}

CRetangulo::CRetangulo () {
base = 5;
altura = 6;
}

int main () {

CRetangulo Ret(3,4);
CRetangulo Ret2;

cout << "Area de Ret: " << Ret.area() << endl;

cout << "Area de Ret2: " << Ret2.area() << endl;


}

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:

CRetangulo Ret2; // certo


CRetangulo Ret2(); // errado!

____________________________________________________________________________ 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:

class classe_derivada: public classe_base;

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().

// herança – classe base e classes derivadas


#include <iostream.h>

class CPoligono {
protected:
int base, altura;
public:
void define_valores (int a, int b)
{ base=a; altura=b;}
};

class CRetangulo: public CPoligono {


public:
int area (void)
{ return (base * altura); }
};

class CTriangulo: public CPoligono {


public:
int area (void)
{ return (base * altura / 2); }
};

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

O que é herdado da classe base?

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:

class CRetangulo: public CPoligono, public CSaida {


class CTriangulo: public CPoligono, public CSaida {

aqui está um exemplo completo:

// 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);
};

void CSaida::saida (int i) {


cout << i << endl;
}

class CRetangulo: public CPoligono, public CSaida {


public:
int area (void)
{ return (base * altura); }
};

class CTriangulo: public CPoligono, public CSaida {


public:
int area (void)
{ return (base * altura / 2); }
};

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:

1 – São criadas as classes gato e cachorro, ambas possuindo o método imprime().

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

Resolução tarefas propostas.

Tarefa proposta1: (Altera-se somente a função main())

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

Вам также может понравиться