Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Apostila C++, Notas de estudo de Engenharia Informática

apostila básica de c++, contendo estruturas da linguagem e explicações.

Tipologia: Notas de estudo

2010

Compartilhado em 06/05/2010

marcilio-oliveira-9
marcilio-oliveira-9 🇧🇷

4 documentos

1 / 108

Documentos relacionados


Pré-visualização parcial do texto

Baixe Apostila C++ e outras Notas de estudo em PDF para Engenharia Informática, somente na Docsity! Apontamentos de Programação em C/C++ Paulo Baltarejo e Jorge Santos Instituto Superior de Engenharia do Porto Departamento de Engenharia Informática Versão Draft – Março de 2006 Aviso de licença de utilização: Este documento pode ser utilizado livremente para fins não comerciais, é permitido aos seus utilizadores, copiar, distribuir e exibir publicamente os seus conteúdos, desde que sejam ressalvados os direitos de autor do mesmo, nomeadamente, deverá ser sempre incluída esta página em todas as cópias. Paulo Baltarejo e Jorge Santos, 2006 i 1.6.7.4 Converter entre escalas de temperaturas . . . . . . . 25 1.6.7.5 Calcular índice de massa corpórea (IMC) . . . . . . . 26 1.6.7.6 Determinar ano bissexto . . . . . . . . . . . . . . . . 26 1.6.7.7 Parque de estacionamento . . . . . . . . . . . . . . . 26 1.6.8 Instruções de Repetição . . . . . . . . . . . . . . . . . . . . . 27 1.6.8.1 Instrução do-while . . . . . . . . . . . . . . . . . . 27 1.6.8.2 Instrução while . . . . . . . . . . . . . . . . . . . . 28 1.6.8.3 Instrução for . . . . . . . . . . . . . . . . . . . . . . 28 1.6.9 Exercícios Resolvidos . . . . . . . . . . . . . . . . . . . . . . . 30 1.6.9.1 Calcular somatório entre dois limites . . . . . . . . . 30 1.6.9.2 Calcular factorial de um número . . . . . . . . . . . 31 1.6.9.3 Determinar se um número é primo . . . . . . . . . . 32 1.6.9.4 Determinar número e idade da pessoa mais nova de um grupo . . . . . . . . . . . . . . . . . . . . . . . . 32 1.6.9.5 Determinar o aluno melhor classificado e a média das notas de uma turma . . . . . . . . . . . . . . . . . . 33 1.6.10 Exercícios Propostos . . . . . . . . . . . . . . . . . . . . . . . 34 1.6.10.1 Divisão através de subtracções sucessivas . . . . . . . 34 1.6.10.2 Determinar o máximo e mínimo de uma série . . . . 35 1.6.10.3 Determinar quantidade de números primos . . . . . . 35 1.6.10.4 Determinar se um número é perfeito . . . . . . . . . 35 1.6.10.5 Calcular potência por multiplicações sucessivas . . . 35 1.6.10.6 Maior número ímpar de uma sequência de valores . . 35 1.6.10.7 Algarismos de um número . . . . . . . . . . . . . . . 35 1.6.10.8 Apresentação gráfica de temperaturas . . . . . . . . 35 1.6.10.9 Soma dos algarismo de um número . . . . . . . . . . 36 1.6.10.10 Jogo de adivinhar o número . . . . . . . . . . . . . . 36 1.6.10.11 Capicua de um número . . . . . . . . . . . . . . . . 36 1.6.10.12 Conversão de base numérica . . . . . . . . . . . . . . 36 1.7 Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 1.7.1 Âmbito da variáveis – global e local . . . . . . . . . . . . . . . 38 1.7.2 Passagem de argumentos . . . . . . . . . . . . . . . . . . . . . 41 1.7.2.1 Passagem por valor . . . . . . . . . . . . . . . . . . . 41 1.7.2.2 Passagem por referência . . . . . . . . . . . . . . . . 42 1.7.2.3 Valores por omissão nos argumentos . . . . . . . . . 43 1.7.3 Protótipos de funções . . . . . . . . . . . . . . . . . . . . . . . 44 1.7.4 Estrutura de um programa em C++ . . . . . . . . . . . . . . 45 1.7.5 Exercícios resolvidos . . . . . . . . . . . . . . . . . . . . . . . 46 1.7.5.1 Função que devolve o maior algarismo de um número 46 1.7.5.2 Função que indica se um número é perfeito . . . . . . 46 1.7.6 Exercícios propostos . . . . . . . . . . . . . . . . . . . . . . . 47 1.7.6.1 Função média de dois números . . . . . . . . . . . . 47 1.7.6.2 Função lei de Ohm . . . . . . . . . . . . . . . . . . . 47 1.7.6.3 Função somatório . . . . . . . . . . . . . . . . . . . . 48 1.7.6.4 Funções para codificar e descodificar números . . . . 48 1.7.6.5 Números primos . . . . . . . . . . . . . . . . . . . . 48 1.8 Vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 1.8.1 Definição de vectores . . . . . . . . . . . . . . . . . . . . . . . 49 iv 1.8.2 Atribuição dos valores iniciais . . . . . . . . . . . . . . . . . . 49 1.8.3 Acesso aos elementos de um vector . . . . . . . . . . . . . . . 50 1.8.4 Exercícios resolvidos . . . . . . . . . . . . . . . . . . . . . . . 51 1.8.4.1 Funções manipulando vectores . . . . . . . . . . . . . 51 1.8.5 Exercícios propostos . . . . . . . . . . . . . . . . . . . . . . . 52 1.8.5.1 Determinar desvio padrão de uma série . . . . . . . . 52 1.8.5.2 Prova de atletismo . . . . . . . . . . . . . . . . . . . 52 1.8.5.3 Suavização . . . . . . . . . . . . . . . . . . . . . . . 53 1.9 Vectores multi–dimensionais . . . . . . . . . . . . . . . . . . . . . . . 53 1.9.1 Exercícios resolvidos . . . . . . . . . . . . . . . . . . . . . . . 55 1.9.1.1 Funções manipulação de matrizes . . . . . . . . . . . 55 1.9.2 Exercícios propostos . . . . . . . . . . . . . . . . . . . . . . . 58 1.9.2.1 Máximo local . . . . . . . . . . . . . . . . . . . . . . 58 1.9.2.2 Determinar se uma matriz é simétrica . . . . . . . . 58 1.10 Vectores como parâmetros . . . . . . . . . . . . . . . . . . . . . . . . 58 1.11 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 1.11.1 Iniciação de strings . . . . . . . . . . . . . . . . . . . . . . . . 60 1.11.2 Funções para manipulação de strings . . . . . . . . . . . . . . 61 1.11.3 Conversão de strings para outros tipos . . . . . . . . . . . . . 62 1.11.4 Exercícios resolvidos . . . . . . . . . . . . . . . . . . . . . . . 63 1.11.4.1 Programa para manipulação de strings e caracteres . 63 1.11.5 Exercícios propostos . . . . . . . . . . . . . . . . . . . . . . . 68 1.11.5.1 Função que determine o número de ocorrências . . . 68 1.11.5.2 Função que verifique se uma string é inversa de outra 68 1.11.5.3 Função que conta as palavras de uma string . . . . . 68 1.11.5.4 Função que formate uma string . . . . . . . . . . . . 68 1.12 Ponteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 1.12.1 Operador endereço & . . . . . . . . . . . . . . . . . . . . . . . 68 1.12.2 Operador de referência * . . . . . . . . . . . . . . . . . . . . . 69 1.12.3 Ponteiros e vectores . . . . . . . . . . . . . . . . . . . . . . . . 72 1.12.4 Ponteiros para ponteiros . . . . . . . . . . . . . . . . . . . . . 72 1.12.5 Ponteiros do tipo void . . . . . . . . . . . . . . . . . . . . . . 73 1.13 Tipos de dados não nativos . . . . . . . . . . . . . . . . . . . . . . . 74 1.13.1 Estruturas de dados – instrução struct . . . . . . . . . . . . 74 1.13.2 Definição de tipos – instrução typedef . . . . . . . . . . . . . 79 1.13.3 União – instrução union . . . . . . . . . . . . . . . . . . . . . 79 1.13.4 Enumeradores – instrução enum . . . . . . . . . . . . . . . . . 81 1.13.5 Exercícios resolvidos . . . . . . . . . . . . . . . . . . . . . . . 83 1.13.5.1 Ponto e recta . . . . . . . . . . . . . . . . . . . . . . 83 1.13.5.2 Gestão de clientes de uma discoteca . . . . . . . . . 85 1.13.6 Exercícios propostos . . . . . . . . . . . . . . . . . . . . . . . 86 1.13.6.1 Empresa de construção civil . . . . . . . . . . . . . 86 1.13.6.2 Empresa de construção civil . . . . . . . . . . . . . 86 1.14 Programas de grandes dimensões . . . . . . . . . . . . . . . . . . . . 90 1.14.1 Divisão em módulos . . . . . . . . . . . . . . . . . . . . . . . 91 v vi Lista de Tabelas 1.1 Palavras reservadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2 Tipos de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.3 Caracteres especiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.4 Exemplo operadores compostos . . . . . . . . . . . . . . . . . . . . . 12 1.5 Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.6 Operadores aritméticos compostos . . . . . . . . . . . . . . . . . . . . 13 1.7 Operadores de incremento (modo prefixo e sufixo) . . . . . . . . . . . 14 1.8 Operadores relacionais . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.9 Exemplos de operadores relacionais . . . . . . . . . . . . . . . . . . . 14 1.10 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.11 Exemplos de operadores lógicos . . . . . . . . . . . . . . . . . . . . . 15 1.12 Tabelas de verdade: conjunção, disjunção e negação . . . . . . . . . . 15 1.13 Operadores de manipulação de bits . . . . . . . . . . . . . . . . . . . 16 1.14 Tabela de verdade dos operadores de manipulação bits . . . . . . . . 16 1.15 Relação de precedência dos operadores . . . . . . . . . . . . . . . . . 22 1.16 Índice de massa corpórea . . . . . . . . . . . . . . . . . . . . . . . . 26 ix x Resumo Estes apontamentos têm como objectivo principal apoiar os alunos que pretendam aprender programação de computadores utilizando a linguagem C++, em particular aqueles que frequentam a disciplina de Introdução à Informática do Ano 0 leccionada no Instituto Superior de Engenharia do Porto (ISEP). A estrutura destes apontamentos foi definida de acordo com a abordagem de aprender-por-exemplo, pelo que, os conceitos são apenas introduzidos de acordo com a necessidade de explicar a resolução de um determinado programa. De forma a suportar esta abordagem é apresentado um grande número de exercícios resolvidos. Porto, Janeiro de 2006 Jorge Santos e Paulo Baltarejo xi ISEP/DEI - Jorge Santos e Paulo Baltarejo Listing 1.1: Programa clássico - Bom dia mundo 1 // o meu primeiro programa em C++ 2 #inc lude<iostream . h> 3 int main ( ) 4 { 5 cout<<"Bom dia Mundo ! " ; 6 return 0 ; 7 } Este programa depois compilado e executado produziria o seguinte resultado: Bom dia Mundo! Apesar do resultado simples, o programa contém um conjunto de elementos que merecem um análise detalhada. • //O meu primeiro programa em C++ Isto é uma linha de comentário. Todas as linhas começadas por duas barras // são consideradas comentários e não tem qualquer efeito na compilação/exe- cução do programa. Servem para inserir explicações e observações no código do programa; • #include <iostream.h> As linhas que começam pelo carácter cardinal (#) são directivas de pré-compilação. Estas não são linhas de código executável mas apenas indicações para o com- pilador. Neste caso #include <iostream.h> indica ao pré–processador do compilador que inclua os cabeçalhos existentes no ficheiro iostream.h relati- vos a funções utilizadas na entrada e saída de dados de um programa; • int main() Esta linha corresponde ao início da declaração da função main. A função main é o ponto por onde todos os programas em C++ começam a sua execução. main é seguido de parêntesis ((, )) porque é uma função. O conteúdo da função main que está imediatamente a seguir à declaração formal, está contido entre chavetas ({ }) conforme o exemplo; • cout < < "Bom dia Mundo!"; Esta instrução coloca no ecrã a frase "Olá Mundo!". cout (console output) é um objecto normalmente associado ecrã. cout está declarado no ficheiro de cabeçalhos (header file) iostream.h, portanto para poder usar o cout é neces- sário incluir o ficheiro iostream.h. Note–se que esta frase termina com ponto vírgula (;). Este carácter significa o fim da instrução e tem obrigatoriamente que ser incluído depois de qualquer instrução; • return 0; A instrução return provoca o fim da execução da função main e (devolve) retorna o que está a seguir, neste caso o zero (0). 2 Apontamentos de Programação C/C++ 1.3 Criação de um programa em C++ O desenvolvimento de programas em linguagem C++, tal como na maioria das linguagens compiladas, é um processo que compreende quatro fases: escrita, compi- lação, "linking" e execução (ver figura 1.1). Escrita do código-fonte ficheiro código-fonte .cpp ou .cc ficheiro objecto .obj ou .o ficheiro executável .exe Compilação Execução“Link ing” ficheiros de cabeçalhos .h bibliotecas .lib Erros de compilação Erros de ”linking” Erros de execução Fluxo dados Dependência funcional Figura 1.1: Ciclo de vida de um programa em C Normalmente, os ambiente de desenvolvimento integrado de aplicações (IDE1) incluem um conjunto de ferramentas que suportam as referidas fases do desenvolvi- mento de um programa/aplicação. • Escrita do código–fonte – A primeira fase do processo é criação/edição de um (ou mais) ficheiro de texto contendo o código–fonte. Isto pode ser realizado com recurso a um editor de texto. O referido ficheiro tem que ter a extensão ".cpp" ou ".cc". O conteúdo do programa tem que obedecer rigorosamente à sintaxe da linguagem. • Compilação – A segunda fase, a da compilação, é realizada com recurso a um compilador específico para linguagem, neste caso o de C++. Nesta fase se existirem erros de sintaxe, o compilador detecta-os e reportará a sua localização (tipicamente número de linha) e uma breve descrição do erro. Note-se que os erros de lógica não são detectados nesta fase. Se o programa não tiver erros de sintaxe o compilador produzirá o código executável, isto é, um programa pronto a ser executado. Nesta fase são incluídos os ficheiros de cabeçalhos nos quais são declaradas as funções que serem posteriormente incluídas no fase de linking. • "Linking" – A terceira fase, a da linking, é feita com recurso a um programa especial, designado linker que se responsabiliza por transformar os programa objecto (independente da plataforma de hardware) numa aplicação executável na plataforma em hardware específica. Nesta fase, as declarações das funções (cabeçalhos) são substituídas por código executável, propriamente dito. Caso ocorram erros nesta fase, o processo retorna à primeira fase. • Execução – A quarta e última fase, execução, só poderá ocorrer no caso das fases terminarem com sucesso. 1Do anglo-saxónico Integrated Environment Development 3 ISEP/DEI - Jorge Santos e Paulo Baltarejo 1.4 Modelo de compilação da linguagem C++ A figura 1.2 apresenta o modelo de compilação da linguagem C++. Pré-processador ficheiro código-fonte (alterado) .cpp ou .cc código “assembly” ficheiro objecto .obj ou .o Compilador “Linker”“Assembler” ficheiros de cabeçalhos .h bibliotecas .lib ficheiro código-fonte .cpp ou .cc Erros de compilação Erros de compilação Figura 1.2: Modelo de compilação No modelo de compilação da linguagem C++ são quatro os intervenientes prin- cipais: pré-processador, compilador, assembler e linker . • Pré–processador – O pré-processador executa as directivas existentes no código fonte. As directivas de pré-processamento começam pelo carácter # e de entre as tarefas mais comuns compreendidas na pré–compilação, destacam- se: – #include – Inclusão de ficheiros, tipicamente ficheiros de cabeçalhos; – #define – definição de constantes (ver secção 1.5.4.2); – Remoção de linhas de comentários. • Compilador – Alguns compiladores traduzem o código fonte (texto) recebido do pré–processamento para linguagem assembly (também texto). Enquanto que outros geram directamente o código objecto (instruções do processador em código binário); • Assembler – O assembler traduz código em linguagem assembly (texto) para código binário. Pode estar integrado no compilador. O código gerado pelo assembler geralmente tem a extensão ".o" (nos sistemas operativos Unix e Linux) e ".obj" (nos sistema operativos Windows); • Linker – Se o programa fonte tem referências a elementos definidos nas bi- bliotecas padrão ou outras, o Linker é o responsável por adicionar o código desses elementos de forma a criar o ficheiro executável. A referência a variáveis globais externas é resolvida pelo Linker . 1.5 Variáveis, Tipos de dados e Constantes O programa OlaMundo (ver listagem 1.1) apesar de estruturalmente completo, tem apenas como funcionalidade, escrever na consola de saída, tipicamente o ecrã, a frase "Ola Mundo!". 4 Apontamentos de Programação C/C++ Tabela 1.2: Tipos de dados Nome Tam. Descrição Gama de valores char 1 Caracter ou inteiro de 8 bits de comprimento c/sinal: -128 a 127 e s/sinal: 0 a 255 short 2 Inteiro de 16 bits de compri- mento c/sinal: -32768 a 32767 e s/sinal: 0 a 65635 long 4 Inteiro de 32 bits de compri- mento c/sinal: -2147483648 a 2147483647 e s/sinal: 0 a 42949967295 int ∗ ver short e long float 4 Número real 3.4e±38 double 8 Número real, virgula flutu- ante, dupla precisão 1.7e±308 long double 10 Número real longo, virgula flutuante, dupla precisão 1.2e±4932 bool 1 Valores lógicos - booleanos true e false wchar_t 2 Caracter extendido, necessá- rio para a representação de caracteres internacionais Caracteres (incluindo in- ternacionais) 4 long int sa ldo ; 5 f loat media ; 6 double r a i z ; 7 bool s e n t i n e l a ; 8 char t e c l a ; 9 . . . 10 11 // Declaração de v a r i á v e i s 12 // mais do que uma v a r i á v e l por l i nha 13 int x , y , z ; 14 f loat area , per imetro ; 15 . . . 16 17 // Declaração de v a r i á v e i s 18 // mais do que uma v a r i á v e l por l i nha 19 // e com in i c i a ç õ e s 20 bool ePrimo=fa l se ; 21 int nDiv=0,produto=1; 22 long double numero=1.0 , f a c t o r i a l =1.0 ; 23 char _1letra=’ a ’ , _2letra=’b ’ ; 24 } No excerto são apresentados três blocos que pretendem ilustrar: • No primeiro bloco, a declaração de uma variável por linha de código; • No segundo bloco, múltiplas declarações por linha; • No terceiro bloco, para além da declaração múltipla por linha, a iniciação de variáveis. 7 ISEP/DEI - Jorge Santos e Paulo Baltarejo Conforme se verifica no excerto, no caso de se declarar mais do que uma variável por instrução, estas têm estar separadas por virgula. Quando uma variável é declarada, o seu valor é indeterminado por omissão, isto é, não se sabe qual é o valor. Por vezes é útil iniciar uma variável com um determinado valor aquando da sua declaração. Para tal acrescenta-se o sinal de igual e o valor com que se quer inicializar a variável. Formalmente a sintaxe subjacente à declaração/iniciação de uma ou mais variá- veis é: <tipo-de-dados> <id1>=<Valor inicial1>, <id2>=<Valor inicial2>,...; sendo que os parêntesis rectos representam o caracter opcional, neste caso, as inici- ações de variáveis são opcionais. 1.5.4 Constantes Uma constante é qualquer identificador que tem um valor fixo, portanto que não pode ser alterada durante a execução do programa. Num programa em C++ podem coexitir três tipos de constantes: literais, definidas e declaradas. 1.5.4.1 Literais Um literal é uma qualquer identificador com o valor fixo e podem ser divididos em: números inteiros, números reais, caracteres e cadeias de caracteres3 • Números inteiros - são constantes numéricas que identificam números na base decimal. Note-se que para expressar uma constante numérica não é ne- cessário escrever aspas(") ou qualquer outro caracter especial. 1776 707 -203 Quando é escrito 1776 num programa está-se a referir ao valor numérico 1776 (mil setecentos e setenta e seis). Além da base decimal é possível utilizar a notação octal ou hexadecimal. Para a notação octal precede-se o numero do caracter 0 (zero) e para hexadecimal precede-se o número dos caracteres 0x(zero e ’x’). De seguida são apresentadas várias formas de representar o mesmo valor, neste caso o 75 (setenta e cinco). Valor Notação 75 decimal 0113 octal 0x4b hexadecimal • Numeros Reais Expressam números com décimas e/ou expoente. Podem incluir um ponto decimal, o caracter e (que expressa "10 elevado a X", onde X é um valor inteiro) ou ambos. 3Do anglo-saxónico String . 8 Apontamentos de Programação C/C++ Notacão Valor 3.14159 3.14159 6.022e23 6.022 ∗ 1023 1.6e-19 1.6 ∗ 10−19 1.41421356237309504 √ 2 3.0 3.0 Estas são cinco formas válidas de representar números reais em C++. Res- pectivamente, o valor de π, o número de Avogadro, a carga eléctrica de um electrão (extremamente pequena), raiz de dois e o numero três expresso como decimal; • Caracteres e strings Também existem constantes não numéricas, como a seguir se exemplifica: ’z’ ’p’ "Olá Mundo!" "Como estás?" As primeiras duas expressões representam caracteres simples enquanto as ou- tras duas representam strings (cadeia de caracteres). Para os caracteres sim- ples é usada a plica (’) e para as strings são usados as aspas ("). Existem caracteres especiais que não podem ser expressos sem ser no código fonte do programa, como por exemplo, nova linha (’\n’) ou tabulação (’\t’). Todos são precedidos pela barra "\". A tabela 1.3 seguinte apresenta a lista dos caracteres especiais. Tabela 1.3: Caracteres especiais Caracter Significado \n nova linha \r cursor para 1ªcoluna \t tabulação \b backspace \’ plica simples \" aspas simples 1.5.4.2 Definidas Para além dos literais existem constantes que podem ser declaradas pelo programa- dor, podendo ser definidas através de uma directiva de pré-processamento #define ou através da palavra–chave const (ver secção 1.5.4.3). A seguir apresenta-se a forma como definir uma constante através da directiva de pré–processamento #define e cuja a sintaxe é a seguinte: #define <identificador> <expressão> 9 ISEP/DEI - Jorge Santos e Paulo Baltarejo • cin > > valorEuro; – Esta instrução fica aguardar que o utilizador digite (via teclado) o valor em euros. O valor digitado é extraído para a variável valorEuro através do operador de extracção > >. O cin (console input) é um objecto que está normalmente associado ao teclado. O operador de extracção extraí do objecto que representa o teclado na aplicação para uma variável da aplicação; • #include<iostream.h> – A directiva de pré-processador promove a inclusão a definição dos dois objectos associados quer a entrada de dados (cin) quer à saída de dados (cout); • valorDolar=valorEuro*taxa; – Esta linha de código realiza duas operações, a operação de multiplicação valorEuro*taxa; e a operação de atribuição do cálculo à variável valorDolar; • cout < < "Valor em dolar = " < < valorDolar < < endl; – Esta linha de código coloca no ecrã da nossa aplicação o texto "Valor em dolar = "concate- nado com o conteúdo da variável valorDolar seguido do manipulador de saída de dados endl que coloca o caracter ’\n’ (nova linha) e obriga o objecto cout a escrever no ecrã. 1.6.1.1 Operadores aritméticos O operador de atribuição serve para atribuir o resultado de uma expressão a uma variável. Conforme exemplo seguinte: a=5; A linha de código anterior atribuí o valor 5 à variável a. A parte esquerda do operador = é conhecida como lvalue (left value) e a parte direita como rvalue (lado direito). rvalue é uma expressão (e.g., uma variável, uma constante, uma operação ou qualquer combinação destes elementos) enquanto que o lvalue é o nome de uma variável; A operação de atribuição é realizada da direita para a esquerda e nunca o inverso. De seguida são apresentados alguns exemplos (ver tabela 1.4): Tabela 1.4: Exemplo operadores compostos Exemplo Resultado a=10; O valor da variável c será 10b=4; c=a; a=10; O valor da variável a será 60 ( 4*(10+5) )b=4; c=5; a=b*(c + a); b=c=a=0; O valor das variáveis a, b e c será 0 A linguagem C++ define os seguintes operadores aritméticos simples (ver tabela 1.5). Na exemplificação dos operadores considerem-se as variáveis a e b com valores, 13 e 5, respectivamente. 12 Apontamentos de Programação C/C++ Tabela 1.5: Operadores aritméticos Operador Nome Exemplo Resultado + soma a+b 18 - subtracção a-b 8 * multiplicação a*b 65 / divisão a/b 2.6 % resto da divisão inteira a%b 5 O C++ fornece igualmente os seguintes operadores aritméticos compostos (ver tabela 1.6). Na exemplificação dos operadores considerem-se as variáveis a e b com valores, 13 e 5, respectivamente. Tabela 1.6: Operadores aritméticos compostos Operador Nome Exemplo Significado += soma/atribuição a+=b a=a+b -= subtração/atribuição a-=b a=a-b *= multiplicação/atribuição a*=b a=a*b /= divisão/atribuição a/=b a=a/b %= resto divisão inteira/atribuição a%=b a=a%b ++ incremento a++ a=a+1 −− decremento b−− b=b-1 Note-se que os operadores aritméticos compostos da linguagem de programação C++ permitem modificar o valor de uma variável com um operador básico. Os operadores incremento (++) e decremento (−−) só podem ser usados com variáveis. Estes incrementam e decrementam o valor da variável em uma unidade. Portanto, (++) e (−−) são equivalentes a +=1 e a -=1, respectivamente. Uma característica destes operadores é que podem ser usado como prefixo (pré– incremento ou pré–decremento) ou como sufixo (pos–incremento ou pos-decremento). Para tal, o operador tem de ser escrito antes da variável (++a) ou depois da (a++;), prefixo e sufixo, respectivamente. Embora quando usadas em expressões simples tipo (++a;) ou (a++;) tenham o mesmo significado. Em operações na quais o resultado da operação de incremento ou de decremento é avaliada noutra expressão, podem ser diferentes. No caso do operador de incremento usado como prefixo (++a;) o valor é incrementado e depois a expressão é avaliada e portanto o valor incrementado é considerado na expressão. No caso operador de incremento ser usado como sufixo (a++;), o valor da variável é incrementado após a avaliação da expressão. A seguir apresentam-se dois exemplos para explicitar melhor as diferenças entre os dois modos: 1.6.1.2 Operadores relacionais e lógicos Nesta secção são apresentados os operadores relacionais e lógicos utilizados na lin- guagem C++, bem como exemplos da sua utilização. 13 ISEP/DEI - Jorge Santos e Paulo Baltarejo Tabela 1.7: Operadores de incremento (modo prefixo e sufixo) Exemplo Resultado b=3; O valor final de a será 4 e o de b também a=++b; b=3; O valor final de a será 3 e o de b 4 a=b++; Operadores relacionais A tabela 1.8 apresenta os operadores relacionais da linguagem C++. Os operadores relacionais avaliam a comparação entre duas expressões. O resultado dessa compa- ração é um valor do tipo bool que pode ser true ou false, obviamente de acordo com o resultado da comparação. Tabela 1.8: Operadores relacionais Símbolo Significado < menor que > maior que ≤ menor ou igual que ≥ maior ou igual que == igual ! = diferente Na tabela 1.9 apresenta-se alguns exemplos da utilização dos operadores relaci- onais nos quais se consideram os valores a=5; b=6; c=7;. Tabela 1.9: Exemplos de operadores relacionais Exemplo Resultado (7==5) falso (a!=b) verdade (a<=7) verdade ((a*b)>=c) verdade Operadores lógicos A tabela 1.10 apresenta os operadores lógicos da linguagem C++ O resultado das operações com os operadores lógicos também é verdade ou falso. O operador !, colocado à esquerda da expressão, inverte o valor lógico da mesma. Isto é, se a expressão é verdadeira passa a falsa e vice–versa. A tabela 1.11 apresenta alguns exemplos da utilização dos operadores lógicos. De seguida são apresentadas as tabelas de verdades das operações lógicas: con- junção, disjunção e negação (tabela 1.12). 14 Apontamentos de Programação C/C++ 7 double d i s t a n c i a ; 8 cout<<"Coordenadas ponto1 (x/y ) : " ; 9 cin>>x1>>y1 ; 10 cout<<"Coordenadas ponto2 (x/y ) : " ; 11 cin>>x2>>y2 ; 12 13 d i s t a n c i a=sq r t (pow( ( x2−x1 ) ,2)+pow( ( y2−y1 ) , 2 ) ) ; 14 15 cout<<"Dis tanc ia="<<di s t anc i a <<endl ; 16 return 0 ; 17 } 1.6.2.2 Determinar perímetro e área de circunferência O programa da listagem 1.7 permite determinar o perímetro e área de uma circun- ferência, a partir do valor do raio. Listing 1.7: Área e perímetro de um circunferência 1 #include <iostream . h> 2 #include <math . h> 3 const double pi =3.1415; 4 int main ( ) 5 { 6 double area , per imetro ; 7 int r a i o ; 8 9 cout<<" Introduza o va lo r do r a i o : " ; 10 cin>>ra i o ; 11 area=pi ∗pow( ra io , 2 ) ; 12 per imetro=2 ∗ pi ∗ r a i o ; 13 cout<<"Area : "<<area<<endl ; 14 cout<<"Perimetro : "<<perimetro<<endl ; 15 16 return 0 ; 17 } 1.6.3 Exercícios Propostos Nesta secção são propostos alguns problemas com vista à aplicação conjugada de instruções sequenciais. 1.6.3.1 Calcular índice de massa corpórea (IMC) O índice de massa corpórea (IMC) de um indivíduo é obtido dividindo-se o seu peso (em Kg) por sua altura (em m) ao quadrado. Assim, por exemplo, uma pessoa de 1,67m e pesando 55kg tem IMC igual a 20,14, já que: IMC = peso altura2 = 55kg 1, 67m ∗ 1, 67m = 20, 14 17 ISEP/DEI - Jorge Santos e Paulo Baltarejo Escreva um programa que solicite ao utilizador o fornecimento do seu peso em kg e de sua altura em m e a partir deles calcule o índice de massa corpórea do utilizador. 1.6.3.2 Converter horas, minutos e segundos Escreva um programa que a partir de um determinado número de segundos cal- cula o número de horas, minutos e segundos correspondentes. Conforme o seguinte exemplo: 8053s = 2h + 14m + 13s 1.6.3.3 Teorema de Pitágoras Escreva um programa para determinar a hipotenusa de um triângulo rectângulo, dados os catetos. 1.6.3.4 Converter temperaturas Escreva um programa que a partir de uma temperatura expressa em graus Fahrenheit (tempF), calcule a temperatura expressa em graus Celsius (tempC). A conversão pode ser realizada de acordo com a fórmula 1.6.2. tempF = 32 + 9 ∗ tempC 5 (1.6.2) 1.6.4 Instruções de Decisão As instruções de decisão, ou selecção, permitem a selecção em alternância de um ou outro conjunto de acções após a avaliação lógica de uma condição. 1.6.4.1 Decisão binária A decisão binária permite bifurcar a execução de um programa em dois fluxos dis- tintos, para tal é utilizada instrução if. Esta instrução pode ser utilizada de duas formas: if e if-else. No primeiro caso, se a condição for verdadeira é executado o bloco-instruções caso contrário nada acontece: if (<condição>) { <bloco-instruções> } No caso do <bloco-instruções> ser constituído por uma só instruções não é necessário delimitar essa instrução por chavetas ({ e }). A listagem 1.8 apresenta um programa codificado em C++, cujo objectivo é escrever no ecrã que o aluno foi aprovado no caso da nota ser superior a 9.5 valores. 18 Apontamentos de Programação C/C++ Listing 1.8: Estrutura de decisão – if 1 #inc lude<iostream . h> 2 int main ( ) 3 { 4 double nota ; 5 cout<<" Introduza nota : " ; 6 cin>>nota ; 7 i f ( nota >=9.5) 8 cout<<"O aluno f o i aprovado" ; 9 10 return 0 ; 11 } No segundo caso, se a condição for verdadeira é executado o bloco-instruções 1 senão é executado o bloco-instruções 2: if (<condição>) { <bloco-instruções 1> } else { <bloco-instruções 2> } Considere-se o programa em C++ presente na listagem 1.9. Neste programa são lidas as medidas dos lados de uma figura rectangular, sendo que no caso particular de os dois lados serem iguais estamos na presença de um quadrado. Em qualquer um dos casos é apresentada a mensagem correspondente assim como o valor da área. Listing 1.9: Exemplo de utilização da instrução if-else 1 #include<iostream . h> 2 int main ( ) 3 { 4 int lado1 , lado2 , area ; 5 cout<<" Introduza medidas dos lados : " ; 6 cin>>lado1>>lado2 ; 7 area=lado1 ∗ lado2 ; 8 i f ( lado1==lado2 ) 9 cout<<"Area do quadrado= "<<area ; 10 else 11 cout<<"Area do rec tangu lo= "<<area ; 12 13 return 0 ; 14 } 1.6.4.2 Decisão múltipla A instrução de de decisão múltipla é um caso particular de instruções if-else encadeadas. 19 ISEP/DEI - Jorge Santos e Paulo Baltarejo 1.15 apresenta a relação dos principais operadores da linguagem C++. Note-se que alguns dos operadores referidos na tabela serão posteriormente apresentados. Tabela 1.15: Relação de precedência dos operadores Prior. Operador Descrição Sentido 1 (, ), [, ], ->, ., sizeof −→ 2 ++, - - incremento/decremento ←− ˜ complemento para um ←− ! negação ←− & , * referência/ponteiro ←− (tipo de dados) conversão de tipos de dados ←− +, - sinal ←− 3 *, /, % operadores aritméticos −→ 4 +, - operadores aritméticos −→ 5 < <, > > operador de deslocamento (bit a bit) −→ 6 <, <=, >, >= operadores relacionais −→ 7 ==, != operadores relacionais −→ 8 &, ˆ, | operadores de manipulação de bits −→ 9 &&, || operadores lógicos −→ 10 ?, : condicional ←− 11 =, +=, -=, *=, /=, %=, > >=, < <=, &=, ˆ=, |= atribuição ←− 12 , vírgula, separador −→ No entanto pode-se resolver algumas dúvidas em relação à precedência e sequên- cia de avaliação com a utilização de parêntesis((,)). 1.6.6 Exercícios Resolvidos Nesta secção são apresentados alguns problemas e respectivas soluções com o objec- tivo de ilustrar a utilização de instruções de decisão. 1.6.6.1 Distância euclidiana entre dois pontos O programa da listagem 1.12 permite realizar o cálculo da distância euclidiana entre dois pontos, sendo que cada ponto é definido pelas coordenadas (x,y). no cálculo da distância pode ser utilizada a fórmula 1.6.3. distância = √ (x2 − x1)2 + (y2 − y1)2 (1.6.3) Caso os pontos sejam coincidentes mostra mensagem "Pontos Coincidentes". Listing 1.12: Distância euclidiana entre dois pontos 1 #include<iostream . h> 2 #include<math . h> 3 22 Apontamentos de Programação C/C++ 4 int main ( ) 5 { 6 int x1 , y1 , x2 , y2 ; 7 double d i s t a n c i a ; 8 cout<<"Coordenadas ponto1 (x/y ) : " ; 9 cin>>x1>>y1 ; 10 cout<<"Coordenadas ponto2 (x/y ) : " ; 11 cin>>x2>>y2 ; 12 13 d i s t a n c i a=sq r t (pow( ( x2−x1 ) ,2)+pow( ( y2−y1 ) , 2 ) ) ; 14 15 i f ( ( int ) d i s t a n c i a==0) 16 cout<<"Os pontos sao c o i n c i d en t e s "<<endl ; 17 else 18 cout<<"Dis tanc ia="<<d i s t anc i a <<endl ; 19 return 0 ; 20 } 1.6.6.2 Classificar em função da média O programa da listagem 1.13 permite ler as notas de um aluno às disciplinas de Matemática, Português, Inglês e Geografia e calcular a média. Em função da média mostra uma mensagem com o conteúdo "Aprovado" ou "Reprovado". Consideram-se notas positivas as notas iguais ou superiores a 9,5. Listing 1.13: Calcular a média 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int mat , por , ing , geo ; 6 double media ; 7 cout<<" Introduza as notas (mat/por/ ing /geo ) : " ; 8 cin>>mat>>por>>ing>>geo ; 9 media=(double ) (mat+por+ing+geo ) /4 ; 10 11 i f (media>=9.5) 12 cout<<"Aprovado"<<endl ; 13 else 14 cout<<"Reprovado"<<endl ; 15 return 0 ; 16 } 1.6.6.3 Determinar o máximo de 3 valores O programa da listagem 1.14 permite determinar o maior de três números. Listing 1.14: Máximo de três valores 1 #include<iostream . h> 2 3 int main ( ) 23 ISEP/DEI - Jorge Santos e Paulo Baltarejo 4 { 5 int A,B,C,maximo ; 6 cout<<" Introduza numero1 , numero2 , numero3 : " ; 7 cin>>A>>B>>C; 8 i f (A>=B) 9 { 10 i f (A>=C) 11 maximo=A; 12 } 13 else 14 { 15 i f (B>=C) 16 maximo=B; 17 else 18 maximo=C; 19 } 20 cout<<"O numero maior e : "<<maximo ; 21 return 0 ; 22 } Sugestão: Baseando-se nas soluções propostas escreva um programa que permita a determinação do máximo entre 5 números. Qual é a solução mais elegante? 1.6.6.4 Determinar triângulo válido O programa da listagem 1.15 permite ler três pontos geométricos e determinar se estes formam um triângulo. Pode ser utilizada a fórmula da distância entre dois pontos para calcular as medidas dos lados do triângulo. Note-se que um triângulo só é válido se a medida de cada um dos seus lados é menor que a soma dos lados restantes. Listing 1.15: Triângulo válido 1 #include<iostream . h> 2 #include<math . h> 3 4 int main ( ) 5 { 6 int x1 , y1 , x2 , y2 , x3 , y3 ; 7 double lado1 , lado2 , lado3 ; 8 cout<<"Coordenadas ponto1 (x/y ) : " ; 9 cin>>x1>>y1 ; 10 cout<<"Coordenadas ponto2 (x/y ) : " ; 11 cin>>x2>>y2 ; 12 cout<<"Coordenadas ponto3 (x/y ) : " ; 13 cin>>x3>>y3 ; 14 15 16 lado1=sq r t (pow( ( x2−x1 ) ,2)+pow( ( y2−y1 ) , 2 ) ) ; 17 lado2=sq r t (pow( ( x3−x1 ) ,2)+pow( ( y3−y1 ) , 2 ) ) ; 18 lado3=sq r t (pow( ( x3−x2 ) ,2)+pow( ( y3−y2 ) , 2 ) ) ; 19 20 i f ( lado1 <( lado2+lado3 ) 24 Apontamentos de Programação C/C++ 1.6.8 Instruções de Repetição As instruções de repetição, ou ciclos, permitem a execução, de forma repetitiva, de um conjunto de instruções. Esta execução depende do valor lógico de uma condição que é testada em cada iteração para decidir se a execução do ciclo continua ou termina. A linguagem de programação C++ compreende três estruturas distintas de con- trolo do tipo cíclico: do-while, while e for. 1.6.8.1 Instrução do-while O ciclo do-while é um ciclo condicional que executa a iteração enquanto uma con- dição for verdadeira. Esta condição é avaliada no fim. A seguir apresenta-se a sintaxe da estrutura do-while. O controlo do ciclo processa-se da seguinte forma. O <bloco-instruções> é sempre executado pelo menos uma vez de pois é avaliada a <condição>. Caso o resultado da avaliação seja verdade o ciclo continua até que o resultado da avaliação seja falso. Portanto quando o resultado da avaliação da condição for falso o ciclo termina. do { <bloco-instruções> } while (<condição>); Considere-se o seguinte exemplo em cuja utilização da estrutura do-while per- mite garantir que o valor da nota introduzida está situado entre 0 e 20. O programa da listagem 1.16 apresenta uma solução para o problema anterior utilizando a estrutura do-while. Neste programa o ciclo é executado uma vez e caso o valor introduzido pelo utilizador (armazenado na variável nota) seja inferior a 0 ou superior a 20 a ciclo continua até que o valor da nota seja válido, isto é, entre 0 e 20. Listing 1.16: Exemplo da instrução do-while 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int nota ; 6 do 7 { 8 cout<<" In t r oduz i r nota ent r e 0−20: " ; 9 cin>>nota ; 10 } 11 while ( nota<0 | | nota >20); 12 13 return 0 ; 14 } 27 ISEP/DEI - Jorge Santos e Paulo Baltarejo 1.6.8.2 Instrução while O ciclo while é também um ciclo condicional que realiza a iteração enquanto uma determinada condição for verdadeira. A condição é avaliada no inicio da iteração. Por isso, o ciclo while pode não realizar nenhuma iteração. A seguir apresenta–se a sintaxe da estrutura while. O controlo do ciclo processa–se da seguinte forma. A avaliação da condição é feita no inicio e caso o resultado da seja verdade então o <bloco-instruções> é executado. No caso do resultado ser falso então o ciclo termina. while (<condição>) { <bloco-instruções> } Considere-se o seguinte exemplo em cuja utilização da estrutura while permite calcular e escrever a tabuada de um número. A seguir apresenta–se uma possível codificação (1.17) em C++ do problema anterior. Neste exemplo o ciclo while vai fazer 10 iterações, uma vez que a variável i é iniciada a 1, em cada iteração esta é incrementada em um valor e o ciclo só termina quando a variável i for >10. Listing 1.17: Exemplo da instrução while 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int numero , r e su l tado , i ; 6 cout<<" Introduza o numero : " ; 7 cin>>numero ; 8 i =1; 9 while ( i <=10) 10 { 11 r e su l t ado=numero ∗ i ; 12 cout<<numero<<" ∗ "<<i<<" = "<<resu l tado<<endl ; ; 13 i++; 14 } 15 16 return 0 ; 17 } 1.6.8.3 Instrução for O ciclo for é bastante versátil porque aceita tanto iterações fixas como condicionais. Esta instrução é constituída por três elementos (todos opcionais). O primeiro ele- mento é a iniciação das variáveis de controlo das iterações. A iniciação só acontece na primeira iteração. O segundo elemento é a condição de controlo das iterações. Uma iteração só acontece se a condição for verdadeira. A condição é sempre avaliada. O terceiro elemento é usado para actualizar as variáveis de controlo do ciclo. Este elemento não é executado na primeira iteração, só nas subsequentes. Um aspecto muito importante é que a avaliação acontece sempre depois dos outros elementos. 28 Apontamentos de Programação C/C++ Na primeira iteração, as variáveis são iniciadas e só depois é avaliada a condição. Nas iterações subsequentes as variáveis são actualizadas e só depois é feita a avalia- ção. Obviamente, a iteração só acontece se a condição for verdadeira. Embora não seja muito frequente, pode acontecer que nenhuma iteração seja efectuada. for(<iniciação>;<condição>;<actualização>) { <bloco-instruções> } Considere–se o seguinte exemplo em cuja utilização da instrução for permite calcular a soma os 100 primeiros números inteiros. Neste exemplo é introduzido um conceito importante para a programação, o conceito de acumulador. A variável soma em cada iteração é adicionada do valor da variável i, permitindo que no final: soma = 1 + 2 + 3 + 4 + 5 + ... + 100 = 5050 Por outro lado, a instrução i++; faz com que a variável i tome todos os valores inteiros de 1 a 100. Listing 1.18: Exemplo da instrução for 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int soma=0, i ; 6 for ( i =1; i <=100; i++) 7 soma+=i ; 8 cout<<soma ; 9 return 0 ; 10 } Instrução break A instrução break permite sair de um ciclo mesmo que a condição ainda seja ver- dadeira. O código 1.19 apresenta um exemplo de um ciclo que é interrompido pela instrução break. O ciclo começa com a variável n =10 e enquanto n for >0 vai exe- cutar o bloco de instruções. De cada iteração a variável n é decrementada numa unidade. O bloco de instruções tem uma estrutura de decisão que quando o n for igual a 3 vai escrever uma mensagem no ecrã e executar a instrução break. O que significa que o ciclo vai terminar quando a variável n é 3, portanto maior que 0. Listing 1.19: Exemplo da instrução break 1 #include <iostream . h> 2 3 int main ( ) 4 { 5 int n ; 6 for (n=10; n>0; n−−) 7 { 8 cout << n << " , " ; 29 ISEP/DEI - Jorge Santos e Paulo Baltarejo 1.6.9.3 Determinar se um número é primo Um número é primo se for apenas divisível por si próprio e pela unidade, por exem- plo: 11 é número primo (visto que é apenas divisível por 11 e por 1), enquanto que 21 não é primo, pois tem os seguintes divisores: 1,3,7 e 21. O programa da listagem 1.23 permite determinar se um número é ou não primo. Listing 1.23: Determinar se um número é primo 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int i , numero ; 6 bool primo=true ; 7 cout<<" Introduza numero : " ; 8 cin>>numero ; 9 i =2; 10 while ( primo==true && i<=(numero /2)) 11 { 12 i f ( numero%i==0) 13 primo=fa l se ; 14 i++; 15 } 16 i f ( ! primo ) 17 cout<<"O numero "<<numero<<" nao e primo"<<endl ; 18 else 19 cout<<"O numero "<<numero<<" e primo"<<endl ; 20 21 return 0 ; 22 } 1.6.9.4 Determinar número e idade da pessoa mais nova de um grupo O programa da listagem 1.24 permite ler o número e a idade de uma série de pessoas. Este programa deve terminar quando for introduzido o número da pessoa = 0. No final deve ser mostrado o número e idade da pessoa mais nova. Listing 1.24: Pessoa mais nova 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int numero , idade , menorNumero=0,menorIdade=0; 6 bool pr ime i ro=true ; 7 do 8 { 9 cout<<" Introduza numero : " ; 10 cin>>numero ; 11 i f ( numero>0) 12 { 13 cout<<" Introduza a idade : " ; 32 Apontamentos de Programação C/C++ 14 cin>>idade ; 15 i f ( ! p r ime i ro ) 16 { 17 i f ( idade<menorIdade ) 18 { 19 menorNumero=numero ; 20 menorIdade=idade ; 21 } 22 } 23 else 24 { 25 menorNumero=numero ; 26 menorIdade=idade ; 27 pr ime i ro=fa l se ; 28 } 29 } 30 } 31 while ( numero !=0) ; 32 33 i f ( pr ime i ro ) 34 cout<<"Nao foram i n s e r i d o s e lementos "<<endl ; 35 else 36 { 37 cout<<"O numero e idade da pessoa mais nova : " ; 38 cout<<menorNumero<<" , "<<menorIdade<<endl ; 39 } 40 return 0 ; 41 } 1.6.9.5 Determinar o aluno melhor classificado e a média das notas de uma turma O programa da listagem 1.25 permite ler as notas de português obtidas pelos ele- mentos de uma turma. Este programa termina quando for introduzido o numero do aluno 0. No final deve ser mostrado o número do aluno melhor classificado e a média de notas de turma. Listing 1.25: Melhor aluno e média das notas 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int numero , nota , melhorNumero=0,melhorNota=0; 6 int somaNotas=0,numAlunos=0; 7 bool pr ime i ro=true ; 8 double media=0.0; 9 do{ 10 cout<<" Introduza numero : " ; 11 cin>>numero ; 12 i f ( numero>0) 13 { 33 ISEP/DEI - Jorge Santos e Paulo Baltarejo 14 numAlunos++; 15 do 16 { 17 cout<<" Introduza a nota : " ; 18 cin>>nota ; 19 } 20 while ( nota<0 | | nota >20); 21 22 somaNotas+=nota ; 23 24 i f ( ! p r ime i ro ) 25 { 26 i f ( nota>melhorNota ) 27 { 28 melhorNumero=numero ; 29 melhorNota=nota ; 30 } 31 } 32 else 33 { 34 melhorNumero=numero ; 35 melhorNota=nota ; 36 pr ime i ro=fa l se ; 37 } 38 } 39 } 40 while ( numero !=0) ; 41 i f ( numAlunos>0) 42 { 43 media=(double ) somaNotas/numAlunos ; 44 cout<<"Numero do melhor aluno : "<<melhorNumero<<endl ; 45 cout<<"Nota do melhor aluno : "<<melhorNota<<endl ; 46 cout<<"Media das notas : "<<media<<endl ; 47 } 48 else 49 cout<<"Nao foram i n s e r i d o s notas "<<endl ; 50 51 return 0 ; 52 } 1.6.10 Exercícios Propostos Nesta secção são propostos alguns problemas com vista à aplicação dos diferentes tipos de instruções anteriormente introduzidas com particular ênfase na instruções cíclicas. 1.6.10.1 Divisão através de subtracções sucessivas O resultado da divisão inteira de um número inteiro por outro número inteiro pode sempre ser obtido utilizando–se apenas o operador de subtracção. Assim, se quiser- 34 Apontamentos de Programação C/C++ <bloco-de-instruções> } na qual: • <tipo-de-dados> – este é o tipo de dados devolvido pela função; • <id-da-função> – este é o identificador pela qual a função é conhecida. As regras para definição de um identificador são as mesmas que para as variáveis; • <argumento 1>, <argumento 2>,... – estes são os argumentos da função (o número de argumentos é variável, pode ser zero). Cada argumento consiste num tipo de dados seguido do identificador pelo qual esse argumento vai ser identificado dentro da função (por exemplo int x). Um argumento é como uma declaração de uma variável dentro da função. Os argumentos permitem passar parâmetros para dentro de uma função quando esta é invocada. Os diferentes parâmetros tem que ser separados pelo operador vírgula (’,’); • <bloco de instruções> – é o corpo da função e tem que estar sempre deli- mitado por chavetas({}). É constituído por zero ou mais instruções. A listagem 1.26 apresenta um exemplo de uma função. Para examinar este código interessa lembrar que um programa em C++ começa a sua execução pela função main. Listing 1.26: Exemplo da utilização de funções 1 #include <iostream . h> 2 3 int soma ( int n1 , int n2 ) 4 { 5 int r ; 6 r=n1+n2 ; 7 8 return r ; 9 } 10 11 int main ( ) 12 { 13 int a , b , z ; 14 a=5; 15 b=3; 16 z = soma (a , b ) ; 17 cout << "Resultado : " << z ; 18 19 return 0 ; 20 } Na função main é declarada uma variável do tipo int com o identificador z. A seguir é chamada a função soma. Os parâmetros tem uma correspondência clara com os argumentos. Na função main é invocada a função soma passando–lhe dois valores 5 e o 3 que na função soma correspondem às variáveis n1 e n2. Quando a função soma é invocada o fluxo de execução sai função main e passa para a função 37 ISEP/DEI - Jorge Santos e Paulo Baltarejo int main () { int z; z = soma(5,3); cout << "Resultado: " << z; return 0; } int soma (int n1, int n2) { int r; r=n1+n2; return r; } Figura 1.3: Execução de uma função soma (ver figura 1.3). Os valores são copiados para as variáveis int n1 e int n2 da função soma . Na função soma é definida a variável r do tipo int à qual é atribuído o resultado da operação a mais b. Neste caso como a variável a tem o valor 5 e a variável b tem o valor 3 o resultado é 8. Portanto a variável r tem o valor 8. A seguinte linha return r; termina a função soma e retorna o controlo do fluxo de execução à função main, que recomeça onde foi interrompida pela chamada à função soma. Além disso, a instrução return retorna o valor de r. Este valor retornado pela função soma é atribuído à variável z; Uma função não tem obrigatoriamente que ter argumentos e pode não retornar nada. Para os casos em que a função não retorna nenhum valor o tipo da função é void, neste caso a função é também designada de procedimento. A seguir (1.27) apresenta–se uma função que não tem argumentos nem retorna qualquer valor. Listing 1.27: Exemplo de uma função void 1 #include <iostream . h> 2 3 void imprime (void ) 4 { 5 cout<<" I s t o e uma funcao ! " ; 6 } 7 8 int main ( ) 9 { 10 imprime ( ) ; 11 return 0 ; 12 } Apesar da função imprime não receber nenhum parâmetros é necessário colocar os parêntesis ((,)). 1.7.1 Âmbito da variáveis – global e local Em relação ao âmbito de uma variável, a linguagem de programação C++ permite dois tipos de variáveis, as variáveis globais e as variáveis locais. Uma variável global é definida fora de qualquer função, inclusive da função main. As variáveis locais 38 Apontamentos de Programação C/C++ são definidas dentro de uma função. Quando uma variável é definida como global significa que está disponível em qualquer parte do programa. Estar disponível sig- nifica que em qualquer ponto do programa é possível manipular (ler e escrever) esta variável. Pelo contrário uma variável local só está disponível dentro da função onde está definida. No exemplo da listagem 1.28 é definida uma variável global do tipo int e com o identificador a e iniciada com o valor 0. Listing 1.28: Variáveis locais e globais 1 #include <iostream . h> 2 // v a r i a v e l g l o b a l 3 int a=0; 4 5 void f 1 ( int d) 6 { 7 // v a r i a v e l l o c a l 8 int x ; 9 // l e i t u r a da v a r i a v e l a 10 x=a+3; 11 // e s c r e v e r na v a r i a v e l a 12 a=d+x ; 13 } 14 int main ( ) 15 { 16 // v a r i a v e l l o c a l 17 int z=5; 18 cout<<"Valor de a= "<<a<<endl ; 19 f 1 ( z ) ; 20 cout<<"Valor de a= "<<a<<endl ; 21 return 0 ; 22 } Na função main é definida uma variável local (int z=5;). A instrução cout< <"Valor de a=« <a< <endl; imprime no ecrã a seguinte frase: Valor de a= 0 Na qual 0 refere o valor da variável a. A variável a não está definida na função main logo para que se possa aceder tem que ser uma variável global. Na chamada à função f1 na função main é passado o valor da variável local z, que é 5. Portanto o parâmetro d (a variável d na função f1) é iniciado com o valor 5. Além da variável int d na função f1 é definida outra variável (x do tipo int). À variável x da função f1 é atribuído o valor da variável a. Esta variável não está definida na função f1, mas como é global pode ser acedida para leitura e também para escrita. O que acontece na instrução a=d+x; que altera o valor da variável a para 5. Assim que a função f1 terminar é executada a instrução cout< <"Valor de a= « <a< <endl; que imprime no ecrã: Valor de a=5 Uma variável global "vive"enquanto o programa estiver em execução. Uma va- riável local "vive"enquanto a função onde está definida estiver a ser executada. 39 ISEP/DEI - Jorge Santos e Paulo Baltarejo int soma( int n1 , int n2 ) int main { int a,b,z; a=5; b=3; z=soma( a , b ); } 35 Figura 1.6: Passagem de variáveis por valor Daí que, qualquer alteração à variáveis n1 e n2 na função soma não tem qualquer significado nas variáveis a e b na função main. Listing 1.30: Variáveis locais e globais 1 #include <iostream . h> 2 3 int soma ( int n1 , int n2 ) 4 { 5 int r ; 6 r=n1+n2 ; 7 8 return r ; 9 } 10 11 int main ( ) 12 { 13 int a , b , z ; 14 a=5; 15 b=3; 16 z = soma (a , b ) ; 17 cout << "Resultado : " << z ; 18 19 return 0 ; 20 } 1.7.2.2 Passagem por referência Existem situações em que é necessário alterar o valor de uma variável dentro de uma função que foi definida noutra função. Para este caso a variável tem que ser passada por referência. O programa da listagem 1.31 apresenta a função divisaoInteira que tem quatro argumentos, dois passados por valor (int dividendo e int divisor) e dois por referência (int &quociente e int &resto). A sintaxe da linguagem C++ obriga a que os argumentos passados por referência são identificados por um & entre o tipo de dados e o identificador da variável. Listing 1.31: Passagem por referência 1 #include<iostream . h> 2 42 Apontamentos de Programação C/C++ 3 void d i v i s a o I n t e i r a ( int dividendo , int d i v i s o r , int &quociente , int &re s t o ) 4 { 5 quoc i ente =0; 6 while ( dividendo>=d i v i s o r ) 7 { 8 div idendo=dividendo−d i v i s o r ; 9 quoc i ente=quoc i ente +1; 10 } 11 r e s t o=div idendo ; 12 } 13 14 int main ( ) 15 { 16 int D=23, d=5, q , r ; 17 18 d i v i s a o I n t e i r a (D, d , q , r ) ; 19 20 cout<<"O quoc . da div . i n t e i r a . de "<<D<<" por "<<d<<" e "<<q<<endl ; 21 cout<<"O re s . da div . i n t e i r a de "<<D<<" por "<<d<<" e "<<r<<endl ; 22 23 return 0 ; 24 } Desta forma as variáveis q e r definidas na função main que dentro da função divisaoInteira são quociente e resto, respectivamente. Isto significa que qual- quer alteração dentro da função divisaoInteira nas variáveis quociente ou resto altera o valor das variáveis q e r da função main (listagem 1.7). void divisaoInteira(int dividendo , int divisor , int &quociente , int &resto ) 523 int main() { int D,d,q,r; D=23; d=5; divisaoInteira( D , d , q , r ) } endereço de q endereço de r Figura 1.7: Passagem de variáveis por referência Esta é uma forma de fazer uma função retornar mais do que um valor. 1.7.2.3 Valores por omissão nos argumentos Quando se declara uma função pode-se especificar valores para cada um dos parâ- metros. Para tal basta atribuir valores com o operador = na declaração da função. O programa da listagem 1.32 apresenta a função divide, cujo o segundo argumento tem um valor por omissão. Significa isto que a função pode ser chamada de duas formas, com um parâmetro ou com dois. Com um serve para iniciar o argumento a, sendo que o b está iniciado por omissão. Com dois inicia os dois argumentos. 43 ISEP/DEI - Jorge Santos e Paulo Baltarejo Listing 1.32: Passagem por referência 1 #include <iostream . h> 2 3 int d iv id e ( int a , int b=2) 4 { 5 int r ; 6 r=a/b ; 7 return r ; 8 } 9 int main ( ) 10 { 11 cout<<d iv ide (12)<<endl ; 12 cout<<d iv ide (20 ,4)<< endl ; 13 return 0 ; 14 } Deste modo a saída deste programa é 6 e 5. A chamada divide(12) tem como retorno o 6, resultado da operação 12/6. A chamada (20,4) tem como resultado 5 (20/4). 1.7.3 Protótipos de funções Nos programas apresentados anteriormente as funções aparecem definidas antes da primeira chamada a essa função. A estrutura usada foi então definir as funções antes da função main, portanto esta aparece no fim do ficheiro de código. Mas caso, as funções aparecessem depois da função main os programas apresentados anteriormente dariam erro de compilação. No exemplo apresentado no programa da listagem 1.33 o compilador indicaria que não conhecia o identificador da "divide". Isto porque no processo de compilação é feita uma analise sequencial e neste caso quando o compilador encontra o identificador "divide" não sabe o que é que ele é. Listing 1.33: Erro de compilação 1 #include <iostream . h> 2 3 int main ( ) 4 { 5 int x ; 6 x=d iv ide ( 2 0 , 5 ) ; 7 return 0 ; 8 } 9 10 int d iv id e ( int a , int b=2) 11 { 12 int r ; 13 r=a/b ; 14 return r ; 15 } A solução para este problema consiste em definir os protótipos das funções antes da sua chamada. No protótipo da função é necessário referir qual o tipo ou tipos de dados dos argumentos (respeitando a ordem) e qual o tipo de dados de retorno além do identificador da função. A forma para definir um protótipo é a seguinte: 44 Apontamentos de Programação C/C++ 7, 14, cuja soma é exactamente 28. (Os seguintes números são perfeitos: 6, 28, 496, 8128.) A listagem do programa da listagem 1.36 apresenta uma função que recebe um número inteiro e devolva os valores booleanos true ou false se o número é ou não perfeito, respectivamente. Listing 1.36: perfeito(N) que indica se um número é perfeito 1 #include<iostream . h> 2 3 bool p e r f e i t o ( int n) 4 { 5 int soma=0,x ; 6 for ( x=1;x<=(n /2 ) ; x++) 7 { 8 i f (n%x==0) 9 soma+=x ; 10 } 11 i f ( soma==n) 12 return true ; 13 return fa l se ; 14 } 15 16 int main ( ) 17 { 18 int numero ; 19 cout<<" Introduza a numero= " ; 20 cin>>numero ; 21 22 i f ( p e r f e i t o ( numero ) ) 23 cout<<"O numero "<<numero<<" e p e r f e i t o "<<endl ; 24 else 25 cout<<"O numero "<<numero<<" nao e p e r f e i t o "<<endl ; 26 27 return 0 ; 28 } 1.7.6 Exercícios propostos Nesta secção são propostos alguns problemas relacionados com a utilização de pro- cedimentos e funções na escritas de programas modulares. 1.7.6.1 Função média de dois números Escreva uma função que, dados dois números reais, retorna a média deles arredon- dada para um inteiro, e devolve os números por ordem crescente. Faça um programa que permita testar a função anterior. 1.7.6.2 Função lei de Ohm A lei de Ohm é uma relação entre a corrente (I), a tensão (V) e a resistência (R), de acordo com o circuito eléctrico representado na figura 1.9. 47 ISEP/DEI - Jorge Santos e Paulo Baltarejo R I à V Lei de Ohm: I = V/R Figura 1.9: Ilustração da lei de Ohm a) Escreva uma função que recebe os valores de V e R como parâmetros, e calcule a corrente I . b) Escreva um programa que permita testar a função anterior. 1.7.6.3 Função somatório Calcular o somatório n∑ i=1 2i√ i Sugestão: crie uma função para determinar cada termo i da série. 1.7.6.4 Funções para codificar e descodificar números Uma empresa pretende enviar cifrada uma sequência de inteiros decimais de 4 dígitos (DigDigDigDig). A cifra consiste em: substituir cada dígito Dig por (Dig+8)%10 (i.e., adiciona 8 e calcula o resto da divisão do resultado por 10); depois troca o terceiro dígito com o primeiro e troca o quarto dígito com o segundo. a) Escreva uma função que receba um inteiro decimal de 4 dígitos e o devolva cifrado. b) Escreva uma função que receba um inteiro cifrado e o decifre para o valor original. c) Escreva uma função que apresente um «menu» com 2 opções, cifrar e decifrar número, peça ao utilizador para escolher uma das opções, e retorne a opção escolhida. d) Faça um programa que permita testar as funções anteriores. 1.7.6.5 Números primos Escreva um procedimento que imprima os números primos existentes entre dois números. Na resolução deste problema deve ser utilizada uma função que determina se um número é primo. 1.8 Vectores Nesta secção é descrita a forma de definir e manipular vectores. 48 Apontamentos de Programação C/C++ 1.8.1 Definição de vectores Os vectores são conjuntos de elementos (variáveis) do mesmo tipo de dados, colo- cados consecutivamente na memória. Podem ser acedidos individualmente ou refe- renciados por indexação. Isto significa que, por exemplo, para guardar vinte valores do tipo int não é necessário declarar vinte variáveis. Para tal é possível declarar um vector com vinte posições do tipo int com um único identificador. A seguir é apresentada a sintaxe para declarar um vector: <tipo-de-dados> <id-do-vector> [<num-de-elem>] na qual: • <tipo-de-dados> – indica qual o tipo de dados (e.g., : int, float, char) de cada elemento do vector; • <id-do-vector> – este é o identificador do vector; • <num-de-elem> – entre parêntesis rectos ([,]) especifica o número de elemen- tos do vector. O número de elementos tem que ser um valor constante, isto é, não pode ser uma variável, porque os vectores são conjuntos estáticos de memória de um determinado tamanho e o compilador tem que ser capaz de determinar exactamente a quantidade de memória necessaria para o vector antes de qualquer instrução ser considerada. Por exemplo um vector para armazenar vinte números inteiros (do tipo de dados int) pode ser definido da seguinte forma: int vec[20]; A figura 1.10 representa graficamente um vector com 20 elementos. Onde cada rectângulo representa um elemento do vector, que neste caso são elementos do tipo int. Os elementos estão numerados de 0 a 19, uma vez que o primeiro elemento de um vector é sempre a posição 0, independentemente do números de elementos do vector. ... 1112 8 9 17 15Valor 190 1 2 3 4Índice ... Figura 1.10: Representação gráfica do vector vec 1.8.2 Atribuição dos valores iniciais Quando se declara um vector é possível, como acontece com qualquer outra variá- vel, iniciar os seus elementos. Para tal deve-se colocar os elementos separados por virgulas (’,’) dentro de chavetas ({}). Como por exemplo: int vec[5]={1,3,2,55,67}; O número de elementos deve ser igual ao número de posições do vector. No entanto, é possível não definir o número de elementos do vector, sendo o número de posições definido em função dos elementos que estão dentro das chavetas. A 49 ISEP/DEI - Jorge Santos e Paulo Baltarejo 5 for ( i =1; i<dim ; i++) 6 { 7 i f ( vec [ i ]>maximo) 8 maximo=vec [ i ] ; 9 else 10 i f ( vec [ i ]<minimo ) 11 minimo=vec [ i ] ; 12 } 13 return (maximo−minimo ) ; 14 } No seguinte extracto (listagem 1.41) é definido o vector e evocadas as funções e procedimento anteriormente definidos. Listing 1.41: Função main e protótipos das funções 1 #include<iostream . h> 2 #define MAX 10 3 4 void l e i t u r a ( int vec [ ] , int dim ) ; 5 int contaPares ( int vec [ ] , int dim ) ; 6 int maiorDi ferenca ( int vec [ ] , int dim ) ; 7 8 int main ( ) 9 { 10 int vec to r [MAX] ; 11 12 l e i t u r a ( vector ,MAX) ; 13 cout<<contaPares ( vector ,MAX)<<endl ; 14 cout<<maiorDi ferenca ( vector ,MAX)<<endl ; 15 16 return 0 ; 17 } 1.8.5 Exercícios propostos 1.8.5.1 Determinar desvio padrão de uma série Escreva um programa modular que permita determinar o desvio padrão de um série de números de acordo com a formula 1.8.1. Considere a definição de funções e procedimento para os diversos sub–problemas. desvioPadrao = √√√√√√ n∑ i=1 (xi − media) n− 1 (1.8.1) 1.8.5.2 Prova de atletismo Faça a leitura das pontuações que 5 juízes de uma determinada prova atribuíram a um atleta (valores compreendidos entre 0 e 9 inclusive). Determine e apresente com formato adequado, os seguintes valores: 52 Apontamentos de Programação C/C++ • média obtida pelo atleta; • a pior e a melhor pontuação; • a percentagem de pontuações iguais ou superiores a 8 valores; • supondo que a 1ª nota foi atribuída pelo juiz nº1 e assim sucessivamente determine os números dos juízes que atribuíram a melhor nota do atleta. 1.8.5.3 Suavização Em qualquer experiência existe um certo erro associado aos valores obtidos. Uma técnica conhecida como suavização pode ser utilizada para reduzir o efeito desse erro na análise dos resultados. Escreva então um programa que permita ler os dados para um vector de N reais e implemente uma função que produza uma suavização sobre esses dados. A suavização consiste em substituir o valor actual de uma posição pela média do valor da posição anterior, da posterior e dele próprio. Assumindo que o identificador do vector é v, então v[i]=(v[i-1]+v[i]+v[i+1])/3, (excepto o primeiro e o último). O primeiro elemento do vector é suavizado com base na média entre os dois primeiros valores e o último elemento é suavizado com base na média entre os dois últimos. 1.9 Vectores multi–dimensionais Um vector multidimensional pode ser descrito como um vector de vectores. Por exemplo, um vector bidimensional (matriz) pode ser visto como uma tabela bidi- mensional em que todos os elementos são do mesmo tipo dados. A figura 1.11 faz a representação gráfica de uma matriz. ...... ......... 7990 1 2 ... ... 512 8 1 ... 64 56 11 ... 45 83 9639 0 1 ... Figura 1.11: Representação gráfica de uma matriz A variável mat representa um vector bidimensional de 3 linhas e 5 colunas. A forma de o definir em C++ é: int mat[3][5]; A instrução mat[1][3] referencia o elemento da segunda linha e da quarta co- luna. Importa relembrar que os indices começam sempre em 0. Os vectores multi– dimensionais não estão limitados a duas dimensões. Podem ter as dimensões que o 53 ISEP/DEI - Jorge Santos e Paulo Baltarejo programador achar necessário. O exemplo seguinte define um vector de três dimen- sões. int multiVec[100][200][50]; Há contudo um aspecto a ter em consideração na definição de vectores multi– dimensionais, a memória necessária na definição destes. Pois o número de ele- mentos que o vector multiVec aloca é obtido pela multiplicação das dimensões e não pela soma, como erroneamente se poderia pensar. Portanto, este vector aloca 100*200*50=1000000 elementos. Sendo que a memória necessária para armazenar 1000000 elementos do tipo int é cerca de 4Mbytes (1000000*4bytes). Note–se que os vectores multi–dimensionais não são mais do que uma abstracção, uma vez que é possível obter os mesmos resultados com um vector simples. Por exemplo: int mat[3][5]; é equivalente a int mat[15]; (3*5=15) A única diferença consiste no facto do compilador guardar a profundidade de cada dimensão imaginária. A seguir apresenta-se duas listagens de programas, na quais, numa é usado um vector multidimensional (listagem 1.42) e na outra um vector simples (listagem 1.43). Apesar de aparentemente guardarem a informação em estruturas de dados diferentes, na prática são idênticos. Listing 1.42: Vector multidimensional 1 #define COLUNAS 5 2 #define LINHAS 3 3 4 int main ( ) 5 { 6 int mat [LINHAS ] [COLUNAS] ; 7 int n ,m; 8 for (n=0;n<LINHAS; n++) 9 for (m=0;m<COLUNAS;m++) 10 { 11 mat [ n ] [m]=(n+1)∗(m+1); 12 } 13 return 0 ; 14 } Listing 1.43: Vector pseudo-multidimensional 1 #define COLUNAS 5 2 #define LINHAS 3 3 4 int main ( ) 5 { 6 int mat [LINHAS ∗ COLUNAS] ; 7 int n ,m; 8 for (n=0;n<LINHAS; n++) 9 for (m=0;m<COLUNAS;m++) 10 { 11 mat [ n ∗ COLUNAS + m]=(n+1)∗(m+1); 12 } 13 return 0 ; 14 } 54 Apontamentos de Programação C/C++ 71 }while ( op<0 | | op>4); 72 return op ; 73 } 74 75 void l e i t u r a ( int mat [ ] [MAX_C] ) 76 { 77 int i , j ; 78 char bu f f e r [MAX] ; 79 for ( i =0; i<MAX_L; i++ ) 80 for ( j =0; j<MAX_C; j++ ) 81 { 82 cout << " mat [ " << i << " , " << j << " ] : " ; 83 c in . g e t l i n e ( bu f f e r ,MAX) ; 84 mat [ i ] [ j ]= a t o i ( bu f f e r ) ; 85 } 86 } 87 88 f loat mediaDiagonal ( int mat [ ] [MAX_C] ) 89 { 90 int i , soma=0; 91 92 for ( i =0; i<MAX_L; i++ ) 93 soma += mat [ i ] [ i ] ; 94 95 return ( f loat ) soma / MAX_L; 96 } 97 98 int super iorMedia ( int mat [ ] [MAX_C] , int vec [ ] ) 99 { 100 int i , j , n=0; 101 f loat media ; 102 103 media = mediaDiagonal ( mat ) ; 104 105 for ( i =0; i<MAX_L; i++ ) 106 for ( j =0; j<MAX_C; j++ ) 107 i f ( mat [ i ] [ j ] > media ) 108 vec [ n++] = mat [ i ] [ j ] ; 109 110 return n ; 111 } 112 113 int o c o r r en c i a s ( int mat [ ] [MAX_C] , int mat1 [ ] [ 2 ] ) 114 { 115 int i , j , n=0; 116 117 for ( i =0; i<MAX_L; i++ ) 118 for ( j =0; j<MAX_C; j++ ) 119 inc_ocorr (mat [ i ] [ j ] , mat1 , &n ) ; 120 121 return n ; 57 ISEP/DEI - Jorge Santos e Paulo Baltarejo 122 } 123 124 void inc_ocorr ( int valor , int m[ ] [ 2 ] , int ∗n ) 125 { 126 int i =0; 127 bool i n c = fa l se ; 128 129 do 130 { 131 i f ( m[ i ] [ 0 ] == va lo r ) 132 { 133 m[ i ] [ 1 ]++; 134 i n c = true ; 135 } 136 i++; 137 } 138 while ( ! i nc && i<∗n ) ; 139 140 i f ( ! i nc ) 141 { 142 m[∗n ] [ 0 ] = va lo r ; 143 m[ ( ∗ n)++] [1 ] = 1 ; 144 } 145 } 1.9.2 Exercícios propostos 1.9.2.1 Máximo local Um elemento de uma matriz é considerado um máximo local se for superior a todos os seus vizinhos. Escreva um programa que dada uma matriz forneça todos os máximos locais e as respectivas posições, considerando que os elementos da periferia da matriz não podem ser máximos locais. 1.9.2.2 Determinar se uma matriz é simétrica Escreva um programa que dada uma matriz quadrada de dimensão n determine se ela é ou não simétrica. Uma matriz A diz–se simétrica se aij = aji com 1 ≤ i, j ≤ n . 1.10 Vectores como parâmetros A programação modular que é possível através da utilização de funções obriga em que em certos casos é necessário passar vectores (uni e multi–dimensionais) para as funções como parâmetro. Na linguagem C++ não é possível passar por valor um vector, isto é, um vector é sempre passado por referência (através do seu endereço). Para passar um vector por parâmetro a única coisa que é necessário fazer é colocar na declaração da função especificar que a função tem um argumento que é um vector. Por exemplo a seguir apresenta-se o protótipo de uma função que recebe um vector por parâmetro. 58 Apontamentos de Programação C/C++ void funcao(int v[]) Neste exemplo os elementos do vector são do tipo int e vector dentro da função é identificado pelo identificador v. Além do tipo e do identificador é necessário colocar os parêntesis rectos ([]), sendo que é opcional colocar a dimensão, isto no caso de ser um vector unidimensional. O exemplo presente na listagem 1.45 ilustra a vantagem da utilização da passa- gem de vectores a uma função. A função void printvector (int v[], int len) imprime no ecrã os len elementos do vector v. Importa realçar que a mesma função é usada para imprimir o conteúdo do vector vector1 e do vector2. Na chamada è função printvector na função main não é especificado a dimensão do vector, basta colocar o nome do vector. Listing 1.45: Vector como argumento de funções 1 #include <iostream . h> 2 3 void p r i n t v e c t o r ( int v [ ] , int l en ) 4 { 5 for ( int n=0; n<len ; n++) 6 cout << v [ n ] << " " ; 7 cout << "\n" ; 8 } 9 10 int main ( ) 11 { 12 int vector1 [ ] = {5 , 10 , 15} ; 13 int vector2 [ ] = {2 , 4 , 6 , 8 , 10} ; 14 p r i n t v e c t o r ( vector1 , 3 ) ; 15 p r i n t v e c t o r ( vector2 , 5 ) ; 16 return 0 ; 17 } Quando se pretende passar vectores multi–dimensionais como parâmetro a uma função é obrigatório especificar o numero de elementos de cada dimensão com ex- cepção da primeira. A seguir apresenta o formato: <tipo-de-dados> <id-do-vec> [][dim][dim][dim]... Por exemplo, se se pretende que uma função receba um vector tridimensional é obrigatório especificar pelo menos a 2ª e 3ª dimensão. void funcao(int m[][3][4]) Isto é obrigatório porque o compilador precisa de ser capaz de determinar o número de elementos das 2ª e 3ª dimensões. 1.11 Strings As strings são vectores de elementos do tipo char e permitem representar palavras, frases e textos. De seguida apresenta–se a definição de uma string com capacidade para 20 caracteres. char str[20]; Está convencionado que uma string é terminada pelo carácter ’\0’, assim, na prática a string acima definida tem capacidade para 19 caracteres + 1. Portanto no 59 ISEP/DEI - Jorge Santos e Paulo Baltarejo 7 { 8 char s t r i n g 1 [ SIZE]="Bom dia mundo ! " ; 9 char s t r i n g 2 [ SIZE ] ; 10 int n ; 11 12 s t r cpy ( s t r ing2 , s t r i n g 1 ) ; 13 14 i f ( strcmp ( s t r ing2 , s t r i n g 1 )==0) 15 cout<<"As s t r i n g s sao i g u a i s "<<endl ; 16 17 n=s t r l e n ( s t r i n g 1 ) ; 18 cout<<"A s t r i n g 1 tem"<<n<<" ca r a c t e r e s "<<endl ; 19 20 return 0 ; 21 } 1.11.3 Conversão de strings para outros tipos Dado que uma string pode conter representações de outros tipo de dados, como números, em algumas situações pode ser útil transformar uma string num dado numérico. Por exemplo, uma string qualquer pode conter o seguinte número "1977", mas em termos de codificação esta não é mais nem menos do que uma sequência de 5 caracteres, portanto, não sendo possível realizar operações aritméticas. Para tal é necessário, converter esta string num tipo de dado numérico, como por exemplo: int, long, float. Na biblioteca cstdlib (é necessário a inclusão do ficheiro de cabeçalho stdlib.h) estão definidas três funções que permitem fazer essa conversão. As funções são: int atoi (const char *string ); – converte a string num int. long atol (const char *string ); – converte a string num long. double atof (const char *string ); – converte a string num double. A seguir apresenta-se um exemplo da utilização destas funções Listing 1.47: Exemplo da utilização de funções da biblioteca cstring 1 #include<iostream . h> 2 #include<s t d l i b . h> 3 4 #define SIZE 50 5 6 int main ( ) 7 { 8 char bu f f e r [ SIZE ] ; 9 int i ; 10 long l ; 11 double d ; 12 13 cout<<"Dig i t e um numero i n t e i r o : "<<endl ; 14 c in . g e t l i n e ( bu f f e r , SIZE ) ; 15 i=a t o i ( bu f f e r ) ; 16 17 cout<<"Dig i t e um numero i n t e i r o ( long ) : "<<endl ; 62 Apontamentos de Programação C/C++ 18 c in . g e t l i n e ( bu f f e r , SIZE ) ; 19 l=a t o l ( bu f f e r ) ; 20 21 cout<<"Dig i t e um numero f r a c c i o n a r i o : "<<endl ; 22 c in . g e t l i n e ( bu f f e r , SIZE ) ; 23 d=ato f ( bu f f e r ) ; 24 25 cout<<"Os va l o r e s i n s e r i d o s foram : "<<i<<" ( i n t ) : " ; 26 cout<<l<<" ( long ) : "<<d<<" ( double ) "<<endl ; 27 28 return 0 ; 29 } 1.11.4 Exercícios resolvidos 1.11.4.1 Programa para manipulação de strings e caracteres Desenvolva um programa que implemente as seguintes funções: 1. Uma função que recebe uma string e retorne o seu comprimento. O protótipo da função deverá ser int mystrlen(char *s); 2. Uma função que copie o conteúdo de uma string para a outra. Portanto a função recebe duas strings , uma designada de origem e a outra de destino. O objectivo é copiar o conteúdo da de origem para a de destino. Note que as strings podem ter tamanhos diferentes. Daí que, a função recebe as duas strings e um número com o comprimento da string de destino (comp_strDest). O objectivo da função é que copiar da string de origem para a string de destino no máximo comp_strDest –1 caracteres. A função deve retornar o número de caracteres efectivamente copiados. O protótipo da função deverá ser int mystrncpy(char *strDest, char *strSource, int comp_strDest); 3. Uma função que receba duas strings e retorne: 1 se as strings forem iguais; e 0 se forem diferentes. O protótipo da função deverá ser int str1Igualstr2(char *s1, char *s2)); 4. Uma função que receba um carácter e caso seja maiúsculo retorne o correspon- dente minúsculo. O protótipo da função deverá ser char mytolower(char s); 5. Uma função que receba um carácter e caso seja minúsculo retorne o correspon- dente maiúsculo. O protótipo da função deverá ser char mytoupper(char s); 6. Uma função que elimine todos os espaços à esquerda do primeiro carácter(diferente de espaço). O protótipo da função deverá ser void mylefttrim(char *s); 7. Uma função que elimine todos os espaços á direita do último carácter (diferente de espaço). O protótipo da função deverá ser void myrighttrim(char *s); 8. Uma função que elimine todos os espaços múltiplos entre as palavras ou letras. O protótipo da função deverá ser void removemultiplespace(char *s). 63 ISEP/DEI - Jorge Santos e Paulo Baltarejo Listing 1.48: Exemplo de manipulação de strings[] 1 #include<iostream . h> 2 #include<s t d l i b . h> 3 #include<s td i o . h> 4 #define MAX 50 5 6 int menu ( ) ; 7 int mystr len (char ∗ s ) ; 8 int mystrncpy (char ∗ strDest , char ∗ s t rSource , int comp_strDest ) ; 9 int s t r 1 I g u a l s t r 2 (char ∗ s t r1 , char ∗ s t r 2 ) ; 10 char mytolower (char s ) ; 11 char mytoupper (char s ) ; 12 void myle f t t r im (char ∗ s ) ; 13 void myrighttr im (char ∗ s ) ; 14 void removemult ip lespace (char ∗ s ) ; 15 16 int main ( ) 17 { 18 int op , n ; 19 char s t r 1 [MAX] , s t r 2 [MAX] , c ; 20 do{ 21 op=menu ( ) ; 22 switch ( op ){ 23 case 1 : cout<<"\ nDig i te a s t r i n g : " ; 24 c in . g e t l i n e ( s t r1 ,MAX) ; 25 n=mystr len ( s t r 1 ) ; 26 cout<<"A s t r i n g tem "<<n<<" ca r a c t e r e s "<<endl ; 27 break ; 28 case 2 : 29 cout<<"\ nDig i te a s t r i n g : " ; 30 c in . g e t l i n e ( s t r1 ,MAX) ; 31 n=mystrncpy ( st r2 , s t r1 , MAX) ; 32 cout<<"Copiou "<<n<<" ca r a c t e r e s "<<endl ; 33 cout<<str2<<endl ; 34 break ; 35 case 3 : 36 cout<<"\ nDig i te a s t r i n g 1 : " ; 37 c in . g e t l i n e ( s t r1 ,MAX) ; 38 cout<<"\ nDig i te a s t r i n g 2 : " ; 39 c in . g e t l i n e ( s t r2 ,MAX) ; 40 n=s t r 1 I g u a l s t r 2 ( s t r2 , s t r 1 ) ; 41 i f (n==1) 42 cout<<"As s t r i n g s sao i g u a i s "<<endl ; 43 else 44 cout<<"As s t r i n g s sao d i f e r e n t e s "<<endl ; 45 46 break ; 47 case 4 : 48 cout<<"\ nDig i te o c a r a c t e r : "<<endl ; 49 c=getchar ( ) ; 50 cout<<mytoupper ( c)<<endl ; 64 Apontamentos de Programação C/C++ 153 i f ( s !=NULL) 154 { 155 while ( s [0]== ’ ’ ) 156 { 157 x=mystr len ( s ) ; 158 for ( i =1; i<x ; i++) 159 s [ i−1]=s [ i ] ; 160 } 161 } 162 } 163 164 void myrighttr im (char ∗ s ) 165 { 166 int i ; 167 i=mystr len ( s ) ; 168 i f ( i >0) 169 { 170 while ( s [ i−1]== ’ ’ && i >0) 171 { 172 s[−− i ]= ’ \0 ’ ; 173 } 174 } 175 } 176 177 void removemult ip lespace (char ∗ s ) 178 { 179 int i , j , x ; 180 181 i =0; 182 while ( s [ i ]== ’ ’ ) i++; 183 184 j=mystr len ( s )−1; 185 while ( s [ j ]== ’ ’ ) j−−; 186 187 while ( i<j ) 188 { 189 i f ( s [ i ]== ’ ’ && s [ i+1]== ’ ’ ) 190 { 191 for ( x=i ; x<mystr len ( s ) ; x++) 192 s [ x]= s [ x+1] ; 193 j−−; 194 } 195 else 196 i++; 197 } 198 } 67 ISEP/DEI - Jorge Santos e Paulo Baltarejo 1.11.5 Exercícios propostos 1.11.5.1 Função que determine o número de ocorrências Escreva uma função que receba duas strings e retorne o número de ocorrências de uma na outra. Por exemplo, a string "DEIDE DEDEI DEEI"tem duas ocorrências da string "DEI". 1.11.5.2 Função que verifique se uma string é inversa de outra Escreva uma função que recebe duas strings e verifica se uma é inversa de outra. Caso seja deve retornar 1 e 0 caso não seja. Note que uma strings vazia não tem inversa. 1.11.5.3 Função que conta as palavras de uma string Escreva uma função que recebe uma string e retorne o número de palavras. Entendendo- se por palavra cadeias de caracteres terminadas pelo carácter espaço. As palavras têm que ter mais do que um carácter. 1.11.5.4 Função que formate uma string Escreva uma função que receba uma string e a formate da seguinte forma: 1. As palavras começam sempre com letras maiúscula e o resto com letra minús- cula; 2. As palavras têm que ter mais do que um carácter; 3. As letras (caracteres isolados em letra minúscula). 1.12 Ponteiros As variáveis na linguagem C++ são definidas através de um identificador e do tipo de dados. No entanto, estas estão armazenadas algures na memória. A memória é uma lista de bytes, na qual cada um tem endereço único. Assim, as variáveis podem também ser referenciadas através do endereço de memória onde estão armazenadas. A memória de um computador com 512 MBytes (512*1024*1024=536870912 bytes) é ilustrada na figura 1.13. Todos os bytes são endereçáveis (o endereço está escrito em notação hexadecimal). 1.12.1 Operador endereço & Sempre que se declara uma variável, ela é armazenada num local concreto da memó- ria, sendo que o local é definido pelo sistema operativo em tempo de execução. Uma vez atribuído o local de armazenamento, por vezes é necessário aceder directamente a esse local. Isto pode ser feito colocando o & antes do identificador da variável. A listagem 1.49 apresenta um programa que permite mostrar no ecrã o endereço de memória onde a variável está declarada. 68 Apontamentos de Programação C/C++ ByteEndereço 0x00000000 0x00000001 0x00000002 0x00000003 0x1FFFFFFF Figura 1.13: Representação da memória do computador Listing 1.49: Endereço de uma variável 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int i ; 6 7 cout<<&i<<endl ; 8 9 return 0 ; 10 } A colocação do & antes do identificador da variável significa o "endereço de". No caso do exemplo da listagem 1.49 a instrução cout< <&i< <endl; imprime no ecrã o endereço da variável i. 1.12.2 Operador de referência * Os ponteiros são variáveis para que armazenam endereços. A forma de definir va- riáveis do tipo ponteiro é a seguinte: <tipo-de-dados> * <identificador>; A única diferença em relação à forma de definir variáveis é a colocação do caracter * entre o tipo-de-dados e o identificador. • <tipo-de-dados>– é um qualquer tipo de dados válido; • <identificador> – tem o mesmo significado que o identificador de uma variável de um qualquer tipo de dados estudado até agora. A seguir apresenta–se a definição de duas variáveis do tipo ponteiro. int *pi; char *pc; 69 ISEP/DEI - Jorge Santos e Paulo Baltarejo Note-se que o valor das variáveis pode ser alterado através dos ponteiros ou através das próprias variáveis. 1.12.3 Ponteiros e vectores Um identificador de um vector é equivalente a um ponteiro para a primeira posição desse vector. Por exemplo, a seguir apresenta–se a declaração de um vector e de um ponteiro; int vec[10]; int *p; a seguinte instrução é valida p=vec; esta instrução faz com que o ponteiro p e vec sejam equivalentes, e tem as mesmas propriedades. A única diferença está relacionada com o facto de poder ser atribuído outro valor ao ponteiro p enquanto que ao ponteiro vec não. Portanto, p é uma va- riável do tipo ponteiro enquanto que vec é uma variável do tipo ponteiro constante. Daí que a seguinte instrução é inválida: vec=p;. A seguir apresenta–se um exemplo que mostra a utilização de ponteiro para aceder aos elementos de um vector. Listing 1.52: Exemplo da utilização de ponteiros 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int ∗p=NULL; 6 int vec [ 10 ]={0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9} ; 7 p=vec ; 8 for ( int i =0; i <10; i++) 9 cout<<p [ i ]<<endl ; 10 return 0 ; 11 } 1.12.4 Ponteiros para ponteiros A linguagem C++ permite a utilização de ponteiros para ponteiros no qual o úl- timo aponta para uma variável. Para tal basta acrescentar um asterisco por cada referência. Por exemplo: Listing 1.53: Exemplo da utilização de ponteiros 1 #include<iostream . h> 2 3 int main ( ) 4 { 5 int ∗∗pp=NULL,∗p=NULL, a=10; 6 7 p=&a ; 72 Apontamentos de Programação C/C++ 8 pp=&p ; 9 10 cout<<" endereco de a : "<<&a<<endl ; 11 cout<<" endereco de p : "<<&p<<endl ; 12 cout<<" endereco de pp : "<<&pp<<endl ; 13 14 cout<<"O va lo r da va r i a v e l a : "<<a<<endl ; 15 cout<<"O va lo r da va r i a v e l p : "<<p<<endl ; 16 cout<<"O va lo r da va r i a v e l pp : "<<pp<<endl ; 17 18 cout<<"O va lo r da va r i a v e l a : "<<a<<endl ; 19 cout<<"O va lo r apontado por p : "<<∗p<<endl ; 20 cout<<"O va lo r apontado pe lo apontado por pp : "<<∗∗pp<<endl ; 21 22 return 0 ; 23 } A saída do programa anterior poderia ser a seguinte. endereco de a: 0x0012FF74 endereco de p: 0x0012FF78 endereco de pp:0x0012FF7C O valor da variavel a: 10 O valor da variavel p:0x0012FF74 O valor da variavel pp: 0x0012FF78 O valor da variavel a: 10 O valor apontado por p: 10 O valor apontado pelo apontado por pp: 10 Press any key to continue Como se pode ver, o valor do ponteiro pp é o endereço do ponteiro p que por sua vez tem o endereço da variável a. A figura 1.15 apresenta graficamente a interacção entre as variáveis presentes no programa da listagem 1.53. a *p **pp 10 &a p *pp 0x0012FF74 0x0012FF78 &p pp Figura 1.15: Ponteiro para ponteiro 1.12.5 Ponteiros do tipo void Os ponteiros de tipo void são um tipo especial de ponteiros que permite que um ponteiro possa apontar para variáveis de qualquer tipo. O único problema é que 73 ISEP/DEI - Jorge Santos e Paulo Baltarejo para se aceder ao valor da variável apontado por este tipo de ponteiros é necessário fazer a conversão do tipo de dados (cast). Este tipo de ponteiro é muito interessante para o desenvolvimento de funções genéricas, que podem receber vários tipos de dados diferentes. O exemplo 1.54 mostra a utilização dos ponteiros de tipo void. Listing 1.54: Exemplo da utilização de ponteiros do tipo void 1 #include <iostream . h> 2 #define TPCHAR 1 3 #define TPINT 2 4 #define TPFLOAT 3 5 6 void incremento (void∗ ap , int t i po ) 7 { 8 switch ( t i po ) 9 { 10 case TPCHAR : (∗ ( ( char∗) ap))++; break ; 11 case TPINT: (∗ ( ( long ∗) ap))++; break ; 12 case TPFLOAT : (∗ ( ( f loat ∗) ap))++; break ; 13 } 14 } 15 int main ( ) 16 { 17 char a = ’ a ’ ; 18 int b = 9 ; 19 f loat c = 7 . 5 ; 20 21 incremento (&a ,TPCHAR) ; 22 incremento (&b ,TPINT) ; 23 incremento (&c ,TPFLOAT) ; 24 25 cout << a << " , " << b << " , " << c<<endl ; 26 27 return 0 ; 28 } A saída deste programa é a seguinte: b, 10, 8.5 Press any key to continue 1.13 Tipos de dados não nativos Nesta secção é apresentada a forma de definir novos tipos de dados na linguagem C++. 1.13.1 Estruturas de dados – instrução struct Uma estrutura de dados é um conjunto de dados de variáveis de diversos tipos de dados agrupados sob um único identificador. A seguir apresenta–se a forma de definir uma estrutura: 74 Apontamentos de Programação C/C++ pfilme->titulo; A instrução seguinte é equivalente à anterior. (*pfilme).titulo; A definição de estruturas dentro de estruturas é muito útil e comum. O programa da listagem 1.56 contém duas estruturas: data_t e filmes_t, note–se que um dos elementos da estrutura filmes_t é ele próprio uma estrutura (data_estreia. Listing 1.56: Estruturas dentro de estruturas 1 #include <iostream . h> 2 #include <s t d l i b . h> 3 4 struct data_t 5 { 6 int dia , mes , ano ; 7 } ; 8 9 struct f i lmes_t 10 { 11 char t i t u l o [ 5 0 ] ; 12 data_t data_est re ia ; 13 } ; 14 15 int main ( ) 16 { 17 char bu f f e r [ 5 0 ] ; 18 f i lmes_t a f i lme ; 19 20 cout << "Dig i t e o t i t u l o : " ; 21 c in . g e t l i n e ( a f i lme . t i t u l o , 5 0 ) ; 22 cout << "Dig i t e a data de e s t r e i a ( d ia /mes/ano ) : " ; 23 c in . g e t l i n e ( bu f f e r , 5 0 ) ; 24 a f i lme . data_est re ia . d ia = a t o i ( bu f f e r ) ; 25 c in . g e t l i n e ( bu f f e r , 5 0 ) ; 26 a f i lme . data_est re ia . mes = a t o i ( bu f f e r ) ; 27 c in . g e t l i n e ( bu f f e r , 5 0 ) ; 28 a f i lme . data_est re ia . ano = a t o i ( bu f f e r ) ; 29 30 cout << a f i lme . t i t u l o ; 31 cout << " ( " << a f i lme . data_est re ia . dia<<" : " 32 cout <<a f i lme . data_est re ia . mes<<" : " ; 33 cout<<a f i lme . data_est re ia . ano<<" )\n" ; 34 35 return 0 ; 36 } Da mesma forma que é possível definir vectores de tipos de dados primitivos, tam- bém é possível definir vectores, tanto uni–dimensionais como multi–dimensionais, de estruturas. Assim como, os elementos de uma estrutura também pode ser vectores. O programa da listagem 1.57 mostra como definir estruturas nas quais alguns dos seus elementos são também estruturas e vectores de estruturas. O objectivo deste exemplo é mostrar a forma de aceder/utilizar os elementos de uma estrutura deste tipo. 77 ISEP/DEI - Jorge Santos e Paulo Baltarejo Listing 1.57: Vectores de estruturas 1 #include <iostream . h> 2 3 #define STR_LEN 50 4 #define NUM_DISC 30 5 #define NUM_ALUNOS 100 6 7 struct data_t 8 { 9 int dia , mes , ano ; 10 } ; 11 12 struct d i s c i p l i n a_t 13 { 14 char d i s c i p l i n a [STR_LEN] 15 int c l a s s i f i c a c a o ; 16 } ; 17 18 struct aluno_t 19 { 20 char nome [STR_LEN] ; 21 char morada [STR_LEN ∗ 2 ] ; 22 data_t data_nasc ; 23 d i s c i p l i n a_t d i s c i p l i n a s [NUM_DISC] ; 24 } ; 25 26 void l i s t a rA l uno s ( aluno_t a l [ ] ) ; 27 28 int main ( ) 29 { 30 aluno_t alunos [NUM_ALUNOS] ; 31 32 l i s t a rA l uno s ( a lunos ) ; 33 34 return 0 ; 35 } 36 37 void l i s t a rA l uno s ( aluno_t a l [ ] ) 38 { 39 for ( int i =0; i<NUM_ALUNOS; i++) 40 { 41 cout<<"Nome : "<<a l [ i ] . nome<<endl ; 42 cout<<"Morada : "<<a l [ i ] . morada<<endl ; 43 cout<<"Data de nascimento : "<<a l [ i ] . data_nasc . dia<<" : " ; 44 cout<<<<a l [ i ] . data_nasc . mes<<" : " ; 45 cout<<<<a l [ i ] . data_nasc . ano<<endl ; 46 for ( int j =0; j<NUM_DISC; j++) 47 { 48 cout<<"\ tD i s c i p l i n a : "<<a l [ i ] . d i s c i p l i n a s [ j ] . d i s c i p l i n a <<endl ; 49 cout<<"\ tD i s c i p l i n a : "<<a l [ i ] . d i s c i p l i n a s [ j ] . c l a s s i f i c a c a o <<endl ; 50 } 78 Apontamentos de Programação C/C++ 51 } 52 } 1.13.2 Definição de tipos – instrução typedef A linguagem C++ permite a definição de tipos de dados não nativos através da instrução typedef. A forma para o fazer é a seguinte: typedef <tipo-de-dados-existente> <novo-tipo-dados>; no qual • <tipo-de-dados-existente> – é um tipo de dados já definido, por exemplo um tipo de dados primitivos ou uma tipo de dados composto (estrutura); • <novo-tipo-dados> – a designação do novo tipo de dados. A seguir apresenta-se vários exemplos da definição de novos tipos de dados. typedef char CARACTER; typedef int INTEIRO; typedef float REAL; typedef char STRING [50]; A partir destas definições é possível usar os novos tipos da mesma forma que se podem todos os outros. No extracto de código seguinte são definidoas variáveis com base nos tipos anteriores. CARACTER a, b,c=’A’; INTEIRO x=5; REAL f=4.9; STRING str="Bom dia mundo!!"; 1.13.3 União – instrução union Uma união5 permite fazer a definição de um tipo de dados de forma disjunta, isto significa que em termos práticos uma variável deste tipo pode ser de qualquer dos sub–tipos utilizados na definição. A sintaxe de uma união é muito semelhante com a definição de uma estrutura: union <identificador-da-união> { <tipo-de-dados> <identificador 1>; <tipo-de-dados> <identificador 2>; ... <tipo-de-dados> <identificador N>; }<identificador-da-variavel>; na qual • <identificador-da-união>– é a designação do novo tipo de dados; 5do anglo-saxónico union. 79 ISEP/DEI - Jorge Santos e Paulo Baltarejo sem referenciar qualquer outro tipo de dados já existente. No entanto, uma qualquer variável do tipo cor_t só pode assumir os valores escritos entre {}. De facto os enumeradores são compilados como inteiros, em que se nada for referido o primeiro elemento assume o valor 0 e o seguinte 1 e assim por diante. No entanto, pode–se definir qual o valor inicial. Por exemplo, a instrução seguinte cria o tipo de dados mes_t em que o primeiro elemento assume o valor 1. mes_t {jan=1,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez}; O exemplo 1.58 mostra a utilização dos tipos de dados definidos através de enum, struct e union. Listing 1.58: Exemplo da utilização de tipos de dados definidos 1 #include <iostream . h> 2 3 #define PI 3.1415 4 5 enum t ipo_f igura_t {RECTANGULO, CIRCULO} ; 6 7 struct rectangulo_dimensoes_t 8 { 9 double comprimento ; 10 double l a r gu ra ; 11 } ; 12 struct circulo_dimensoes_t 13 { 14 double r a i o ; 15 } ; 16 struct f i gura_t 17 { 18 t ipo_f igura_t tag ; 19 union 20 { 21 rectangulo_dimensoes_t rec tangu lo ; 22 circulo_dimensoes_t c i r c u l o ; 23 } dimensoes ; 24 } ; 25 double area ( f igura_t ∗ f i g ) 26 { 27 switch ( f i g−>tag ) 28 { 29 case RECTANGULO: 30 { 31 double c = f i g−>dimensoes . r e c tangu lo . comprimento ; 32 double l = f i g−>dimensoes . r e c tangu lo . l a r gu ra ; 33 return c ∗ l ; 34 } 35 case CIRCULO: 36 { 37 double r = f i g−>dimensoes . c i r c u l o . r a i o ; 38 return PI ∗ ( r ∗ r ) ; 39 } 40 82 Apontamentos de Programação C/C++ 41 default : return −1.0; /∗ tag i n v a l i d a ∗/ 42 } 43 } 44 int main ( ) 45 { 46 f i gura_t f i g ; 47 48 f i g . tag=RECTANGULO; 49 f i g . dimensoes . r e c tangu lo . comprimento=3.0; 50 f i g . dimensoes . r e c tangu lo . l a r gu ra =2.0 ; 51 52 cout<<area(& f i g )<<endl ; 53 54 return 0 ; 55 } A figura 1.17 apresenta um resumo dos principais tipos de dados em C++. Tipos de dados void simples compostos númericos enumerados ponteiros struct union enum char int float double lógicos bool vectores ficheiros Figura 1.17: Tipos de dados 1.13.5 Exercícios resolvidos 1.13.5.1 Ponto e recta Sabendo que um ponto é constituído por três coordenadas e uma recta pode ser definida por dois pontos. Desenvolva um programa que implemente as seguintes estruturas e funções. 1. Defina as estruturas ponto e recta; 2. Desenvolva uma função que permita definir um ponto. A função deverá ter o seguinte protótipo: void inserirPonto(Ponto &p); 3. Desenvolva uma função que mostre o valor das coordenadas de um ponto. A função deverá ter o seguinte protótipo: void imprimirPonto(Ponto p); 83 ISEP/DEI - Jorge Santos e Paulo Baltarejo 4. Desenvolva uma função que permita alterar valor das coordenadas de um ponto. A função deverá ter o seguinte protótipo: void alterarPonto(Ponto *p); 5. Desenvolva uma função que permita definir uma recta. A função deverá ter o seguinte protótipo: void inserirRecta(Recta *r); 6. Desenvolva a função que mostre o valor das coordenadas dos pontos de uma recta. A função deverá ter o seguinte protótipo: void inserirRecta(Recta &r); 7. Desenvolva a função que calcule o comprimento de uma recta. A função deverá ter o seguinte protótipo: double comprimentoRecta(Recta r); Listing 1.59: Exercicio de manipulação de estruturas 1 #include <iostream . h> 2 #include <math . h> 3 4 struct Ponto 5 { 6 double x ; 7 double y ; 8 double z ; 9 } ; 10 struct Recta 11 { 12 Ponto p1 ; 13 Ponto p2 ; 14 } ; 15 16 void i n s e r i rPon to ( Ponto &p ) ; 17 void imprimirPonto ( Ponto p ) ; 18 void a l t e ra rPonto ( Ponto ∗p) 19 void i n s e r i rR e c t a ( Recta &r ) ; 20 void imprimirRecta ( Recta &r ) ; 21 double comprimentoRecta ( Recta r ) ; 22 23 int main ( ) 24 { 25 Ponto p ; 26 Recta r ; 27 i n s e r i rPon to (p ) ; 28 imprimirPonto (p ) ; 29 a l t e ra rPonto (&p ) ; 30 imprimirPonto (p ) ; 31 i n s e r i rR e c t a ( r ) ; 32 imprimirRecta ( r ) ; 33 cout<<"comprimento da r e c t a : "<<comprimentoRecta ( r)<<endl ; 34 return 0 ; 35 } 36 37 void i n s e r i rPon to ( Ponto &p) 38 { 84
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved