apostila c

120
Algoritmos e Programação Departamento de Informática _________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias 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( ) { }

Upload: andrey-ribeiro

Post on 29-Dec-2015

16 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 execu tada { 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( ) { }

Page 2: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

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 lin ha numero dois”); }

3)

main( ) { printf (“o número 2”); }

Page 3: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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)

Page 4: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 5: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 (im primirá: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); }

Page 6: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

7)

main( ) // %e permite impressão de pot. de 10 em f ormato científico {

printf (“%e + %e = %E”, 8.2e+002, 3.36e+005, 8.2e+0 02 + 3.36e+005); }

Obs: 8.2e+002 = 8.2x10 2 = 8.2x100 = 820 3.36e+005 = 3.36x10 5 = 3.36x100000 = 336000

8) main( ) // Um mesmo número, 16, escrito com format açõ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

Page 7: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 dec imais {

printf (“%6.2f %8.4f”, 12.3456, 12.3456); }

6)

main( ) // especificador %f, com mais de 19 digitos significativos {

printf (“o numero %f”, 7493752094857203945087405.34 53458); }

Page 8: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 f lutuante 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

Page 9: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.

Page 10: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.6 47

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;

Page 11: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 12: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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”, c orrida); printf (“ \ n do evento %d foi % f”, evento, te mpo); }

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 outr as...

6 FUNÇÃO SCANF( )

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

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

Page 13: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.

Page 14: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

Exemplo:

main( ) { int valor; printf ( “digite um valor ponto flutuante e um i nteiro”); 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”, c h); }

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

Page 15: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 result ado 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;

Page 16: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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”, divide ndo); 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

Page 17: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 18: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.

Page 19: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 20: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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;

Page 21: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 resu ltado.

Exemplo:

main( ) { int verdadeiro, falso; verdadeiro = (15 < 20); falso = (15 == 20); printf (“Verdadeiro = %d, falso = %d”, verdadeir o, 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:

Page 22: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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)

Page 23: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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;

Page 24: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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:

if (<condicao>) { <comando_composto_1>; } else { <comando_composto_2>; }

exemplo: if (a<b) { a=b; c=2; } else { 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”:

if (<condicao>) { <comando_composto_1>; }

Exemplo: if (a<b) { 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>;

Page 25: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 26: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.

Page 27: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 pa ra 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 re fere 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 v alido"); getch(); }

Page 28: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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;

Page 29: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 30: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 31: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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.

Page 32: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 33: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 34: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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, c oluna); 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); }

Page 35: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

Page 36: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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:

Page 37: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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

} }

cuj a 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, con soantes);

}

Page 38: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Jeferson Antonio Quimelli e Ivo Mário Mathias

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 encer rar: ”); 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.

Page 39: Apostila C

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ÇÃO1. 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

Page 40: Apostila C

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

Page 41: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 39

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 el emento do vetor scanf(“%d”, &vetor[5]); // leitura do sexto element o 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.

Page 42: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 40

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 + ... + nota 299) / 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++; } }

Page 43: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 41

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.

Page 44: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 42

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 condica o: \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], n ota2[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;

Page 45: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 43

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

Page 46: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 44

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

Page 47: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 45

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.

Page 48: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 46

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.

Page 49: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 47

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:

Page 50: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 48

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

Page 51: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 49

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]",m enor,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"); } }

Page 52: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 50

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, col b, 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 matr iz 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.

Page 53: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 51

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 caracter es, "); printf ("%s", "tambem chamada de cadeia de caract eres"); 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.

Page 54: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 52

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

Page 55: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 53

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 funci ona puts("Frase digitada: "); puts (frase); // gets (&frase); Esta instrucao NAO funciona // puts("Frase digitada: ", frase); Também NAO fu nciona // 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 CARACTERE S

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",ve tor, tamanho); puts("Impressao da string utilizando puts: "); p uts(vetor); getch(); }

Page 56: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 54

• 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", ocorre ncias, 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(); }

