Prévia do material em texto
Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 1 Universidade Estadual Paulista Faculdade de Engenharia Departamento de Engenharia Elétrica Introdução à Ciência da Computação Notas de Aula da Disciplina Prof. Dr. Leonardo Nepomuceno Bauru, 2011 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 2 CONTEÚDO CAPÍTULO 1 RESOLUÇÃO DE PROBLEMAS COMPUTACIONAIS E INTRODUÇÃO À LINGUAGEM C/C++ ......................................................................................................................... 4 1.1 INTRODUÇÃO ......................................................................................................................... 4 1.2 PROBLEMAS COMPUTACIONAIS ............................................................................................ 4 1.2.1 Dados de Entrada e Saída ....................................................................................................................4 1.2.2 Variáveis de Entrada e Saída ...............................................................................................................5 1.3 SOLUCIONANDO UM PROBLEMA COMPUTACIONAL – COMANDOS INICIAIS ....................... 6 1.3.1 O compilador C/C++ ............................................................................................................................6 1.3.2 Iniciando um programa C/C++ .............................................................................................................7 1.3.3 Tipos de Dados Primitivos ...................................................................................................................8 1.4 COMPILANDO E EXECUTANDO PROGRAMAS ........................................................................ 9 1.5 COMANDOS QUE ALTERAM OS DADOS NA MEMÓRIA ........................................................ 11 1.5.1 Comandos de leitura de dados ..........................................................................................................11 1.5.2 Comando de Atribuição .....................................................................................................................12 1.6 COMANDOS DE IMPRESSÃO ................................................................................................ 14 1.7 RESOLVENDO NOSSO PRIMEIRO PROBLEMA COMPUTACIONAL ........................................ 15 1.8 EXERCÍCIOS........................................................................................................................... 16 1.9 EXPRESSÕES ARITMÉTICAS .................................................................................................. 17 1.9.1 Conversão de Tipos ...........................................................................................................................17 1.9.2 Operadores Aritméticos ....................................................................................................................17 1.9.3 Funções Matemáticas .......................................................................................................................19 1.10 EXERCÍCIOS........................................................................................................................... 20 1.11 RELAÇÕES DE PRECEDÊNCIA EM EXPRESSÕES ARITMÉTICAS .............................................. 20 CAPÍTULO 2 ESTRUTURAS DE SELEÇÃO EM LINGUAGEM C/C++ .......................................... 23 2.1 INTRODUÇÃO ....................................................................................................................... 23 2.2 ESTRUTURAS DE SELEÇÃO .................................................................................................... 23 2.2.1 Exemplo – Cálculo do INSS ................................................................................................................25 2.3 ESTRUTURAS DE SELEÇÃO EM LINGUAGEM C/C++ .............................................................. 26 2.4 EXPRESSÕES LÓGICAS .......................................................................................................... 28 2.4.1 Operadores Relacionais .....................................................................................................................29 2.4.2 Operadores Lógicos ...........................................................................................................................30 2.4.3 Relações de Precedência para Operadores Relacionais e Lógicos ......................................................32 2.5 EXERCÍCIOS........................................................................................................................... 32 CAPÍTULO 3 ESTRUTURAS DE REPETIÇÃO EM LINGUAGEM C/C++ ...................................... 34 3.1 INTRODUÇÃO ....................................................................................................................... 34 3.2 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR VARIÁVEL DE CONTROLE ....................... 34 3.3 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR VARIÁVEL DE CONTROLE EM C/C++ ...... 35 3.3.1 Exemplo – Impressão de números inteiros ........................................................................................36 3.3.2 Exemplo – Impressão de inteiros pares .............................................................................................37 3.3.3 Exemplo – Contagem de múltiplos de 3.............................................................................................37 3.3.4 Exemplo – Cálculo do Fatorial ...........................................................................................................38 3.4 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR CONDIÇÃO ............................................ 39 3.4.1 Exemplo – Lista fornecida por um usuário .........................................................................................39 3.5 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR CONDIÇÃO EM C/C++ ............................ 41 3.5.1 Exemplo – Números primos ..............................................................................................................43 3.5.2 Exemplo – Lista de números primos ..................................................................................................43 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 3 3.6 EXERCÍCIOS........................................................................................................................... 44 CAPÍTULO 4 MANIPULAÇÃO DE VETORES E MATRIZES EM C/C++ ....................................... 46 4.1 INTRODUÇÃO ....................................................................................................................... 46 4.2 VETORES E MATRIZES EM C/C++ .......................................................................................... 46 4.3 SINTAXE DE DECLARAÇÃO DE VETORES E MATRIZES ........................................................... 47 4.4 MANIPULAÇÃO DE VETORES E MATRIZES ............................................................................ 48 4.4.1 Exemplo em sala de aula – Média aritmética.....................................................................................50 4.4.2 Exemplo em sala de aula – Seqüência matemática ............................................................................50 4.5 MÉTODOS ASSOCIADOS À CLASSE VECTOR ......................................................................... 50 4.5.1 Exemplo – Operações com a classe vector ........................................................................................514.5.2 Exemplo – Armazenamento de listas de dados terminadas com dígito chave ....................................52 4.5.3 Exemplo – Redimensionamento de matrizes em tempo real .............................................................52 4.5.4 Exemplo – Cálculo da quantidade de números primos em uma matriz ..............................................53 4.5.5 Exemplo – Diagonal principal em uma matriz ....................................................................................55 4.5.6 Exemplo – Jogo da Loteria .................................................................................................................56 4.5.7 Exemplo – Armazenando índices de matrizes em vetores .................................................................57 4.5.8 Exemplo – Informações de funcionários de uma empresa .................................................................58 4.5.9 Exemplo – Escala de plantão em um hospital ....................................................................................59 CAPÍTULO 5 FUNÇÕES E PROCEDIMENTOS EM C/C++ ......................................................... 61 5.1 INTRODUÇÃO ....................................................................................................................... 61 5.2 DEFININDO FUNÇÕES EM C/C++ .......................................................................................... 61 5.2.1 Declarando Funções em C/C++ ..........................................................................................................62 5.2.2 Exemplos de declaração de interfaces de funções .............................................................................62 5.3 DIFERENCIANDO FUNÇÕES E PROCEDIMENTOS .................................................................. 63 5.3.1 Função – Definição: ...........................................................................................................................63 5.3.2 Procedimento – Definição: ................................................................................................................63 5.3.3 Descrevendo Funções C/C++ .............................................................................................................63 5.3.4 Exemplo – Combinação matemática de números ..............................................................................65 5.3.5 Exemplo – Combinação matemática de uma lista de números ..........................................................67 5.4 REGRAS DE ESCOPO DE FUNÇÕES ........................................................................................ 68 5.5 PROCEDIMENTOS ................................................................................................................. 70 5.5.1 Exemplos – Declaração de procedimentos ........................................................................................72 5.5.2 Exemplo – leitura e impressão de vetores .........................................................................................74 5.6 EXERCÍCIOS........................................................................................................................... 75 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 4 Capítulo 1 Resolução de Problemas Computacionais e Introdução à Linguagem C/C++ 1.1 INTRODUÇÃO No capítulo anterior foram estudadas, de forma bastante simplificada, as estruturas que compõem o sistema computacional. Para que possamos resolver a maioria dos problemas computacionais não é necessário que conheçamos a forma complexa pela qual estas estruturas se inter-relacionam (arquitetura dos computadores), nem é necessário o conhecimento de linguagens de máquinas complexas e de difícil utilização. A resolução de um problema através de um computador exige, entretanto, que conheçamos uma linguagem de programação. Neste capítulo daremos início ao processo de resolução de um problema computacional, através da linguagem de programação C/C++. 1.2 PROBLEMAS COMPUTACIONAIS São problemas relacionados com manipulações de informações, as quais podem envolver transmissão, armazenamento, recuperação, comparação e combinação de dados. Os problemas de computador geralmente envolvem uma pergunta de caráter geral que deve ser respondida. Alguns exemplos de problemas computacionais são dados a seguir: a) Dados dois números reais quaisquer, qual a sua soma? b) Dados dois números inteiros quaisquer, qual a sua média aritmética? c) Dados N números inteiros quaisquer, qual o maior número? d) Dada uma lista qualquer contendo NC candidatos ao vestibular, contendo os dados de nome, número, e nota dos candidatos, qual o nome e número do candidato que teve a maior nota e qual foi esta nota? Repare que a resposta às perguntas acima sempre envolve pelo menos um tipo de manipulação de dados tal como armazenamento, recuperação, comparação ou combinação de informações. 1.2.1 Dados de Entrada e Saída É muito importante perceber que todas as perguntas acima são feitas supondo-se que alguns dados são conhecidos ou fornecidos. Para respondermos à pergunta (a), por exemplo, temos que supor que 2 números reais (quaisquer) sejam fornecidos. Ou seja, só podemos calcular a soma de dois números, caso estes dois números sejam escolhidos por “alguém”. A pergunta simples Qual a soma de dois números? não faz sentido. Para respondê-la, é necessário que “alguém” diga quais são estes números. Os dados necessários a respondermos a uma pergunta geral formulada por um problema computacional são chamados dados de entrada do problema. Os dados de entrada dão sentido à Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 5 pergunta formulada pelo problema computacional, ou seja, sem eles não é possível respondermos à pergunta. Voltando aos exemplos (a) – (d) das perguntas anteriores, podemos identificar os seguintes dados de entrada. a) Dois números reais quaisquer b) Dois números inteiros quaisquer c) N números inteiros. Repare que além dos números o valor de N também é um dado de entrada. Ou seja, para respondermos a pergunta precisamos saber quantos números são (valor de N) e quais são estes números. d) Lista contendo NC candidatos, sendo que devem ser fornecidos os dados de nome, número, e nota de cada candidato. Repare que o valor de NC é também um dado de entrada. Precisamos saber quantos candidatos são e os dados específicos de cada um. Uma vez que temos em mãos os dados de entrada, podemos então partir para a resposta à pergunta formulada. Isto é feito através de um programa de computador escrito em uma linguagem de computação qualquer. O programa de computador deve ser capaz de ler os dados de entrada, armazenando-os na memória do computador, e fazer a manipulação dos dados até encontrar a(s) resposta(s) à(s) pergunta(s) formulada(s). O(s) dado(s) que corresponde(m) à resposta para a pergunta é (são) denominado(s) dado(s) de saída do problema. Nos exemplos anteriores, teremos as seguintes saídas: a) a soma dos números fornecidos b) a média aritmética dos números fornecidos c) o maior dentre os números fornecidos d) o nome e número do candidato com a maior nota e o valor desta nota (neste caso temos 3 dados de saída) Resumindo, temos que um problema computacional é resolvido por um programa de computador que recebe os dados de entrada, e manipula os dados de forma a responder a uma (ou mais) pergunta(s) através dos dados de saída, conforme mostrado na Figura 1.1. FIGURA 1.1. DIAGRAMA ESQUEMÁTICO DE UM PROGRAMA COMPUTACIONAL 1.2.2 Variáveis de Entrada e SaídaUtilizando a representação de um programa de computador conforme mostrado na Figura 1.1, podemos fornecer a resposta à pergunta (a), através de um programa que soma dois números (dados de entrada) e fornece a soma como um dado de saída conforme mostrado na Figura 1.2. Repare que nesse exemplo foram escolhidos alguns nomes para os dados de entrada e saída. Para os dados de entrada escolhemos os nomes N1 e N2, e para o dado de saída escolhemos o nome Soma. Os nomes escolhidos para os dados de entrada e saída, e que serão utilizados no programa, pelo programador, são denominados variáveis. Os nomes das variáveis são sempre escolhidos pelo programador, e é um bom procedimento de programação escolher nomes que Programa dados de entrada dados de saída Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 6 tenham a ver com o significado da variável no problema. Escolhemos os nomes N1 e N2 para representar os 2 números que serão somados e escolhemos o nome Soma para armazenar a soma dos 2 números. FIGURA 1.2. DIAGRAMA ESQUEMÁTICO DO PROGRAMA ADIÇÃO Temos, portanto, as variáveis de entrada N1 e N2 e a variável de saída Soma. A palavra variável é utilizada por que não sabemos quais serão os valores de entrada nem de saída. Estes valores podem variar dependendo de “quem” esteja fornecendo os dados de entrada para o programa. Uma vez fornecidos os dados de entrada, a saída também será calculada, podendo também assumir valores variáveis, dependendo das variáveis de entrada. Os dados de entrada podem ser fornecidos por um usuário (pessoa que vai utilizar o programa) ou podem ser fornecidos por outro programa computacional. 1.3 SOLUCIONANDO UM PROBLEMA COMPUTACIONAL – COMANDOS INICIAIS Um programa de computador é solucionado utilizando-se uma linguagem de programação. No nosso caso, utilizaremos a linguagem C/C++. Para tal, é necessário conhecermos o algoritmo que soluciona o problema computacional estudado. Um algoritmo consiste em uma seqüência de comandos (instruções, tarefas) que devem ser seguidos pelo computador de forma a respondermos à pergunta que é a essência do problema computacional proposto. Um algoritmo é, portanto, uma “receita de bolo”. As palavras algoritmo e programas muitas vezes se confundem. Podemos dizer que estamos descrevendo um programa ou que estamos descrevendo o algoritmo do programa. 1.3.1 O compilador C/C++ A seguir serão estudados os comandos iniciais, que definem um programa em C/C++. O primeiro passo para especificarmos (descrevermos) um programa C/C++ é carregar em nosso computador um compilador da linguagem. Vários compiladores C/C++ são disponíveis no mercado. Em nossa disciplina utilizaremos o compilador DEV C++ que pode ser obtido gratuitamente em http://www.bloodshed.net/dev/devcpp.html. Por questões de compatibilidade,(de maneira que todos, alunos e o professor, possam utilizar o mesmo compilador) sugere-se que os alunos baixem a versão Dev-C++ 5.0 beta 9.2 (4.9.9.2) (9.0 MB) with Mingw/GCC 3.4.2. Após baixar o programa, deve ser feita a instalação clicando duas vezes no arquivo executável devcpp-4.9.9.2_setup.exe baixado e seguir as instruções de instalação. Após a instalação, o compilador deve ser carregado, clicando duas vezes no ícone do DEV C++. Ao carregarmos o compilador é importante escolher a opção de um Arquivo-> Novo-> Projeto. Na janela de novo projeto marcar a opção Console Application e também a opção Projeto C++. Finalmente, é necessário especificar o nome para o projeto. Escolha um nome que expresse os objetivos do seu programa, os quais estão relacionados com o problema computacional que você N1 = 10.3 N2 = 20.2 Soma =30.5 Programa Adição Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 7 está solucionando. Feito isso, o compilador solicita que u local em que o projeto deverá ser salvo. É conveniente que você crie uma pasta específica onde seus projetos C++ serão salvos. Feito isso, o compilador DEV C++ automaticamente abre uma interface de desenvolvimento de programa e escreve os comandos iniciais conforme mostrado em Programa 1.1, a seguir. É importante termos uma noção inicial do que significam esses comandos descritos no Programa 1.1. PROGRAMA 1.1. TELA INICIAL DO COMPILADOR DEV C++. A diretiva #include, no início do programa, inclui bibliotecas de arquivo do C++. Bibliotecas são arquivos padrão dos compiladores contendo várias funções que podem ser incorporadas aos programas. No programa acima, estão sendo incluídas duas bibliotecas básicas do C++: cstdlib e iostream A biblioteca iostream dá suporte a funções de entrada e saída (input/output, ou i/o) de dados. A biblioteca cstdlib dá suporte a uma miscelânea de funções, que não se encaixam em nenhuma outra biblioteca (padrão ANSI). A linha de comando int main(int argc, char *argv[]), define o topo do programa principal (main). Neste ponto, ainda não é possível entendermos completamente esse comando; à medida que formos avançando na linguagem C/C++ esse comando será mais bem compreendido. Por hora, é suficiente sabermos que este comando define o programa principal, e que ele retorna um valor inteiro após a sua execução (return EXIT_SUCCESS;). Os colchetes { } definem onde deve ser escrito o corpo do programa. O corpo do programa consiste de uma série de comandos introduzidos pelo programador. No corpo do programa devem estar os comandos que resolvem o problema computacional, os quais devem ser inseridos pelo programador do sistema. O comando system("PAUSE"); quando executado, pára a execução do programa e imprime na tela a mensagem “Pressione qualquer tecla para continuar...”. A seguir, é importante especificarmos os objetivos do algoritmo e definirmos suas variáveis de entrada e de saída. É muito importante definirmos também o tipo das variáveis de entrada e saída. 1.3.2 Iniciando um programa C/C++ Podemos iniciar a resolução do problema computacional que calcula a soma de dois números por meio do algoritmo inicialmente especificado no Programa 1.2. Nesse algoritmo, os tipos das variáveis de entrada e saída são definidos utilizando-se o comando float N1, N2, soma;. Os detalhes desse comando serão discutidos a seguir. Por enquanto, esse programa simplesmente define as variáveis, mas ainda não resolve o nosso problema computacional. Vamos fazer isso ao longo dessa seção. Outros comandos introduzidos no Programa 1.2 são comentados a seguir. #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 8 PROGRAMA 1.2. DECLARANDO AS VARIÁVEIS DE ENTRADA E SAÍDA. Os comandos /* e */ definem respectivamente o início e fim de um comentário feito no texto. Os comentários não têm efeito algum quando da execução do programa, mas servem para introduzir informações importantes para o programador e para tornar o texto do programa mais claro e legível. O comando float N1, N2, soma; define espaços de memória para as variáveis N1, N2, e soma. O comando especifica ainda que as variáveis são do tipo real, isto é, pertencem ao conjunto dos números reais. É fundamental que se especifique na linguagem C/C++ o tipo de todas as variáveis que estamos utilizando no programa. Esta especificação do tipo de dados está relacionada à dimensão (tamanho) da informaçãoe serve para que o compilador C/C++ possa reservar um espaço de memória correspondente para o armazenamento de tal informação. A maioria das linguagens de programação exige que os tipos de todos os dados sejam especificados. 1.3.3 Tipos de Dados Primitivos Em geral, na linguagem de programação C/C++, os comandos de especificação das variáveis ocupam o topo (início) do programa. Cada linguagem de programação possui seus próprios tipos de dados internamente definidos. Os tipos de dados internos de uma linguagem de programação são denominados tipos primitivos. Os tipos primitivos variam de linguagem para linguagem. Geralmente, têm-se os tipos básicos mostrados na Figura 1.3 a seguir. FIGURA 1.3. TIPOS DE DADOS PRIMITIVOS BÁSICOS Para cada um dos tipos de dados definidos acima está associada uma palavra reservada da linguagem, de modo que variáveis de tais tipos possam ser identificadas e criadas na memória do computador. Tais palavras reservadas em C/C++ estão mostradas na Tabela 1.1, a seguir. Estas /* Programa que calcula a soma entre dois números reais fornecidos pelo usuário */ #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { float N1, N2, soma; system("PAUSE"); return EXIT_SUCCESS; } Conjunto de números inteiros {1, 2, 3, -1, -10... }. Conjunto de números reais {1.2, 3.45, -5.78,... }. Conjunto de caracteres {‘a’, ‘b’, ‘x’, ‘! ’, ‘|’,... }. Conjunto definido por cadeia de caracteres {“teatro”, “b”, “casa”...}. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 9 palavras são ditas reservadas, pois não podem ser utilizadas no programa, isto é, estão reservadas para a identificação dos tipos de dados. TABELA 1.1. PALAVRAS RESERVADAS PARA OS TIPOS DE DADOS PRIMITIVOS BÁSICOS EM C/C++. Tipo de dado Palavra C/C++ Inteiro (positivos e negativos) int Real (positivos e negativos) float Caractere (um único caractere) char Real (reais de precisão dupla) double Cadeia de caracteres string Apesar de não ser considerado um tipo primitivo, é importante ainda destacar o tipo utilizado para o armazenamento de uma cadeia de caracteres, a qual pode ser declarada utilizando-se a palavra reservada string. Nos exemplos a seguir são apresentados comandos que definem algumas variáveis dos mais diferentes tipos. Exemplos: int a, b; float num1, num2; double lado1, lado2, area; string nome, sobreNome; char nome1[40]; No exemplo acima são criadas as variáveis inteiras a, b; as variáveis reais de precisão simples num1, num2; as variáveis reais de precisão dupla lado1, lado2, area; as variáveis tipo texto nome, sobreNome e a variável nome1, tipo caractere. Com relação a esta última, é importante destacar que esta variável é criada como um vetor de caracteres com 40 posições, e que, de certa forma é equivalente a criar uma variável string nome1 com 40 posições (veremos mais adiante que isso não é exatamente o que ocorre). A manipulação de vetores de dados será estudada mais adiante na disciplina. É importante destacar que as variáveis criadas possuem apenas o espaço de memória reservado, mas ainda não possuem valores internos armazenados. A atribuição e leitura de valores para estas variáveis, que de fato introduzem valores na memória para tais variávies, serão discutidas a seguir. Com estes tipos de dados básicos podemos trabalhar a resolução da maioria dos problemas computacionais. Em capítulos subseqüentes aprenderemos também a criar novos tipos de dados a partir dos tipos de dados primitivos definidos acima. 1.4 COMPILANDO E EXECUTANDO PROGRAMAS Para que todo programa possa ser executado por um usuário, é necessário que antes ele seja compilado. Para executarmos, por exemplo, um programa escrito na linguagem C/C++, é necessário um compilador C/C++. A função básica dos compiladores é reconhecer a seqüência de comandos. Se todos os comandos forem reconhecidos e não violarem a sintaxe (forma padrão na qual cada comando deve ser inserido) da linguagem de programação, então o compilador gera um programa executável, que pode ser utilizado por qualquer usuário. O programa executável criado em geral possui um nome igual ao do arquivo fonte (onde o programa C/C++ está definido), seguido da extensão “.exe”. Supondo que o trecho de programa Adição abaixo Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 10 estivesse salvo no arquivo Adição.C++ (os arquivos fonte da linguagem C/C++ são salvos com extensão .C ou .C++ ), ao compilarmos o programa Adição, seria criado o executável Adição.exe. Se o compilador encontrar algum comando que ele não reconhece no algoritmo, ou algum comando reconhecido, mas com uma sintaxe errada, ele não consegue criar um programa executável e geralmente escreve na tela alguma mensagem, para que o programador possa saber onde existe um erro. O processo de compilação do programa Adição está representado na Figura 1.4, a seguir. FIGURA 1.4. PROCESSO DE COMPILAÇÃO Todo comando da linguagem possui a sua sintaxe própria, e o C/C++ é extremamente rigoroso com relação à correção desta sintaxe. À medida que formos conhecendo cada comando, conheceremos também a sua sintaxe, para que possamos utilizar o comando de forma correta. A sintaxe de definição de uma variável de entrada na linguagem C/C++, conforme vimos no programa Adição, escrito anteriormente, é do tipo: float N1, N2, soma; Ou seja, deve aparecer a palavra reservada float seguida de um espaço em branco, e a seguir a lista das variáveis que estão sendo criadas, separadas por vírgula. Ao fim da linha, introduzimos o símbolo “;”, o qual identifica o fim do comando. Pode-se declarar um número qualquer de variáveis. Quando o programa for executado por algum usuário, o comando float acima é executado a partir do programa executável da seguinte forma: são reservados 3 espaços de memória correspondentes aos nomes N1, N2, soma. Nestes espaços de memória serão armazenados somente números reais. Uma imagem da memória do computador com espaços reservados de memória para as variáveis N1 e N2 é mostrado na Figura 1.5. FIGURA 1.5. RESERVANDO ESPAÇOS DE MEMÓRIA É muito importante salientar que se um espaço de memória é reservado para uma variável inteira, não se pode armazenar valores reais, ou caracteres, etc., neste espaço. Neste espaço de memória só serão permitidos números inteiros. A declaração do tipo de todas as variáveis do problema computacional é absolutamente necessária em C/C++. compilação Adição.exe Programa Adição 1.1.1.1.1.1 Memória N1 N2 soma Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 11 1.5 COMANDOS QUE ALTERAM OS DADOS NA MEMÓRIA 1.5.1 Comandos de leitura de dados Vimos que todo programa necessita de dados de entrada, para que as informações de saída possam ser calculadas. Vimos também que estes dados devem ser escolhidos por um usuário do programa. Como então podemos fazer com que o usuário escolha o valor das variáveis de entrada? A linguagem C/C++ não define uma única palavra-chave que identifique a entrada/saída de dados. Em C/C++ a entrada/saída de dados é feita através de funções de biblioteca. Alguns dos comandosque permitem ao usuário escolher os dados de entrada do programa são: cin, gets (utilizado para a leitura de variáveis do tipo caractere ou cadeia de caracteres) e scanf (utilizado para a leitura de variáveis de vários tipos). Tais comandos são denominados comandos de leitura. Eles têm basicamente a função de permitir ao usuário que este entre com as informações necessárias à solução de determinado problema computacional. Exemplos de especificação da sintaxe destes comandos são dados a seguir. Exemplos: gets(nome); scanf(&N1); scanf(&N2); cin >> N1 >> N2; O primeiro comando lê a variável nome. O segundo e terceiro comandos fazem a leitura das variáveis N1 e N2 respectivamente repare a necessidade do caractere &. O quarto comando lê as duas variáveis N1 e N2 em um único comando. Ao serem executados, esses comandos de leitura fazem com que o programa fique aguardando que o usuário escolha um número inteiro qualquer e tecle ENTER. Assim que o usuário escolher este número ele será armazenado no espaço de memória reservados às variáveis lidas. É necessário que o comando que declara as variáveis tenha sido já introduzido antes do comando de leitura dado acima, de modo que o espaço de memória já esteja reservado para as respectivas variáveis. O comando scanf possui uma sintaxe bem mais complexa (cheia de detalhes e especificidades) a qual não é conveniente para um curso inicial de programação. Assim, neste texto, sempre que quisermos fazer uma leitura de dados utilizaremos o comando cin, o qual pode ser utilizado para a leitura de qualquer dos tipos de dados primitivos descritos anteriormente. Entretanto, em situações em que é necessário fazer a leitura de uma string que apresente espaços em branco, armazenando-a, por exemplo, na variável s1, o comando cin >> s1 não deve ser utilizado, já que este interpretará o espaço em branco como sendo o fim do conteúdo desta variável. Nesses casos é conveniente utilizar o comando gets(s1). Este comando lê todo o conteúdo digitado, incluindo-se os caracteres de espaço em branco e ENTER. Assim, o comando gets é utilizado para a leitura de caracteres ou de uma cadeia de caracteres, contendo caracteres especiais tais como os espaços em branco. Entretanto, alguns problemas podem aparecer quando da utilização do comando de leitura gets, pois como ele é sensível ao caractere ENTER, ele pode acabar lendo valores anteriormente armazenados na stream de entrada de comandos anteriores, conforme mostrado no Programa 1.3 a seguir. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 12 PROGRAMA 1.3. DETALHES DO COMANDO GETS ( ). É importante destacar que no comando scanf(&N1) aparece, antes da variável N1, o caractere &. Para o compilador C/C++ isto significa que o comando recebe um ponteiro para a variável N1 e não a própria variável. Isto ficará mais claro quando estudarmos o conceito de ponteiros. Por hora, é importante lembrarmos que ao utilizarmos o comando scanf para fazer a leitura, devemos introduzir o & antes das variáveis lidas. É importante lembrar ainda que o comando scanf também é sensível aos caracteres especiais, e que se deve tomar as mesmas precauções comentadas acima para o comando gets quando de sua utilização. Suponha que após a execução dos comandos cin >> N1; o usuário tenha digitado o número inteiro 12 e ENTER. A memória do computador será alterada conforme mostra a Figura 1.6. FIGURA 1.6. ALTERANDO O CONTEÚDO DA VARIÁVEL N1 ATRAVÉS DE UM COMANDO DE LEITURA. 1.5.2 Comando de Atribuição Os comandos de atribuição são utilizados para que um determinado conteúdo seja atribuído a uma variável. Um exemplo de sintaxe do comando de atribuição em C/C++ é mostrado a seguir. a = 45; #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { string a; char c[40]; cin >> a; gets(c); // esta leitura lê o ENTER do comando anterior (refugo) gets(c); // esta leitura lê a string que realmente se deseja ler system("PAUSE"); return EXIT_SUCCESS; } Memória 12 N1 N2 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 13 O conteúdo da variável a, à esquerda do comando de atribuição = será alterado para o valor mostrado à direita da igualdade. Ao ser executado, este comando altera, portanto, o conteúdo da variável a para o valor 45, o que está mostrado na Figura 1.7. FIGURA 1.7. ATRIBUINDO VALOR 45 À VARIÁVEL a. Outros exemplos de comandos de atribuição são mostrados a seguir. Exemplos: x = 4; x = x + 2; y = 2.5; sexo = ‘F’; No primeiro exemplo, o valor 4 é armazenado na variável x. No segundo exemplo, o valor atual de x é substituído pela soma do valor atual com o valor 2. No terceiro exemplo, o valor real 2.5 é armazenado em y. No último exemplo, o caractere F é armazenado na variável sexo. Para atribuir um valor a uma variável declarada como uma cadeia de caracteres (tal como a variável nome de exemplos anteriores) é necessário utilizar a função strcpy, conforme mostrado nos exemplos a seguir. Para que a função strcpy possa ser de fato utilizada no programa é necessário incluir (através do comando #include), a biblioteca string.h no topo do programa. Nesse caso o comando de atribuição desse tipo de variável é feito sem a utilização do operador =. Exemplos de atribuição para as variáveis nome e time são mostrados a seguir. No primeiro caso, a variável nome armazena o conteúdo “Joaquim” e no segundo caso, a variável time guarda o conteúdo “S. E. Palmeiras”. Exemplos: strcpy(nome, “Joaquim”); strcpy(time, “S. E. Palmeiras”); É importante salientar as diferenças entre os comandos de leitura de informações e o comando de atribuição. Ambos alteram a memória do computador, mas somente o comando de leitura permite que o usuário faça tal alteração. No comando de atribuição o usuário não participa no processo de alteração da memória. Neste caso o valor do lado direito do comando de atribuição é armazenado na memória sem a participação do usuário. O comando de atribuição também permite que o lado direito da igualdade seja dado por uma expressão aritmética geral, envolvendo outras variáveis (já declaradas e inicializadas), tais como nos exemplos a seguir. Em todos os casos, a operação à direita do operador de igualdade é realizada e, a seguir, o resultado é armazenado na variável à esquerda da igualdade. Memória 45 a Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 14 Exemplos: a = (45*b)/2; a = (45+b)/2; b = (2*b)- (2*c)/2; 1.6 COMANDOS DE IMPRESSÃO A essência de um programa de computação é a sua solução, que deve ser dada através das suas variáveis de saída. É necessário haver um comando que mostre ao usuário os dados de saída através da impressão destes em algum meio compreensível pelo usuário. Para imprimirmos um valor (na tela ou em outro dispositivo de saída) para que o usuário possa visualizá-lo, utilizamos os comandos de impressão C/C++ printf e cout. A sintaxe básica destes comandos é mostrada nos exemplos abaixo que são detalhados a seguir. Exemplos: printf (“%f”, N1); cout << N1 ; Ao ser executado, os comandos acimaimprimem na tela (ou em outro dispositivo de saída) o conteúdo da variável float N1. O formato de impressão “%f” do comando foi utilizado, pois a variável a ser impressa (N1) é do tipo float. O comando cout não necessita da definição do tipo da variável, o que facilita a sua utilização. Ao utilizarmos o comando printf, se necessitarmos imprimir outros tipos de variáveis, devemos utilizar os formatos mostrados na Tabela 1.2. TABELA 1.2. FORMATAÇÃO PARA OS COMANDOS PRINTF E SCANF. Código Formato %c Caractere %d Inteiros decimais com sinal %i Inteiros decimais com sinal %e Notação científica (com e minúsculo) %E Notação científica (com E maiúsculo) %f Ponto flutuante decimal %g Usa %e ou %f (o que for mais curto) %G Usa %E ou %f (o que for mais curto) %s String de caracteres %% Escreve o símbolo % Os comandos de impressão também podem ser utilizados para imprimir mensagens informativas para o usuário, na tela do computador e ou em outro dispositivo de saída qualquer. Nesse caso, a sintaxe dos comandos de impressão é diferente, conforme exemplos mostrados a seguir. Exemplos: printf (“Usuário, entre com o valor do primeiro lado”); printf (“Não é possível fazer os cálculos solicitados”); printf (“Erro! Incompatibilidade de dados”); cout << “Erro! Incompatibilidade de dados” ; Neste caso, a sintaxe do comando exige que mensagem venha sempre escrita entre duplas aspas. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 15 Quando este comando for executado aparecerá na tela do computador (ou em outro dispositivo de saída qualquer) o texto entre as duplas aspas. Pode-se ainda imprimir informações textuais combinadas com dados obtidos do conteúdo de determinadas variáveis; Exemplos: printf(“Valor de N1 = %5.2f”, N1); cout << “Valor de N1 = ” << N1 ; No primeiro exemplo, o comando imprimirá na tela o texto “Valor de N1 = “ e em seguida irá imprimir o conteúdo da variável N1 formatado com 5 casas decimais, sendo 2 casas após a vírgula. No segundo comando, o mesmo texto será impresso juntamente com o conteúdo da variável, o qual será terá formato padrão internamente definido pela linguagem. O comando \n utilizado internamente ao comando printf faz com que se salte uma linha na formatação de impressão. No comando cout, devemos utilizar o comando endl, se quisermos saltar uma linha, conforme mostrado nos exemplos abaixo. Outros comandos são também utilizados para a formatação das informações de saída, tanto para o comando printf como para o cout. Exemplos: printf(“Valor de N1 = %5.2f \n”, N1); cout << “Valor de N1 = ” << N1 << endl ; Em suma, o comando de impressão permite que o programador escreva na tela o conteúdo de uma variável ou uma mensagem qualquer que deve ser recebida pelo usuário. Este também é, portanto, um comando que permite comunicação com o usuário, mostrando-lhe informações ou conteúdos de variáveis. Nesta disciplina, utilizaremos preferencialmente o comando de impressão cout, por sua facilidade de sintaxe e de utilização. 1.7 RESOLVENDO NOSSO PRIMEIRO PROBLEMA COMPUTACIONAL Já temos conhecimento de cada um dos comandos para escrevermos um programa C++ que leia dois números quaisquer e calcule a soma destes dois números. O Programa 1.4, nosso primeiro programa computacional, é mostrado a seguir. Este programa resolve o problema de soma de dois números quaisquer, isto é, para quaisquer valores que o usuário escolha, a soma será corretamente calculada. Os comandos que compõem a seqüência de resolução do programa devem ficar entre as chaves {} no corpo do programa. Repare que todo o trecho de programa interno às chaves foi espaçado de modo a tornar mais visível o início e fim do programa. Este espaçamento é denominado de endentação. Esta é uma boa prática em programação e permite que o texto do programa fique bem organizado visualmente. Repare que os comandos de impressão de mensagens avisam ao usuário quando ele deve entrar com o valor das variáveis. Sem esses comandos o usuário não saberia o que está se passando, e não teria noção de que é necessário entrar com os dados. O comando de impressão também facilita a comunicação com o usuário imprimindo a mensagem "A soma dos numeros vale: ", antes de imprimir a soma. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 16 PROGRAMA 1.4. PRIMEIRO PROGRAMA COMPUTACIONAL 1.8 EXERCÍCIOS 1) Escrever um programa em C que calcula o montante de dinheiro em um fundo de aplicação de um banco, conhecendo-se o saldo inicial, a taxa de juros mensal paga pelo banco e a quantidade de meses que o dinheiro está aplicado. Todas estas informações devem ser fornecidas pelo usuário. O programa deve solicitar ainda o nome do usuário, o nome do banco, e imprimir a seguinte informação na tela. “Nome do usuário, você tem um saldo atual de (valor em reais) no banco (nome do banco)”. 2) Seja o seguinte problema computacional: dados dois lados de um retângulo calcular a sua área. a) Quais são os dados de entrada do problema? b) Quais são os dados de saída? c) Escolha um nome para as variáveis de entrada e saída. d) Qual o tipo das variáveis de entrada e de saída? e) Escreva, com a sintaxe correta, o comando que cria um algoritmo chamado Area para a resolução deste problema. f) Escreva, com a sintaxe correta, o comando que define os objetivos do algoritmo Area. O que acontece quando este comando é executado? g) Escreva, com a sintaxe correta, o comando que define as variáveis de entrada e saída do Algoritmo Area. O que acontece quando este comando é executado? h) Escreva, com a sintaxe correta, os comandos que permitam que o usuário do programa Area possa escolher as variáveis de entrada. O que acontece quando o comando é executado? i) Se após a execução do comando dado em (h), houver um comando de atribuição alterando o valor de uma das variáveis de entrada. O que acontece com esta variável? /* Programa que calcula a soma entre dos números reais fornecidos pelo usuário */ #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { float a,b,s; cout << "Entre com o primeiro numero: "; cin >> a; cout << "Entre com o segundo numero: "; cin >> b; s = a+b; cout << "A soma dos numeros vale: " << s << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 17 1.9 EXPRESSÕES ARITMÉTICAS As expressões aritméticas são freqüentemente utilizadas em programação, em geral com o intuito de atribuir valores a uma determinada variável. Por exemplo, para calcularmos a média das notas dos alunos de uma turma é necessário atribuirmos a expressão aritmética que calcula a média a uma determinada variável. Conforme já estudado, a tarefa de atribuir valor a uma determinada variável é feita pelo operador de atribuição, que em C/C++ é descrito pelo operador = . O operador de atribuição já foi utilizado no programa anterior, quando atribuímos N1+N2 à variável soma (ver programa anterior). O operador de atribuição atribui o valor calculado pela expressão aritmética da direita à variável localizada à esquerda, conforme representado na Figura 1.8. FIGURA 1.8. ATRIBUIÇÃO ATRAVÉS DE EXPRESSÃO ARITMÉTICA As expressões aritméticas são basicamente compostas por uma combinaçãode operadores aritméticos e funções. A seguir, os principais operadores aritméticos e as principiais funções aritméticas C/C++ serão descritos. Antes, porém, é importante destacar o processo de conversão de tipos de dados, uma característica importante da linguagem C/C++, o que será feito na seção a seguir. 1.9.1 Conversão de Tipos Em determinadas expressões aritméticas C/C++ é possível que variáveis de diferentes tipos estejam presentes. Esta é uma característica bastante específica da linguagem, que permite uma maior flexibilidade de utilização por parte do programador, mas que também pode dar margem a utilizações incorretas e/ou a perda de informações. Quando uma expressão aritmética C/C++ envolve variáveis de mais de um tipo, o compilador adota a seguinte regra: Regra Exemplo O trecho descrito no Programa 1.5, a seguir, mostra uma série de atribuições e comenta os valores que serão armazenados nas variáveis. 1.9.2 Operadores Aritméticos Na matemática um operador é uma entidade que quando aplicada a um operando produz um resultado específico desejado. O operador de soma, por exemplo, quando aplicado a dois números reais quaisquer, produz a soma destes números. A linguagem C/C++ possui os operadores básicos definidos na matemática (soma, subtração, etc.) e também alguns outros não definidos na matemática. A maioria dos operadores C/C++ necessita de dois operandos para que variável = expressão aritmética O valor da expressão do lado direito do comando de atribuição é convertido para o tipo de dado da variável do lado esquerdo do comando de atribuição. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 18 a operação seja possível. Quando o operador C/C++ pede apenas um operando, dizemos que este operador é unário. A Tabela 1.3 a seguir, descreve os operadores aritméticos mais significativos da linguagem C/C++. PROGRAMA 1.5. TRECHO DE PROGRAMA EXPLORANDO A CONVERSÃO DE TIPOS. TABELA 1.3. PRINCIPAIS OPERADORES ARITMÉTICOS C/C++. Operador Ação + Soma de dois operandos. - Subtração (e também menos unário). * Multiplicação de dois operandos. / Divisão de dois operandos. % Resto da divisão de dois operandos. -- Decremento unitário de um operando ++ Incremento unitário de um operando Exemplos O trecho do Programa 1.6, descrito a seguir, mostra uma série de operações aritméticas e comenta os valores que serão armazenados nas variáveis em decorrência destas operações. Para um melhor entendimento de cada comando, é conveniente que o aluno execute o programa em casa e faça alterações com objetivo de testar os operadores. #include <stdio.h> #include <stdlib.h> int main( ) { int x char ch; float f; : : ch = x; /* ch armazena os 8 bits menos significativos de x */ x = f; /* x armazena a parte inteira de f */ f = ch; /* o valor inteiro associado a ch com 8 bits é convertido em ponto flutuante e armazenado em f */ f = x; /* o valor inteiro associado a x com 16 bits é convertido em ponto flutuante e armazenado em f */ system("PAUSE"); return 0; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 19 PROGRAMA 1.6. TRECHO DE PROGRAMA EXPLORANDO OS OPERADORES ARITMÉTICOS. 1.9.3 Funções Matemáticas O C/C++ possui funções matemáticas específicas pré-programadas que podem ser utilizadas pelo programador no desenvolvimento de seus programas. Todo compilador C/C++ possui uma biblioteca padrão de funções que realizam as tarefas necessárias mais comuns. O padrão ANSI especifica o conjunto mínimo de funções que estará contido na biblioteca, entretanto cada compilador C/C++ possui o seu conjunto de funções de biblioteca. Em geral, os compiladores possuem muitas funções adicionais, além das funções previstas no padrão ANSI. Neste curso, trabalharemos apenas com as funções matemáticas mais comuns e utilizadas na engenharia. Funções mais específicas devem ser consultadas no manual do compilador C/C++. Uma descrição de algumas das principais funções matemáticas C/C++ é dada na Tabela 1.4. #include <cstdlib> #include <iostream> using namespace std; int main( ) { int x,y,z,k,k1; float a,b; x = 5; y = 2; a = x/y; /* cuidado!! a não conterá o resultado da divisão, mas o valor 2.0 */ cout << "o valor contido em a sera: " << a <<endl; a = float(x)/y; /* a conterá o resultado da divisão, ou seja o valor 2.5 */ cout << "o novo valor contido em a sera: " << a <<endl; b = x*y; /* b conterá o resultado da multiplicação convertido para real, ou seja, 10.0 */ cout << "o valor contido em b sera: " << b <<endl; k1 = y/x; /* k1 a parte inteira da divisão de y por x, ou seja 0 */ cout << "o valor contido em k1 sera: " << k1 <<endl; z = x%y; /* z conterá o resto da divisão de x pó y, ou seja, 1*/ cout << "o valor contido em z sera: " << z <<endl; k = y%x; /* k conterá o resto da divisão de y pó x, ou seja, 2*/ cout << "o valor contido em k sera: " << k <<endl; ++x; /* x é incrementado de 1, ou seja, conterá o valor 6 */ cout << "o valor contido em x sera: " << x <<endl; --y; /* y é decrementado de 1, ou seja, conterá o valor 1 */ cout << "o valor contido em y sera: " << y <<endl; system("PAUSE"); return 0; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 20 TABELA 1.4. ALGUMAS DAS FUNÇÕES MATEMÁTICAS C/C++ MAIS UTILIZADAS Função Exemplo Comentário sin sin(x) Calcula sen(x) cos cos(x) Calcula cos(x) tan tan(x) Calcula tan(x) sqrt sqrt(x) Calcula x exp exp(x) Calcula xe log log(x) Calcula ln x log10 log10(x) Calcula 10log x pow pow(x,y) Calcula yx fabs fabs(x) Calcula x floor floor(x) Arredonda x real para o inteiro inferior mais próximo ceil ceil(x) Arredonda x real para o inteiro superior mais próximo É conveniente lembrar que as operações envolvendo as funções trigonométricas (seno, cosseno, etc.) são feitas, em C/C++, utilizando ângulos em radianos. Importante: para que possamos trabalhar com estas funções é necessário incluir no início do programa C/C+ + uma biblioteca de funções matemáticas. Em C++ esta biblioteca é o cmath, e pode ser incluída utilizando-se a diretiva de inclusão #include <cmath> no topo do programa. Exemplos O trecho de Programa 7, descrito a seguir, mostra uma série de funções matemática, e comenta os valores que devem ser armazenados nas variáveis em decorrência da utilização destas funções; 1.10 EXERCÍCIOS 1) Escreva um programa C/C++ que recebe dois catetos de um triângulo retângulo e calcula a hipotenusa deste triângulo. 2) Escreva um programa C/C++ que recebe os valores de a, b e c associados a uma função do segundo grau dada por 2ax bx c , e calcula as raízes desta função. Por simplicidade, suponha que todas as equações do segundo grau avaliadas pelo programa possuem duas raízes reais e distintas. 3) Escreva um programa C/C++ que recebe um valor de medida expresso em mm e calcula a quantidade de metros (m), decímetros (dm), centímetros (cm) e milímetros (mm) associados à medida. 1.11 RELAÇÕES DE PRECEDÊNCIAEM EXPRESSÕES ARITMÉTICAS Uma expressão aritmética pode conter uma grande quantidade de operações aritméticas, utilizando uma combinação de operações matemáticas básicas e funções aritméticas para Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 21 produzir o resultado esperado pelo programador. É necessário estabelecer uma relação de precedência entre as operações, de modo que se tenha uma expectativa do valor exato da expressão aritmética. Seja a expressão aritmética C/C++ dada a seguir: a=5/2+3; PROGRAMA 1.7. TRECHO DE PROGRAMA EXPLORANDO ALGUMAS FUNÇÕES MATEMÁTICAS. O resultado armazenado na variável a depende de qual operação aritmética será realizado primeiro. Se realizarmos primeiramente a operação de divisão o resultado esperado é que o valor 5.5 seja armazenado na variável a; entretanto, se a operação de soma for realizada primeiramente, o resultado passa a ser a = 1. A Figura 1.9 a seguir mostra a relação de precedência das principais operações aritméticas em C/C++. #include <cstdlib> #include <iostream> #include <cmath> using namespace std; int main() { const double PI=3.14159265358979; double a,b,c,d; double k0,k1,k2,k3,k4,k5; b=5.325; d=90.; a=PI/2.0; k0=sin(d); /* k0 conterá o valor 0.8939 (sen(90) radianos e não graus)*/ cout << "valor de K0 sera: " << k0 << endl; k1=cos(a); /* k1 conterá o valor 0 correspondente a cos(pi/2) radianos*/ cout << "valor de K1 sera: " << k1 << endl; k2=sin(a); /* k2 conterá o valor 1 correspondente a sen(pi/2) radianos*/ cout << "valor de K2 sera: " << k2 << endl; k3=ceil(b); /* k3 conterá o valor 6 arredondamento de 5.325 para cima*/ cout << "valor de K3 sera: " << k3 << endl; k4=floor(b); /* k4 conterá o valor 5 arredondamento de 5.325 para baixo*/ cout << "valor de K4 sera: " << k4 << endl; k5=pow(a,b); /* k5 conterá o valor 11.0748 = ba */ cout << "valor de K5 sera: " << k5 << endl; system("PAUSE"); return 0; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 22 FIGURA 1.9. RELAÇÕES DE PRECEDÊNCIA DAS PRINCIPAIS OPERAÇÕES ARITMÉTICAS. ++ -- - ( menos unário) * / % + - aumento da precedência aumento da precedência Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 23 Capítulo 2 Estruturas de Seleção em Linguagem C/C++ 2.1 INTRODUÇÃO No capítulo anterior foram estudados os comandos básicos que permitem estruturar um problema computacional em termos de suas variáveis de entrada e saída. Vimos que cada problema computacional deve ser analisado e estruturado em uma linguagem comercial de forma que possa ser decomposto em comandos que serão executados pelo compilador. Não existem “fórmulas” mágicas para a resolução dos algoritmos. Cada algoritmo deve ser analisado separadamente e sempre terá múltiplas soluções, dependendo do programador. A análise se constitui, portanto, em uma das fases mais importantes da resolução dos problemas computacionais. Na análise, procura-se estudar o problema, utilizando todo o conhecimento que se dispõe, até que ele possa ser estruturado em comandos de linguagem algorítmica. Para que possamos estruturar melhor os problemas computacionais é preciso conhecer os principais comandos que são aceitos pelas linguagens de programação. Quanto mais comandos conhecermos de uma linguagem, mais teremos capacidade de estruturar um problema nesta linguagem. Além dos comandos já estudados, existem algumas estruturas que são utilizadas em praticamente todas as linguagens de programação, e que permitem estruturar melhor as soluções de problemas computacionais. São elas as estruturas de seleção e as estruturas de repetição. As estruturas de seleção permitem que o programador escolha entre dois blocos (conjuntos) de comandos a serem executados. Tais estruturas permitem, portanto, que o programa se desvie para executar um ou outro bloco de comandos, dependendo de uma determinada condição. As estruturas de repetição permitem que um determinado bloco de comandos seja executado de forma repetitiva. Pode-se fazer o controle das repetições por uma condição ou por uma variável (chamada variável de controle) que controla o número de repetições que se deseja. As estruturas de seleção serão estudadas neste capítulo. As estruturas de repetição serão estudadas no capítulo seguinte. 2.2 ESTRUTURAS DE SELEÇÃO Muitas vezes, a resolução de um problema computacional depende de uma determinada condição qualquer. Se tal condição ocorre, então a resolução deste problema é feita de um determinado modo, caso tal condição não ocorra o problema é resolvido de forma alternativa. Seja, por exemplo, o problema prático de calcular o imposto INSS de um funcionário qualquer de uma empresa. Suponha que pelas leis vigentes atuais, um funcionário só terá este desconto se o seu rendimento for maior do que um valor qualquer (chamado valor base) instituído pelo governo. Temos então a pergunta que dá origem ao problema computacional: Qual será o imposto devido por um funcionário qualquer, dado o seu rendimento? A resposta a esta questão depende do rendimento do funcionário. Esta condição é mostrada a seguir na Figura 2.1. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 24 FIGURA 2.1. ESTRUTURA DE SOLUÇÃO DO PROBLEMA COMPUTACIONAL PARA CALCULAR O IMPOSTO INSS DE UM FUNCIONÁRIO Veja que a solução do problema não é direta; ela depende de uma condição a qual está destacada em vermelho na Figura 2.1. As estruturas de seleção foram projetadas nos compiladores para permitir que o programa faça um teste em uma determinada condição e selecione (escolha) um, ou outro caminho, para resolver um problema, dependendo desta condição. A estrutura de seleção é mostrada, em linguagem algorítmica, na Figura 2.2. FIGURA 2.2. ESTRUTURA DE SELEÇÃO EM LINGUAGEM ALGORÍTMICA. Esta estrutura estabelece que se a condição for avaliada como sendo verdadeira (V), o programa executará o bloco de comando 1 na figura (deixando o bloco de comando 2 sem ser executado). Se a condição for avaliada como falsa (F), somente o bloco de comando 2 será executado, (deixando o bloco de comando 1 sem ser executado). Após a execução de um ou outro bloco, o programa segue a sua estrutura seqüencial. A estrutura de seleção pode ser ainda descrita utilizando um diagrama de blocos, que mostra a seqüência dos comandos, conforme mostrado na Figura 2.3. FIGURA 2.3. ESTRUTURA DE SELEÇÃO EM DIAGRAMA DE BLOCOS. Repare que, na Figura 2.2 e Figura 2.3, os retângulos marcam o início e fim dos comandos que devem ser executados, dependendo da condição. É interessante que os blocos de comandos sejam ligeiramente afastados (como mostrado na Figura 2.2), para que possamos visualizar mais se (condição) então bloco de comandos 1 senão bloco de comandos 2 condição bloco de comandos 1 bloco de comandos 2 V F : : : : Se (funcionário possui rendimento menor ou igual ao valor base) então seu imposto será zero senão seu imposto será calculado sobre o seu rendimentoNotas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 25 facilmente que bloco de comandos será executado pelo programa. Esta é uma regra de programação que deixa o programa visualmente mais claro para o programador. Este afastamento é às vezes denominado endentação (em inglês, “indent” significa parágrafo). Pode- se omitir o desenho do retângulo na estrutura de seleção, no caso em que o bloco de comandos é composto por apenas 1 comando. Mesmo neste caso, é interessante manter a endentação. Pode ser que dentro de um bloco de comandos seja necessário utilizar outra estrutura de seleção. Neste caso alguns autores dizem que as estruturas estão “aninhadas”. Um exemplo de estruturas de seleção aninhadas é mostrado na Figura 2.4. A condição que aparece nos comandos de seleção sempre é dada como uma (ou mais) relação entre variáveis e/ou constantes. As expressões relacionais e lógicas que expressam condições verdadeiras ou falsas na linguagem C/C++ serão estudadas em seções subseqüentes. FIGURA 2.4. ESTRUTURAS DE SELEÇÃO ANINHADAS 2.2.1 Exemplo – Cálculo do INSS Suponha que no problema do cálculo do INSS, descrito anteriormente, tenha havido uma mudança na legislação de modo a diferenciar o imposto devido da seguinte forma: o contribuinte com salário abaixo do valor base continua pagando imposto nulo; o contribuinte com salário acima do valor base tem taxas diferenciadas de imposto, sendo que aquele que recebe até R$ 3.000,00 deve pagar uma taxa de 20% sobre o seu salário; e para aquele que recebe salários acima deste valor a taxa deverá ser de 30%. A partir dessa mudança na legislação, é necessário reescrever a estrutura de seleção relacionada a este novo problema computacional. Isso é feito a seguir utilizando-se uma linguagem algorítmica e o diagrama de blocos. Adotando-se as variáveis inss, salario, salarioBase, cujas representações são óbvias no problema, temos a estrutura de seleção mostrada na Figura 2.5. Nessa estrutura aninhada, se a primeira condição (salario <= salarioBase) for verdadeira, o inss será feito nulo e nenhum dos demais comandos da estrutura será executado. Se tal condição for falsa, o curso do programa é deslocado para a avaliação da condição mais interna (salário <= 3000). Se esta condição for verdadeira, o inss é calculado como 20% do rendimento e caso contrário o inss é calculado como 30% do rendimento. se (condição 1) então bloco de comandos 1 senão se (condição 2) então bloco de comandos 2 senão bloco de comandos 3 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 26 FIGURA 2.5. ESTRUTURA DE SELEÇÃO ANINHADA DO EXEMPLO – CÁLCULO DO INSS O diagrama de blocos correspondente à estrutura de seleção da Figura 2.5 é dado na Figura 2.6. FIGURA 2.6. DIAGRAMA DE BLOCOS DA ESTRUTURA DE SELEÇÃO ANINHADA DO EXEMPLO 1 2.3 ESTRUTURAS DE SELEÇÃO EM LINGUAGEM C/C++ Na linguagem C/C++, a seleção é implementada através da estrutura if – else. A estrutura de seleção descrita, em linguagem de algoritmo, na Figura 1 é implementada na linguagem C/C++, conforme mostrado no trecho dado no Programa 2.1. A condição que aparece na estrutura de seleção é uma expressão lógica, cujo resultado é verdadeiro ou falso. Se a condição for verdadeira, o comando executará o bloco de comandos abaixo do comentário /* bloco de comandos 1 */; caso contrário será executado o trecho abaixo do comentário /* bloco de comandos 2 */. Uma expressão lógica pode envolver alguns operadores ainda não descritos, tais como os operadores relacionais e lógicos, os quais serão estudados em detalhes em seções a seguir. Em C/C++, considera-se verdadeiro qualquer valor que seja diferente de zero. Assim, qualquer expressão lógica cujo resultado tiver valor diferente de zero será verdadeira; e qualquer expressão lógica cujo resultado seja igual a zero será falsa. Alguns exemplos simples de expressões lógicas C/C++ são dados nos exemplos a salario <=salarioBase inss = 0; V F : : inss =salario*0.20; salario <=3000 inss =salario*0.30; V F se (salario <= salarioBase) então inss = 0; senão se (salário <= 3000) então inss =salario*0.20; senão inss = salario*0.30; Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 27 seguir, sendo que as regras formais de estruturação de expressões lógicas serão discutidas mais adiante, quando forem introduzidos os operadores lógicos e relacionais. PROGRAMA 2.1. TRECHO DE PROGRAMA DESTACANDO A ESTRUTURA DE SELEÇÃO EM C/C++. Figura 6. Estrutura de Seleção em C Exemplos: a > b /* condição será verdadeira se a > b */ a >(b-2) /* condição será verdadeira se a > (b-2) */ a <= 0 /* condição será verdadeira se for negativo ou nulo */ A representação do início e fim dos blocos de comandos que serão executados é feita utilizando respectivamente a abertura e fechamento de chaves {}. Os colchetes e parênteses não podem ser utilizados para este fim na linguagem C/C++. Repare que apenas um dos blocos de comando será executado. Uma estrutura de seleção simplificada, conforme mostrado no trecho de Programa 2.2 também é possível em C/C++. Neste caso o /* bloco de comandos 1 */ será executado se a condição for verdadeira e em caso contrário o programa segue seu curso de forma seqüencial, sem executar o bloco de comandos 1. A estrutura de seleção dada no Programa 2.2 é chamada de estrutura de seleção simples, em contraposição àquela mostrada no Programa 2.1 e denominada de estrutura de seleção composta. PROGRAMA 2.2. TRECHO DE PROGRAMA DESTACANDO A ESTRUTURA DE SELEÇÃO SIMPLES EM C/C++. Exemplo 3.2 Com o conhecimento da sintaxe da estrutura de seleção é possível descrever um programa C/C++ que recebe o salário de um funcionário e calcula o INSS que deve ser pago por este funcionário. A regra para o cálculo do INSS é conforme está descrito na Figura 2.5. O programa C/C++ utilizado para resolver este problema deve implementar a lógica de cálculo do inss descrita no diagrama de blocos da Figura 2.6. A solução é mostrada no Programa 2.3. if (condição) /* bloco de comandos 1 */ else /* bloco de comandos 2 */ { { } } : : if (condição) /* bloco de comandos 1 */ { } : : Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 28 PROGRAMA 2.3. SOLUÇÃO DO EXEMPLO 3.2. A linguagem C/C++ permite a omissão das chaves {} nos casos em que o bloco de comandos é constituído apenas por um único comando. Assim, como exemplo, pode-se simplificar um trecho do , conforme mostrado na Figura 2.7. FIGURA 2.7. SIMPLIFICAÇÃO DO COMANDO IF – ELSE PARA BLOCOS COM COMANDO ÚNICO. 2.4 EXPRESSÕES LÓGICAS Vimos que a estrutura de seleção está diretamente relacionada às expressões lógicas, através do conceito de condição. De fato, quando a condição da estrutura de seleção, representada por uma expressão lógica, apresenta um valor verdadeiro (em C/C++ qualquer valor diferente de zero), o programa segue seqüencialmente por um caminho; e em casocontrário, o programa toma outro caminho seqüencial. Na maioria das linguagens, as expressões lógicas são diretamente associadas ao conceito de relação. Definiremos aqui, uma relação como sendo uma comparação entre dois dados utilizando os operadores relacionais (basicamente os operadores da matemática <, >, =, etc.). Esta #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { float inss,salario,salarioBase; cout << "Entre com seu salario: "; cin >> salario; salarioBase=1000; if(salario<=salarioBase) inss=0.0; else { if(salario<=3000) { inss=0.2*salario; } else { inss=0.3*salario; } } cout << "O imposto (INSS) devido e de: " << inss << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } if(salario < salarioBase) { inss=0 } if(salario < salarioBase) inss=0 equivale a Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 29 comparação quando é feita resulta em um valor verdadeiro se a relação se confirma como uma verdade e falso, caso contrário. Exemplos: a > b; /*o resultado será verdadeiro (diferente de zero em C) se a for maior que b no momento da avaliação da relação; e falso (igual a zero em C) caso contrário */ a <= b; /*o resultado será verdadeiro (diferente de zero em C) se a for menor o igual que b no momento da avaliação da relação; e falso (igual a zero em C)caso contrário */ Uma expressão lógica pode ser entendida como um inter-relacionamento lógico entre relações, construído utilizando-se operadores relacionais, lógicos e aritméticos, descritos a seguir. Em C/C++, ao contrário de muitas outras linguagens, não são definidas variáveis lógicas, já que se trabalha sempre com os valores 0 e não-zero para representar respectivamente os estados falso e verdadeiro. Assim, em C/C++ podem aparecer expressões lógicas sem que operadores relacionais estejam envolvidos. Por exemplo, o comando a seguir if(b) printf("o inverso de b vale: %f", 1/b); está correto, já que irá imprimir na tela o inverso do número real b, somente se este número for diferente de zero. Veja que a expressão lógica da estrutura do if não apresenta nenhuma comparação explícita entre valores de variáveis, já que só a variável b aparece na expressão lógica. Internamente, entretanto, o comando compara o valor de b com o zero para verificar se a expressão é verdadeira ou falsa. 2.4.1 Operadores Relacionais Os operadores relacionais basicamente comparam dados, verificando, em geral, as relações de dimensão entre estes dados (comparações de maior, menor, igual diferente, etc.). Em geral, os mesmos símbolos da matemática são utilizados para as comparações em C/C++, à exceção dos símbolos para as comparações de igualdade e diferença de valores. Um resumo dos principais operadores relacionais da linguagem C/C++ é mostrado na Tabela 2.1. TABELA 2.1. OPERADORES RELACIONAIS EM C/C++. Operador Ação > maior que >= maior ou igual que < menor que <= menor ou igual que = = igual ! = diferente Em C/C++, o resultado de uma relação é igual a 1 se a expressão lógica é verdadeira e igual a zero se a expressão lógica for falsa. Exemplos: int a,b,c,d,e; Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 30 a = 2; b = 1; c = a > b /* a variável c irá armazenar o valor 1 */ d = a >(b-2) /* a variável d irá armazenar o valor 1 */ e = a <= 0 /* a variável e irá armazenar o valor 0 */ O trecho de programa acima não teria o mesmo resultado se as variáveis fossem declaradas como reais (faça um teste!). 2.4.2 Operadores Lógicos Os operadores lógicos operam de modo a conectar relações. Assim, é muito freqüente que os operadores lógicos e relacionais apareçam juntos em uma expressão aritmética. Os operadores lógicos aparecem na álgebra de Boole, sendo que os principais são estão descritos na Tabela 2.2. TABELA 2.2. OPERADORES LÓGICOS EM C. Operador Operação && AND (E) || OR (OU) ! NOT (NÃO) Os operadores lógicos são muito utilizados quando se quer construir uma expressão lógica composta por mais de uma relação. Seja por exemplo o cálculo do inss do exemplo 3.2. Pode-se dizer que o INSS de uma pessoa cujo salário é maior que o salário base e menor que 3000 reais é dado por 20% do salário desta pessoa. Se utilizarmos as variáveis descritas no Programa 2.3, a expressão lógica que descreve este fato é dada por: (sal > salarioBase) && (sal<3000) As operações para cada um dos operadores lógicos são em geral descritas pelas chamadas tabelas-verdade. Estas tabelas estabelecem quais os resultados esperados dependendo dos valores dos operandos A e B. As tabelas-verdade das principais operações lógicas são mostradas na Figura 2.8. FIGURA 2.8. TABELAS-VERDADE DAS OPERAÇÕES LÓGICAS AND, OR E NOT. Com as informações da tabela verdade, vê-se que a expressão (sal > salarioBase) && (sal<3000) terá como resultado uma valor verdadeiro sempre que um determinado salário for maior que o salário base e este salário for superior a 3000. Podemos utilizar expressões lógicas semelhantes para refazermos o Programa 2.3, conforme mostrado no Programa 2.4. Operador AND Operador OR Operador NOT A V F F V V F F F F F V V A B V F V F V F V V A B Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 31 PROGRAMA 2.4. REESCREVENDO O PROGRAMA DO EXEMPLO 3.2 COM USO DE OPERADORES LÓGICOS O trecho do Programa 2.5, descrito a seguir, mostra uma série de expressões lógicas e comenta os valores que serão armazenados nas variáveis. PROGRAMA 2.5. TRECHO DE PROGRAMA EXPLORANDO OS OPERADORES LÓGICOS #include <stdio.h> #include <stdlib.h> int main() { float sal, inss, salarioBase; salarioBase = 1000; cout << "Entre com o salario do funcionario: "; cin >> sal; if(sal < salarioBase) inss=0; if((sal > salarioBase) && (sal<3000)) inss= sal*0.20; if((sal > salarioBase) && (sal>=3000)) inss= sal*0.30; cout << "O funcionario devera pagar um INSS correspondente a: " << inss <<endl <<endl; system("PAUSE"); return EXIT_SUCCESS; } #include <stdio.h> #include <stdlib.h> int main() { int a,b,c,d,k1,k2,k3,k4,k5,k6,k7; a=5; b=2; c=3; d=3; k1=a==b; /* k1 armazena o valor 0*/ cout << "k1 = " << k1 << endl; k2=c==d; /* k2 armazena o valor 1*/ cout << "k2 = " << k2 << endl; k3=(c==d) || ( a==b); /* k3 armazena o valor 1*/ cout << "k3 = " << k3 << endl;; k4=(c==d) && ( a!=b); /* k4 armazena o valor 1*/ cout << "k4 = " << k4 << endl; k5=(a>b) and ( c>=d); /* k5 armazena o valor 1*/ cout << "k5 = " << k5 << endl; k6=(a>b) && ( b>c); /* k6 armazena o valor 0*/ cout << "k6 = " << k6 << endl; k7=!k6; /* k7 armazena o valor 1*/ cout <<"k7 = " << k7 << endl; system("PAUSE"); return 0; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 32 2.4.3 Relações de Precedência para Operadores Relacionais e Lógicos Da mesma forma que os operadores aritméticos, os operadores lógicos, e relacionais também possuem um relação de precedência interna pré-definida pela linguagem C/C++. Esta relação é mostrada na Figura 2.9. FIGURA 2.9. RELAÇÃO DE PRECEDÊNCIA PARA OS OPERADORES RELACIONAIS E LÓGICOS 2.5 EXERCÍCIOS 1) Dados 3 números inteiros distintos escrever um programa C/C++ que imprime na tela o maior entre eles. 2) Uma empresa decidiu dar uma gratificação de natal aos seus funcionários com base no número de horas extras trabalhadas. Dado o número de horas extras trabalhadas de um empregado, escrever um programa C/C++ que calcula o prêmio, baseado na Tabela 2.3. TABELA 2.3. TABELA PARA O EXERCÍCIO 2 3) Seja um funcionário que trabalha em uma empresa de tecidos. O seu salário é calculado de acordo com a quantidade total de metros de tecidos vendidos em um mês. Este valor é calculado de acordo com a Tabela 2.4. Descrever um programa C/C++ que calcula o salário mensal do funcionário dada a quantidade de tecido (em metros) vendida no mês. TABELA 2.4. TABELA PARA O EXERCÍCIO 3 ! = = != && aumento da precedência aumento da precedência | | > >= < <= Horas Prêmio (0, 10] 20,00 (10,40] 60,00 (40, ... 200,00 metros salário (R$) (0, 300] 600,00 (300,600] 800,00 (600 .. ] 900,00 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 33 Média Ponderada Classificação [ 8 , 10 ] A [ 6 , 8 ) B [ 4 , 6 ) C [ 0 , 4 ) D 4) Dado um número inteiro qualquer, descrever um programa C/C++ que estipula se este número é ou não múltiplo de 3. 5) Dado um número inteiro, descrever um programa C/C++ que estipula se este número é par ou ímpar. 6) Dado um número inteiro x, e 4 números a, b , c e d, sendo que a < b < c < d, descrever um programa C/C++ que define em qual intervalo se encontra o número de entrada x. Se x for igual a alguns dos números (a, b , c, d), mostrar um aviso na tela. 7) Suponha que a contribuição do INSS de uma determinada pessoa física é calculada de acordo com um valor percentual do salário anual, conforme a Tabela 2.5. Descreva um algoritmo que faça o cálculo do INSS com base nesta tabela. TABELA 2.5. TABELA PARA O EXERCÍCIO 7 8) É dado um gabarito de uma prova que consta de 3 questões, cujas respostas podem ser qualquer uma das 3 alternativas ‘A’, ‘B’ ou ‘C’. São dadas também as respostas para as três questões fornecidas por um estudante que se submeteu à prova. Sabendo-se que a primeira questão vale 4 pontos e as demais valem 3 pontos descrever um programa C/C++ que calcula a nota do aluno. 9) Descrever um programa C/C++ que classifica um aluno de acordo com suas notas (dadas de 0 a 10). São fornecidas 5 notas (em 5 matérias diferentes). A classificação do aluno é dada pela média ponderada das 5 matérias, de acordo com a Tabela 2.6. TABELA 2.6. TABELA PARA O EXERCÍCIO 9 Salário (RS) Valor % Até 10,000 0.0 10,000 a 15,000 8.0 15,000 a 50,000 15.0 Acima de 50,000 35.0 Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 34 Capítulo 3 Estruturas de Repetição em Linguagem C/C++ 3.1 INTRODUÇÃO Em algumas situações, é necessário que um comando, ou uma seqüência (ou bloco) de comandos seja executado de forma repetida. Suponha, por exemplo, que se deseja fazer um programa que faça a leitura de 500 números. Com o que foi aprendido até aqui, seria necessário declarar 500 variáveis e utilizar o comando de leitura de dados 500 vezes. Assim, é bastante conveniente que apenas uma variável seja declarada e utilizada dentro de um processo de repetição, de forma que os 500 números possam ser lidos. Neste capítulo será introduzida a estrutura de repetição, que permite implementar situações como esta. A estrutura de repetição permite que um bloco de comandos qualquer seja repetido várias vezes. O número de vezes que o bloco de comandos deve ser repetido pode ser controlado por uma variável de controle ou por uma condição (escrita geralmente como uma expressão lógica já estudada no capítulo anterior). No primeiro caso, diz-se que a estrutura de repetição é controlada por variável de controle; no segundo caso, diz-se que é uma estrutura de repetição controlada por condição. A utilização de uma ou outra estrutura de repetição em geral tem a ver com a natureza do problema que se está estruturando. De uma forma geral, se o número de vezes que queremos repetir um determinado bloco de comando for conhecido de antemão, usa-se a repetição controlada por variável. Se o número de vezes que se deseja repetir um bloco de comandos não for conhecido, e só se souber que é necessário repetir o bloco de comandos até que determinada situação aconteça (ou enquanto determinada situação acontecer), então se utiliza a repetição controlada por condição. 3.2 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR VARIÁVEL DE CONTROLE Neste tipo de estrutura de repetição, utilizamos uma variável interna, denominada de variável de controle, para controlar o número de vezes que o bloco de comandos será executado. Podemos compreender melhor esta estrutura através de um exemplo. Seja o , que avalia se um número dado é ou não múltiplo de 3. Suponha que se deseja fazer um programa que avalie 500 números fornecidos pelo usuário de forma a verificar se cada um dos números é ou não primo. Para que não seja necessário recorrer ao expediente de reescrever o trecho de código (bloco de comandos) 500 vezes, deve-se utilizar o comando de repetição nesse caso. Veja que nesse caso sabe-se de antemão quantas vezes é necessário repetir o bloco de comandos, ou seja: 500 vezes. Na verdade não é necessário repetir o programa inteiro 500 vezes, basta repetir o bloco de comandos que nos interessa, conforme esquematizado na Figura 3.1. Esquematização de uma repetição de um bloco de comandos 500 vezes. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 35 PROGRAMA 3.1. PROGRAMA QUE AVALIA SE UM NÚMERO DADO É OU NÃO MÚLTIPLO DE 3 FIGURA 3.1. ESQUEMATIZAÇÃO DE UMA REPETIÇÃO DE UM BLOCO DE COMANDOS 500 VEZES É importante notar que a cada vez que o trecho do programa (mostrado na Figura 3.1) for executado, a variável numero perderá seu valor anterior e assumirá o valor de um novo número. Isso não será um problema para a lógica do programa, uma vez que a variável anterior já terá sido avaliada quanto a ser ou não múltipla de três. Assim, o programa funcionará perfeitamente. 3.3 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR VARIÁVEL DE CONTROLE EM C/C++ A estrutura de repetição C/C++ utilizada para a repetição controlada por variável de controle é dada pelo comando for. Sua sintaxe geral é mostrada a seguir no Programa 3.2. PROGRAMA 3.2. TRECHO DE PROGRAMA DESTACANDO ASINTAXE DO COMANDO FOR #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero; cout << "Entre com o numero: " ; cin >> numero; if (numero%3==0) cout << "O numero " << numero << " e multiplo de 3 " << endl; else cout << "O numero " << numero << " nao e multiplo de 3 " << endl; system("PAUSE"); return EXIT_SUCCESS; } for (inicialização; condição; incremento) { /* bloco de comando a ser repetido */ } cout << "Entre com o numero: " ; cin >> numero; if (numero%3==0) cout << "O numero " << numero << " e multiplo de 3 " << endl; else cout << "O numero " << numero << " nao e multiplo de 3 " << endl; Repetir 500 vezes Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 36 Cada uma dos termos do comando (mostrados em vermelho no Programa 3.2) é descrito a seguir: Inicialização: comando de atribuição utilizado para inicializar a variável de controle do comando de repetição (laço). Pode conter também um comando de declaração desta variável, que deve ser declarada como um inteiro. Exemplos de inicialização: int i=0 int x=15 int k=0 Condição: expressão lógica que determina quando a repetição deve terminar. A repetição (laço) termina quando a expressão lógica possui valor falso, isto é, zero. Exemplos de condição: i < 500 x < 1400 Incremento: comando de incrementa a variável de controle da repetição. Exemplos de incremento: i=i+1 // incrementos de um em um x=x+2 // incrementos de dois em dois i++ // incremento utilizando o operador de incrementos Com o conhecimento da sintaxe do comando for, pode-se agora implementar o algoritmo que foi esquematizado na Figura 3.1, conforme mostrado no Programa 3.3. PROGRAMA 3.3. IMPLEMENTAÇÃO DA REPETIÇÃO ESQUEMATIZADA NA FIGURA 3.1. 3.3.1 Exemplo – Impressão de números inteiros Escrever um programa C/C++ que calcula e imprime na tela os números inteiros de 1 a 100. A implementação deste exemplo é mostrada no Programa 3.4. #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero; for(int i=1; i<=500; i++) { cout << "Entre com o numero: " ; cin >> numero; if (numero%3==0) cout << "O numero " << numero << " e multiplo de 3 " << endl; else cout << "O numero " << numero << " nao e multiplo de 3 " << endl; } system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 37 PROGRAMA 3.4. IMPLEMENTAÇÃO C/C++ DO ALGORITMO DA SEÇÃO 3.3.1 3.3.2 Exemplo – Impressão de inteiros pares Escrever um programa C/C++ que calcula e imprime na tela os números inteiros pares de 1 a 100. A implementação deste exemplo é mostrada no Programa 3.5. PROGRAMA 3.5. IMPLEMENTAÇÃO C/C++ DO ALGORITMO DA SEÇÃO 3.3.2 3.3.3 Exemplo – Contagem de múltiplos de 3 Escrever um programa C/C++ que lê uma lista de 500 números inteiros e conta a quantidade de números inteiros que são múltiplos de 3. A implementação deste exemplo é mostrada no Programa 3.6. #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero; for(int i=1; i<=100; i++) { cout << i << endl ; } system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero; for(int i=1; i<=100; i++) { if((i%2)==0) cout << i << endl ; } system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 38 PROGRAMA 3.6. IMPLEMENTAÇÃO C/C++ DO ALGORITMO DA SEÇÃO 3.3.3. 3.3.4 Exemplo – Cálculo do Fatorial Escrever um programa C/C++ que lê um número fornecido pelo usuário e calcula o fatorial deste número. A implementação deste exemplo é mostrada no Programa 3.7. PROGRAMA 3.7. IMPLEMENTAÇÃO C/C++ DO ALGORITMO DA SEÇÃO 3.3.4 #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero, fat; cout << "Entre com o numero: " ; cin >> numero; fat=1; for(int i=1; i<=numero; i++) { fat=fat*i; } cout << "O Fatorial de " << numero << "vale: " << fat << endl; system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int numero, conta; conta=0; for(int i=1; i<=500; i++) { cout << "Entre com o numero: " ; cin >> numero; if (numero%3==0) conta++; } cout << "A quantidade de multiplos de 3 digitada foi de: " << conta << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 39 3.4 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR CONDIÇÃO Em algumas situações não sabemos exatamente quantas vezes devemos repetir um trecho de programa, só sabemos que devemos repeti-lo até que uma determinada condição aconteça (ou enquanto uma determinada condição acontecer). Nesses casos, não faz sentido utilizar uma variável de controle, já que não se sabe precisar o número de vezes que o bloco de comandos será repetido. Para a implementação deste tipo de repetição, é definida, na maioria das linguagens de programação estruturada, uma estrutura denominada estrutura de repetição controlada por condição. Podemos compreender melhor este tipo de estrutura através de um exemplo. 3.4.1 Exemplo – Lista fornecida por um usuário Seja o seguinte exemplo: dada uma lista de números inteiros positivos qualquer, fornecida pelo usuário, determinar os seus quadrados. A lista termina quando o usuário digitar um número não positivo. Repare que o exemplo não cita quantos são os números (se são N ou M, etc.). Isso significa que nem o usuário nem o programador sabem exatamente quantos números devem ser analisados no algoritmo. De fato, a cada vez que o usuário utilizar o programa ele pode digitar uma quantidade qualquer de números positivos. Podemos fazer um esboço do algoritmo conforme mostrado na Figura 3.2. FIGURA 3.2. ESBOÇO DO ALGORITMO DO EXEMPLO DADO NA SEÇÃO Se soubéssemos o número de vezes que o algoritmo deveria ser executado poderíamos utilizar o comando de repetição já estudado para resolver o problemadescrito no esboço acima. Sabemos, entretanto, que a lista é composta por um conjunto de números inteiros positivos. Isto significa que assim que aparecer um número negativo (ou nulo) devemos parar de repetir o bloco. Sabemos, portanto, que a condição de parada para a repetição é o aparecimento de um número negativo (ou nulo). Na linguagem algorítmica, podemos representar uma repetição controlada por condição através de duas formas básicas mostradas na Figura 3.3. Enquanto elemento for positivo repetir Ler primeiro elemento da lista Imprimir resultado Elevar elemento ao quadrado Ler próximo elemento Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 40 FIGURA 3.3. DUAS FORMAS DE REPRESENTAÇÃO DO COMANDO DE REPETIÇÃO CONTROLADA POR CONDIÇÃO EM LINGUAGEM ALGORÍTMICA No primeiro comando, fazem parte da sintaxe do comando as palavras “enquanto” e “faça”. A condição de parada de repetição é uma expressão lógica como aquelas estudadas nos comandos de seleção. No segundo comando, fazem parte da sintaxe as palavras “repita” e “até que”. A condição de parada também é uma expressão lógica. Existe uma pequena diferença entre os comandos. No primeiro, o bloco de comandos só é executado se a condição for verdadeira. No segundo, o bloco de comandos é executado pelo menos uma vez, porque somente após a execução do bloco a condição é testada. Podemos utilizar qualquer um dos comandos para a solução de problemas com estruturas de repetição controladas por condição. Vamos utilizar o comando enquanto - faça para resolvermos o problema dado no exemplo que estamos estudando. A aplicação do comando é direta, conforme mostrado no algoritmo da Figura 3.4. FIGURA 3.4. RESOLUÇÃO DO EXEMPLO 5 UTILIZANDO A ESTRUTURA ALGORÍTMICA ENQUANTO-FAÇA Observe que os próximos números são lidos ao fim do comando de repetição. Isso garante que o próximo número só será processado se ele for não negativo. Também é conveniente ressaltar que cada número lido o conteúdo do anterior é perdido, já que se está utilizando uma mesma variável para ler os elementos da lista. Podemos também utilizar a estrutura de comando repita – enquanto para resolvermos o exercício. O algoritmo é mostrado a seguir, na Figura 3.5. Repare que se entrarmos com uma lista nula de elementos, ou seja, se o primeiro elemento da lista já for negativo, esta estrutura de comando irá executar pelo menos uma vez o bloco de comandos. Assim, a execução apresentará o quadrado deste número negativo, o que não está de acordo com as especificações do algoritmo. Neste caso, o primeiro algoritmo é mais eficaz para a resolução do problema. bloco de comandos enquanto (condição) faça repita até que (condição) bloco de comandos Leia(A); enquanto (A > 0) faça Q = A*A ; Imprima (A,Q) ; Leia(A) ; Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 41 FIGURA 3.5. RESOLUÇÃO DO EXEMPLO 5 UTILIZANDO A ESTRUTURA REPITA – ENQUANTO 3.5 ESTRUTURAS DE REPETIÇÃO CONTROLADAS POR CONDIÇÃO EM C/C++ Em C/C++, as estruturas de repetição enquanto-faça e repita-enquanto, são descritas pelos comandos while e do-while, respectivamente. A sintaxe do comando while é mostrada no trecho do Programa 3.8. Este comando é análogo à estrutura enquanto-faça. O bloco de comandos a ser executado é definido internamente ao abre- e-fecha das chaves. PROGRAMA 3.8. TRECHO DE PROGRAMA DESTACANDO A SINTAXE DO COMANDO WHILE. A condição é representada por uma expressão lógica. Se a condição for verdadeira (diferente de 0, em C/C++), o bloco de comandos é executado mais uma vez; se a condição for falsa, a execução do programa se desloca para fora das chaves, de modo que o comando que se segue ao while será executado. É importante reparar que neste tipo de repetição não existe nenhuma variável interna (variável de controle) que conte quantas vezes a repetição está sendo executada. A sintaxe do comando do-while é mostrada no trecho do Programa 3.9. Este comando é análogo à estrutura retipa-enquanto. O bloco de comandos a ser executado é definido internamente ao abre-e-fecha das chaves. PROGRAMA 3.9. TRECHO DE PROGRAMA DESTACANDO A SINTAXE DO COMANDO DO-WHILE. É importante enfatizar que na primeira vez que esta estrutura é executada o teste da condição ocorre após a execução do bloco de comandos a ser repetido. Leia(A); repita Q = A*A ; Imprima(A,Q) ; Leia(A) ; enquanto (A> 0) while (condição) { /* bloco de comando a ser repetido */ } do { /* bloco de comando a ser repetido */ } while (condição); Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 42 Podemos implementar o algoritmo que resolve exemplo estudado nesta seção, descrito na Figura 3.4 utilizando a estrutura while, conforme mostrado no Programa 3.10. O mesmo exemplo é implementado no Programa 3.11, utilizando-se a estrutura do-while. PROGRAMA 3.10. RESOLUÇÃO DO EXEMPLO 5 UTILIZANDO A ESTRUTURA WHILE PROGRAMA 3.11. RESOLUÇÃO DO EXEMPLO 5 UTILIZANDO A ESTRUTURA DO-WHILE #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int a,q; cout << "Entre com o primeiro numero: " ; cin >> a; while (a>0) { q=a*a; cout << "O valor de " << a << " ao quadrado vale: " << q << endl << endl; cout << "Entre com o proximo numero: (numero negativo para terminar)" ; cin >> a; } system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int a,q; cout << "Entre com o primeiro numero: " ; cin >> a; do { q=a*a; cout << "O valor de " << a << " ao quadrado vale: " << q << endl << endl; cout << "Entre com o proximo numero: (numero negativo para terminar)" ; cin >> a; } while(a>0); system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 43 Como exercício, execute este último programa e entre com uma lista nula, para verificar a deficiência do programa para esse caso. 3.5.1 Exemplo – Números primos Seja o exemplo a seguir: descrever um programa C/C++ que verifica se um número dado é ou não primo. Sabe-se os números primos são aqueles que são divisíveis por 1 e por ele mesmo. Assim, se quisermos avaliar se um determinado número N fornecido pelo usuário é ou não primo, deve-se fazer uma repetição e gerar uma lista de números variando de dois até N-1; devemos dividir o número N por cada um desses números da lista e a cada vez que o resto da divisão for zero, saberemos queele é divisível por outro número que não seja o 1 e ele mesmo. O algoritmo é mostrado no Programa 3.12. PROGRAMA 3.12. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 3.5.1. 3.5.2 Exemplo – Lista de números primos Seja o seguinte exemplo: descrever um programa C/C++ que verifica se os elementos de uma lista de números fornecidos pelo usuário são ou não primos. A lista termina quando o usuário digita um número negativo. A idéia geral consiste em repetirmos um trecho do Programa 3.12 enquanto o usuário digitar números N maiores ou iguais a zero. A solução deste algoritmo é mostrada no Programa 3.13. #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int n, cont; cout << "Entre com o numero a ser analisado: "; cin >> n; cont=0; for(int i=2;i<n;i++) { if(n%i==0) cont++; } if (cont==0) cout << "O numero " << n << " eh primo" << endl << endl; else cout << "O numero " << n << " nao eh primo" << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 44 PROGRAMA 3.13. RESOLUÇÃO DO EXEMPLO 7. 3.6 EXERCÍCIOS 1) Descrever um programa C/C++ que dada uma lista de números inteiros positivos, terminada com o número 999, calcula a raiz quadrada destes números. 2) Descrever um programa C/C++ que, dada uma lista de números terminada com –999, calcula a média aritmética destes números. 3) Descrever um programa C/C++ que, dada uma lista de números terminada com –1, calcula o maior dentre estes números. 4) Descrever um programa C/C++ que dada uma lista de caracteres que termina com ‘F’ encontra a posição (da lista) em que aparece a letra ‘A’. 5) Descrever um programa C/C++ que dados o nome, a idade e salário de uma lista de funcionários, calcula o funcionário com maior salário, e imprime seu nome e idade. A lista de funcionários termina com a palavra ‘FIM’. 6) Elabore um programa C/C++ que determine o valor de S, onde S é definido por: #include <cstdlib> #include <iostream> using namespace std; int main(int argc, char *argv[]) { int n, cont; cout << "Entre com o primeiro numero a ser analisado: "; cin >> n; while (n>=0) { cont=0; for(int i=2;i<n;i++) { if(n%i==0) cont++; } if (cont==0) cout << "O numero " << n << " eh primo" << endl << endl; else cout << "O numero " << n << " nao eh primo" << endl << endl; cout << "Entre com o proximo numero a ser analisado: "; cin >> n; } system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 45 1 2 3 4 50 2*3 3* 4 4*5 5*6 51*52 S 7) Faça um programa C/C++ que calcule o primeiro termo da seqüência de Fibonacci que seja maior que 5000. A seqüência de Fibonacci é construída de modo que o termo atual corresponde à soma dos dois últimos termos, conforme abaixo: {1,1,2,3,5 ...} Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 46 Capítulo 4 Manipulação de Vetores e Matrizes em C/C++ 4.1 INTRODUÇÃO Imagine que seja necessário armazenar em seu programa um milhão de informações. Pelo que vimos até aqui, seria necessário declarar um milhão de variáveis, do tipo dado1, dado2,..., etc. Nessas situações, é importante estruturarmos as informações de determinadas variáveis na forma vetorial. Várias situações práticas envolvem a manipulação de vetores e matrizes, especialmente quando se trata da resolução de problemas de engenharia. Vimos anteriormente, que se um dado é guardado em uma determinada variável, a informação anteriormente armazenada naquela variável é perdida. Assim, só é possível armazenar uma informação por vez em uma dada variável. Os vetores/matrizes são estruturas que permitem que várias informações sejam armazenadas na mesma variável, sem que as informações anteriormente armazenadas sejam perdidas. O que permite que essa característica seja implementada é a definição de índices de armazenamento para cada uma das informações armazenadas nos vetores/matrizes. Assim, por exemplo, se armazenarmos as informações na primeira posição de um vetor e a segunda informação na segunda posição do vetor, e assim sucessivamente, as informações anteriormente armazenadas no vetor não serão perdidas. Assim, ao final da leitura de um vetor, todas as informações salvas neste vetor podem também ser recuperadas através da especificação dos respectivos índices. 4.2 VETORES E MATRIZES EM C/C++ Vetores/Matrizes são uma maneira de gerenciar uma coleção de “um milhão” de dados do tipo double, ou int, ou até mesmo do tipo string. Muitas outras estruturas de dados conseguem gerenciar coleções de dados, mas os vetores/matrizes se diferenciam dos outros pela facilidade de utilização e pelo fato de manipularem apenas dados do mesmo tipo. A linguagem C/C++ possibilita algumas formas de trabalharmos com as estruturas de dados de vetores/matrizes. Nesta apostila adotaremos a classe vector para trabalharmos com a manipulação destas estruturas de dados. A classe vector é uma forma de gerenciamento de dados relativamente bem projetada que admite a semântica de matrizes (indexação), bem como uma série de métodos específicos associados às matrizes, tais como alteração de elementos, remoção de elementos, gerenciamento de tamanho em tempo de execução, etc. Para a utilização da classe vector em um determinado programa é necessário que a biblioteca <vector> seja incluída no cabeçalho deste programa. A classe vector permite criar vetores de qualquer outro tipo de dados, incluindo o próprio vector. Assim, os vetores mostrados na Figura 4.1 podem ser facilmente armazenados e manipulados utilizando-se a classe vector. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 47 FIGURA 4.1. EXEMPLOS DE VETORES E MATRIZES PASSÍVEIS DE ARMAZENAMENTO UTILIZANDO-SE A CLASSE VECTOR É importante ressaltar que os vetores/matrizes manipulam apenas um tipo de dado. Assim, por exemplo, não é possível criar uma matriz de caracteres e inteiros na mesma estrutura matricial. Na classe vector uma matriz é armazenada como um vetor de vetores, conforme veremos a seguir. 4.3 SINTAXE DE DECLARAÇÃO DE VETORES E MATRIZES Vale ressaltar que para a declaração de qualquer vetor no programa é necessário que antes seja incluída a classe vector no seu projeto C++ (a classe não está incluída em C). A forma geral de declaração de um objeto do tipo vetor é dada na forma mostrada a seguir: O tipo de dado T pode assumir qualquer dos tipos de dado C/C++ já estudados, inclusive o próprio tipo vector. E importante destacar que os vetores declarados desta forma possuem dimensão inicial nula. Uma forma de se declarar um vetor, já definindo inicialmente a suadimensão é mostrada a seguir: Onde tam representa uma expressão aritmética que resulta em um tamanho a ser definido para o vetor. Como exemplo de declaração de variáveis do tipo vetor/matriz, as variáveis mostradas na Figura 4.1 são declaradas conforme mostrado abaixo: A 4 ; B 4,4 ; C 4,4 ; D 2,2 ; vector int vector vector string vector vector float vector vector int Outra forma de declararmos um vetor é inicializando-o com valores default, conforme mostrado a seguir: Neste caso será criado um vetor de dimensão tam e cujos valores iniciais serão cópias de e. Os exemplos a seguir ilustram a utilização desta forma de declaração de vetores. No primeiro exemplo será criado um vetor k com 10 posições e valores iniciais iguais a zero; no segundo, 1 5 4 6 A @ * / 1 & 8 A K c s B f g L c x z 1.2 3.2 5.4 6.4 7.1 3.2 8.9 0.4 3.6 2.3 7.5 3.1 2.9 2.4 5.2 8.0 C 1 2 1 3 D identificadorTvector identificadorT tamvector identificador , T tam evector Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 48 será criado um vetor D de 3 posições contendo valores iniciais iguais a 10. k 10,0 ; D 3,10 ; vector int vector float 4.4 MANIPULAÇÃO DE VETORES E MATRIZES A manipulação (alteração e recuperação) de elementos de um vetor é feita de forma simples, bastando especificar o índice do elemento que se deseja manipular. Assim, operações de leitura, atribuição e impressão de elementos de um vetor podem ser feitas conforme exemplos mostrados no trecho de código do Programa 4.1. PROGRAMA 4.1. EXEMPLO DE MANIPULAÇÃO DE VETORES E MATRIZES UTILIZANDO-SE A CLASSE VECTOR. A leitura/impressão completa de um vetor deve ser feita utilizando-se um comando de repetição que deve variar desde o índice inicial até o índice final dos elementos que se deseja imprimir. Um exemplo de leitura e impressão de um vetor completo, fornecido pelo usuário é mostrado no Programa 4.2. É importante destacar que em C/C++ todos os vetores possuem índice inicial zero. A leitura/impressão completa de uma matriz deve ser feita varrendo-se todos os índices de linhas e colunas, de modo que se possa armazenar cada elemento da matriz. Para gerar os índices de linhas e colunas da matriz, é comum utilizar dois comandos de repetição aninhados. Desta forma, para cada linha i, varreremos os valores de j de zero até a quantidade de colunas. Um exemplo de leitura e impressão de uma matriz completa de caracteres é mostrado no Programa 4.3. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { vector <int> A(4); vector < vector < string> > B(4,4); vector < vector < float> > C(4,4); vector < vector < int> > D(2,2); cin >> A[0]; // leitura do elemento de índice 0 do vetor A B[2][2]= "x"; // atribuição da string "x" à posição (2,2) da matriz B cout << C[1][1]; // impressão da string do elemento (1,1) da matriz C cin >> B[i][j]; // leitura do elemento (i,j) da matriz B : system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 49 PROGRAMA 4.2. LEITURA DE UM VETOR COMPLETO FORNECIDO PELO USUÁRIO. PROGRAMA 4.3. LEITURA DE UMA MATRIZ DE STRINGS COMPLETA FORNECIDO PELO USUÁRIO. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { vector<int> A(5); cout << endl << endl << "Lendo os elementos do vetor" << endl ; for(int i=0; i<5; i++) { cout << "A[ " << i << " ]: " ; cin >> A[i]; } cout << endl << endl << "Imprimindo os elementos do vetor" << endl ; for(int i=0; i<5; i++) { cout << "A[ " << i << " ]: " ; cout << A[i] << endl; } cout << endl <<endl; system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { vector < vector <string> > B(4,4); cout << endl << endl << "Lendo os elementos da matriz" << endl ; for(int i=0; i<4; i++) { for(int j=0; j<4; j++) { cout << "B[ " << i << " , " << j << " ]: " ; cin >> B[i][j]; } } cout << endl << endl << "Imprimindo os elementos da matriz" << endl ; for(int i=0; i<4; i++) { for(int j=0; j<4; j++) { cout << "B[ " << i << " , " << j << " ]: " ; cout << B[i][j] <<endl; } } cout << endl <<endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 50 É importante destacar que as estruturas de repetição descritas nos programas acima são utilizadas sempre que se deseja percorrer todos os elementos da matriz, de modo que são utilizadas com muita freqüência na manipulação dos elementos da matriz. 4.4.1 Exemplo em sala de aula – Média aritmética Descrever um programa C/C++ que armazena em um vetor as notas de 50 alunos e imprime estas notas e a média aritmética das notas. 4.4.2 Exemplo em sala de aula – Seqüência matemática Seja a seqüência de números reais dada a seguir. Armazenar os 50 primeiros elementos da seqüência em um vetor e imprimir os termos da seqüência menores que um determinado valor escolhido pelo usuário. 1 15; 5 2 k k y y y 4.5 MÉTODOS ASSOCIADOS À CLASSE VECTOR Os tipos de dados que utilizamos até aqui são relativamente simples: números, caracteres, etc. Além desses tipos de dados básicos, alguns tipos novos de dados podem ser criados por cada programador utilizando a linguagem de programação orientada a objeto C++. Não estudaremos em profundidade os conceitos relacionados à linguagem orientada a objetos, mas daremos um panorama geral para que possamos compreender a utilização de elementos de classes internas ao C++. Dentre os tipos de dados compostos que podem ser definidos pelo programador, destacam-se as classes. Uma classe é definida como: uma estrutura de dados composta de dados fundamentais, e mesmo por outros dados compostos associada a um conjunto de operações permitidas. Os objetos internos a uma classe são chamados de atributos e as operações permitidas são denominadas de métodos. Um elemento que representa uma implementação específica de uma classe é denominado um objeto da classe. Assim como em int i, pode-se dizer que i é um objeto, já que é uma implementação específica de um int, também se pode declarar um objeto qualquer, que pertençaa uma determinada classe previamente definida pelo programador. Isso já foi feito na seção anterior, ao declararmos objetos pertencentes à classe vector. Nesta apostila não serão abordadas formas de definição de novas classes, mas procuraremos trabalhar com algumas classes internamente definidas em C/C++. Neste capítulo especificamente, trabalharemos com a classe vector. Um método de uma classe pode ser chamado utilizando-se o operador de acesso “.” (ponto). Os principais métodos e operadores associados à classe vector estão mostrados na Tabela 7. Nesta tabela, considera-se que os objetos v e v2 foram previamente declarados como objetos da classe vector. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 51 TABELA 7. RELAÇÃO DE MÉTODOS E OPERADORES ASSOCIADOS O OBJETOS DA CLASSE VECTOR Método Semântica v2 = v; Atribui uma cópia de v a v2 v.assign(num,e); Substitui o conteúdo de v por num cópias de e v.clear(); Esvazia o vetor v. v.empty(); Retorna verdadeiro se o vetor estiver vazio v.pop_back(); Remove o último elemento do vetor v v.push_back(e); Concatena o elemento e ao vetor v v.resize(novoTam); Altera o tamanho do vetor v para novoTam v.resize(novoTam,e); Altera o tamanho do vetor v para novoTam e coloca em cada elemento criado o valor de e v.size(); Retorna o número de elementos de v. 4.5.1 Exemplo – Operações com a classe vector O trecho do Programa 4.4 mostrado a seguir utiliza alguns métodos da classe vector, de modo que se possa avaliar qual a exata operação realizada por alguns métodos definidos na Tabela 7. PROGRAMA 4.4. EXEMPLO DE UTILIZAÇÃO DOS PRINCIPAIS MÉTODOS DA CLASSE VECTOR #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,nt,tamanho; float media; vector <float> v1, v2; nt = 50; /* Utilizacao do método resize*/ v1.resize(nt); v1[0]=5; cout << "Impressao do vetor inicial: " <<endl; for(i=0;i<(nt-1);i++) { v1[i+1]=v1[i]/2 +5; cout << "v1( " << i+1 << ")= " << v1[i]<< endl; } /* Utilizacao do método size e da atribuição*/ cout <<endl << "Impressao da copia do vetor inicial: " <<endl; v2=v1; for(i=0;i<v2.size();i++) { cout << "v2( " << i+1 << ")= " << v2[i]<< endl; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 52 Repare que os vetores de float v1 e v2 são declarados inicialmente como vetores vazios. A seguir, o vetor v1 é redimensionado com tamanho nt. No próximo comando, o valor 0 é atribuído ao elemento inicial do vetor v1. Após, as demais 49 posições do vetor são preenchidas com a seqüência de valores descrita no exemplo da seção 4.4.2. A seguir, é feita a impressão do vetor v1 na tela, com o auxílio do método que retorna o número de elementos de v1. Outros métodos para a inserção e eliminação de elementos do vetor são utilizados, de modo a dar uma idéia geral da utilização dos principais métodos descritos na Tabela 7. 4.5.2 Exemplo – Armazenamento de listas de dados terminadas com dígito chave Seja uma lista de números reais fornecida pelo usuário e terminada com o número 999 (que não faz parte da lista). Descrever um programa C/C++ que armazena esta lista em um vetor de números reais e calcula a média geral dos elementos da lista. A solução desse exemplo é mostrada no Programa 4.5. Neste caso, a lista é irrestrita e é redimensionada em tempo de execução através de métodos do tipo push_back( ). Repara-se que a cada vez que o programa é executado o vetor será dimensionado de uma forma diferente. Esse tipo de redimensionamento em tempo de execução do programa é feito utilizando conceitos de alocação dinâmica de memória. 4.5.3 Exemplo – Redimensionamento de matrizes em tempo real Descrever um programa C/C++ que redimensiona uma matriz de números reais em tempo de execução com as dimensões de linha e coluna definidas pelo usuário. A solução desse exemplo é mostrada no Programa 4.6. O conceito de alocação dinâmica de memória pode ser também utilizado para o redimensionamento de matrizes em tempo de /* Utilizacao dos metodos pop_back*/ cout <<endl << "Retirando ultimo elemento do vetor: " <<endl; v2=v1; v2.pop_back(); for(i=0;i<v2.size();i++) { cout << "v2( " << i+1 << ")= " << v2[i]<< endl; } /* Utilizacao dos metodos push_back*/ cout <<endl << "Introduzindo ultimo elemento (15) do vetor: " <<endl; v2=v1; v2.push_back(15); for(i=0;i<v2.size();i++) { cout << "v2( " << i+1 << ")= " << v2[i]<< endl; } system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 53 execução. Nesse caso, como se trata de um vetor de vetores (matriz), é necessário redimensionar cada um dos elementos do vetor, de modo que é preciso utilizar um comando de repetição. PROGRAMA 4.5. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.2. 4.5.4 Exemplo – Cálculo da quantidade de números primos em uma matriz Escrever um programa C/C++ que lê os dados de uma matriz de inteiros 3 5 e calcula o número de termos da matriz que são primos. A solução deste exemplo é descrita no Programa 4.7. A idéia básica consiste em percorrer toda a matriz lida avaliando cada número quanto a ser ou não primo. Para avaliar se um número n é ou não primo, é necessário utilizar um comando de repetição gerando uma lista de números de 2 até n-1 e contando quantas vezes o número n é divisível pelos números da lista. Se a quantidade for 0, o número é primo, caso contrário, o número não é primo. Este algoritmo já foi previamente estudado no capítulo 4 (envolvendo estruturas de repetição), e é utilizado aqui para avaliar cada elemento da matriz de inteiros. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,nt,ne; float media, e; vector <float> lista; cout << "Entre com elemento 1: "; cin >> e; ne=0; media =0; while(e!= 999) { ne++; lista.push_back(e); cout << "Entre com elemento " << ne+1 << ": "; cin >> e; } cout << endl << "Imprimindo a Lista: " << endl; for(i=0; i<lista.size(); i++) { cout << "Elemento ( " << i+1 << ")= " << lista[i] << endl; media =media+lista[i]; } media=media/lista.size(); cout << endl << "Media da Lista: " << media << endl<< endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 54 PROGRAMA 4.6. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.3.PROGRAMA 4.7. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.4. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,j,nl,nc; vector < vector <float> > A; cout << " Entre com numero de linhas: "; cin >> nl; cout << " Entre com numero de colunas: "; cin >> nc; cout << endl; /* Redimensionando a Matriz em tempo de Execução*/ A.resize(nl); for(j=0;j<nl;j++) A[j].resize(nc); /* Lendo a Matriz*/ for(i=0;i<nl;i++) { for(j=0;j<nc;j++) { cout << "Elemento(" << i+1 << "; " << j+1 << "): "; cin >> A[i][j]; } } cout << endl << "Imprimindo a matriz lida" <<endl; for(i=0;i<nl;i++) { for(j=0;j<nc;j++) { cout << "Elemento(" << i+1 << "; " << j+1 << "): "; cout << A[i][j] << endl; } } system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int np,cont; vector < vector <int> > A(3,5); cout << "Entre com os elementos da matriz de inteiros" << endl <<endl; for(int i=0;i<3;i++) { for (int j=0;j<5;j++) { cout << "Elemento " << i << "," << j << " : "; cin >> A[i][j]; } } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 55 4.5.5 Exemplo – Diagonal principal em uma matriz Escrever um programa C/C++ que lê os elementos de uma matriz de caracteres 4 4 e calcula a quantidade de vezes que a letra ‘x’ aparece acima da diagonal principal. PROGRAMA 4.8. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.5. RESOLUÇÃO DO EXEMPLO 5.7 (CONTINUAÇÃO) np=0; for(i=0;i<3;i++) { for (j=0;j<5;j++) { cont=0; for (int k=2;k<n;k++) { if(A[i][j]%k==0) cont++; } if (cont==0) np++; } cout << "A matriz possui " << np << " primos " << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,j,nx,tamL,tamC; vector < vector <char> > A(4,4); cout << "Entre com os elementos da matriz de inteiros" << endl <<endl; for(i=0;i<4;i++) { for (j=0;j<4;j++) { cout << "Elemento " << i+1 << "," << j+1 << " : "; cin >> A[i][j]; } } nx=0; for(i=0;i<4;i++) { for (j=0;j<4;j++) { if((i<j) and (A[i][j]=='x')) nx++; } } cout << "A matriz possui " << nx << " letras 'x' acima da diagonal" << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 56 A solução do algoritmo é mostrada no Programa 4.8. A idéia geral consiste em percorrer toda a matriz, mas avaliando apenas aqueles que estão acima da diagonal. Os elementos da diagonal são aqueles cujos índices de linha e coluna são iguais; elementos acima da diagonal são aqueles cujos índices de linha são menores que os respectivos índices de coluna; elementos abaixo da diagonal são aqueles cujos índices de linha são menores que os respectivos índices de coluna. 4.5.6 Exemplo – Jogo da Loteria Seja uma matriz 13 3 corresponde a um jogo de loteria. Cada linha corresponde ao resultado de um determinado jogo de futebol. Se o apostador achar que o primeiro time vencerá, então ele deve marcar um ‘x’ na primeira coluna. Se ele optar por empate deverá marcar um ‘x’ na coluna do meio; Se ele optar por vitória do segundo time ele deve marcar o ‘x’ na segunda coluna. O apostador também tem o direito de fazer um (ou mais) duplo ou mesmo um triplo (ou mais). Um duplo corresponde a marcar dois ‘x’ no mesmo jogo. Um triplo corresponde a marcar 3 ‘x’ no mesmo jogo, fechando todas as possibilidades deste jogo. Ao total, a loteria é composta de 13 jogos. Assim, as apostas do jogador podem ser armazenadas em uma matriz de caracteres de 13 3 . Escrever um programa C/C++ que armazena em uma matriz 13 3 um jogo de loteria e calcula a quantidade de duplos e triplos do jogo. A solução é mostrada no Programa 4.9. PROGRAMA 4.9. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.6. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,j,tamL,tamC; int duplos, triplos, contl; vector < vector <char> > A(13,3); tamL=13; tamC=3; for(i=0;i<tamL;i++) { for (j=0;j<tamC;j++) { cout << "Elemento " << i+1 << "," << j+1 << " : "; cin >> A[i][j]; } } duplos=0; triplos=0; for(i=0;i<tamL;i++) { contl=0; for (j=0;j<tamC;j++) { if(A[i][j]=='x') contl++; } if(contl==2) duplos++; if(contl==3) triplos++; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 57 4.5.7 Exemplo – Armazenando índices de matrizes em vetores Escrever um programa C/C++ que armazena em vetores as linhas e colunas de uma matriz real 120 100 onde aparecem elementos negativos. Imprimir estes vetores. A solução do exemplo é mostrada no Programa 4.10. Basta ir acrescentando os elementos aos vetores de linha/coluna. PROGRAMA 4.10. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.7. cout << endl << "Existem: " << endl << duplos << " Duplos " << endl << triplos << " Triplos " <<endl << endl; system("PAUSE"); return EXIT_SUCCESS; } #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { int i,j,l,c,tamL,tamC; vector < vector <int> > A(10,10); vector <int> lin,col; tamL=2; tamC=2; for(i=0;i<tamL;i++) { for (j=0;j<tamC;j++) { cout << "Elemento " << i << "," << j << " : "; cin >> A[i][j]; } } for(i=0;i<tamL;i++) { for (j=0;j<tamC;j++) { if (A[i][j]<0) { lin.push_back(i); col.push_back(j);} } } cout << endl << "Posicoes com numeros negativos: " << endl ; for(i=0;i<lin.size();i++) { l=lin[i]; c=col[i]; cout << "Elemento( " << l << ", " << c << ")= " << A[l][c] << endl; } cout << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 58 4.5.8 Exemplo – Informações de funcionários de uma empresa Seja um conjunto de 1000 funcionários de uma empresa, com informações de nome, sobrenome, número e salário de cada funcionário. Descrever um programa C/C++ que lê estas informações, armazenando-as em vetores e imprime os dados do funcionário com maior salário. A solução do exemplo é mostrada no Programa 4.11. A idéia do programa consiste em encontrar a posição pos dos vetores onde está o funcionário com maior salário. PROGRAMA 4.11. RESOLUÇÃO DO EXEMPLO DA SEÇÃO 4.5.8. #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { string a,b; int pos,c; float d,msal; vector <string> nome; vector <string> sobre; vector <int> numero; vector <float> sal; cout << "Lendo os dados do primeiro funcionario" << endl ; cout << "Nome: " ; cin >> a; while(a!= "FIM") { nome.push_back(a); cout << "Sobrenome: " ; cin >> b; sobre.push_back(b); cout << "Numero: " ; cin >> c; numero.push_back(c); cout << "Salario: " ; cin >> d; sal.push_back(d); cout << endl << endl; cout << "Outro funcionario (Tecle FIM para terminar)" << endl ; cout << "Nome: " ; cin >> a; } msal=sal[0]; pos=0; for(int i=0;i<nome.size();i++) { if(sal[i]>msal) { msal=sal[i]; pos=i; } } cout << endl << endl<< "Os dados do funcionario mais bem pago sao:" << endl; cout << "Nome: " << nome[pos] << endl; cout << "Sobrenome: " << sobre[pos] << endl; cout << "Numero " << numero[pos] << endl; cout << "Salario: " << sal[pos] << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 59 4.5.9 Exemplo – Escala de plantão em um hospital Um hospital necessita ter sempre um médico de plantão. Para isso criou uma escala representada por uma matriz H (24 linhas e 7 colunas), onde as linhas representam as horas do dia e as colunas os dias da semana. Cada posição da matriz H[i,j] da matriz é preenchida com o sobrenome do médico de plantão. Escrever um programa C que: a) Leia a matriz H armazenando os nomes dos médicos de plantão em cada hora, para todos os dias de uma semana; b) Calcule a quantidade total de horas trabalhadas por um médico qualquer escolhido pelo usuário; c) Calcule o número de dias que o médico escolhido em (b) irá trabalhar na semana; d) Gere uma lista (sem repetir nomes) com os sobrenomes de todos os médicos de plantão na semana. A solução do exemplo 5.11 é mostrada no Programa 4.12 a seguir. PROGRAMA 4.12. RESOLUÇÃO DO EXEMPLO 5.11 #include <cstdlib> #include <iostream> #include <vector> using namespace std; int main(int argc, char *argv[]) { vector< vector <string> > H(24,7); vector < string > lista; string nome; int conta, contadia,flag,nlist; for(int i=0;i<2;i++){ for(int j=0;j<2;j++){ cout << "Entre com o medico da hora " << i << " do dia " << j << ": "; cin >> H[i][j]; } } cout << endl<< endl<< endl<< "Imprimindo o Horario dos Medicos" <<endl; for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { if(j==1) cout << H[i][j] << endl; else cout << H[i][j] << " "; } } cout << endl << "Escolha um medico: "; cin >> nome; conta=0; contadia=0; for(int j=0;j<2;j++){ flag=0; for(int i=0;i<2;i++) { if(H[i][j]== nome) { conta++; if(flag==0)contadia++; flag=1; } } } } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 60 cout << "O medido " << nome << " trabalha " << conta << " horas na semana " << endl << endl; cout << "O medido " << nome << " trabalha " << contadia << " dias na semana " << endl; for(int j=0;j<2;j++) { for(int i=0;i<2;i++) { flag=0; for(int m=0;m<lista.size();m++) { if(H[i][j]==lista[m]) flag=1; } if(flag==0){ lista.push_back(H[i][j]); } } } cout << endl << "A lista dos medicos da semana e: " << endl; for(int i=0;i<lista.size();i++) cout << lista[i] << endl; cout << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 61 Capítulo 5 Funções e Procedimentos em C/C++ 5.1 INTRODUÇÃO O desenvolvimento de programas envolve a descrição, em uma linguagem de programação qualquer, de uma seqüência de comandos que devem ser executados pelo computador de modo seqüencial, para a resolução de um problema computacional. Uma boa prática de programação consiste em decompor o programa em pequenas partes, denominadas funções, procedimentos, rotinas, subprogramas, ou sub-rotinas, de modo que estas partes possam ser programadas e testadas de forma independente. O processo de dividir grandes tarefas em pequenas tarefas mais simples de serem resolvidas é natural do ser humano, e torna mais simples a solução de problemas complexos. A decomposição de um programa em sub-rotinas possui algumas vantagens, tais como: 1. Um programa complexo pode ser elaborado a partir de sub-rotinas menores e mais simples de serem desenvolvidas; 2. As partes independentes (sub-rotinas) podem ser escritas e testadas de forma independente. Isto permite inclusive que equipes diferentes possam trabalhar no desenvolvimento de diferentes partes do programa; 3. Uma determinada sub-rotina pode ser reutilizada várias vezes em um mesmo programa, ou mesmo em outras unidades de programa. Por exemplo, o cálculo do fatorial pode ser programado e testado em uma sub-rotina independe e depois ser utilizado várias vezes no mesmo programa ou em outras unidades de programa. 5.2 DEFININDO FUNÇÕES EM C/C++ A idéia fundamentalde uma sub-rotina consiste em empacotar dentro de uma “caixa preta” um determinado algoritmo que executa uma tarefa específica. Ao utilizarmos esta sub-rotina em uma determinada parte do nosso programa, não é necessário conhecer “como” o algoritmo interno à caixa preta está programado, basta sabermos o que esse algoritmo precisa para executar a tarefa específica (dados de entrada), e quais informações a sub-rotina irá nos retornar (dados de saída). De fato, já fizemos isso no capítulo 2, quando utilizamos algumas funções (seno, cosseno, etc.) pré-programas em C/C++. Naquela situação, o programa principal simplesmente chamava uma função interna, por exemplo, a função seno, pelo seu nome sin acrescido do parâmetro de entrada, que era colocado entre parêntesis. Assim, quando era desejado calcular o seno de uma determinada variável a, utilizava-se no programa principal a chamada sin(a). O programa que chama a rotina para que ela execute uma determinada tarefa é chamado aqui de programa chamador. Tanto o programa principal como outra sub-rotina, podem exercer o papel de programa chamador. Uma estrutura geral de entrada e saída específica para uma sub-rotina é mostrada na Figura 5.1. Repara-se que a estrutura de entrada/saída de uma sub-rotina é a mesma de um programa computacional conforme descrito no capítulo 2, justificando a utilização do termo subprograma. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 62 FIGURA 5.1. DIAGRAMA ESQUEMÁTICO DE UMA SUB-ROTINA. 5.2.1 Declarando Funções em C/C++ O empacotamento de um algoritmo em uma sub-rotina é feito utilizando-se uma interface bem definida. A interface especifica apenas as informações essenciais para a utilização da sub-rotina, quais sejam: o seu nome e a estrutura dos dados de entrada e de saída. Se a estrutura detalhada da interface de uma sub-rotina for bem conhecida, pode-se fazer uso dela, simplesmente chamando- a (pelo nome) em uma determinada parte do programa. É importante frisar que para que uma determinada função seja chamada, não é importante conhecer a maneira como a função está internamente programada, basta conhecer a sua interface. A descrição de uma interface é feita em C/C++ a partir da seguinte instrução genérica C/C++: Onde: Tipo: representa o tipo de dado que a função deverá retornar. Todos os tipos de dados estudados podem ser utilizados; indentificador: representa o nome da função que está sendo declarada; lista de dados de entrada: corresponde à lista de dados de entrada, separados por vírgula, e precedidos pelos tipos correspondentes de cada dado. A ordem em que a lista de dados é estabelecida é importante, pois o programa chamador deve passar os dados na mesma ordem em que eles aparecem na interface da função; 5.2.2 Exemplos de declaração de interfaces de funções Alguns exemplos de declaração de interfaces de funções são mostrados a seguir int fatorial(int n); double soma (double a, double b); vector <float> soma (vector <float> x, vector <float> y); A primeira interface declara um função que calcula o fatorial de um número inteiro n, retornando o valor inteiro correspondente ao fatorial de n. A segunda interface declara uma função que calcula a soma de dois números do tipo double, retornando essa soma, que obviamente também deve ser um double. A terceira função recebe dois vetores de float x e y, calcula a soma destes vetores e retorna o vetor soma dado por x+y, o qual necessariamente também deverá ser um vetor do tipo double. Sub-rotina (Fatorial) dados de entrada dados de saída Tipo identificador (lista de dados de entrada) Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 63 5.3 DIFERENCIANDO FUNÇÕES E PROCEDIMENTOS Os termos funções, procedimentos, rotinas, subprogramas e sub-rotinas, são em geral utilizados de forma indistinta para a descrição de algoritmos empacotados em uma interface. Em C/C++ os algoritmos empacotados podem ser classificados de funções ou procedimentos. Uma distinção entre os procedimentos e funções é dada a seguir: 5.3.1 Função: Uma função é um algoritmo que recebe dados de entrada e retorna um único dado de saída ao programa chamador. O dado de saída, o qual pode envolver qualquer um dos tipos estudados, tais como: inteiros, reais, vetores, matrizes, etc., é calculado por meio da execução da tarefa específica da função, sem referência ou alteração em nenhum dos dados no ambiente do programa chamador. 5.3.2 Procedimento: Um procedimento é um algoritmo que não retorna dado algum ao seu chamador, mas que trabalha diretamente alterando os dados do ambiente do programa chamador para execução da tarefa. Para compreendermos melhor os conceitos acima, seja a tarefa de preenchimento de um imposto de renda. No conceito de função, os dados de entrada (informações contida em extratos, e outros dados de entrada para o cálculo do imposto) são enviados pelo chamador à função, a qual calcula o valor do imposto de renda, retornando uma única informação de saída (imposto devido) ao programa chamador. No caso do procedimento, nenhuma informação é enviada ao programa chamador, os dados do ambiente do programa chamador são simplesmente utilizados e alterados pelo procedimento e os valores são calculados no próprio ambiente do programa chamador, alterando suas próprias variáveis. É importante destacar que como o procedimento altera diretamente as informações do programa chamador, ele pode também fazer alterações indevidas. Assim, o programa chamador pode sofrer alterações não planejadas, após a chamada de um procedimento. 5.3.3 Descrevendo Funções C/C++ A declaração de uma função descreve alguns detalhes de como ela deve ser utilizada: quais os dados de entrada ela deve receber e o tipo do dado de saída que ela deverá retornar quando for utilizada. Entretanto, a declaração da interface não descreve como a função realmente calcula os dados de saída, apenas sua estrutura de entrada-saída. Portanto, é preciso também descrever o algoritmo da função, ou seja, a sequência de comandos que permite que a tarefa da função possa ser executada. No caso da utilização de uma função interna do C/C++, esse algoritmo que descreve a tarefa da função já está pré-programado em uma biblioteca interna da linguagem. Neste caso, basta chamar a função pré-programada, sem se preocupar com a descrição de seu algoritmo interno. Nos casos em que o programador deseja desenvolver as suas próprias funções, então é necessário descrever seu algoritmo em detalhe. A descrição do algoritmo de uma função é feita em C/C++ conforme mostrado na Figura 5.2. O topo da função é exatamente igual à interface da função, previamente declarada no topo do Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 64 programa. O corpo da função consiste no algoritmo em si, utilizado para a solução do problema computacional associado à função. FIGURA 5.2. DESCRIÇÃO DO ALGORITMO DE UMA FUNÇÃO. Assim, por exemplo, se desejamos descrever uma função que implemente o cálculo do fatorial, conforme mostrado na interface do exemplo anterior, teríamos a função mostrada no Programa 5.1. PROGRAMA 5.1. TRECHO DE PROGRAMA DESCREVENDO A FUNÇÃO FATORIAL Repare que o algoritmo é descrito de forma genérica para um certo valor de entrada n. Também é muito importante frisar que a descriçãodo algoritmo da função fatorial é feita de forma independente do programa chamador. Assim, não é necessário pensar no programa chamador quando do desenvolvimento da função, basta ter em mente a estrutura de entrada e saída dada pela interface. Essa separação é lógica, já que o algoritmo de cálculo de fatorial é único, e de fato deve ser independente dos dados de quem o está chamando. Repara-se que o último comando do Programa 5.1 é o comando return fat;. Este comando deve ser posto no corpo da função de modo que o valor inteiro armazenado na variável fat seja retornado ao programa chamador. O valor retornado pela função pode ser armazenado pelo programa chamador em uma variável qualquer de seu escopo ou pode mesmo ser utilizado em expressões aritméticas, como ocorre com outras funções internas da linguagem. Exemplos de utilização de uma função no programa chamador serão vistos a seguir. Quando a função for chamada, o programa chamador será incumbido de passar os parâmetros de entrada para que a função faça os cálculos. Assim, se o programa chamador passar como entrada o parâmetro 3, utilizando o comando fatorial(3), então o valor 3 será atribuído a n na função, a qual calculará, portanto, o fatorial de 3. Suponha, por exemplo, que o programa chamador tenha os comandos mostrados no corpo do Programa 5.2. Notamos que a função fatorial foi utilizada quatro vezes no programa principal. De fato, ela poderia ter sido utilizada quantas vezes o programador quisesse. A cada vez que a função é chamada, um valor diferente de parâmetro é passado à variável n, a qual assume este valor. Assim, a função é sempre programada para calcular o fatorial da variável n, porém, como a variável n assume os diferentes valores de parâmetros, a cada vez um fatorial diferente é int fatorial(int n) { int fat=1; for(int i=1; i<=n; i++) fat=fat*i; return fat; } Tipo identificador (lista de dados de entrada) { corpo da função } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 65 calculado e retornado ao programa chamador. A forma como a função retorna um determinado valor é através do comando de return. Assim, é necessário que o programa chamador receba esse valor retornado em alguma variável, ou faça uso deste valor em expressão aritmética. Na chamada k=fatorial(3); o valor retornado (que é o fatorial de 3) é armazenado na variável k, que pertence ao programa principal (chamador). Na chamada x = fatorial(3)* fatorial(2); o valor do fatorial não é armazenado em nenhuma variável, mas é diretamente utilizado em uma expressão aritmética. Após a execução do comando o valor de x irá armazenar o valor do fatorial de 3 vezes o fatorial de 2. Comandos em que o valor retornado não é nem armazenado e nem utilizado em uma expressão, não são sintaticamente corretos. Ou seja, o comando fatorial(3); não está correto e deve resultar em erro de compilação. PROGRAMA 5.2. EXEMPLOS DE UTILIZAÇÃO DA FUNÇÃO FATORIAL NO PROGRAMA CHAMADOR Destaca-se ainda que para a utilização da função fatorial no Programa 5.2 foi necessária a inserção da declaração da interface da função no topo do programa. O programa principal não mostra, mas também seria necessária a descrição completa da função, conforme dada no Programa 5.1, o que poderia ser feito após o programa principal. A seguir, o conceito de função será utilizado para resolver um problema computacional simples. 5.3.4 Exemplo – Combinação matemática de números Escreva um programa C/C++ que calcule o número de combinações de n objetos tomados r de cada vez, dado pela fórmula conhecida a seguir. Os valores de n e r devem ser informados pelo usuário. , ! !( - )! n r n C r n r #include <cstdlib> #include <iostream> #include <vector> using namespace std; int fatorial(int n); int main(int argc, char *argv[]) { int n,r,k,l; int x; n=3; r=2; k=fatorial(n); l=fatorial(r); x=k*l; x= fatorial(3)* fatorial(2); system("PAUSE"); return EXIT_SUCCESS; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 66 Como o valor do fatorial deve ser calculado 3 vezes para valores diferentes, torna-se interessante a utilização de uma função fatorial, que pode ser chamada três vezes no programa principal. A função fatorial já foi descrita no Programa 5.1, de modo que basta que ela seja incorporada ao programa chamador, que neste caso será o próprio programa principal. O desenvolvimento do programa chamador é feito como se houvesse uma função fatorial previamente pronta, ou seja, o programa chamador não deve se preocupar com os detalhes de implementação da função, mas apenas com os seguintes aspectos: i) passar as informações de entrada de forma correta, para que a função faça os cálculos da maneira esperada; ii) receber de forma correta os dados que são retornados a ele pela função. Assim, o programa chamador deve apenas solicitar ao usuário os valores de n e r e, a seguir, chamar a função fatorial 3 vezes, para que o cálculo da combinação ,n rC possa ser efetuado. O programa completo, envolvendo o programa principal e a função fatorial, é descrito no Programa 5.3 a seguir. PROGRAMA 5.3. CÁLCULO DO NÚMERO DE COMBINAÇÕES DE n OBJETOS TOMADOS r DE CADA VEZ #include <cstdlib> #include <iostream> #include <vector> using namespace std; int fatorial(int n); int main(int argc, char *argv[]) { int n,r,c; cout << "Calculo da combinação de n numeros tomados r de cada vez" << endl << "Entre com os numeros: "; cout << endl << "n: "; cin >> n; cout << "r: "; cin >> r; c=fatorial(n)/(fatorial(r)*fatorial(n-r)); cout << endl << "A combinacao de " << n << " elementos tomados " << r << " de cada vez, vale: " << c << endl << endl; system("PAUSE"); return EXIT_SUCCESS; } int fatorial(int n) { int fat; fat=1; for(int i=1;i<=n;i++) fat=fat*i; return fat; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 67 5.3.5 Exemplo – Combinação matemática de uma lista de números Escrever um programa C/C++ com as seguintes características: a) Uma função que recebe dois números n e r e calcula o número de combinações de n objetos tomados r de cada vez ,n rC . b) Um programa principal que lê uma lista de pares de números x e y, fornecidos pelo usuário, e terminada com 999 e calcula os respectivos valores de ,x yC . Neste programa é solicitada a criação de uma função específica para o cálculo de ,n rC . Esta função deve ter a estrutura geral mostrada na Figura 5.3. FIGURA 5.3. REPRESENTAÇÃO ESQUEMÁTICA DA SUB-ROTINA COMBINAÇÃO Dada a estrutura geral da função, conforme mostrada na Figura 5.3, sua interface pode ser dada através da declaração a seguir: int combinação(int n, int r); Repare que para construção do algoritmo da função combinação será necessário chamar a função fatorial já descrita no exercício anterior. Isso não é problemapara o compilador C/C++ que permite que uma função faça referência a outra, desde que a interface desta última esteja devidamente declarada. A descrição do algoritmo da função combinação é mostrada no trecho do Programa 5.4. Esta função é muito simples, ela somente faz 3 chamadas à função fatorial de modo que a combinação possa ser calculada. PROGRAMA 5.4. TRECHO DE PROGRAMA DESCREVENDO A FUNÇÃO COMBINAÇÃO O programa completo é mostrado no Programa 5.5. No programa principal é solicitada ao usuário uma lista de pares de números x e y que são utilizados para as chamadas à função combinação, a qual por sua vez retorna os valores de ,x yC que são impressos na tela. int combinação(int n, int r) { int c; c = factorial(n)/(factorial(r)*factorial(n-r)); return c; } função Combinação n , r (int) ,n rC (int) Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 68 PROGRAMA 5.5. RESOLUÇÃO DO EXEMPLO 2 - PROGRAMA COMPLETO 5.4 REGRAS DE ESCOPO DE FUNÇÕES Quando uma função é chamada por um programa chamador, a lista de argumentos passados do programa chamador à função está diretamente correlacionada à lista de parâmetros da função. Assim, quando, por exemplo, o programa principal chama a função combinação com o comando c = combinacao(x,y); o conteúdo dos argumentos x e y são diretamente passados para os parâmetros n e r, que assumem, nesse instante seus respectivos valores. Uma ilustração da dinâmica das variáveis e de seus escopos de aplicação é mostrada na Figura 5.4. A figura procura destacar os processos de chamada às funções combinacao e fatorial. A primeira chamada ocorre no programa principal com o comando c = combinacao(x,y); Quando este comando é executado, os valores das variáveis x e y são automaticamente passados respectivamente para as variáveis n e r, que passam a ter neste instante valores n = #include <cstdlib> #include <iostream> #include <vector> using namespace std; int fatorial(int n); int combinacao(int n, int r); int main(int argc, char *argv[]) { int x,y,c; cout << "Entre com pares de numeros" << endl; cout << "x: (digite 999 para terminar)"; cin >> x; while(x!=999) { cout << "y: "; cin >> y; c = combinacao(x,y); cout << endl<< "A combinacao de " << x << " elementos tomados " << y << " de cada vez, vale: " << c << endl << endl; cout << "x: (digite 999 para terminar)"; cin >> x; } } int fatorial(int n) { int fat; fat=1; for(int i=1;i<=n;i++) fat=fat*i; return fat; } int combinacao(int n, int r) { int c = fatorial(n)/(fatorial(r)*fatorial(n-r)); return c; } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 69 5 e r = 2. Neste caso, o programa chamador é o programa principal. Quando o comando a = fatorial(n) é executado na função combinacao, o valor de n da função combinacao é passado para o valor de n da função fatorial. É muito importante reparar que as duas variáveis, apesar de possuírem o mesmo nome, não têm nenhuma relação direta na memória, ou seja, são duas variáveis completamente independentes e com espaços de memórias diferentes. Entretanto, devido ao mecanismo de passagem de parâmetros, no momento em que o comando a = fatorial(n) é executado, o conteúdo de uma é passado para a outra. Isso fica claro no comando a seguir b = fatorial(r), em que a variável n assume agora o valor da variável r da função combinação. FIGURA 5.4. ILUSTRAÇÃO DA DINÂMICA DOS VALORES DE VARIÁVEIS PERTENCENTES A DIVERSOS AMBIENTES DE UM PROGRAMA EXEMPLO Diz-se que o escopo (área de abrangência) de uma variável é local a uma função quando esta variável só pode ser acessada internamente à função. Como exemplos, a variável n da função combinacao possui um escopo que compreende apenas a função combinação; a variável n da função fatorial possui escopo que compreende apenas a função fatorial. Assim, se o programador escrever um comando cout << n; interno à função combinacao, o comando poderá imprimir um valor diferente se o mesmo comando fosse introduzido na função fatorial. E possível declarar variáveis e funções que tenham escopo global, isto é que possam ser 5 x 2 y cin >> x; cin >> y; c = combinacao(x,y); 5 n 2 r a = fatorial(n) 5 n b = fatorial(r) c = fatorial(n-r) 2 n 3 n AMBIENTE DO PROGRAMA PRINCIPAL AMBIENTE DA FUNÇÃO COMBINACAO AMBIENTE DA FUNÇÃO FATORIAL Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 70 acessadas internamente a qualquer função ou procedimento. Para isso tais variáveis globais podem ser declaradas no topo do programa, antes do programa principal, conforme mostrado no exemplo dado pelo Programa 5.6. Neste programa, as variáveis a,b,c; são globais, o que quer dizer que estas variáveis podem ser acessadas em todo o escopo do programa. É importante destacar que a utilização de variáveis globais não consiste em uma boa prática de programação. Isso se deve ao fato de que se todas as funções têm acesso à variável todas elas podem alterá-la de forma indiscriminada. Assim, se durante o desenvolvimento do programa houver algum erro com essa variável não será possível descobrir “quem” (qual das funções) de fato fez a alteração, dificultando a identificação do erro. PROGRAMA 5.6. PROGRAMA EXEMPLIFICANDO A UTILIZAÇÃO DE VARIÁVEIS GLOBAIS 5.5 PROCEDIMENTOS Vimos que funções trabalham simplesmente recebendo dados de entrada, processando estes dados e retornando um valor qualquer, correspondente à solução da tarefa desempenhada pela função. Os procedimentos não retornam nenhum valor, eles simplesmente têm acesso às informações #include <cstdlib> #include <iostream> #include <vector> using namespace std; int soma(int x, int y); int a,b,c; int main(int argc, char *argv[]) { cout << "Entre com dois numeros inteiros quaisquer: "; cout << endl << "a: "; cin >> a; cout << "b: "; cin >> b; c=soma(a,b); cout << endl<< "Soma vale: " << c << endl << endl; // cout << "D = " << d); system("PAUSE"); return EXIT_SUCCESS; } int soma(int x,int y) { int d; cout << endl<< endl << "Entrando na funcao ... "; cout << endl << "A variavel global eh acessivel na funcao-> a= " << a << endl; d=x+y; cout << endl <<endl <<"Saindo da funcao ... "; return(d); } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 71 do programa chamador e processam a sua tarefa diretamente sobre os dados do programa chamador. Assim, os procedimentos podemfazer mais do que as funções, mas eles podem ser mais “perigosos”, já que os dados do programa chamador ficam expostos às alterações feitas pelo procedimento. A declaração de um procedimento também é feita através de uma interface, e de forma muito parecida com a de uma função. A interface de um procedimento é declarada conforme mostrado a seguir: Em que: indentificador: representa o nome do procedimento que está sendo declarado; lista de dados de entrada: corresponde à lista de dados de entrada, separados por vírgula, e precedidos pelos tipos correspondentes de cada dado. A ordem em que a lista de dados é estabelecida é importante, pois o programa chamador deve passar os dados na mesma ordem em que eles aparecem na interface do procedimento; A diferença básica entre uma função e um procedimento é que este último não retorna nenhum tipo de dado, daí a utilização da palavra reserva void (que em inglês significa vazio). A descrição do algoritmo interno do procedimento também é feito de forma análoga ao que já foi mostrado para a função, conforme mostrado na Figura 5.5. Ou seja, basta escrever, entre as chaves, o algoritmo que resolve a tarefa básica atribuída ao procedimento. FIGURA 5.5. EXEMPLO GENÉRICO DE DESCRIÇÃO DE UM PROCEDIMENTO. No corpo do procedimento não aparece o comando return, já que o procedimento não retorna nada (void). Mas de fato, para que deve servir um procedimento, se ele não pode retornar nenhum valor através do comando return? Existem duas formas básicas de utilização de um procedimento descritas a seguir. Na primeira forma, o procedimento não retorna nenhum dado ao chamador, mas ele pode acessar o exterior do programa (imprimindo informações na tela, ou em um arquivo, por exemplo). Um exemplo clássico seria um procedimento para a impressão formatada de uma matriz. Neste caso o procedimento receberia a matriz a ser impressa (como argumento) e simplesmente imprimiria esta matriz na tela, sem retornar nenhum valor ao programa chamador. Na segunda forma, que é a mais útil, o procedimento recebe os dados de entrada de forma que ele seja de capaz de acessar diretamente uma (ou mais) variável do programa chamador. Isso é possível, pois ao chamar o procedimento, o programa chamador passa como argumento para o procedimento não uma cópia da variável, mas uma referência ao seu endereço de memória. Com a referência ao endereço de memória em mãos, o procedimento tem acesso amplo ao conteúdo void identificador (lista de dados de entrada) void identificador (lista de dados de entrada) { corpo do procedimento } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 72 da variável podendo, inclusive, alterar seu valor. Assim, na utilização de um procedimento, a variável do programa chamador que é alterada funciona como se fosse uma variável que retorna informação, já que ela foi “enviada” como um parâmetro com um determinado valor e “retornou” com seu valor alterado. Assim, o procedimento não retorna nenhuma informação através do comando return, mas pode “retornar” várias informações através da alteração de variáveis do programa chamador. É fundamental que esteja bem claro na interface do procedimento, quais variáveis podem e quais não podem ser alteradas pelo procedimento. Isso é feito utilizando o caractere “&” internamente à lista de variáveis do procedimento, para cada variável que poderá ser alterada no programa chamador. Alguns exemplos de interfaces de procedimentos que permitem que determinadas variáveis sejam alteradas pelo procedimento são mostradas a seguir. 5.5.1 Exemplos – Declaração de procedimentos void imprime (vector <int> v); void le (vector <int> &v, int n); void leFuncionario (string &nome, int &num, float &sal) No primeiro exemplo, o procedimento é simplesmente utilizado para a comunicação com o meio externo, isto é, ele recebe um vetor v e simplesmente possui a tarefa de imprimi-lo na tela do computador. No segundo exemplo, o procedimento le recebe um vetor v (vazio) e um número inteiro n, que corresponde ao tamanho do vetor a ser lido. A tarefa do procedimento é fazer a leitura do vetor v. É importante destacar a ocorrência do caractere “&” antes da variável v, o que significa que se esta variável for alterada pelo procedimento ela também será alterada no programa chamador. Assim, a variável v na verdade é utilizada como uma variável que irá retornar com o vetor lido. O mesmo ocorre para as variáveis nome, num e sal da função leFuncionario. Elas serão passadas vazias para o procedimento que irá povoá-las com valores e retorná-las ao programa chamador. Isso só é possível porque antes de cada uma delas utilizou- se o caractere “&”. Assim, cada variável da lista de parâmetros que o procedimento pode alterar deve ser precedida pelo caractere “&”. A título de exemplificação da descrição de um procedimento, a implementação da interface void le (vector <int> &v, int n); é mostrada no trecho do Programa 5.7. A idéia básica do algoritmo consiste em inicialmente redimensionar o vetor para o tamanho n, e após fazer a leitura do vetor utilizando um comando de repetição e lendo elemento a elemento, digitado pelo usuário. PROGRAMA 5.7. TRECHO DE PROGRAMA EXEMPLIFICANDO O PROCEDIMENTO DE LEITURA DE UM VETOR void le(vector <int> &v, int n) { cout << "Lendo os elementos do vetor" << endl ; v.resize(n); for(int i=0; i<n; i++) { cout << "elemento[ " << i << " ]: " ; cin >> v[i]; } } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 73 É importante destacar que nos procedimentos não aparece o comando return, já que os procedimentos não retornam nada através do return, mas através de determinadas variáveis escolhidas do programa chamador, através do caractere “&”. A forma como os procedimentos são chamados é também ligeiramente diferente da forma como as funções são chamadas no programa chamador. Para as funções, vimos que a chamada deve ser feita de modo que a variável retornada seja guardada em outra variável do programa chamador (por exemplo, a = fatorial(x); ), ou utilizada diretamente em uma expressão aritmética (por exemplo, y = fatorial(x)/fatorial(x-r)). Como os procedimentos não retornam nenhum valor através do comando return, suas chamadas são diretas. Alguns exemplos de chamadas para as interfaces dos exemplos anteriores são mostradas a seguir. imprime (vector <int> a); leVetor (vector <int> &x, int k); leFuncionario (string &nome1, int &num1, float &sal1) No primeiro exemplo, a função imprime apenas receberá o vetor a e o imprimirá na tela, não retornando nenhuma informação ao programa chamador. No segundo, o procedimento leVetor recebe o vetor x vazio e seu tamanho k e faz a leitura deste vetor, retornando-o ao programa chamador. No terceiro exemplo, o procedimento leFuncionario recebe as variáveis nome1, num1 e sal1, lendo esse valores e retornando-os ao programa chamador. Repara-se que as funções podem retornar apenas um valor utilizando o comando return e que os procedimentos podem retornar vários valores ao programa chamador (como no procedimento leFuncionario). A forma de passagem de parâmetro da função é conhecida como passagem de parâmetro por valor, já que o programa chamador envia cópias de suas variáveis à função. Na utilização do procedimento, o programa chamador não passa uma cópia, mas referências às variáveisque podem ser alteradas pelo procedimento. Neste caso dizemos que a passagem de parâmetro é feita por referência. A ilustração da Figura 5.6 destaca duas chamadas à função leVetor. Na primeira chamada leVetor (a,2); a variável a é passada por referência à variável v e o número inteiro 2 é passado por valor à variável n. Assim, quando o vetor v, com dimensão 2, for lido na execução do algoritmo do procedimento, o vetor a estará sendo automaticamente atualizado, já que existe um vículo de memória entre estas duas variáveis, que foi estabelecido pelo caratere “&”. O mesmo ocorre quando a chamada leVetor (b,3); é feita, só que neste caso o procedimento lê um vetor de dimensão 3, e este vetor é “retornado”, através do vínculo estabelecido entre as variáveis b e v. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 74 FIGURA 5.6. ILUSTRAÇÃO DA DINÂMICA DOS VALORES DE VARIÁVEIS PERTENCENTES AOS AMBIENTES DE UM PROGRAMA CHAMADOR E DO PROCEDIMENTO 5.5.2 Exemplo – leitura e impressão de vetores Descrever um programa C/C++ com as seguintes características: a) Um procedimento que recebe um número inteiro n, representando o tamanho de um vetor v e lê este vetor retornando-o; b) Um procedimento que recebe um vetor v e o imprime na tela; c) Um programa principal que lê 3 vetores a com 3 posições, b com 2 posições e c com 4 posições, utilizando o procedimento descrito em (a), e os imprime na tela, utilizando o procedimento descrito em (b). A solução do exemplo é mostrada no Programa 5.8. Os algoritmos de leitura e impressão de vetores já são bem conhecidos do capítulo anterior. A única diferença é que estes algoritmos estão, agora, empacotados em dois procedimentos, que podem ser utilizados sempre que necessário. leVetor (a,2) void leVetor (vector <int> &v, int n); AMBIENTE DO PROGRAMA CHAMADOR AMBIENTE DO PROCEDIMENTO leVetor a v 2 n Execução do algoritmo do procedimento v 5 8 a 5 8 vetor é lido vínculo entre a e v vínculo entre a e v leVetor (b,3) b v 3 n Execução do algoritmo do procedimento v 1 3 4 1 3 4 b vetor é lido vínculo entre b e v vínculo entre b e v Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 75 PROGRAMA 5.8. RESOLUÇÃO DO EXEMPLO 3 - IMPRESSÃO E LEITURA DE VETORES 5.6 EXERCÍCIOS 1) Descrever um programa C/C++ com as seguintes características: a) Um procedimento que lê um vetor A com N elementos b) Um procedimento que imprime um vetor K qualquer c) Um procedimento que calcula o produto escalar entre dois vetores d) Um procedimento que calcula a soma entre dois vetores e) Um programa principal que lê 4 vetores X, Y, Z, K e calcula a seguinte operação T X Y Z K #include <cstdlib> #include <iostream> #include <vector> using namespace std; void le(vector <int> &v, int n); void imprime(vector <int> v); int main(int argc, char *argv[]) { vector <int> a, b,c; cout << "Entre com os dados do primeiro vetor: " << endl; le(a,3); cout << endl << "Entre com os dados do segundo vetor: " << endl ; le(b,2); cout << endl << "Entre com os dados do terceiro vetor: " << endl; le(c,4); imprime(a); imprime(b); imprime(c); system("PAUSE"); return EXIT_SUCCESS; } void le(vector <int> &v, int n) { cout << "Lendo os elementos do vetor" << endl ; v.resize(n); for(int i=0; i<n; i++) { cout << "elemento[ " << i << " ]: " ; cin >> v[i]; } } void imprime(vector <int> v) { cout << endl << endl << "Imprimindo os elementos do vetor" << endl ; for(int i=0; i<v.size(); i++) { cout << "elemento[ " << i << " ]: " ; cout << v[i] << endl; } } Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru ®LN(ceno) 76 2) Seja o cálculo do seno feito conforme mostrado na série matemática abaixo. Descrever um programa C/C++ com as seguintes características: 3 5 7 9 sen(x) 3! 5! 7! 9! x x x x x a) Uma função que recebe um número inteiro n e calcula o seu fatorial. b) Uma função que recebe dois número y e nt e calcula o sen( )y utilizando uma precisão de nt termos. c) Um programa principal que solicita ao usuário vários pares de números k e m, até que usuário digite um número chave 999. O programa principal deve calcular e imprimir na tela sen( )k utilizando a expressão acima, com precisão m. 3) Um número complexo é expresso por suas partes real e imaginária. Escreva um programa C/C++ com as seguintes características: a) Um procedimento que leia um número complexo e retorne o número complexo lido b) Um procedimento que imprima um número complexo. c) Um procedimento que recebe dois números complexos, e retorne a soma desses dois números d) Um procedimento que recebe dois números complexos e retorne o produto entre esses dois números e) Um programa principal que lê 4 números complexos C, D, E, F e calcula a seguinte operação (C+D)*(E+F) 4) Descrever um programa C/C++ com as seguintes características: a) Uma função que recebe um número inteiro k e verifica se este número é ou não primo, retornando 1, quando o número for primo e 0 caso contrário. b) Um procedimento que recebe dois números nl e nc relacionados aos números de linhas e de colunas de uma matriz e faz a leitura dessa matriz retornando-a. c) Um procedimento que recebe uma matriz e imprime a matriz na tela. d) Uma função que recebe uma matriz M e conta quantos números primos existem acima da diagonal principal dessa matriz e) Um programa principal que solicita ao usuário 4 matrizes quadradas, de dimensões também definidas pelo usuário, e imprime na tela as matrizes que possuem mais de 4 números primos acima da diagonal principal. 5) Descrever um programa C/C++ com as seguintes características: a) Um procedimento que recebe um número nf associado ao número de funcionários de uma empresa e lê as informações desses funcionários guardando-as em vetores e retornando esses vetores. As informações envolvem nome, sobrenome, salário e CPF dos funcionários. b) Um procedimento que recebe um conjunto de vetores com informações de funcionários conforme descrito em (a) e imprime essas informações na tela. c) Um procedimento que recebe um conjunto de vetores com informações de funcionários conforme descrito em (a) e retorna as informações do funcionário com maior salário. d) Um programa principal que: solicita aos usuários a quantidade qt de funcionários, lê as informações de todos os funcionários, e imprime na tela os dados do funcionário com maior salário. Notas de Aula – Introdução à Ciência da Computação – UNESP – Bauru®LN(ceno) 77 6) Descrever um programa C/C++ com as seguintes características: a) Um procedimento que lê as informações de uma lista de alunos de uma classe fornecida pelo usuário e terminada com a palavra “FIM”, retornando também a quantidade de alunos. As informações dos alunos envolvem nome, RA, P1, P2 e P3 de cada aluno e são salvas em vetores. b) Um procedimento que recebe os dados de um aluno e imprime suas informações na tela. c) Uma função que calcula as notas finais dos alunos utilizando 1 2 2NF P P . d) Outra função que calcula as notas finais dos alunos utilizando 1 2 2 3 4NF P P P . e) Um programa principal que lê as informações da turma, utilizando o procedimento (a) e calcula as notas finais utilizando a função dada em (b). Para os alunos que não passaram, atualiza as notas finais utilizando a função (d). Finalmente, imprime os dados do aluno que obteve a maior nota. 7) A distância que um avião percorre entre duas cidades distintas deve ser armazenada em uma matriz M , na qual os índices de linha i e coluna j representam as cidades i e M , e o conteúdo ,M i j representa a distância entre as cidades. Descreva um programa C/C++ com as seguintes características: a) Um procedimento que lê a matriz M , a partir da dimensão T da matriz que este procedimento recebe. A leitura é feita de modo que cada par , ,i j j i seja lido apenas uma vez e que os elementos da diagonal principal sejam nulos (distância entre uma cidade e ela mesma). b) Um procedimento que recebe a matriz M acima e uma cidade k qualquer, e calcula a menor distância entre esta cidade e as demais cidades. c) Um procedimento que recebe uma matriz M e duas cidades, e retorna a distância entre as duas cidades. d) Um programa principal que lê uma matriz Dist que representa as distâncias de vôo entre 200 cidades do interior de São Paulo. O programa principal recebe do usuário uma cidade específica cid e calcula a menor distância entre esta cidade e as demais cidades do interior paulista. O programa principal lê ainda uma lista de pares de cidades x e y fornecida pelo usuário e que termina com 999 e imprime na tela a distância entre tais cidades. 8) Descrever um programa C/C++ com as seguintes características: a) Uma função que recebe um número inteiro k e verifica se este número é ou não primo, retornando 1, quando o número for primo e 0 caso contrário. b) Uma função que gera os N primeiros números da sequência de Fibonacci, retornando essa sequência em um vetor. c) Uma função que recebe um vetor e calcula a soma dos elementos do vetor. d) Um programa principal que recebe uma quantidade NP do usuário, e constrói um vetor F com NP elementos da sequência de Fibonacci utilizando (a). Utilizando o vetor F, o programa constrói ainda um novo vetor FP formado apenas pelos números primos da seqüência. O programa calcula ainda a soma total dos elementos de FP.