Вы находитесь на странице: 1из 23
SERVIÇO PÚBLICO FEDERAL MINISTÉRIO DA EDUCAÇÃO CENTRO FEDERAL DE EDUCAÇÃO TECNOLÓGICA DE MINAS GERAIS Prof.

SERVIÇO PÚBLICO FEDERAL

MINISTÉRIO DA EDUCAÇÃO CENTRO FEDERAL DE EDUCAÇÃO TECNOLÓGICA DE MINAS GERAIS

Prof. Cristiano Amaral Maffort

Curso Básico de C++

Av. Amazonas 7675 - Nova Gameleira - Belo Horizonte - MG

Sumário

Capítulo 1: Conceitos básicos da linguagem C++

3

 

1.1. C++

Standard Library

3

1.2. Ambiente de desenvolvimento C++ típico

3

1.3. Estrutura básica de um programa

4

1.4. Armazenamento de dados e programas na memória

4

1.5. Bibliotecas

5

1.6. Verificação e validação

5

Capítulo 2: Estruturas seqüenciais, entrada/saída de dados

6

2.1.

Variáveis

6

2.1.1. Tipos

básicos

6

2.1.2. Declaração de variáveis

6

2.1.3. Variáveis com valores indefinidos

7

2.2. Constantes

7

2.3. Operadores

8

 

2.3.1

Operadores Aritméticos

8

2.3.2.

Operadores

de atribuição

8

2.3.3.

Operadores

relacionais

8

2.3.4.Operadores lógicos

9

2.5. Comando de entrada e saída

9

2.6. Exercícios

10

Capítulo 3: Abstração de controle

11

3.1. Operador

lógico condicional

11

3.2. Operador de seleção

12

3.3. Operador de repetição

12

3.3.1. Repetição com teste no início

12

3.3.2. Repetição com variável de controle no laço

12

3.3.3. Repetição com teste no fim

13

3.4.

Exercícios

13

Capítulo 4: Abstração de Dados

15

4.1.

Variáveis compostas homogêneas

15

4.1.1. unidimensionais

Variáveis

15

4.1.2. multidimensionais

Variáveis

15

4.1.3. Exercícios

16

4.2.

Variáveis compostas heterogêneas

16

4.2.1. Registros (Struct)

16

4.2.2. Registro de conjuntos

17

4.2.3. Conjunto de registros

17

22

Capítulo 1: Conceitos básicos da linguagem C++

A linguagem C++ desenvolveu-se a partir da linguagem C. Foi criada por Bjarne Stroustrup no início da década de 1980 na Bell Laboratories. A linguagem C++ fornece vários recursos que aprimoram a linguagem C, mas, sobretudo, fornece capacidades para desenvolvimento de software baseados no paradigma de Programação Orientada a Objetos (POO), o qual será estudado mais adiante.

1.1. C++ Standard Library

Os programas C++ consistem em partes chamadas classes e funções. Um programa em C++ é formado a partir da composição dessas partes. A maioria dos programadores em C++ tira proveito das ricas coleções de classes e funções existentes na C++ Standard Library. A utilização dessas bibliotecas pode melhorar o desempenho do programa, porque elas são escritas cuidadosamente para executar com eficiência. Além disso, elas tanto diminuem o tempo no desenvolvimento de software, através de uma prática chamada reutilização de software (fundamental para a programação orientada por objetos), como também melhoram a portabilidade do programa, ou seja, permite escrever programas que podem ser executados, com pouca ou nenhuma modificação, em uma grande variedade de plataformas e/ou arquiteturas de computadores.

1.2. Ambiente de desenvolvimento C++ típico

Em geral, os sistemas C++ consistem em três partes: um ambiente de desenvolvimento, a linguagem e a C++ Standard Library. Os programas C++, em geral, passam pelas seguintes fases:

Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste
Os programas C++, em geral, passam pelas seguintes fases: • Fase 1: Criando um Programa Consiste

Fase 1: Criando um Programa Consiste em editar um arquivo com um programa editor onde vc realiza quaisquer alterações no código- fonte e o salva em um dispositivo de armazenamento secundário. Neste curso utilizaremos a IDE Dev- C++ (disponível em www.bloodshed.net/devcpp.html) para realização desta e de outras etapas a seguir.

Fase 2: Pré-processamento O pré-processador C++ obedece a comandos chamados de diretivas de pré-processador, que indicam que certas manipulações devem ser realizadas no programa antes da compilação. Essas manipulações incluem outros arquivos de texto a serem compilados e realizam várias substituições de texto.

Fase 3: Compilação Quando o programador executa o comando para compilar o programa, o pré-processador é automaticamente chamado. Após o pré-processamento, o compilador converte o programa C++ em código de linguagem de máquina (também referido como código-objeto).

Fase 4: Linkagem Em geral, os programas C++ contêm referências a funções e dados definidos em outra parte, como nas bibliotecas padrão. Normalmente, o código-objeto produzido pelo compilador C++ contém “lacunas” devido a essas partes ausentes. Um linker vincula o código-objeto com o código das funções ausentes para produzir uma imagem executável.

Fase 5: Carregamento Antes de um programa ser executado, ele deve ser previamente colocado na memória principal. Isso é feito pelo carregador, que pega a imagem executável do disco e a transfere para a memória. Componentes adicionais de bibliotecas compartilhadas que suportam o programa também são carregados.

Fase 6: Execução Por fim, o computador, sob o controle da CPU, executa o programa.

1.3. Estrutura básica de um programa

Exemplo padrão utilizado como introdução à maioria das linguagens de programação. O programa exibe o texto “Olá Mundo!” no monitor.

1.

#include <iostream> // permite que o programa gere saída de dados na tela

2.

3.

int main() // a função main inicia a execução do programa

4.

{

5.

std::cout << "Alô mundo!!!"; // exibe a mensagem

6.

return 0; // indica que o programa terminou com sucesso

7.

} // fim da função main

A primeira linha do programa é uma directiva de pré-processamento #include, que diz ao

compilador para substituir aquela linha pela totalidade do conteúdo do ficheiro a que a directiva se refere

(neste caso iostream). Assim, o ficheiro padrão iostream (que contém os protótipos de funções para operações de entrada e saída) irá substituir a linha.

A linha 3 declara uma função, obrigatória para qualquer programa em C++, denominada “main”. A função “main” tem um significado especial no algoritmo em C++, pois é a função chamada no início do fluxo de execução do programa, assim que ele começa. Os caracteres { } (linhas 4 e 7) delimitam, neste caso, a extensão da função. O termo int define que a função “main” retorna um número inteiro.

O trecho de código da linha 5 é responsável por executar uma função de saíde de dados para a

tela, chamada cout, a qual é responsável por imprimir a mensagem “Alô mundo!!!”.

O comando return (linha 6) é responsável por interromper o fluxo de execução da função e

retornar um resultado, do mesmo tipo definido no corpo da função, para o método que invocou essa função.

A linguagem C++ não impõe o uso de uma formatação rígida. Nela, o programador escolhe a forma mais apropriada para escrever seu código. Podemos, por exemplo, escrever vários comandos em uma única linha ou dividir um mesmo comando em diversas linhas. No entanto, para que nossos códigos tenham clareza, na maioria das vezes, optamos por escrever cada comando em um linha, sempre que possível.

Pode-se também inserir comentários no código-fonte, iniciados com //, conforme ilustrado anteriormente. Comentários de múltiplas linhas devem ser inicializados com /* e finalizados com */. Devemos notar também que comandos e declarações em C++ são terminados pelo caractere ponto-e- vírgula (;)

1.4. Armazenamento de dados e programas na memória

A memória do computador é dividida em unidades de armazenamento chamadas bytes. Cada

posição de memória tem um endereço único.

Como veremos nas próximas seções, quando reservamos um espaço de memória para armazenar um determinado valor, esse espaço é finito, composto de 1 ou mais bytes. Portanto, a faixa de valores e a

pecisão com que representamos um valor no computador são finitas, pois temos um número finito de bits para essa representação. Assim, em um espaço de 1 byte (8 bits), só podemos representar 2 8 (=256) valores distintos.

Se só podemos armazenar números na memória do computador, como fazemos para armazenar um texto (um documento ou uma mensagem)? Para armazenar uma seqüência de caracteres, que representa o texto, atribui-se a cada caractere um código numérico (por exemplo, pode-se associar ao caractere A o código 65, ao caractere B o código 66, e daí por diante). Se todos os caracteres tiverem código associados (inclusive os caracteres de pontuação e de formatação), podemos armazenar um texto na memória do computador como uma seqüência de códigos numéricos.

A mesma estratégia é usada para representar um programa na memória do computador. Um

computador só pode executar programas em linguagens de máquina. Cada programa executável é uma seqüência de instruções que a CPU interpreta, executando as operações correspondentes. Essa seqüência de instruções também é representada como uma seqüência de códigos numéricos. Os programas ficam armazenados em disco e, para serem executados pelo computador, devem ser carregados (transferidos) para a memória principal. Uma vez na memória, o computador executa a seqüência de operações correspondentes.

1.5. Bibliotecas

A tarefa das bibliotecas é permitir que funções de interesse geral estejam disponíveis com

facilidade. Nosso exemplo usa a biblioteca de entrada/saída padrão de C++, iostream, que oferece funções para permitir a captura de dados a partir do teclado e a saída de dados para a tela, entre outras. Além de bibliotecas preparadas pelo fornecedor do compilador ou por outros fornecedores de software, podemos ter bibliotecas preparadas por um programador qualquer, que pode “empacotar” classes e funções com utilidades relacionadas em uma biblioteca e, dessa maneira, facilitar seu uso em outros programas.

1.6. Verificação e validação

Programas, via de regra, podem conter erros, que precisam ser identificados e corrigidos. Quase sempre a verificação é realizada por meio de testes, que executam o programa a ser testado com diferentes valores de entrada. Identificado um ou mais erros, o código-fonte é corrigido e deve ser novamente verificado. O processo de edição, compilação, ligação e teste é repetido até que os resultados sejam satisfatórios, e o programa seja considerado validado.

O ciclo de desenvolvimento de um programa pode ser facilitado pela utilização de um “ambiente

integrado de desenvolvimento” (Integrated Development Environment). O IDE é um programa que oferece interfaces para a edição, compilação e depuração de programas. Se um IDE estiver disponível, é possível criar e testar um programa, tudo em um mesmo ambiente, e todo o ciclo mencionado acontece de maneira mais confortável dentro de um mesmo ambiente, de preferência com uma interface amigável.

Capítulo 2: Estruturas seqüenciais, entrada/saída de dados

Nesta

seção

serão

apresentadas

as

indispensáveis à implementação de programas.

principais

operações

e

comandos

da

linguagem

C++

2.1. Variáveis

Podemos dizer que uma variável representa um espaço na memória do computador para armazenar um determinado tipo de dado. Na linguagem C++, todas as variáveis devem ser explicitamente declaradas. Na declaração de uma variável, devem ser especificados seu tipo e nome: o nome da variável serve de referência ao dado armazenado no espaço de memória da variável e o tipo da variável determina a natureza do dado que será armazenado. Só podemos armazenar valores do tipo especificado na declaração da variável. Assim, se declaramos uma variável como sendo do tipo inteiro, só podemos armazenar valores inteiros no espaço de memória correspondente.

2.1.1. Tipos básicos

A linguagem C++ oferece alguns tipos básicos. Esses tipos diferem entre si pelo espaço de

memória que ocupam e, conseqüentemente, pelo intervalo de valores que podem representar. O tipo char, por exemplo, ocupa 1 byte de memória (8 bits), e pode representar 2 8 (=256) valores distintos. A Tabela 1 ilustra os principais tipos básicos da linguagem C++.

Tipo Tamanho (bytes) Representatividade char 1 -128 a 127 unsigned char 1 0 a 255
Tipo
Tamanho (bytes)
Representatividade
char
1
-128 a 127
unsigned char
1
0 a 255
short int
2
-32.768 a 32.767
unsigned short int
2
0 a 65.535
long int
4
-2.147.483.648 a 2.147.483.647
unsigned long int
4
0 a 4.294.967.295
long long int
8
-9.223.372.036.854.775.808 a
9.223.372.036.854.775.807
unsigned long long int
8
0 a 18.446.744.073.709.551.615
float
4
10 -38 a 10 38
10 -308 a 10 308
double
8
10 -4932 a 10 4932
long double
16

Tabela 1: Tipos básicos e suas representatividades

Um outro importante tipo da linguagem C++ é o tipo int puro, o qual é, em geral, mapeado para o tipo inteiro natural da máquina. Nas maioria das máquinas usadas hoje, que funcionam com processadores de 32 bits (4 bytes), o tipo int é mapeado para o inteiro de 4 bytes (long int).

Alguns desses tipos podem ainda ser modificados para representar apenas valores positivos, o que pode ser feito precedendo o tipo com o modificador unsigned (sem sinal).

O tipo char é freqüentemente usado para representar códigos de caracteres, como veremos nas

seções subseqüentes.

A linguagem oferece ainda dois tipos básicos para a representação de números reais: float e

double. O tipo double é recomendado para as situações nas quais a precisão numérica das operações é de fundamental importância. Por exemplo, em aplicações que fazem simulações numéricas, em geral precisamos trabalhar com maior precisão.

2.1.2. Declaração de variáveis

Para armazenar um dado (valor) na memória do computador, devemos reservar o espaço correspondente ao tipo do dado. A declaração de uma variável reserva um espaço de memória para armazenar um dado do tipo da variável e associa o nome da variável a esse espaço de memória.

No fragmento de código a abaixo, são declaradas três variáveis, duas (a e b) para armazenar valores inteiros (tipo int) e uma (c) para armazenar valores reais (tipo float). Uma vez declaradas as variáveis, podemos armazenar valores dos tipos correspondentes. Isso é feito atribuindo-se valores às variáveis. Não é possível, por exemplo, armazenar um número real numa variável do tipo int.

int a;

/* declara uma variável do tipo int */

int b;

/* declara outra variável do tipo int */

float c;

/* declara uma variável do tipo float */

a = 5;

/* armazena o valor 5 em a */

b = 10;

/* armazena o valor 10 em b */

c = 5.3;

/* armazena o valor 5.3 em c */

A linguagem permite que variáveis de mesmo tipo sejam declaradas juntas. Assim, essas duas primeiras declarações poderiam ser substituídas por:

int a, b;

A linguagem permite escrever:

int a = 5, b = 10; float c = 5.3;

que as variáveis sejam inicializadas na declaração. Podemos, por exemplo,

/* declara duas variáveis do tipo int */

/* declara uma variável do tipo int */ /* declara outra variável do tipo float */

2.1.3. Variáveis com valores indefinidos