Page 57: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 55

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 funca o strlen printf ("\nTamanho do nome (%s) = %d ", nome, str len(nome)); tamanho = strlen(&sobrenome[0]); printf ("\nTamanho do sobrenome (%s)= %d", sobren ome, 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

Page 58: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 56

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

Page 59: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 57

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 c omo 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 valid a printf ("\nSobrenome apos o uso de strcpy= %s", s obrenome); getch(); }

Page 60: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 58

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.

Page 61: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 59

18.1.1 PROCEDIMENTOS SEM PARÂMETROS / QUE NÃO RECEB EM 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 f uncao\n"); getch(); }

Page 62: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 60

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",digit o); } main() { int i=10; mostra_digito(5); mostra_digito(i); getch(); }

Page 63: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 61

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

Page 64: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 62

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

Page 65: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 63

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

Page 66: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 64

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

Page 67: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 65

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?

Page 68: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 66

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:

Problema - calcular a seguinte seqüência: S(x, n) = x/1! + x2/2! + x3/3! + ... + xn/ 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, termo s)); }

Page 69: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 67

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

Page 70: Apostila C

Algoritmos e Programação Departamento de Informática

_________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 68

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.

Page 71: Apostila C

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

Page 72: Apostila C

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

Page 73: Apostila C

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

Page 74: Apostila C

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

Page 75: Apostila C

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

Page 76: Apostila C

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

Page 77: Apostila C

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

Page 78: Apostila C

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

Page 79: Apostila C

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

Page 80: Apostila C

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

Page 81: Apostila C

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

Page 82: Apostila C

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

Page 83: Apostila C

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

Page 84: Apostila C

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 M22FF41 e22FF42 n22FF43 s22FF44 a22FF45 g22FF46 e22FF47 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

Page 85: Apostila C

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

Page 86: Apostila C

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

Page 87: Apostila C

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

Page 88: Apostila C

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 22FF50Valor 2.200000 Endereco 22FF54Valor 3.300000 Endereco 22FF58Valor 4.400000 Endereco 22FF5CValor 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

Page 89: Apostila C

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

Page 90: Apostila C

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

Page 91: Apostila C

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

Page 92: Apostila C

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

Page 93: Apostila C

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

Page 94: Apostila C

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

Page 95: Apostila C

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

Page 96: Apostila C

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

Page 97: Apostila C

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

Page 98: Apostila C

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

Page 99: Apostila C

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

Page 100: Apostila C

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

Page 101: Apostila C

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

Page 102: Apostila C

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

Page 103: Apostila C

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

Page 104: Apostila C

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

Page 105: Apostila C

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

Page 106: Apostila C

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

Page 107: Apostila C

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

Page 108: Apostila C

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

Page 109: Apostila C

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.____________________________________________________________________________Professor Jeferson Antonio Quimelli

104

Page 110: Apostila C

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

____________________________________________________________________________Professor Jeferson Antonio Quimelli

105

Page 111: Apostila C

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.____________________________________________________________________________Professor Jeferson Antonio Quimelli

106

Page 112: Apostila C

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

____________________________________________________________________________Professor Jeferson Antonio Quimelli

107

Page 113: Apostila C

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

____________________________________________________________________________Professor Jeferson Antonio Quimelli

108

Page 114: Apostila C

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.

____________________________________________________________________________Professor Jeferson Antonio Quimelli

109

Page 115: Apostila C

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.

____________________________________________________________________________Professor Jeferson Antonio Quimelli

110

Page 116: Apostila C

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: 12Area 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; // certoCRetangulo Ret2(); // errado!

____________________________________________________________________________Professor Jeferson Antonio Quimelli

111

Page 117: Apostila C

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

2010

____________________________________________________________________________Professor Jeferson Antonio Quimelli

112

Page 118: Apostila C

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);____________________________________________________________________________Professor Jeferson Antonio Quimelli

113

Page 119: Apostila C

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.

____________________________________________________________________________Professor Jeferson Antonio Quimelli

114

Page 120: Apostila C

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.

____________________________________________________________________________Professor Jeferson Antonio Quimelli

115