Baixe o app para aproveitar ainda mais
Prévia do material em texto
Faculdade SATC Técnicas de Programação II Linguagem C Engenharias Elétrica e Mecânica Profº.: Giovani Martins Cascaes giovani.cascaes@satc.edu.br http://www.satc.edu.br/gcascaes/ versão 1.03.136 2008/2009 2 Sumário Listas de Figuras..........................................................................................................5 Lista de Tabelas...........................................................................................................6 1 Introdução.................................................................................................................7 1.1 Organização básica de programas C .....................................................................7 1.2 Criando, compilando e executando programas em linguagem C............................9 2 Tipos, bases, operadores e expressões ....................................................................12 2.1 Componentes básicos de um computador ...........................................................13 2.1.1 Unidade Lógica e Aritmética .......................................................................14 2.1.2 Registradores ..............................................................................................14 2.1.3 Memória .....................................................................................................15 2.1.4 Bases ..........................................................................................................16 2.1.5 Unidade de Controle ...................................................................................18 2.2 Declarações de variáveis ....................................................................................18 2.2.1 Definindo contantes.....................................................................................19 2.2.2 Definindo variáveis globais ..........................................................................19 2.3 Expressões.........................................................................................................20 2.3.1 Expressões Aritméticas................................................................................21 2.3.2 Expressões Condicionais .............................................................................23 2.3.3 Prioridade de operadores.............................................................................24 2.4 Exercícios propostos..........................................................................................24 3 O controle do fluxo de execução.............................................................................26 3.1 Seqüências .........................................................................................................26 3.2 If’s.....................................................................................................................27 3.3 Switch................................................................................................................29 3.4 Comando ternário ..............................................................................................33 3.5 Repetições .........................................................................................................34 3.5.1 Comando while ...........................................................................................34 3.5.2 Comando do while ......................................................................................36 3.5.3 Comando for ...............................................................................................37 3.6 Exercícios propostos..........................................................................................42 4 Funções ...................................................................................................................46 4.1 Protótipos de funções.........................................................................................47 4.2 Escopo de variáveis............................................................................................48 4.2.1 Variáveis locais ...........................................................................................48 4.2.2 Variáveis estáticas .......................................................................................50 3 4.2.3 Variáveis globais .........................................................................................50 4.2.4 Parâmetros formais......................................................................................51 4.2.5 Passagem de parâmetros por valor...............................................................51 4.2.6 Passagem de parâmetros por referência .......................................................51 4.2.7 O comando de returno.................................................................................51 4.2.8 Funções sem retorno ...................................................................................52 4.3 Recursão............................................................................................................52 4.4 A função printf() ................................................................................................54 4.5 A função scanf().................................................................................................56 4.5.1 Operador de endereço (&)...........................................................................57 4.6 Funções getche() e getch() .................................................................................58 4.7 Funções getchar() e putchar().............................................................................59 4.6 Exercícios propostos..........................................................................................60 5 Arranjos ..................................................................................................................63 5.1 Vetores ..............................................................................................................64 5.1.1 Exercícios Propostos...................................................................................66 5.2.1 Vetores de caracteres ..................................................................................68 5.2.2 Funções para manipulação de vetores de caracteres .....................................71 5.2.2 Exercícios Propostos...................................................................................75 5.3 Matrizes.............................................................................................................76 5.3.1 Matrizes bidimensionais...............................................................................76 5.3.2 Matrizes de cadeias de caracteres ................................................................77 5.3.3 Inicializações...............................................................................................78 5.3.4 Inicializações sem a especificação de tamanho .............................................79 5.3.5 Passagem de vetores e matrizes ...................................................................79 5.3.6 Exercícios propostos ...................................................................................81 5.4 Definição de estruturas.......................................................................................82 5.5 Enumerações......................................................................................................84 5.6 Definição de nomes de tipos...............................................................................86 5.7 Exercícios propostos..........................................................................................87 6 Ponteiros .................................................................................................................91 6.1 Exercícios propostos..........................................................................................926.2 Aritmética de Ponteiros......................................................................................92 6.3 Ponteiros e Arranjos...........................................................................................93 6.3.1 Exercícios propostos ...................................................................................95 6.4 Ponteiro genérico ...............................................................................................96 6.5 Ponteiro como argumento de funções.................................................................96 6.6 Ponteiros e estruturas.........................................................................................98 6.7 Ponteiros para funções .....................................................................................101 6.8 Exercícios propostos........................................................................................103 7 Estruturas de dados..............................................................................................105 7.1 Filas .................................................................................................................105 7.1.1 Filas com prioridades.................................................................................108 7.2 Pilhas ...............................................................................................................109 7.2.1 Exercícios propostos .................................................................................118 4 7.3 Listas lineares ligadas .......................................................................................118 7.3.1 Função sizeof()...................................................................................119 7.3.2 Alocação dinâmica de memória .................................................................120 7.3.3 Lista ordenada...........................................................................................125 7.3.4 Exercícios propostos .................................................................................128 Bibliografia ..............................................................................................................133 Apêndice A - Palavras reservadas em C e C++ ......................................................134 Anexo A – Tabela de caracteres ASCII ..................................................................135 Obs.: Este material foi adaptado da apostila do Prof.º Ivan L. M. Ricarte lotado no Departamento de Engenharia de Computação e Automação Industrial da Unicamp. Os exercícios presentes nas listas em cada capítulo desta, foram retirados ou adaptados de livros e páginas da internet e pertencem a seus respectivos donos. 5 Listas de Figuras Figura 1.1 - Estrutura básica de um algoritmo...............................................................8 Figura 1.2 - Programa que faz nada. .............................................................................8 Figura 1.3 – Fluxo no compilador C..............................................................................9 Figura 2.1 - Composição básica de um computador.[Linhas tracejadas indicam fluxo de controle e Linhas contínuas fluxo de dados] .........................................................15 Figura 2.2 – Complemento de base (2)........................................................................18 Figura 3.1 – Seqüência em C - Fluxograma.................................................................26 Figura 3.2 – Seleção com if ... else em C. .......................................................29 Figura 3.3 – Seleção usando switch ... case em C...........................................32 Figura 3.4 – Seleção usando switch ... case em C, omitindo-se o break. ......33 Figura 3.5 – Repetição usando while ... em C....................................................34 Figura 3.6 – Repetição usando do while ... em C...............................................36 Figura 3.7 – Repetição usando for ... em C. .......................................................38 Figura 5.1 – Armazenando caracteres na memória RAM.............................................68 Figura 6.1 - Espaço ocupado pelas variáveis. ..............................................................92 Figura 7.1 – Fila com prioridade descendente............................................................108 Figura 7.2 – Lista ligada simples. ..............................................................................119 6 Lista de Tabelas Tabela 2.1 – Tipos de dados em C ..............................................................................12 Tabela 2.2 – Caracteres de controle ............................................................................13 Tabela 2.3 – Operadores aritméticos ...........................................................................22 Tabela 2.4 – Operadores bit a bit ................................................................................22 Tabela 2.5 – Operadores relacionais............................................................................23 Tabela 2.6 – Operadores lógicos .................................................................................23 Tabela 2.7 – Tabela verdade dos operadores lógicos &&, || e !. ...................................24 Tabela 2.8 – Prioridade de operadores ........................................................................24 7 1 Introdução A linguagem de programação C foi desenvolvida no início dos anos 70 nos Laboratórios AT&T Bell, nos Estados Unidos. A motivação para que o autor de C, Dennis Ritchie, criasse uma nova linguagem de programação foi o desenvolvimento do sistema operacional Unix. C é uma ferramenta tão básica que praticamente todas as ferramentas suportadas por Unix e o próprio sistema operacional foram desenvolvidas em C. C acompanhou o ritmo da distribuição do sistema operacional Unix, que foi amplamente divulgado e livremente distribuído na década de 70. Apesar de haver compiladores para linguagens mais “tradicionais” na distribuição Unix, aos poucos C foi ganhando simpatizantes e adeptos. Atualmente, não há dúvidas de que C é uma das linguagens de programação de maior aceitação para uma ampla classe de aplicações. A simplicidade de C não restringe, no entanto, a potencialidade de suas aplicações. Blocos desempenhando tarefas muito complexas podem ser criados a partir da combinação de blocos elementares, e este mecanismo de combinação de partes pode se estender por diversos níveis. Esta habilidade de construir aplicações complexas a partir de elementos simples é um dos principais atrativos da linguagem. O sucesso de C foi tão grande que diversas implementações de compiladores surgiram, sendo que nem todos apresentavam o mesmo comportamento em pontos específicos, devido a características distintas da arquitetura dos computadores ou a “extensões” que se incorporavam à linguagem. Para compatibilizar o desenvolvimento de programas em C, o Instituto Norte-Americano de Padrões (ANSI) criou em 1983 um comitê com o objetivo de padronizar a linguagem. O resultado deste trabalho foi publicado em 1990, e foi prontamente adotado como padrão internacional. 1.1 Organização básica de programas C Na linguagem C, todo algoritmo deve ser traduzido para uma função. Uma função nada mais é do que um conjunto de expressões da linguagem C (possivelmente incluindo invocações ou chamadas para outras funções) com um nome e argumentos associados. Em geral, a definição de uma função C tem a forma: tipo nome (lista de argumentos) { declaracoes; comandos; } 8 inicio fim O tipo indica o valor de retorno de uma função, podendo assumir qualquer valor válido da linguagem C (que serão vistos adiante). O tipo da função pode ser omitido, sendo que neste caso o compilador irá assumir que o tipo int (inteiro) será retornado. O nome é o rótulo dadoà função, que em geral deve expressar de alguma forma o que a função realiza. Nos compiladores mais antigos, o número de caracteres em um nome era limitado (em geral, a 6 ou 8 caracteres). Atualmente, não há restrições ao comprimento de um nome, de forma que nomes significativos devem ser preferencialmente utilizados. Em C, todo nome que estiver seguido por parênteses será reconhecido como o nome de uma função. A lista de argumentos que fica no interior dos parênteses indica que valores a função precisa para realizar suas tarefas. Quando nenhum valor é necessário para a função, a lista será vazia, como em ( ). O que se segue na definição da função, delimitado entre chaves { e }, é o corpo da função. Declarações correspondem às variáveis internas que serão utilizadas pela função, e comandos implementam o algoritmo associado à função. Todo algoritmo (e, conseqüentemente, todo programa) deve ter um ponto de início e um ponto de fim de execução. Na figura abaixo (fig.1.1), a estrutura básica de um algoritmo é ilustrada - onde há uma “nuvem” faz alguma coisa, deve ser inserido o corpo do algoritmo que descreve a função a ser realizada. faz alguma coisa Figura 1.1 - Estrutura básica de um algoritmo. Um programa em C é basicamente um conjunto de funções. O ponto de início e término de execução de um programa em C está associado com uma função de nome especial: a função main “principal”. O menor programa em C que pode ser compilado corretamente é um programa que nada faz (Figura 1.2). Este programa em C é: main(){ } Figura 1.2 - Programa que faz nada. inicio fim 9 Editor de Ligação Compilador Pré-processador Montador Neste caso, a função de nome main() é definida sem nenhum comando. Neste ponto, algumas observações devem ser feitas: Todo programa em C tem que ter pelo menos uma função. Pelo menos uma função do programa em C tem o nome main() - esta função indica o ponto onde se iniciará a execução do programa e após executado seu último comando o programa finaliza sua execução. Ao contrário do que ocorre em Pascal ou FORTRAN, que diferenciam proce- dimentos (sub-rotinas) de funções, em C há apenas funções - mesmo que elas não retornem nenhum valor. 1.2 Criando, compilando e executando programas em linguagem C Após as etapas de entendimento do problema e definição de uma ou mais soluções para o mesmo é chegado o momento de criar a representação da solução do problema por meio do código fonte. Este por sua vez nada mais é do que a representação de uma solução utilizando a sintaxe correta de uma linguagem de programação, tal qual o C. Uma ferramenta de edição de texto se faz necessária. Prefira editores de texto que tenha poucos recursos e que sejam capazes de salvar seus arquivos fontes em modo caractere, ou seja, ASCII (American Standard Code for Information Interchange). Evite utilizar editores muito sofisticados tais como Microsoft Word, Open Office Writer, etc. estes armazenam caracteres extras (de controle) que não interessam ao programa ou código fonte. Por convenção o arquivo fonte deve ter a extensão .c, em minúsculo. Esta atitude facilita o reconhecimento imediato de um arquivo com código C, assim como extensões .pas para Pascal ou .cpp para C++. headers (.h) código fonte código assembly bibliotecas (.obj) código objeto código executável Figura 1.3 – Fluxo no compilador C. 10 Como mostra a figura anterior (fig 1.3) o código fonte é somente o início do trabalho de criação de programas executáveis. Ainda na mesma figura (fig. 1.3) pode-se observar outras etapas. O conceito de cada uma destas etapas é apresentado a seguir: pré-processador – este é recebe o arquivo com o código fonte e é responsável por retirar os comentários e interpretar diretivas especiais denotadas por #. Por exemplo: #include <math.h> - arq da biblioteca math. #include <stdio.h> - arq da biblioteca padrão de E/S. #define TAM_MAX_ARRAY 100 compilador – o compilador C traduz o código fonte em código assembly. Ele recebe o arquivo fonte do pré-processador. montador – cria o código objeto. No UNIX estes arquivos são encontrados com a extensão .o e no MS DOS com .obj. editor de ligação – se o arquivo fonte referencia alguma função de alguma biblioteca ou de algum arquivo externo cabe ao editor de ligação combinar estes arquivos com a função main() para criar o arquivo executável. Exemplo 1.1 - Digite o programa abaixo e compile-o. O que o programa faz? (Não incluir os números e os dois pontos) 1: #include <stdio.h> 2: #include <conio.h> 3: int raio, area; 4: 5: main() 6: { 7: printf("Entre com o raio (ex. 10): "); 8: scanf("%d", &raio); 9: area = (int) (3.14159 * raio * raio); 10: printf("\n\nArea = %d\n", area); 11: getch(); 12: return(0); 13: } Exemplo 1.2 - Digite e compile o programa a seguir. O que o programa faz? 1: #include <stdio.h> 2: #include <conio.h> 3: int x,y; 4: 5: main() 6: { 7: for (x = 0; x < 10; x++, printf ("\n")) 11 8: for (y = 0; y < 10; y++) 9: printf("X"); 10: getch(); 11: return(0); 12: } Exemplo 1.3 - Caçador de Erros: O programa seguinte apresenta um problema. Use o editor e o compilador, encontre e corrija o erro. Qual a linha contem o erro? 1: #include <stdio.h> 2: #include <conio.h> 3: main(); 4: { 5: printf("Fique Olhando!"); 6: printf(" Voce o encontrara!\n"); 7: getch(); 8: return(0); 9: } Exemplo 1.4 - Caçador de Erros: O programa seguinte apresenta um problema. Use o editor e o compilador, encontre e corrija o erro. Qual a linha contem o erro? 1: #include <stdio.h> 2: #include <conio.h> 3: main() 4: { 5: printf("Este e um programa com um "); 6: do_it ("problema!\n"); 7: getch(); 8: return(0); 9: } Exemplo 1.5 - Faça a seguinte alteração no programa do exemplo 1.2. Use o editor e o compilador e execute novamente o programa. O que acontece agora? 9: printf( "%c", 1 ); 12 2 Tipos, bases, operadores e expressões A linguagem C suporta os tipos de dados básicos usualmente suportados pelos computadores. Na linguagem padrão, os tipos básicos suportados são char, int, float e double. A tabela completa dos tipos de dados em C, aparece (tab. 2.1) logo a frente. O tipo char ocupa um único byte, sendo adequado para armazenar um caractere do conjunto ASCII e pequenos valores inteiros. O tipo int representa um valor inteiro que pode ser positivo ou negativo. O número de bytes ocupado por este tipo (e, conseqüentemente, a faixa de valores que podem ser representados) reflete o tamanho “natural” do inteiro na máquina onde o programa será executado. Os tipos float e double representam valores reais, limitados apenas pela precisão da máquina que executa o programa. O tipo float oferece cerca de seis dígitos de precisão enquanto que double suporta o dobro da precisão de um float. Alguns destes tipos básicos podem ser modificados por qualificadores. Por exemplo, o tipo char pode ser acompanhado pelo qualificador signed ou unsigned. O tipo signed char seria utilizado para indicar que a variável do tipo char estaria representando pequenos números inteiros (na faixa de -128 a +127). O tipo unsigned char seria utilizado para indicar que a variável estaria armazenando valores inteiros exclusivamente positivos (sem sinal) na faixa de 0 a 255. Tabela 2.1 – Tipos de dados em C Tipo Tamanho (Byte) Valor inicial Valor final char 1 -128 +127 unsigned char 1 0 255 int 2 -32.768 +32.767 unsigned int 2 0 65535 long int 4 -2.147.483.648 +2.147.483.647 unsigned long int 4 0 4.294.264.295 short int 2 -32.768 +32.767 unsigned short int 2 0 65535 float 4 -3.4E-38 +3.4E+38 double 8 -1.7E-308 +1.7E+308 long double 10 -3.4E-4932 +3.4E+4932 O tipo int também pode ser qualificado. Um tipo unsigned int indica que a variável apenas armazenarávalores positivos. Um tipo short int indica que (caso seja possível) o compilador deverá usar um número menor de bytes para 13 representar o valor numérico - usualmente, dois bytes são alocados para este tipo. Uma variável do tipo long int indica que a representação mais longa de um inteiro deve ser utilizada, sendo que usualmente quatro bytes são reservados para variáveis deste tipo. Estas dimensões de variáveis denotam apenas uma situação usual definida por boa parte dos compiladores, sendo que não há nenhuma garantia quanto a isto. A única coisa que se pode afirmar com relação à dimensão de inteiros em C é que uma variável do tipo short int não terá um número maior de bits em sua representação do que uma variável do tipo long int. Valores com representação em ponto flutuante (reais) são representados em C por meio do uso do ponto decimal, como em 1.5 para representar o valor um e meio. A notação exponencial também pode ser usada, como em 1.2345e-6 ou em 0.12E3. Caracteres ASCII são denotados entre aspas simples, tais como ’A’. Cada caractere ASCII corresponde também a uma representação binária usada internamente. Por exemplo, o caractere ASCII A equivale a uma seqüência de bits que corresponde ao valor hexadecimal 41H ou decimal 65. Os valores definidos para os caracteres ASCII são apresentados na tabela localizada no anexo A. Além dos caracteres alfanuméricos e de pontuação, que podem ser representados em uma função diretamente pelo símbolo correspondente entre aspas, C também define representações para caracteres especiais de controle do código ASCII por meio de seqüências de escapes iniciados pelo símbolo \ (contrabarra). As principais seqüências são encontradas na tabela a seguir (tab. 2.2). Tabela 2.2 – Caracteres de controle Caractere de Controle Finalidade \n Nova linha \t Tabulação \b Retrocesso \r Retorno de carro \f Salto de página \a Sinal sonoro \\ Contra barra \’ Apóstrofo \” Aspas \0 O caractere nulo 2.1 Componentes básicos de um computador Apesar da existência de uma grande diversidade em termos de arquiteturas de computadores, pode-se enumerar, em um ponto de vista mais genérico os componentes básicos desta classe de equipamentos. A figura a seguir (fig 2.1) apresenta um esquema de um computador, destacando os elementos que o compõem. Apesar da grande evolução ocorrida na área de informática desde o aparecimento dos primeiros computadores, o esquema apresentado na figura seguinte (fig 2.1) pode ser utilizado tanto para descrever um sistema compu- tacional atual como os computadores da década de 40, projetados por engenheiros como 14 John Von Neuman. Os parágrafos que seguem apresentam os componentes principais de um computador genérico. 2.1.1 Unidade Lógica e Aritmética O primeiro componente essencial num computador (ou sistema computacional) é a Unidade Lógica e Aritmética (ALU), a qual, como o próprio nome indica, assume todas as tarefas relacionadas às operações lógicas (ou, e, negação, etc...) e aritméticas (adições, subtrações, etc...) a serem realizadas no contexto de uma tarefa realizada por meio dos computadores. Neste contexto, é importante observar a evolução que este elemento sofreu ao longo dos anos e quais são os parâmetros a ele associados que influenciam no desempenho global de um sistema computacional. Um parâmetro importante é o tamanho da palavra processada pela unidade lógica e aritmética, lembrando que o sistema de numeração adotado nas arquiteturas de computadores é o binário, o qual tem como unidade básica de informação o bit, que pode assumir os valores 0 ou 1. Quanto maior o tamanho da palavra manipulada pelo microprocessador, maior é o seu potencial de cálculo e maior a precisão das operações realizadas. As primeiras CPU’s integradas num único chip, como por exemplo, o 4004 fabricado pela Intel em 1968 manipulava palavras (dados e instruções) expressas por 4 dígitos binários. Os microprocessadores mais recentes são capazes de manipular palavras entre 32 bits (caso dos 486) e 64 bits (Pentium e Power PC). A velocidade de cálculo é outro fator de peso para as arquiteturas de computador, uma vez que ela será determinante para o tempo de resposta de um sistema computacional com respeito à execução de uma dada aplicação. A velocidade de cálculo está diretamente relacionada com a freqüência do relógio que pilota o circuito da CPU como um todo. Ainda relacionada com a ALU, é possível destacar a quantidade de operações que ela suporta. Os primeiros processadores suportavam um conjunto relativamente modesto de operações lógicas e aritméticas. Em particular, no que diz respeito às operações aritméticas, os primeiros processadores suportavam apenas operações de adição e subtração, sendo que as demais operações tinham de ser implementadas através de seqüências destas operações básicas 2.1.2 Registradores São dispositivos de alta velocidade, localizados fisicamente na UCP, para armazenamento temporário de dados. O número de registradores varia em função da arquitetura de cada processador. Alguns registradores são de uso específico e têm propósitos especiais, enquanto outros são ditos de uso geral. o contador de instruções (CI) ou program counter (PC) é o registrador respon- sável pelo armazenamento de endereço da próxima instrução que a UCP deverá execu- tar. Toda vez que a UCP executa uma instrução, o PC é atualizado com um novo endereço; 15 UC ULA R e g i s t r a d o r e s Dispositivos de Entrada Armazenamento Dispositivos de Saída o apontador da pilha (AP) ou stack pointer (SP) é o registrador que contém o endereço de memória do topo da pilha, que é a estrutura onde o sistema mantém informações sobre tarefas que estavam sendo processadas, e tiveram que ser interrompidas por algum motivo. 2.1.3 Memória Todo computador é dotado de uma quantidade (que pode variar de máquina para máquina) de memória a qual se constitui de um conjunto de circuitos capazes de armazenar (temporariamente ou por longos períodos de tempo) as unidades de dados e os programas a serem executados pela máquina. Nos computadores de uso geral, é possível encontrar diferentes denominações para as diferentes categorias de memória que neles são encontradas: a memória principal, ou memória de trabalho, onde normalmente devem estar armazenados os programas e dados a serem manipulados pelo processador; a memória secundária que permitem armazenar uma maior quantidade de dados e instruções por um período de tempo mais longo; os discos rígidos são os exemplos mais evidentes de memória secundária de um computador, mas podem ser citados outros dispositivos como CD-ROM, DVD, cartões de memória, etc; a memória cache, que se constitui de uma pequena porção de memória com curto tempo de resposta, normalmente integrada aos processadores e que permite incrementar o desempenho durante a realização de um programa. UCP Figura 2.1 - Composição básica de um computador.[Linhas tracejadas indicam fluxo de controle e Linhas contínuas fluxo de dados] Os circuitos de memória são normalmente subdivididos em pequenas unidades de armazenamento denominadas palavras. Cada palavra é identificada no circuito por um 16 endereço único, o qual vai ser referenciado pelo processador no momento de consultar ou alterar o seu conteúdo. Historicamente, cada palavra de memória permitia armazenar 8 dígitos binários (ou bits), o que introduziu o conceito de Byte como sendo uma palavra de 8 bits, unidade até hoje utilizada para medir a capacidade de armazenamento das memórias e o tamanho dos programas. As quantidades de memória hoje são definidas em termos de KBytes (QuiloBytes) que correspondem a 1024 Bytes ou (210 Bytes) e MBytes (MegaBytes), que correspondem a 1024 KBytes ou (220 Bytes). 2.1.4 Bases A representação de um número depende da base escolhida ou disponível na máquina em uso. A base Decimalé a mais empregada atualmente. Na antiguidade, foram utilizadas outras bases como a base 12, a base 60, etc. Já um computador opera na base 2. (Por quê?) O que ocorre na interação entre o usuário (aplicação) e o computador? O usuário passa seus dados no sistema decimal e estes são convertidos em binário pelo computador. Os resultados numéricos obtidos no sistema binário são convertidos para o sistema decimal e finalmente são transmitidos ao usuário. A seguir os processos de conversão do sistema binário e decimal. Binário para Decimal (347)10 = 3 x 102 + 4 x 101 + 7 x 100 (10111)2 = 1 x 24 + 0 x 23 + 1 x 22 + 1 x 21 + 1 x 20 de modo geral, dado um número N na base b, na forma polinomial é: N = an bn + an-1 bn-1 + ... + a2 b2 + a1 b1 + a0 b0 com esta representação pode-se facilmente converter um número do sistema binário para o sistema decimal, por exemplo: (10111)2 = 1 x 24 + 0 x 23 + 1 x 22 + 1 x 21 + 1 x 20 = 16 + 0 + 4 +2 +1 = (23)10 17 Decimal para Binário Método da Divisão Sucessiva por 2. (11)10 = (?)2 11 2 1 5 2 1 2 2 0 1 (1011)2 Representação de números Como o computador armazena inteiros positivos? Usando todos os bits disponíveis para armazenamento. [0, b n-1] Como o computador armazena inteiros negativos? Sinal-magnitude 0 = ‘+’ e 1 = ‘-‘ Ex.: 1111, ..., 1000 = - 7, ..., - 0; 0000, ..., 0111 = 0, ..., 7; Complemento da Base Veja figura (fig. 2.2). N = b n-1 - a Ex.: 1000, ..., 1111 = - 8, ..., - 1; 0000, ..., 0111 = 0, ..., 7; Como o computador armazena os caracteres? ?“não existe”? Exercícios 1.1 – Sabe-se que os computadores têm a sua disposição apenas zeros e uns para representarem os mais diversos dados. Abaixo vemos números em notação decimal, normalmente utilizada pelo ser humano no seu cotidiano. Converter esses números, em uma notação que o computador possa trabalhar, ou seja, em números binários. 57d 79d 010d 33d 44d 67d 89d 204d 03d 99d 224d 27d 75d 101d 73d 94d 62d 19d 106d 13d 39d 020d 18 Exercícios 1.2 – Os números binários abaixo, que bem poderiam estar na memória de um computador, estão representando quais números decimais? 11011010b 11101010b 01011101b 00100011b 11010001b 11011110b 10001010b 01110101b 00110011b 11010010b 10011010b 10101010b 01010111b 00110111b 11010100b Figura 2.2 – Complemento de base (2) 2.1.5 Unidade de Controle Este último componente de um sistema computacional tem, sem dúvida alguma, a maior importância na operação de um computador, uma vez que é esta unidade quem assume toda a tarefa de controle das ações a serem realizadas pelo computador, comandando todos os demais componentes de sua arquitetura. É este elemento quem deve garantir a correta execução dos programas e a utilização dos dados corretos nas operações que as manipulam. É a unidade de controle que gerencia todos os eventos associados às operações dos computadores, particularmente as chamadas interrupções. Tanto a unidade de controle quanto a unidade lógica e aritmética são imple- mentadas em um circuito único conhecido sob a sigla de CPU (Central Processing Unit) ou por Unidade Central de Processamento, Microprocessador ou, simplesmente, Processador. 2.2 Declarações de variáveis Variáveis representam uma forma de identificar por um nome simbólico uma região da memória que armazena um valor sendo utilizado por uma função. Em C, uma variável deve estar associada a um dos tipos de dados presentes na tabela anterior (tab. 2.1). Toda variável que for utilizada em uma função C deve ser previamente declarada. A forma geral de uma declaração de variável é: tipo nome_variavel; ou tipo nome_var1, nome_var2, ... ; 19 onde nome_var1, nome_var2, ... são variáveis de um mesmo tipo de dado. Exemplos válidos de declaração de variáveis em C são: int um_inteiro; unsigned int outro_inteiro; char c1, c2; float salario; double x, y; Nomes de variáveis podem ser de qualquer tamanho, sendo que usualmente nomes significativos devem ser utilizados, mas somente os 31 primeiros caracteres são considerados. C faz distinção entre caracteres maiúsculos e caracteres minúsculos, de forma que Salario é diferente de salario. Há restrições aos nomes de variáveis. Palavras associadas a comandos e definições da linguagem (tais como if, for e int) são reservadas, não podendo ser utilizadas para o nome de variáveis. A lista de palavras reservadas em C é apresentada no Apêndice A. O nome de uma variável pode conter letras, números e o sublinha ( _ ), mas deve começar com uma letra. Como se pode observar no exemplo acima diversas variáveis de um mesmo tipo podem ser declaradas em um mesmo comando, sendo que o nome de cada variável neste caso estaria separado por vírgulas. Além disto, variáveis podem ser também inicializadas enquanto declaradas, como em: int a = 0, b = 20; char c = ’X’; long int d = 12345678; 2.2.1 Definindo contantes Uma maneira de se declarar constantes é muito semelhante à declaração de variáveis com inicialização, exceto que o seu valor permanecerá inalterado. Uma variável cujo valor não será alterado pelo programa pode ser qualificada como const, como em: const int NotaMaxima = 100; Neste caso, a variável NotaMaxima não poderá ter seu valor alterado. Evidentemente, variáveis deste tipo devem ser inicializadas no momento de sua declaração. Outra forma de declarar constantes simbólicas é por meio da cláusula #define logo no início do programa. #define MAX 1024 #define PI 3.1415926 2.2.2 Definindo variáveis globais 20 Variáveis globais são definidas acima da função main() e seguem a forma abaixo: short int soma; char letra; main () { soma = 0; letra = ‘a’; ... } Estas também podem ser inicializadas como mostra a função main() no mesmo exemplo. 2.3 Expressões Após a declaração das variáveis, o corpo de uma função é definido através dos comandos que serão executados pela função. Estes comandos devem ser expressos sob a forma de uma seqüência de expressões válidas da linguagem C. Antes de mas nada, é interessante que se apresente a forma de se expressar comentários em um programa C. Comentários em C são indicados pelos terminadores /* (início de comentário) e */ (fim de comentário). Quaisquer caracteres entre estes dois pares de símbolos são ignorados pelo compilador. Comentários em C não podem ser aninhados, mas podem se estender por diversas linhas e podem começar em qualquer coluna. Por exemplo, /* Exemplo de * comentario */ main(){ /* esta funcao que faz coisa alguma */ } As expressões na linguagem C são sempre terminadas pelo símbolo ; (ponto e vírgula). Uma expressão nula é constituída simplesmente pelo símbolo terminador. Assim, o exemplo acima é equivalente a /* Exemplo de * comentario */ main(){ /* esta funcao nao faz coisa alguma */ ; } 21 2.3.1 Expressões Aritméticas O comando de atribuição em C é indicado pelo símbolo =, como em: main(){ int a, b, c; a = 10; /* a recebe valor 10 */ b = c = a; /* b e c recebem o valor de a (10) */ } Observe neste exemplo que a atribuição pode ser encadeada - na última linha da função acima, c recebe inicialmente o valor da variável a, e então o valor de c será atribuído à variável b. Expressões aritméticas em C podem envolver os operadores binários (isto é, operadores que tomam dois argumentos) de soma (+), subtração (-), multiplicação (*), divisão (/), lista completa na tabela abaixo (tab 2.3). Valores negativos são indicados pelo operador unário -. Adicionalmente, para operações envolvendo valores inteiros são definidos os operadores de resto da divisão inteira ou módulo (%), incremento (++) e decremento (--). Por exemplo, main(){ int a = 10, b, c, d; b = 2 * a; /* b = 20 */ a++; /* a = a+1 (11) */ c = b / a; /* divisao inteira: c = 1 */ d = b % a; /* resto da divisao: d = 9 */ }Cada um dos operadores de incremento e decremento tem duas formas de uso, dependendo se eles ocorrem antes do nome da variável (pré-incremento ou pré- decremento) ou depois do nome da variável (pós-incremento ou pós-decremento). No caso do exemplo acima, onde o operador de incremento ocorre de forma isolada em uma expressão (sozinho na linha), as duas formas possíveis são equivalentes. A diferença entre eles ocorre quando estes operadores são combinados com outras operações. No exemplo acima, as linhas de atribuição à b e incremento de a poderiam ser combinados em uma única expressão, b = 2*(a++); /* b recebe 2*a e entao a recebe a+1 */ Observe como esta expressão é diferente de b = 2*(++a); /* a recebe a+1 e entao b recebe 2*a */ Na prática, os parênteses nas duas expressões acima poderiam ser omitidos uma vez que a precedência do operador de incremento é maior que da multiplicação, ou seja, o incremento será avaliado primeiro. A subseção 2.3.3 apresenta a ordem de avaliação para todos os operadores da linguagem C (tab. 2.7). 22 Tabela 2.3 – Operadores aritméticos Símbolo Significado + adição * multiplicação - subtração ou inversor do sinal / divisão % resto da divisão de inteiros ++ incremento -- decremento = atribuição +=, -=, *=, /= formas compactas para operações aritméticas A linguagem C tem também uma forma compacta de representar expressões na forma: var = var op (expr); onde uma mesma variável var aparece nos dois lados de um comando de atribuição. A forma compacta é: var op= expr; Por exemplo, a += b; /* equivale a a = a+b */ c *= 2; /* equivale a c = c*2 */ O operador % trabalha somente com operadores int. Já a / (barra) funciona para int e float. Mas esteja atento, pois r = 5/2 será igual a 2, mesmo que r tenha sido declarado como float. Esteja certo de especificar r = 5/2.0 ou r = 5.0/2, ou ainda melhor r = 5.0/2.0. Se preferir, r = (float) 5/2;. A linguagem C oferece também operadores que trabalham sobre a representação binária de valores inteiros e caracteres. Estes operadores são apresentados na tabela (tab. 2.4) a seguir. Tabela 2.4 – Operadores bit a bit Símbolo Significado & and bit a bit (mascara de 0’s) | or bit a bit (mascara de 1’s) ^ xor bit a bit << deslocamento de bits à esquerda (* por 2) >> deslocamento de bits à direita (/ por 2) ~ complemento (inverte cada bit) Estes operadores tomam dois argumentos exceto pelo operador ~, que é unário. Ex.: 23 a = a & 0xDF; /* Dec 223, converte a para maiúsculo */ x = y >> 3; /* divide por 8 */ x = y << 2; /* multiplica por 4 */ 2.3.2 Expressões Condicionais Um tipo muito importante de expressão em C é a expressão condicional, cujo resultado é um valor que será interpretado como falso ou verdadeiro. Como a linguagem C não suporta diretamente o tipo de dado booleano, ela trabalha com representações inteiras para denotar estes valores. O resultado de uma expressão condicional é um valor inteiro que será interpretado como falso quando o valor resultante da expressão é igual a 0, e como verdadeiro quando o valor resultante é diferente de 0. Assim, qualquer expressão inteira pode ser interpretada como uma expressão condicional. A situação mais comum, entretanto, é ter uma expressão condicional comparando valores através dos operadores relacionais. Os operadores relacionais (tab. 2.5) em C são: Tabela 2.5 – Operadores relacionais Operador Operação == igual a != diferente de < menor que > maior que <= menor que ou igual a >= maior que ou igual a Observe que o operador de igualdade é ==, e não = que é o símbolo de atribuição conforme mostra a tabela anterior (tab. 2.3). Esta é uma causa comum de erros para programadores que estão acostumados com outras linguagens onde = é um operador relacional. Expressões condicionais elementares (comparando duas variáveis ou uma variável e uma constante) podem ser combinadas para formar expressões complexas através do uso de operadores booleanos. Estes operadores (tab. 2.6) são: Tabela 2.6 – Operadores lógicos Operador Operação && conjunção (and) || disjunção (or) ! negação (not) O operador && (and) resulta verdadeiro (tab. 2.7) quando as duas expressões envolvidas são verdadeiras (ou diferente de 0). O operador || (or) resulta verdadeiro quando pelo menos uma das duas expressões envolvidas é verdadeira. Além destes dois conectores binários, há também o operador unário de negação, !, que resulta falso quando a expressão envolvida é verdadeira (diferente de 0) ou resulta verdadeiro quando a expressão envolvida é falsa (igual a 0) . 24 Tabela 2.7 – Tabela verdade dos operadores lógicos &&, || e !. A B Operador && Operador || Operador ! (A) 1 1 1 1 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 Expressões lógicas complexas, envolvendo diversos conectores, são avaliadas da esquerda para a direita. Além disto, && tem precedência maior que ||, e ambos têm precedência menor que os operadores lógicos relacionais e de igualdade. Entretanto, recomenda-se sempre a utilização de parênteses em expressões para tornar claro quais operações são desejadas. A exceção a esta regra ocorre quando um número excessivo de parênteses pode dificultar ainda mais a compreensão da expressão; em tais casos, o uso das regras de precedência da linguagem pode facilitar o entendimento da expressão. 2.3.3 Prioridade de operadores Durante a execução de uma expressão que envolve vários operadores, é necessário existir certas prioridades, caso contrário pode-se obter valores que não representam o resultado esperado. A linguagem C utiliza as seguintes prioridades de operadores (tab. 2.8): Tabela 2.8 – Prioridade de operadores Alta prioridade ( ) [ ] -> . ! - * & sizeof (type) ++ -- da direita para esquerda * / % + - << >> < <= >= > == != & ^ | && || ?: da direita para esquerda = += -= etc. da direita para esquerda , vírgula Baixa prioridade 2.4 Exercícios propostos Exercício 2.3 – Entre com dois números e dê como resultado a soma, a média e o somatório dos quadrados dos números. 25 Exercício 2.4 – Escreva um programa que mostre o maior e o menor valor de um conjunto de 10 valores inteiros fornecidos. Exercício 2.5 – Escreva um programa que leia um número fracionário representado um número de grau Celsius e exiba como um número fracionário à temperatura equivalente em Fahrenheit. Mostre o resultado como segue: 100.0 graus Celsius convertido para 212.0 graus Fahrenheit. Exercício 2.6 – Escreva um programa para ler um número de unidade de comprimento (um fracionário) e mostre a área do círculo deste raio. Assuma com valor do pi 3.14159 (uma apropriada declaração deve ser dado a esta constante). A saída deveria ter a seguinte forma: A área do círculo de raio ___ unidades e ___ unidades. Se você desejar melhorar este código, exiba a mensagem: Erro: valores negativos não são permitidos. Se o valor de estrada for negativo. Exercício 2.7 – Dada uma entrada de um número de centímetros em fracionário, mostre o equivalente em número de pés e polegadas (fracionário, 1 decimal), isto é, uma precisão de uma casa decimal. Assuma 2.54 centímetros por polegada e 12 polegadas por pé. Se a entrada for 333.3, a saída deveria ser: 333.33 centímetros e 10 pés ou 11.2 polegadas. Exercício 2.8 – Dada uma entrada de um número de segundos, exibir como saída o equivalente em horas, minutos e segundos. Recomenda-se o formato de saída alguma coisa como: 7322 segundos e equivalente a 2 horas, 2 minutos e 2 segundos. Exercício 2.9 – Faça um programa que, dada a área de um círculo, calcule o perímetro da circunferência que o limita. Use precisão simples. A = pi r2, portanto r = raiz (A / pi) e P = 2 pi r. Exercício 2.10 – Faça um programa que determine o volume de uma esfera, sendo dado o respectivo raio. (V = 4 / 3 pi r3). Repare que, em C, a divisão de 4 por 3 dá um valorinteiro se não tomar medidas adequadas. Exercício 2.11 – Faça um programa que determine se um ano introduzido pelo usuário é ou não bissexto. Um ano é bissexto se for múltiplo de 4 sem ser de 100 ou se for múltiplo de 400. 26 3 O controle do fluxo de execução C é uma linguagem que suporta a programação estruturada, ou seja, permite agrupar comandos na forma de seqüência, seleção e repetição. 3.1 Seqüências Uma seqüência de comandos em uma função C é denotada simplesmente como uma seqüência de expressões, como exemplificado na figura a seguir (fig. 3.1). Há duas possibilidades: usar comandos isolados (a forma mais utilizada) ou usar uma única expressão com a seqüência de comandos separados pelo operador , (vírgula). Figura 3.1 – Seqüência em C - Fluxograma. Formas equivalentes em C: a = 10; b = a*5; c = a+b; ou a=10, b=a*5, c=a+b; 27 3.2 If’s A seguir mostram-se algumas partes de programas com exemplos de uso do comando if: Exemplo 3.1 - Validando a entrada de um inteiro que representa um dia do mês. ... scanf("%d", &dia); if (dia > 31 && dia < 1) printf("Dia invalido\n"); ... Exemplo 3.2 - Teste com um número inteiro, indicando se este é positivo ou negativo. ... scanf("%d", &numero); if (numero >= 0) printf("Numero positivo\n"); else printf("Numero negativo\n"); ... Exemplo 3.3 - Determinando a alíquota a ser calculada sobre a variável fracionária salario. ... scanf("%f", &salario); if (salario < 1280.00) { printf("Aliquota de imposto = 0.1\n"); imposto = salario * 0.1; } else { printf("Aliquota de imposto = 0.25\n"); imposto = salario * 0.25; } ... Uma construção que pode aparecer são os comandos if's aninhados ou encaixados, cuja forma geral é a seguinte: if (expressao) comando; else if (expressao) comando; else if (expressao) comando; ... else comando; 28 Exemplo 3.4 - O programa, Calculadora Simples, a seguir mostra um exemplo com if's encaixados e aninhados. #include <stdio.h> #include <conio.h> main(){ float num1,num2,res; char oper; printf("\nCalculadora simples\n"); printf("Por favor entre com os dois operandos.\n"); scanf("%f %f", &num1, &num2); printf("%f %f\n", num1, num2); printf("Qual a operacao \n"); oper = getch(); printf("A operacao e %c\n", oper); if (oper == '+') res = num1 + num2; else if (oper == '-') res = num1 - num2; else if (oper == '*') res = num1 * num2; else if (oper == '/') if (num2 == 0.0){ printf("Divisao por 0 invalida"); getch(); return(1); } else res = num1 / num2; else{ printf("Operacao invalida!\n"); getch(); return(1); } printf("O resultado da %c vale %f.\n", oper, res); getch(); return(0); } A construção de seleção IF-THEN-ELSE é expressa em C na forma if ... else (fig. 3.2). Após a palavra-chave if deve haver uma expressão condicional entre parênteses. Se a expressão for avaliada como verdadeira, então a expressão sob o if será realizada; se for falsa, a expressão sob o else será executada. Nesta figura (fig. 3.2), introduz-se o conceito de expressão composta, ou seja, a expressão da parte else deste exemplo é na verdade um bloco contendo diversas expressões. Neste caso, o bloco de comandos que deve ser executado nesta condição deve ser delimitado por chaves { e }. Algumas observações adicionais relevantes com relação a este comando são: 29 1. Em C, há diferenças entre letras minúsculas e maiúsculas. Como todos os comandos em C, as palavras chaves deste comando estão em letras minúsculas. Assim, as formas IF (ou If ou iF) não são formas válidas em C para denotar o comando if. 2. Ao contrário do que ocorre em Pascal ou FORTRAN, a palavra then não faz parte da sintaxe deste comando em C. 3. A cláusula else pode ser omitida quando a expressão a executar na condição falsa for nula. 4. No caso de haver mais de um if que possa ser associado a uma cláusula else, esta será associada ao comando if precedente mais próximo. Figura 3.2 – Seleção com if ... else em C. Forma equivalente em C: if (a == 10) b = b-c; else { c = a*10; b = b+a; } 3.3 Switch A construção estruturada de seleção CASE-SELECT é suportada em C pelo comando switch ... case (fig. 3.3). Neste caso, após a palavra-chave switch deve haver uma variável do tipo inteiro ou caractere entre parênteses. Após a variável, deve haver uma lista de casos que devem ser considerados, cada caso iniciando com a palavra-chave case seguida por um valor ou uma expressão inteira. O comando if, em todas suas formas, é suficiente para resolver problemas de seleção de comandos. Porém em alguns casos, como no exemplo anterior (ex. 3.4) o programa se torna mais trabalhoso para ser escrito. O comando switch facilita a escrita de partes de programa em que a seleção deve ser feita entre várias alternativas. A forma geral do comando switch é a seguinte: switch (expressao) { 30 case constante1: comandos; break; case constante2: comandos; break; case constante3: comandos; break; ... default: comandos; } A execução do comando segue os seguintes passos: 1. A expressão é avaliada; 2. O resultado da expressão é comparado com os valores das constantes que aparecem nos comandos case; 3. Quando o resultado da expressão for igual a uma das constantes, a execução se inicia a partir do comando associado com esta constante. A execução continua com a execução de todos os comandos até o fim do comando switch, ou até que um comando break seja encontrado; 4. Caso não ocorra nenhuma coincidência o comando default é executado. O comando default é opcional e se ele não aparecer nenhum comando será executado; 5. O comando break é um dos comandos de desvio da linguagem C. O break se usa dentro do comando switch para interromper a execução e pular para o comando seguinte ao comando switch; Há alguns pontos importantes que devem ser mencionados sobre o comando switch: 1. O resultado da expressão deve ser um tipo compatível com um inteiro, isto é, expressões com resultados tipo char também podem ser usadas; 2. Notar que caso não apareça um comando de desvio todas as instruções seguintes ao teste case que teve sucesso serão executadas, mesmo as que estejam relacionadas com outros testes case; 3. O comando switch só pode testar igualdade, veja exemplo a seguir (ex. 3.5); 4. Não podem aparecer duas constantes iguais em um case; Exemplo 3.5 - O programa, Calculadora Simples 2, mostra um exemplo com a utilização do comando switch. #include <stdio.h> 31 #include <conio.h> main(){ float num1,num2,res; char oper; printf("\nCalculadora simples\n"); printf("Por favor entre com os dois operandos.\n"); scanf("%f %f", &num1, &num2); printf("%f %f\n", num1, num2); printf("Qual a operacao \n"); oper = getch(); printf("A operacao e %c\n", oper); switch (oper){ case ‘+’ : res = num1 + num2; break; case ‘-’ : res = num1 - num2; break; case ‘*’ : case ‘x’ : case ‘X’ : res = num1 * num2; break; case ‘/’ : if (num2 == 0.0){ printf("Divisao por 0 invalida"); getch(); return(1); } else{ res = num1 / num2; break; } default:{ printf("Operacaoinvalida!\n"); getch(); return(1); } } printf("O resultado da %c vale %f.\n", oper, res); getch(); return(0); } 32 Figura 3.3 – Seleção usando switch ... case em C. Forma equivalente em C: switch (a){ case 10: a = a+1; break; case 20: a = a+2; break; case 100:a = a+5; break; default: a = a+10; } Observe que o conjunto de ações associado a cada caso encerra-se com a palavra-chave break. Neste exemplo (ex. 3.5), a variável a pode ser do tipo int ou char. A palavra-chave especial default indica que a ação deve ser tomada quando a variável assume um valor que não foi previsto em nenhum dos casos. Assim como a condição else no comando if é opcional, a condição default também é opcional para o switch-case. Observe também a importância da palavra-chave break para delimitar o escopo da ação de cada caso - fossem omitidas as ocorrências de break no exemplo anterior (ex. 3.5), a semântica associada ao comando seria essencialmente diferente (fig. 3.4). 33 Figura 3.4 – Seleção usando switch ... case em C, omitindo-se o break. Forma equivalente em C: switch (a){ case 10: a = a+1; case 20: a = a+2; case 100:a = a+5; default: a = a+10; } 3.4 Comando ternário O comando ternário tem este nome porque necessita de três operandos para ser avaliado. O comando ternário tem a seguinte forma: expressao1 ? expressao2 : expressao3 Para avaliar o resultado da expressão primeiro expressão1 é avaliada. Caso este resultado seja correspondente ao valor verdadeiro então o resultado da expressão será igual ao valor da expressao2, caso contrário a expressão3 é avaliada e se torna o resultado. Exemplo 3.6 - O programa determina o maior número entre dois valores fornecidos pelo usuário usando o comando ternário. #include <stdio.h> 34 #include <conio.h> main(){ float num1,num2,maior; printf("Por favor entre com os dois numeros.\n"); scanf("%f %f", &num1, &num2); maior = (num1 > num2) ? num1 : num2; printf("O maior dos numeros lidos e %f.\n", maior); getch(); return(0); } 3.5 Repetições Comandos de repetição em C são suportados em três formas distintas. A primeira forma é while, cuja construção equivale ao comando estruturado WHILE-DO, enquanto que a segunda forma equivale ao comando estruturado DO-WHILE. Por fim, tem-se o comando for que equivale a estrutura FOR-DO. 3.5.1 Comando while O comando while tem a seguinte forma geral: while (expressao) comando; A expressão pode assumir o valor falso (igual a 0) ou verdadeiro (diferente de 0). Os passos para execução do comando são os seguintes: 1. A expressão é avaliada; 2. Se a expressão for verdadeira então o comando é executado, caso contrário a execução do comando é terminada; 3. Voltar para o passo 1. 4. Uma característica do comando while, como pode ser visto dos passos acima, é que o comando pode não ser executado. Figura 3.5 – Repetição usando while ... em C. 35 Forma equivalente em C: while (a != 10) a = a+1; Exemplo 3.7 - O programa que mostra os números de 1 a 100 usando o comando while. #include <stdio.h> #include <conio.h> main(){ int i; i = 1; while (i <= 100){ printf("Numero %d\n", i); i++; } getch(); return(0); } O trecho acima (ex. 3.7) escrito de forma mais compacta fica: i = 1; while (i <= 100) printf("Numero %d\n", i++); ... A expressão do comando pode incluir chamada de função. Lembrar que qualquer atribuição entre parênteses é considerada como uma expressão que tem o valor da atribuição sendo feita. Por exemplo, o trecho abaixo (ex 3.8) repete um bloco de comandos enquanto o usuário usar a tecla 'c' para continuar, qualquer outra tecla o bloco é interrompido. Exemplo 3.8 - O programa que o usuário pode controlar o número de vezes os comandos do while irão ser executados. #include <stdio.h> #include <conio.h> main(){ char c; printf("Tecle c para continuar.\n"); 36 while ((c=getche()) == 'c'){ printf("\nNao Acabou.\n"); } printf("\nAcabou.\n"); getch(); return(0); } 3.5.2 Comando do while O comando do while tem a seguinte forma geral: do comando; while (expressao); Observar que neste comando a expressão de teste está após a execução do comando, portanto o comando é executado pelo menos uma vez. A execução do comando segue os seguintes passos: 1. Executa o comando; 2. Avalia a expressão; 3. Se a expressão for verdadeira então volta para o passo 1, caso contrário interrompe-se o do-while. Figura 3.6 – Repetição usando do while ... em C. Forma equivalente em C: do a = a+1; while (a != 10); Exemplo 3.9 - O programa que mostra os números de 1 a 100 usando o comando do while. #include <stdio.h> 37 #include <conio.h> main(){ int i; i = 1; do{ printf("Numero %d\n", i); i++; }while (i <= 100); getch(); return(0); } 3.5.3 Comando for Este comando aparece em várias linguagens de programação, mas na linguagem C ele apresenta um grau maior de flexibilidade. A idéia básica do comando for é a seguinte: A forma geral do comando for é a seguinte: for (expressao1; expressao2; expressao3) comando; é equivalente a expressao1; while (expressao2){ comando; expressao3; } As três expressões geralmente têm os seguintes significados: A expressao1 é utilizada para inicializar a variável de controle do laço; A expressao2 é um teste que controla o fim do laço; A expressao3 normalmente faz um incremento ou decremento da variável de controle. A execução do comando for segue os seguintes passos: 1. A expressao1 é avaliada; 2. A expressao2 é avaliada para determinar se o comando deve ser executado; 38 3. Se o resultado da expressao2 for verdadeiro o comando é executado, caso contrário o laço é terminado; 4. A expressao3 é avaliada; 5. Voltar para o passo 2. Figura 3.7 – Repetição usando for ... em C. Forma equivalente em C: for (a = 0; a < MAX; ++a) b = 2 * b; Exemplo 3.10 - O programa que mostra os números de 0 a 100 usando o comando for. #include <stdio.h> #include <conio.h> main(){ int i; for (i = 0; i <=100; i++) printf("Numero %d\n", i); getch(); return(0); } É possível omitir qualquer uma das expressões. Por exemplo, se a expressao2 for omitida o programa assume que ela é sempre verdade de modo que o laço só termina com um comando de desvio como o break. Exemplo 3.11 - O programa que mostra os números de 0 a 5 usando o comando for em conjunto com o comando break. #include <stdio.h> #include <conio.h> main(){ 39 int i; for (i = 0; ; i++){ printf("Numero %d\n", i); if (i == 5) break; } getch(); return(0); } Exemplo 3.12 - O programa que mostra a tabuada da multiplicação dos números de 1 a 10. #include <stdio.h> #include <conio.h> main(){ int i, j; for (i = 1;i < 10; i++){ printf("Tabuada de %d\n", i); getch(); for (j = 1;j < 10; j++) printf("\n%d x%d = %d\n", i, j, i * j); } getch(); return(0); } Exemplo 3.13 - O programa que mostra como se pode calcular o fatorial de um número usando o comando for. #include <stdio.h> #include <conio.h> int main(){ unsigned int numero, i, fat = 1; printf("\nEntre com um numero positivo."); scanf("%u", &numero); for (i = numero; i > 1; i--) fat = fat * i; printf("O fatorial de %u vale %u.", numero, fat); getch(); return(0); } 40 Laços for com mais de um comando por expressão Outra possibilidade que o comando for em C permite e a inclusão de mais de um comando, separados por vírgulas, nas expressões. O exemplo a seguir (ex. 3.13)mostra um exemplo de uso de comando for com vários comandos nas expressões. Exemplo 3.13 - O programa que usa seqüência nas expressões do comando for. #include <stdio.h> #include <conio.h> void main(){ int i,j; for (i = 1, j = 10; i <= 10; i++, j += 10){ printf ("i = %d, j = %d\n", i, j); } getch(); } Laços for com testes com outras variáveis A expressão de controle não precisa necessariamente envolver somente um teste com a variável que controla o laço. O teste de final do laço pode ser qualquer expressão relacional ou lógica. No exemplo seguinte (ex. 3.14) o laço pode terminar porque a variável de controle já chegou ao seu valor limite ou foi batida a tecla '*'. Obs.: getchar() necessitado do <enter> para terminar. Exemplo 3.14 - O programa que testa mais de uma expressão no comando for. #include <stdio.h> #include <conio.h> #include <ctype.h> void main(){ char c; int i; for (i = 0 ;(i < 5) && (c = getchar()) != '*';i++){ printf("%c\n", toupper(c)); c = getchar(); } getch(); } Laços for com expressões faltando 41 Um outro ponto importante do for é que nem todas as expressões precisam estar presentes. No exemplo na seqüência (ex. 3.15) a variável de controle não é incrementada. A única maneira de programa terminar é o usuário digitar o número -1. Exemplo 3.15 - O programa que depende do usuário para terminar usando o comando for. #include <stdio.h> #include <conio.h> void main(){ int i; for (i = 0 ;i != -1; ){ printf("%d\n", i); scanf("%d", &i); } getch(); } Laço infinito Uma construção muito utilizada é o laço infinito. No laço infinito o programa para quando se usa o comando break. No exemplo abaixo (ex. 3.16) o programa só para quando for digitada ou a tecla 's' ou 'S'. Exemplo 3.16 - O programa que depende do usuário para terminar usando o comando for. #include <stdio.h> #include <conio.h> #include <ctype.h> void main(){ int c; for ( ; ; ){ printf("\nVoce deseja parar?\n"); c = getche(); if (toupper(c) == ‘S’) break; } getch(); } Laços aninhados Uma importante construção aparece quando colocamos como comando a ser repetido um outro comando for. Esta construção aparece quando estamos trabalhando 42 com matrizes. O exemplo anterior (ex. 3.12) mostra um programa que imprime uma tabuada. Desviando laços Qualquer que seja a forma usada para indicar o comando de repetição - while, do while ou for - há duas formas de se desviar a seqüência de execução do padrão do comando. A primeira forma, continue, serve para indicar o fim prematuro de uma iteração. A outra forma de interrupção de um comando de repetição é o comando break, que indica o fim prematuro de todo o comando de iteração. Como por exemplo: ... for (a = 0; a < MAX; ++a){ if (b == 0){ b = a; continue; } c = c / b; b = b – 1 ; } ... se a linha com o comando continue for executada, o valor de a será incrementado e então o teste da iteração será reavaliado para definir a continuidade ou não do laço de repetição. Já no exemplo abaixo, ... for (a = 0; a < MAX; ++a){ if (b == 0) break; c = c / b; b = b - 1; } ... quando (se) b assumir o valor 0, o laço será simplesmente interrompido. 3.6 Exercícios propostos Exercício 3.1 – Faça um programa que leia vários números e informe a média dos números fornecidos. Quando o usuário digitar 0 (zero) o programa termina. 43 Exercício 3.2 – Faça um programa que calcule que determine as raízes de uma equação do tipo ax2 + bx + c = 0. Sendo ∆ = b2 - 4ac, as raízes da equação são dadas por: Se a = 0, o programa não deve efetuar nenhum cálculo, mas visualizar uma mensagem adequada. Exercício 3.3 – Faça um programa que determine se um ano introduzido pelo usuário é ou não bissexto. Um ano é bissexto se for múltiplo de 4 sem ser de 100 ou se for múltiplo de 400. Exercício 3.4 – Faça um programa que calcule a soma de dez números inteiros dados pelo usuário. Exercício 3.5 – Faça um programa que calcule a soma n números inteiros consecutivos que começam num valor dado pelo usuário. Exercício 3.6 – Ler um caractere do teclado e verificar se é um caractere de pontuação: ’,’ ou ‘.’ ou ‘;’ ou ‘!’ ou ‘?’. Exercício 3.7 – Verificar se um caractere lido do teclado é maiúsculo ou minúsculo (entre ‘a’ e ‘z’ é minúsculo). Exercício 3.8 – Ler uma string do teclado com a função gets(s) e imprimir o número de ocorrências do caractere ‘a’. Exercício 3.9 – Fazer maiúscula(s): transforma todas as letras minúsculas em maiúsculas em s. Exercício 3.10 – Escreva um programa que calcule x elevado a n. Assuma que n é um valor inteiro. Exercício 3.11 – Escreva um programa que exiba um menu com as opções "1- multiplicar" e "2-somar", leia a opção desejada, leia dois valores, execute a operação (utilizando o comando "if") e exiba o resultado. 44 Exercício 3.12 – Utilizando if's em escada, inclua, no programa do exercício anterior, as opções "3-Subtrair" e "4-Dividir". Exercício 3.13 – Simplifique os programas anteriores da seguinte forma: Reescreva o programa do exercício 1 substituindo o comando "if" pelo comando ternário. Reescreva o programa do exercício 2 substituindo os if's em escada pelo comando "switch". Exercício 3.14 – Utilizando um laço "while" e o comando "break", escreva um programa que exiba a mensagem “HA-HA-HA!! Você está preso” até que a senha “FUI!!” seja digitada. Exercício 3.15 – Utilizando um laço "for" dentro de outro, escreva um programa que exiba as tabuadas de multiplicação dos números de 1 a 9. Exercício 3.16 – Escreva um programa que tenha um número (inteiro) como entrada do usuário e escreva como saída a seqüência de bits que forma esse numero. Por exemplo, após ter digitado o número 10, a saída deve ser 0000000000001010. Exercício 3.17 – Escreva um programa que imprima todos os números pares entre 0 e 50 e em seguida imprima todos os impares. Deixar um espaço entre os números. Exercício 3.18 – Escreva um programa que leia 10 números. O programa deve imprimir a media, o maior e o menor deles. Obs: Os números devem ser entre 0 e 10. Exercício 3.19 – Escreva um programa que leia 10 números. O programa deve imprimir a media, o maior e o menor deles. Obs: Considere agora que os números podem ser quaisquer. Exercício 3.20 – Escreva um programa que exibe a tabela ascii. Exercício 3.21 – Crie um programa para verificar se um número dado é primo. Exercício 3.22 – Escreva um programa que leia um número inteiro do teclado e acha se o número e primo ou não. O programa deve informar o menor divisor. Exercício 3.23 – Escreva um programa que leia um número do teclado e ache todos os seus divisores. Exercício 3.24 – Escreva um programa que imprima a seqüência "987654321876543217654321654321543214321321211", mas sem imprimir nenhuma constante use apenas variáveis. Em outra linha imprima as letras maiúsculas de A até Z (ABCD...Z). 45 Exercício 3.25 – Escreva um programa que conte de 100 a 999 (inclusive) e exiba, um por linha, o produto dois três dígitos dos números. Por exemplo, inicialmente o programa irá exibir: 0 (1*0*0) 0 (1*0*1) 0 (1*0*2) (...) 0 (1*1*0) 1 (1*1*1) 2 (1*1*2) até 9*9*9=729 Exercício 3.26 – Faça um programa que leia vários números e informar quantos estão no intervalo de 50 e 100. Quando o usuário digitar 0 (zero) o programa termina após mostrar a quantidade de números do intervalo. Exercício 3.27 – Faça um programa que leia vários números e informar quantos estão no intervalo de 50 e 100. Quando o usuário digitar 0 (zero) o programa termina após mostrar a quantidade de números do intervalo. Exercício 3.28 – Faça um programa que calcule n! (n factorial). Use a precisão dupla para o cálculo de n!, porque, embora seja um valor inteiro, o seu cálculo rapidamente conduz avalores elevados. O programa deve ainda não aceitar valores de n que sejam negativos ou que originem um cálculo de n! superior ao limite da precisão dupla (n > 170). Exercício 3.29 – Faça um programa que leia dois valores inteiros e que visualize: A mensagem "Valores iguais" se os valores lidos forem iguais; A lista dos inteiros compreendidos entre os valores introduzidos (inclusive) por ordem crescente. Coloque 10 valores em cada linha e faça uma parada programada quando o visor estiver cheio. Considere que uma linha pode conter até 80 caracteres e que o visor pode conter até 25 linhas. Exercício 3.30 – José tem 1,50m e cresce 2 centímetros por ano, enquanto Juca tem 1,10m e cresce 3 centímetros por ano. Faça um programa que calcule e informe quantos anos seão necessários para que Juca seja maior que José. Exercício 3.31 – Faça um programa que calcule a fatura de energia elétrica para cada consumidor. Leia o número do medidor, a quantidade de kWh consumidor durante o mês e o tipo de consumidor. 1 – residencial, preço R$ 0,34 por kWh, 2 – comercial, preço R$ 0,54 por kWh e 3 – industrial, preço R$ 0,84 por kWh. Os dados devem ser lidos até que seja fornecido um código 0 (zero) para um consumidor. Mostrar o total da fatura do consumidor. 46 4 Funções Em C, diferentemente de outras linguagens como Pascal, todas as ações ocorrem dentro de funções. Na linguagem C não há conceito de um programa principal, o que existe é uma função chamada main que é sempre a primeira a ser executada. A forma geral de uma função em C é a seguinte: tipo nome (tipo nome1, tipo nome2, ..., tipo nomeN){ declaração das variáveis corpo da função } O tipo na definição da função especifica o tipo do resultado que será devolvido ao final da execução da função. Caso nenhum tipo seja especificado o compilador assume que um tipo inteiro é retornado. O tipo void (vazio) pode ser usado para declarar funções que não retornam valor algum. Há duas maneiras básicas de terminar a execução de uma função. Normalmente usa-se o comando return para retornar o resultado da função. Portanto, quando o comando return (expressao) ; for executado, o valor da expressão é devolvido para a função que chamou. Quando não há valor para retornar o comando return não precisa ser usado e a função termina quando a chave que indica o término do corpo da função é atingida. O nome da função é qualquer identificador válido. A lista de parâmetros é uma lista, separada por vírgulas, de variáveis com seus tipos associados. É possível que existam funções que não tenham lista de parâmetros, mas ainda assim é necessário que os parênteses sejam usados. Os parâmetros são valores que a função recebe para realizar as tarefas para as quais foi programada. Por exemplo, uma função que calcule a raiz quadrada de um número do tipo float, deve declarar como parâmetro uma variável deste tipo para receber o valor. É importante notar que diferentemente de declarações de variáveis onde podemos associar vários nomes de variáveis a uma declaração como em: int a, dia, mes, i; 47 na lista de parâmetros é necessário associar um tipo a cada variável como no exemplo abaixo: float media (float n1, float n2, float n3) Suponha que uma determinada função, A, deseje usar uma outra função, B. A função A deve colocar no local desejado o nome da função B e a lista de valores que deseja passar. Por exemplo, uma função que deseje usar a função media, cujo protótipo foi definido acima, para calcular a média de três valores: nota1, nota2 e nota3, deve escrever no local onde quer que a média seja calculada o seguinte comando: resultado = media(nota1, nota2, nota3); onde resultado é a variável que irá receber a média calculada. É importante notar que os tipos e o número de parâmetros que aparecem na declaração da função e na sua chamada devem estar na mesma ordem e ter tipos equivalentes. Se os tipos são incompatíveis, o compilador não gera um erro, mas podem ser gerados avisos na compilação e resultados estranhos. 4.1 Protótipos de funções O padrão ANSI estendeu a declaração da função para permitir que o compilador faça uma verificação mais rígida da compatibilidade entre os tipos que a função espera receber e àqueles que são fornecidos. Protótipos de funções ajudam a detectar erros antes que eles ocorram, impedindo que funções sejam chamadas com argumentos inconsistentes. A forma geral de definição de um protótipo é a seguinte: tipo nome (tipo nome1, tipo nome2, ..., tipo nomeN); Exemplo 4.1 - Programa para calcular a soma de dois números utilizando a declaração de uma função e seu protótipo. #include <stdio.h> #include <conio.h> /* Prototipo da funcao */ int soma (int a, int b); /* Funcao Principal */ main(){ int x = 3, y = 4; printf("\nSoma = %d", soma(x,y)); getch(); return (0); } 48 /* Definicao da funcao */ int soma(int a, int b){ return (a + b); } 4.2 Escopo de variáveis Variáveis podem ser usadas dentro de uma função particular, e somente dentro desta função, ou pode ocorrer que uma ou mais variáveis precisem ser acessíveis a diversas funções diferentes. Por esta razão temos que definir onde as variáveis de um programa podem ser definidas e a partir deste local inferir onde elas estarão disponíveis. As variáveis podem ser declaradas basicamente em três lugares: dentro das funções, fora de todas as funções e na lista de parâmetros das funções. As variáveis dentro das funções são chamadas de variáveis locais, as que aparecem fora de todas as funções são conhecidas como variáveis globais e aquelas que aparecem na lista de parâmetros são os parâmetros formais. É importante notar que em C todas as funções estão no mesmo nível, por isto é não é possível definir uma função dentro de outra função. 4.2.1 Variáveis locais As variáveis locais são aquelas declaradas dentro de uma função. Elas passam a existir quando do início da execução do bloco de comandos ou função onde foram definidas e são destruídas ao final da execução do bloco. Uma variável local só pode ser referenciada, ou seja, usada, dentro das funções onde foram declaradas. Outro ponto muito importante é que como as variáveis locais deixam de existir ao final da execução da função, elas são invisíveis para outras funções do mesmo programa. O código que define uma função e os seus dados são particulares a função. O programa a seguir (ex. 4.2) exibe o uso de algumas variáveis locais. Na função main tem-se cinco variáveis locais: numero, potencia, continua, para e linha. A função Eleva possui somente a variável res, enquanto que ParaMaiusculas não possui variável local. A única variável nesta função é c que é um parâmetro. Exemplo 4.2 - Programa para elevar um número qualquer a uma potência inteira usando função. O programa deve terminar ao desejo do usuário. #include <stdio.h> #include <conio.h> #include <stdlib.h> #define SIM 1 #define NAO 0 int Eleva (int, int); char ParaMaiuscula (char c); 49 main(){ int num, pot; char continuar; int parar; char linha[80]; do{ puts("Entre com um numero"); gets(linha); num = atoi(linha); puts("Entre com a potencia"); gets(linha); pot = atoi(linha); printf("\n%d Elevado a %d ", num, pot); printf("eh = %d\n", Eleva(num, pot)); puts("Continuar? [S]im ou [N]ao? "); continuar = getchar(); getchar(); continuar = ParaMaiuscula(continuar); parar = continuar == 'S'? NAO : SIM; }while (!parar); return(0); } int Eleva(int a, int b){ int res = 1; for ( ; b > 0; b--) res *= a; return (res); } char ParaMaiuscula (char c){ if ('a' <= c && c <= 'z') c = c - 'a' + 'A'; return (c); } Alguns autores usam o termo variáveis automáticas para se referir as variáveis locais. Em C
Compartilhar