Um dos erros comuns em programas de computador é o uso de variáveis cujos valores ainda estão indefinidos. Se declaramos uma variável sem explicitamente inicializar seu valor, ele é indefinido. Existe um valor armazenado, representado pela seqüência de bits do espaço reservado, mas, como não temos controle sobre esse valor, não faz sentido utilizá-lo. Costumamos dizer que o valor da variável é “lixo”. Por exemplo, o trecho de código a seguir está errado, pois o valor armazenado na variável b está indefinido e tentamos usá-lo na atribuição a c.

int a, b, c;

a

= 5;

c

= a + b;

/* ERRO: b tem “lixo” */

Alguns desses erros são óbvios (como o ilustrado), e o compilador é capaz de nos reportar uma advertência. No entanto, muitas vezes o uso de uma variável não definida é difícil de ser identificado no código. É importante ressaltar que esse é um erro comum em programas, e é uma razão para alguns programas funcionarem na parte da manhã e não funcionarem na parte da tarde (ou funcionarem durante o desenvolvimento e não funcionarem quando os entregamos ao cliente).

Todos os erros em computação tem lógica. A razão de o programa funcionar uma vez e não funcionar outra é que, como já mencionamos, apesar de indefinido, o valor da variável existe. No nosso caso citado anteriormente, pode acontecer de o valor armazenado na memória ocupada por b ser 0, fazendo com que o programa funcione. Por outro lado, pode acontecer de o valor ser, por exemplo, -29345 e o programa não funcionar conforme esperado.

2.2. Constantes

Tem-se como definição de constante tudo aquilo que e fixo ou estável, e existirão vários momentos em que este conceito deverá estar em uso. Em nossos códigos, usamos também valores constantes. Quando escrevemos a atribuição:

a = b + 123;

sendo a e b variáveis supostamente já decladas, deve-se representar internamente também a constante

123, para que a operação possa ser avaliada em tempo de execução.

As constantes também podem ser do tipo real. Uma constante real deve ser escrita com um ponto decimal ou valor de expoente. Sem nenhum sufixo, uma constante real é do tipo double. Se quisermos uma constane real do tipo float, devemos acrescentar o sufixo F ou f. Alguns exemplos de constantes reais são:

12.45

/* constante real do tipo double */

1245e-2

/* constante real do tipo double */

12.45F

/* constante real do tipo float */

Alguns compiladores exibem uma advertência quando encontram este código:

float x = 12.45;

pois o código, a rigor, armazena um valor double (12.45) em uma variável do tipo float. Desde que a constante seja representável dentro de um float, não precisamos nos preocupar com esse tipo de advertência. Todavia, se quisermos evitá-lo, podemos representar a constante em precisão de float:

2.3. Operadores

Tanto variáveis como constantes podem ser utilizadas na elaboração de cálculos matemáticos com a utilização de operadores aritméticos. Os operadores aritméticos são classificados em duas categorias, sendo binários ou unários. A Tabela 2 apresenta um resumo dos operadores aritméticos utilizados na linguagem C++.

Operador Operação Tipo Resultado + Manutenção de sinal Unário - - Inversão de sinal Unário
Operador
Operação
Tipo
Resultado
+
Manutenção de sinal
Unário
-
-
Inversão de sinal
Unário
-
%
Resto de divisão
Binário
Inteiro
/
Divisão
Binário
Inteiro ou Real
*
Multiplicação
Binário
Inteiro ou Real
+
Adição
Binário
Inteiro ou Real
-
Subtração
Binário
Inteiro ou Real
pow(base, expoente)
Exponenciação
Binário
Real
sqrt(valor numérico)
Raiz quadrada
Unário
Real

Tabela 2: Principais operadores aritméticos da linguagem C++

Para utilizar as funções pow() e sqrt(), é necessário que a biblioteca cmath seja inicialmente incluída no programa com o comando #include antes da função principal main(), utilizando a sintaxe:

#include <cmath>.

2.3.1 Operadores Aritméticos

As operações aritméticas são realizadas na precisão dos operandos. Assim, a expressão 5/2.5, o valor do operando de menor expressividade (no caso, int) é implicitamente convertido para o tipo de maior expressividade (double), e a operação é feita na precisão double.

O operador módulo (%) não se aplica a valores reais e seus operandos devem ser do tipo inteiro.

Esse operador produz o resto da divisão do primeiro operador pelo segundo.

Os operadores *, / e % têm precedência maior do que os operadores + e -. O operador unário -- tem precedência maior do que *, / e %. Operadores com a mesma precedência são avaliados da esquerda para a direita.

Quando desejamos controlar a ordem na avaliação das operações podemos utilizar os parênteses.

2.3.2. Operadores de atribuição

 

A

atribuição é uma expressão cujo valor resultante correspondente ao valor atribuído. A expressão

‘a

=

5

+

2’ resulta no valor 7, além, é claro de armazenar o valor 7 na variável a. A avaliação das

operações de atribuição são realizadas da direita para a esquerda. Assim, na avaliação da expressão ‘y =

x = 5’, o computador irá avaliar x = 5, armazenando 5 em x, e em seguida, armazena em y o valor produzido por x = 5, que é 5.

2.3.3. Operadores relacionais

Os operadores relacionais são usados para comparar dois valores. A Tabela 3 apresentada os operadores relacionais da linguagem C++.

Símbolo

Significado

== Igual a != Diferente de > Maior que < Menor que >= Maior ou
==
Igual a
!=
Diferente de
>
Maior que
<
Menor que
>=
Maior ou igual que
<=
Menor ou igual que

Tabela 3: Operadores relacionais da linguagem C++

Esses operadores comparam dois valores. O resultado produzido por um operador relacional é zero ou um. O valor zero é interpretado como falso, e qualquer valor diferente de zero é considerado verdadeiro.

2.3.4.Operadores lógicos

Os operadores lógicos servem para combinar expressões booleanas. A Tabela 4 apresenta os operadores lógicos da linguagem C++.

Símbolo Significado

&&

Operador binário E (AND)

Símbolo Significado && Operador binário E (AND) || Operador binário OU (OR) Operador binário OU EXCLUSIVO

||

Operador binário OU (OR)

Operador binário E (AND) || Operador binário OU (OR) Operador binário OU EXCLUSIVO (XOR) ^ !

Operador binário OU EXCLUSIVO (XOR)

^
^

! Operador unário de NEGAÇÃO (NOT)

Tabela 4: Operadores lógicos da linguagem C++

Expressões conectadas por && ou || são avaliadas da esquerda para direita, e a avaliação pára assim que a veracidade ou falsidade de resultado for conhecida.

2.5. Comando de entrada e saída

Nesta Seção abordaremos apenas aquilo que é essencial para implementação dos exercícios presentes nesta apostila. Quando se tornar necessário, um detalhamento mais aprofundado será apresentado.

A entrada e saída padrão de C++ são implementadas na biblioteca iostream. Os objetos de fluxo-

padrão são: cin, cout, cerr e clog, os quais são abertos automaticamente pelo sistemas quando da inclusão desta biblioteca.

cin: corresponde à entrada padrão

cout: corresponde à saída padrão

cerr: corresponde à saída padrão de erros, sem “bufferização”

clog: corresponde à saída padrão de erros, com “bufferização”

O

operador << (operador de deslocamento para a esquerda) permite inserir valores em um fluxo de

saída. Já o operador >> (operador de deslocamento para a direita) permite extrair valores de um fluxo de entrada.

Exemplo 2.1: Elaborar um programa que efetue a apresentação do valor da conversão em dólar (US$) de um valor lido em real (R$). O programa deverá solicitar o valor da cotação do dólar e também a quantidade de reais disponível com o usuário. #include <iostream>

int main() {

float cotacaoDolar, // valor da cotação do dolar

qteReais,

// quantidade de reais disponível

qteDolares;

// quantidade de dolares

std::cout << "Informe a cotacao do dolar: "; std::cin >> cotacaoDolar; std::cout << "Informe a quantidade de reais: "; std::cin >> qteReais;

qteDolares = qteReais/cotacaoDolar;

std::cout << "Total de dolares: " << qteDolares << std::endl;

return 0;

}

O operador << pode ser usado, de forma encadeada, para inserir diversos valores em um mesmo fluxo de saída.

2.6. Exercícios

1)

Elaborar um programa que efetue a leitura de três valores inteiros (representados pelas variáveis, A, B e C) e apresente como resultado a soma, a média e o produto dessas três variáveis.

2)

Ler uma temperatura em graus Celsius e apresentá-la convertida em graus Fahrenheit. A fórmula de conversão é: F (9* C+160)/5, sendo F a temperatura em Fahrenheit e C a temperatura em Celsius.

3)

Ler dois valores para as variáveis A e B, efetuar a troca dos valores de forma que a variável A passe a possuir o valor da variável B e a variável B passe a possuir o valor da variável A. Apresentar os valores trocados.

4)

Elaborar um programa que efetue a apresentação do valor da conversão em real (R$) de um valor lido em dólar (US$). O programa deverá solicitar o valor da cotação do dólar e também a quantidade de dólares disponível com o usuário.

5) Efetuar o cálculo de quantidade de litros de combustível gastos em uma viagem, utilizando-se um automóvel que faz 12 Km por litro. Para obter o cálculo, o usuário deverá fornecer o tempo gasto e a velocidade média durante viagem. Desta forma, será possível obter a distância percorrida com a fórmula DISTÂNCIA TEMPO * VELOCIDADE. Tendo o valor da distância, basta calcular a quantidade de litros de combustível utilizada na viagem com a fórmula: LITROS_USADOS DISTÂNCIA/12. O programa deverá apresentar os valores da velocidade média, o tempo gasto na viagem, a distância percorrida e a quantidade de combustível gasto.

Em uma eleição sindical concorreram ao cargo de presidente três candidatos (A, B, C). Durante a apuração dos votos foram computados votos nulos e votos em branco, além dos votos válidos para cada candidato. Deve ser criado um programa de computador que efetue a leitura da quantidade de votos válidos para cada candidato, além de efetuar também a leitura de votos nulos e votos em branco. Ao final o programa deve apresentar o número total de eleitores, considerando votos válidos, votos nulos e em branco; o percentual correspondente de votos válidos em relação à quantidade de eleitores; o percentual correspondente de votos válidos do candidato A relação à quantidade de eleitores; o percentual correspondente de votos válidos do candidato B em relação à quantidade de eleitores; o percentual correspondente de votos válidos do candidato C em relação á quantidade de eleitores; o percentual correspondente de votos nulos em relação em relação à quantidade de eleitores; e por último o percentual correspondente de votos em branco em relação à quantidade de eleitores.

6)

Capítulo 3: Abstração de controle

Na seção anterior os programas eram formados apenas por seqüências simples de comandos. Para construção de programas mais elaborados, precisamos ter acesso a mecanismos que permitam controlar o fluxo de execução dos comandos. Por exemplo, é fundamental ter meios para tomar decisões que se baseiem em condições avaliadas em tempo de execução. Também precisamos de mecanismos para a construção de procedimentos iterativos, isto é, procedimentos que repetem a execução de uma seqüência de comandos um determinado número de vezes.

Nesta Seção, discutiremos os principais mecanismos para abstração de controle oferecidos pela linguagem C++. Ela provê as construções fundamentais de controle de fluxo necessárias para programas bem estruturados: agrupamento de comandos, tomadas de decisão (operadores lógicos condicionais), seleção de um caso entre um conjunto de casos possíveis (operador de seleção) e laços de repetição com teste de encerramento no início, no fim, e com variavél de controle.

3.1. Operador lógico condicional

O comando if é o comando básico para codificar tomada de decisão em C++. Sua sintaxe básica é ilustrada abaixo.

a) if (expr) { comando 1; comando 2;

comando n;

}

b) if (expr) { bloco de comandos 1;

} else { bloco de comandos 2;

}

Em b, se a avaliação de expr resultar em um valor diferente de 0 (isto é, se o valor for verdadeiro), o bloco de comandos 1 será executado. A inclusão do else requisita a execução do bloco de comandos 2 se

a expressão resultar no valor 0 (falso). Cada bloco de comandos deve ser delimitado por uma chave aberta

e uma fechada. No entanto, se dentro de um bloco tivermos apenas um único comando a ser executado, as chaves podem ser omitidas (a rigor, deixamos de ter um bloco).

Em C++, um else é associado ao último if que não tiver seu próprio else. Para os casos em que

a associação entre if e else não está clara, recomendo a criação explícita de blocos, mesmo contendo

um único comando. No trecho de código ilustrado abaixo, o resultado para o valor 5 seria a mensagem ‘Temperatura muito quente’. Isto é, o programa está incorreto.

#include <iostream>

int main() { int temp; std::cout << "Digite a temperatura: "; std::cin >> temp; if (temp < 30) if (temp > 20) std::cout << "Temperatura agradavel.\n";

else std::cout << "Temperatura muito quente.\n"; return 0;

}

Se reescrevermos o programa, podemos obter o efeito desejado.

#include <iostream>

int main() { int temp; std::cout << "Digite a temperatura: "; std::cin >> temp; if (temp < 30) { if (temp > 20) std::cout << "Temperatura agradavel.\n";

}

else std::cout << "Temperatura muito quente.\n"; return 0;

}

3.2.

Operador de seleção

Além da construção if-else, C++ provê um comando (switch) para selecionar um entre um conjunto de casos possíveis. Sua forma geral é:

switch (expr) { case op1:

/* comandos executados se expr == op1 */

break;

case op2:

 

/* comandos executados se expr == op2 */

break;

case opN:

 

/* comandos executados se expr == opN */

break;

default:

 

/* executados se expr for diferente de todos */

break;

}

op i deve ser um número inteiro ou um caractere. Se expr resultar no valor op i , os comandos seguintes ao caso op i são executados até encontrar um break. Se o comando break for omitido, a execução do caso continua com os comandos do caso seguinte. Se o valor de expr for diferente de todos os casos

enumerados, o bloco de comandos associado a default é executado. O bloco default normalmente é colocado por último e não é obrigatório.

3.3. Operador de repetição

Em programas computacionais, procedimentos iterativos são muito comuns, isto é, procedimentos que devem ser executados em vários passos. Como exemplo, vamos considerar o cálculo do valor do fatorial de um número inteiro não negativo.

n! = n * (n-1) * (n-2) *

*

2

*

1

|

n

>= 0,

f(0) = 1

Para calcular o fatorial de um número com um programa de computador, normalmente utilizamos um processo iterativo, em que o valor da variável varia de 1 a n, avaliando o produtório dos valores nesse intervalo.

A linguagem C++ oferece diversas construções possíveis para a realização de laços iterativos.

3.3.1. Repetição com teste no início

Na linguagem C++, o operador de repetição com teste de parada no início é o comando while. Sua forma geral é:

while (expr_de_parada) {

bloco de comandos

}

Se a avaliação da expressão de parada (expr_de_parada) resultar em verdadeiro, o bloco de comando é executado. Ao final do bloco, a expressão volta a ser avaliada e, enquanto a expressão de parada resultar em verdadeiro, o bloco de comandos é executado repetidamente. Quando a expressão de parada for avaliada em falso, o bloco de comandos deixa de ser executado, e a execução do programa prossegue com a execução dos comandos subseqüentes ao bloco.

3.3.2. Repetição com variável de controle no laço

Uma segunda forma de construção de laços em C++, mais compacta e amplamente utilizada, é o operador de repetição for. Sua forma geral é:

for (expr_inicial; expr_de_parada; expr_de_incremento) {

bloco de comandos

}

A construção com for é equivalente ao uso do while, com a ordem de avaliação das expressões ilustradas a seguir:

expr_inicial;

while (expr_de_parada) {

bloco de comandos

expr_de_incremento;

}

Isto é, a expressão inicial é avaliada uma única vez antes da execução do laço. Em seguida, a expressão de parada, que controla a execução do laço, é avaliada e, enquanto for verdadeira, o bloco de comandos é executado. Imediatamente após cada execução do bloco de comandos, a expressão de incremento é avaliada, o laço se completa e a expressão de parada volta a ser avaliada.

3.3.3. Repetição com teste no fim

Tanto a construção com while como a construção com for avaliam a expressão de parada, que caracteriza o teste de encerramento, no ínicio do laço. Assim, se essa expressão for falsa, quando for avaliada pela primeira vez, os comandos do corpo do bloco não serão executados nem uma vez.

A linguagem C++ provê um outro operador de repetição para a construção de laços cujo teste de encerramento é avaliado no final. Esse operador é o do-while, cuja forma geral é:

do{

bloco de comandos

} while (expr_de_parada);

3.4. Exercícios

1. Ler três valores para os lados de um triângulo, considerando lado como: A, B e C. Verificar se os lados fornecidos formam realmente um triângulo, e se for esta condição verdadeira, deverá se indicado qual tipo de triângulo foi formado: isósceles, escaleno ou eqüilátero.

2. Construa uma calculadora convencional que efetua as quatro operações básicas (+, -, *, /). Faça uma versão desse programa usando comando de decisão e uma versão usando comando de seleção.

3. Efetuar a leitura de três valores (variáveis A, B e C) e efetuar o cálculo da equação de segundo grau, apresentando as duas raízes, se para os valores informados for possível efetuar o referido cálculo.

4. Efetuar a leitura de três números inteiros e identificar o maior e o menor valor.

5. Efetuar a leitura de três valores (variáveis A, B e C) e apresentar os valores dispostos em ordem crescente.

6. Elabore um programa que leia um valor numérico positivo ou negativo e apresentar o valor lido como sendo um valor positivo.

7. Faça um programa que leia o ano de nascimento de uma pessoa, calcule e mostre sua idade e, também, verifique e mostre se ela já tem idade para votar (16 anos ou mais) e para conseguir a Carteira de Habilitação (18 anos ou mais).

8. Elabore um programa que calcule o que deve ser pago por um produto, considerando o preço normal de etiqueta e a escolha da condição de pagamento. Utilize os códigos da tabela a seguir para ler qual a condição de pagamento escolhida e efetuar o cálculo adequado.

Código

Condição de pagamento

1.

Á vista em dinheiro ou cheque, recebe 10% de desconto

2.

Á vista no cartão de crédito, recebe 5% de desconto

3.

Em duas vezes, preço normal da etiqueta sem juros

4.

Em três vezes, preço normal de etiqueta mais juros de 10%

9. Elabore um programa que leia 2 valores numéricos inteiros e apresente o resultado da diferença do maior valor pelo menor valor.

um algoritmo dentro deste contexto, que leia o número imaginado e os chutes, ao final mostre quantas tentativas foram necessárias para descobrir o número.

11. Construa um programa que permita fazer um levantamento do estoque de vinhos de uma adega, tendo como dados de entrada tipos de vinho, sendo: ’T’ para tinto, ’B’ para branco e ‘R’ para rosê. Apresente a porcentagem de cada tipo sobre o total geral de vinhos; a quantidade de vinhos é desconhecida.

12. Faça um programa que efetue a soma de todos os números ímpares que são múltiplos de 3 e que se encontram no conjunto dos números de 1 até 500.

13. Prepare um programa capaz de inverter um número qualquer fornecido pelo usuário. Ou seja, se o número informado pelo usuário for 123456789, o programa deve exibir o número 987654321.

14. Construa um programa que seja capaz de dar a classificação olímpica de 3 países informados. Para cada país é informado o nome, a quantidade de medalhas de ouro, prata e bronze. Considere que cada medalha de ouro tem peso 3, cada prata tem peso 2 e cada bronze, peso 1.

15. Elabore um programa que obtenha o mínimo múltiplo comum (MMC) entre dois números fornecidos.

16. Construa um programa que gere os 20 termos sucessores da série de Fibonacci a partir de 2 primeiros termos fornecidos pelo usuário.

17. Construa um programa que leia um conjunto de dados contento altura e sexo (‘M’ para masculino e ‘F’ para feminino) de 50 pessoas, e depois, calcule e escreva:

A maior e a menor altura do grupo;

A média de altura das mulheres;

O número de homens;

A diferença percentual entre os homens e as mulheres;

18. Anacleto tem 1,50 metros e cresce 2 centímetros por ano, enquanto Felisberto tem 1,10 metros e cresce 3 centímetros por ano. Construa um algoritmo que calcule e imprima quantos anos serão necessários para que Felisberto seja maior que Anacleto.

19. Apresentar o total da soma dos cem primeiros números inteiros: (1+2+3+4+5+6+7+

20. Fazer um programa que, a partir de um número de CPF informado pelo usuário, verifique se o número informado é válido ou não.

Capítulo 4: Abstração de Dados

Apesar da grande quantidade de tipos de dados primitivos vistos na Seção 2.1.1., os quais representam apenas alguns dos principais tipos primitivos da linguagem C++, em muitos casos esses tipos não são suficientes para representar toda e qualquer informação que possa surgir. Portanto, em muitas situações esses recursos de representações são escassos, o que poderia ser suprido se existissem mais tipos de dados ou, ainda melhor, se esses tipos pudessem ser criados à medida que se fizessem necessários.

4.1. Variáveis compostas homogêneas

Uma variável pode ser interpretada como um elemento, e uma estrutura de dados como um conjunto. Quando uma determinada estrutura de dados é composta de variáveis com o mesmo tipo primitivo, temos um conjunto homogêneo de dados. Podemos considerar que uma variável composta homogênea seja como uma alcatéia, e seus elementos (variáveis) sejam como lobos (que são da mesma espécie).

4.1.1. Variáveis unidimensionais

Uma matriz de uma dimensão, comumente chamada de vetor, é representada por seu nome, tamanho (dimensão) e seu tipo, tendo a seguinte sintaxe:

tipo NOME[dimensão];

Onde:

tipo:

É o tipo de dado a ser armazenado no vetor;

NOME:

É o nome atribuído ao vetor, o qual será utilizado para manipular os valores nele contidos;

dimensão:

Refere-se

ao tamanho do vetor, ou seja, à capacidade de armazenamento de dados no vetor,

em número de elementos.

Assim, a declaração int v[10];’ cria um vetor de inteiros dimensionado com 10 elementos, isto é, reserva-se um espaço de memória contínuo para armazenar 10 valores inteiros. Desta forma, se cada int ocupa 4 bytes, a declaração reserva um espaço de memória de 40 bytes. Além disso, o acesso a cada elemento do vetor é feito por meio de uma indexação da variável v. No entanto, deve-se atentar para o fato de que a indexação de um vetor em C++ é realizada apenas por números inteiros positivos. Ou seja, suponha um vetor v de tamanho N, nesse caso os índices de v variam no intervalo de 0 a N-1.

Assim, para a declaração do vetor v de 10 posições ilustrado anteriormente, a entrada v[10] resultaria na indexação de uma posição inválida do vetor, e conseqüentemente numa invazão de memória.

É importante não confundir o índice com o elemento. Índice é o endereço de alocação de uma unidade do vetor, enquanto elemento é o conteúdo armazenado em um determinado endereço.

Vetores também podem ser inicializados na declaração:

int v[] = {1, 2, 3, 4, 5};

Observe que o compilador dimensiona o vetor de acordo com o número de elementos especificados na inicialização.

4.1.2. Variáveis multidimensionais

A linguagem C++ permite a criação de vetores bidimensionais, declarados estaticamente. Esse tipo de vetor é comumente chamado de matriz e relaciona-se diretamente com problemas que envolvam estruturas de dados homogêneas, mas que podem ser organizadas em forma de tabela.

Uma matriz é declarada de forma semelhante a um vetor, sendo representada pelo seu nome, tamanho (agora definidos através de duas dimensões) e seu tipo, tendo a seguinte sintaxe:

tipo NOME[dimensão_1][dimensão_2];

Onde:

tipo:

É o tipo de dado a ser armazenado na matriz;

NOME:

É o nome atribuído à matriz, o qual será utilizado para manipular os valores nela contidos;

dimensão_1: . Refere-se ao tamanho da matriz em número de linhas.

dimensão_2: . Refere-se ao tamanho da matriz em número de colunas.

Podemos declarar uma matriz de valores reais com 4 linhas e 3 colunas da seguinte forma:

float m[4][3];

Essa declaração reserva um espaço de memória necessário para armazenar os 12 elementos da matriz, que são armazenados de maneira contínua, organizados linha a linha.

Os elementos da matriz são acessados com indexação dupla: mat[l][c]. O primeiro índice, l, acessa a linha, e o segundo, c, acessa a coluna.

Da mesma forma que os vetores, as matrizes também podem ser inicializadas na declaração:

int matriz[4][3] = {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}};

Ou ainda podemos inicializar seqüencialmente:

int matriz[4][3] = {1,2,3,4,5,6,7,8,9,10,11,12};

O número de elementos por linha pode ser omitido numa inicialização, mas o número de colunas deve ser sempre fornecido:

int matriz[][3] = {1,2,3,4,5,6,7,8,9,10,11,12};

4.1.3. Exercícios

1. Ler 8 elementos em um vetor A. Construir um vetor B da mesma dimensão contendo os elementos do vetor A multiplicados por 5.

2. Ler um vetor com 15 elementos. Construir um vetor B de mesmo tipo, sendo que cada elemento do vetor B seja o fatorial do elemento correspondente do vetor A.

3. Ler 20 elementos em uma matriz qualquer, considerando que ela tenha o tamanho de 4 linhas por 5 colunas, em seguida apresentar a matriz.

4. Ler duas matrizes A e B, cada uma de duas dimensões com 5 linhas e 3 colunas. Construir uma matriz C de mesma dimensão, que seja formada pela soma dos elementos da matriz A com os elementos da matriz B. Apresentar os elementos da matriz C.

5. Num frigorífico existem 90 bois. Cada boi traz, preso em seu pescoço, um cartão contendo o seu número de identificação (inteiro) e seu peso (real). Fazer um algoritmo que:

imprima os números e respectivos pesos dos bois mais gordos e dos bois mais magros.

imprima os números e respectivos pesos dos dois bois mais gordos e dos dois bois mais magros.

6. Um comerciante deseja fazer o levantamento do lucro das mercadorias que ele comercializa. Para isso, montou uma ficha (uma para cada mercadoria) com o nome, o preço de compra e o preço de venda das mesmas. Fazer um programa que:

Determine e escreva quantas mercadorias proporcionam:

lucro < 10% 10% <= lucro <= 20% lucro > 20%

Determine e escreva o valor total de compra e de venda de todas as mercadorias, assim como o lucro

total.

4.2. Variáveis compostas heterogêneas

Nas seções anteriores foram apresentadas técnicas de programação que envolveram o uso de tipos primitivos e de estruturas de dados homogêneas. Nesses casos, somente foi possível trabalhar com um tipo de dado nas variáveis compostas (vetores e matrizes). Essa restrição induz a utilização de múltiplas matrizes para alocar valores de diferentes tipos. Além disso, conduz o programador a adotar técnicas de programação que dificultam a legibilidade do código.

4.2.1. Registros (Struct)

Registros são estruturas de dados que encapsulam múltiplos dados de tipos diferentes em um novo tipo de dado. Por esta razão, este tipo de dado é considerado heterogêneo.

No exercício 5 da seção anterior era solicitado, para cada boi, o cadastro do seu identificador (expresso como um número inteiro) e do seu respectivo peso (expresso como um número real). Essa diversidade de tipos obrigou a utilização de dois vetores, um para armazenar o identificador do animal e outro para armazenar seu peso. Isso criou uma solução com foco na manipulação das estruturas de dados, e não nos elementos fundamentais do problema, que são os animais. Imagine como seria mais fácil agrupar os dois tipos de dados em uma mesma estrutura, a qual descreve especificamente os dados de um elemento do mundo real, nesse caso, o boi. Em programas mais complexos, os registros fornecem uma maneira mais abstrata para representar os dados.

A linguagem C++ oferece mecanismos para estruturar dados heterogêneos, nos quais as

informações são compostas por diversos campos. Essas estruturas servem, basicamente, para agrupar

diversas variáveis dentro de um único contexto. Para ilustrar, vamos considerar o desenvolvimento do exercício descrito no último parágrafo. Nesse caso, cada boi pode ser representado por dois valores, um inteiro e um real. A sintaxe para a definição de uma estrutura é esta:

struct boi { int id; float peso;

};

Dessa maneira, a estrutura boi passa a ser um tipo, e podemos então declarar variáveis desse tipo da seguinte forma:

boi b;

A linha de código acima declara b como sendo uma variável do tipo boi. Os elementos de uma

estrutura podem ser acessados usando o operador de acesso “ponto” (.). Assim é válido escrever:

b.id = 1263; b.peso = 513.84;

Manipulamos os elementos de uma estrutura da mesma forma que manipulamos variáveis simples. Podemos acessar seus valores, atribuir-lhes novos valores etc.

4.2.2. Registro de conjuntos

Vimos anteriormente que é possível alocar múltiplos tipos de dados no interior de um registro. Além disso, em C++ é possível definir um vetor, ou mesmo uma matriz, dentro de um registro, para não ser necessário utilizar somente os tipos primitivos de dados.

Vamos considerar, como exemplo, um sistema de cadastro de notas escolares. Nesse caso há a necessidade de se criar um registro denominado aluno, nos quais serão registradas as notas obtidas pelo aluno nos quatro bimestres do ano. Além disso, deve ser registrado o número de matrícula do aluno e o seu nome. O trecho de código abaixo ilustra a implementação desse registro:

struct aluno { int matricula; string nome; float nota[4];

};

Como descrito anteriormente, os campos da estrutura são acessados usando o operador de acesso “ponto”(.). E então manipulados de acordo com o contexto em que são utilizados. Assim, é válido escrever:

aluno a; a.matricula = 12345; a.nome = "Aelerson"; a.nota[0] = 12.5; a.nota[1] = 17.2; a.nota[2] = 20.0; a.nota[3] = 19.7;

4.2.3. Conjunto de registros

Com as técnicas de programação anteriormente apresentadas, passou-se a ter uma flexibilidade bastante grande, podendo-se trabalhar de uma forma mais adequada com diversos problemas, principalmente os que envolvem a utilização de dados heterogêneos, facilitando a construção de programas mais organizados. Porém, os exemplos apresentados até aqui com a utilização de registros, só fizeram menção à leitura e escrita de um único registro.

Em C++ é possível fazer a manipulação de registros como conjuntos, a partir da criação de vetores e matrizes de registros. A declaração de um conjunto de registros é feita de forma semelhante à declaração de um vetor ou matriz, onde o nome do registro é especificado na posição destinada ao nome do tipo de dado. O trecho de código abaixo ilustra a declaração de um vetor de alunos contendo 90 posições:

aluno a[90];

O acesso às posições do conjunto de registros é realizado de forma semelhante ao acesso aos

elementos de um conjunto de tipos primitivos. Assim, é válido escrever:

a[0].matricula = 12345; a[0].nome = "Aelerson"; a[0].nota[0] = 12.5; a[0].nota[1] = 17.2; a[0].nota[2] = 20.0; a[0].nota[3] = 19.7;

4.3. Outros tipos de dados

Além dos tipos de dados descritos anteriormente, a linguagem C++ possui contrutores para definição de outros tipos de dados.

4.3.1. Cadeia de caracteres (string)

Um texto é representado por uma seqüência de caracteres. A representação dessa seqüência de caracteres é de fundamental importância para o desenvolvimento de sistemas computacionais. Em qualquer tipo de cadastro é necessário informar cadeias de caracteres, como descrição de produtos, nomes de pessoas etc.

Efetivamente, a linguagem C++ não oferece um tipo caractere. Como já discutimos, os caracteres são representados internamente na memória do computador por códigos numéricos. A linguagem C++ oferece então o tipo char, que pode armazenar valores inteiros de 1 byte, 8 bits, podendo assim representar 256 (2 8 ) valores distintos. Como os códigos associados aos caracteres da tabela ASCII estão dentro desse intervalo, usamos o tipo char para representar caracteres. Embora a tabela ASCII seja a mais usada, diferentes máquinas podem usar diferentes tabelas de códigos. Por isso deve-se evitar o uso explicito de códigos referentes a uma determinada tabela, ou seja, deve-se usar “char C = ‘A’” no lugar de “char C = 65”.

Em C++, a diferença entre caracteres e inteiros está apenas na maneira como são tratados. Por exemplo, podemos imprimir o mesmo valor de duas formas diferentes, a partir de formatos diferentes. Vamos analisar o trecho de código abaixo:

unsigned char I; for(I=1; I<255; I++) std::cout << (int)I << " - " << I << "\n";

Nesse caso, a variável I, que foi inicializada com o valor 1, representa os caracteres da tabela da máquina na qual a aplicação está sendo executada. O objeto cout imprime o conteúdo da variável I em dois formatos distintos: a representação numérica do caractere – onde utiliza-se um casting para forçar a impressão numérica; o formato de caractere – onde será impresso o caractere associado ao código numérico.

terminadas,

obrigatoriamente, pelo caractere nulo (‘\0’). Portanto, para armazenar uma cadeia de caracteres, devemos reservar uma posição adicional para o caractere de fim da cadeia.

O código a seguir ilustra a representação de uma cadeia e caracteres. Como queremos representar

a palavra “CEFET”, composta por 5 caracteres, declaramos um vetor com dimensão 6 (um elemento adicional para armazenarmos o caractere nulo no final da cadeia). O código abaixo preenche os elementos

do vetor, incluindo o caractere ‘\0’, e imprime a palavra na tela.

Cadeias

de

caracteres

em

C++

são

representadas

por

vetores

do

tipo

char

int main() { char instituicao[6]; instituicao [0] = ‘C’; instituicao [1] = ‘E’; instituicao [2] = ‘F’; instituicao [3] = ‘E’; instituicao [4] = ‘T’; instituicao [5] = ‘\0’; std::cout << instituicao; return 0;

}

Se o caractere ‘\0’ não fosse colocado, o objeto cout poderia ser executado de forma errada, pois não conseguiria identificar o final da cadeia.

Como cadeias de caracteres são vetores, podemos reescrever o código anterior com a inicialização dos valores dos elementos do vetor na declaração:

char instituicao[] = {‘C’, ‘E’, ‘F’, ‘E’, ‘T’, ‘\0’};

A inicialização de cadeias de caracteres é tão comum em código C++ que a linguagem permite que

elas sejam inicializadas escrevendo-se os caracteres entre aspas duplas. Nesse caso, o caractere nulo é representado explicitamente. O código anterior pode ser reescrito da seguinte forma:

char instituicao[] = “CEFET”;

4.3.2.

Definição de “novos” tipos

A linguagem C++ permite criar nomes de tipos. Por exemplo, se escrevemos:

typedef float Real;

Podemos usar o nome Real como um mnemônico para o tipo float. O uso de typedef é muito útil para abreviar nomes de tipos e para tratar tipos complexos. Alguns exemplos válidos de typedef:

typedef int Integer; typedef char String[256];

4.3.3. Tipo pré-processado

Um código C++, antes de ser compilado, passa por um pré-processador. Esse pré-processador reconhece determinadas diretivas e altera o código para, então, enviá-lo ao compilador.

Uma diretiva muito comum, e já utilizada nos nossos exemplos, é a diretiva #include. Ela é seguida por um nome de arquivo, e o pré-processador a substitui pelo corpo do arquivo especificado. É como se o texto do arquivo incluído fizesse parte do código-fonte.

Outra diretiva de pré-processamento bastante usada é a diretiva de definição, denominada #define. Por exemplo, uma função para calcular a área de um círculo pode ser escrita da seguinte forma:

#define PI 3.14159F int main() { float area, r; cin >> r; AREA = PI * r * r; cout << "Area: " << area; return 0;

}

Nesse caso, antes da compilação, toda ocorrência da palavra PI (desde que não esteja envolvida em aspas) será trocada pelo número 3.14159F. O uso de diretivas de difinição para representar constantes simbólicas é fortemente recomendável, pois facilita a manutenção e acrescenta clareza ao código.

4.3.4. Tipo enumeração

Uma enumeração é um conjunto de constantes inteiras com nomes que especifica os valores possíveis para uma variável daquele tipo. É uma forma mais elegante de organizar valores constantes. Como exemplo, consideremos a criação de um tipo booleano. Para isso, podemos enumerar os valores que um determinado tipo pode assumir. Isso pode ser feito da seguinte forma:

enum bool { TRUE = 1, FALSE = 0

};

A declaração de uma variável do tipo criado pode ser realizada por:

bool resultado; resultado = FALSE;

4.3.5. Exercícios

1. Num frigorífico existem no máximo 90 bois. Cada boi traz, preso em seu pescoço, um cartão contendo o seu nome, número de identificação (inteiro) e seu peso (real). Fazer um algoritmo que:

imprima dados do boi mais gordo e do boi mais magro.

imprima a soma dos pesos dos bois.

Obs:

-> somente uma função poderá ser executada por vez, e a escolha do usuário. -> somente o usuário poderá encerrar a aplicação

2. Um comerciante deseja fazer o levantamento do lucro das mercadorias que ele comercializa. Para isso, montou uma ficha (uma para cada mercadoria) com o nome, o preço de compra e o preço de venda das mesmas. Fazer um programa que:

Determine e escreva quantas mercadorias proporcionam:

lucro < 10%

10% <= lucro <= 20%

lucro > 20%

Determine e escreva o valor total de compra e de venda de todas as mercadorias, assim como o lucro total.

Capítulo 5: Modularização de algoritmos

Para a construção de programas estruturados, é sempre preferível dividir as grandes tarefas de computação em tarefas menores e utilizar seus resultados parciais para compor o resultado final desejado. Na linguagem C++, a criação de funções é o mecanismo adequado para codificar tarefas específicas. Um programa estruturado em C++ deve ser composto por diversas funções pequenas. Essa estratégia de codificação traz dois grandes benefícios: primeiro, facilita a codificação, pois codificar diversas funções pequenas, que resolvem problemas específicos, é mais fácil do que codificar uma única função maior; segundo, funções específicas podem ser facilmente reutilizadas em outros códigos. De fato, a criação de funções pode evitar a repetição de código, de modo que um procedimento repetido deve ser transformado em uma função que, então, será chamada diversas vezes.

É importante destacar que as funções são independentes entre si. As variáveis locais definidas dentro do corpo de uma função (incluído os parâmetros das funções) não existem fora dela. Cada vez que a função é executada, as variáveis locais são criadas, e, quando sua execução termina, as variáveis deixam de existir.

5.1. Declaração de funções

A forma geral para definir uma função é:

tipo_retornado nome_da_função ( lista de parâmetros

corpo da função

}

)

{

5.2. Passagem de parâmetros por valor

A passagem de parâmetro por valor ocorre quando é feita uma cópia dos valores contidos nas variáveis, ou valores constantes, para os parâmetros formais do método chamado. Dessa forma, qualquer modificação que ocorra nas variáveis locais ao método invocado não afeta o valor de qualquer variável usada como parâmetro desse método, ou seja, o processamento é executado somente dentro do método, ficando o resultado obtido “preso” dentro da subrotina.

Para ilustrar a criação de funções, consideremos o cálculo do fatorial de um número inteiro. Podemos escrever uma função que, dado um determinado número inteiro não negativo n, imprime o valor de seu fatorial.

// função que calcula e retorna o valor do fatorial int fatorial(int n) { int i; int f = 1; for(i = n; i > 1; i--)

}

f

=

f * i;

return f;

int main( ) { int n, f; cin >> n; f = fatorial(n); cout << “Fatorial de “ << n << “ eh “ << f; getch(); return 0;

}

Uma função pode ou não ter um valor de retorno associado. Para ilustrar a possibilidade de uma função não retornar um valor, vamos reescrever o código anterior da seguinte forma.

// função que calcula e retorna o valor do fatorial void fatorial(int n) { int i; int f = 1; for(i = n; i > 1; i--)

f

=

f *

i;

cout << “Fatorial de “ << n << “ eh “ << f;

}

int main( ) { int n; cin >> n; fatorial(n); getch(); return 0;

}

Observe que, nesse caso, a função fatorial não retorna valor algum.

5.3. Passagem de parâmetros por referência

Conforme ilustrado anteriormente, uma função pode retornar um tipo de valor. No entanto, a possibilidade de retornar um valor nem sempre é satisfatória. Muitas vezes, precisamos retornar mais do que um resultado para o ponto de execução que chama uma função, e isso não pode ser feito com o retorno explícito de valores, pois dessa forma é possível retornar apenas um valor.

A linguagem C++ permite o armazenamento e a manipulação de valores de endereços de memória. Para cada tipo existente, há um tipo ponteiro capaz de armazenar endereços de memória em que existem valores do tipo correspodente armazenados.

A linguagem C não reserva uma palavra especial para a declaração de ponteiros; usamos a mesma palavra do tipo com os nomes das variáveis precedidos pelo caractere *. Então, podemos escrever:

int *p;

Nesse caso, declaramos uma variável de nome p que pode armazenar endereços de memória em que existe um inteiro armazenado.

Para atribuir e acessar endereços de memória, a linguagem C oferece dois operadores unários. O operador uninário & (“endereço de”), aplicado a variáveis, resulta no endereço da posição da memória reservada para a variável. O operador unário * (“conteúdo de”), aplicado a variáveis do tipo ponteiro, acessa o conteúdo do endereço de memória armazenado pela variável ponteiro. Para exemplificar, observe o trecho de código abaixo:

int a = 15; int *b = &a; printf("%d\n", a); printf("%d\n", &a); printf("%d\n", *&a); printf("%d\n", b); printf("%d\n", &b); printf("%d\n", *&b);

Os valores impressos serão:

15

endereço da variável a

15

endereço da variável a

endereço da variável b endereço da variável a

Podemos utilizar ponteiros para alterar valores de variáveis ao acessá-las indiretamente. Para tanto, se passarmos para uma função os valores dos endereços de memória em que suas variáveis estão armazenadas, essa função pode alterar, indiretamente, os valores das variáveis situadas no ponto de execução que realizou a chamada à função.

Para ilustrar, vamos considerar agora uma função para realizar a troca de valores entre duas variáveis. Assim, para que os valores possam ser trocados precisamos passar os endereços das variáveis para a função. O código a seguir ilustra essa implementação.

// função que troca os valores entre duas variáveis void troca(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp;

}

int main( ) { int a = 5, b=7; printf(“Antes da troca: x=%d ; y=%d\n”, a, b); troca(&a, &b); printf(“Depois da troca: x=%d ; y=%d\n”, a, b); getch(); return 0;

}

5.4. Variáveis globais

Outra forma de fazer a comunicação entre funções é através de variáveis globais. As variáveis globais são declaradas fora do corpo das funções. Uma variável global é visível a todas as funções subseqüentes. Assim, qualquer uma dessas funções pode acessar e/ou alterar o valor da variável diretamente.

As variáveis globais não são armazenadas na pilha de execução, portanto não deixam de existir quando a execução de uma função termina; elas existem enquanto o programa estive sendo executado.

5.5. Exercícios

1. Implemente uma função para testar se um número inteiro é primo ou não. Se o valor retornado pela função for 1, então o número é primo, se o valor for 0, então o número não é primo.

2. Implemente uma função que retorne a soma dos n primeiros números naturais ímpares.

3. Implemente uma função que calcule as raízes de uma equação do segundo grau, do tipo ax 2 + bx + c =

0.

Essa função deve ter como valor de retorno o número de raízes reais e distintas da equação. Se existirem raízes reais. Além disso, deve ser possível a impressão das raízes da equação a partir do fluxo de execução principal.

4. Implemente uma função que calcule a área da superfície e o volume de uma esfera de raio r.

A área da superfície e o volume são dados, respectivamente, por 4r 2 e 4r 3 /3.

5. Crie uma função capaz de inverter um número qualquer fornecido pelo usuário. Ou seja, se o número informado pelo usuário for 123456789, o programa deve exibir o número 987654321.

6. Elabore uma função que obtenha o mínimo múltiplo comum (MMC) entre dois números fornecidos.

7. Crie uma função que, a partir de um número informado pelo usuário, efetue o cálculo do dígito verificador usando o algoritmo Módulo 10.

8. Faça uma função que calcule o Máximo Divisor Comum entre dois números.

Referências:

CELES, W. e CERQUEIRA, R. e RANGEL, J. L. Introdução a estruturas de dados. 1ª ed. Rio de Janeiro. Campus, 2004.

SINTES, Anthony. Aprenda Programação Orientada a Objetos: em 21 dias. São Paulo: Pearson Education do Brasil. 2002.

DEITEL, H. M.; DEITEL, P. J. C++: como programar. São Paulo: Pearson Prentice-Hall, 2006.