Вы находитесь на странице: 1из 211

Curso de Linguagem C Em Construo v0.003.

11
Adriano Joaquim de Oliveira Cruz Instituto de Matemtica Ncleo de Computao Eletrnica UFRJ 2011 Adriano Cruz 8 de junho de 2013

Sumrio
1 Introduo 1.1 1.2 Sucessos e Fracassos da Computao . . . . . . . . . . . . . . . . Um Pouco da Histria da Computao . . . . . . . . . . . . . . . 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.4 1.5 1.6 O Incio . . . . . . . . . . . . . . . . . . . . . . . . . . . . A Era Moderna . . . . . . . . . . . . . . . . . . . . . . . . O Desenvolvimento durante as Grandes Guerras . . . . . As Geraes . . . . . . . . . . . . . . . . . . . . . . . . . . Microcomputadores . . . . . . . . . . . . . . . . . . . . . Memrias . . . . . . . . . . . . . . . . . . . . . . . . . . . Bits e Bytes . . . . . . . . . . . . . . . . . . . . . . . . . . Perifricos . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 15 15 15 18 20 21 22 23 25 26 26 32 33 34 34 35 37 37 37 38 40 41 42 42 43

O Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

O Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Um programa em C . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Algoritmos 2.1 2.2 2.3 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Primeiros Passos . . . . . . . . . . . . . . . . . . . . . . . . . . . Representao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 2.3.2 2.3.3 2.4 2.5 Linguagem Natural . . . . . . . . . . . . . . . . . . . . . . Fluxogramas . . . . . . . . . . . . . . . . . . . . . . . . . Pseudo-Linguagem . . . . . . . . . . . . . . . . . . . . . .

Modelo de von Neumann . . . . . . . . . . . . . . . . . . . . . . . Estruturas Bsicas de Algoritmos . . . . . . . . . . . . . . . . . . 2.5.1 2.5.2 2.5.3 Comandos de leitura . . . . . . . . . . . . . . . . . . . . . Comandos de escrita . . . . . . . . . . . . . . . . . . . . . Expresses . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2.5.4 2.5.5 2.5.6 2.6 2.7

Comandos de atribuio . . . . . . . . . . . . . . . . . . . Comandos de controle . . . . . . . . . . . . . . . . . . . . Comandos de repetio . . . . . . . . . . . . . . . . . . .

45 46 47 48 53 55 55 55 55 56 56 57 58 59 59 60 61 62 62 63 63 64 65 67 67 67 68 68 71 73 73 74 75 75 76 78

Exemplos de Algoritmos . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3 Tipos de Dados, Constantes e Variveis 3.1 3.2 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipos de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 3.2.2 3.3 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.4 3.5 3.4.1 3.5.1 3.5.2 3.5.3 3.6 Tipos Bsicos . . . . . . . . . . . . . . . . . . . . . . . . . Modicadores de tipos . . . . . . . . . . . . . . . . . . . . Constantes Inteiras na base 10 . . . . . . . . . . . . . . . Constantes Inteiras Octais . . . . . . . . . . . . . . . . . . Constantes Inteiras Hexadecimais . . . . . . . . . . . . . . Converso entre Bases . . . . . . . . . . . . . . . . . . . . Constantes em Ponto Flutuante . . . . . . . . . . . . . . . Constantes Cadeias de Caracteres . . . . . . . . . . . . . Nomes das Variveis . . . . . . . . . . . . . . . . . . . . . Declarao de variveis . . . . . . . . . . . . . . . . . . . Atribuio de valores . . . . . . . . . . . . . . . . . . . . .

Constantes Numricas . . . . . . . . . . . . . . . . . . . . . . . .

Constantes Caracteres . . . . . . . . . . . . . . . . . . . . . . . . Variveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4 Entrada e Sada pelo Console 4.1 4.2 4.3 4.4 4.5 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Biblioteca Padro . . . . . . . . . . . . . . . . . . . . . . . . . . . Sada - A Funo printf . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Cdigos de Converso . . . . . . . . . . . . . . . . . . . . Entrada - A Funo scanf . . . . . . . . . . . . . . . . . . . . . . Lendo e Imprimindo Caracteres . . . . . . . . . . . . . . . . . . . 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6 Funes getchar e putchar . . . . . . . . . . . . . . . . . Lendo e Imprimindo Cadeias de Caracteres . . . . . . . . Lendo e Imprimindo cadeias com scanf e printf . . . . . Lendo e Imprimindo cadeias com gets e puts . . . . . . . A Funo fgets . . . . . . . . . . . . . . . . . . . . . . .

Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

5 Operadores e Expresses 5.1 5.2 5.3 5.4 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operador de Atribuio . . . . . . . . . . . . . . . . . . . . . . . Operadores Aritmticos . . . . . . . . . . . . . . . . . . . . . . . Operadores Relacionais e Lgicos . . . . . . . . . . . . . . . . . . 5.4.1 5.4.2 5.5 5.6 5.7 5.8 5.9 Operadores Relacionais . . . . . . . . . . . . . . . . . . . Operadores Lgicos . . . . . . . . . . . . . . . . . . . . .

79 79 79 80 81 81 81 83 85 86 86 87 88 89 91 91 91 91 92 93 95 97 97

Operadores com Bits . . . . . . . . . . . . . . . . . . . . . . . . . Operadores de Atribuio Composta . . . . . . . . . . . . . . . . Operador vrgula . . . . . . . . . . . . . . . . . . . . . . . . . . . Operador sizeof() . . . . . . . . . . . . . . . . . . . . . . . . . . Converso de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . .

5.10 Regras de Precedncia . . . . . . . . . . . . . . . . . . . . . . . . 5.11 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Comandos de Controle 6.1 6.2 6.3 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Blocos de Comandos . . . . . . . . . . . . . . . . . . . . . . . . . Comandos de Teste . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 6.3.2 6.3.3 6.4 6.4.1 6.4.2 6.4.3 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.6 Comando if . . . . . . . . . . . . . . . . . . . . . . . . . . Comando switch . . . . . . . . . . . . . . . . . . . . . . . Comando Ternrio . . . . . . . . . . . . . . . . . . . . . . Comando for . . . . . . . . . . . . . . . . . . . . . . . . .

Laos de Repetio . . . . . . . . . . . . . . . . . . . . . . . . . .

Comando while . . . . . . . . . . . . . . . . . . . . . . . 100 Comando do-while . . . . . . . . . . . . . . . . . . . . . 101 Comando break . . . . . . . . . . . . . . . . . . . . . . . 102 Comando continue . . . . . . . . . . . . . . . . . . . . . 102 Comando goto . . . . . . . . . . . . . . . . . . . . . . . . 103 Funo exit() . . . . . . . . . . . . . . . . . . . . . . . . 103 Comando return . . . . . . . . . . . . . . . . . . . . . . . 103

Comandos de Desvio . . . . . . . . . . . . . . . . . . . . . . . . . 102

Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

7 Vetores e Cadeias de Caracteres 7.1 7.2 7.3 7.4 7.5 7.6 7.7

107

Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Declarao de Vetores Unidimensionais . . . . . . . . . . . . . . . 107 Cadeias de Caracteres . . . . . . . . . . . . . . . . . . . . . . . . 108 Declarao de Vetores Multidimensionais . . . . . . . . . . . . . . 112 Vetores de Cadeias de Caracteres . . . . . . . . . . . . . . . . . . 114 Inicializao de Vetores e Matrizes . . . . . . . . . . . . . . . . . 116 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 122

8 Funes 8.1 8.2 8.3 8.4

Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Forma Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Prottipos de Funes . . . . . . . . . . . . . . . . . . . . . . . . 124 Escopo de Variveis . . . . . . . . . . . . . . . . . . . . . . . . . 125 8.4.1 8.4.2 Variveis Locais . . . . . . . . . . . . . . . . . . . . . . . 125 Variveis Globais . . . . . . . . . . . . . . . . . . . . . . . 127 Passagem de Parmetros por Valor . . . . . . . . . . . . . 128 Passagem de Parmetros por Referncia . . . . . . . . . . 129 Passagem de Vetores e Matrizes . . . . . . . . . . . . . . . 129

8.5

Parmetros Formais . . . . . . . . . . . . . . . . . . . . . . . . . 127 8.5.1 8.5.2 8.5.3

8.6 8.7 8.8 8.9

O Comando return . . . . . . . . . . . . . . . . . . . . . . . . . 132 Recurso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Argumentos - argc e argv . . . . . . . . . . . . . . . . . . . . . . 133 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 138

9 Ponteiros 9.1 9.2

Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Operaes com Ponteiros . . . . . . . . . . . . . . . . . . . . . . 139 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 Declarao de Ponteiros . . . . . . . . . . . . . . . . . . . 139 Os Operadores Especiais para Ponteiros . . . . . . . . . . 140 Atribuio de Ponteiros . . . . . . . . . . . . . . . . . . . 141 Incrementando e Decrementando Ponteiros . . . . . . . . 142 Comparao de Ponteiros . . . . . . . . . . . . . . . . . . 144 . . . . . . . . . . . . . . . . . 145

9.3 9.4 9.5 9.6 9.7 9.8 9.9

Ponteiros e Vetores . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Ponteiros e Cadeias de Caracteres Alocao Dinmica de Memria . . . . . . . . . . . . . . . . . . . 146 Ponteiros e Matrizes . . . . . . . . . . . . . . . . . . . . . . . . . 147 Vetores de Ponteiros . . . . . . . . . . . . . . . . . . . . . . . . . 148 Ponteiros para Ponteiros . . . . . . . . . . . . . . . . . . . . . . . 151 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 4

10 Estruturas

160

10.1 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 10.2 Denies Bsicas . . . . . . . . . . . . . . . . . . . . . . . . . . 160 10.3 Atribuio de Estruturas . . . . . . . . . . . . . . . . . . . . . . . 163 10.4 Matrizes de Estruturas . . . . . . . . . . . . . . . . . . . . . . . . 163 10.5 Estruturas e Funes . . . . . . . . . . . . . . . . . . . . . . . . . 164 10.6 Ponteiros para Estruturas . . . . . . . . . . . . . . . . . . . . . . 166 10.7 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 11 Entrada e Sada por Arquivos 171

11.1 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 11.2 Fluxos de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 11.2.1 Fluxos de Texto . . . . . . . . . . . . . . . . . . . . . . . 171 11.2.2 Fluxo Binrio . . . . . . . . . . . . . . . . . . . . . . . . . 172 11.2.3 Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 11.3 Funes de Entrada e Sada . . . . . . . . . . . . . . . . . . . . . 173 11.4 Incio e Fim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 11.4.1 Abrindo um Arquivo . . . . . . . . . . . . . . . . . . . . . 174 11.4.2 Fechando um Arquivo . . . . . . . . . . . . . . . . . . . . 175 11.4.3 Fim de Arquivo . . . . . . . . . . . . . . . . . . . . . . . . 175 11.4.4 Volta ao Incio . . . . . . . . . . . . . . . . . . . . . . . . 176 11.5 Lendo e Escrevendo Caracteres . . . . . . . . . . . . . . . . . . . 176 11.6 Testando Erros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 11.7 Lendo e Escrevendo Cadeias de Caracteres . . . . . . . . . . . . . 180 11.8 Entrada e Sada Formatada . . . . . . . . . . . . . . . . . . . . . 181 11.9 Lendo e Escrevendo Arquivos Binrios . . . . . . . . . . . . . . . 182 11.10Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 12 Problemas Extras A Tabela ASCII B Palavras Reservadas 188 199 201

Lista de Figuras
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 Fotograa de um circuito integrado de microprocessador Pentium. 14 Imagem de um baco. . . . . . . . . . . . . . . . . . . . . . . . . Blaise Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Charles Babbage . . . . . . . . . . . . . . . . . . . . . . . . . . . Fotograa da Dierence Engine . . . . . . . . . . . . . . . . . . . Computador Eniac . . . . . . . . . . . . . . . . . . . . . . . . . . Diagrama Bsico de um Computador Digital . . . . . . . . . . . Nveis de hierarquia da memria de um computador. . . . . . . . Tamanho de Bits, Bytes e Palavras . . . . . . . . . . . . . . . . . 15 16 17 17 19 22 23 25 30 38 39 42 46 48 50

1.10 Ciclo de desenvolvimento de um programa. . . . . . . . . . . . . 2.1 2.2 2.3 2.4 2.5 2.6 7.1 9.1 9.2 9.3 9.4 9.5 9.6 9.7 Smbolos mais comumente usados em uxogramas. . . . . . . . . Fluxograma para resolver uma equao do primeiro grau. . . . . Modelo de memria . . . . . . . . . . . . . . . . . . . . . . . . . Fluxograma do comando se ... ento ... seno. . . . . . . Fluxograma para decidir se deve levar um guarda-chuva. . . . . . Fluxograma do comando enquanto. . . . . . . . . . . . . . . . .

Mapa de memria de uma matriz. . . . . . . . . . . . . . . . . . 116 Mapa de memria com duas variveis e ponteiro. . . . . . . . . . 138 Ponteiro apontando para rea de memria contendo vetor. . . . . 139 Declarao de ponteiros. . . . . . . . . . . . . . . . . . . . . . . . 140 Atribuio de endereo de uma varivel a um ponteiro. . . . . . . 141 Uso de um ponteiro para copiar valor de uma varivel. . . . . . . 141 Exemplos de atribuies de ponteiros. . . . . . . . . . . . . . . . 142 Armazenamento de matrizes com vetores de ponteiros. . . . . . . 152

11.1 Fluxos de dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

Lista de Tabelas
1.1 1.2 1.3 1.4 1.5 2.1 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 4.1 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 7.1 Transistores por circuito integrado nos microprocessadores da Intel 14 Tempo de execuo das instrues aritmticas no ENIAC . . . . Exemplos de Microprocessadores . . . . . . . . . . . . . . . . . . Abreviaes usadas em referncias s memrias. . . . . . . . . . . Exemplos de perifricos . . . . . . . . . . . . . . . . . . . . . . . Operadores Aritmticos. . . . . . . . . . . . . . . . . . . . . . . . Tipos de dados denidos pelo Padro ANSI C. . . . . . . . . . . Constantes Inteiras na Base 10 . . . . . . . . . . . . . . . . . . . Constantes octais . . . . . . . . . . . . . . . . . . . . . . . . . . . Constantes hexadecimais . . . . . . . . . . . . . . . . . . . . . . . Constantes em ponto utuante . . . . . . . . . . . . . . . . . . . Exemplos de constantes caractere . . . . . . . . . . . . . . . . . . Exemplos de caracteres invisveis. . . . . . . . . . . . . . . . . . . Tabela do exercicio 6 . . . . . . . . . . . . . . . . . . . . . . . . . Cdigos de Converso para escrita de dados. . . . . . . . . . . . . Operadores aritmticos. . . . . . . . . . . . . . . . . . . . . . . . Operadores Relacionais. . . . . . . . . . . . . . . . . . . . . . . . Operador Lgico E. . . . . . . . . . . . . . . . . . . . . . . . . . . Operador Lgico OU. . . . . . . . . . . . . . . . . . . . . . . . . . Operador Lgico NO. . . . . . . . . . . . . . . . . . . . . . . . . Precedncia dos operadores lgicos e relacionais. . . . . . . . . . Operadores com bits. . . . . . . . . . . . . . . . . . . . . . . . . . Operador Lgico OU. . . . . . . . . . . . . . . . . . . . . . . . . . Precedncia dos operadores. . . . . . . . . . . . . . . . . . . . . . 19 22 26 26 45 57 58 59 59 61 62 62 66 69 80 81 82 83 83 83 84 84 88

Passos executados durante o algoritmo da bolha. . . . . . . . . . 109 7

11.1 Exemplos de funes de Entrada e Sada. . . . . . . . . . . . . . 174 A.1 Conjunto de caracteres ASCII . . . . . . . . . . . . . . . . . . . . 199 A.2 Conjunto de cdigos especiais ASCII e seus signicados . . . . . 200

Lista de Algoritmos
2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Exemplo de Algoritmo. . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo para resolver uma equao do primeiro grau. . . . . . . Algoritmo para calcular a mdia das notas de um aluno. . . . . . Algoritmo para calcular a maior nota de um grupo de notas. . . . Modelo de memria e funcionamento de um algoritmo . . . . . . . Comando se em pseudo-linguagem . . . . . . . . . . . . . . . . . . Algoritmo para decidir o que fazer no domingo. . . . . . . . . . . Algoritmo para decidir se deve levar um guarda-chuva. . . . . . . Algoritmo para ler 10 nmeros e imprimir se so pares ou no. . . 36 36 38 40 41 46 47 47 49 51 51 52 54 54 60

2.10 Algoritmo para ler nmeros e imprimir se so pares ou no. A quantidade de nmeros a ser lida de 2.11 Algoritmo para calcular a maior nota de uma turma de 25 alunos. 2.12 Algoritmo para calcular a nota mdia de uma turma de 25 alunos. 2.13 Algoritmo para calcular a maior temperatura do ano. . . . . . . . 2.14 Algoritmo do exerccio 11. . . . . . . . . . . . . . . . . . . . . . . 2.15 Algoritmo do exerccio 11. . . . . . . . . . . . . . . . . . . . . . . 3.1 Algoritmo para converter inteiros na base 10 para uma base b. . .

Listings
1.1 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 5.1 5.2 5.3 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 Exemplo de Programa em C. . . . . . . . . . . . . . . . . . . . . Exemplo de impresso de resultados . . . . . . . . . . . . . . . . Exemplo de justicao de resultados. . . . . . . . . . . . . . . . Exemplo de uso de especicador de preciso. . . . . . . . . . . . Exemplo de uso de scanf. . . . . . . . . . . . . . . . . . . . . . Exemplo de uso de getchar e putchar. . . . . . . . . . . . . . . Exemplo de uso de getchar e putchar. . . . . . . . . . . . . . . Exemplo de uso de printf e scanf na leitura de cadeias. . . . . Exemplo de uso de puts e gets na leitura de cadeias. Exemplo de operadores de deslocamento. Exemplo do operador sizeof. Variveis da questo 9. Exemplo de comandos if. Exemplo de switch. . . . . . . . . . . . . . . . . . . . 32 68 70 71 72 73 74 75 76 85 86 90 92 94 96 97 98 99 99

. . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Programas com ifs em escada e aninhados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exemplo de comando ternrio. . . . . . . . . . . . . . . . . . . . Exemplo de comando for. . . . . . . . . . . . . . . . . . . . . . . Exemplo de comando for com testes sobre outras variveis. . . . Exemplo de comando for sem alterao da varivel de controle. Exemplo de comando for sem teste de m. Comando for aninhados.

. . . . . . . . . . . . 100

. . . . . . . . . . . . . . . . . . . . . . 100 . . . . . . . . . . . . . . . . . 101 . . . . . . . . . . . . . . . . . . . . . 106 . . . . . . . . . . . . . . . . . . 110 . . . . . . . . . . . . . . . . . 111

6.10 Comando while com uma funo. 6.11 Programa do exercicio 17. 7.1 7.2 7.3 7.4 7.5

Exemplo de vetores. . . . . . . . . . . . . . . . . . . . . . . . . . 108 Produto escalar de dois vetores. Ordenao pelo mtodo da bolha. Leitura de uma matriz.

Exemplos de funes para cadeias. . . . . . . . . . . . . . . . . . 113 . . . . . . . . . . . . . . . . . . . . . . . 114

10

7.6 7.7 7.8 7.9 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9

Multiplicao de duas matrizes.

. . . . . . . . . . . . . . . . . . 115

Leitura de um vetor de nomes. . . . . . . . . . . . . . . . . . . . 117 Exemplos de tratamento de vetores. . . . . . . . . . . . . . . . . 118 Exemplos de tratamento de vetores. . . . . . . . . . . . . . . . . 118 . . . . . . . . . . . . . . . . . . . . . 121 Exemplo de prottipos. . . . . . . . . . . . . . . . . . . . . . . . 124 Exemplos de variveis locais. . . . . . . . . . . . . . . . . . . . . 126 Denio de varivel dentro de um bloco. . . . . . . . . . . . . . 126 Denio de varivel global. . . . . . . . . . . . . . . . . . . . . 127 Exemplo de passagem por valor. . . . . . . . . . . . . . . . . . . 128 Uso indevido de variveis locais. . . . . . . . . . . . . . . . . . . 129 Passagem de vetor com dimenses. Passagem de vetores sem dimenses.
n

7.10 Programa do exercicio 14.

. . . . . . . . . . . . . . . . 130 . . . . . . . . . . . . . . . 131

Funo recursiva para calcular x . . . . . . . . . . . . . . . . . . 133 . . . . . . . . . . . . . . . . . . . . . . 136

8.10 Uso de argc e argv. . . . . . . . . . . . . . . . . . . . . . . . . . 134 8.11 Programa do exerccio 8. 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 8.12 Programa do problema 9. . . . . . . . . . . . . . . . . . . . . . . 136 Exemplo de atribuio de ponteiros. . . . . . . . . . . . . . . . . 142 Exemplos de operaes com ponteiros. . . . . . . . . . . . . . . 143 Exemplo de subtrao de ponteiros. . . . . . . . . . . . . . . . . 144 Exemplo de comparao de ponteiros. . . . . . . . . . . . . . . . 144 Exemplo de alteraes invlidas sobre ponteiros. . . . . . . . . . 145 Exemplo de notaes de vetores. . . . . . . . . . . . . . . . . . . 145 Exemplo de ponteiro varivel. . . . . . . . . . . . . . . . . . . . 146 . . . . . . . . . 146 Exemplo de ponteiro para cadeia de caracteres.

Exemplo de cpia de cadeias de caracteres. . . . . . . . . . . . . 147 . . . . . . . . . . . . . . . . . 148 . . . . . . . . 150 . . . . . . . . . . . . . . . . . . . . . 149 . . . . . . . . . . . . 150 . . . . . . . . . . . 153 . . 154

9.10 Exemplo de uso de calloc e free. 9.11 Exemplo de uso de malloc.

9.12 Exemplo de matriz normal sem uso de ponteiros. 9.13 Exemplo de matriz mapeada em um vetor. 9.14 Exemplo de uso de vetor de ponteiros. 9.15 Exemplo de uso de ponteiros para ponteiros. 9.17 Continuao do exemplo 9.16. 9.18 Programa do exercicio 11. 9.19 Programa do exercicio 12.

. . . . . . . . . . . . . . 151

9.16 Exemplo de uso de ponteiros para ponteiros usando funes.

. . . . . . . . . . . . . . . . . . . 155

. . . . . . . . . . . . . . . . . . . . . 157 . . . . . . . . . . . . . . . . . . . . . 158

9.20 Listagem do exerccio 13. . . . . . . . . . . . . . . . . . . . . . . 158 11

9.21 Programa do exerccio 14.

. . . . . . . . . . . . . . . . . . . . . 159

10.1 Denio de uma estrutura. . . . . . . . . . . . . . . . . . . . . . 161 10.2 Atribuio de Estruturas. . . . . . . . . . . . . . . . . . . . . . . 163 10.3 Ordenao de Estruturas. . . . . . . . . . . . . . . . . . . . . . . 163 10.4 Passando elementos para funes. 10.6 Funo que ordena estruturas. . . . . . . . . . . . . . . . . . 164 . . . . . . . . . . . . . . . 165 10.5 Passagem de estruturas para funes.

. . . . . . . . . . . . . . . . . . . 166

10.7 Alocao de espao para estruturas. . . . . . . . . . . . . . . . . 167 10.8 Alocao de espao para vetores de estruturas. . . . . . . . . . . 168 10.9 Listagem do exercicio 3. 11.1 Uso da funo feof(). . . . . . . . . . . . . . . . . . . . . . . 169 . . . . . . . . . . . . . . . . . . . . . . . 176 . . . . . . . . . . . . 178 . . . . . . . . . . . . 179 . . . . . . 180

11.4 Uso da funo ferror(). . . . . . . . . . . . . . . . . . . . . . . 177 11.2 Exemplo de leitura e escrita de caracteres. 11.3 Exemplo de leitura e escrita de caracteres.

11.5 Exemplo de leitura e escrita de cadeias de caracteres. 11.7 Exemplo de leitura e escrita na forma binria. 11.8 Exemplo de leitura e escrita de estruturas. 11.9 (I) Trecho de programa do problema 14.

11.6 Exemplo de leitura e escrita de dados formatados. . . . . . . . . 181 . . . . . . . . . . 182 . . . . . . . . . . . . 184 . . . . . . . . . . . . . 187

11.10(II) Trecho de programa do problema 14. . . . . . . . . . . . . . 187 12.1 Processando o CPF. . . . . . . . . . . . . . . . . . . . . . . . . . 191 12.2 Estrutura do problema 8. . . . . . . . . . . . . . . . . . . . . . . 195

12

Captulo 1

Introduo
1.1 Sucessos e Fracassos da Computao

Os objetivos principais deste captulo so mostrar para o aluno iniciante alguns aspectos da histria da computao e denir, informalmente, termos e palavraschave que os prossionais da rea de computao usam no seu dia a dia. Adriano Cruz . A histria do desenvolvimento dos computadores tem sido impressionante. O avano da tecnologia e a presena da computao na nossa vida so inegveis. Embora a histria deste fantstico desenvolvimento seja recente e bem documentada, h lacunas e controvrsias impressionantes sobre diversos pontos. Neste captulo iremos ver histrias de espionagem e brigas na justia por roubo de ideias. H oportunidades perdidas e gente que soube aproveitar a sua chance. H verdades estabelecidas que tiveram de ser revistas. O avano na tecnologia dos computadores se deu em passos to largos que os primeiros computadores parecem to distantes no tempo quanto a Pr-Histria. O aumento de velocidade, desde os anos 40, foi da vrias ordens de grandeza, enquanto que o custo dos computadores caiu de milhes de dlares para valores em torno de centenas de dlares. As primeiras mquinas tinham milhares de vlvulas, ocupavam reas enormes e consumiam quilowatts de energia. O microprocessador Pentium, lanado em 1993, tinha em torno de 3,1 milhes de transistores, ocupava uma rea de aproximadamente 25 cm2 e consumia alguns watts de energia, custando aproximadamente 1000 dlares, somente o microprocessador. A Figura 1.1 mostra a imagem de um circuito integrado de microprocessador Pentium. No entanto, esta histria de reduo de tamanho, aumento de velocidade e diminuio de gasto de potncia, pode, para alguns pesquisadores, j ter uma data xada para terminar. Em 1965, Gordon Moore, um dos fundadores da Intel, fabricante do Pentium e uma dos maiores fabricantes de circuitos integrados do mundo, enunciou o que cou conhecido como a Lei de Moore: Cada novo circuito integrado ter o dobro de transistores do anterior e ser lanado dentro de um intervalo de 18 a 24 meses. Moore achava que esta lei seria vlida somente at 1975, no entanto, ela continua vlida at hoje. Na tabela 1.1, pode-se observar a evoluo dos microprocessadores usados em nossos computadores. 13

Figura 1.1: Fotograa de um circuito integrado de microprocessador Pentium. Ano 1971 1972 1974 1982 1985 1989 1993 1997 1999 2000 Processador 4004 8008 8080 80286 80386 80486 DX Pentium Pentium II Pentium III Pentium 4 Transistores 2.250 2.500 5.000 120.000 275.500 1.180.000 3.100.000 7.500.000 24.000.000 42.000.000

Tabela 1.1: Transistores por circuito integrado nos microprocessadores da Intel

Os transistores, que compem os circuitos eletrnicos, esto diminuindo de tamanho, e estamos nos aproximando da fronteira nal, os eltrons. J se houve falar em tamanho de transistores medidos em nmeros de eltrons. Devemos nos lembrar que toda a tecnologia atual est baseada em uxo de eltrons, ou seja uma corrente eltrica. Os os conduzem correntes de eltrons e os transistores controlam este uxo. Se o tamanho diminuir alm dos eltrons estaremos em outro domnio. No entanto, na histria da computao, muitas promessas no foram cumpridas e falhas gigantescas aconteceram. Como em diversas questes, artistas geniais, apontam o que no conseguimos ou no queremos ver e mostram que o rei est nu. H uma frase de Picasso que diz: Computadores so estpidos, eles somente conseguem responder perguntas. Esta frase expe com ironia um fracasso da comunidade de computao que havia prometido criar rapidamente computadores inteligentes, computadores que poderiam questionar-se e nos questionar. Muitos acreditaram nesta promessa e muitos livros de co cientca foram publicados em que este tipo de computador estaria disponvel em um futuro muito prximo. Com notvel exemplo podemos citar o lme 2001 Uma Odissia no Espao, de Stanley Kubrik que estreou em 1968 e foi baseado no conto The Sentinel, escrito em 1950 por Arthur Clark, um dos mestres da co cientca. Neste lme o enlouquecido computador HAL 9000, que era capaz de ver, falar, raciocinar etc, mata quase todos os tripulantes de uma nave

14

espacial. Ora, j passamos por 2001 e no existe a menor possibilidade de se ver um computador como o HAL ou to louco de pedra como ele. Na computao, um exemplo fantstico de sucesso a Internet. A Internet est se tornando essencial para o funcionamento do mundo moderno. Freqentemente ouvimos dizer que ela o meio de comunicao que mais rapidamente se difundiu pelo mundo. Pode parecer verdade, j que conhecemos tantos internautas e as empresas esto se atropelando para fazer parte desta onda e aproveitar as suas possibilidades. Esta corrida provocou alguns acidentes e muitos sonhos de riqueza se esvaram no ar. Hoje, pode-se fazer quase tudo pela Internet, namorar, comprar, pagar contas, fazer amigos, estudar, jogar etc. Quem sabe, em um futuro prximo, voltaremos Grcia Antiga e nos reuniremos em uma enorme praa virtual para, democraticamente, discutir nossas leis, dispensando intermedirios.

1.2
1.2.1

Um Pouco da Histria da Computao


O Incio

A primeira tentativa de se criar uma mquina de contar foi o baco. A palavra vem do rabe e signica p. Os primeiros bacos eram bandejas de areia sobre as quais se faziam guras para representar as operaes. Aparentemente, os chineses foram os inventores do baco de calcular. No entanto, h controvrsias, e os japoneses tambm reivindicam esta inveno, que eles chamam de soroban. Alm disso h os russos, que inventaram um tipo mais simples, chamado de tschoty. So conhecidos exemplares de baco datados de 2500 A.C. A Figura 1.2 ilustra um exemplar com as suas contas e varetas.

Figura 1.2: Imagem de um baco. Em 1901 mergulhadores, trabalhando perto da ilha grega de Antikythera, encontraram os restos de um mecanismo, parecido com um relgio, com aproximadamente 2000 anos de idade. O mecanismo, de grande complexidade, parece ser um dispositivo para calcular os movimentos de estrelas e planetas.

1.2.2

A Era Moderna

Em 1614 John Napier, matemtico escocs, inventou um dispositivo feito de marm para demonstrar a diviso por meio de subtraes e a multiplicao por 15

meio de somas. A semelhana entre marm e ossos, fez com que o dispositivo fosse conhecido como os ossos de Napier. Um dos primeiros instrumentos modernos de calcular, do tipo mecnico, foi construdo pelo lsofo, matemtico e fsico francs Blaise Pascal (Figura 1.3). Em 1642 aos 19 anos, na cidade de Rouen, Pascal desenvolveu uma mquina de calcular, para auxiliar seu trabalho de contabilidade. A engenhoca era baseada em 2 conjuntos de discos interligados por engrenagens: um para a introduo dos dados e outro para armazenar os resultados. A mquina utilizava o sistema decimal para calcular, de maneira que quando um disco ultrapassava o valor 9, retornava ao 0 e aumentava uma unidade no disco imediatamente superior.

Figura 1.3: Blaise Pascal Pascal recebeu uma patente do rei da Frana, o que lhe possibilitou o lanamento de sua mquina no mercado. A comercializao das calculadoras no foi satisfatria devido a seu funcionamento pouco convel, apesar dele ter construdo cerca de 50 verses. As mquinas de calcular, derivadas da Pascalina, como cou conhecida sua mquina, ainda podiam ser encontradas em lojas at alguns poucos anos atrs. Antes de morrer, aos 39 anos, em 1662, Pascal que contribura em vrios campos da Cincia, ainda teve tempo de criar uma variante de sua mquina, a caixa registradora. Em 1666, Samuel Morland adaptou a calculadora de Pascal para resolver multiplicaes por meio de uma srie de somas sucessivas. Independentemente, em 1671 Leibniz projetou uma outra calculadora que somava e multiplicava. Esta calculadora s foi concluda em 1694. O primeiro computador de uso especco comeou a ser projetado em 1819 e terminou em 1822, ou seja, h mais de 180 anos atrs, pelo britnico Charles (1791-1871, Figura 1.4), que o batizou de Dierence Engine (Figura 1.5). A motivao de Babbage era resolver polinmios pelo mtodo das diferenas. Naquele tempo as tbuas astronmicas e outras tabelas eram calculadas por humanos, em mtodos tediosos e repetitivos. Em 1823, ele iniciou o projeto de construir uma outra mquina mais avanada e capaz de calcular polinmios de at sexta ordem. Ele esperava terminar esta mquina em trs anos, mas a construo se arrastou at 1834. Este projeto que no foi completado, usou dinheiro do governo ingls e possivelmente a maior parte da fortuna pessoal de Babbage. A mquina, inteiramente mecnica, teria as seguintes caractersticas: Arredondamento automtico; 16

Figura 1.4: Charles Babbage

Figura 1.5: Fotograa da Dierence Engine

Preciso dupla; Alarmes para avisar m de clculo; Impresso automtica de resultados em placas de cobre. Em 1834 ele tinha completado os primeiros desenhos da mquina que denominou Analytical Engine que tinha as seguintes caractersticas: 50 dgitos decimais de preciso; Memria para 1000 destes nmeros (165000 bits); Controle por meio de cartes perfurados das operaes e endereos dos dados; Tempo de soma e subtrao igual a 1 segundo; tempo de multiplicao e diviso igual a 1 minuto; Sub-rotinas; 17

Arredondamento automtico e deteco de transbordo (overow ); Babagge nunca conseguiu terminar este ambicioso projeto. No entanto, os mais importantes conceitos de computao, que somente vieram a tona nos anos 40 do sculo vinte, j tinham sido considerados por Charles Babbage em o seu projeto. Um fato curioso que entre os auxiliares de Babagge estava Augusta Ada Byron, Countess of Lovelace. Considera-se, hoje, que ela escreveu para Charles Babbage o primeiro programa para computadores. Ada que mudou seu nome para Augusta Ada King, aps seu casamento, estudava Matemtica com DeMorgan que provou um dos teoremas bsicos da lgebra Booleana, que a base matemtica sobre a qual foram desenvolvidos os projetos dos modernos computadores. No entanto, no havia nenhuma ligao entre o trabalho do projetista dos primeiros computadores e o do matemtico que estudava o que viria a ser o fundamento terico de todo a computao que conhecemos hoje.

1.2.3

O Desenvolvimento durante as Grandes Guerras

Antes da Segunda Grande Guerra, em vrios pases, cientistas trabalhavam em projetos que visavam construir computadores com rels, que so dispositivos eletromecnicos usados para ligar ou desligar circuitos eltricos. Rels so os dispositivos que antecederam os transistores na construo de computadores. Alguns destes computadores eram de uso geral, outros tinha nalidades especcas. Alguns destes projetos esto listados a seguir. Na Alemanha Em 1934 na Alemanha Konrad Zuze, engenheiro projetista de avies, concebeu uma mquina de somar para resolver os clculos que deveria realizar em seus projetos. Em 1938, ele concluiu o Z1, um calculador mecnico com uma unidade aritmtica que usava a base 2 para representar os nmeros, base que hoje os computadores modernos empregam. Em 1938 ele melhorou o desempenho do Z1, graas aos rels. O governo alemo patrocinou os trabalhos de Zuze e em 1941 estava pronto o Z2, uma mquina eletromecnica capaz de receber instrues por meio de uma ta de papel. Em 1941 foi introduzido o Z3, que calculava trs a quatro adies por segundo, uma multiplicao em 4 ou 5 segundos e era capaz de extrair a raiz quadrada. Nos Estados Unidos Em 1944 a IBM e H. Haiken da Universidade de Harvard, concluam a construo de um verdadeiro computador: o Harvard Mark I, que operava em base 10. O Mark I efetuava as quatro operaes fundamentais, mais o clculo de funes trigonomtricas, exponenciais e logartmicas. As instrues eram fornecidas por meio de tas de papel e os dados lidos de cartes perfurados. Os resultados eram fornecidos em forma de cartes perfurados ou impressos por meio de mquinas de escrever. 18

Em 1943 na Universidade da Pensilvnia, J. Eckert e J. Mauchly iniciaram a construo de um computador vlvulas, ou seja eletrnico que foi chamado de ENIAC. O projeto foi concludo em 1946 e usado na segunda guerra mundial. O ENIAC podia ser reprogramado para executar diversas operaes diferentes atravs de ligaes por meio de os e conectores. Durante muitos anos o computador ENIAC foi considerado o primeiro computador eletrnico construdo. A mquina projetada pelos Drs. Eckert and Mauchly era gigantesca quando comparada com os computadores pessoais atuais. Quando foi terminado, o ENIAC (Figura 1.6) enchia um laboratrio inteiro, pesava trinta toneladas, e consumia duzentos quilowatts de potncia.

Figura 1.6: Computador Eniac Ele gerava tanto calor que teve de ser instalado em um dos poucos espaos da Universidade que possua sistemas de refrigerao forada. Mais de 19000 vlvulas, eram os elementos principais dos circuitos do computador. Ele tambm tinha quinze mil rels e centenas de milhares de resistores, capacitores e indutores. Toda esta parafernlia eletrnica foi montada em quarenta e dois painis com mais 2,70 metros de altura, 60 centmetros de largura e 30 centmetros de comprimento, montados na forma da letra U. Uma leitora de cartes perfurados e uma perfuradora de cartes eram usados para entrada e sada de dados. Os tempos de execuo do ENIAC so mostrados na Tabela 1.2. Compare estes tempos com os tempos dos computadores atuais que esto na ordem de nano segundos, ou 109 segundos. Operao soma multiplicao diviso Tempo 200 s 2,8 ms 6,0 ms

Tabela 1.2: Tempo de execuo das instrues aritmticas no ENIAC Em 1939 John Vincent Atanaso e Cliord E. Berry, na Universidade Estadual de Iowa construram um prottipo de computador digital eletrnico, que usa aritmtica binria. Em 19 de outubro de 1973, o juiz federal Earl R. Larson assinou uma deciso, em seguida a uma longa batalha judicial, que declarava 19

a patente do ENIAC de Mauchly e Eckert invlida e atribua a Atanaso a inveno computador eletrnico digital, o ABC ou Atanaso-Berry Computer. Na Inglaterra Jnos von Neumann, emigrante hngaro que vivia nos EUA, sugeriu que a memria do computador deveria ser usada para armazenar as instrues do computador de maneira codicada, o conceito de programa armazenado. Esta idia foi fundamental para o progresso da computao. Os primeiros computadores, como o ENIAC, eram programados por os que os cientistas usavam para conectar as diversas partes. Quando um programa terminava, estes cientistas trocavam os os de posio de acordo com a nova tarefa a ser executada. Com o programa armazenado na memria, juntamente com os dados, no era mais necessrio interromper as atividades. Carregava-se o programa na memria, uma tarefa extremamente rpida, junto com os dados e dava-se partida no programa. Ao trmino da execuo do programa passava-se imediatamente para a prxima tarefa sem interrupes para troca de os. Em 1949, na Inglaterra, dois computadores que usavam a memria para armazenar tanto programas como dados foram lanados. Na Universidade de Cambridge foi lanado o EDSAC, (Electronic Delay Storage Automatic Calculator) e em Manchester o computador chamado de Manchester Mark I. O EDSAC considerado o primeiro computador de programa armazenado a ser lanado. Curiosamente, a Universidade de Manchester reivindica que o primeiro computador de programa armazenado foi o chamado Baby, um prottipo do Mark I, que comeou a operar onze meses antes do EDSAC. Outro fato curioso em relao Inglaterra e que foi divulgado recentemente relata que um computador chamado COLOSSUS entrou em operao secretamente na Inglaterra em 1943. Este computador foi usado para auxiliar na quebra dos cdigos de criptograa alemes durante a segunda grande guerra.

1.2.4

As Geraes

Costumava-se dividir os projetos de computadores em geraes. Hoje em dia como a taxa de evoluo muito grande no se usa mais este tipo de terminologia. No entanto interessante mencionar estas divises. Primeira Gerao: Os computadores construdos com rels e vlvulas so os da primeira gerao. Estes computadores consumiam muita energia e espao. Segunda Gerao: Os computadores da segunda gerao foram construdos com transistores, os quais tinham a vantagem de serem mais compactos e consumirem muito menos energia. Por gerarem menos calor eram mquinas mais conveis. Terceira Gerao: Com o advento dos circuitos integrados, que so componentes em que vrios transistores so construdos em uma mesma base de semicondutor, chegamos aos computadores de terceira gerao. Com a integrao o tamanho dos computadores e seu consumo diminuiu ainda mais e aumentou a capacidade de processamento. 20

Quarta Gerao: Os computadores de quarta gerao utilizavam circuitos com a tecnologia (Very Large Scale Integration), que permitia uma escala de integrao de transistores muito grande.

1.3

O Hardware

O hardware corresponde aos circuitos eletrnicos e componentes mecnicos que compem o computador. Um tpico diagrama em blocos de um computador digital monoprocessado esta mostrado na Figura 1.7. A Unidade Central de Processamento (UCP), em ingls Central Processing Unit (CPU), como o prprio nome diz, a unidade onde os dados so processados, ou seja alterados, no computador. Ou seja, dentro das UCPs os dados so somados, subtrados etc. A UCP tambm controla a movimentao dos dados dentro de todo o sistema. Os mdulos que constituem a UCP so os seguintes: Unidade de Controle (UC): comanda a operao do computador. Esta unidade l da memria tanto as instrues como os dados e comanda todos os circuitos para executar cada instruo lida da memria. Atualmente as unidades de controle so capazes de executar mais de uma instruo por ciclo de mquina, o que caracteriza o processamento paralelo. A tcnica mais comum para conseguir este paralelismo conhecida como pipelining, que ser detalhada mais adiante. Unidade Aritmtica e Lgica (UAL): local onde as transformaes sobre os dados acontecem. Atualmente esta unidade bastante sosticada e diversos mtodos so empregadas para acelerar a execuo das instrues. Alguns processadores duplicam circuitos para permitir que mais de uma operao aritmtica seja executada por vez. muito comum, por exemplo, a existncia de uma unidade aritmtica para executar instrues que operam sobre nmeros inteiros e outra sobre nmeros reais, chamada de unidade de ponto utuante. As vezes a UCP conta com mais de uma de cada uma destas unidades. Unidade de Entrada e Sada (UES): controla a comunicao com os usurios do computador e os equipamentos perifricos tais como discos e impressoras. Em alguns computadores mais simples esta unidade no existe independentemente, sendo distribuda pela Unidade Central de Processamento. Em computadores mais poderosos ao contrrio as Unidades de Entrada e Sada so computadores completos que foram programados para controlar a comunicao com os perifricos. O termo pipelining que mencionamos acima se refere a um dos modos de como o processador pode paralelizar a execuo de instrues. Este termo pode ser traduzido por linha de montagem, porque exatamente isto que a tcnica faz, uma linha de montagem de instrues. Por exemplo, em uma linha de montagem de uma fbrica de automveis, mais de um automvel montado ao mesmo tempo. No incio da linha o carro no existe. Em cada estgio da linha de montagem os operrios vo adicionando partes e ao m da linha sai um carro novo. Se voc olhar a linha poder ver carros em diversos estgios da 21

Unidade de Controle

Unidade de Entrada e Sada Memria Discos Vdeo

Unidade Arimtica

Unidade Central de Processamento

Figura 1.7: Diagrama Bsico de um Computador Digital

montagem. Repare que a linha no pra, e a montagem de um carro no espera que o que comeou a ser montado antes dele esteja terminado. Nas linhas de montagem muitos carros so montados ao mesmo tempo. O efeito nal que cada carro continua levando bastante tempo para ser montado, mas como vrios vo sendo montados ao mesmo tempo, algum que se colocasse no nal da linha de montagem teria a impresso que cada carro leva muito pouco tempo para ser montado. Isto porque no nal da linha de montagem sai um carro a cada poucos minutos. O mesmo acontece com a linha de montagem de instrues.

1.3.1

Microcomputadores

Uma UCP integrada em um nico circuito (chip) comumente chamada de microprocessador . Os microprocessadores atuais incluem outros circuitos que normalmente cavam fora da UCP, tais como processadores de ponto utuante e memrias cache. Alguns exemplos de microprocessadores so mostrados na Tabela 1.3 Microprocessador Arquitetura Intel X86 PowerPC Power MIPS ARM SPARC Empresa Intel, AMD Consrcio Apple/IBM/Motorola IBM MIPS Technologies ARM Technologies SUN

Tabela 1.3: Exemplos de Microprocessadores Usualmente se chama de computador, o processador mais os seus perifricos e os sistemas para controlar estes perifricos, ou seja todo o sistema de processamento de dados. Os perifricos so os dispositivos usados para fazer a entrada e sada dos dados que sero processados. Os microcomputadores so computadores baseados em microprocessadores. As assim chamadas placas me dos microprocessadores atuais incluem 22

diversos componentes que formam o microcomputador. Por exemplo, uma placa me pode incluir o microprocessador e seus circuitos de suporte, que, no conjunto so conhecidos como o chipset. Alm disso a placa me pode incluir tambm a memria principal e as placas de controle de perifricos, como a placa de vdeo, e os conectores para os perifricos, enm, quase todo o sistema.

1.3.2

Memrias

Os dados no computador podem ser armazenados em diversos nveis de memria semicondutoras ou em perifricos (discos, tas, etc). Quanto mais rpida a memria, usualmente mais cara ela . A idia por traz da separao em nveis colocar mais perto do processador, em memrias rpidas e mais caras, os dados que o processador ir precisar mais freqentemente. A medida que vamos nos afastando do processador as memrias vo, ao mesmo tempo, cando mais baratas, aumentando de capacidade e diminuindo de velocidade. A Figura 1.8 ilustra esta hierarquia.

PROCESSADOR

MEMRIA
CACHE
REGISTRADORES

DISCO

Figura 1.8: Nveis de hierarquia da memria de um computador. No nvel mais prximo do processador esto os registradores , que, na verdade, cam internamente ao processador. So poucos e extremamente rpidos e, portanto, no podem armazenar grandes quantidades de dados. Somente os dados que so necessrios ao processador com rapidez extraordinria devem ser colocados nos registradores. Durante o processamento normal, na memria principal onde o processador busca instrues e dados de um programa para executar. Alm da memria principal esto os discos. Devido a sua velocidade menor que a da memria principal e a sua grande capacidade, os discos so considerados dispositivos de armazenamento secundrio. Os discos tambm so memrias de armazenamento permanente, isto , quando os computadores so desligados o seu contedo se mantm. Ao contrrio, a memria principal e os registradores so memrias semicondutoras e perdem seus contedos quando a energia eltrica desligada. Em alguns computadores os discos esto sendo substitudos por memrias semicondutoras. Para acelerar o acesso aos dados freqentemente usados, os computadores dispem de memrias mais rpidas, porm de menor capacidade, que cam entre os registradores e a memria principal. Estas funcionam como as bolsas ou carteiras em que carregamos documentos e outros itens que precisamos freqentemente. Este tipo de memria conhecido como memria cache. O cache opera de forma invisvel para o processador. Ao pedir um dado para memria, 23

circuitos especiais vericam se este dado est no cache, caso esteja, ele repassado para o processador. Para o processador o que aconteceu que a memria entregou o dado com uma rapidez maior do que o normal. Uma memria como se fosse uma srie de cofres numerados capazes de armazenar os dados, como est ilustrado na Figura 2.3. Os dados e instrues na memria so apontados ou referenciados por estes nmeros, conhecidos como endereos. Ou seja, para ler um dado da memria necessrio fornecer um endereo para que a memria possa encontrar e devolver o contedo pedido. Isto similar ao o que ocorre quando enviamos uma carta, o endereo faz com que o carteiro saiba onde ele deve entregar a correspondncia. Em operao normal, toda vez que o processador precisa de um dado ele envia um pedido de leitura memria junto com o endereo da memria onde o dado est. Nas escritas o processador envia o endereo, o dado e pedido de escrita. Normalmente a memria somente pode atender um pedido de cada vez. Portanto, para ler 1000 nmeros o processador ter de fazer 1000 acessos seqencialmente. Os dois tipos bsicos de memria mais comuns so ROM e RAM. Estas siglas tm diversas variaes (PROM, EPROM, DRAM, etc), mas os princpios bsicos so os mesmos. Estas siglas indicam os dois tipos bsicos de memria que so usadas em computadores. A sigla bsica ROM signica Read Only Memory, ou seja, memria de somente de leitura. A outra sigla RAM (Random Access Memory) signica memria de acesso randmico, portanto, memria que se pode ler em qualquer endereo. A sigla RAM muito confusa porque em uma memria ROM tambm se pode ler em qualquer endereo. A diferena real que nas RAMs se pode ler e escrever com a mesma velocidade em qualquer endereo, enquanto que na ROM, o acesso rpido somente para leituras, a escrita uma histria mais complicada. A ROM normalmente contm dados que no podem ser modicados durante o funcionamento do computador. Outro tipo de dados armazenados em ROMs so os que no devem ser perdidos quando o computador desligado. Exemplos de uso de ROM so as memrias que armazenam os programas que so executados quando os computadores so ligados, os famosos BIOS (Basic Input Output System ). Um computador ao ser ligado deve ter um programa mnimo capaz de iniciar o seu funcionamento normal, caso contrrio seria como uma pessoa que perdeu totalmente a memria. Para isto so escritos programas simples que fazem acesso aos perifricos em busca do Sistema Operacional da mquina. As primeiras memrias do tipo ROM eram gravadas nas fbricas e nunca mais eram modicadas. Isto trazia algumas diculdades, por exemplo, quando um programa precisava ser atualizado. Para resolver este tipo de problemas surgiram as PROMs, que so ROMs programveis. Ou seja possvel desgravar o contedo antigo e gravar novos programas nesta memria. Antigamente este era um processo complicado e exigia que a memria fosse retirada sicamente do circuito e colocada em dispositivos especiais capazes de apagar o contedo antigo. Em seguida um circuito programador de PROMs era usado para gravar o novo contedo e somente aps tudo isto a memria era recolocada no local. O computador cava literalmente sem a memria dos programas iniciais. Hoje em dia existem PROMs que podem ser apagadas e regravadas muito facilmente. Por exemplo, as EEPROMs (Eletricaly Erasable PROMs), que so memrias que podem ser apagadas eletricamente sem a necessidade de serem retiradas

24

dos circuitos. Flash memory uma forma de memria no voltil que pode ser apagada e reprogramada eletricamente. Diferentemente das EEPROMs, ela deve ser apagada em blocos de endereos. Este tipo de memria custa menos do que EEPROMs e portanto so preferidas quando necessrio usar memria no voltil em forma de circuitos integrados. As memrias RAMs so as memrias onde os nossos programas comuns rodam. Elas so modicveis e de acesso rpido tanto na leitura quanto na gravao. Muitas siglas aparecem e desaparecem quando falamos de memrias RAM. Existem as DRAM, memrias EDO, SIMM, etc. Tudo isto ou se refere ao mtodo de acesso dos dados na memria ou a tecnologia de construo ou a outra caracterstica acessria. O certo que todas elas tem como caracterstica bsica o fato dos acessos de leitura e escrita poderem ser feitos na mesma velocidade.

1.3.3

Bits e Bytes

A memria do computador composta de bits, a menor unidade de informao que o computador armazena. Um bit pode conter o valor 0 ou 1, que so os dgitos usados na base dois, a base usada nos computadores. Um conjunto de 8 bits forma o byte. Uma palavra de memria um conjunto de bytes. Atualmente a maioria dos computadores tem palavras de memria com largura de 32 (4 bytes) ou 64 (8 bytes) bits. Na Figura 1.9 mostramos os diversos tamanhos dos dados.
BIT

BYTE 8 BITS

PALAVRA 32 BITS 4 BYTES

Figura 1.9: Tamanho de Bits, Bytes e Palavras Observar que estamos falando de dados na memria e no do tamanho dos dados que o computador pode processar. Considere que este tamanho relacionado com a quantidade mxima de algarismos que um nmero pode ter para ser processado. Um computador pode ter capacidade de processar 64 bits de cada vez. Caso sua memria tenha palavras de 32 bits o processador dever, ento, ler duas palavras da memria para poder processar um nmero. Lembre-se que as duas leituras so atendidas uma de cada vez. Da mesma forma o computador pode processar 32 bits de cada vez e a memria ter largura 64 bits. Isto pode acelerar o processamento, j que o processador est se adiantando e recebendo o que poder ser o prximo dado a ser processado, ou seja economizando uma leitura. Devido a base 2 o fator kilo tem um signicado diferente em computao. Por exemplo 1 Kbyte de memria corresponde a 2 elevado a 10 (210 ), ou seja 25

1024 bytes. Da mesma forma 1 Megabyte corresponde a 1024 x 1024 bytes e 1 Gigabyte igual a 1024 x 1024 x 1024 bytes. Na Tabela 1.4 esto mostradas as diversas abreviaes usadas quando se fazem referncias s memrias. Nome Kilobyte Megabyte Gigabyte Terabyte Petabyte Exabyte Smbolo Kb MB GB TB PB EB Multiplicador 210 = 1024 220 230 240 250 260

Tabela 1.4: Abreviaes usadas em referncias s memrias.

1.3.4

Perifricos

Como j mencionamos antes, os dados no cam guardados somente na memria, h tambm os perifricos . H perifricos de entrada, outros de sada e alguns que servem tanto para entrada como sada de dados. Perifricos no servem somente para armazenar dados. H perifricos que so usados para permitir a interao entre os usurios e o computador. A tabela 1.5 ilustra alguns destes perifricos. Entrada Teclados Mouse CD-ROM Scanner Sada Impressoras Vdeo Plotter Alto-falantes Ambos Discos Rgidos Fitas Magnticas Disquetes Discos Zip

Tabela 1.5: Exemplos de perifricos

1.4

O Software

Tudo isto que sobre o que acabamos de escrever constitui o hardware do computador, o que se v e o que se toca. A partir de agora falaremos brevemente no software, o que no se v nem se toca, mas tambm est l. Para que um computador execute alguma tarefa primeiro se desenvolve um algoritmo , que uma espcie de receita que diz precisamente, ao computador, como o problema deve ser resolvido. Esta denio informal de algoritmo enganosamente simples, e a chave para entender o engano est nas palavras dizer precisamente ao computador. Por exemplo, uma receita em gastronomia normalmente no um algoritmo. Receitas so entendidas pela comunidade de cozinheiros, que as seguem facilmente durante o preparo do prato. No entanto, receitas esto cheias de expresses como, por exemplo, mexer at car no ponto 26

e colocar sal a gosto. Fora da comunidade de cozinheiros estas expresses so passveis de vrias interpretaes. Para escrever algoritmos precisamos de uma linguagem matematicamente precisa e sem ambigidades. A escrita de um algoritmo consta de uma denio do estado inicial do problema a ser resolvido e de regras precisas que estabelecem a cada instante os passos a serem seguidos. Como em um jogo, alm de denir os passos, so necessrias regras que denam se aps a execuo de um passo do algoritmo o novo estado do problema vlido. As regras do xadrez denem o estado inicial do tabuleiro, os movimentos possveis de cada pea e se aps um movimento de uma pea a congurao atingida vlida. Ou seja precisamos vericar em cada instante qual dos movimentos (instrues) pode ser usado. Algoritmos podem ser chamados de procedimentos efetivos e devem obedecer aos seguintes limites: sempre dar alguma resposta; sempre dar a resposta correta e nunca uma resposta incorreta; terminar em um nmero nito de passos; trabalhar em todos os exemplos da classe de problemas que o algoritmo se prope a resolver. Em seguida este algoritmo deve ser traduzido para uma linguagem que possa ser entendida pelo computador ou que possa ser traduzida para esta linguagem. No incio da computao eletrnica com programas armazenados, estes eram escritos diretamente em linguagem de mquina que a linguagem que o computador realmente entende. Estas instrues so conjuntos de bits indicando a operao que deve ser executada e, caso necessrio, onde como achar os dados que sero operados. Por esta razo tambm costuma-se dizer que so programas escritos em binrio. Com a evoluo da computao os programas passaram a ser escritos em assembly , que uma representao em mnemnicos das instrues de mquina. Deste modo era mais fcil escrever os algoritmos. Por exemplo, um fragmento de um programa escrito em assembly do processador PowerPC : li r3,4 * O primeiro numero a ser somado e 4. li r4,8 * 8 e o segundo numero add r5,r4,r3 * Some os contedos de r3 (4) e r4 (8) * e armazene o resultado em r5 Este pequeno trecho de programa armazena os nmeros 4 e 5 em registradores internos do processador em seguida os soma e armazena o resultado em um terceiro registrador. As informaes aps os asteriscos so comentrios usados para explicar o que o programa est fazendo naquela instruo. O PowerPC um microprocessador criado em 1991 por um consrcio formado pela IBM, Apple e Motorola Os microprocessadores PowerPC podem ser usados para equipar desde sistemas embutidos at computadores de alto desempenho. A Apple usou este microprocessador para equipar suas mquinas at 2006.

27

Um programa escrito em assembly deve ser traduzido para a representao binria, tarefa que normalmente se chama de montar o programa. A palavra assembler frequentemente usada erradamente para signicar a linguagem e no o programa que traduz o programa de assembly para linguagem binria de mquina. Este tipo de programao pode levar a se escrever programas muito ecientes, devido ao controle quase que total do programador sobre a mquina. No entanto devido ao fato de ser uma linguagem prxima do computador e afastada da maneira de raciocinar do ser humano mais difcil de ser usada. Alm deste fato h outros problemas tais como: diculdade de leitura por humanos, diculdade de manuteno dos programas, maior tempo de desenvolvimento etc. Para evitar estes problemas foram desenvolvidas as linguagens de programao chamadas de linguagens de alto nvel, por estarem mais prximas da linguagem natural empregada pelos serem humanos. Alguns exemplos de linguagens de programao so: Fortran: Usada em programao cientca e engenharia; Pascal: Usada em ensino de linguagens e desenvolvimento de sistemas; COBOL: Usada em ambientes comerciais; Basic: O nome diz tudo, bsica; C: Mesmas caractersticas do Pascal com facilidades que permitem mais controle do computador; C++: Linguagem originria do C com metodologia de orientao objetos; Java: Linguagem tambm baseada na sintaxe do C e tambm seguindo o modelo de orientao objetos. Delphi: Linguagem originria do Pascal com metodologia de orientao objetos; Lisp e Prolog: Linguagens usadas para desenvolver programas de Inteligncia Articial. Aplicativos importantes para os programadores so os compiladores. Estes programas traduzem programas escritos em linguagens de alto nvel para a linguagem de mquina, de modo que o computador possa execut-los. De maneira geral um compilador um programa que traduz um programa de uma linguagem para outra. Podemos resumir os passos necessrios para criar um programa em uma linguagem de programao, por exemplo C, nos passos descritos a seguir. A Figura 1.10 ilustra a ordem em que se desenvolvem estes passos. Criao do Algoritmo: neste passo criado o algoritmo que ir resolver o problema. As diversas maneiras de descrever um algoritmo sero apresentadas no prximo captulo.

28

Codicao do Algoritmo: O algoritmo preparado no passo anterior escrito em uma linguagem de programao. Neste passo o programador conta, normalmente, com a ajuda de um editor de textos (no processador de textos). Para esta edio qualquer editor pode ser usado. Hoje em dia muitos ambientes de desenvolvimento integram todas as ferramentas necessrias para criar um programa, inclusive o editor, em um nico aplicativo. Compilao do Programa: O arquivo texto contendo o programa passa por um programa especial chamado compilador que gera, caso no hajam erros, uma sada que quase o programa executvel, ou seja o programa em cdigo binrio do processador em que ser executado. Os erros mais comuns nesta etapa so erros de uso correto da linguagem de programao. Estes erros so chamados de erros de compilao. As linguagens de programao so baseadas em regras gramaticais muito rgidas e qualquer violao destas regras pode implicar em erro. No caso de erros serem encontrados o programador deve voltar ao passo de codicao para a correo dos erros. Ligao: Em ingls este passo conhecido por link edition. Um programa completo composto por vrios mdulos que podem ter sido criados pelo prprio programador ou por outras programadores. Por exemplo, em C os trechos de programa que interagem com os usurios, os comandos de entrada e sada de dados, normalmente vm com o programa compilador. Estes trechos podem estar guardados em bibliotecas de programas e so ligados ao programa do usurio para completar o programa. Depurao e Testes: Nesta etapa o programa ser testado para a retirada dos possveis erros de lgica que o programador cometeu. Caso algum erro de execuo seja encontrado o programador deve reelaborar o que estiver errado no algoritmo e em seguida ir para a etapa de codicao do algoritmo. Este ciclo pode repetir-se inmeras vezes at que o desenvolvedor acredite que os erros foram corrigidos. Uso do Programa: O programa foi entregue aos seus usurios para ser usado. Durante o uso, erros que no foram encontrados durante o desenvolvimento do programa podem ser descobertos e precisam ser corrigidos. A correo pode ser feita pelos mesmos programadores que desenvolveram o programa ou por outro grupo devidamente treinado. Costuma-se chamar esta correo de manuteno do programa. Algumas linguagens de programao no so compiladas e sim interpretadas. Isto signica que o programa para ser executado no precisa ser traduzido diretamente para linguagem de mquina, gerando um arquivo executvel. Este arquivo nal, se torna independente do programa fonte. Para executar o programa podemos usar somente o arquivo executvel. Em um programa interpretado um aplicativo l o programa instruo por instruo, diretamente na prpria linguagem de alto nvel, traduz cada uma destas instrues para linguagem de mquina e as executa. No h, portanto, o processo de traduo antecipada do programa. A interpretao de um programa funciona como o processo de traduo simultnea do discurso de um orador. A medida que ele pronuncia seu 29

Incio Ligao

Criao de Algoritmo

Depurao e Testes

Codificao do Algoritmo

Sim

Erros de Execuo? No

Compilacao do Programa

Uso do programa

Sim Erros de Compilao? No

Sim Erros de Execuo?

Figura 1.10: Ciclo de desenvolvimento de um programa.

discurso um tradutor repete as frases na linguagem destino. Um programa compilado funciona como se primeiro, o tradutor traduzisse todo o discurso e depois o lesse. A linguagem Basic uma linguagem interpretada. Em Java ocorre um processo um pouco diferente. Um programa em Java traduzido para uma linguagem intermediria e depois interpretado por meio de uma chamada mquina virtual. No h efetivamente uma compilao para linguagem de mquina. A execuo de um programa escrito em uma linguagem interpretada mais lenta, j que o processo de interpretao e execuo ao mesmo tempo mais lento do que a simples execuo de um programa traduzido antecipadamente. Hoje em dia a maior parte dos usurios de computadores no so programadores e sim pessoas que usam programas para resolver seus problemas do dia a dia. Aplicativos tpicos que rodam nos computadores so: editores de texto, processadores de texto, planilhas eletrnicas, compiladores, bancos de dados, jogos, etc. Para gerenciar os recursos do computador existe um programa especial normalmente chamado de Sistema Operacional (S. O.). Por exemplo, considere o problema de gerenciar como os diversos programas que um usurio normalmente utiliza partilharo o processador do computador. Um usurio pode estar ouvindo msica, digitando um texto e imprimindo um outro documento ao mesmo tempo. Portanto, os computadores so capazes de executar um nmero de tarefas muito maior do que o nmero de processadores disponveis. Atualmente a maior parte dos computadores possui somente um processador. O Sistema Operacional controla a alocao de recursos tais como: comunicao com os usurios, espao em discos, uso de memria, tempo que cada programa pode rodar etc. Alguns dos sistemas operacionais conhecidos so os baseados no padro UNIX, por exemplo o LINUX. Outros sistemas muito usados so os da famlia 30

Windows. Compilando Programas Simples em C Para resolver os exerccios deste livro voc ir precisar de um compilador para a linguagem C e de um editor de textos simples (no processador como o Word). O editor pode ser to simples quanto o Notepad, na verdade recomendamos fortemente que o editor seja simples para que voc possa ter contato com todas as etapas do processo de desenvolvimento de um programa. Para compilar empregaremos o compilador gcc que gratuito e pode ser obtido na Internet como veremos adiante. No ser necessrio nenhum ambiente mais complexo, tal como um Integrated Development Environment (IDE). A coleo de compiladores da GNU (GNU Compiler Collection) usualmente abreviada por gcc, uma coleo de compiladores produzidos pelo projeto GNU. A abreviao gcc, originalmente, signicava GNU C Compiler. Este aplicativo distribudo gratuitamente pela Free Software Foundation (FSF) sob a licena GNU GPL e GNU LGPL. Este o compilador padro para os sistemas operacionais livres do tipo Unix, como o LINUX, e diversos sistemas operacionais proprietrios como o Apple Mac OS X. Atualmente o gcc pode compilar C++, Objective-C, Java, Fortran e ADA, entre outras linguagens. Vamos considerar, como exemplo, um programa chamado teste.c. Para compilar e gerar o executvel para este programa digitamos o comando gcc -o teste teste.c -Wall em uma janela de comandos no sistema Windows ou em um terminal nos sistemas Unix. O suxo .c no nome do programa normalmente usado para indicar que o arquivo de um programa C. Este comando deve ser digitado no diretrio onde est o arquivo fonte teste.c. O arquivo executvel ser armazenado no mesmo diretrio. Nos sistemas Unix normalmente o gcc faz parte da distribuio padro e nada precisa ser feito. No Windows uma maneira fcil de obter uma verso do gcc instalar o MinGW (Minimalist GNU for Windows). MinGW uma coleo de arquivos e bibliotecas distribudas livremente as quais combinadas com outras ferramentas da GNU permitem que programas para Windows sejam produzidos sem a necessidade de bibliotecas extras e pagas. O MinGW dispe de um programa instalador que facilita enormemente o processo. Este programa pode ser obtido no stio ocial do MinGW. Caso aps a instalao, o comando indicado no funcione uma das razes para a falha pode ser que o sistema operacional no sabe onde se encontra o compilador gcc. Suponha que o programa gcc foi instalado no diretrio C:\MinGW\bin. Uma soluo digitar o caminho completo do compilador. Neste caso o comando se torna C:\MinGW\bin\gcc -o teste teste.c -Wall Para que no seja necessrio digitar o caminho completo, preciso adicionar este caminho varivel PATH do Windows. Consulte o manual para obter informaes de como fazer este passo no seu sistema Windows.

31

1.5

Um programa em C

Vamos terminar este captulo mostrando um exemplo simples de programa escrito em C(Listagem 1.1). A nica coisa que este programa faz imprimir Alo Mundo! e terminar [Kernighan e Ritchie 1978, Schildt 1997, ?]. A primeira linha do programa avisa ao compilador que ir usar funes de entrada e sada de dados guardadas na biblioteca stdio. Neste caso a funo usada printf. A segunda linha o incio real do programa. A linha indica que esta a funo main que todo programa C deve conter, pois nesta funo que o programa obrigatoriamente comea sua execuo. A funo vai retornar um valor inteiro (int) ao nal de sua execuo e no vai precisar receber nenhum argumento para sua execuo (void). As chaves ({ e }) marcam o incio e o m da funo. Para imprimir o texto Alo Mundo! o programa usa a funo printf. O incio e o m do texto a ser impresso so marcados pelo caractere ". A funo termina com o comando return 0, que avisa ao sistema operacional, que foi quem iniciou a execuo do programa, que o programa terminou sem problemas. Este programa simples ilustra alguns das estruturas bsicas que sero usadas nos programas C que sero apresentados neste livro.

Listagem 1.1: Exemplo de Programa em C.


#include < stdio .h > i n t main ( void ) { printf ( " Alo Mundo !\ n " ) ; return 0; }

32

1.6

Exerccios

1.1: O que o hardware do computador? 1.2: Quais os principais componentes de um computador? 1.3: Quais as diferenas entre um microprocessador e o microcomputador? 1.4: D exemplos de microprocessadores e de microcomputadores. 1.5: Qual o nmero exato de bytes em 64 Kbytes? 1.6: Se voc j usa computadores, liste alguns aplicativos que voc normalmente usa. 1.7: Dena Sistema Operacional. 1.8: Qual a diferena bsica entre memrias ROM e RAM? 1.9: Procure em manuais, internet e outras fontes quais so os tempos de acesso das memrias RAMs atuais. 1.10: Faa trs listas, uma de perifricos de entrada, outra de perifricos de sada e nalmente uma de perifricos de entrada e sada. 1.11: Explique o que faz um compilador. 1.12: Discuta as vantagens e desvantagens das linguagens interpretadas e as compiladas. 1.13: O que so erros de compilao e de execuo. 1.14: Procure nomes de linguagens de programao no listadas no texto e diga quais so as suas caractersticas principais.

33

Captulo 2

Algoritmos
2.1 Introduo

O objetivo deste captulo fazer uma breve introduo ao conceito de algoritmos e apresentar algumas formas mais comuns de representar algoritmos para facilitar o entendimento dos demais captulos deste livro. Iremos apresentar as construes mais comuns empregadas no desenvolvimento de algoritmos e apresentaremos exemplos bsicos de algoritmos usando algumas destas formas de representao e construes. Para resolver um problema no computador necessrio que seja primeiramente encontrada uma maneira de descrever este problema de uma forma clara e precisa. preciso que encontremos uma seqncia de passos que permitam que o problema possa ser resolvido de maneira automtica e repetitiva. Alm disto preciso denir como os dados que sero processados sero armazenados no computador. Portanto, a soluo de um problema por computador baseada em dois pontos: a seqncia de passos e a forma como os dados sero armazenados no computador. Esta seqncia de passos chamada de algoritmo. Usamos algoritmos em diversas atividades que realizamos diariamente. Uma grande parte destas atividades no esto relacionadas com computao. Um exemplo simples e prosaico, de como um problema pode ser resolvido caso forneamos uma seqncia de passos que mostrem a maneira de obter a soluo, uma receita para preparar um bolo. Uma vez que foi criado um algoritmo para resolver um determinado problema usando computadores passamos para a prxima fase que a escrita deste algoritmo em alguma linguagem de programao. A noo de algoritmo central para toda a computao. A criao de algoritmos para resolver os problemas uma das maiores diculdades dos iniciantes em programao em computadores. Isto porque no existe um conjunto de regras, ou seja um algoritmo, que nos permita criar algoritmos. Caso isto fosse possvel a funo de criador de algoritmos desapareceria. Claro que existem linhas mestras e estruturas bsicas, a partir das quais podemos criar algoritmos, mas a soluo completa depende em grande parte do criador do algoritmo.

34

Geralmente existem diversos algoritmos para resolver o mesmo problema, cada um segundo o ponto de vista do seu criador. No seu livro Fundamental Algorithms vol. 1 Donald Knuth [Knuth 1973] apresenta uma verso para a origem desta palavra. Ela seria derivada do nome de um famoso matemtico persa chamado Abu Jafar Maom ibn Ms alKhowrism (825) que traduzido literalmente quer dizer Pai de Jafar, Maom, lho de Moiss, de Khowrizm. Khowrizm hoje a cidade de Khiva, na ex Unio Sovitica. Este autor escreveu um livro chamado Kitab al jabr walmuqabala (Regras de Restaurao e Reduo). O ttulo do livro deu origem tambm a palavra lgebra. O signicado da palavra muito similar ao de uma receita, procedimento, tcnica, rotina. Um algoritmo um conjunto nito de regras que fornece uma seqncia de operaes para resolver um problema especco. Segundo o dicionrio do prof. Aurlio Buarque de Holanda um algoritmo um: Processo de clculo, ou de resoluo de um grupo de problemas semelhantes, em que se estipulam, com generalidade e sem restries, regras formais para a obteno de resultado ou de soluo de problema. Um algoritmo opera sobre um conjunto de entradas (no caso do bolo, farinha ovos, fermento, etc.) de modo a gerar uma sada que seja til (ou agradvel) para o usurio (o bolo pronto). Um algoritmo computacional tem cinco caractersticas importantes: Finitude: Um algoritmo deve sempre terminar aps um nmero nito de passos. Denio: Cada passo de um algoritmo deve ser precisamente denido. As aes devem ser denidas rigorosamente e sem ambiguidades. Entradas: Um algoritmo deve ter zero ou mais entradas. Entradas so as quantidades que so lhe so fornecidas para processamento. Sadas: Um algoritmo deve ter uma ou mais sadas, isto quantidades que tem uma relao especca com as entradas. Efetividade: Um algoritmo deve ser efetivo. Isto signica que todas as operaes devem ser sucientemente bsicas de modo que possam ser, em princpio, executadas com preciso em um tempo nito por um humano usando papel e lpis.

2.2

Primeiros Passos

claro que todos ns sabemos construir algoritmos. Se isto no fosse verdade, no conseguiramos sair de casa pela manh, ir ao trabalho, decidir qual o melhor caminho para chegar a um lugar, voltar para casa, etc. Para que tudo isto seja feito necessrio uma srie de entradas do tipo: a que hora acordar, que hora 35

sair de casa, qual o melhor meio de transporte etc. Um fator importante que pode haver mais de um algoritmo para resolver um determinado problema. Por exemplo, para ir de casa at o trabalho, posso escolher diversos meios de transporte em funo do preo, conforto, rapidez, etc. A escolha ser feita em funo do critrio que melhor se adequar as nossas necessidades. Um exemplo de algoritmo pode ser as instrues que um professor passa aos seus alunos em uma academia de ginstica, mostrado no Algoritmo 2.1. Observar que nesta representao do algoritmo cada linha contm uma instruo. Algoritmo 2.1: Exemplo de Algoritmo. incio enquanto no fez 10 vezes faa Levantar e abaixar brao direito Levantar e abaixar brao esquerdo Levantar e abaixar perna esquerda Levantar e abaixar perna direita m enqto m Computadores so mquinas muito ecientes na resoluo de problemas matemticos ou que envolvam nmeros. Como exemplo de um algoritmo matemtico, vamos considerar o problema de resolver uma equao do primeiro grau da forma ax + b = 0 A soluo desta equao x = b/a se o valor de a for diferente de 0. Caso a seja igual a 0, a equao no possui soluo, j que no possvel dividir por 0. Este algoritmo escrito (Algoritmo 2.2) em uma pseudo-linguagem de programao caria da seguinte maneira: Algoritmo 2.2: Algoritmo para resolver uma equao do primeiro grau. Entrada: Coecientes a e b da equao ax + b = 0 Sada: Resultado x da Equao incio ler a ler b se a = 0 ento imprimir A equao nao tem soluo seno x b/a imprimir A raiz da equao vale , x m se m As instrues do algoritmo so executadas passo a passo e uma instruo somente executada quando a anterior terminou sua tarefa. Os dois primeiros 36

passos do algoritmo servem para o algoritmo obter os valores dos coecientes a e b. Os valores podem, por exemplo, serem digitados em um teclado pelo usurio que est usando o algoritmo. O valor digitado vai para uma posio da memria do computador, que para facilitar o manuseio do dado, recebe um nome. Neste exemplo demos os nomes a, b e x as posies de memria usadas pelo programa para armazenar dados. Aps os dois primeiros passos o algoritmo executa uma instruo de teste para vericar se o valor de a diferente de 0. Neste caso podemos ter duas respostas e o computador ir escolher entre dois caminhos independentes e exclusivos. Caso a seja igual a zero, o algoritmo executa as instrues entre a palavra ento e seno, e portanto, imprime uma mensagem de aviso para o usurio e termina. Esta mensagem normalmente aparece em um monitor de vdeo. No caso de a ser diferente de zero, o algoritmo executa as instrues entre seno e fim se. Isto signica calcular o resultado da equao e atribuir este resultado x. O ltimo passo, desta opo a impresso do resultado da equao.

2.3

Representao

As formas mais comuns de representao de algoritmos so as seguintes: Linguagem Natural: Os algoritmos so expressos diretamente em linguagem natural, como nos exemplos anteriores. Fluxograma Convencional: Esta um representao grca que emprega formas geomtricas padronizadas para indicar as diversas aes e decises que devem ser executadas para resolver o problema. Pseudo-linguagem: Emprega uma linguagem intermediria entre a linguagem natural e uma linguagem de programao para descrever os algoritmos. No existe consenso entre os especialistas sobre qual seria a melhor maneira de representar um algoritmo. Atualmente a maneira mais comum de representar-se algoritmos atravs de uma pseudo-linguagem ou pseudo-cdigo. Esta forma de representao tem a vantagem de fazer com que o algoritmo seja escrito de uma forma que est mais prxima de uma linguagem de programao de computadores.

2.3.1

Linguagem Natural

A representao em linguagem natural tem a desvantagem de colocar uma grande distncia entre a soluo encontrada e o resultado nal do processo que um programa em linguagem de programao. O Algoritmo 2.3 mostra um algoritmo, escrito em linguagem natural, para calcular a mdia de um aluno que faz trs provas e precisa de obter mdia acima de 5.0 para ser aprovado.

2.3.2

Fluxogramas

Esta forma de representao de algoritmos emprega vrias formas geomtricas para descrever cada uma das possveis aes durante a execuo do algoritmos. 37

Algoritmo 2.3: Algoritmo para calcular a mdia das notas de um aluno. Entrada: Notas n1 , n2 e n3 . Sada: Resultado media do aluno e se ele foi aprovado ou no. incio Obter as notas n1 , n2 e n3 Calcular mdia. Usar a frmula ((n1 + n2 + n3 )/3.0). Se a mdia for maior que 5.0 imprimir que o aluno foi aprovado Caso contrrio imprimir que o aluno foi reprovado. Imprimir a mdia. m

Existem algumas formas geomtricas que usualmente so empregadas neste mtodo. Estas formas esto mostradas na Figura 2.1. Cada uma destas formas se aplica a uma determinada ao como est indicado na gura. Estas formas so apenas alguns exemplos, existindo outras, no entanto, nesta apostila estas sero sucientes para os exemplos que sero mostrados.
Incio e Fim de Fluxograma Processamento

Ponto de Deciso

Entrada de Dados Manual

Impresso de Resultados

Conector para mesma pgina

Figura 2.1: Smbolos mais comumente usados em uxogramas. Como exemplo de um algoritmo escrito em forma de uxograma, vamos considerar o algoritmo 2.2 para resolver uma equao do primeiro grau da forma ax + b = 0. A Figura 2.2 mostra um uxograma para resolver este problema. Os dois primeiros passos do algoritmo lem os valores dos coecientes a e b da equao. Em seguida h um teste para descobrir se o valor de a igual a zero. Se o valor de a for igual a zero o algoritmo manda que seja impressa uma mensagem informando que a equao no tem soluo. Caso o valor de a seja diferente os prximos passos calculam o valor da soluo e em seguida imprimem este resultado

2.3.3

Pseudo-Linguagem

Este modo de representar algoritmos procura empregar uma linguagem que esteja o mais prximo possvel de uma linguagem de programao de computadores de alto nvel, mas evitando de denir regras de construo gramatical muito rgidas. A idia usar as vantagens do emprego da linguagem natural, mas restringindo o escopo da linguagem. Normalmente estas linguagens so verses ultra reduzidas de linguagens de alto nvel do tipo Pascal ou C. O algoritmo 2.2 foi escrito em uma pseudo-linguagem. A maioria destas linguagens so muito 38

Incio

Obter a

Obter b

Sim a =0 No x=-b/a No h razes reais

Imprime x

Fim

Figura 2.2: Fluxograma para resolver uma equao do primeiro grau.

parecidas e tm a vantagem de serem facilmente entendidas. Vamos apresentar agora um outro exemplo (Algoritmo 2.4) escrito em pseudo-linguagem. Este algoritmo serve para descobrir qual a maior nota de um grupo de trs notas de um aluno. O algoritmo inicialmente l a primeira nota e guarda esta nota como a maior nota. Em seguida, l cada uma das outras notas e compara com a nota guardada como a maior nota. Caso a nota lida seja maior substitui o valor anterior pelo novo valor. Apesar destas pseudo-linguagens terem poucas regras existem algumas que normalmente so usadas para facilitar o entendimento entre os programadores. Vamos detalhar algumas delas. As palavras incio e m indicam onde comea e termina o algoritmo. As palavras em negrito indicam instrues que devem ser executadas pelo computador onde o algoritmo ser rodado. Por exemplo, ler notaAluno uma instruo do algoritmo que ordena ao computador para obter a nota de um aluno para ser processada. Em algoritmos esta nota normalmente ser obtida de um perifrico de entrada de dados, sendo mais comum o teclado. As palavras em itlico so normalmente chamadas de variveis e representam locais na memria do computador onde os valores a serem usados durante o processamento esto armazenados. Os programadores podem incluir nos algoritmos explicaes que facilitem o entendimento do seu funcionamento. Estes comentrios no so executados pelos computadores e somente so lidos pelos programadores. Existem diversas maneiras de indicar que o texto no algoritmo apenas um comentrio. Neste exemplo usamos dois caracteres para indicar que o restante da linha apenas um comentrio. Mais adiante outras explicaes sero apresentadas. 39

Algoritmo 2.4: Algoritmo para calcular a maior nota de um grupo de notas. Entrada: Trs notas de um aluno, (notaAluno). Sada: Maior das notas do aluno, (maiorN ota) incio L primeira nota ler notaAluno maiorN ota notaAluno L segunda nota ler notaAluno se notaAluno > maiorN ota ento maiorN ota notaAluno m se L terceira nota ler notaAluno se notaAluno > maiorN ota ento maiorN ota notaAluno m se imprimir A maior nota das notas , maiorN ota m

2.4

Modelo de von Neumann

Algoritmos para computadores se baseiam em alguns conceitos bsicos e em um modelo de computador, os quais devem ser bem entendidos para que se possa criar algoritmos ecientes. Este modelo foi proposto pelo matemtico hngaro Neumann Jnos Lajos Margittai. Em hngaro o nome de famlia aparece antes. Assim em portugus o seu nome seria Jnos Lajos Margittai Neumann. O seu pai, que era rico, comprou um ttulo de nobreza e ele passou a se chamar Jnos Lajos Margittai von Neumann. No modelo de computador proposto por von Neumann as instrues e os dados cam juntos na memria. O processador busca na memria e executa uma instruo de cada vez. Para ilustrar como este modelo funciona vamos analisar passo a passo a execuo de um algoritmo simples. Na Figura 2.3 mostramos nos endereos 0, 1 e 2, de uma memria, um grupo de instrues formando parte de um algoritmo. As instrues tambm podem ser acompanhadas no Algoritmo 2.5. Vamos assumir que o computador inicie executando o algoritmo a partir da instruo que est no endereo 0. O procedimento normal a Unidade de Controle (UC) do computador continuar buscando e executando uma instruo de cada vez nos endereos seguintes, a no ser que haja uma ordem para desviar o uxo das instrues. importante observar que o computador executa uma instruo de cada vez, e como veremos adiante, tambm um dado buscado de cada vez. Portanto, as transferncias entre a memria e o processador so feitas passo a passo. O ciclo normal da execuo de um programa ento: 1. Busca instruo; 2. Decodica instruo; 40

3. Executa instruo; 4. Volta para o passo 1 buscando a instruo seguinte na memria. Portanto, aps a instruo do endereo 0 ser lida da memria e trazida para a UCP, ela decodicada pela UC, que descobre que a instruo manda carregar o valor inteiro 2 na posio de memria 10, que chamada de a para facilitar. A instruo seguinte ordena a carga do valor inteiro 8 na posio de memria chamada de b, que a posio 11. A ltima instruo ordena a soma dos valores armazenados em a e b e que o resultado seja armazenado na posio 12, que chamada c. Algoritmo 2.5: Modelo de memria e funcionamento de um algoritmo Armazena 2 na memria no lugar chamado a a2 Armazena 8 na memria no lugar chamado b b8 Soma a com b e armazena no lugar chamado c ca+b Observe que no modelo de von Neumann instrues e dados cam na memria. Considere a ltima instruo do programa que pede para que os dados da posio 10 (a) e 11 (b) sejam somados e o resultado armazenado na posio 12 (c). Para executar esta instruo, o computador faz as seguintes operaes na memria: 1. ler a instruo no endereo 2; 2. ler o dado a na posio 10; 3. ler o dado b na posio 11; 4. escrever o resultado da soma no endereo 12. Portanto, temos 3 leituras e uma escrita. Destas operaes, a primeira uma leitura de instruo e as restantes operaes com dados.

2.5

Estruturas Bsicas de Algoritmos

Com a nalidade de ilustrar como criar algoritmos para computadores usando este modelo, vamos discutir alguns tipos bsicos de estruturas usados nesta tarefa. Para isto, iremos usar a representao que for apropriada no momento. No iremos neste livro discutir em detalhes estes tpicos, nem apresentar de uma maneira formal qualquer uma destas formas. O interesse apenas apresentar e discutir algumas estruturas bsicas para ilustrar o pensamento usado pelos programadores quando criam um algoritmo para resolver um problema especco. Estas estruturas so importantes e sero reapresentadas quando formos apresentar a linguagem C. Para o programador iniciante esta discusso serve como introduo. 41

a<-2
8

b<-8
9

c<-a+b
10 11 12 13 14 15

2
16 17 18

8
19

10
20 21 22 23

24

25

26

27

28

29

30

31

Endereo Endereo Endereo Endereo Endereo Endereo

0: Instruo a <--2 1: Instruo b <--8 2: Instruo c <--a+b 10: Dado a 11: Dado b 12: Dado c

Figura 2.3: Modelo de memria

2.5.1

Comandos de leitura

Estes comandos servem para obter dados do mundo exterior, normalmente digitados por um usurio em um teclado. Outros exemplos de lugares de onde podem ser obtidos dados so os arquivos em discos rgidos, disquetes e tas magnticas. Estes dados so lidos e guardados na memria para posterior uso. Normalmente os lugares para onde vo estes dados recebem um nome para facilitar o seu manuseio durante a execuo do algoritmo. Por exemplo, o comando ler a signica que o algoritmo ir obter um dado do teclado e ir armazen-lo em uma posio de memria que passar a ser conhecida pelo nome a. Estas posies so conhecidas por variveis em computao. Costuma-se dizer ento que a uma varivel do algoritmo. Apesar de a no algoritmo 2.2 representar uma das constantes da equao do primeiro grau no algoritmo ela uma das variveis. Observe que cada vez que o algoritmo executado a pode assumir um valor diferente, portanto varia de acordo com a execuo do algoritmo. O comando pode ser seguido por uma lista de nomes separados por vrgulas. Por exemplo o comando ler a, b l dois valores do teclado e os atribui as variveis a e b. Observe que a ordem em que os valores foram digitados determina como os valores sero atribudos. O primeiro valor lido atribudo a primeira varivel, no caso a. O segundo valor lido atribudo a segunda varivel (b). Os valores normalmente so digitados separados por um ou mais espaos em branco ou em linhas diferentes.

2.5.2

Comandos de escrita

Aps a obteno dos resultados do algoritmo, estes devem ser apresentados ao usurio, e para isto usamos os comandos de escrita. Por exemplo o comando 42

imprimir x imprime o valor atual que est na memria representada pelo nome x. Da mesma forma que nos comandos de leitura possvel colocar uma lista de nomes de variveis aps o comando. Por exemplo, o comando imprimir x1, x2 imprime os valores das variveis x1 e x2. O meio de apresentao dos resultados, normalmente, um monitor de vdeo. O comando imprimir pode ser usado para mandar mensagens de texto para o usurio do algoritmo das formas mais variadas. Alguns exemplos de comandos de impresso so os seguintes:

imprimir Entre com o valor do coeciente imprimir O valor de x , x imprimir O valor de x1 , x1, e o de x2 , x2 Notar que os textos entre aspas indicam um texto que deve ser impresso no perifrico de sada sem nenhuma modicao. Vamos considerar que as variveis destes exemplos valem x = 10, x1 = 5 e x2 = 8. Os trs comandos mostrariam no perifrico de sada os seguintes resultados: Entre com o valor do coeficiente. O valor de x 10 O valor de x1 5 e o de x2 8

2.5.3

Expresses

Expresses so usadas para denir os clculos requeridos pelo algoritmo, por exemplo b/a. Iremos discutir dois tipos bsicos de expresses: expresses aritmticas e expresses lgicas. Expresses manipulam dados dentro dos algoritmos. Uma pergunta importante neste momento : que tipo de dados poderemos manipular? As linguagens de programao normalmente estabelecem regras precisas para denir que tipos de dados elas iro manipular. Nesta discusso vamos estabelecer, ainda que informalmente, algumas regras que limitam os conjuntos de dados existentes na Matemtica e estabelecem que dados podero ser manipulados pelos algoritmos. Isto ocorre porque os computadores possuem limitaes que os impedem de manipular todos os tipos de dados que um ser humano pode tratar. Mais adiante, quando formos estudar a linguagem C iremos apresentar mais formalmente as regras desta linguagem para estas representaes. Existem trs tipos bsicos de dados que iremos discutir: Dados numricos: como o nome indica so os nmeros que sero operados. Dados alfa-numricos: so os dados representados por caracteres. Como caracteres podem ser letras, algarismos e sinais diversos estes dados recebem este nome. 43

Dados Lgicos: estes dados podem assumir dois valores verdadeiro e falso. Estes dados resultam de expresses do tipo x > 0. Os dados numricos que os algoritmos que iremos construir podem manipular so de dois tipos: inteiros e reais. So exemplos de nmeros inteiros: +3 3 -324 -50 So exemplos de nmeros reais: +0.5 0.5 -8.175 2.0 Dados alfa-numricos servem para tratamento de textos e normalmente so compostos por uma seqncia de caracteres contendo letras, algarismos e caracteres de pontuao. Nos algoritmos so normalmente representados por uma seqncia de caracteres entre aspas, por exemplo: Linguagem de programao Qual o seu nome? 12345 Dados lgicos so intensamente aplicados durante o processo de tomada de decises que o computador frequentemente obrigado a fazer. Em muitos textos este tipo de dados tambm chamado de tipo de dados booleanos, devido a George Boole, matemtico que deu ao nome lgebra (lgebra booleana) que manipula este tipo de dados. Os dados deste tipo somente podem assumir dois valores: verdadeiro e falso. Computadores tomam decises, durante o processamento de um algoritmo, baseados nestes dois valores. Por exemplo, considere a deciso abaixo:

se a = 0 ento imprimir A equao nao tem soluo seno x b/a imprimir A raiz da equao vale , x m se Neste algoritmo aparece a expresso a = 0, que procura descobrir se o valor de raiz igual a 0. Esta expresso somente pode ter como resultado os valores: verdadeiro ou falso. Nos nossos algoritmos estes valores sero representados por verdadeiro e falso.

44

Expresses Aritmticas Para os nossos exemplos iniciais iremos adotar os operadores aritmticos binrios mostrados na Tabela 2.1. A coluna prioridade indica a prioridade relativa dos operandos. O menor nmero indica a maior prioridade. Quando temos dois operandos de mesma prioridade o computador ir executar primeiro a operao mais esquerda. Em computao, as expresses devem ser escritas em linhas e para alterar a prioridade deve-se usar parnteses. Maiores detalhes sobre expresses sero apresentados no Captulo 5. No momento, estamos apenas apresentando alguns conceitos bsicos para facilitar a discusso de alguns algoritmos. Operador / * % + Descrio Diviso Multiplicao Mdulo (resto da diviso de operandos inteiros) Soma Subtrao Prioridade 0 0 0 1 1

Tabela 2.1: Operadores Aritmticos. Para ilustrar o uso de operadores aritmticos vamos considerar alguns exemplos de expresses. Os exemplos a seguir mostram como converter uma expresso matemtica para a forma que usaremos em pseudo-linguagem. Observer o uso de parnteses para alterar a prioridade das operaes. Expresso Matemtica
a b+c a+b c +d 2

b 4ac
1 1 1+ a+ b

Expresso em Pseudo-linguagem a/(b+c) (a+b)/(c+d) b*b-4*a*c 1/(1 + 1/(a+b))

2.5.4

Comandos de atribuio

Servem para atribuir valores posies de memria. Normalmente estes valores podem ser constantes ou resultados de expresses dos diversos tipos. Exemplos de comandos de atribuio so mostrados a seguir. x b/a media (n1 + n2)/2 inicio 0 nome Ze Sa ii+1 A seta aponta sempre da direita para a esquerda. O valor ou o resultado da expresso a direita da seta armazenado na varivel que est no lado esquerdo 45

da seta. A direo da seta no pode ser alterada. No caso da ltima expresso temos que o valor atual de i incrementado e depois substitui este valor, portanto, se ele valia 10, ao nal da instruo ele vale 11.

2.5.5

Comandos de controle

So usados para controlar o uxo de execuo das instrues. Nas linguagens de programao existem diversos tipos de comandos de controle. Para estes exemplos iniciais vamos mostrar somente o comando mais bsico que serve para o computador escolher entre dois possveis caminhos qual o algoritmo deve seguir. Este comando, representado em uxograma, pode ser visto na Figura 2.4 e em pseudo linguagem tem a forma mostrada no algoritmo 2.6.

Algoritmando

Falso Condio

Verdadeiro

Faa isto

Faa aquilo

Continuo algoritmando

Figura 2.4: Fluxograma do comando se ...

ento ...

seno.

Algoritmo 2.6: Comando se em pseudo-linguagem se Condio sendo testada ento Faa isto seno Faa aquilo m se Um exemplo de uso desta construo, escolhido a partir da vida real, pode ser o seguinte. Tenho que decidir o que fazer em um domingo de folga. Se estiver chovendo irei ao cinema, caso contrrio irei praia. Observe que para ir para a praia basta apenas que no esteja chovendo, nenhum outro teste foi 46

feito. Esta deciso em forma de pseudo-linguagem ca da maneira mostrada no Algoritmo 2.7. Algoritmo 2.7: Algoritmo para decidir o que fazer no domingo. se est chovendo ento vou ao cinema seno vou praia m se Nem sempre nos algoritmos precisamos de duas alternativas. As vezes precisamos somente de decidir se devemos fazer algo ou no Por exemplo, vamos assumir que decidimos ir ao cinema no domingo chova ou faa sol. No entanto, preciso decidir se levo um guarda-chuva. O algoritmo para este caso est mostrado no Algoritmo 2.8. Observe que no caso de no estar chovendo nada precisa ser feito. A Figura 2.5 mostra esta deciso em forma de uxograma. Algoritmo 2.8: Algoritmo para decidir se deve levar um guarda-chuva. Vestir para ir ao cinena se est chovendo ento pego guarda-chuva m se Vou ao cinema

2.5.6

Comandos de repetio

As linguagens de programao normalmente possuem diversos comandos que permitem que um trecho de algoritmo seja repetido um nmero de vezes. Para estes exemplos iniciais iremos apresentar um comando de repetio que sucientemente geral para substituir todos os outros. Este comando, que chamaremos de comando enquanto tem a forma mostrada na Figura 2.6. O comando funciona da seguinte maneira: Passo 1: Testar se a condio verdadeira. Caso seja verdade executar o bloco de comandos situados entre o incio do comando e o nal do comando. O nal do comando enquanto normalmente marcado de alguma forma. Em nossa pseudo-linguagem marcaremos o m pelas palavras fim enquanto ou fim eqto. Passo 2: Executar o bloco de comandos at o m do enquanto. Quando chegar ao m retornar automaticamente para o incio do comando e refazer o passo 1. Como exemplo consideremos o caso em que precisamos ler um conjunto de 10 nmeros e imprimir se cada um dos nmeros lidos par ou no. Para descobrir se o nmero par vamos dividi-lo por 2 e testar o resto. Para simplicar, 47

Vestir para ir ao cinema

Falso Chovendo?

Verdadeiro

Pegar guarda-chuva

Ir ao cinema

Figura 2.5: Fluxograma para decidir se deve levar um guarda-chuva.

vamos considerar que caso o resto da diviso seja igual a zero o nmero par. Neste algoritmo sabemos a quantidade de nmeros a serem lidos e portanto o nmero de repeties pr-determinado. O Algoritmo 2.9 mostra como seria implementada uma soluo para este problema. Vamos mostrar um exemplo onde o nmero de repeties no conhecido. Considere no exemplo anterior que o total de nmeros a ser lido no conhecido. Mas ento, como o algoritmo ir terminar de ler nmeros? Usaremos o que costuma-se chamar de sentinela em computao. O algoritmo ir se manter lendo nmeros enquanto os nmeros forem positivos. No momento que for lido um nmero negativo o algoritmo pra. A sentinela que indica o nal da lista de nmeros um nmero negativo. O Algoritmo 2.10 mostra como ca em pseudo-linguagem o algoritmo modicado. Observe que neste algoritmo o primeiro nmero tem de ser lido antes do comando enquanto. Isto porque assumimos que o primeiro nmero que for digitado pode ser negativo e portanto a lista de nmeros no tem nenhum nmero.

2.6

Exemplos de Algoritmos

Nesta seo iremos apresentar uma srie de algoritmos escritos na pseudolinguagem que acabamos de apresentar. Exemplo 2.1: Este algoritmo serve para descobrir qual a maior nota de uma turma de alunos. Neste algoritmo iremos considerar que as notas podem variar entre 0.0 e 10.0 e que a turma tem 25 alunos. O algoritmo 2.11 inicialmente inicia a maiorN ota com zero, depois compara cada nota com esta maiorN ota 48

Algoritmo 2.9: Algoritmo para ler 10 nmeros e imprimir se so pares ou no. Entrada: 10 nmeros, (numero). Sada: Se o nmero par ou no incio totalN umeros 10 enquanto totalN umeros > 0 faa ler numero se numero%2 = 0 ento imprimir numero, par seno imprimir numero, impar m se totalN umeros totalN umeros 1 m enqto m

Algoritmo 2.10: Algoritmo para ler nmeros e imprimir se so pares ou no. A quantidade de nmeros a ser lida desconhecida. Entrada: nmeros, (numero). O algoritmo para quando um nmero negativo lido Sada: Se o nmero par ou no incio ler numero enquanto numero > 0 faa se numero % 2 = 0 ento imprimir numero, par seno imprimir numero, impar m se ler numero m enqto m

49

Algoritmando

Falso Testa Condio

Verdadeiro

Bloco de comandos do enquanto

Continuo algoritmando

Figura 2.6: Fluxograma do comando enquanto.

caso ela seja maior substitui o valor anterior pelo novo valor. Observar que criamos uma varivel para armazenar a quantidade de alunos. Voc poderia perguntar porque no usar o nmero 25 toda vez que for necessrio. A razo simples. Suponha que voc gostaria de usar este algoritmo para calcular a maior nota de uma turma de 40 alunos. Neste algoritmo bastaria substituir o nmero 25 por 40. No entanto, existe uma soluo mais geral ainda que permite que o algoritmo possa ser usado para qualquer tamanho de turma. Este problema est apresentado na lista de exerccios deste captulo. Exemplo 2.2: Vamos mostrar outro exemplo de algoritmo muito usado. Precisamos ler as notas de uma turma de alunos e calcular a mdia destas notas. Vamos assumir que a turma tem 25 alunos e as notas esto entre 0 e 10. O algoritmo est mostrado em Algoritmo 2.12. Exemplo 2.3: Neste exemplo considere o seguinte problema. Um escritrio de previso do tempo armazena diariamente a temperatura mdia de uma determinada regio. A tarefa descobrir qual a maior temperatura do ano passado. Assuma que foram armazenadas 365 temperaturas, uma para cada dia do ano. Neste caso no podemos aplicar o algoritmo 2.11 usado para descobrir a maior nota da turma. Antes de continuar procure encontrar a razo. Como dica, considere que estamos no plo sul e portanto todas as temperaturas lidas so negativas. Uma soluo possvel para este exemplo est mostrada no algoritmo 2.13. Este algoritmo faz o seguinte. Pega a primeira temperatura e a anota como a maior j encontrada. A partir da o algoritmo ca repetidamente lendo tem50

Algoritmo 2.11: Algoritmo para calcular a maior nota de uma turma de 25 alunos. Entrada: Nota de cada um dos dos 25 alunos da turma, (notaAluno). Sada: Maior das notas dos alunos, (maiorN ota) incio totalAlunos 25 maiorN ota 0.0 enquanto totalAlunos > 0 faa ler notaAluno se notaAluno > maiorN ota ento maiorN ota notaAluno m se totalAlunos totalAlunos 1 m enqto imprimir A maior nota das notas , maiorN ota m

Algoritmo 2.12: Algoritmo para calcular a nota mdia de uma turma de 25 alunos. Entrada: Nota de cada um dos dos 25 alunos da turma, (notaAluno). Sada: Mdia das notas dos alunos, (mediaN otas) incio totalAlunos 25 i0 somaN otas 0.0 enquanto i < totalAlunos faa ler notaAluno somaN otas somaN otas + notaAluno ii+1 m enqto mediaN otas somaN otas/totalAlunos imprimir A mdia das notas , mediaN otas m

51

peraturas dos registros do escritrio comparando com a temperatura que no momento consta como a maior de todas. Se a temperatura tirada dos arquivos for maior que a menor atual, o algoritmo joga fora a temperatura anotada e guarda a que foi lida como a nova maior temperatura. Quando no houver mais temperaturas para ler a que estiver anotada como a maior a maior verdadeiramente. Algoritmo 2.13: Algoritmo para calcular a maior temperatura do ano. Entrada: Temperaturas registradas em ano, (temperatura). Sada: Maior das temperaturas, (maiorT emperatura) incio totalT emperaturas 365 ler temperatura J li uma temperatura totalT emperaturas totalT emperaturas 1 A primeira temperatura a maior temperatura maiorT emperatura temperatura enquanto totalT emperaturas > 0 faa ler temperatura se temperatura > maiorT emperatura ento maiorT emperatura temperatura m se totalT emperaturas totalT emperaturas 1 m enqto imprimir A maior nota das temperaturas , maiorT emperatura m

52

2.7

Exerccios

2.1: Uma empresa paga R$10.00 por hora normal trabalhada e R$ 15.00 por hora extra. Escreva um algoritmo que leia o total de horas normais e o total de horas extras trabalhadas por um empregado em um ano e calcule o salrio anual deste trabalhador. 2.2: Assuma que o trabalhador do exerccio anterior deve pagar 10% de imposto se o seu salrio anual for menor ou igual a R$ 12000.00. Caso o salrio seja maior que este valor o imposto devido igual a 10% sobre R$ 12000.00 mais 25% sobre o que passar de R$ 12000.00. Escreva um programa que calcule o imposto devido pelo trabalhador. 2.3: Escreva um algoritmo que descubra a maior nota de uma turma de alunos. O tamanho da turma deve ser o primeiro dado pedido ao usurio. 2.4: Modique o algoritmo anterior de modo que ele imprima tambm quantas vezes a maior nota aparece. 2.5: Nos exerccios anteriores assumimos que os usurios sempre digitam uma nota entre 0 e 10. Vamos assumir agora que o usurio sempre digita um nmero, mas este nmero pode estar fora do intervalo 0 a 10. Ou seja, poderemos ter uma nota menor que zero ou maior que 10. Modique o algoritmo anterior para que ele verique a nota digitada e, caso o aluno tenha digitado uma nota invlida, uma mensagem avisando o usurio seja impressa e uma nova nota seja pedida. O algoritmo deve insistir at que o usurio digite um valor vlido. 2.6: Escreva um programa que leia um conjunto de 100 temperaturas e imprima a menor temperatura lida. Observe que temperaturas podem assumir valores menores do que zero. 2.7: Escreva um algoritmo que leia trs nmeros e os imprima em ordem crescente. 2.8: Escreva um algoritmo que leia um nmero inteiro entre 100 e 999 e imprima na sada cada um dos algarismos que compem o nmero. Observe que o nmero lido com um valor inteiro, e, portanto, ele tem de ser decomposto em trs nmeros: os algarismos das centenas, dezenas e unidades. 2.9: Escreva um algoritmo que leia uma hora em horas, minutos e segundos e some um segundo a hora lida. 2.10: Escreva um algoritmo que leia duas datas em dia, ms e ano e imprima a data mais recente. 2.11: Um aluno est escrevendo um programa que l uma nota no intervalo entre 0 e 100, inclusive. Foi pedido ao aluno que o programa aceite as notas vlidas e rejeite as invlidas. Marque a letra que mostra a expresso que falta no trecho pontilhado do algoritmo mostrado em 2.14. (a) (nota < 0) e (nota > 100) (b) (nota <= 0) e (nota >= 100) 53

(c) (nota < 0) ou (nota > 100) (d) (nota <= 0) ou (nota >= 100) (e) (nota >= 0) e (nota <= 100) Algoritmo 2.14: Algoritmo do exerccio 11. incio imprimir Entre com a nota ler nota se .............. ento imprimir Nota invlida seno imprimir Nota vlida m se m 2.11: Considere que os valores -3, -4 e -5, nesta ordem, foram fornecidos ao algoritmo 2.15: Algoritmo 2.15: Algoritmo do exerccio 11. Dados: t1, t2, t3, maior incio ler (t1, t2, t3) maior 0 se t1 > maior ento maior t1 m se se t2 > maior ento maior t2 m se se t3 > maior ento maior t3 m se imprimir (maior) m Marque a letra que indica o que foi impresso em cada vez que o programa foi executado. (a) 0 (b) -3 (c) -4 (d) -5 (e) nenhuma das respostas anteriores.

54

Captulo 3

Tipos de Dados, Constantes e Variveis


3.1 Introduo

Variveis e constantes so os elementos bsicos que um programa manipula. Uma varivel corresponde a um espao reservado na memria do computador para armazenar um determinado tipo de dado. Variveis devem receber nomes para poderem ser mais facilmente referenciadas e modicadas sempre que necessrio. Muitas linguagens de programao exigem que os programas declarem todas as variveis antes que elas possam ser usadas. Estas declaraes especicam de que tipo so as variveis usadas pelos programas e as vezes um valor inicial. Tipos podem ser por exemplo: inteiros, reais, caracteres, etc. As expresses combinam variveis e constantes para calcular novos valores.

3.2
3.2.1

Tipos de Dados
Tipos Bsicos

Os dados em C podem assumir cinco tipos bsicos que so os seguintes: char: O valor armazenado um caractere. Caracteres geralmente so armazenados em cdigos (usualmente o cdigo ASCII). A Tabela A.1 mostra este cdigo. Caracteres so armazenados em um byte. int: O valor armazenado um nmero inteiro e o tamanho do subconjunto que pode ser representado pelo computador normalmente depende da mquina em que o programa est rodando. Atualmente em C os nmeros inteiros so armazenados em 32 bits. oat: Nmero em ponto utuante de preciso simples, normalmente 32 bits. So conhecidos como nmeros reais, no entanto, os computadores somente podem armazenar e trabalhar com uma pequena parte do conjunto dos nmeros reais. 55

double: Nmero em ponto utuante de preciso dupla, com isto a preciso e as vezes a excurso dos nmeros aumenta. Este tipo armazenado em 64 bits. void: Este tipo serve para indicar que um resultado no tem um tipo denido. Uma das aplicaes deste tipo em C criar um tipo vazio que pode posteriormente ser modicado para um dos tipos anteriores.

3.2.2

Modicadores de tipos

Modicadores podem ser aplicados a estes tipos. Estes modicadores so palavras que alteram o tamanho do conjunto de valores que o tipo pode representar. Por exemplo, um modicador permite que possam ser usados mais bits para armazenar nmeros inteiros. Um outro modicador obriga que s nmeros inteiros sem sinal possam ser armazenados pela varivel. Deste modo no necessrio guardar o bit de sinal do nmero e somente nmeros positivos so armazenados. O resultado prtico que o conjunto praticamente dobra de tamanho. Os modicadores de tipos so os seguintes: unsigned: Este modicador pode ser aplicado aos tipos int e char e faz com que o bit de sinal no seja usado, ou seja o tipo passa a ter um bit a mais. signed: Este modicador tambm pode ser aplicado aos tipos int e char. O uso de signed com int redundante. long: Modicador que pode ser aplicado aos tipos int e double aumentando o nmero de bytes reservado para armazenamento de dados. possvel combinar estes modicadores de diversas maneiras como est mostrado na Tabela 3.1 que lista os tipos bsicos denidos no padro ANSI e a sua excurso.

3.3

Constantes Numricas

Constantes so valores que o programa no pode modicar durante a execuo de um programa. Elas so usadas em expresses para representar vrios tipos de valores. Em C existem regras rgidas para determinar como devem ser escritos estes valores. A seguir iremos mostrar estas regras. Para escrever constantes numricas vamos usar as seguintes denies: dgito: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 dgito_sem_zero: 1, 2, 3, 4, 5, 6, 7, 8, 9 dgito_octal: 0, 1, 2, 3, 4, 5, 6, 7 dgito_hexa: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, A, b, B, c, C, d, D, e, E, f, F sinal: +, 56

Tipo char unsigned char signed char int unsigned int signed int short int, short unsigned short int signed short int long int, long signed long int unsigned long int long long int long long signed long long int signed long long unsigned long long int unsigned long long oat double long double

Bytes 1 1 1 4 4 4 2 2 2 4 4 4 8 8 8 4 8 12

Faixa Mnima -127 a 127 0 a 255 -127 a 127 -2.147.483.648 a 2.147.483.647 0 a 4.294.967.295 -2.147.483.648 a 2.147.483.647 -32.768 a 32.767 0 a 65.535 -32.768 a 32.767 -2.147.483.648 a 2.147.483.647 -2.147.483.648 a 2.147.483.647 0 a 4.294.967.295 -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 0 a 18.446.744.073.709.551.615 oito dgitos de preciso 16 dgitos de preciso 16 dgitos de preciso

Tabela 3.1: Tipos de dados denidos pelo Padro ANSI C.

ponto_decimal: . suxo: sufixo_sem_sinal, sufixo_longo suxo_sem_sinal: u, U suxo_longo: l, L suxo_utuante: f, F, l, L O uso destas caracteres ser mostrado com exemplos nas sees seguintes.

3.3.1

Constantes Inteiras na base 10

So valores numricos sem ponto decimal, precedidos ou no por um sinal. No possvel separar, por espaos em branco, o sinal do valor numrico. Podemos descrever este formato com uma notao simplicada da seguinte maneira: [sinal]dgito_sem_zero{dgito}[sufixo_sem_sinal|sufixo_longo] Esta notao deve ser entendida da seguinte maneira. Colchetes indicam opo, portanto, o fato de sinal (+ ou -) estar entre colchetes signica que um nmero inteiro pode ou no ter sinal, isto o sinal opcional. Em seguida temos 57

um dgito_sem_zero que obrigatrio. Isto dados inteiros devem comear por, pelo menos, um algarismo entre 1 e 9. A seguir temos a palavra dgito entre chaves. As chaves indicam que o fator entre elas pode ser repetido zero ou mais vezes. Portanto, um nmero inteiro, aps o algarismo inicial obrigatrio, pode ser seguido por uma seqncia de zero ou mais algarismos. Como suxo podemos ter opcionalmente as letras u (U) ou l (L) ou uma mistura, em qualquer ordem, das duas. Para constantes inteiras o suxo U (u) representa o modicador unsigned. O suxo L (l) representa o modicador long. Um par de Ls (ls) indica que a constante do tipo long long. A tabela 3.2 mostra exemplos de nmeros inteiros: Tipo int unsigned int long int unsigned long int long long unsigned long long 1997 1997U 1234L 1997UL 134LL 1997ULL Constantes -3 +5 45u 12345U 1997L -3l 45Lu 23Ul 1997Ll -3ll 45LLu 23Ull 7 0U +0L 0LU +0LL 0LLU

Tabela 3.2: Constantes Inteiras na Base 10 Alguns exemplos de erros na escrita de constantes inteiras so: 1.0 (No possvel usar ponto decimal.) - 345 (No possvel colocar um espao entre o sinal e o valor numrico.) 23 (No possvel usar notao de expoentes.) Nos compiladores modernos o nmero de bytes usados para armazenar os valores inteiros o mesmo tanto para tipos inteiros (int) quanto para tipos inteiros longos (long int), como est mostrado na Tabela 3.1. Por esta razo, a diferena entre estes dois tipos de constantes perdeu a razo de ser. Alguns exemplos de constantes inteira longas esto mostrados na Tabela 3.2.

3.3.2

Constantes Inteiras Octais

So constantes representadas na base 8. Normalmente so representadas sem sinal e devem comear com um 0. Usando a notao apresentada na seo anterior podemos denir a forma que deve ter uma constante octal como: 0 {dgito_octal}[sufixo_sem_sinal|sufixo_longo] Na Tabela 3.3 mostramos exemplos de constantes octais e o seu valor na base 10. Nmeros escritos na base 8 somente podem ser escritos com algarismos entre 0 e 7 inclusive.

58

Base 8 025 077 011 010ul 0175

Base 10 21 63 9 8 125

Tabela 3.3: Constantes octais

3.3.3

Constantes Inteiras Hexadecimais

So constantes representadas na base 16. So iniciadas com um 0x ou 0X . Usando a notao podemos denir uma contante hexadecimal como: [0x|0X]dgito_hexa{dgito_hexa}[sufixo_sem_sinal|sufixo_longo] Na Tabela 3.4 mostramos exemplos de constantes hexadecimais e o seu valor na base 10. Para escrever constantes na base 16 usamos todos os algarismos e ainda as letras A (ou a), B (ou b), C (ou c), D (ou d), E (ou e), F (ou f), que representam respectivamente os seguintes valores 10, 11, 12, 13, 14 e 15. Base 16 0xF 0X25 0XAB 0XBEEF Base 10 15 37 171 48879

Tabela 3.4: Constantes hexadecimais

3.3.4

Converso entre Bases

A converso de nmeros inteiros entre a base 8 e a base 10 tem uma frmula simples, que pode ser estendida para converter nmeros entre qualquer base e a base 10. Vamos considerar que um nmero (N )8 escrito na base 8 tenha a seguinte forma (N )8 = dn1 dn2 . . . d1 d0 onde 7 di 0 A frmula para converter um nmero da base 8 para a base 10 a seguinte N10 = dn1 8n1 + dn2 8n2 + + d1 81 + d0 80 (3.1)

Esta equao est escrita na base 10. Por exemplo, aplicando a equao 3.1 para converter o nmero 0175 da base 8 para a base 10 camos com (0175)8 = 1 82 + 7 81 + 5 80 = (125)10

59

A frmula para converso da base 8 para a base 10 pode se estendida para uma base qualquer com a substituio do algarismo 8. Considere uma base qualquer representada por b. Nesta base os dgitos di cam no intervalo b 1 di 0. A equao 3.2 mostra a frmula para converter um nmero em uma base b qualquer para a base 10. N10 = dn1 bn1 + dn2 bn2 + + d1 b1 + d0 b0 (3.2)

Vamos considerar a contante inteira (3AF )16 . Aplicando a frmula 3.2 temos (3AF )16 = 3 162 + 10 161 + 15 160 = (943)10 O algoritmo para converter um nmero inteiro da base 10 para uma determinada base b feito por um conjunto de divises sucessivas do nmero pela base at que o resultado da diviso seja 0. O Algoritmo 3.1 mostra como converter um nmero (N )10 para uma base b. importante notar que os algarismos na base b vo sendo impressos na ordem inversa, do menos signicativo para o mais signicativo. Por exemplo, caso forneamos para o algoritmo o nmero (943)10 e a base 16, o algoritmo iria imprimir os resultados 15, 10 e 3 nesta ordem. Isto corresponderia aos seguintes dgitos da base 16: F, A e 3 e, portanto, a resposta seria (3AF )16 . Algoritmo 3.1: Algoritmo para converter inteiros na base 10 para uma base b. Entrada: nmero, (numero) e base b (baseb). Sada: Dgitos do nmero na base b incio ler numero ler base enquanto numero > 0 faa resto numero % base numero numero / base imprimir resto m enqto m

3.3.5

Constantes em Ponto Flutuante

Constantes em ponto utuante so usadas para representar nmeros reais. O nome ponto utuante devido ao modo como os valores so armazenados pelo computador. Constantes de ponto utuante podem ser do tipo oat , double , long ou long double. Constantes sem nenhum suxo so consideradas do tipo double. Caso seja usado o suxo F ou o f a constante ser considerada como do tipo oat . O suxo L ou o l torna a constante long double. Uma constante em ponto utuante pode ser denida de duas maneiras. Voc pode escrever um nmero com ponto decimal (1.5) ou na chamada forma cientca, em que um expoente usado (0.15E1). Na segunda forma o nmero 60

igual a 0.15 101 . possvel omitir ou os dgitos antes do ponto (a parte inteira) ou aps (a parte fracionria), mas nunca os dois grupos. possvel escrever um nmero em ponto utuante sem ponto, desde que um expoente seja usado. Portanto, os nmeros .8, 1234., 1E1 so nmeros de ponto utuante. Para mostrar mais formalmente como se deve escrever as constantes de ponto utuante vamos usar a mesma notao usada at aqui, mas usando uma hierarquia de denies para facilitar o entendimento. Primeiro damos uma denio mais geral que vai sendo mais detalhada a medida que avanamos. Lembrar que termos entre chaves podem ser repetidos 0 ou mais vezes e termos entre colchetes so opcionais. Portanto, usando a forma hierrquica, uma constante de ponto utuante (CPF) pode ser denida das seguintes maneiras: CPF = [sinal]frao[expoente][sufixo_flutuante] CPF = [sinal]seq_dgitos expoente[sufixo_flutuante] A seguir denimos cada um dos componentes. Uma frao denida como: frao = [seq_digitos] ponto_decimal seq_dgitos ou frao = seq_dgitos ponto_decimal O expoente e a seqencia de dgitos so denidos como: expoente = [e | E][sinal]seq_dgitos seq_dgitos = dgito{dgito} A Tabela 3.5 mostra exemplos de constantes em ponto utuante. Descrio sinal frao expoente frao frao expoente frao sufixo seq_dgitos ponto_decimal Nmero +23.45e-10 123.45 123.45E+10 123.45F 123.

Tabela 3.5: Constantes em ponto utuante

3.4

Constantes Caracteres

Uma constante caractere um nico caractere escrito entre , como em a. Alm disso, uma constante de tamanho igual a um byte pode ser usada para denir um caractere, escrevendo-se, por exemplo, \ddd, onde ddd uma constante com entre um e trs dgitos octais. Em C, caracteres podem participar normalmente de expresses aritmticas. O valor que entra na expresso o do cdigo usado para representar o caractere. Exemplos de constantes do tipo caractere so mostrados na Tabela 3.6. Certos caracteres que no so visveis podem ser representados antepondo-se o caractere \ (barra invertida), como no exemplo nova linha da Tabela 3.6. Este caractere tambm conhecido como caractere de escape. Exemplos so mostrados na Tabela 3.7. 61

Caractere a A \0141 ( 9 \n

Signicado caractere a caractere A Constante octal correspondente ao caractere a caractere abre parnteses algarismo 9 Nova linha, posiciona o cursor no incio da nova linha. Tabela 3.6: Exemplos de constantes caractere

Caractere \n \t \b \f \r \a \0

Signicado Passa para uma nova linha. Tabulao horizontal, move o cursor para a prxima parada de tabulao. Retorna um caractere. Salta uma pgina. Carriage return, posiciona o cursor no incio da linha atual. Alerta, faz soar a campainha do sistema. Null, caractere que em C termina uma cadeia de caracteres.

Tabela 3.7: Exemplos de caracteres invisveis.

3.4.1

Constantes Cadeias de Caracteres

Neste livro vamos usar em alguns casos a palavra cadeia para signicar cadeia de caracteres (string em ingls). Uma constante do tipo cadeia de caracteres uma seqncia de qualquer nmero de caracteres entre " como no exemplo: "alo mundo!!!". importante notar que a linguagem C insere automaticamente ao nal de uma cadeia de caracteres um caractere null (\0). Este caractere ser usado em diversos algoritmos como sinal de m de cadeia. Os caracteres \ (caractere escape) e " (incio e m de cadeia) tm signicados especiais em cadeias de caracteres e para serem representados precisam ser antecedidos pelo caractere escape. Portanto, \\ e \" devem ser usados dentro de cadeias de caracteres para representar \ e " respectivamente. Por exemplo, "Estas so \" (aspas) dentro de cadeias." As aspas no meio da cadeia no indicam o m, j que elas esto precedidas do caractere de escape.

3.5

Variveis

Variveis so nomes dados para posies de memria a m de facilitar o manuseio dos dados durante a criao dos programas. Os dados podem ser de 62

qualquer dos tipos denidos para a linguagem C.

3.5.1

Nomes das Variveis

Existem algumas regras bsicas que regulam o batismo de variveis. Estas regras so as seguintes: Nomes de varivel s podem conter letras, dgitos e o caractere _; Todo primeiro caractere deve ser sempre uma letra ou o caractere _; Letras maisculas e minsculas so consideradas caracteres diferentes, isto , C diferencia a caixa das letras; Palavras reservadas no podem ser usadas como nome de variveis. Palavras reservadas so palavras usadas para indicar os comandos da linguagem, tipos de dados ou outras funes. O Anexo B mostra as palavras reservadas da linguagem C. boa poltica escolher nomes que indiquem a funo da varivel. Por exemplo: soma mediaNotas total salarioMensal nome taxa_imposto raio _inicio

Em C nomes como raio, Raio e RAIO referem-se a diferentes variveis. No entanto, para afastar confuses, evite diferenciar nomes de variveis por letras maisculas e minsculas. Normalmente, os programadores usam letras maisculas para representar constantes. Observe que em alguns nomes combinamos duas palavras para melhor indicar o dado armazenado na varivel. Note tambm que o caractere espao no pode ser usado em nomes de variveis. Os programadores ao longo do tempo desenvolveram algumas regras informais para fazer esta combinao. Por exemplo, usa-se o caractere _ para separar as palavras que compem o nome, como em taxa_imposto. Outra maneira usar letras maisculas para indicar quando comea uma palavra, como em mediaNotas. Alguns programadores usam a conveno de no comear nomes de variveis por letras maisculas. No existem regras formais para denir como nomes devem ser criados. O melhor analisar as regras que programadores mais experientes usam ou os padres que empresas adotam, para ento escolher o que mais lhe agrada e segui-lo. Uma vez adotado um padro ele deve ser seguido para evitar incoerncias.

3.5.2

Declarao de variveis

Para serem usadas, as variveis precisam ser declaradas de modo que o compilador possa reservar espao na memria para o valor a ser armazenado. A forma geral de uma declarao : tipo lista_de_variveis; onde uma lista_de_variveis uma lista de nomes de variveis separadas por vrgulas. Por exemplo: 63

int i; unsigned i n t a , b , c ; unsigned short i n t dia , mes , ano ; f l o a t raio , diametro ; double salario ;

3.5.3

Atribuio de valores

Aps ser declarada, uma varivel pode receber valores. O operador de atribuio = indica que o resultado da expresso direita do operador ser atribudo varivel. Nada se pode armar sobre o contedo de uma uma varivel que j foi declarada mas ainda no recebeu um valor. A seguir so mostrados exemplos de atribuies de valores s variveis durante as declaraes.
i n t i = 0 , j = 10; f l o a t raio = 2.54; char c = 'd '; double precisao = 0.00001 L ;

A seguir mostramos um trecho de programa com exemplos de atribuio de valores aps a denio das variveis.
int i, j; f l o a t raio ; char c ; i = 0; j = 10; raio = 2.54; c = 'd ';

64

3.6

Exerccios

3.1: Indique os nomes de variveis que so vlidos. Justique os nomes invlidos. (a) tempo (e) 2dias (b) nota_final (f) teste 1 (c) us$ (g) raio.do.circulo (d) char (h) DiaHoje 3.2: Marque a letra que contm pelo menos um nome de varivel invlido. (a) (b) (c) (d) (e) raio, _ nome, hoje, provaFinal 2dia, aluno, real, podeSer = 2dia Alo, ALO, alo, aLO errado, certo, ok, dia2 nome_, prova_final, raio, nao_sei_nao

3.3: Indique quais dos nmeros abaixo so constantes inteiras (longas ou no) vlidas. Justique suas respostas. (a) 100 (e) - 234 (b) 2 345 123 (f) 0L (c) 3.0 (g) 21 (d) -35 (h) 0xF1 3.4: Qual o valor na base 10 das constantes abaixo? (a) (b) (c) (d) 025 0123 0xD 0x1D

3.5: Considere um computador que armazene nmeros inteiros em 32 bits. (a) Caso um bit seja reservado para o sinal diga qual o menor nmero inteiro negativo que este computador pode armazenar? (b) Para os nmeros sem sinal, qual o maior nmero positivo? 3.6: Indique na tabela 3.8 os tipos que voc usaria para armazenar os dados indicados. 3.7: Marque a opo que indica quantos dos nmeros abaixo representam resultados da operao (175)8 + (AB)16 . (602)7 , (100101000)2, (128)16 , (450)8 (a) 0 (b) 1 (c) 2 (d) 3 (e) 4

65

Descrio total de alunos em uma sala a nota de aluno em Computao I Primeira letra de um nome pontos de um jogador de voleibol ao nal do ano; o raio de um crculo. Tabela 3.8: Tabela do exercicio 6

Tipo da varivel

66

Captulo 4

Entrada e Sada pelo Console


4.1 Introduo

Neste captulo vamos apresentar conceitos bsicos de entrada e sada de dados para que os exemplos e exerccios iniciais possam ser construdos. Um programa que no fornece resultados nem pede valores para operar no deve ter grande utilidade. A entrada de dados ser feita pelo teclado e a sada poder ser vista na tela do computador. Em C, quando um programa se inicia, normalmente trs uxos (arquivos) de dados so abertos para operaes de entrada e sada: um para entrada, um para sada e um para imprimir mensagens de erro ou diagnstico. Normalmente o uxo de entrada est conectado ao teclado, enquanto que o uxo de sada e o de mensagens de erro, para serem visualizados, esto conectados ao monitor. Estas conguraes podem ser alteradas de acordo com as necessidades dos usurios e estas operaes so chamadas de redirecionamento. O uxo de entrada chamado de entrada padro (standard input); o uxo de sada chamado de sada padro (standard output) e o uxo de erros chamado de sada padro de erros (standard error output). Estes termos so substitudos pelas suas formas abreviadas: stdin, stdout e stderr.

4.2

Biblioteca Padro

Na linguagem C no existem comandos de entrada e sada. As operaes de entrada e sada so executadas com auxlio de variveis, macros e funes especiais. Para termos acesso biblioteca que contm estas ferramentas o programa deve conter a declarao
#include <stdio.h>

no incio do programa. A diretiva #include instrui o compilador a ler o arquivo indicado entre < e >, e process-lo como se ele fosse parte do arquivo original e seu contedo estivesse no ponto onde a diretiva foi escrita. Se o nome do arquivo estiver entre os sinais de maior e menor, como no exemplo, ele ser procurado em um diretrio especco de localizao pr-denida, onde esto os arquivos de 67

incluso. Quando se usa aspas o arquivo procurado de maneira denida pela implementao, isso pode signicar procurar no diretrio de trabalho atual, ou em um diretrio indicado no comando usado para compilar o programa. Normalmente os programadores usam maior e menor para incluir os arquivos de cabealho padro e aspas para a incluso de arquivos do prprio projeto.

4.3

Sada - A Funo printf

A funo printf faz com que dados sejam escritos na sada padro, que normalmente a tela do computador. O prottipo da funo :
int printf(controle, arg1, arg2, ...);

onde os argumentos arg1, arg2, ... so impressos de acordo com o formato indicado pela cadeia de caracteres que compe controle. O formato ao mesmo tempo de uso simples e bastante exvel, permitindo que os resultados possam ser apresentados de diversas maneiras. A funo retorna o nmero de caracteres impressos, no incluindo o nulo em vetores de caracteres. No caso de um erro de sada um valor negativo retornado. Um exemplo simples pode tornar a explicao mais clara. O programa 4.1 imprime o valor da varivel ano.

Listagem 4.1: Exemplo de impresso de resultados


#include < stdio .h > i n t main ( void ) { i n t ano = 1997; /* Imprime o valor do ano */ printf ( " Estamos no ano % d " , ano ) ; return 0; }

Este programa ir imprimir na tela do computador: Estamos no ano 1997 Como controle uma cadeia ele aparece entre " ". Ele dene como sero impressos os valores representados pelos argumentos. No controle podem existir dois tipos de informaes: caracteres comuns e cdigos de formatao. Os caracteres comuns, como no exemplo o texto Estamos no ano, so escritos na tela sem nenhuma modicao. Os cdigos de formatao, aparecem precedidos por um caractere% e so aplicados aos argumentos na ordem em que aparecem. Deve haver um cdigo de formatao para cada argumento. O cdigo %d indica que o valor armazenado em ano deve ser impresso na notao inteiro decimal. importante notar que o campo de controle aparece somente uma vez na funo printf e sempre no incio.

68

4.3.1

Cdigos de Converso

Os cdigos de converso esto mostrados na tabela 4.3.1. Cdigo %c %d %i %E %e %f %G %g %o %s %u %x %X %p %% Comentrio Caracter simples Inteiro decimal com sinal Inteiro decimal com sinal Real em notao cientca com E Real em notao cientca com e Real em ponto utuante %E ou %f, o que for mais curto %g ou %f, o que for mais curto Inteiro em base octal Cadeia Caracteres Inteiro decimal sem sinal Inteiro em base hexadecimal (letras minsculas) Inteiro em base hexadecimal (letras maisculas) Endereo de memria Imprime o caractere %

Tabela 4.1: Cdigos de Converso para escrita de dados. Entre o caractere % e o cdigo de converso podem ser inseridos caracteres que alteram o formato. A seguir so mostrados a ordem de insero destes caracteres e o seu signicado: %[modificadores][largura][.preciso][comprimento]cdigo modificadores: Usados logo aps o caractere %. - Um sinal de menos serve para especicar que o argumento deve ser justicado esquerda no seu campo de impresso. Caso nenhum sinal seja usado o argumento ser ajustado direita. O programa 4.2 ilustra os dois tipos de justicao. + Fora que o resultado seja precedido por sinal de menos ou de mais, mesmo para nmeros positivos. O padro que somente negativos sejam precedidos por sinal de menos. espao Caso nenhum sinal v ser escrito, um espao inserido antes do valor. # Usado com o, x ou X precede o valor com 0, 0x ou 0X respectivamente para valores diferentes de zero. Usado com e, E e f, fora que a sada contenha um ponto decimal mesmo que no haja parte fracionria. Por padro, se no h parte fracionria o ponto decimal no escrito. Usado com g ou G o resultado o mesmo que com e ou E, mas os zeros nais no so retirados. 0 Completa o campo, pela esquerda, com zeros (0) ao invs de espaos, sempre que a opo para completar seja especicada (ver especicador de largura do campo). 69

largura: Caso seja usado um nmero inteiro, este especica o tamanho mnimo do campo onde o argumento ser impresso. Na listagem 4.2 o nmero especica que 8 espaos so reservados para imprimir o resultado. Os espaos livres sero completados com espaos em branco. Se o argumento precisar de mais espao que o especicado ele ser escrito normalmente e o tamanho mnimo ignorado. .preciso Este nmero tem diferentes signicados dependendo do cdigo usado. caracteres: No caso de impresso de cadeia de caracteres (s), este nmero especica o nmero mximo de caracteres de uma cadeia de caracteres a serem impressos. ponto utuante: No caso de formato (e, E, f) o nmero de dgitos a serem impressos a direita do ponto, ou seja o nmero de casas decimais. Para o formato g ou G o nmero mximo dgitos signicativos. inteiros: No formatos inteiros (d, i, o, u, x, X) a preciso especicou o nmero mximo de dgitos a serem impressos. Se o nmero de caracteres a serem impressos menor que este o resultado completado com brancos. O valor no truncado comprimento: Modica os formatos da seguinte maneira: l Aplicado aos formatos de tipo d, i, o, u, x e X indicando que o dado do tipo long int e no int. h Modica o dado, nos formatos d, i, o, u, x e X para tipo short int. L Nos formatos e, E, f, g e G o argumento modicado para long double. O programa 4.2 ir imprimir o seguinte resultado: Justificado para direita Ano = 1997 Justificado para esquerda Ano = 1997

Listagem 4.2: Exemplo de justicao de resultados.


#include < stdio .h > i n t main ( void ) { i n t ano = 1997; printf ( " Justificado para direita Ano = %8 d \ n " , ano ) ; printf ( " Justificado para esquerda Ano = % -8 d \ n " , ano ) ; return 0; }

O programa exemplo 4.3 imprimir o seguinte resultado: O resultado e = 0.333 . Alo Nos exemplos anteriores verique que \n no impresso. A barra inclinada chamada de seqencia de escape, indicando que o prximo caractere no para 70

Listagem 4.3: Exemplo de uso de especicador de preciso.


#include < stdio .h > i n t main () { f l o a t r = 1.0/3.0; char s [] = " Alo Mundo " ; printf ( " O resultado e = %9.3 f \ n " , r ) ; printf ( " %9.3 s \ n " , s ) ; return 0; }

ser impresso mas representa caracteres invisveis ou caracteres que no esto representados no teclado. Esta seqncia de escape indica que o programa deve passar a imprimir na prxima linha.

4.4

Entrada - A Funo scanf

A funo scanf pode ser utilizada para entrada de dados a partir do teclado e seu prottipo :
scanf(controle, arg1, arg2, ...);

Uma diferena fundamental que existe entre esta funo e a funo printf est nos argumentos que vm depois do controle. No caso de scanf os argumentos so os endereos das variveis que iro receber os valores lidos e no, como em printf, as prprias variveis. A indicao que estamos referenciando um endereo e no a varivel se faz pelo operador &. Por exemplo, o comando
scanf("%d %d", &a, &b);

espera que dois valores inteiros sejam digitados no teclado. O primeiro armazenado na varivel a e o segundo em b. Os valores sero armazenados diretamente nos endereos indicados por &a e &b respectivamente. Um outro exemplo incluindo variveis reais :
int i; fl oat x; scanf ( " % d % f " , &i , & x ) ;

Assumindo que a linha de entrada no teclado fosse 34 56.43 a execuo do exemplo iria terminar com o valor inteiro 34 sendo armazenado na varivel i e o valor real 56.43 em x. Usualmente o campo de controle s contm especicaes de converso, como os listados na Tabela 4.3.1, que so utilizadas para interpretar os dados que sero

71

lidos, no entanto, como em printf, outros caracteres podem aparecer. O campo de controle pode conter: Caracteres branco: A funo l e ignora todos os caracteres branco e/ou <enter> e/ou tab que aparecerem antes de qualquer caractere diferente destes. Caracteres comuns: (no %) que devem casar com o prximo caractere diferente de branco da entrada. Isto signica que qualquer caractere que no for igual a branco e/ou <enter> e/ou tab ou parte de um especicador de formato faz com que a funo leia o prximo caractere da entrada (stdin) e se for igual a este ele descartado. Caso os caracteres sejam diferentes a funo falha e retorna deixando os caracteres seguintes no lidos. Especicaes de converso: Um especicador de converso de formato seguindo um modelo similar ao da funo printf. O modelo o seguinte: %{*}{largura}{modificadores}tipo O caracteres entre chaves so opcionais. O asterisco indica que o dado ser lido de stdin mas ignorado. A largura especica o nmero mximo de caracteres a serem lidos. Os modicadores alteram o tamanho do especicadores de tipo que vm logo a seguir. Existem os seguintes modicadores: h: Os tipos d, i e n, que so int passam a ser short int e os tipos o, u e x, tambm int passam a ser unsigned short int. l: Os tipos d, i e n passam a ser long int e os tipos o, u e x passam a unsigned long int. Os tipos e, f e g passam de oat para double. L: Os tipos e, f e g passam de oat para long double. Por exemplo, para que os valores digitados sejam separados por vrgulas, o comando deveria ser escrito da seguinte maneira:
scanf("%d, %f", &i, &x);

Observar que deve haver uma correspondncia exata entre os caracteres no brancos do controle e os caracteres digitados. Neste caso a entrada deveria ser: 35, 46.3 O programa 4.4 mostra exemplos de uso da funo scanf. O resultado da execuo deste programa : Entre com um caractere qualquer. d Codigo ASCII do caractere d vale 100. Agora dois inteiros separados por espaco. 2 4 A soma destes numeros vale 6. A funo scanf retorna o nmero de itens lidos com sucesso. Este nmero pode ser usado para vericar se todos os valores pedidos foram lidos. No caso de ocorrer uma falha antes da leitura se iniciar a constante EOF retornada. 72

Listagem 4.4: Exemplo de uso de scanf.


#include < stdio .h > i n t main () { char c ; i n t num1 , num2 ; printf ( " Entre com um caractere qualquer .\ n " ) ; scanf ( " % c " , & c ) ; printf ( " Codigo ASCII do caractere % c vale % d .\ n " , c , c ); printf ( " Agora dois inteiros separados por espaco .\ n ") ; scanf ( " % d % d " , & num1 , & num2 ) ; printf ( " A soma destes numeros vale % d .\ n " , num1 + num2 ) ; return 0; }

4.5
4.5.1

Lendo e Imprimindo Caracteres


Funes getchar e putchar

Para ler e escrever caracteres do teclado as funes de entrada e sada mais simples so getchar e putchar, que esto na biblioteca stdio.h e cujos prottipos so os seguintes:
i n t getchar ( void ) ; i n t putchar ( i n t c ) ;

Apesar da funo getchar retornar um parmetro inteiro possvel atribuir este valor a uma varivel do tipo char porque o cdigo do caractere est armazenado no byte ordem mais baixa. O mesmo acontece com a funo putchar que recebe um inteiro, mas somente o byte de ordem mais baixa passado para a tela do computador. A funo putchar retorna o caractere que foi escrito e EOF em caso de erro. O programa da listagem 4.5 mostra exemplos de uso destas funes, e o seu resultado : Entre com um algarismo entre 0 e 9. 7 O caractere lido foi o 7 Observar que, normalmente, quando algum dado fornecido pelo teclado termina-se a digitao com a tecla <enter>. No entanto, o <enter> um caractere tambm, e isto pode causar problemas. Vamos analisar o que acontece quando antes do comando getchar, se l um dado do tipo inteiro, por exemplo. O comando scanf l o nmero inteiro mas no o <enter> digitado. Deste modo, quando logo em seguida o programa executar a funo getchar, o que ser lido o <enter> digitado ao nal do nmero. A listagem 4.6 um exemplo de programa onde isto pode ocorrer. Considere que o usurio digitou 35<enter> como resposta ao comando scanf. O comando getchar ir ler o <enter> e em 73

Listagem 4.5: Exemplo de uso de getchar e putchar.


#include < stdio .h > i n t main ( void ) { char c ; printf ( " Entre com um algarismo entre 0 e 9.\ n " ) ; c = getchar () ; printf ( " O caractere lido foi o " ) ; putchar ( c ) ; return 0; }

seguida o programa ir imprimir o nmero 35, lido no scanf, e apenas uma linha em branco correspondente ao caractere <enter>, lido pelo getchar, como est indicado a seguir. Mais adiante mostraremos como resolver este problema. Entre com um numero inteiro. 35 Agora um caractere. Numero lido 35 Caractere lido

Listagem 4.6: Exemplo de uso de getchar e putchar.


#include < stdio .h > i n t main ( void ) { char c ; int i; printf ( " Entre com um numero inteiro .\ n " ) ; scanf ( " % d " , & i ) ; printf ( " Agora um caractere .\ n " ) ; c = getchar () ; printf ( " Numero lido % d \ n " , i ) ; printf ( " Caractere lido % c \ n " , c ) ; return 0; }

4.5.2

Lendo e Imprimindo Cadeias de Caracteres

Uma cadeia de caracteres (string) em C um vetor de caracteres. Vetores, que sero vistos mais adiante no Captulo 7, so conjuntos de caracteres em que 74

cada um deles pode ser acessado independentemente dos outros por meio de um endereo. Nesta etapa iremos apresentar rapidamente alguns conceitos que nos permitiro criar alguns exemplos simples com cadeias de caracteres. Para usar cadeias preciso primeiro denir um espao para armazen-las. Para isto preciso declarar o nome, o tamanho e o tipo do vetor. Considere que precisamos armazenar uma cadeia de caracteres chamada nome com 40 caracteres. A denio desta cadeia caria da seguinte maneira:
char nome [41];

Quando denir o tamanho do vetor de caracteres, observar que toda cadeia em C termina com o caractere NULL (\0), que automaticamente inserido pelo compilador. Portanto o vetor nome deve ser denido com um espao a mais. Aps este passo, o vetor nome pode ser usado durante a execuo do programa.

4.5.3

Lendo e Imprimindo cadeias com scanf e printf

O programa 4.7 mostra como ler e imprimir um cadeia usando os comandos scanf e printf respectivamente.

Listagem 4.7: Exemplo de uso de printf e scanf na leitura de cadeias.


#define DIM 40 #include < stdio .h > i n t main ( void ) { char nome [ DIM ]; /* linha de caracteres lidos */ /* Entrada de dados do vetor */ printf ( " Por favor , qual o seu nome ?\ n " ) ; scanf ( " % s " , nome ) ; printf ( " Sou um computador . Posso ajuda - lo % s ?\ n " , nome ); return 0; }

Considere que este programa se chama util. Uma possvel interao entre este programa e um usurio poderia ser da seguinte maneira. $ util Por favor, qual o seu nome? Ze Sa Sou um computador. Posso ajuda-lo Ze? O smbolo $ o prompt tpico dos sistemas Unix. Aparentemente o computador se tornou ntimo do usurio Ze Sa e o tratou apenas pelo primeiro nome. A explicao para esta intimidade est no modo de leitura. Quando se usa scanf para ler uma cadeia deve-se empregar o cdigo de converso %s. Este comando no l o nome todo, mas encerra a leitura dos caracteres quando encontra um 75

caractere espao (ou branco), ou seja o separador de cadeias no comando scanf o caractere espao. Mas como ler para um vetor um nome inteiro, ou um cadeia que contenha brancos? Para isto deve-se usar a funo gets que ser nosso prximo assunto.

4.5.4

Lendo e Imprimindo cadeias com gets e puts

Diferentemente do comando scanf a funo gets l toda a cadeia at que a tecla <enter> seja digitada. No vetor so colocados todos os cdigos dos caracteres lidos excetuando-se o da tecla <enter>, que no armazenado sendo substitudo pelo cdigo NULL. Caso a funo scanf do exemplo anterior fosse substituda pela gets o programa imprimiria Posso ajuda-lo Ze Sa? O comando que substitui o scanf gets(nome). O prottipo da funo gets o seguinte:
#include < stdio .h > char * gets ( char * str ) ;

A funo gets retorna str caso nenhum erro ocorra. Caso o nal do arquivo seja encontrado antes de qualquer caractere ser lido, o vetor permanece inalterado e um ponteiro nulo retornado. Caso um erro ocorra durante a leitura, o contedo do array ca indeterminado e novamente um ponteiro nulo retornado. A funo puts tem o seguinte prottipo:
#include < stdio .h > i n t puts ( const char * str ) ;

Ela imprime a cadeia apontado por str. O programa 4.8 semelhante ao exemplo anterior com as funes printf substitudas por puts. Observe que a impresso sempre termina e passa para a prxima linha. A funo puts retorna um valor positivo caso nenhum erro ocorra. Em caso de erro retornado um valor negativo. Entre com o seu nome, por favor. Ze Sa Alo Ze Sa Eu sou um computador, em que posso ajuda-lo?

4.5.5

A Funo fgets

A funo gets pode abrir porta para invases de computadores pelo fato dela no controlar o nmero de caracteres lido de stdin. Apesar do usurio denir um tamanho mximo para o vetor que ir armazenar os caracteres a funo 76

Listagem 4.8: Exemplo de uso de puts e gets na leitura de cadeias.


#define DIM 41 #include < stdio .h > i n t main ( void ) { char nome [ DIM ]; /* linha de caracteres lidos */ /* Entrada de dados do vetor */ puts ( " Entre com o seu nome , por favor . " ) ; gets ( nome ) ; puts ( " Alo " ) ; puts ( nome ) ; puts ( " Eu sou um computador , em que posso ajuda - lo ? " ); return 0; }

ignora o limite e continua lendo valores at que o usurio digite o caractere <enter>. Para evitar este problema recomenda-se o emprego da funo fgets cujo prottipo
#include < stdio .h > i n t * fgets ( const char * str , i n t tam , FILE * fluxo ) ;

A funo fgets l no mximo um caractere a menos que o nmero de caracteres especicado no parmetro tam a partir do uxo de entrada de dados denido por fluxo. No caso de leitura do teclado, como temos feito, uxo igual a stdin. A leitura interrompida quando um caractere <enter> encontrado ou o nal do arquivo foi atingido. Diferentemente do que ocorre na funo gets, aqui o caractere <enter> armazenado no vetor onde os demais caracteres esto sendo guardados. O caractere nulo adicionado aps o ltimo caractere lido. A funo retorna str caso seja bem sucedida. Se o nal do arquivo for atingido e nenhum caractere tiver sido lido, o vetor str permanece inalterado e um ponteiro nulo retornado. Caso ocorra um erro de leitura o contedo do vetor ca indeterminado e um ponteiro nulo retornado.

77

4.6

Exerccios

4.1: Escreva um programa que declare variveis do tipo int, char e float, inicialize-as, e imprima os seus valores. 4.2: Escreva um programa que dena variveis do tipo int e armazene nelas constantes octais e hexadecimais e imprima o seu contedo no formato original e em formato decimal. 4.3: Faa um programa que leia um valor inteiro no formato decimal e escreva, na tela, este mesmo valor nas bases hexadecimal e octal. Exemplo de Entrada e Sada: Entre com o valor: 10 Hexadecimal: A Octal: 12 4.4: Faa um programa capaz de ler um valor real e escrev-lo com apenas uma casa decimal. 4.5: Faa um programa que leia trs palavras de at 10 letras e reescreva estas palavras alinhadas direita da tela. 4.6: Sabendo que os argumentos da funo printf podem ser expresses (a+b, a/b, a*b, 3*a...), e no somente argumentos, faa um programa capaz de ler um valor inteiro e escrever seu triplo, seu quadrado, e a sua metade. Exemplo de Entrada e Sada: Valor: 6 Triplo: 18 Quadrado: 36 Meio: 3 4.7: Escreva um programa que leia 3 nmeros reais e imprima a mdia aritmtica destes nmeros. 4.8: Escreva um programa que pegue o valor de uma conta de restaurante e imprima o valor total a ser pago, considerando que o restaurante cobra 10% de taxa para os atendentes. 4.9: Faa um programa que pea ao usurio a quilometragem atual, a quilometragem anterior, os litros consumidos e informe a taxa de consumo (quilmetros por litro) de um automvel. 4.10: Escreva um programa que converta uma temperatura de Farenheit para Celsius. 4.11: Escreva um programa que, dado o permetro de um crculo, calcule sua rea. 4.12: Faa um programa que utilize a funo gets para ler duas cadeias de tamanho at 20 e em seguia s reescreva na linha de baixo, uma ao lado da outra e separadas por "/-/ "; 78

Captulo 5

Operadores e Expresses
5.1 Introduo

O objetivo deste captulo apresentar os operadores existentes na linguagem C e a forma correta de construir expresses que envolvam estes operadores, constantes e variveis.

5.2

Operador de Atribuio

Este o operador usado para transferir o resultado de uma expresso para uma varivel. Em C este operador o sinal de igual (=). Esta escolha do sinal de igual para servir de operador de atribuio pode causar problemas. Isto porque este sinal no est representando que o resultado da expresso do lado direito igual ao resultado do lado esquerdo e sim uma atribuio. Observe que o comando de atribuio termina em ponto e vrgula. Isto faz parte das regras da linguagem C, que determina que comandos terminam com este caractere. Por exemplo: soma = a + b; pi = 3.1415; possvel fazer-se vrias atribuies em uma nica linha, como no exemplo a seguir: a = b = c = 1.0; as trs variveis recebem o mesmo valor. As atribuies so feitas na seguinte ordem: 1. c = 1.0; c recebe o valor 1.0. 2. b recebe o resultado da expresso sua direita, que o valor atribudo c, ou seja 1.0. 3. a recebe o resultado da expresso sua direita, que o valor atribudo b, ou seja 1.0. 79

5.3

Operadores Aritmticos

A Tabela 5.3 mostra os operadores aritmticos e as suas ordens de precedncia. Operador + ++ * / % + Descrio Mais unrio Menos unrio Incremento Decremento Multiplicao Diviso Resto da diviso Soma Subtrao Prioridade 0 0 1 1 2 2 2 3 3

Tabela 5.1: Operadores aritmticos. Os smbolos mostrados na Tabela 5.3 so os nicos que podem ser usados para representar as operaes acima listadas. Expresses aritmticas em C devem ser escritas no formato linear para facilitar a digitao dos programas e tambm porque alguns smbolos usados em Matemtica no existem nos teclados. O exemplo mais comum deste formato a operao de diviso que deve ser escrita a/b. Parnteses tm um papel importante nas expresses e permitem que a ordem das operaes seja alterada. Expresses entre parnteses so calculadas em primeiro lugar, portanto eles conferem o maior grau de prioridade as expresses que eles envolvem. Podemos ter pares de parnteses envolvendo outros pares. Dizemos que os parnteses esto aninhados. Neste caso as expresses dentro dos parnteses mais internos so avaliadas primeiro. Outro ponto importante so as regras de precedncia que determinam que operao deve ser executada primeiro. Na tabela os operadores esto listados em ordem decrescente de prioridade. Para os operadores aritmticos a operao de mais alta precedncia o - unrio, vindo em seguida ++, com a mesma prioridade. Os operadores de multiplicao, diviso e mdulo tem a mesma prioridade. O operador menos unrio multiplica seu operador por -1. Quando duas operaes de mesmo nvel de prioridade tm de ser avaliadas, a operao mais esquerda ser avaliada primeiro. Um ponto importante que deve ser sempre levado em considerao quando uma expresso for calculada so os tipos das variveis, porque eles alteram radicalmente os resultados das expresses. Por exemplo, a diviso entre operandos do tipo inteiro tem como resultado um valor inteiro. Portanto, se o resultado possuir uma parte fracionria ela ser truncada. No possvel aplicar a operao de mdulo a operandos do tipo float e double. Algumas regras de converso simples existem e sero discutidas em detalhes mais adiante. Por exemplo a operao 1/3 em C fornece como resultado o valor 0, enquanto que 1 % 3 igual a 1. A seguir mostramos alguns exemplos de expresses aritmticas escritas na 80

notao da linguagem C. Observe o uso de parnteses para evitar ambigidades que poderiam fazer com que a expresso fosse calculada erradamente. Exemplo 5.4: 1. a +
b b+c

= a + b/(b+c)

2. b2 + c2 = b*b + c*c 3.
x b a+ c

= x/(a+b/c)

5.4
5.4.1

Operadores Relacionais e Lgicos


Operadores Relacionais

Os operadores relacionais esto mostrados na Tabela 5.2. Nesta tabela mostramos somente a ordem de precedncia destes operadores. A ordem de precedncia que inclui todos os operadores est mostrada na Tabela 5.10. Operador >= > <= < == != Descrio Maior ou igual a Maior que Menor ou igual a Menor que Igual a Diferente de Prioridade 0 0 0 0 1 1

Tabela 5.2: Operadores Relacionais. Os operadores >, >=, < e <= tm a mesma precedncia e esto acima de == e !=. Estes operadores tm precedncia menor que os aritmticos, portanto expresses como ( i < limite - 1) e i < (limite -1) tm o mesmo signicado.

5.4.2

Operadores Lgicos

Os operadores lgicos denem as maneiras como as relaes acima podem ser conectadas. Por exemplo podemos querer testar se ao mesmo tempo uma nota maior ou igual a 5.0 e a taxa de presena maior que 75%. Para simplicar a apresentao destes operadores sero usadas variveis para substituir as relaes. Neste caso a expresso acima seria representada como p e q, onde p est representando nota maior ou igual a 5.0 e q taxa de presena maior que 75%. Estas expresses podem ter dois resultados verdadeiro e falso. Observar que, assim como em operaes aritmticas, podemos ter combinaes de mais de duas relaes em uma nica expresso. Por exemplo, podemos ter a seguinte combinao: ano maior que 2000 e ms menor que 6 e dia maior que 15. Nas linguagens de programao os valores verdadeiro e falso podem ser representados de diversas maneiras. Uma das maneiras mais 81

comum representar verdadeiro por true e falso por false. Em C o valor falso representado por 0 e verdadeiro por qualquer valor diferente de 0. A seguir iremos mostrar os operadores lgicos existentes na linguagem C. E lgico O smbolo usado para representar o operador E lgico &&. A Tabela 5.3 mostra a tabela verdade do operador. O resultado da expresso verdadeiro se e somente se todas as variveis forem iguais a verdadeiro. Por exemplo, considere o seguinte trecho de programa:
i n t i = 3 , j = -5; f l o a t z = 3.0; i n t resultado ; resultado = (10 > 5) && ( i > -5) && ( z != 0) ; printf ( " O resultado e vale % d . " , resultado ) ;

O resultado deste trecho a impresso de um valor diferente de 0, ou seja o valor correspondente a verdadeiro. Isto porque 10 maior que 5 E i maior que -5 E z diferente de 0. p 0 0 1 1 q 0 1 0 1 p && q 0 0 0 1

Tabela 5.3: Operador Lgico E.

OU lgico O smbolo usado para representar o operador OU lgico ||. A Tabela 5.4 mostra a tabela verdade do operador. Para que o resultado da expresso seja verdade basta que uma das variveis seja verdade. Por exemplo, considere o seguinte trecho de programa:
f l o a t x = 3.0; i n t n = 55 , i = 0; i n t resultado ; resultado = ( i != 0) || ( x == 0) || ( n < 100) ; printf ( " O resultado e % d " , resultado ) ;

O resultado deste trecho a impresso do valor 1. Isto porque, apesar de i no ser diferente de 0 e x no ser diferente de zero, temos que n menor que 100. Como basta um dos testes ser verdade para o resultado ser verdade ser impresso um valor diferente de 0. 82

p 0 0 1 1

q 0 1 0 1

p || q 0 1 1 1

Tabela 5.4: Operador Lgico OU.

No lgico O smbolo usado para representar o operador NO lgico !. A Tabela 5.5 mostra a tabela verdade do operador. Este operador unrio e quando aplicado uma varivel ele troca seu valor. Por exemplo, considere o seguinte trecho de programa:
i n t dia = 25 , ano = 1959; i n t resultado ; resultado = ! ( ( dia < 30) && ( ano > 1950) ) printf ( " O resultado vale \% d . " , resultado ) ;

Este trecho de programa imprime 0 (falso), porque dia menor que 30 E ano maior que 1950. Portanto, o resultado do parnteses vale 1 (verdadeiro). No entanto, o operador ! nega este valor que vira 0. p 0 1 !p 1 0

Tabela 5.5: Operador Lgico NO. A tabela 5.6 mostra, em ordem decrescente, a precedncia dos operadores lgicos e relacionais. Operador ! >, >=, <, <= ==, != && || Prioridade 0 1 2 3 4

Tabela 5.6: Precedncia dos operadores lgicos e relacionais.

83

5.5

Operadores com Bits

Para operaes com bits, a linguagem C dispe de alguns operadores que podem ser usados nos tipos char, int, long e long long mas no podem ser usados em float, double, long double e void. A diferena entre estes operadores e os lgicos que estes operam em pares de bits enquanto que os operadores lgicos anteriores consideram a palavra toda. Por exemplo, para um valor int ser falso necessrio que todos os 32 bits sejam iguais a zero. Os operadores em bits esto mostrados na Tabela 5.7. [fragile]Operadores com bits Operador ~ & ^ | Descrio Desloca para direita Desloca para esquerda No E Ou exclusivo OU Prioridade 0 0 1 2 3 4

Tabela 5.7: Operadores com bits.

Os operadores &, | e ~ tm a mesma tabela verdade que os operadores &&, || e ! respectivamente. O operador ^ (OU Exclusivo) est descrito pela Tabela 5.8. O resultado da operao verdadeiro se e somente se os dois operandos so diferentes. p 0 0 1 1 q 0 1 0 1 p ^ q 0 1 1 0

Tabela 5.8: Operador Lgico OU. Os operandos de deslocamento tm os seguintes modos de operao: operando vezes: o operando deslocado vezes bits para a direita. operando vezes: o operando deslocado vezes bits para a esquerda. Observaes: Nos deslocamentos direita em variveis unsigned e nos deslocamentos esquerda, os bits que entram so zeros; Nos deslocamentos direita em variveis signed, os bits que entram correspondem ao sinal do nmero (1= sinal negativo, 0 = sinal positivo).

84

Um deslocamento para a direita equivalente a uma diviso por 2. Deslocamento para a esquerda equivalente a uma multiplicao por 2. Assim a = a * 2; e a = a 1; so equivalentes. O exemplo 5.1 ilustra o uso dos operandos de deslocamento:

Listagem 5.1: Exemplo de operadores de deslocamento.


#include < stdio .h > i n t main ( void ) { unsigned i n t c = 7; i n t d = -7; c = c < <1; printf ( " %3 d = %08 X \ n " , c , c ) ; c = c > >1; printf ( " %3 d = %08 X \ n " , c , c ) ; d = d < <1; printf ( " %3 d = %08 X \ n " , d , d ) ; d = d > >1; printf ( " %3 d = %08 X \ n " , d , d ) ; return 0; }

Este programa teria como resposta os seguintes resultados. 14 7 -14 -7 = = = = 0000000E 00000007 FFFFFFF2 FFFFFFF9

Os resultados mostram que o nmero 7 aps o primeiro deslocamento de 1 bit para a esquerda cou igual a 14, portanto um 0 entrou no nmero. Quando o nmero foi deslocado para direita 1 bit, ele retornou ao valor original. Observe que quando o nmero -14 foi deslocado para a direita entrou um bit 1, que igual ao sinal negativo.

5.6

Operadores de Atribuio Composta

Em C qualquer expresso da forma: variavel = variavel operador expressao pode ser escrita como: variavel operador= expressao Por exemplo: ano = ano + 10; equivalente a ano += 10; Outros exemplos so: 85

raiz = raiz * 4; raiz *= 4; soma = soma / ( a + b); soma /= (a + b); a = a 1; a = 1; i = i % 2; i %= 2;

5.7

Operador vrgula

O operador vrgula (,) usado para separar duas ou mais expresses que so escritas onde somente uma esperada. Quando o conjunto de expresses tem de ser reduzido a somente um valor, somente a expresso mais direita considerada. Por exemplo, considere o seguinte trecho de cdigo:
y = ( x =5 , x +2) ;

A expresso comea a ser avaliada da esquerda para a direita. Portanto, primeiro seria atribudo o valor 5 a varivel x. Em seguida atribui x+2 para a varivel y. Ao nal a varivel x contm o valor 5 e y o valor 7.

5.8

Operador sizeof()

O operador sizeof() um operador unrio que retorna o tamanho em bytes da expresso ou tipo fornecido entre parnteses. Por exemplo, suponha que o tipo float tenha quatro bytes ento o operador sizeof(float) retorna o valor 4. Para se calcular o tamanho de bytes de uma expresso no necessrio o uso de parnteses. No exemplo 5.2 ilustramos alguns exemplos de uso do operador sizeof(). Este programa imprime os seguintes resultados: Tamanho Tamanho Tamanho Tamanho Tamanho Tamanho em de do do do do bytes de alguns tipos int 4 float 4 double 8 char 1 vetor de 10 inteiros 40

5.9

Converso de Tipos

Quando operandos de tipos diferentes aparecem em expresses so convertidos para um tipo comum, que permita o clculo da expresso da forma mais eciente. Por exemplo, uma operao que envolva um tipo int e um float, o valor int convertido para float.

86

Listagem 5.2: Exemplo do operador sizeof.


#define DIM 10 #include < stdio .h > #include < conio .h > i n t main () { i n t i =0; f l o a t f =3.0; char c = 'a '; i n t v [ DIM ]; printf ( " Tamanho em bytes de alguns tipos \ n " ) ; printf ( " Tamanho de int % d \ n " , s i z e o f i ) ; printf ( " Tamanho do float % d \ n " , s i z e o f f ) ; printf ( " Tamanho do double % d \ n " , s i z e o f ( double ) ) ; printf ( " Tamanho do char % d \ n " , s i z e o f c ) ; printf ( " Tamanho do vetor de % d inteiros % d \ n " , DIM , s i z e o f ( v ) ) ; return 0; }

Observar que converses ocorrem somente quando necessrio. Por exemplo, em uma diviso de inteiros o resultado do tipo inteiro. Isto pode causar surpresas desagradveis para programadores iniciantes. A expresso 1/3*3 tem como resultado o valor inteiro 0. J que a primeira expresso executada 1/3 tem como resultado 0. Operandos do tipo char e int podem ser livremente misturados em expresses aritmticas. Os tipos char so convertidos para int. Caso o conjunto de caracteres esteja codicado segundo a tabela ASCII, esta facilidade permite a realizao de algumas transformaes interessantes. Por exemplo, a converso de uma letra maiscula para minscula pode ser facilmente implementada com o comando: l = l - A + a; A letra maiscula armazenada na varivel l subtrada do cdigo da letra maiscula A, fornecendo a posio desta letra no alfabeto. Em seguida este valor somado ao cdigo da letra minscula a, resultando da converso para minscula. Portanto, converses aritmticas ocorrem de maneira quase que natural. Em operaes binrias as seguintes converses ocorrem quando diferentes tipos esto envolvidos: char convertido para int; float convertido para double. Ento, se algum dos operandos double o outro convertido para double e o resultado double. Caso contrrio, se algum dos operandos long, o outro convertido para long e o resultado long. Caso contrrio, se algum 87

dos operandos unsigned, o outro convertido para unsigned e o resultado deste tipo. Caso contrrio os operandos so int e o resultado int. Note que todos os floats em uma expresso so convertidos para double e a expresso avaliada em double. O resultado de uma expresso convertido para o tipo da varivel onde o resultado ser armazenado. Um resultado float ao ser carregado em uma varivel do tipo int causa o truncamento da parte fracionria, porventura existente. A converso de inteiro para caractere bem comportada, mas o contrrio nem sempre ocorre convenientemente. A linguagem no especica se o tipo char um tipo com sinal ou no. Quando um caractere armazenado em uma varivel do tipo inteiro podem ocorrer problemas com caracteres que tm o bit mais esquerda igual a 1. Isto porque algumas arquiteturas podem estender este bit e outras no.

5.10

Regras de Precedncia

A Tabela 5.10 mostra, em ordem decrescente de prioridade, as regras de precedncia dos operadores em C. Os operadores que esto na mesma linha da tabela e com a mesma ordem tm a mesma prioridade. Alguns dos operadores listados na Tabela somente sero mostrados nos captulos seguintes. Pri 0 1 1 2 3 4 5 6 7 8 9 10 11 12 13 13 14 Operador () [] -> . ! ++ * & (tipo) sizeof() * / % + < <= >= > == != & ^ | && || ? () : () = += -= *= /= %= = = &= |= , Descrio Agrupamento; acesso vetor; acesso membro Unrias lgicas, aritmticas e com ponteiros; Conformao de tipo; tamanho Multiplicao, diviso e mdulo soma e subtrao Deslocamento de bits direita e esquerda Operadores relacionais Igualdade e diferena E bit a bit Ou exclusivo bit a bit Ou bit a bit E Ou Ternrio Atribuies Atribuies Separador de expresses

Tabela 5.9: Precedncia dos operadores.

88

5.11

Exerccios

5.1: Escreva as expresses C abaixo na sua forma matemtica usual: 1. (a/b)*(c/d) 2. (a/b*c/d) 3. (a/(b*c)/d) 4. a*x*x+b*x+c 5.2: Escreva as expresses matemticas na linguagem C. 1. b2 4 b c 2. 3.
1 1+
1+ 1 1 1+x

a+b c +d x c +d

4. a

5.3: Diga a ordem de clculo e o resultado das expresses abaixo: 1. x = 5 * 4 / 6 + 7; 2. x = 5 * 4.0 / 6 + 7; 3. x = 5 * 4 % 6 + 7; 4. x = ((4 / 2) + (3.0 * 5)); 5.4: Escreva um programa que imprima a tabela verdade da funo ou exclusivo. 5.5: Escreva um programa que calcule o produto entre um valor x e 2n , onde n e x so inteiros. Utilize operadores binrios. 5.6: Escreva um programa que leia um ngulo em segundos e imprima quantos graus, minutos e segundos h neste ngulo. 5.7: Escreva um programa que leia um tempo em segundos e imprima quantas horas, minutos e segundos h neste tempo. 5.8: Escreva um programa que leia um comprimento em centmetros e imprima quantos metros, decmetros e centmetros h neste comprimento. 5.9: Uma empresa est selecionando entre seus empregados os que iro fazer um treinamento especial. O funcionrio selecionado deve satisfazer a dois critrios. O primeiro critrio para que um funcionrio seja pr-selecionado que ele deve ter um salrio menor ou igual a R$ 400,00 ou maior ou igual a R$ 1.000,00. O segundo critrio leva em conta o tempo de trabalho e o funcionrio deve ter mais de 5 anos na empresa. Marque a resposta que indica a expresso lgica que representa este critrio. Considere que existem as seguintes variveis. 89

Listagem 5.3: Variveis da questo 9.


f l o a t salario ; i n t tempo ;

(a) (salario <= 400.00) && (salario >= 1000.00) && (tempo > 5) (b) (salario <= 400.00) || (salario >= 1000.00) && (tempo > 5) (c) ((salario <= 400.00) || (salario >= 1000.00)) || (tempo > 5) (d) ((salario <= 400.00) || (salario >= 1000.00)) && (tempo > 5) (e) ((salario <= 400.00) && (salario >= 1000.00)) || (tempo > 5)

90

Captulo 6

Comandos de Controle
6.1 Introduo

Este captulo tem por objetivo apresentar os comandos de controle da linguagem C. Estes comandos servem para controlar o uxo de execuo das instrues de um programa. Estes comandos permitem que o computador tome decises independentemente do usurio que est rodando o programa.

6.2

Blocos de Comandos

Blocos de comando so grupos de comandos que devem ser tratados como uma unidade lgica. O incio de um bloco em C marcado por uma chave de abertura ({) e o trmino por uma chave de fechamento (}). O bloco de comandos serve para agrupar comandos que devem ser executados juntos. Por exemplo, usase bloco de comandos quando em comandos de teste deve-se escolher entre executar dois blocos de comandos. Um bloco de comandos pode ser utilizado em qualquer trecho de programa onde se pode usar um comando C. interessante observar que um bloco de comandos pode ter zero comandos C. Um bloco de comandos com 0 ou 1 comando pode dispensar as chaves. Um bloco de comandos mostrado a seguir.
/* bloco_de_c o ma nd o s */ { i = 0; j = j + 1; printf ( " % d % d \ n " , i , j ) ; }

6.3

Comandos de Teste

Os comandos de teste permitem ao computador decidir o caminho a seguir, durante a execuo do programa, independentemente do usurio. Estes testes 91

so baseados em estados internos disponveis ao processador. Estes estados podem ser resultantes de uma operao aritmtica anterior, de uma operao anterior etc.

6.3.1

Comando if

O comando if utilizado quando for necessrio escolher entre dois caminhos. A forma geral do comando if a seguinte:
i f ( expresso ) bloco_de_co ma n do s 1 ; else bloco_de_co ma n do s 2 ;

Neste comando a expresso avaliada, e caso o resultado seja verdadeiro (qualquer resultado diferente de zero) o bloco_de_comandos1 executado, caso contrrio o bloco_de_comandos2 executado. Pela denio do comando a expresso deve ter como resultado um valor diferente de zero para ser considerada verdade. Observar que somente um dos dois blocos ser executado. Como a clusula else opcional a forma abaixo do comando if perfeitamente vlida.
i f ( expresso ) bloco_de_com a nd o s ;

Lembrar que os blocos de comandos devem ser delimitados pelas chaves, a no ser quando o bloco composto por 0 ou 1 comando. Na listagem 6.1 mostramos alguns exemplos de uso de comando if. Uma construo que pode aparecer so os comandos ifs em escada, cuja forma geral a seguinte:
i f ( expresso ) bloco_de_co ma nd o s e l s e i f ( expresso1 ) bloco_de_co m an d os 1 e l s e i f ( expresso2 ) bloco_de_c o ma nd o s2 ... else bloco_de_c o ma nd o sn

O programa 6.2 mostra um exemplo com ifs em escada e aninhados. Um exemplo de uso deste programa o seguinte:

92

Listagem 6.1: Exemplo de comandos if.


scanf ( " % d " , & dia ) ; i f ( dia > 31 || dia < 1 ) printf ( " Dia invalido \ n " ) ; scanf ( " % d " , & numero ) ; i f ( numero > 0 ) printf ( " Numero positivo \ n " ) ; else printf ( " Numero negativo \ n " ) ; scanf ( " % f " , & salario ) ; i f ( salario < 800.00) { printf ( " Aliquota de imposto = 0.1\ n " ) ; imposto = salario * 0.1; } else { printf ( " Aliquota de imposto = 0.25\ n " ) ; imposto = salario * 0.25; }

Este programa simula uma calculadora simples. Por favor entre com os dois operandos. 3 5 Qual a operacao + O resultado da + vale 8.000000. Para evitar que o recuo da escada seja muito profundo o comando if em escada foi escrito da seguinte maneira:
i f ( expresso ) bloco_de_com a nd o s ; e l s e i f ( expresso ) bloco_de_com a nd o s ; e l s e i f ( expresso ) bloco_de_com a nd o s ; ... e l s e bloco_de_co ma n do s ;

6.3.2

Comando switch

O comando if, em todas suas formas, suciente resolver problemas de seleo de comandos. Porm em alguns casos, como no exemplo 6.2 o programa se torna mais trabalhoso para ser escrito e entendido. O comando switch facilita

93

Listagem 6.2: Programas com ifs em escada e aninhados.


#include < stdio .h > i n t main ( void ) { f l o a t num1 , /* primeiro operando */ num2 , /* segundo operando */ res ; /* resultado da operacao */ char oper ; /* caractere que define a operacao */ printf ( " \ nPrograma que simula calculadora simples .\ n ") ; printf ( " Entre com os dois operandos .\ n " ) ; scanf ( " % f % f " , & num1 , & num2 ) ; getchar () ; /* tirar o cr */ printf ( " Qual a operacao ? \ n " ) ; oper = getchar () ; i f ( oper == '+ ') res = num1 + num2 ; e l s e i f ( oper == ' - ') res = num1 - num2 ; e l s e i f ( oper == '* ') res = num1 * num2 ; e l s e i f ( oper == '/ ') { i f ( num2 == 0.0) { printf ( " Operacao de divisao por 0 invalida !\ n"); return 1; } e l s e res = num1 / num2 ; } else { printf ( " Operacao invalida !\ n " ) ; return 1; } printf ( " O resultado da % c vale % f .\ n " , oper , res ) ; return 0; }

94

a escrita de trechos de programa em que a seleo deve ser feita entre vrias alternativas. A forma geral do comando switch a seguinte:
switch ( expresso ) { case constante1 : seqncia_ d e _c o ma n d os ; break ; case constante2 : seqncia_ d e _c o ma n d os ; break ; case constante3 : seqncia_ d e _c o ma n d os ; break ; ... default: seqncia_ d e_ c om a n do s ; }

Uma seqncia de comandos diferente de um bloco de comandos. Um bloco de comandos inicia com uma chave e termina com uma chave, enquanto que uma seqncia apenas uma srie de comandos. Por exemplo, uma vez que um bloco de comandos foi selecionado por um comando if ele ser executado at a ltima instruo do bloco, a menos que haja um comando de desvio. Uma srie de comandos so apenas comandos colocados um aps outro. A execuo do comando switch segue os seguintes passos: 1. A expresso avaliada; 2. O resultado da expresso comparado com os valores das constantes que aparecem nos comandos case; 3. Quando o resultado da expresso for igual a uma das constantes, a execuo se inicia a partir do comando associado com esta constante. A execuo continua at o m do comando switch, ou at que um comando break seja encontrado; 4. Caso no ocorra nenhuma coincidncia os comandos associados ao comando default so executados. O comando default opcional, e se ele no aparecer nenhum comando ser executado. O comando break um dos comandos de desvio da linguagem C. O break usado dentro do comando switch para interromper a execuo da seqncia de comandos e pular para o comando seguinte ao comando switch. H alguns pontos importantes que devem ser mencionados sobre o comando switch. O resultado da expresso deve ser um tipo enumervel, por exemplo o tipo int. Tambm podem ser usados tipos compatveis com int, isto , expresses com resultados tipo char podem ser usadas; 95

Notar que caso no aparea um comando de desvio, todas as instrues seguintes ao teste case que teve sucesso sero executadas, mesmo as que estejam relacionadas com outros testes case; O comando switch s pode testar igualdade; No podem aparecer duas constantes iguais em um case; O programa 6.3 mostra um exemplo de uso de comandos switch.

6.3.3

Comando Ternrio

O comando ternrio tem este nome porque necessita de trs operandos para ser avaliado. O comando ternrio tem a seguinte forma: expresso1 ? expresso2 : expresso3 Para avaliar o resultado total da expresso, primeiro a expresso1 avaliada. Caso este resultado seja correspondente ao valor verdadeiro ento o resultado da expresso ser igual ao resultado da expresso2. Caso contrrio a expresso3 avaliada e se torna o resultado. O programa 6.4 mostra um exemplo de uso de comando ternrio.

6.4

Laos de Repetio

Estes comandos permitem que trechos de programa sejam repetidos um certo nmero de vezes controlado pelo programa. O nmero de vezes que um lao ser executado pode ser xo ou depender de condies que mudam durante a execuo do lao.

6.4.1

Comando for

Este comando aparece em vrias linguagens de programao, mas na linguagem C ele apresenta um grau maior de exibilidade. A idia bsica do comando for a seguinte. Uma varivel de controle, geralmente um contador, recebe um valor inicial. O trecho de programa que pertence ao lao executado e ao nal a varivel de controle incrementada ou decrementada e comparada com o valor nal que ela deve alcanar. Caso a condio de trmino tenha sido atingida o lao interrompido. A forma geral do comando for a seguinte:
f o r ( expresso1 ; expresso2 ; expresso3 ) blocodecoman do s ;

As trs expresses geralmente tm os seguintes signicados: 1. A expresso1 utilizada para inicializar a varivel de controle do lao; 2. A expresso2 um teste que controla o m do lao;

96

Listagem 6.3: Exemplo de switch.


#include < stdio .h > i n t main ( void ) { float num1 , /* primeiro operando */ num2 , /* segundo operando */ res ; /* resultado da operacao */ char oper ; /* caracter que define a operacao */ printf ( " \ nEste programa simula uma calculadora simples .\ n " ) ; printf ( " Por favor entre com os dois operandos .\ n ") ; scanf ( " % f % f " , & num1 , & num2 ) ; getchar () ; printf ( " Qual a operacao \ n " ) ; oper = getchar () ; printf ( " A operacao e % c \ n " , oper ) ; switch ( oper ) { case '+ ': res = num1 + num2 ; break ; case ' - ': res = num1 - num2 ; break ; case '* ': res = num1 * num2 ; break ; case '/ ': i f ( num2 == 0.0) { printf ( " Divisao por zero e uma opcao invalida .\ n " ) ; return 1; } else { res = num1 / num2 ; break ; } default: printf ( " Operacao invalida !\ n " ) ; return 2; } printf ( " O resultado da % c vale % f .\ n " , oper , res ) ; return 0; }

97

Listagem 6.4: Exemplo de comando ternrio.


#include < stdio .h > i n t main ( void ) { float num1 , /* primeiro operando */ num2 , /* segundo operando */ max ; /* resultado da operacao */ printf ( " Imprime o maior valor de dois numeros .\ n ") ; printf ( " Por favor entre com os dois mumeros .\ n ") ; scanf ( " % f % f " , & num1 , & num2 ) ; max = ( num1 > num2 ) ? num1 : num2 ; printf ( " O maior dos numeros lidos e % f .\ n " , max ) ; return 0; }

3. A expresso3 normalmente faz um incremento ou decremento da varivel de controle. A execuo do comando for segue os seguintes passos: 1. A expresso1 avaliada; 2. A expresso2 avaliada para determinar se o comando deve ser executado; 3. Se o resultado da expresso2 for verdadeiro o bloco de comandos executado, caso contrrio o lao terminado; 4. A expresso3 avaliada; 5. Voltar para o passo 2. O trecho a seguir imprime todos os nmeros entre 1 e 100.
f o r ( i = 1; i <= 100; i ++) { printf ( " Numero % d \ n " , i ) ; }

O programa 6.5 mostra como se pode calcular o fatorial de um nmero usando-se o comando for. Laos for com mais de um comando por expresso Outra possibilidade que o comando for em C permite a incluso de vrios comandos, separados por vrgulas, nas expresses. O trecho de programa a seguir mostra um exemplo de uso de comando for com vrios comandos nas expresses.

98

Listagem 6.5: Exemplo de comando for.


#include < stdio .h > #include < stdlib .h > i n t main () { i n t numero , fat =1 , i ; printf ( " \ nEntre com um numero positivo . " ) ; scanf ( " % d " , & numero ) ; f o r ( i = numero ; i >1; i - -) fat = fat * i ; printf ( " O fatorial de % u vale % u . " , numero , fat ) ; return 0; }

int i,j; f o r ( i =1 , j = 10; i <= 10; i ++ , j += 10) { printf ( " i = %d , j = % d \ n " , i , j ) ; }

Laos for com testes usando outras variveis A expresso de controle no precisa necessariamente envolver somente um teste com a varivel que controla o lao. O teste de nal do lao pode ser qualquer expresso relacional ou lgica. No programa 6.6 o lao pode terminar porque a varivel de controle j chegou ao seu valor limite ou foi batida a tecla *, e neste caso o lao termina antecipadamente.

Listagem 6.6: Exemplo de comando for com testes sobre outras variveis.
#include < stdio .h > i n t main () { char c = ' '; int i; f o r ( i =0 ; (i <5) && ( c != '* ') ; i ++ ) { printf ( " % c \ n " , c ) ; c = getchar () ; } return 0; }

Laos for com expresses faltando Um outro ponto importante do for que nem todas as expresses precisam estar presentes. No exemplo 6.7 a varivel de controle no incrementada. A nica maneira do programa terminar o usurio bater o nmero -1. 99

Listagem 6.7: Exemplo de comando for sem alterao da varivel de controle.


#include < stdio .h > i n t main () { int i; f o r ( i =0 ; i != -1 ; ) { printf ( " % d \ n " ,i ) ; scanf ( " % d " , & i ) ; } return 0; }

possvel omitir qualquer uma das expresses. Por exemplo, se a expresso2 for omitida o programa assume que ela sempre verdade de modo que o lao s termina com um comando de desvio como o break. O programa do exemplo 6.8 pra quando o valor da varivel de controle for igual a 5. Neste caso o teste ser verdade o lao termina por meio do break.

Listagem 6.8: Exemplo de comando for sem teste de m.


#include < stdio .h > i n t main () { int i; f o r ( i = 0; ; i ++) { printf ( " numero % d \ n " , i ) ; i f ( i == 5) break ; } return 0; }

Lao innito Uma construo muito utilizada o lao innito. No lao innito o programa pra quando se executa o comando break. O trecho de programa a seguir somente pra quando for digitada a tecla s ou S .
for ( ; ; ) { printf ( " \ nVoce quer parar ?\ n " ) ; c = getchar () ; i f ( c == 'S ' || c == 's ') break ; }

100

Laos for aninhados Uma importante construo aparece quando colocamos como comando a ser repetido um outro comando for. Esta construo pode aparecer quando estamos trabalhando com matrizes. O exemplo 6.9 mostra um programa que imprime uma tabuada.

Listagem 6.9: Comando for aninhados.


#include < stdio .h > i n t main ( void ) { int i, j; printf ( " Imprime tabuada de multiplicacao .\ n ") ; f o r ( i =1 ; i <10 ; i ++) { printf ( " Tabuada de % d \ n " , i ) ; f o r ( j =1; j <10; j ++) { printf ( " % d x % d = % d \ n " , i , j , i * j ) ; } } return 0; }

6.4.2

Comando while

O comando while tem a seguinte forma geral:


while ( expresso ) bloco_de_co ma nd o s

A expresso pode assumir o valor falso (igual a 0) ou verdade (diferente de 0). Os passos para execuo do comando so os seguintes: 1. A expresso avaliada; 2. Se o resultado for verdadeiro ento o bloco de comandos executado, caso contrrio a execuo do bloco terminada; 3. Voltar para o passo 1. Uma caracterstica do comando while, como pode ser visto dos passos acima, que o bloco de comandos pode no ser executado caso a condio seja igual a falso logo no primeiro teste. O trecho de abaixo imprime os 100 primeiros nmeros usando um comando while.
i = 1;

101

while ( i <= 100) { printf ( " Numero % d \ n " , i ) ; i ++; }

A expresso do comando pode incluir chamadas de funo. Lembrar que qualquer atribuio entre parnteses considerada como uma expresso que tem como resultado o valor da atribuio sendo feita. Por exemplo, o programa 6.10 repete um bloco de comandos enquanto o usurio usar a tecla c para continuar, qualquer outra tecla o bloco interrompido.

Listagem 6.10: Comando while com uma funo.


#include < stdio .h > i n t main ( void ) { int c; puts ( " Tecle c para continuar .\ n " ) ; while (( c = getchar () ) == 'c ') { puts ( " Nao Acabou .\ n " ) ; getchar () ; /* tira o enter */ } puts ( " Acabou .\ n " ) ; return 0; }

6.4.3

Comando do-while

A forma genrica do comando a seguinte:


do bloco_de_co ma nd o s while ( expresso ) ;

Observar que neste comando a expresso de teste est aps a execuo do comando, portanto o bloco de comandos executado pelo menos uma vez. A execuo do comando segue os seguintes passos: 1. Executa o comando; 2. Avalia a expresso; 3. Se o resultado da expresso for verdadeiro ento volta para o passo 1, caso contrrio interrompe o do-while O exemplo de comando for para imprimir os 100 primeiros nmeros escrito com comando do-while ca da seguinte maneira:

102

i = 1; do { printf ( " Numero % d \ n " , i ) ; i ++; } while ( i <= 100) ;

6.5
6.5.1

Comandos de Desvio
Comando break

O comando break pode ser tanto usado para terminar um teste case dentro de um comando switch quanto interromper a execuo de um lao. Quando o comando utilizado dentro de um comando for o lao imediatamente interrompido e o programa continua a execuo no comando seguinte ao comando for. No trecho de programa abaixo o comando for deve ler 100 nmeros inteiros positivos. No entanto, se for digitado um nmero negativo o comando for interrompido imediatamente sem que o nmero seja impresso.
f o r ( i = 0; i < 100; i ++) { scanf ( " % d " , & num ) ; i f ( num < 0) break ; printf ( " % d \ n " , num ) ; }

6.5.2

Comando continue

O comando continue parecido com o comando break. A diferena que o comando continue simplesmente interrompe a execuo da iterao corrente passando para a prxima iterao do lao, se houver uma. No comando for o controle passa a execuo da expresso3. Nos comandos while e do-while o controle passa para a fase de testes. No trecho de programa abaixo o lao l 100 nmeros inteiros, caso o nmero seja negativo, um novo nmero lido.
f o r ( i = 0; i < 100; i ++) { scanf ( " % d " , & num ) ; i f ( num < 0) continue ; printf ( " % d \ n " , num ) ; }

6.5.3

Comando goto

O comando goto causa um desvio incondicional para um outro ponto da funo em que o comando est sendo usado. O comando para onde deve ser feito o 103

desvio indicado por um rtulo, que um identicador vlido em C seguido por dois pontos. importante notar que o comando goto e o ponto para onde ser feito o desvio pode estar em qualquer ponto dentro da mesma funo. A forma geral deste comando : goto rtulo; ... rtulo: Este comando durante muito tempo foi associado a programas ilegveis. O argumento para esta armao se baseia no fato de que programas com comandos goto perdem a organizao e estrutura porque o uxo de execuo pode car saltando erraticamente de um ponto para outro. Atualmente as restries ao uso do comando tem diminudo e seu uso pode ser admitido em alguns casos.

6.5.4

Funo exit()

A funo exit provoca a terminao de um programa, retornando o controle ao sistema operacional. O prottipo da funo a seguinte: void exit (int codigo); Observar que esta funo interrompe o programa como um todo. O cdigo usado para indicar qual condio causou a interrupo do programa. Usualmente o valor 0 indica que o programa terminou sem problemas. Um valor diferente de 0 indica um erro.

6.5.5

Comando return

O comando return usado para interromper a execuo de uma funo e retornar um valor ao programa que chamou esta funo. Caso haja algum valor associado ao comando return este devolvido para a funo, caso contrrio um valor qualquer retornado. A forma geral do comando : return expresso; Notar que a expresso opcional. A chave que termina uma funo equivalente a um comando return sem a expresso correspondente. possvel haver mais de um comando return dentro de uma funo. O primeiro que for encontrado durante a execuo causar o m da execuo. Uma funo declarada como do tipo void no pode ter um comando return que retorne um valor. Isto no faz sentido, j que funes deste tipo no podem retornar valores.

104

6.6

Exerccios

6.1: Escreva um programa que calcule x elevado a n. Assuma que n um valor inteiro. 6.2: Escreva um programa que exiba as opes 1-multiplicar e 2-somar de um menu, leia a opo desejada, leia dois valores, execute a operao (utilizando o comando if) e exiba o resultado. 6.3: Utilizando ifs em escada, inclua, no programa do exerccio anterior, as opes 3-Subtrair e 4-Dividir. 6.4: Simplique os programas anteriores da seguinte forma: Reescreva o programa do exerccio 1 substituindo o comando if pelo comando ternrio. Reescreva o programa do exerccio 2 substituindo os ifs em escada pelo comando switch. 6.5: Utilizando um lao for dentro de outro, escreva um programa que exiba as tabuadas de multiplicao dos nmeros de 1 9. 6.6: Escreva um programa com menu de 5 opes que utilize o comando de desvio goto para executar a opo desejada e s saia do programa caso a opo 5-Sair seja selecionada. 6.7:Escreva um programa que tenha um nmero (inteiro) como entrada do usurio e escreva como sada a seqencia de bits que forma esse numero. Por exemplo, aps digitado o nmero 10, a sada deve ser 0000000000001010. 6.8:Escreva um programa que imprima todos os pares entre 0 e 50 e em seguida imprima todos os impares. Deixar um espao entre os nmeros. 6.9: Escreva um programa que leia 10 nmeros. O programa deve imprimir a media, o maior e o menor deles. Obs: Os nmeros devem ser entre 0 e 10. 6.10: Escreva um programa que leia 10 nmeros. O programa deve imprimir a media, o maior e o menor deles. Obs: Considere agora que os nmeros podem ser quaisquer. 6.11: Escreva um programa que exibe a tabela ascii. 6.12: Crie um programa para vericar se um nmero dado primo. 6.13: Escreva um programa que leia um numero do teclado e ache todos os seus divisores. 6.14: Escreva um programa que imprima a seqncia 987654321876543217654321654321543214321321211

105

No use nenhuma constante, use apenas variveis. Em outra linha imprima as letras maisculas de A at Z (ABCD...). 6.15: Escreva um programa que conte de 100 a 999 (inclusive) e exiba, um por linha, o produto dos trs dgitos dos nmeros. 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) 9*9*9=729 Faa seu programa dar uma pausa a cada 20 linhas para que seja possvel ver todos os nmeros pouco a pouco. Solicite que seja pressionada alguma tecla para ver a prxima seqncia de nmeros. 6.16: Escreva um programa que imprima uma gura como a mostrada abaixo. O nmero de linhas da gura deve ser pedido ao usurio. ****** ***** **** *** ** *

6.17: O que ser impresso pelo programa 6.11. Indique os nmeros que voc ir digitar para o programa. Os nmeros devem ser todos diferentes.

106

Listagem 6.11: Programa do exercicio 17.


#include < stdio .h > i n t main ( void ) { int i, j, m; f o r ( i = 0; i < 6; i ++) { scanf ( " % d " , & m ) ; i f ( m % 2) { f o r ( j = 0; j < m ; j ++) { printf ( " # " ) ; } } else { f o r ( j = m ; j > 0; j - -) { printf ( " * " ) ; } } printf ( " \ n " ) ; } return 0; }

107

Captulo 7

Vetores e Cadeias de Caracteres


7.1 Introduo

Vetores so usados para tratamento de conjuntos de dados que possuem as mesmas caractersticas. Uma das vantagens de usar vetores que o conjunto recebe um nome comum e elementos deste conjunto so referenciados atravs de ndices. Pelo nome vetor estaremos referenciando estruturas que podem ter mais de uma dimenso, como por exemplo matrizes de duas dimenses. Neste captulo estaremos mostrando vetores de tamanhos xos. Somente aps apresentarmos ponteiros iremos abordar alocao de memria para vetores.

7.2

Declarao de Vetores Unidimensionais

A forma geral da declarao de vetores de uma dimenso :


tipo nome [tamanho];

onde tipo um tipo qualquer de dados, nome o nome pelo qual o vetor vai ser referenciado e tamanho o nmero de elementos que o vetor vai conter. Observar que em C o primeiro elemento tem ndice 0 e o ltimo tamanho - 1. Exemplos de declaraes de vetores so:
i n t numeros [1000]; f l o a t notas [65]; char nome [40]; /* vetor de 1000 inteiros */ /* conjunto de 65 numeros reais */ /* conjunto de 40 caracteres */

O espao de memria, em bytes, ocupado por um vetor de tipo qualquer igual a:


espao = tamanho * sizeof(tipo)

importante notar que em C no h vericao de limites em vetores. Isto signica que possvel ultrapassar o m de um vetor e escrever em outras 108

variveis, ou mesmo em trechos de cdigo. tarefa do programador fazer com que os ndices dos vetores estejam sempre dentro dos limites estabelecidos pela declarao do vetor. O programa 7.1 ilustra como se declara um vetor, inicializa seus valores e imprime o contedo. Notar o uso da diretiva #define DIM 5 para denir uma constante, que posteriormente foi usada para estabelecer o tamanho do vetor. Esta constante passa a ser usada nas referncias ao vetor, por exemplo no comando de gerao do conjunto de dados armazenado no vetor. Caso seja necessrio trocar o tamanho do vetor basta alterar o valor da constante e recompilar o programa.

Listagem 7.1: Exemplo de vetores.


#define DIM 5 #include < stdio .h > i n t main ( void ) { i n t vetor [ DIM ]; unsigned i n t i , num ; puts ( " Este programa gera um vetor de inteiros .\ n ") ; puts ( " Entre com o numero inicial do conjunto . ") ; scanf ( " % d " , & num ) ; /* Geracao do conjunto */ f o r ( i = 0 ; i < DIM ; i ++) vetor [ i ] = num ++; /* Impressao do conjunto */ f o r ( i = 0; i < DIM ; i ++) printf ( " Elemento % d = % d \ n " , i , vetor [ i ]) ; return 0; }

O programa 7.2 calcula o produto escalar de dois vetores inteiros. Observar como na leitura dos elementos do vetor usa-se o operador de endereo & antes do nome de cada elemento. O programa 7.3 ilustra o mtodo da bolha para ordenao em ordem crescente de um vetor de inteiros. Neste mtodo a cada etapa o maior elemento movido para a sua posio. A cada iterao os elementos do vetor so comparados dois a dois, sendo trocados caso seja necessrio. Ao trmino da primeira passada pelo vetor, o maior elemento levado para a sua posio, no nal do vetor. Portanto, ele no precisa ser mais considerado, da o valor da varivel que aponta para o nal do vetor (fim) diminuda de 1. O processo repetido at que todos os elementos sejam levados para as suas posies ou que nenhuma troca seja realizada. Quando nenhuma troca realizada o vetor est ordenado. A Tabela 7.1 mostra os passos executados pelo algoritmo at ordenar o vetor.

109

Operao Passo 1 v[0] > v[1]? Trocar v[0] e v[1] > v[2]? Trocar v[1] e v[2] > v[3]? Trocar v[2] e v[3] > v[4]? Trocar v[3] e Passo 2 v[0] > v[1]? Trocar v[0] e v[1] > v[2]? Trocar v[1] e v[2] > v[3]? Trocar v[2] e Passo 3 v[0] > v[1]? v[1] > v[2]? Trocar v[1] e Passo 4 v[0] > v[1]? Trocar v[0] e

v[0] 20 15 15 15 15 15 15 15 15 8 8 8 8 8 8 8 8 8 5

v[1] 15 20 20 8 8 8 8 8 8 15 15 12 12 12 12 12 5 5 8

v[2] 8 8 8 20 20 12 12 12 12 12 12 15 15 5 5 5 12 12 12

v[3] 12 12 12 12 12 20 20 5 5 5 5 5 5 15 15 15 15 15 15

v[4] 5 5 5 5 5 5 5 20 20 20 20 20 20 20 20 20 20 20 20

v[1] v[2] v[3] v[4]

v[1] v[2] v[3]

v[2]

v[1]?

Tabela 7.1: Passos executados durante o algoritmo da bolha.

110

Listagem 7.2: Produto escalar de dois vetores.


#define DIM 5 #include < stdio .h > i n t main ( void ) { i n t vetor1 [ DIM ] , vetor2 [ DIM ] , i , prod =0; printf ( " Entre com um vetor de % d elementos \ n " , DIM ) ; f o r ( i = 0; i < DIM ; i ++) { printf ( " Elemento % d " , i ) ; scanf ( " % d " , & vetor1 [ i ]) ; } printf ( " Entre com outro vetor de % d elementos \ n " , DIM ) ; f o r ( i = 0; i < DIM ; i ++) { printf ( " Elemento % d " , i ) ; scanf ( " % d " , & vetor2 [ i ]) ; } f o r ( i = 0; i < DIM ; i ++) prod += vetor1 [ i ] * vetor2 [ i ]; printf ( " O produto vale % d " , prod ) ; return 0; }

7.3

Cadeias de Caracteres

Um cadeia de caracteres (string ) um conjunto de caracteres terminado por um caractere nulo, que representado como \0. Para especicar um vetor para armazenar um cadeia deve-se sempre reservar um espao para este caractere. Por exemplo, para armazenar um cadeia de 40 caracteres deve-se reservar um vetor de 41 de caracteres. Em C possvel haver constantes cadeia, que so denidas como uma lista de caracteres entre aspas. Por exemplo, "programando em C" No necessrio a colocao do caractere nulo ao nal da cadeia. Em C no h o tipo cadeia (string) e, portanto, conjuntos de caracteres teriam de ser tratados como conjuntos de nmeros inteiros, por exemplo. Para facilitar a programao foram criadas algumas funes para manipular cadeias. Algumas das funes mais comuns esto resumidamente descritas a seguir: Nas denies a seguir, size_t o tipo inteiro sem sinal que volta como resultado do operador sizeof. char *strcat(char *dest, const char *orig): Concatena cadeia orig ao nal de dest. O primeiro caractere de orig substitui o caractere nulo de dest. A funo retorna o valor de dest. char *strncat (char *dest, const char *orig, size_t n): Concatena cadeia orig ao nal de dest, usando no mximo n caracteres de orig. O 111

Listagem 7.3: Ordenao pelo mtodo da bolha.


#define DIM 5 #define FALSO 0 #define VERDADE 1 #include < stdio .h > i n t main ( void ) { i n t vetor [ DIM ] , i ; i n t trocou = FALSO , fim = DIM , temp ; printf ( " Entre com um vetor de % d elementos \ n " , DIM ) ; f o r ( i = 0; i < DIM ; i ++) { printf ( " Elemento % d " , i ) ; scanf ( " % d " , & vetor [ i ]) ; } do { trocou = FALSO ; f o r ( i =0; i < fim -1; i ++) { i f ( vetor [ i ] > vetor [ i +1]) { temp = vetor [ i ]; vetor [ i ] = vetor [ i +1]; vetor [ i +1] = temp ; trocou = VERDADE ; } } fim - -; } while ( trocou ) ; f o r ( i =0; i < DIM ; i ++) printf ( " % d \ n " , vetor [ i ]) ; return 0; }

112

primeiro caractere de orig substitui o caractere nulo de dest. A funo retorna o valor de dest. char *strcmp (const char *cad1, const char *cad2): Compara lexicogracamente as duas cadeias. Retorna zero se as cadeias so iguais, menor que 0 se cad1 < cad2, maior que 0 se cad1 > cad2. char *strncmp (const char *cad1, const char *cad2, size_t n): Compara lexicogracamente at n caracteres das duas cadeias. Retorna zero se as cadeias so iguais, menor que 0 se cad1 < cad2, maior que 0 se cad1 > cad2. size_t strlen(const char *cad): Calcula o comprimento da cadeia sem contar o caraca ter nulo. O comprimento da cadeia determinado pelo caractere nulo. No confundir o tamanho da cadeia com o tamanho do vetor que armazena a cadeia. char *strcpy(char *dest, const char *orig): Copia cadeia orig para dest. A cadeia destino deve ter espao suciente para armazenar orig. O valor de dest retornado. Estas funes esto na biblioteca string.h. O programa 7.4 mostra exemplos de uso de algumas das funes de cadeia. Neste exemplo, o programa primeiro l um nome e em seguida um sobrenome. O programa ir ento concatenar as duas cadeias. Observe que sempre colocado um branco ao nal do nome para separ-lo do sobrenome. Este branco inserido usando a funo strcat, e esta razo das aspas, ou seja, uma cadeia de um caractere apenas. A seguir mostramos um resultado da execuo do programa 7.4. Entre com um nome Ze Ze Entre com um sobrenome Sa Sa Ze Sa Qual caracter? a O caractere aparece na posicao 4

7.4

Declarao de Vetores Multidimensionais

Em C existe a possibilidade de declararmos vetores de mais de uma dimenso. A forma geral da declarao a seguinte: tipo nome [dim1][dim2][dim3]...[dimN]; onde dimI o tamanho da dimenso I. Deve-se tomar cuidado com armazenamento de matrizes multidimensionais, por que a memria necessria para guardar estes dados igual a
sizeof(tipo)*dim1*dim2*dim3*...*dimN

113

Listagem 7.4: Exemplos de funes para cadeias.


#include < string .h > #include < stdio .h > i n t main ( void ) { char c , nome [81] , sobrenome [41]; int i; printf ( " Entre com um nome " ) ; fgets ( nome , 41 , stdin ) ; nome [ strlen ( nome ) -1] = ' \0 '; /* tira cr do fim */ puts ( nome ) ; printf ( " Entre com um sobrenome " ) ; fgets ( sobrenome , 41 , stdin ) ; sobrenome [ strlen ( sobrenome ) -1] = ' \0 '; puts ( sobrenome ) ; strcat ( nome , " " ) ; strcat ( nome , sobrenome ) ; puts ( nome ) ; printf ( " Qual caracter ? " ) ; c = getchar () ; f o r ( i =0; i < strlen ( nome ) ; i ++) { i f ( c == nome [ i ]) { printf ( " O caractere aparece na posicao % d \ n " , i); } } return 0; }

114

Por exemplo a declarao


int matriz[10][20];

dene uma matriz quadrada de 10 linhas por 20 colunas, enquanto o comando


c = 2 * matriz[3][8];

armazena o dobro do elemento que est na quarta linha e nona coluna na varivel c. Observar que o primeiro ndice indica a linha e o segundo a coluna. Lembrar que o nmero da primeira linha (coluna) igual a 0. O programa 7.5 l uma matriz de trs linhas e cinco colunas e imprime os valores lidos.

Listagem 7.5: Leitura de uma matriz.


#define DIML 3 #define DIMC 5 #include < stdio .h > i n t main ( void ) { int i, j; i n t matriz [ DIML ][ DIMC ]; f o r ( i =0; i < DIML ; i ++) f o r ( j =0; j < DIMC ; j ++) scanf ( " % d " , & matriz [ i ][ j ]) ; f o r ( i =0; i < DIML ; i ++) { f o r ( j =0; j < DIMC ; j ++) printf ( " %4 d " , matriz [ i ][ j ]) ; printf ( " \ n " ) ; } return 0; }

A matriz armazenada na memria linha a linha e a Figura 7.4 ilustra esta idia com uma matriz de nmeros inteiros de trs por trs. Estamos assumindo que cada nmero inteiro ocupa quatro bytes, o endereo aponta um byte e a matriz est armazenada a partir do endereo 1000. Uma operao muito comum em matemtica a multiplicao de matrizes. Considere a matriz M 1 com L1 linhas e C 1 colunas e a matriz M 2 com L2 linhas e C 2 colunas. O nmero de colunas C 1 de M 1 deve ser igual ao nmero de linhas L2 de M 2. O elemento M Rij da matriz resultado M R do produto destas matrizes denido pela equao 7.1. O programa 7.6 multiplica duas matrizes de acordo com a frmula.
C1

M Rij =
k=1

M 1ik M 2kj

(7.1)

115

Listagem 7.6: Multiplicao de duas matrizes.


#include < stdio .h > #define #define #define #define L1 L2 C1 C2 3 3 3 3

i n t main ( void ) { f l o a t m1 [ L1 ][ C1 ] , m2 [ L2 ][ C2 ]; f l o a t mr [ L1 ][ C2 ] , m ; int i, j, k; f o r ( i =0; i < L1 ; i ++) { f o r ( j =0; j < C1 ; j ++) { printf ( " %d , % d " , i , j ) ; scanf ( " % f " , & m1 [ i ][ j ]) ; } } f o r ( i =0; i < L2 ; i ++) { f o r ( j =0; j < C2 ; j ++) { printf ( " %d , % d " , i , j ) ; scanf ( " % f " , & m2 [ i ][ j ]) ; } } f o r ( i =0; i < L1 ; i ++) { f o r ( j =0; j < C2 ; j ++) { m = 0; f o r ( k =0; k < C1 ; k ++) { m += m1 [ i ][ k ]* m2 [ k ][ j ]; } mr [ i ][ j ] = m ; } } f o r ( i =0; i < L1 ; i ++ ) { f o r ( j =0; j < C2 ; j ++) { printf ( " %.3 f " , mr [ i ][ j ]) ; } printf ( " \ n " ) ; } return 0; }

116

1000

m[0][0]

1004

m[0][1]

1008

m[0][2]

1012

m[1][0]

1016

m[1][1]

1020

m[1][2]

1024

m[2][0]

1028

m[2][1]

1032

m[2][2]

Figura 7.1: Mapa de memria de uma matriz.

7.5

Vetores de Cadeias de Caracteres

A declarao abaixo mostra uma matriz de cadeias de caracteres com 30 linhas de 80 caracteres. char nome_turma[30][80]; O Programa 7.7 mostra um programa que l uma matriz de nomes e imprime os seus contedos. importante notar que para ler um nome o programa no l um caracter de cada vez mas usa a funo fgets. Como cada linha da matriz uma cadeia de caracteres, o programa l o nome que est na linha i como fgets(nomes[i], DIMC-1, stdin).

7.6

Inicializao de Vetores e Matrizes

Em C possvel inicializar vetores da mesma forma que variveis, isto , no momento em que so declarados. A forma de fazer isto a seguinte: tipo nome[dim] = {lista_de_valores}; onde lista_de_valores um conjunto de valores separados por vrgulas. Por exemplo, a declarao abaixo inicializa um vetor inteiro de cinco posies. int vetor[5] = { 10, 15, 20, 25, 30 };

117

Listagem 7.7: Leitura de um vetor de nomes.


#define DIML 5 #define DIMC 41 #include < stdio .h > #include < string .h > i n t main ( void ) { int i; char nomes [ DIML ][ DIMC ]; f o r ( i =0; i < DIML ; i ++) { printf ( " Entre com a linha % d " , i ) ; fgets ( nomes [ i ] , DIMC -1 , stdin ) ; nomes [ i ][ strlen ( nomes [ i ]) -1] = ' \0 '; } f o r ( i =0; i < DIML ; i ++) { printf ( " O nome % d e " , i ) ; puts ( nomes [ i ]) ; } return 0; }

Observe que nesta declarao necessrio que o tamanho do conjunto seja conhecido antecipadamente. No entanto, tambm possvel inicializar vetores em que no se conhece o seu tamanho. Neste caso, ento, importante que o programador preveja um modo de indicar o m do vetor. O Programa 7.8 mostra os casos ilustrados acima. No primeiro exemplo o tamanho do vetor conhecido e foi denido pela constante DIM. Para descobrir o como parar de processar o vetor, quando desconhecemos seu tamanho, apresentamos duas solues possveis. No primeiro caso a condio de m do vetor o nmero negativo -1. Neste caso uma posio do vetor perdida para armazenar esta condio. No segundo caso usado o operador sizeof para descobrir o tamanho do vetor. Observe que sizeof calcula o tamanho do vetor em bytes e por esta razo necessrio uma diviso pelo tamanho em bytes do tipo de cada elemento. possvel inicializar matrizes multidimensionais e neste caso necessrio especicar todas as dimenses menos a primeira, para que o compilador possa reservar memria de maneira adequada. A primeira dimenso somente especica quantos elementos o vetor ir armazenar e isto lendo a inicializao o compilador pode descobrir. A declarao a seguir ilustra como declarar e inicializar uma matriz de trs linhas por quatro colunas de nmeros reais.
f l o a t mat [][4] = { {1.0 , 2.0 , 3.0 , 4.0} , // linha 0 {8.0 , 9.0 , 7.5 , 6.0} , // linha 1 {0.0 , 0.1 , 0.5 , 0.4} }; // linha 2

118

Listagem 7.8: Exemplos de tratamento de vetores.


#define DIM 5 #include < stdio .h > i n t main () { i n t vetor [ DIM ] = {10 , 15 , 20 , 25 , 30}; i n t vetor1 [] = {10 , 20 , 30 , 40 , 50 , 60 , -1}; i n t vetor2 [] = {3 , 6 , 9 , 12 , 15 , 18 , 21 , 24}; unsigned i n t i , tam ; printf ( " Este programa imprime vetores " ) ; printf ( " contendo numeros inteiros e \ n " ) ; printf ( " que foram inicializado s durante " ) ; printf ( " a sua declaracao .\ n " ) ; /* Impressao dos conjuntos */ printf ( " \ nVetor com tamanho pre - definido \ n ") ; f o r ( i =0; i < DIM ; i ++) printf ( " Elemento % d = % d \ n " , i , vetor [ i ]) ; printf ( " \ nVetor terminando por -1\ n " ) ; f o r ( i =0; vetor1 [ i ] >0; i ++) printf ( " Elemento % d = % d \ n " , i , vetor1 [ i ]) ; tam = s i z e o f ( vetor2 ) printf ( " \ nDescobrindo f o r ( i =0; i < tam ; printf ( " Elemento return 0; } / s i ze of ( int ); o tamanho do Vetor \ n " ) ; i ++) % d = % d \ n " , i , vetor2 [ i ]) ;

119

O Programa 7.9 ilustra a denio de um vetor de cadeia de caracteres, que nada mais do que uma matriz de caracteres. Note que as cadeias so separadas uma das outras por vrgulas.

Listagem 7.9: Exemplos de tratamento de vetores.


#define DIM 5 #include < stdio .h > i n t main ( void ) { char disciplinas [][40] = { " disc 0: Computacao para Informatica " , " disc 1: Banco de Dados I " , " disc 2: Banco de Dados II " , " disc 3: Arquitetura de Computadores I " }; int i; printf ( " Qual a disciplina ? " ) ; scanf ( " % d " , & i ) ; puts ( disciplinas [ i ]) ; return 0; }

120

7.7

Exerccios

7.1: Escreva um programa que leia uma linha de at 80 caracteres do teclado e imprima quantos caracteres foram lidos. 7.2: Escreva um programa que leia uma linha de caracteres do teclado e imprima quantas vezes um caractere, tambm fornecido pelo teclado, aparece nesta linha. O programa tambm deve imprimir em que posies o caractere foi encontrado. 7.3: Escreva um programa que leia uma linha do teclado e em seguida um par de caracteres. O programa deve procurar este par na linha e imprimir em que posies o par foi encontrado. Obs. No use funes da biblioteca de strings do C 7.4: Escreva um programa que leia uma linha do teclado e imprima todas as vogais encontradas no texto e o total de vezes que elas aparecem. Obs: Tamanho mximo da linha deve ser 40 caracteres. 7.5: O imperador romano Csar usava um sistema simples para codicar as mensagens que enviava aos seus generais. Neste sistema cada letra era substituda por trs letras frente no alfabeto. A sua misso mais simples ainda, escrever um programa que converta cada letra, e somente as letras, de uma mensagem de at 80 caracteres para a letra imediatamente posterior. Note que a letra z deve ser convertida para a letra a, e a letra Z para A. 7.6: Escreva um programa que leia uma frase de 80 caracteres e a imprime retirando os espaos em branco. 7.7: Escreva um programa que leia uma linha de caracteres do teclado de tamanho 80. A linha somente contm letras. Divida a linha em blocos de 5 letras. Dentro de cada bloco o seu programa deve trocar a primeira letra pela letra seguinte no alfabeto, a segunda letra por duas letras adiante no alfabeto, a terceira por trs letras adiante e assim at a quinta. Os espaos em branco devem ser retirados da frase. Considere o seguinte exemplo. 1. Frase lida: EVA VIU A UVA 2. Retirada dos espaos em branco: EVAVIUAUVA 3. Diviso em blocos de 5 (blocos indicados por tipos diferentes): EVAVIUAUVA 4. Criptograa: FYDANVCYAF Portanto, o que ser impresso pelo programa : FYDANVCYAF 7.8: Escreva um programa que leia uma matriz de 3x3 que contm somente caracteres 0 e X e procure linhas que contenham somente um dos dois caracteres. O caractere a ser procurado deve ser lido do teclado. 121

7.9: Escreva um programa que leia uma linha de caracteres do teclado e converta o primeiro caractere de cada palavra para maisculas. Assuma que as palavras so sempre separadas por um branco. 7.10: Escreva um programa que leia para um vetor um conjunto de nmeros inteiros. Assuma que o conjunto de nmeros lidos menor que o tamanho do vetor. O programa deve inserir no vetor, em uma posio especicada pelo usurio, um nmero lido do teclado. Assuma que a posio especicada pelo usurio corresponde ao ndice do vetor. 7.11: Faa um programa que inverta uma cadeia de caracteres. O programa deve ler a cadeia com gets e armazen-la invertida em outra cadeia. Use o comando for para varrer a cadeia at o seu nal. 7.12: Escreva um programa que leia um conjunto de nomes para uma matriz e imprima estes nomes em ordem alfabtica. Assuma que os nomes sero lidos somente em letras maisculas. Assuma tambm que os nomes tm no mximo 40 caracteres e sero lidos 10 nomes ao todo. 7.13: Escreva um programa que leia um conjunto N (1 N 1000000000) de nmeros inteiros e imprima os seguintes resultados: media dos nmeros; maior dos nmeros; menor dos nmeros; produto de todos os nmeros. 7.14: Considere que voc digitou o seu nome para o programa mostrado na listagem 7.10. O que ser impresso? Indique o que voc digitou. Justique sua resposta com detalhes.

122

Listagem 7.10: Programa do exercicio 14.


#include < stdio .h > #include < string .h > #define MAX 50 i n t main ( void ) { char texto [ MAX +2] , temp ; i n t tam , i ; gets ( texto ) ; tam = strlen ( texto ) ; f o r ( i = 0; i < tam ; i ++) { temp = texto [ i ]; texto [ i ] = texto [ tam -1 - i ]; texto [ strlen ( texto ) -1 - i ] = temp ; } puts ( texto ) ; return 0; }

123

Captulo 8

Funes
8.1 Introduo

Em C, diferentemente de outras linguagens como Pascal, todas as aes ocorrem dentro de funes. Na linguagem C no h conceito de um programa principal, o que existe uma funo chamada main que sempre a primeira a ser executada. Um programa pode ser escrito apenas com a funo main e mais as funes existentes nas bibliotecas da linguagem C. No entanto o uso de funes pode facilitar o desenvolvimento de programas de diversas maneiras. Em primeiro lugar temos as vantagens do reuso de cdigo desenvolvido por outros programadores. As funes de entrada e sada so o exemplo mais direto deste reuso. Em C no existem estes tipos de comandos como na maioria das linguagens. Programas escritos em C usam funes de entrada e sada escritas e testadas por outros programadores. Este reuso de cdigo apresenta vrias vantagens. Primeiro, diminui o tempo de desenvolvimento do programas. Em segundo lugar, como estas funes foram testadas por diversos usurios, a quantidade de erros bastante reduzida. Estes fatores contribuem para a reduo dos custos de desenvolvimento dos projetos. Uma outra vantagem do uso de funes e a maior facilidade na diviso do trabalho necessrio para construir um aplicativo. Funes podem ser desenvolvidas por programadores trabalhando independentemente. Para isto basta que alguns acordos sejam feitos entre os programadores que iro programar a funo e os que iro us-las. Estes acordos precisam denir que parmetros a funo ir receber, que resultados ir fornecer e que operaes ela deve realizar sobre estes parmetros para obter os resultados necessrios. Esta diviso do trabalho concorre para acelerar o desenvolvimento dos programas e na reduo dos custos deste desenvolvimento. A diviso de um programa em funes tambm permite que os testes do sistema completo sejam feitos mais facilmente e com mais garantia de correo. Os programadores podem testar suas funes separadamente em testes menos complexos, j que as funes normalmente so simples e tm requisitos menos complicados de serem avaliados. Isto permite que muitos erros do sistema completo possam ser retirados antes que ele esteja completo. Normalmente testar um programa complexo requer testes complexos. 124

Mesmo quando um programa desenvolvido por um nico programador a sua diviso em funes traz vantagens, por dividir um trabalho complexo em diversas fatias menores permitindo ao programador se concentrar a cada vez em problemas mais simples.

8.2

Forma Geral

A forma geral de uma funo em C a seguinte:


tipo nome ( tipo nome1 , tipo nome2 , ... , tipo nomeN ) { declarao das variveis corpo da funo }

Uma funo recebe uma lista de argumentos (nome1, nome2, ..., nomeN), executa comandos com estes argumentos e pode retornar ou no um resultado para a funo que chamou esta funo. A lista de argumentos, tambm chamados de parmetros, uma lista, separada por vrgulas, de variveis com seus tipos associados. No possvel usar uma nica denio de tipo para vrias variveis. A lista de argumentos pode ser vazia, ou seja, a funo no recebe nenhum argumento. O nome da funo pode ser qualquer identicador vlido. O tipo que aparece antes do nome da funo especica o tipo do resultado que ser devolvido ao nal da execuo da funo. Caso nenhum tipo seja especicado o compilador assume que um tipo inteiro retornado. O tipo void pode ser usado para declarar funes que no retornam valor algum. H basicamente duas maneiras de terminar a execuo de uma funo. Normalmente usa-se o comando return para retornar o resultado da funo. Portanto, quando o comando
return expresso;

for executado, o valor da expresso devolvido para a funo que chamou. Quando no h valor para retornar o comando return no precisa ser usado e a funo termina quando a chave que indica o trmino do corpo da funo atingido. Os parmetros so valores que a funo recebe para realizar as tarefas para as quais foi programada. Por exemplo, uma funo que calcule a raiz quadrada de um nmero do tipo oat , deve declarar como parmetro uma varivel deste tipo para receber o valor. importante notar que diferentemente de declaraes de variveis onde podemos associar vrios nomes de variveis a uma declarao como em
int a, dia, mes, i;

na lista de parmetros necessrio associar um tipo a cada varivel como no exemplo a seguir:
oat media (oat n1, oat n2, oat n3);

Neste exemplo, uma funo chamada media que do tipo oat , isto retorna um resultado oat , recebe trs argumentos (n1, n2, n3) tambm do tipo oat . 125

Um ponto importante como usar a funo. Suponha que uma determinada funo, A, deseje usar uma outra funo, B. A funo A deve colocar no local desejado o nome da funo (B) e a lista de valores que deseja passar. Por exemplo, um programador que deseje usar a funo media em seu programa para calcular a mdia de trs valores, nota1, nota2 e nota3, deve escrever no local onde quer que a mdia seja calculada o seguinte comando:
resultado = media(nota1, nota2, nota3);

onde resultado a varivel que vai receber a mdia calculada. importante notar que o nome da funo pode aparecer em qualquer lugar onde o nome de uma varivel apareceria. Alm disso os tipos e o nmero de parmetros que aparecem na declarao da funo e na sua chamada devem estar na mesma ordem e ser tipos equivalentes. Se os tipos so incompatveis, o compilador no gera um erro, mas podem ser gerados avisos na compilao e resultados estranhos. Outro ponto importante a ser notado e que ser detalhado mais adiante que os nomes das variveis nos programas que usam a funo media podem ser diferentes dos nomes usados na denio da funo.

8.3

Prottipos de Funes

O padro ANSI estendeu a declarao da funo para permitir que o compilador faa uma vericao mais rgida da compatibilidade entre os tipos que a funo espera receber e queles que so fornecidos. Prottipos de funes ajudam a detectar erros antes que eles ocorram, impedindo que funes sejam chamadas com argumentos inconsistentes. A forma geral de denio de um prottipo a seguinte:
tipo nome (tipo nome1, tipo nome2, ..., tipo nomeN);

O exemplo 8.1 mostra a declarao de uma funo e seu prottipo. Tambm possvel declarar um prottipo sem dar os nomes das variveis somente os tipos das funes. No exemplo 8.1 o prottipo da funo soma pode ser declarada da seguinte maneira
int soma (int, int)

8.4

Escopo de Variveis

Variveis podem ser denidas para serem usadas somente dentro de uma funo particular, ou pode ocorrer que variveis precisem ser acessveis diversas funes diferentes. Por esta razo, temos que apresentar os locais onde as variveis de um programa podem ser denidas e a partir destes locais podermos inferir onde elas estaro disponveis. As variveis podem ser declaradas basicamente em trs lugares: dentro de funes, fora de todas as funes, 126

Listagem 8.1: Exemplo de prottipos.


#include < stdio .h > /* Prototipo da funcao */ i n t soma ( i n t a , i n t b ) ; /* Funcao Principal */ i n t main () { i n t a =5 , b =9; printf ( " % d \ n " , soma (a , b ) ) ; return 0; } /* Definicao da funcao */ i n t soma ( i n t a , i n t b ) { return a + b ; }

na lista de parmetros das funes. As variveis denidas dentro das funes so chamadas de variveis locais, as que aparecem fora de todas as funes chamamos de variveis globais e aquelas que aparecem na lista de parmetros so os parmetros formais. importante notar que em C todas as funes esto no mesmo nvel, por esta razo no possvel denir uma funo dentro de outra funo.

8.4.1

Variveis Locais

As variveis locais so aquelas declaradas dentro de uma funo ou um bloco de comandos. Elas passam a existir quando do incio da execuo do bloco de comandos ou funo onde foram denidas e so destrudas ao nal da execuo do bloco. Uma varivel local s pode ser referenciada, ou seja usada, dentro da funo (ou bloco) onde foi declarada. Outro ponto muito importante que como as variveis locais deixam de existir ao nal da execuo da funo (ou bloco), elas so invisveis para outras funes do mesmo programa. O cdigo que dene uma funo e os seus dados so particulares da funo (do bloco). No programa 8.2 podemos ver o uso de variveis locais. A varivel i denida em cada uma das funes do programa (pares, impares). Os valores da varivel no podem ser acessados a partir de nenhuma outra funo e as modicaes feitas dentro da funo somente valem enquanto a funo est sendo executada. Alguns autores usam o termo variveis automticas para se referir as variveis locais. Em C existe a palavra chave auto que pode ser usada para declarar que variveis pertencem classe de armazenamento padro. No entanto, como todas as variveis locais so por denio automticas raramente se usa esta palavra chave.

127

Listagem 8.2: Exemplos de variveis locais.


#include < stdio .h > void pares ( void ) { int i; f o r ( i = 2; i <= 10; i += 2) { printf ( " % d : " , i ) ; } } void impares ( void ) { int i; f o r ( i = 3; i <= 11; i += 2) { printf ( " % d : " , i ) ; } } i n t main ( i n t argc , char * argv []) { pares () ; printf ( " \ n " ) ; impares () ; return 0; }

Observe que um bloco de comandos se inicia em um { e termina em um }. O bloco de comandos, dentro do qual mais comumente se dene uma varivel a funo. Todas as variveis que sero usadas dentro de um bloco de comandos precisam ser declaradas antes do primeiro comando do bloco. Declaraes de variveis, incluindo sua inicializao, podem vir logo aps o abre chaves que inicia um bloco de comandos, no somente o que comea uma funo. O programa 8.3 ilustra este tipo de declarao.

Listagem 8.3: Denio de varivel dentro de um bloco.


#include < stdio .h > i n t main () { int i; f o r ( i =0; i <10; i ++) { i n t t = 2; printf ( " % d \ n " , i * t ) ; } return 0; }

Existem algumas vantagens em se declarar variveis dentro de blocos. Como as variveis somente existem durante a execuo do bloco, o programa pode ocupar menos espao de memria. Por exemplo, se a execuo do bloco for condicional a varivel pode nem ser alocada. Outra vantagem que como a 128

varivel somente existe dentro do bloco, pode-se controlar melhor o uso da varivel, evitando erros de uso indevido da varivel.

8.4.2

Variveis Globais

As variveis globais so denidas fora de qualquer funo e so portanto disponveis para qualquer funo. Este tipo de varivel pode servir como uma canal de comunicao entre funes, uma maneira de transferir valores entre elas. Por exemplo, se duas funes tem de partilhar dados, mais uma no chama a outra, uma varivel global tem de ser usada. O programa 8.4 ilustra este tipo de declarao. O resultado da execuo deste programa o seguinte: Funcao soma1: i = 1 Funcao sub1: i = 9 Funcao main: i = 1 Observe que a varivel global i recebe o valor 0 no incio da funo main. A funo soma1 ao executar um comando que aumenta o valor de i em uma unidade est aumentando a varivel global. Em seguida vemos que a funo sub1 dene uma varivel local tambm chamada i e, portanto, a alterao feita por esta funo somente modica esta varivel. Finalmente, a funo main imprime o valor nal da varivel global.

Listagem 8.4: Denio de varivel global.


#include < stdio .h > int i; void soma1 ( void ) { i += 1; printf ( " Funcao soma1 : i = % d \ n " , i ) ; } void sub1 ( void ) { i n t i = 10; i -= 1; printf ( " Funcao sub1 : i = % d \ n " , i ) ; } i n t main ( i n t argc , char * argv []) { i = 0; soma1 () ; sub1 () ; printf ( " Funcao main : i = % d \ n " , i ) ; return 0; }

129

8.5

Parmetros Formais

As variveis que aparecem na lista de parmetros da funo so chamadas de parmetros formais da funo. Eles so criados no incio da execuo da funo e destrudos no nal. Parmetros so valores que as funes recebem da funo que a chamou. Portanto, os parmetros permitem que uma funo passe valores para outra. Normalmente os parmetros so inicializados durante a chamada da funo, pois para isto foram criados. No entanto, as variveis que atuam como parmetros so iguais a todas as outras e podem ser modicadas, operadas, etc, sem nenhuma restrio. Parmetros podem ser passados para funes de duas maneiras: passagem por valor ou passagem por referncia.

8.5.1

Passagem de Parmetros por Valor

Na passagem por valor uma cpia do valor do argumento passado para a funo. Neste caso a funo que recebe este valor, ao fazer modicaes no parmetro, no estar alterando o valor original que somente existe na funo que chamou. O exemplo 8.5 mostra o uso de passagem de parmetros por valor. Observe que as funo Eleva recebe dois parmetros (a,b) e opera usando os valores recebidos. O resultado da funo retornado por meio da varivel local res.

Listagem 8.5: Exemplo de passagem por valor.


#include < stdio .h > #include < stdlib .h > f l o a t Eleva ( f l o a t a , i n t b ) { f l o a t res = 1.0; f o r ( ; b >0; b - -) res *= a ; return res ; } i n t main () { f l o a t numero ; i n t potencia ; char linha [80]; puts ( " Entre com um numero " ) ; gets ( linha ) ; numero = atof ( linha ) ; puts ( " Entre com a potencia " ) ; gets ( linha ) ; potencia = atoi ( linha ) ; printf ( " \ n % f Elevado a % d e igual a % f \ n " , numero , potencia , Eleva ( numero , potencia ) ) ; return 0; }

130

Para ilustrar o fato de que somente o valor passado vamos usar o exemplo 8.6. Neste programa as variveis a e b recebem os valores 10 e 20 respectivamente. Na funo trocar estes valores so recebidos e so trocados localmente. Aps o retorno da funo, o programa imprime os valores originais das variveis, j que estes no sofreram nenhuma alterao. O resultado da execuo deste programa o seguinte: a = 10, b = 20

Listagem 8.6: Uso indevido de variveis locais.


#include < stdio .h > void trocar ( i n t a , i n t b ) { i n t temp ; temp = a ; a = b ; b = temp ; } i n t main ( i n t argc , char * argv []) { i n t a = 10 , b = 20; trocar (a , b ) ; printf ( " a = %d , b = % d \ n " , a , b ) ; return 0; }

8.5.2

Passagem de Parmetros por Referncia

Na passagem por referncia o que passado para a funo o endereo do parmetro e, portanto, a funo que recebe pode, atravs do endereo, modicar o valor do argumento diretamente na funo que chamou. Para a passagem de parmetros por referncia necessrio o uso de ponteiros. Este assunto ser discutido no prximo captulo e portanto, por agora, usaremos somente funes com passagem por valor.

8.5.3

Passagem de Vetores e Matrizes

Matrizes so um caso especial e exceo a regra que parmetros so passados sempre por valor. Como veremos mais adiante, o nome de um vetor corresponde ao endereo do primeiro elemento do array, Quando um vetor passado como parmetro, apenas o endereo do primeiro elemento passado. Existem basicamente trs maneiras de declarar um vetor como um parmetro de uma funo. Na primeira ele declarado como tem sido apresentado em todos os exemplos at agora. O exemplo 8.7 mostra um programa que usa uma funo para descobrir quantas vezes um caractere ocorre em um vetor. Observe que na denio da funo a dimenso do vetor foi declarada explicitamente. Uma outra maneira, leva em conta que apenas o endereo do vetor passado. Neste modo o parmetro declarado como um vetor sem dimenso. Isto 131

Listagem 8.7: Passagem de vetor com dimenses.


#include < stdio .h > #include < conio .h > #define DIM 80 char conta ( char v [] , char c ) ; i n t main () { char c , linha [ DIM ]; i n t maiusculas [26] , minusculas [26]; puts ( " Entre com uma linha " ) ; gets ( linha ) ; f o r ( c = 'a '; c <= 'z '; c ++) minusculas [c - 'a '] = conta ( linha , c ) ; f o r ( c = 'A '; c <= 'Z '; c ++) maiusculas [c - 'A '] = conta ( linha , c ) ; f o r ( c = 'a '; c <= 'z '; c ++) i f ( minusculas [c - 'a ' ]) printf ( " % c apareceu % d vezes \ n " , c , minusculas [ c - 'a ' ]) ; f o r ( c = 'A '; c <= 'Z '; c ++) i f ( maiusculas [c - 'A ' ]) printf ( " % c apareceu % d vezes \ n " , c , maiusculas [ c - 'A ' ]) ; return 0; } char conta ( char v [ DIM ] , char c ) { i n t i =0 , vezes =0; while ( v [ i ] != ' \0 ' ) i f ( v [ i ++] == c ) vezes ++; return vezes ; }

132

perfeitamente possvel porque a funo somente precisa receber o endereo onde se encontra o vetor. Alm disso C no confere limites de vetores e portanto a funo precisa do endereo inicial do vetor e uma maneira de descobrir o nal do vetor. Esta maneira pode ser, por exemplo, uma constante, ou o caractere \0 em um vetor de caracteres. O exemplo 8.8 mostra este modo de passar vetores com um programa que inverte o contedo de um vetor. A terceira maneira de passagem de parmetros implica no uso de ponteiros, o que somente iremos ver no prximo captulo.

8.6

O Comando return

O comando return usado para retornar o valor calculado para a funo que chamou. Qualquer expresso pode aparecer no comando, que tem a a seguinte forma geral:
return expresso

A funo que chamou livre para ignorar o valor retornado. Alm disso a funo pode no conter o comando e portanto nenhum valor retornado e neste caso a funo termina quando o ltimo comando da funo executado. Quando o comando return no existe o valor de retorno considerado indenido. As funes que no retornam valores devem ser declaradas como do tipo void. importante observar que funes que so declaradas com um tipo vlido podem ser includas em qualquer expresso vlida em C.

8.7

Recurso

Funes em C podem ser usadas recursivamente, isto uma funo pode chamar a si mesmo. como se procurssemos no dicionrio a denio da palavra recurso e encontrssemos o seguinte texto: recurso: s.f. Veja a denio em recurso Um exemplo simples de funo que pode ser escrita com chamadas recursivas o fatorial de um nmero inteiro. O fatorial de um nmero, sem recurso, denido como n! = n (n 1) (n 2) 2 1 A partir desta denio podemos escrever a funo fatorial como
unsigned long i n t fat ( unsigned long i n t num ) { unsigned long i n t fato =1 , i ; f o r ( i = num ; i >1; i - -) fato = fato * i ; return fato ; }

133

Listagem 8.8: Passagem de vetores sem dimenses.


#include < stdio .h > #include < conio .h > #define DIM 6 void Le_vetor ( i n t v [] , i n t tam ) ; void Imprime_vetor ( i n t v [] , i n t tam ) ; void Inverte_vetor ( i n t v [] , i n t tam ) ; i n t main () { i n t v [ DIM ]; Le_vetor (v , DIM ) ; Imprime_vetor (v , DIM ) ; Inverte_vetor (v , DIM ) ; Imprime_vetor (v , DIM ) ; return 0; } void Le_vetor ( i n t v [] , i n t tam ) { int i; f o r ( i = 0; i < tam ; i ++) { printf ( " % d = ? " , i ) ; scanf ( " % d " , & v [ i ]) ; } } void Imprime_vetor ( i n t v [] , i n t tam ) { int i; f o r ( i = 0; i < tam ; i ++) printf ( " % d = % d \ n " , i , v [ i ]) ; } void Inverte_vetor ( i n t v [] , i n t tam ) { i n t i , temp ; f o r ( i = 0; i < tam /2; i ++) { temp = v [ i ]; v [ i ] = v [ tam -i -1]; v [ tam -i -1] = temp ; } }

134

Alternativamente, o fatorial pode ser denido como o produto deste nmero pelo fatorial de seu predecessor, ou seja n! = n (n 1)! Deste modo podemos escrever uma funo recursiva em que cada chamada da funo que calcula o fatorial chama a prpria funo fatorial. O exemplo, mostrado a seguir, mostra como a funo pode ser escrita recursivamente.
unsigned long i n t fat ( unsigned long i n t num ) { i f ( num == 0) return 1; else return num * fat ( num -1) ; }

Quando a funo fatorial recursiva chamada, primeiro vericado se o nmero recebido como parmetro vale 0 e neste caso a funo retorna o valor 1. No caso contrrio ela devolve o valor da expresso num * fat(num-1), ou seja o produto do nmero pelo valor do fatorial do nmero predecessor. Ou seja para retornar um valor a funo precisa chamar ela mesma passando como parmetro o valor do nmero menos 1. Este processo continua se repetindo at que o valor passado igual a 0, o que indica o nal da recurso. Neste ponto o processo se reverte e as chamadas comeam a ser respondidas. Um ponto importante que toda funo recursiva deve prever cuidadosamente como o processo de recurso deve ser interrompido. No caso da funo fat o processo interrompido quando o valor do nmero passado como parmetro vale 0. Se este teste no tivesse sido includo na funo as chamadas continuariam indenidamente com valores negativos cada vez menores sendo passados como parmetro. Quando uma funo chama a si mesmo recursivamente ela recebe um conjunto novo de variveis na pilha que usada para transferncia de valores entre funes. importante notar que recurso no traz obrigatoriamente economia de memria porque os valores sendo processados tem de ser mantidos em pilhas. Nem ser mais rpido, e as vezes pode ser at mais lento porque temos o custo de chamada as funes. As principais vantagens da recurso so cdigos mais compactos e provavelmente mais fceis de serem lidos. Um outro exemplo simples de funo que pode ser resolvida por recurso xn , assumindo que n 0. Esta funo pode escrita na sua forma recursiva como xn = x x(n1) que nos leva a escrever a funo da maneira mostrada no exemplo 8.9. Na funo consideramos que x do tipo oat .

8.8

Argumentos - argc e argv

A funo main como todas as funes podem ter parmetros. Como a funo main sempre a primeira a ser executada, os parmetros que ela recebe so 135

Listagem 8.9: Funo recursiva para calcular xn .


f l o a t Elevar ( f l o a t x , i n t n ) { i f ( n <= 1) { return x ; } else { return x * Elevar (x , b -1) ; } }

fornecidos pela linha de comando ou pelo programa que iniciou a sua execuo. No caso da funo main so usados dois argumentos especiais int argc e char **argv. O primeiro argumento, argc, uma varivel inteira que indica quantos argumentos foram fornecidos para a funo. Observar que argc vale sempre pelo menos 1, porque o nome do programa sempre o primeiro argumento fornecido ao programa. A partir do segundo argumento em diante que aparecem os outros argumentos. O outro parmetro um vetor de cadeias de caracteres, e portanto, caso sejam fornecidos nmeros, estes devem ser convertidos para o formato requerido. Cada um dos argumentos do programa um elemento deste vetor. A primeira linha da funo main pode ter a seguinte forma
void main (int argc, char *argv[])

O programa exemplo 8.10 calcula o fatorial dos nmeros fornecidos como argumentos. Os nomes argc e argv so comumente usados mas o programador livre para escolher os nomes mais apropriados.

136

Listagem 8.10: Uso de argc e argv.


#include < stdio .h > #include < stdlib .h > unsigned long i n t fat ( unsigned long i n t num ) { i f ( num == 0) return 1; else return num * fat ( num -1) ; } i n t main ( i n t argc , char * argv []) { unsigned long i n t numero , fatorial , i ; i f ( argc < 2) { printf ( " Para rodar : % s num1 num2 ... .\ n " , argv [0]) ; return 1; } f o r ( i =1; i < argc ; i ++) { numero = ( unsigned long i n t ) ( atoi ( argv [ i ]) ) ; fatorial = fat ( numero ) ; printf ( " O fatorial de % lu vale % lu .\ n " , numero , fatorial ) ; } return 0; }

137

8.9

Exerccios

8.1: Escrever um programa que declare, inicialize e imprima um vetor de 10 inteiros. O vetor deve conter os 10 primeiros mltiplos de 5. A inicializao do vetor e a sua impresso devem ser feitas por funes. 8.2: Escreva um programa para declarar um vetor de caracteres de tamanho 26 e imprimir o seu contedo. O vetor deve ser inicializado com as letras minsculas do alfabeto. A inicializao do vetor e a sua impresso devem ser feitas por funes. 8.3: Escreva um programa que armazene em uma matriz trs nomes de pessoas e em seguida os imprima. Assuma que o tamanho mximo de cada nome 40 caracteres. Neste programa a leitura dos nomes dever ser feita por uma funo e a impresso dos nomes por outra. 8.4: Escreva um programa que imprima o cdigo ASCII de todos os caracteres, das seguintes maneiras: 1. caractere a caractere, a escolha do usurio; 2. a tabela inteira, a partir de um determinado valor decimal. Cada item deste exerccio deve corresponder a uma funo. 8.5: Escreva um programa que crie uma tabela de temperaturas Celsius - Fahrenheit. O programa deve usar uma funo que converta de Celsius para Fahrenheit. A tabela deve iniciar na temperatura 0 graus Celsius e terminar na temperatura 100 graus Celsius. 8.6: Escreva um programa, usando funes, que gere um vetor a partir de uma matriz. Cada elemento do vetor igual a soma dos elementos de uma das linhas da matriz. Considere que a matriz tenha tamanho 10 por 10. 8.7: Escreva um programa usando recursividade para gerar a seqncia do Fibonacci. A seqncia de Fibonacci denida como: f (0) = f (1) = f (n) = 0 1 f (n 1) + f (n 2)

O programa deve ler um numero e exibir o valor dele na seqncia de Fibonacci. Exemplos de entrada e sada do programa so mostrados abaixo. Entre com um numero inteiro: 0 Fibonacci (0) = 0 Entre com um numero inteiro: 1 Fibonacci (1) = 1 Entre com um numero inteiro: 2 Fibonacci (2) = 1 Entre com um numero inteiro: 3 Fibonacci (3) = 2 138

Entre com um numero inteiro: Fibonacci (6) = 8

8.8: O que o programa mostrado na listagem 8.11 imprime, caso sejam fornecidos os valores 48 e 256. Justique a sua resposta. Listagem 8.11: Programa do exerccio 8.
#include < stdio .h > #define MAX 1000 i n t abc ( i n t x , i n t y ) { printf ( " % d % d \ n " , x , y ) ; i f ( y == 0) { return x ; } else { return abc (y , x % y ) ; } } i n t main ( void ) { int a, b; scanf ( " % d % d " , &a , & b ) ; printf ( " % d \ n " , abc (a , b ) ) ; return 0; }

8.9: O programa mostrado na listagem 8.12 converte cadeias de caracteres contendo nmeros na base 16 para a base 10. Neste programa est faltando um trecho. A sua tarefa e completar o trecho que est faltando. Listagem 8.12: Programa do problema 9.
#include < stdio .h > #include < string .h > #include < stdlib .h > i n t fromHexatoD ec im a l ( char c []) ; i n t toNumero ( char ) ; char toLower ( char ) ; i n t main ( void ) { char numeroC [80]; fgets ( numeroC , 80 , stdin ) ; while (! feof ( stdin ) ) { numeroC [ strlen ( numeroC ) -1] = ' \0 '; printf ( " % s = % d \ n " , numeroC , fromHexatoD ec i ma l ( numeroC ) ) ; fgets ( numeroC , 80 , stdin ) ; } return 0;

139

} i n t fromHexatoD ec im a l ( char numeroC []) { /* Aqui falta codigo */ } char toLower ( char c ) { i f ( c >= 'A ' && c <= 'Z ') { c = c - 'A ' + 'a '; } return c ; } i n t toNumero ( char c ) { i n t resultado ; i f ( c >= '0 ' && c <= '9 ') { resultado = c - '0 '; } e l s e i f ( toLower ( c ) >= 'a ' && toLower ( c ) <= 'f ') { resultado = toLower ( c ) - 'a ' + 10; } return resultado ; }

140

Captulo 9

Ponteiros
9.1 Introduo

Ponteiros so usados em situaes em que necessrio conhecer o endereo onde est armazenada a varivel e no o seu contedo. Um ponteiro uma varivel que contm um endereo de uma posio de memria e no o contedo da posio. A memria de um computador pode ser vista como uma seqncia de bytes cada um com seu prprio e nico endereo. No h dois bytes com o mesmo endereo. O primeiro endereo sempre 0 e o ltimo geralmente uma potncia de 2. Por exemplo um computador com memria igual a 512 Mbytes tem 512x1024x1024 bytes. A Figura 9.1 mostra o mapa de um trecho de memria que contm duas variveis inteiras (num, res) ocupando 4 bytes cada uma e mais um ponteiro (pint), que tambm ocupa 4 bytes. Observar que os endereos esto pulando de quatro em quatro bytes devido ao espao que cada um destas variveis ocupa.
0 4 8 12 16 10 120 num res *pint

Figura 9.1: Mapa de memria com duas variveis e ponteiro. Ponteiros so importantes, por exemplo, quando se deseja que uma funo retorne mais de um valor. Neste caso uma soluo a funo receber como argumentos no os valores dos parmetros mas sim ponteiros que apontem para seus endereos. Assim esta funo pode modicar diretamente os contedos 141

destas variveis, que aps o m da funo estaro disponveis para a funo que chamou. Neste caso os argumentos podem funcionar como entrada e sada de dados da funo. Uma outra aplicao importante de ponteiros apontar para reas de memria que devem ser gerenciadas durante a execuo do programa. Com ponteiros, possvel reservar as posies de memria necessrias para armazenamento destas reas somente quando for necessrio e no quando as variveis so declaradas. Neste esquema o programador pode reservar o nmero exato de posies que o programa requer. A Figura 9.2 ilustra como um ponteiro faz referncia para uma rea de memria. Na gura a varivel ponteiro pi aponta para a rea de memria que contm um vetor de 10 inteiros. Com ponteiros, o programador precisa, no incio, denir a varivel ponteiro e seu tipo. Durante a execuo do programa, aps descobrir o tamanho do vetor, reserva a rea necessria para guardar os dados. Observe a diferena do que ocorre quando se usa vetores de tamanho xo. Neste caso a denio do tamanho do vetor dada na declarao do vetor e mantida at o nal da execuo do programa.
0 4 8 1000 pi ponteiro para vetor

1000

120 Vetor de 10 inteiros

1036

97

Figura 9.2: Ponteiro apontando para rea de memria contendo vetor.

9.2
9.2.1

Operaes com Ponteiros


Declarao de Ponteiros

Antes de serem usados os ponteiros, como as variveis, precisam ser declarados. A forma geral da declarao de um ponteiro a seguinte: tipo *nome; Onde tipo qualquer tipo vlido em C e nome o nome da varivel ponteiro. Por exemplo:

142

i n t * res ; /* ponteiro para inteiro */ f l o a t * div ; /* ponteiro para ponto flutuante */

Como as variveis, os ponteiros devem ser inicializados antes de serem usados. Esta inicializao pode ser feita na declarao ou atravs de uma atribuio. Aps a declarao o que temos um espao na memria reservado para armazenamento de endereos. O valor inicial da memria indenido como acontece com variveis. A Figura 9.3 ilustra esta situao. Um ponteiro pode ser inicializado com um endereo ou com o valor NULL. O valor NULL, que equivalente a 0, uma constante denida no arquivo <stdio.h> e signica que o ponteiro no aponta para lugar nenhum. A atribuio de inteiros a ponteiros no faz sentido a no ser em aplicaes muito especiais e o nico valor inteiro que se pode atribuir a um ponteiro o 0. Este tipo de atribuio no faz sentido porque na maioria das aplicaes o sistema operacional que aloca e gerencia a posio dos programas na memria e, portanto, o usurio no tem controle sobre estes endereos.
996

1000

endereo indefinido

*res

1004

endereo indefinido

*div

Figura 9.3: Declarao de ponteiros.

9.2.2

Os Operadores Especiais para Ponteiros

Existem dois operadores especiais para ponteiros: * e &. Os dois operadores so unrios, isto requerem somente um operando. O operador & devolve o endereo de memria do seu operando. Considere a Figura 9.1. Aps a execuo da instruo
pint = &num; /*o endereco de num e carregado em pint */

a varivel ponteiro pint termina com o valor 4, como est mostrado na Figura 9.4. Lembre-se que o valor 4 no tem sentido prtico na maioria das aplicaes. O fato importante que o ponteiro pint passou a apontar para a varivel num. O operador * o complemento de &. O operador * devolve o valor da varivel localizada no endereo apontado pelo ponteiro. Por exemplo, considere que o 143

0 4 8 12 16 10 120 4 num res *pint

Figura 9.4: Atribuio de endereo de uma varivel a um ponteiro.

comando res = *pint; foi executado logo aps pint = &num;. Isto signica que a varivel res recebe o valor apontado por pint, ou seja a varivel res recebe o valor 10, como est mostrado na Figura 9.5.
0 4 8 12 16 10 120 10 4 num res = *pint *pint

Figura 9.5: Uso de um ponteiro para copiar valor de uma varivel. Estes operadores no devem ser confundidos com os j estudados em captulos anteriores. O operador * para ponteiros no tem nada a ver com o operador multiplicao *. O operador ponteiro * unrio e, como o operador &, tem precedncia maior que do que todos os operadores aritmticos.

9.2.3

Atribuio de Ponteiros

Da mesma maneira que ocorre com uma varivel comum, o contedo de um ponteiro pode ser passado para outro ponteiro do mesmo tipo. Por exemplo, uma varivel ponteiro declarada como apontador de dados inteiros deve sempre apontar para dados deste tipo. Observar que em C possvel atribuir qualquer endereo a uma varivel ponteiro. Deste modo possvel atribuir o endereo de uma varivel do tipo oat a um ponteiro do tipo int. No entanto, o programa no ir funcionar da maneira correta. O programa 9.1 mostra exemplos de atribuies de ponteiros. Neste exemplo o endereo do terceiro elemento do 144

vetor v carregado em p1 e o endereo da varivel i carregado em p2. A Figura 9.6 a situao da memria aps estas operaes. Alm disso no nal o endereo apontado por p1 carregado em p2. Os comandos printf imprimem os valores apontados pelos ponteiros respectivos, mostrando os seguintes valores: 30 100 30

Listagem 9.1: Exemplo de atribuio de ponteiros.


#include < stdio .h > i n t main ( void ) { i n t vetor [] = { 10 , 20 , 30 , 40 , 50 }; i n t * p1 , * p2 ; i n t i = 100; p1 = & vetor [2]; printf ( " % d \ n " , * p1 ) ; p2 = & i ; printf ( " % d \ n " , * p2 ) ; p2 = p1 ; printf ( " % d \ n " , * p2 ) ; return 0; }

0 4 8 12 16 20 24 28 32 10 20 30 40 50 12 32 100 v[0] v[1] v[2] v[3] v[4] *p1 *p2 p2 = &i; i p1 = &v[2];

Figura 9.6: Exemplos de atribuies de ponteiros.

145

9.2.4

Incrementando e Decrementando Ponteiros

O exemplo 9.2 mostra que operaes de incremento e decremento podem ser aplicadas em operandos. O primeiro printf imprime 30, que o elemento de ndice igual a 2 no vetor vetor. Aps o incremento do ponteiro o segundo printf imprime 40 e o mesmo acontece com o terceiro printf que imprime 50.

Listagem 9.2: Exemplos de operaes com ponteiros.


i n t main ( void ) { i n t vetor [] = { 10 , 20 , 30 , 40 , 50 }; i n t * p1 ; p1 = & vetor [2]; printf ( " % d \ n " , * p1 ) ; p1 ++; printf ( " % d \ n " , * p1 ) ; p1 = p1 + 1; printf ( " % d \ n " , * p1 ) ; return 0; }

Pode parecer estranho que um ponteiro para um nmero inteiro, que armazenado em quatro bytes, seja incrementado por um e passe para apontar para o prximo nmero inteiro. A primeira vista, para que passasse a apontar para o prximo endereo, seria necessrio aumentar o endereo em quatro. Ou seja, sempre que um ponteiro incrementado (decrementado) ele passa a apontar para a posio do elemento seguinte (anterior). O compilador interpreta o comando p1++ como: passe a apontar para o prximo nmero inteiro e, portanto, aumenta o endereo do nmero de bytes correto. Este ajuste feito de acordo com o tipo do operando que o ponteiro est apontando. Do mesmo modo, somar trs a um ponteiro faz com que ele passe apontar para o terceiro elemento aps o atual. Portanto, um incremento em um ponteiro que aponta para um valor que armazenado em n bytes faz que n seja somado ao endereo. ento possvel somar-se e subtrair-se inteiros de ponteiros. A operao abaixo faz com que o ponteiro p passe a apontar para o terceiro elemento aps o atual.
p = p + 3;

Tambm possvel usar-se o seguinte comando


*(p+1)=10;

Este comando armazena o valor 10 na posio seguinte quela apontada por


p. A operao realizada nos seguintes passos:

1. A expresso p+1 calculada e o seu resultado aponta para o prximo endereo de dado inteiro; 2. A expresso do lado direito do sinal de atribuio, calculada e fornece como resultado o valor 10; 3. Este resultado atribudo ao endereo calculado no primeiro passo. 146

A diferena entre ponteiros fornece quantos elementos do tipo do ponteiro existem entre os dois ponteiros. No exemplo 9.3 impresso o valor 2.

Listagem 9.3: Exemplo de subtrao de ponteiros.


#include < stdio .h > i n t main ( void ) { f l o a t vetor [] = { 1.0 , 2.0 , 3.0 , 4.0 , 5.0 }; f l o a t * p1 , * p2 ; p1 = & vetor [2]; /* endereco do terceiro elemento */ p2 = vetor ; /* endereco do primeiro elemento */ printf ( " Diferenca entre ponteiros % d \ n " , p1 - p2 ) ; return 0; }

No possvel multiplicar ou dividir ponteiros, e no se pode adicionar ou subtrair o tipo oat ou o tipo double a ponteiros.

9.2.5

Comparao de Ponteiros

possvel comparar ponteiros em uma expresso relacional. No entanto, s possvel comparar ponteiros de mesmo tipo. O Programa 9.4 ilustra um exemplo deste tipo de operao.

Listagem 9.4: Exemplo de comparao de ponteiros.


#include < stdio .h > i n t main ( void ) { char *c , *v , a , b ; scanf ( " % c % c " , &a , & b ) ; c = &a; v = &b; i f ( c == v ) printf ( " As variveis estao na mesma posicao . ") ; else printf ( " As variaveis nao estao na mesma posicao . " ); return 0; }

147

9.3

Ponteiros e Vetores

Ponteiros e Vetores esto fortemente relacionados na linguagem C. O nome de um vetor um ponteiro que aponta para a primeira posio do vetor. A declarao int vetor[100] cria um vetor de inteiros de 100 posies e permite que algumas operaes com ponteiros possam ser realizadas com a varivel vetor. No entanto, existe uma diferena fundamental entre declarar um conjunto de dados como um vetor ou atravs de um ponteiro. Na declarao de vetor, o compilador automaticamente reserva um bloco de memria para que o vetor seja armazenado. Quando apenas um ponteiro declarado a nica coisa que o compilador faz alocar um ponteiro para apontar para a memria, sem que espao seja reservado. O nome de um vetor chamado de ponteiro constante e, portanto, no pode ter o seu valor alterado. O nome de um ponteiro constante no pode aparecer em expresses no lado esquerdo do sinal de igual, ou seja, no pode receber valores diferentes do valor inicial atribudo na declarao da varivel. Assim, os comandos que alteram o ponteiro list, mostrados no exemplo 9.5, no so vlidos:

Listagem 9.5: Exemplo de alteraes invlidas sobre ponteiros.


i n t list [5] , i ; /* O ponteiro list nao pode ser modificado recebendo o endereco de i */ list = & i /* O ponteiro list nao pode ser incrementado */ list ++;

O contedo de vetores pode ser acessado usando-se o operador * para obter o contedo do vetor. O Programa 9.6 mostra a notao que usa ndices para buscar o elemento de um vetor e o uso do operador * de ponteiros. Neste programa o contedo do vetor impresso usando-se estas duas notaes.

Listagem 9.6: Exemplo de notaes de vetores.


#include < stdio .h > i n t main ( void ) { f l o a t v [] = {1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0}; int i; f o r ( i = 0; i < 7; i ++) printf ( " %.1 f " , v [ i ]) ; printf ( " \ n " ) ; f o r ( i = 0; i < 7; i ++) printf ( " %.1 f " , *( v + i ) ) ; return 0; }

Para percorrer um vetor alm da maneira mostrada no programa 9.6 pos148

svel usar um ponteiro varivel como ilustrado no Programa 9.7. Observe como o ponteiro p recebe seu valor inicial e a maneira como ele incrementado.

Listagem 9.7: Exemplo de ponteiro varivel.


#include < stdio .h > i n t main ( void ) { f l o a t v [] = {1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0}; int i; fl oat *p; f o r ( i = 0; i < 7; i ++) printf ( " %.1 f " , v [ i ]) ; printf ( " \ n " ) ; f o r ( i = 0; i < 7; i ++) printf ( " %.1 f " , *( v + i ) ) ; printf ( " \ n " ) ; f o r ( i = 0 , p = v ; i < 7; i ++ , p ++) printf ( " %.1 f " , * p ); return 0; }

9.4

Ponteiros e Cadeias de Caracteres

Uma cadeia de caracteres constante escrita como no exemplo:


"Esta e uma cadeia de caracteres."

At agora um dos usos mais comuns de cadeias de caracteres constantes tem sido na funo printf, como no exemplo abaixo
printf("Acabou o programa.\n");

Quando uma cadeia de caracteres como esta enviada para a funo, o que passado o ponteiro para a cadeia. possvel ento carregar o endereo da cadeia em um ponteiro do tipo char, como no exemplo 9.8. Neste programa contado o nmero de caracteres de uma cadeia. Observe o ponteiro *(s+tam++) apontando caractere a caractere. Um outro exemplo (Programa 9.9) mostra uma funo que copia um cadeia de caracteres para outra.

9.5

Alocao Dinmica de Memria

O uso de ponteiros e vetores exige que aps a denio da varivel ponteiro uma rea de memria deve ser reservada para armazenar os dados do vetor. Para obter esta rea o programa deve usar funes existentes na biblioteca stdlib. Estas funes pedem ao sistema operacional para separar pedaos da memria e devolvem ao programa que pediu o endereo inicial deste local. As funes bsicas de alocao de memria que iremos discutir so:

149

Listagem 9.8: Exemplo de ponteiro para cadeia de caracteres.


#include < stdio .h > i n t main ( void ) { char *s , * lista = " 1234567890 " ; i n t tam =0; s = lista ; while (*( s + tam ++) != ' \0 ') ; tam - -; printf ( " O tamanho do string \"% s \" e % d caracteres .\ n " , lista , tam ) ; return 0; }

Listagem 9.9: Exemplo de cpia de cadeias de caracteres.


#include < stdio .h > i n t strcop ( char *d , char * o ) ; i n t main ( void ) { char destino [20]; char * origem = " cadeia de caractere de origem " ; strcop ( destino , origem ) ; printf ( " % s \ n " , origem ) ; printf ( " % s \ n " , destino ) ; return 0; } i n t strcop ( char *d , char * o ) { while ((* d ++ = * o ++) != ' \0 ') ; return 0; }

void *malloc(size_t size); Reserva espao na memria para algum item de um programa. O tamanho em bytes reservado denido pela varivel size. O valor armazenado no espao indenido. A funo retorna um ponteiro de tipo void para o espao reservado ou NULL no caso de algum erro ocorrer. void *calloc(size_t num, size_t size); Reserva espao na memria para um vetor de num itens do programa. Cada item tem tamanho size e todos os bits do espao so inicializados com 0. A funo retorna um ponteiro de tipo void para o espao reservado ou NULL no caso de algum erro ocorrer. void free(void *pont); O espao apontado por pont devolvido ao sistema para uso. Caso pont seja um ponteiro nulo nenhuma ao executada. No caso do ponteiro no ter sido resultado de uma reserva feita por meio de uma das funes calloc, realloc ou malloc o resultado indenido. void realloc(void *pont, size_t size); A funo altera o tamanho do objeto na memria apontado por pont para o tamanho especicado por size. O

150

contedo do objeto ser mantido at um tamanho igual ao menor dos dois tamanhos, novo e antigo. Se o novo tamanho requerer movimento, o espao reservado anteriormente liberado. Caso o novo tamanho for maior, o contedo da poro de memria reservada a mais car com um valor sem especicao. Se o tamanho size for igual a 0 e pont no um ponteiro nulo o objeto previamente reservado liberado. Estas funes podem ser encontradas na biblioteca stdlib.h. O Programa 9.10 ilustra o uso das funo calloc e free.

Listagem 9.10: Exemplo de uso de calloc e free.


#include < stdio .h > #include < stdlib .h > i n t main ( void ) { fl oat *v; i n t i , tam ; printf ( " Qual o tamanho do vetor ? " ) ; scanf ( " % d " , & tam ) ; v = calloc ( tam , s i z e o f ( f l o a t ) ) ; i f (! v ) { printf ( " Nao consegui alocar memoria . " ) ; return 1; } f o r ( i =0; i < tam ; i ++) { printf ( " Elemento % d ? " , i ) ; scanf ( " % f " , v + i ) ; printf ( " Li valor % f \ n " , *( v + i ) ) ; } free ( v ) ; return 0; }

Um outro exemplo, agora empregando a funo malloc() est mostrado no Programa 9.11. Observe que neste programa tambm mostramos exemplos onde um endereo de varivel foi passado para uma funo de modo que a funo main possa receber um valor (vezes).

9.6

Ponteiros e Matrizes

Um ponteiro aponta para uma rea de memria que endereada de maneira linear. Portanto, vetores podem ser facilmente manipulados com ponteiros. No entanto, quando usamos estruturas de dados com maior dimensionalidade, como matrizes, por exemplo, que so arranjos bidimensionais de dados, necessrio mapear o espao bidimensional (ou de maior ordem) para uma dimenso. No caso das matrizes necessrio mapear o endereo de cada elemento na matriz, que denido por um par (linha, coluna) em um endereo linear. Considere uma matriz chamada matriz de tamanho LIN,COL que poderia ser declarada e ter um de seus elementos lidos da maneira mostrada no trecho de 151

Listagem 9.11: Exemplo de uso de malloc.


#include < stdio .h > #include < stdlib .h > void LeVetor ( f l o a t *v , i n t tam ) ; float ProcuraMaior ( f l o a t *v , i n t tam , i n t * vezes ) ; i n t main ( void ) { f l o a t *v , maior ; i n t i , tam , vezes ; printf ( " Qual o tamanho do vetor ? " ) ; scanf ( " % d " , & tam ) ; v = ( f l o a t *) malloc ( tam * s i z e o f ( f l o a t ) ) ; i f (v) { LeVetor (v , tam ) ; maior = ProcuraMaior (v , tam , & vezes ) ; printf ( Maior = % f e aparece % d vezes .\ n ." , maior , vezes ) ; free ( v ) ; } else { printf ( " Nao consegui alocar memoria . " ) ; return 1; } return 0; } void LeVetor ( float *v , int tam ) { int i ; for ( i =0; i < tam ; i ++) { printf ( " Elemento % d ? " , i ) ; scanf ( " % f " , v + i ) ; printf ( " Li valor % f \ n " , *( v + i ) ) ; } } float ProcuraMaior ( float *v , int tam , int * vezes ) { int i ; float maior ; maior = v [0]; * vezes = 1; for ( i =1; i < tam ; i ++) { if ( v [ i ] & gt ; maior ) { maior = v [ i ]; * vezes = 1; } else if ( maior == v [ i ]) * vezes =* vezes +1; } return maior ; }

152

programa listado em 9.12. Caso o programa utilizasse ponteiros ao invs de notao de matrizes, poderamos usar uma soluo que mapeasse a matriz que um objeto de duas dimenses em um vetor que tem apenas uma. Neste caso o programa deve fazer a translao de endereos toda vez que precisar ler ou escrever na matriz. O trecho de programa caria como mostrado no exemplo 9.13. A expresso matriz+(i*COL+j) calcula a posio do elemento matriz [i][j] a partir do primeiro elemento da matriz que est no endereo inicial matriz.

Listagem 9.12: Exemplo de matriz normal sem uso de ponteiros.


#define LIN 3 #define COL 4 i n t matriz [ LIN ][ COL ]; f o r ( i =0; i < LIN ; i ++) { f o r ( j =0; j < COL ; j ++) { printf ( " Elemento % d % d = " , i , j ) ; scanf ( " % d " , & matriz [ i ][ j ]) ; } }

Listagem 9.13: Exemplo de matriz mapeada em um vetor.


#define LIN 3 #define COL 4 i n t * matriz ; int i, j; matriz = malloc ( LIN * COL * s i z e o f ( i n t ) ) ; i f (! matriz ) { printf ( " Erro .\ n " ) ; return 1; } f o r ( i = 0; i < LIN ; i ++) { f o r ( j = 0; j < COL ; j ++) { printf ( " Elemento % d % d = " , i , j ) ; scanf ( " % d " , matriz +( i * COL + j ) ) ; } }

No entanto, esta soluo ainda no a melhor j que o usurio necessita escrever diretamente uma expresso para mapear o endereo bidimensional da matriz matriz[i][j] em um endereo linear. O ideal usar uma notao que use somente ponteiros. Esta notao ser discutida nas sees seguintes.

153

9.7

Vetores de Ponteiros

Uma possibilidade mais interessante utilizar vetores de ponteiros. Esta no a notao ideal, mas um passo na direo da notao mais efetiva. Neste caso cada linha da matriz corresponde a um vetor que apontado por um ponteiro armazenado no vetor de ponteiros. Como ponteiros tambm so variveis possvel ento criar vetores de ponteiros e utiliz-los. O exemplo mostrado em 9.14 mostra um programa onde utilizado um vetor de ponteiros para vrias linhas de caracteres. Observe na funo main a declarao char *linha[LINHAS ]; que dene um vetor de tamanho LINHAS. Este vetor contem ponteiros e no valores. At este momento do programa temos apenas posies reservadas para armazenar ponteiros. A alocao de espao e a inicializao dos ponteiros feito no primeiro comando for. Como cada elemento do vetor linha um ponteiro temos que o endereo retornado pela funo malloc armazenado em cada um dos elementos deste vetor. A leitura das linhas de caracteres feita pela funo gets(linha[i]) que passa o elemento do vetor onde os caracteres sero armazenados.

Listagem 9.14: Exemplo de uso de vetor de ponteiros.


#include < stdio .h > #include < stdlib .h > #define LINHAS 10 #define COLUNAS 60 i n t main ( void ) { char * linha [ LINHAS ]; int i; f o r ( i = 0; i < LINHAS ; i ++) { i f (!( linha [ i ] = malloc ( COLUNAS * s i z e o f ( char ) ) ) ) { printf ( " Sem memria para vetor % d .\ n " , i ) ; return i ; } } f o r ( i = 0; i < LINHAS ; i ++) { printf ( " Entre com a linha % d .\ n " , i ) ; gets ( linha [ i ]) ; } f o r ( i = 0; i < LINHAS ; i ++) { printf ( " Linha % d % s .\ n " , i , linha [ i ]) ; } return 0; }

154

9.8

Ponteiros para Ponteiros

No exemplo anterior podemos observar que o nmero de linhas da matriz xa, e portanto, h uma mistura de notao de ponteiros com matrizes. Vamos considerar um exemplo onde tanto o nmero de linhas como o de colunas desconhecido. Neste exemplo iremos criar um vetor de ponteiros que ir armazenar o endereo inicial de cada linha. Portanto, para obter um elemento da matriz primeiro devemos descobrir onde est a linha no vetor que armazena os endereos das linhas, em seguida procuramos na linha o elemento. A Figura 9.7 ilustra como ser feito o armazenamento desta matriz.
Vetor de ponteiros **matriz *(matriz+0) *(matriz+1) *(matriz+2) linha 2 linha 1 linha 0

*(matriz+n) linha n

Figura 9.7: Armazenamento de matrizes com vetores de ponteiros. O Programa 9.15, listado a seguir, ir pedir ao usurio que digite o nmero de linhas e colunas da matriz. Em seguida ler todos os elementos da matriz e por ltimo ir trocar duas linhas da matriz de posio. Observe que agora foi criado um ponteiro para ponteiro chamado de **matriz. O programa primeiro pergunta o nmero de linhas da matriz para poder alocar espao para armazenar os ponteiros para cada uma das linhas. Em seguida alocado espao para armazenar cada uma das linhas. O comando
matriz = (int **)malloc (lin * sizeof(int *));

foi usado pelo programa para reservar espao para armazenar lin linhas de ponteiros para ponteiros. Observe que o comando sizeof(int *) calcula o espao para armazenar um ponteiro na memria. Note tambm que o valor retornado pela funo malloc foi conformado ao tipo ponteiro para ponteiro pela operao (int **). O interessante do programa que a troca de linhas da matriz envolve simplesmente a troca de dois ponteiros e no a troca de todos os elementos das linhas. Esta soluo muito mais rpida do que trocar elemento a elemento, especialmente para matrizes grandes. A seguir mostramos o programa nas listagens 9.16 e 9.17 que o exemplo anterior modicado para utilizar funes. O propsito mostrar como cam as chamadas e as denies das funes que utilizam ponteiros para ponteiros. 155

Listagem 9.15: Exemplo de uso de ponteiros para ponteiros.


#include < stdio .h > #include < stdlib .h > i n t main ( void ) { i n t ** matriz ; /* ponteiro para os ponteiros */ i n t lin , col ; /* nmero de linhas e colunas */ int i, j; i n t linha1 , linha2 ; /* linhas que serao trocadas */ char linha [80]; /* linha de caracteres com os dados */ i n t * temp ; puts ( " Qual o numero de linhas ? " ) ; gets ( linha ) ; lin = atoi ( linha ) ; matriz = ( i n t **) malloc ( lin * s i z e o f ( i n t *) ) ; i f (! matriz ) { puts ( " Nao h espao para alocar memria " ) ; return 1; } puts ( " Qual o numero de colunas ? " ) ; gets ( linha ) ; col = atoi ( linha ) ; f o r ( i =0; i < lin ; i ++) { *( matriz + i ) = ( i n t *) malloc ( col * s i z e o f ( i n t ) ) ; i f (! *( matriz + i ) ) { printf ( " Sem espao para alocar a linha % d " , i); return 1; } } puts ( " Entre com os dados " ) ;} f o r ( i =0; i < lin ; i ++) { printf ( " Entre com a linha % d \ n " , i ) ; f o r ( j =0; j < col ; j ++) { printf ( " Elemento % d % d \ n " , i , j ) ; scanf ( " % d " , *( matriz + i ) + j ) ; } } puts ( " Qual a primeira linha a ser trocada ? ") ; gets ( linha ) ; linha1 = atoi ( linha ) ; puts ( " Qual a segunda linha a ser trocada ? " ) ; gets ( linha ) ; linha2 = atoi ( linha ) ; temp = *( matriz + linha1 ) ; *( matriz + linha1 ) = *( matriz + linha2 ) ; *( matriz + linha2 ) = temp ; puts ( " Dados trocados . " ) ; f o r ( i =0; i < lin ; i ++) { f o r ( j =0; j < col ; j ++) { printf ( " %7 d " , *(*( matriz + i ) + j ) ) ; } printf ( " \ n " ) ; } return 0; }

156

Listagem 9.16: Exemplo de uso de ponteiros para ponteiros usando funes.


#include < stdio .h > #include < stdlib .h >} i n t ** aloca_linhas ( i n t ) ; void aloca_colunas ( i n t ** , int , i n t ) ; void le_dados ( i n t ** , int , i n t ) ; void imprime_matri z ( i n t ** , int , i n t ) ; void troca_linhas ( i n t ** , int , i n t ) ; i n t main ( void ) { i n t ** matriz ; i n t lin , col ;& nbsp ;& nbsp ; i n t linha1 , linha2 ; char linha [80]; puts ( " Qual o numero de linhas ? " ) ; gets ( linha ) ; lin = atoi ( linha ) ; matriz = aloca_linhas ( lin ) ; puts ( " Qual o numero de colunas ? " ) ; gets ( linha ) ; col = atoi ( linha ) ; printf ( " Alocando espao para linhas .\ n " ) ; aloca_colunas ( matriz , lin , col ) ; le_dados ( matriz , lin , col ) ; imprime_matri z ( matriz , lin , col ) ; puts ( " Qual a primeira linha a ser trocada ? ") ; gets ( linha ) ; linha1 = atoi ( linha ) ; puts ( " Qual a segunda linha a ser trocada ? " ) ; gets ( linha ) ; linha2 = atoi ( linha ) ; troca_linhas ( matriz , linha1 , linha2 ) ; imprime_matri z ( matriz , lin , col ) ; return 0; } i n t ** aloca_linhas ( i n t lin ) { i n t ** m ;} m = ( i n t **) malloc ( lin * s i z e o f ( i n t *) ) ; i f (! m ) { puts ( " Sem espao para alocar memria " ) ; return 1; } return m ; } void aloca_colunas ( i n t ** matriz , i n t lin , i n t col ) { int i; f o r ( i =0; i < lin ; i ++) { *( matriz + i ) = ( i n t *) malloc ( col * s i z e o f ( i n t ) ) ; i f (! *( matriz + i ) ) { printf ( " Sem espao para linha % d " , i ) ; return 1; } } }

157

Listagem 9.17: Continuao do exemplo 9.16.


void le_dados ( i n t ** matriz , i n t lin , i n t col ) { int i, j; puts ( " Entre com os dados " ) ; f o r ( i =0; i < lin ; i ++) { printf ( " Entre com a linha % d \ n " , i ) ; f o r ( j =0; j < col ; j ++) { printf ( " Elemento % d % d \ n " , i , j ) ; scanf ( " % d " , *( matriz + i ) + j ) ; } } } void imprime_matri z ( i n t ** matriz , i n t lin , i n t col ) { int i, j; f o r ( i =0; i < lin ; i ++) { f o r ( j =0; j < col ; j ++) { printf ( " %7 d " , *(*( matriz + i ) + j ) ) ; } printf ( " \ n " ) ; } } void troca_linhas ( i n t ** matriz , i n t linha1 , i n t linha2 ) { i n t * temp ; temp = *( matriz + linha1 ) ; *( matriz + linha1 ) = *( matriz + linha2 ) ; *( matriz + linha2 ) = temp ; }

158

9.9

Exerccios

9.1: Escreva um programa que gere um vetor de trs dimenses (X, Y e Z) em que cada posio guarda a soma de suas coordenadas. As dimenses da matriz devero ser determinadas em tempo de execuo e o programa dever informar os valores gerados. 9.2: Escreva um programa que leia uma frase de at 80 caracteres do teclado e imprima a freqncia com que aparece cada uma das letras do alfabeto na frase. 9.3: Escreva um programa que leia uma frase de at 80 caracteres e a imprima em ordem reversa convertendo todos os caracteres minsculos para maisculos. 9.4: Escreva um programa que leia uma matriz e a imprima. O programa deve ler o numero de colunas e linhas do teclado. O programa deve ainda trocar duas linhas da matriz de posio. Os nmeros das linhas a serem trocadas devem ser lidos do teclado. 9.5: Escreva um programa que simule uma pilha usando vetores. O programa deve implementar as seguintes operaes na pilha: Inserir Remover Listar 9.6: Escreva uma funo que receba um ponteiro para uma cadeia de caractere e troque todo o caracter aps um branco pelo seu equivalente maisculo. 9.7: Escreva um programa que leia seu nome completo e pergunte quantas letras tem o seu primeiro nome. Assuma que a letra a tem ndice 0, a letra b ndice 1 e assim por diante. O programa deve imprimir quantas letras iguais a letra cujo ndice o nmero de letras do seu primeiro nome existem no seu nome completo. 9.8: Escreva um programa que leia seu nome completo e pergunte quantas letras tem o seu primeiro nome. O seu programa deve usar a funo posicao que tem o seguinte prottipo:
int posicao(char *substr, char *str);

Esta funo deve vericar se a cadeia apontada por substr est presente na cadeia apontada por str e retornar a posio em que a sub-cadeia aparece em cadeia. 9.9: Escreva um programa que procure em uma matriz elementos que sejam ao mesmo tempo o maior da linha e o menor coluna. As dimenses da matriz devem ser pedidas ao usurio. 9.10: Escreva um programa que leia duas cadeias de caracteres e concatene a segunda cadeia ao nal da primeira. 159

Listagem 9.18: Programa do exercicio 11.


#include < string .h > #include < stdio .h > #define MAX 80 void misterio ( char * p1 , char * p2 ) ; i n t main ( void ) { char palavra1 [ MAX ]; char palavra2 [ MAX ]; puts ( " Palavra 1? " ) ; fgets ( palavra1 , MAX , stdin ) ; palavra1 [ strlen ( palavra1 ) -1] = ' \0 ' ; puts ( " Palavra 2? " ) ; fgets ( palavra2 , MAX , stdin ) ; palavra2 [ strlen ( palavra2 ) -1] = ' \0 ' ; misterio ( palavra1 , palavra2 ) ; return 0; } void misterio ( char * p1 , char * p2 ) { while (* p1 != ' \0 ' && * p2 != ' \0 ') { putchar (* p1 ) ; putchar (* p2 ) ; p1 = p1 + 1; p2 = p2 + 1; } i f (* p1 != ' \0 ') { while (* p1 != ' \0 ') { putchar (* p1 ) ; p1 = p1 + 1; } } i f (* p2 != ' \0 ') { while (* p2 != ' \0 ') { putchar (* p2 ) ; p2 = p2 + 1; } } }

160

9.11: O que ser impresso pelo programa mostrado na listagem 9.18 caso a primeira palavra fornecida seja o seu primeiro nome e a segunda o seu ltimo sobrenome. 9.12: O que ser impresso pelo programa mostrado na listagem 9.19 caso a primeira palavra fornecida seja o seu primeiro nome e a segunda o seu ltimo sobrenome. Indique os nomes que usou e justique a sua resposta.

Listagem 9.19: Programa do exercicio 12.


#include < string .h > #include < stdio .h > #define MAX 80 void nMisterio ( char * p1 , char * p2 ) { while (* p1 != ' \0 ') { p1 = p1 + 1; } while (* p2 != ' \0 ') { * p1 = * p2 ; p1 = p1 + 1; p2 = p2 + 1; } * p1 = ' \0 '; } i n t main ( void ) { char palavra1 [ MAX ]; char palavra2 [ MAX ]; puts ( " Palavra 1? " ) ; fgets ( palavra1 , MAX , stdin ) ; palavra1 [ strlen ( palavra1 ) -1]= ' \0 '; puts ( " Palavra 2? " ) ; fgets ( palavra2 , MAX , stdin ) ; palavra2 [ strlen ( palavra2 ) -1]= ' \0 '; nMisterio ( palavra1 , palavra2 ) ; puts ( palavra1 ) ; return 0; }

9.13: O que ser impresso pelo programa 9.20. Justique sua resposta. Listagem 9.20: Listagem do exerccio 13.
#include < stdio .h > #include < string .h > i n t main ( void ) { char * frase = " Otimo teste " ; char *p , misterio [80];

161

i n t i = 0; i n t j = 0; p = frase + strlen ( frase ) - 1; while (* p != ' ') { misterio [ i ] = * p ; i ++; p - -; } misterio [ i ] = ' '; i ++; while ( frase [ j ] != ' ') { misterio [ i ] = frase [ j ]; j ++; i ++; } misterio [ i ] = ' \0 '; puts ( misterio ) ; return 0; }

9.14: O que ser impresso pelo programa mostrado na listagem 9.21. Justique sua resposta. Listagem 9.21: Programa do exerccio 14.
#include < stdio .h > void f1 ( i n t v ) { v = v + 1; printf ( " f1 = % d \ n " , v ) ; } void f2 ( i n t * v ) { * v = * v + 1; printf ( " f2 = % d \ n " , * v ) ; } i n t f3 ( i n t v ) { v = v + 1; printf ( " f3 = % d \ n " , v ) ; return v ; } i n t main ( void ) { i n t v = 1; f1 ( v ) ; f2 (& v ) ; v = f3 ( v ) ; printf ( " main = % d \ n " , v ) ; return 0; }

162

Captulo 10

Estruturas
10.1 Introduo

Uma estrutura um conjunto de uma ou mais variveis, que podem ser de tipos diferentes, agrupadas sob um nico nome. O fato de variveis agrupadas em uma estrutura poderem ser referenciadas por um nico nome facilita a manipulao dos dados armazenados nestas estruturas. Um exemplo poderia ser uma estrutura que armazenasse as diversas informaes sobre os alunos de uma Universidade. Nesta estrutura estariam armazenadas, sob o mesmo nome, informaes do tipo: nome, registro, data de nascimento, data de ingresso, CPF, etc. Uma estrutura pode incluir outras estruturas alm de variveis simples. As estruturas facilitam manipular estes agrupamentos complexos de dados. Por exemplo, considere o problema de ordenar as informaes sobre os alunos da Universidade exemplo. A ordenao pode ser efetuada como se todos os dados que compem a estrutura fossem uma entidade nica.

10.2

Denies Bsicas

Uma estrutura, ento, uma coleo de variveis, de tipos diversos ou no, agrupadas sob um nico nome. As variveis que compem a estrutura so os seus membros, elementos ou campos. Normalmente os elementos da estrutura tem alguma relao semntica. Por exemplo: alunos de uma universidade, discos de uma coleo, elementos de uma gura geomtrica, etc. Vamos considerar o exemplo do aluno e assumir que estaremos armazenando o seu nome, registro, ano de entrada e curso. Para este m podemos criar uma estrutura como a descrita no trecho de programa 10.1. A palavra chave struct inicia a declarao da estrutura, em seguida pode aparecer um identicador (ALUNO), que subseqentemente pode ser usado como abreviao da denio da estrutura. A declarao continua com a lista de declaraes entre chaves e termina com um ;. Um membro da estrutura e uma varivel no membro da estrutura podem ter o mesmo nome, j que possvel distingui-las por contexto.

163

Listagem 10.1: Denio de uma estrutura.


s t r u c t ALUNO { char nome [40]; i n t registro ; i n t ano_entrada ; char curso [20]; };

A declarao acima ainda no alocou espao de memria j que nenhuma varivel foi realmente denida. Esta declarao apenas um modelo de como estruturas do tipo ALUNO devem ser construdas. Para denir estruturas deste tipo podemos usar a seguinte declarao.
struct ALUNO paulo, carlos, ana;

Nesta declarao trs estruturas do tipo ALUNO foram criadas. Esta declarao alocou espao para armazenar os dados dos trs alunos. A declarao acima idntica, na forma, a declarao de variveis de um tipo pr-denido, como por exemplo:
int a, b, c;

possvel declarar ao mesmo tempo o modelo da estrutura e as variveis do programa. Por exemplo,
s t r u c t ALUNO { char nome [40]; i n t registro ; i n t ano_entrada ; char curso [20]; } paulo , carlos , ana ;

Para referenciar um elemento da estrutura usa-se o nome da varivel do tipo da estrutura seguida de um ponto e do nome do elemento. Por exemplo,
paulo.ano_entrada = 1999;

armazena o ano em que aluno paulo entrou na universidade. Para ler o nome do curso que paulo cursa pode-se usar o comando
gets(paulo.curso);

Estruturas podem conter outras estruturas como membros. Por exemplo, vamos denir uma estrutura para armazenar uma data com a seguinte denio:
s t r u c t DATA { i n t dia , mes , ano ; };

Agora vamos modicar a estrutura aluno de modo que ela inclua a data de nascimento do aluno. A estrutura ca com a seguinte denio:

164

s t r u c t aluno { char nome [40]; i n t registro ; i n t ano_entrada ; char curso [20]; s t r u c t DATA data_nascim en to ; };

Para se referir ao ms de nascimento de uma varivel paulo do tipo estrutura


aluno usamos a declarao paulo.data_nascimento.mes

Note que o operador ponto (.) associa da esquerda para a direita. Uma forma mais conveniente de denio de estruturas possvel com o uso do comando typedef. Este comando permite dar a um tipo de dados um novo nome. A inteno aumentar a legibilidade do programa. Por exemplo, possvel usar o seguinte cdigo
typedef i n t cores ; typedef i n t laranja ; typedef i n t manga ; ... laranja lima ; manga espada ; cores = AMARELO ; ... espada ++;

Ao mesmo tempo que typedef tem a vantagem de tornar mais claro a nalidade de cada varivel ele pode trazer problemas na medida em que esconde o real tipo da varivel. comum o uso de typedef em conjunto com struct. Considere a denio de uma estrutura para guardar tempos gastos em tarefas. Esta estrutura deve guardar horas, minutos e segundos. Usando esta combinao, a denio usualmente feita da seguinte maneira:
s t r u c t _TEMPO { i n t hora , minuto , segundo ; }; typedef s t r u c t _TEMPO TEMPO ; ... TEMPO t1 ;

Uma forma ainda mais abreviada, junta as duas denies, cando a denio da estrutura da seguinte maneira:
typedef s t r u c t _TEMPO { i n t hora , minuto , segundo ; } TEMPO ;

165

... TEMPO t1 ;

possvel dispensar o nome da estrutura (_TEMPO) e a denio ca ainda mais simples, com a seguinte forma:
typedef s t r u c t { i n t hora , minuto , segundo ; } TEMPO ; ... TEMPO t1 ;

10.3

Atribuio de Estruturas

possvel atribuir o contedo de uma estrutura a outra estrutura do mesmo tipo, no sendo necessrio atribuir elemento por elemento da estrutura. Esta uma das grandes vantagens de estruturas j que o tamanho do cdigo reduzido e a clareza dos programas aumenta. O programa 10.2 ilustra como podemos atribuir uma estrutura a outra. O comando temp = emp1; faz com que todos os dados armazenados na estrutura emp1 sejam transferidos para a estrutura temp. Listagem 10.2: Atribuio de Estruturas.
#include < stdio .h > typedef s t r u c t _EMPREGADO { char nome [40]; f l o a t salario ; } EMPREGADO ; i n t main () { EMPREGADO temp , emp1 ; puts ( " Entre com nome . " ) ; gets ( emp1 . nome ) ; puts ( " Qual o salario ? " ) ; scanf ( " % f " , & emp1 . salario ) ; temp = emp1 ; printf ( " O salario de % s e %.2 f \ n " , temp . nome , temp . salario ) ; return 0; }

10.4

Matrizes de Estruturas

Estruturas aparecem freqentemente na forma de matrizes. Por exemplo, a declarao struct ALUNO turma[100]; dene uma matriz de 100 estruturas do tipo struct ALUNO. 166

O exemplo 10.3 mostra atribuies entre estruturas e operaes aritmticas envolvendo membros de estruturas. O programa coloca um vetor de estruturas em ordem crescente usando como chave de ordenao um dos membros da estrutura (media). Listagem 10.3: Ordenao de Estruturas.
#include < stdio .h > #include < string .h > typedef s t r u c t _ALUNO { char nome [40]; f l o a t n1 , n2 , media ; } ALUNO ; i n t main ( void ) { ALUNO turma [4] , temp ; i n t jaOrdenados = 0 , foraOrdem , i ; f o r ( i = 0; i < 4; i ++) { gets ( turma [ i ]. nome ) ; scanf ( " % f " , & turma [ i ]. n1 ) ; do {} while ( getchar () != '\ n ') ; scanf ( " % f " , & turma [ i ]. n2 ) ; do {} while ( getchar () != '\ n ') ; turma [ i ]. media =( turma [ i ]. n1 + turma [ i ]. n2 ) /2.0; } do { foraOrdem = 0; f o r ( i = 0; i < 4 - 1 - jaOrdenados ; i ++) { i f ( turma [ i ]. media > turma [ i +1]. media ) { temp = turma [ i ]; turma [ i ] = turma [ i +1]; turma [ i +1] = temp ; foraOrdem = 1; } } jaOrdenados ++; } while ( foraOrdem ) ; f o r ( i =0; i <4; i ++) { printf ( " \ nDados do aluno % d \ n " , i ) ; printf ( " % s : %0.1 f , %0.1 f , %0.1 f \ n " , turma [ i ]. nome , turma [ i ]. n1 , turma [ i ]. n2 , turma [ i ]. media ) ; } return 0; }

10.5

Estruturas e Funes

Primeiro vamos considerar o caso de passar elementos da estrutura para funes. Caso os elementos da estrutura sejam variveis de um dos tipos j vistos, a 167

passagem efetuada da maneira normal. O exemplo 10.4 mostra como passar um elemento (c.raio) de uma estrutura para uma funo. Listagem 10.4: Passando elementos para funes.
#include < stdio .h > typedef s t r u c t _CIRCULO { f l o a t x , y , raio ; } CIRCULO ; f l o a t Area ( f l o a t r ) { return 3.141516 * r * r ; } i n t main ( void ) { CIRCULO c ; c . x = c . y = c . raio = 1.0; printf ( " % f \ n " , Area ( c . raio ) ) ; return 0; }

A funo que recebe este parmetro est preparada para receber uma varivel de ponto utuante simples. Caso seja necessrio passar o endereo de um dos membros ou elementos da estrutura basta colocar o operador & antes do nome da estrutura. Por exemplo, para trocar os valores das coordenadas x dos centros de dois crculos c1 e c2 usaramos chamadas da seguinte forma.
troca_x (& c1 .x , & c2 . x ) ;

Para trabalhar com endereos necessrio usar ponteiros dentro da funo Antes vamos vericar como possvel passar uma estrutura inteira para uma funo.
troca_x, mas isto veremos no prximo item.

Estruturas, quando passadas para funes, se comportam da mesma maneira que as variveis dos tipos que j estudamos. Ao passar uma estrutura para uma funo estaremos passando os valores armazenados nos membros da estrutura. Como este tipo de passagem feito por valor, alteraes nos membros da estrutura no modicam os valores da estrutura na funo que chamou. A passagem de estruturas para funes ilustrada no exemplo 10.5 onde o comprimento da reta que liga dois pontos p1 e p2 calculado e impresso. Para ilustrar a passagem de vetores de estruturas para funes considere o programa 10.3. Neste programa iremos substituir o trecho que ordena o vetor de alunos por uma funo, cujo cdigo mostrado na listagem 10.6. No programa o trecho que chama a funo tem a seguinte forma
Ordena(turma, 4);

168

Listagem 10.5: Passagem de estruturas para funes.


#include < stdio .h > #include < math .h > typedef s t r u c t _PONTO { fl oat x, y; } PONTO ; f l o a t comp ( PONTO p1 , PONTO p2 ) { return sqrt ( pow ( p2 .x - p1 .x ,2) + pow ( p2 .y - p1 .y ,2) ) ; } i n t main ( void ) { PONTO p1 , p2 ; puts ( " Coordenadas do ponto 1 " ) ; printf ( " x1 = ? " ) ; scanf ( " % f " , & p1 . x ) ; printf ( " y1 = ? " ) ; scanf ( " % f " , & p1 . y ) ; puts ( " Coordenadas do ponto 2 " ) ; printf ( " x2 = ? " ) ; scanf ( " % f " , & p2 . x ) ; printf ( " y2 = ? " ) ; scanf ( " % f " , & p2 . y ) ; printf ( " \ nComprimento da reta = % f \ n " , comp ( p1 , p2 ) ) ; return 0; }

Listagem 10.6: Funo que ordena estruturas.


void Ordena ( ALUNO turma [] , i n t tam ) { i n t i , foraOrdem , jaOrdenados = 0; ALUNO temp ; do { foraOrdem = 0; f o r ( i = 0; i < 4 - 1 - jaOrdenados ; i ++) { i f ( turma [ i ]. media > turma [ i +1]. media ) { temp = turma [ i ]; turma [ i ] = turma [ i +1]; turma [ i +1] = temp ; foraOrdem = 1; } } jaOrdenados ++; } while ( foraOrdem ) ; }

169

10.6

Ponteiros para Estruturas

Para denir ponteiros para estruturas a declarao similar a declarao de um ponteiro normal. O exemplo abaixo mostra a denio de um ponteiro chamado maria para uma estrutura chamada aluno.
s t r u c t aluno { char nome [40]; i n t ano_entrada ; f l o a t n1 , n2 , media ; } * maria ;

Ponteiros so uteis quando passamos estruturas para funes. Ao passar apenas o ponteiro para estrutura economizamos tempo e memria. O espao de memria, economizado por que se evita passar os dados que compem a estrutura um por um. O tempo economizado porque no necessrio gastar o tempo de empilhar e desempilhar todos os elementos da estrutura no processo de passagem para a funo. Empilhar e desempilhar se referem a pilha de dados usada para transferir os dados entre funes. Para acessar elementos da estrutura apontada por um ponteiro usa-se o chamado operador seta (->). Por exemplo para imprimir a mdia da aluna maria usaramos o comando
printf ( " A media vale %.1 f " , maria - > media ) ;

Para alocar espao para estruturas apontadas por ponteiros necessrio usar o operador unrio sizeof, isto porque o tamanho de uma estrutura sempre igual ou maior que a soma dos tamanhos dos seu componentes. Para explicar esta fato devemos considerar como os dados so armazenados na memria dos computadores. Algumas arquiteturas de computadores endeream os dados na memria por bytes, isto cada endereo de memria se refere a um byte. No entanto, estas arquiteturas lem sempre uma palavra inteira da memria. Usualmente, palavras podem ser compostas de dois bytes e comeam em endereos pares, como est mostrado na gura abaixo. Sabemos que existem variveis que ocupam mais de um byte, por exemplo inteiros que so compostos de dois bytes. Imagine ento uma estrutura composta de um caractere (1 byte) e um nmero de inteiro (2 bytes). Caso a memria do computador seja organizada em palavras de 16 bits ou 2 bytes a estrutura acima ocuparia 3 bytes ou uma palavra e meia. Para ler o nmero inteiro o programa deveria ler duas palavras. Lembrar que se os dados fossem sempre armazenados seqencialmente, metade do nmero inteiro estaria em uma palavra e a metade restante na outra, como est indicado na gura abaixo (parte a). Para facilitar o acesso s variveis, alguns compiladores armazenam as variveis de acordo com o que est indicado na gura (parte b). Observar que agora a estrutura ocupa quatro bytes. Neste caso o acesso ao nmero inteiro ser sempre feito em um passo e portanto ganhou-se em tempo de acesso ao custo de gasto de memria. Este uma troca constante em computao. 170

Vimos ento que embora o total de bytes dos elementos da estrutura fosse trs o compilador pode armazenar a estrutura em quatro bytes, da a necessidade de sempre usar o operador sizeof quando alocar espao. O programa 10.7 mostra como alocar espao para uma varivel simples e como usar esta varivel em diversos tipos de comandos.

Listagem 10.7: Alocao de espao para estruturas.


#include < stdio .h > #include < string .h > #include < stdlib .h > typedef s t r u c t _ALUNO { char nome [40]; f l o a t n1 , n2 , media ; } ALUNO ; i n t main ( void ) { ALUNO * maria ; maria = ( ALUNO *) malloc ( s i z e o f ( ALUNO ) ) ; i f (! maria ) exit (1) ; gets ( maria - > nome ) ; scanf ( " % f % f " , &( maria - > n1 ) , &( maria - > n2 ) ) ; maria - > media = ( maria - > n1 + maria - > n2 ) / 2; printf ( " A media de % s vale %0.2 f \ n " , maria - > nome , maria - > media ) ; return 0; }

O programa 10.8 mostra como utilizar ponteiros para vetores de estruturas e a forma mais segura de alocar espao para os dados. Observar as notaes usadas na funo que l os dados dos funcionrios. Notar que (cadastro+i) ->salario o valor da salrio.
fgets (( cadastro + i ) -> nome , 39 , stdin ) ; sscanf ( linha , " % f " , &(( cadastro + i ) -> salario ) ) ;

171

Listagem 10.8: Alocao de espao para vetores de estruturas.


#include < stdio .h > #include < stdlib .h > typedef s t r u c t _func { char nome [40]; f l o a t salario ; } Tfunc ; void le ( Tfunc * cadastro , i n t funcionarios ) { int i; char linha [40]; f o r ( i =0; i < funcionarios ; i ++) { puts ( " Nome ? " ); fgets (( cadastro + i ) -> nome , 39 , stdin ) ; puts ( " Salario ? " ) ; fgets ( linha , 39 , stdin ) ; sscanf ( linha , " % f " , &(( cadastro + i ) -> salario ) ) ; } } f l o a t media ( Tfunc * cadastro , i n t funcionarios ) { f l o a t media =0.0; int i; f o r ( i =0; i < funcionarios ; i ++) { media += ( cadastro + i ) -> salario ; } return media /= funcionarios ; } i n t main ( void ) { Tfunc * cadastro ; i n t funcionarios ; char linha [40]; puts ( " Quantos funcionarios ? " ) ; fgets ( linha , 39 , stdin ) ; sscanf ( linha , " % d " , & funcionarios ) ; i f (!( cadastro = ( Tfunc *) malloc ( funcionarios * s i z e o f ( Tfunc ) ) ) ) { exit (1) ; } le ( cadastro , funcionarios ) ; printf ( " Salario medio = %.2 f \ n " , media ( cadastro , funcionarios ) ) ; return 0; }

172

10.7

Exerccios

10.1: Considere que uma empresa precisa armazenar os seguintes dados de um cliente: Nome completo com no mximo 50 caracteres; renda mensal do do cliente; ano de nascimento; possui ou no carro. Dena um tipo e uma estrutura para armazenarem estes dados e escreva um programa que leia estes dados armazene-os em uma varivel e em seguida os imprima. 10.2: Considerando a mesma estrutura do exerccio anterior, escreva um programa que leia os dados de 100 clientes e imprima: quantos clientes tm renda mensal acima da mdia; quantos clientes tm carro; quantos clientes nasceram entre 1960 (inclusive) e 1980 (exclusive). 10.3: Reescreva o programa 10.3 empregando funes para implementar as diversas tarefas do programa. A funo main deve car da maneira indicada na Listagem 10.9.

Listagem 10.9: Listagem do exercicio 3.


i n t main ( void ) { s t r u c t aluno turma [ MAX ]; le ( turma ) ; puts ( " Imprimindo dados lidos da turma . " ) ; puts ( " Digite qualquer coisa para continuar . ") ; getchar () ; imprime ( turma ) ; ordena_medias ( turma ) ; puts ( " Imprimindo dados ordenados da turma . ") ; puts ( " Digite qualquer coisa para continuar . ") ; getchar () ; imprime ( turma ) ; getchar () ; }

10.4: Escrever um programa que utilize structs e ponteiro para struct e imprima o contedo das variveis da struct. 173

10.5: Escrever um programa que utilize enumeradores com as matrias do seu perodo. Inicialize cada matria com um numero. Depois imprime os valores das variveis enumeradas. 10.6: Escrever um programa que utilize union. Inicialize as variveis com valores diferentes e imprima o contedo delas. 10.7: Fazer um programa que simule as operaes de uma pilha push e pop, usando structs. Um exemplo de entrada poderia ser o seguinte: empilha C empilha B empilha A desempilha A desempilha B desempilha C 10.8: Escreva um programa que solicite o nome e telefone de uma pessoa e grave essas informaes num vetor de uma estrutura que contem esses dados (nome e telefone). O programa deve ter trs opes apenas: uma que adiciona um novo dado, outra que lista todos os dados atualmente armazenados na memria e outra que sai do programa. Esse vetor de estrutura deve ter apenas 10 elementos e fornecer uma mensagem de erro caso o usurio tente adicionar mais pessoas que este mximo permitido. 10.9: Escreva uma estrutura similar as strings do Delphi (possuem um campo armazenando o tamanho da string e um ponteiro para o primeiro caractere da string) e crie as funes strcpy e strcat para strings nesse formato. 10.10: Escreva um programa fazendo o uso de estruturas. Voc dever criar uma estrutura chamada Ponto, contendo apenas a posio x e y (inteiros) do ponto. Declare 2 pontos, leia a posio (coordenadas x e y) de cada um e calcule a distncia entre eles. Apresente no nal a distncia entre os dois pontos. 10.11: Crie uma estrutura chamada retngulo, que possua duas estruturas ponto (o ponto superior esquerdo e o ponto inferior direito). Faa um programa que receba as informaes acerca de um retngulo (as coordenadas dos dois pontos), e informe a rea, o comprimento da diagonal e o comprimento de cada aresta 10.12: Escreva um programa que use as mesmas estruturas do exerccio anterior para descobrir se um ponto est dentro de um retngulo. 10.13: Considere que foi denida a seguinte estrutura:
typedef s t r u c t _frac { i n t numerador , denominador ; } FRACAO ;

Escreva um programa em C que calcule as quatro operaes usando fraes denidas com estruturas do tipo FRACAO. O programa deve ler duas fraes e imprimir o resultado de cada uma das quatro operaes. 174

Captulo 11

Entrada e Sada por Arquivos


11.1 Introduo

Em C no existem instrues especiais de entrada e sada como em outras linguagens de programao. Estas tarefas, em C so executadas por funes especialmente criadas para esta nalidade e armazenadas em bibliotecas especcas. Por esta razo todos programas em C que precisam de entrada e/ou sada de dados necessitam incluir a diretiva #include<stdio.h> no incio do programa, para permitir o uso da biblioteca padro stdio de funes de entrada e sada.

11.2

Fluxos de Dados

Para isolar os programadores dos problemas de manipular os vrios tipos de dispositivos de armazenamento e seus diferentes formatos a linguagem C utiliza o conceito de uxo de dados (stream ). Todos os diferentes sistemas de arquivos se comportam da mesma maneira que foi denida como semelhante a um uxo contnuo de dados (stream ). Dados podem ser manipulados em dois diferentes tipos de uxos: uxos de texto e uxos binrios.

11.2.1

Fluxos de Texto

Um uxo de texto (text stream ) composto por uma seqncia de caracteres, que pode ou no ser dividida em linhas terminadas por um caractere de nal de linha. Um detalhe que deve ser considerado ao escrever um programa que na ltima linha no obrigatrio o caractere de m de linha. Nem sempre a traduo entre a representao do caractere no uxo de texto e no sistema de arquivos do computador hospedeiro um para um byte. Por exemplo, entre UNIX e DOS h uma diferena na representao de nal de linha (linefeed ) que causa problemas na impresso de arquivos. Em UNIX um nal de linha representado pelo caractere de alimentao de linha (LF). Em DOS um nal de linha representado pelo par retorno de carro/alimentao de linha (CR/LF). Deste modo quando um arquivo gerado em UNIX vai para uma 175

impressora que espera nal de linha no modo DOS surge o que comumente chamado de efeito escada. A impresso continua na linha seguinte mas sem voltar para o incio da linha porque em UNIX o caractere de retorno de carro no inserido no uxo de texto. At agora temos trabalhado com os uxos de dados padro: stdin, para entrada de dados e stdout para sada de dados. Ao iniciar todo programa em C automaticamente associado a estes dois uxos de dados sem necessitar de nenhuma interveno do programador. A denio de que perifricos estaro associados a estes uxos depende do sistema operacional. Normalmente o uxo de entrada (stdin) est associado ao teclado e o uxo de sada (stdout) ao monitor.

11.2.2

Fluxo Binrio

Um uxo binrio composto por uma seqncia de bytes lidos, sem traduo, diretamente do dispositivo externo. Existe uma correspondncia um para um entre os dados do dispositivo e os que esto no uxo que o programa manipula. A Figura 11.1 ilustra estes dois tipos de uxos. No uxo de texto os dados so armazenados como caracteres sem converso para representao binria. Cada um dos caracteres ocupa um byte. O numero 12 ocupa dois bytes e o nmero 113 ocupa 3. Um caractere em branco foi inserido entre cada um dos nmeros para separ-los, de modo que a funo de entrada e sada possa descobrir que so dois nmeros inteiros (12 e 113) e no o nmero 12113. No uxo binrio cada nmero inteiro ocupa 32 bits e armazenado na forma binria. Os caracteres do exemplo esto armazenados seguindo a tabela ASCII. Observe que, em arquivos binrios, no h necessidade de separar os nmeros j que eles sempre ocupam 32 bits.

fluxo de texto 1 2 b 1 1 3 b a b

fluxo binrio
000...01100 000...01110001 01100001 01100010

32 bits 12

32 bits 113

8 bits 8 bits a b

Figura 11.1: Fluxos de dados.

11.2.3

Arquivos

Um arquivo pode estar associado qualquer dispositivo de entrada e sada como, por exemplo: impressora, teclado, disquete, disco rgido etc. No entanto,

176

os programas vem os arquivos atravs de uxos. Para que um determinado arquivo em um perifrico seja associado a um uxo necessrio que o arquivo seja aberto e somente aps esta operao, o programa pode manipular os dados. Normalmente a interao entre o programa e os arquivos feita por meio de buers que intermediam a transferncia dos dados entre os programas e os perifricos. Isto serve para facilitar a operao do sistema operacional. Operaes comuns em arquivos so: abertura e fechamento de arquivos; remover um arquivo; leitura e escrita de um caractere ou byte; procurar saber se o m do arquivo foi atingido; posicionar o arquivo em um ponto determinado. Obviamente algumas dessas funes no se aplicam a todos os tipos de dispositivos. Por exemplo, para uma impressora pode no ser possvel usar a funo que reposiciona o arquivo no incio. Um arquivo em disco permite acesso aleatrio enquanto um teclado no. Ao nal das operaes nos arquivos o programa deve fech-los. Caso o programador esquea de executar esta operao, ao nal do programa todos os arquivos associados so fechados automaticamente e os contedos dos buers so descarregados para o dispositivo externo. Caso o arquivo seja de entrada o contedo do buer esvaziado.

11.3

Funes de Entrada e Sada

As funes de Entrada e Sada normalmente utilizadas pelos programadores esto armazenadas na biblioteca stdio.h. As funes mais comuns esto mostradas na tabela 11.1. Para ter acesso aos dados em um arquivo necessrio a denio de um ponteiro do tipo especial FILE. Este tipo tambm est denido na biblioteca stdio.h. Um ponteiro deste tipo permite que o programa tenha acesso a uma estrutura que armazena informaes importantes sobre o arquivo. Para denir uma varivel deste tipo o programa deve conter a seguinte declarao
FILE *arq;

onde arq o ponteiro que ser usado para executar as operaes no arquivo.

11.4

Incio e Fim

As operaes mostradas a seguir mostram operaes que devem ser realizadas antes e depois de usar um arquivo (fopen() e fclose()). As outras duas funes servem para que o usurio possa detectar o m de um arquivo ou voltar para seu incio. 177

Funo
fopen() fclose() fputc() getc(), fgetc() fprintf() sscanf() fscanf() fseek() rewind() feof() ferror() fflush() fread() fwrite()

Descrio Abre um arquivo Fecha um arquivo Escreve um caractere em um arquivo L um caractere de um arquivo Equivalente a printf() Equivalente a scanf(). L de uma cadeia de caracteres Equivalente a scanf() Posiciona o arquivo em um ponto especco Posiciona o arquivo no incio Retorna verdade se chegou ao m do arquivo Verica a ocorrncia de um erro Descarrega o buer associado ao arquivo Leitura de dados no modo binrio Escrita de dados no modo binrio

Tabela 11.1: Exemplos de funes de Entrada e Sada.

11.4.1

Abrindo um Arquivo

Antes de qualquer operao ser executada com o arquivo, ele deve ser aberto. Esta operao associa um uxo de dados a um arquivo. Um arquivo pode ser aberto de diversas maneiras, de acordo com as operaes que devero ser executadas: leitura, escrita, leitura/escrita, adio de texto etc. A funo utilizada para abrir o arquivo chamada fopen() e tem o seguinte prottipo:
FILE *fopen (const char *parq, const char *modo)

onde parq um ponteiro de arquivo para o arquivo a ser manipulado e modo um ponteiro para uma cadeia de caracteres que dene a maneira como o arquivo vai ser aberto. Este ponteiro no deve ser modicado e a funo retorna um ponteiro nulo (NULL) se o arquivo no puder ser aberto. A seguir listamos os diversos modos que podem ser usados para abrir um arquivo. r: Abre um arquivo para leitura, o arquivo deve existir ou um erro ocorre. w: Cria um arquivo vazio para escrita, caso um arquivo com o mesmo nome exista o seu contedo apagado. a: Adiciona ao nal de um arquivo. O arquivo criado caso ele no exista. r+: Abre um arquivo para leitura e escrita. O arquivo deve existir ou um erro ocorre. w+: Cria um arquivo vazio para leitura e escrita. Se um arquivo com o mesmo nome existe o contedo apagado. a+: Abre um arquivo para leitura e adio. Todas as operaes de escrita so feitas no nal do arquivo. possvel reposicionar o ponteiro do arquivo para qualquer lugar em leituras, mas as escritas movero o ponteiro para o nal do arquivo. O arquivo criado caso no exista. 178

Observar que se um arquivo for aberto com permisso de escrita todo o seu contedo anterior ser apagado. Caso o arquivo no exista ele ser criado. O trecho de programa abaixo ilustra os passos necessrios para abrir um arquivo para escrita. Primeiro declarado o ponteiro pa para o arquivo. Em seguida a funo fopen chamada para associar o nome externo do programa (arquivo.txt) no modo escrita ao ponteiro pa. Um teste para ponteiro nulo feito para vericar se ocorreu algum problema com a operao de abertura do arquivo.

FILE * pa ; /* declaracao do ponteiro para arquivo */ /* nome externo associado ao interno */ pa = fopen ( " arquivo . txt " , " w " ) ; i f ( pa == NULL ) { /* verifica erro na abertura */ printf ( " Arquivo nao pode ser aberto . " ) ; return 1; }

Lembrar que abrir, para escrita, um arquivo que j existe, implica em apagar todo o contedo anterior e a preparao do arquivo para receber dados a partir de seu ponto inicial. Se o programador deseja acrescentar dados ao nal de um arquivo j existente o modo de abertura deve ser a.

11.4.2

Fechando um Arquivo

Um arquivo aberto por meio da funo fopen() deve ser fechado com a funo
fclose() cujo prottipo int fclose (FILE *parq);

onde parq um ponteiro de arquivo para o arquivo que deve ser fechado. Todos os buers internos associados com o uxo de dados do arquivo so descarregados. O contedo de qualquer buer no escrito escrito e dados no lidos de buers so perdidos. Este ponto importante de ser considerado porque em muitos sistemas operacionais uma operao de escrita em um arquivo no ocorre imediatamente a emisso da ordem de escrita. O sistema operacional pode executar a ordem no momento que achar mais conveniente. Um valor zero de retorno signica que a operao foi executada com xito, qualquer outro valor implica em erro.

11.4.3

Fim de Arquivo

A funo feof() indica que um arquivo chegou ao seu nal. A pergunta que pode surgir a seguinte - Se j existe o valor EOF para indicar o nal de arquivo, por que precisamos de uma funo extra do tipo feof()? O problema que EOF um valor inteiro e ao ler arquivos binrios este valor pode ser lido como parte do arquivo e no por ser o nal do arquivo. A funo feof() serve para indicar 179

que o nal de um arquivo binrio foi encontrado. Naturalmente esta funo pode ser aplicada tambm a arquivos texto. O prottipo da funo o seguinte:
int feof(FILE *parq)

Um valor diferente de zero retornado no caso de ter sido atingido o nal do arquivo. O valor zero indica que ainda no se chegou ao nal do arquivo. O exemplo 11.1 mostra um programa que l um caractere do teclado e o mostra na tela. Neste exemplo a leitura termina quando o usurio digita o caractere <ctl>+D, que indica nal de arquivo pelo teclado em Unix (no outro sistema <ctl>+Z).

Listagem 11.1: Uso da funo feof().


#include < stdio .h > i n t main ( void ) { char c ; c = getchar () ; while ( c != EOF ) { putchar ( c ) ; c = getchar () ; } return 0; }

11.4.4

Volta ao Incio

A funo rewind() recoloca o indicador de posio de arquivo no incio do arquivo. Uma operao semelhante ao que fazemos em uma ta cassete de msica ou vdeo. O prottipo da funo o seguinte:
void rewind(FILE *parq)

importante observar que o arquivo deve estar aberto em um modo que permita a execuo das operaes desejadas. Por exemplo, um arquivo aberto somente para escrita e em seguida reposicionado para o incio, no ir permitir outra operao que no seja escrita.

11.5

Lendo e Escrevendo Caracteres

As operaes mais simples em arquivos so a leitura e escrita de caracteres. Para ler um caractere de um arquivo, que foi previamente aberto, pode-se usar as funes getc() e fgetc(), que so equivalentes. Os prottipos destas funes so os seguintes:

180

int fgetc (FILE *parq); int getc (FILE *parq);

As funes getc() e fgetc() so equivalentes e muitos compiladores implementam getc() como uma macro do seguinte modo:
#dene getc(parq)fgetc(parq)

A funo l o caractere como um unsigned char mas retorna o valor como um inteiro, onde o byte mais signicativo vale zero. O apontador do arquivo avana um caractere e passa a apontar para o prximo caractere a ser lido. A funo devolve o cdigo EOF ao chegar ao nal do arquivo ou caso um erro ocorra. O valor EOF tambm um inteiro vlido e portanto ao usar arquivos binrios necessrio que a funo feof() seja utilizada para vericar o nal do arquivo. A funo ferror() pode ser usada para determinar se um erro ocorreu. Para escrever caracteres h duas funes denidas putc() e fputc(). Os prottipos das funes so os seguintes:
int putc(int ch, FILE *parq); int fputc(int ch, FILE *parq)

onde parq um ponteiro de arquivo para o arquivo que foi previamente aberto por meio da funo fopen() e ch o caractere a ser escrito. O programa 11.2 mostra como um arquivo pode ser criado para leitura e escrita. Em seguida um conjunto de caracteres lido do teclado escrito no arquivo. O prximo passo a leitura do arquivo que iniciada aps uma chamada a funo rewind(), fazendo com que o indicador de posio do arquivo volte a apontar para seu incio. Uma outra alternativa mostrada em 11.3 mostra um exemplo onde o arquivo criado para escrita em seguida fechado e reaberto para leitura cando automaticamente posicionado no incio para a leitura.

11.6

Testando Erros

A funo ferror(FILE *parq) serve para vericar se ocorreu um erro associado ao uxo de dados sendo usado. Um valor diferente de zero a indicao do erro, que ocorre geralmente quando a operao previamente executada no uxo falhou. O parmetro parq um ponteiro para o uxo a ser testado. O programa 11.4 abre um arquivo para leitura, mas tenta escrever um caractere o que provoca um erro que testado pela funo ferror. Listagem 11.4: Uso da funo ferror().
#include < stdio .h > i n t main ( void ) { FILE * pArq ; pArq = fopen ( " MeusDados . txt " ," r " ) ; i f ( pArq == NULL ) {

181

Listagem 11.2: Exemplo de leitura e escrita de caracteres.


#include < stdio .h > #include < stdlib .h > i n t main ( void ) { int c; FILE * pa ; char * nome = " texto . txt " ; /* Abre o arquivo para leitura e escrita */ i f (( pa = fopen ( nome , " w + " ) ) == NULL ) { printf ( " \ n \ nNao foi possivel abrir o arquivo .\ n ") ; exit (1) ; } /* Cada caractere digitado sera gravado no arquivo */ c = getchar () ; while (! feof ( stdin ) ) { fputc (c , pa ) ; c = getchar () ; } rewind ( pa ) ; /* volta ao inicio do arquivo */ printf ( " \ nTerminei de escrever , agora vou ler .\ n ") ; c = fgetc ( pa ) ; while (! feof ( pa ) ) { putchar ( c ) ; c = fgetc ( pa ) ; } fclose ( pa ) ; return 0; }

182

Listagem 11.3: Exemplo de leitura e escrita de caracteres.


#include < stdio .h > i n t main ( void ) { int c; FILE * pa ; char * nome = " texto . txt " ; i f (( pa = fopen ( nome , " w " ) ) == NULL ) { printf ( " \ n \ nErro ao abrir o arquivo - escrita .\ n " ); return 1; } c = getchar () ; while (! feof ( stdin ) ) { fputc (c , pa ) ; c = getchar () ; } fclose ( pa ) ; printf ( " \ nTerminei de escrever , agora vou ler .\ n ") ; i f (( pa = fopen ( nome , " r " ) ) == NULL ) { printf ( " \ n \ nErro ao abrir o arquivo - leitura .\ n " ); exit (1) ; } c = fgetc ( pa ) ; while (! feof ( pa ) ) { putchar ( c ) ; c = fgetc ( pa ) ; } fclose ( pa ) ; return 0; }

183

printf ( " Erro abrindo arquivo . " ); return 1; } else { fputc ( 'x ' , pArq ) ; i f ( ferror ( pArq ) ) { printf ( " Erro escrevendo arquivo \ n " ) ; fclose ( pArq ) ; return 1; } } return 0; }

11.7

Lendo e Escrevendo Cadeias de Caracteres

As funes fgets() e fputs() servem para ler e escrever cadeias de caracteres em arquivos. Os prottipos das funes so:
int fputs(char *str, FILE *parq); int fgets(char *str, int comp, FILE *parq);

A funo fputs() escreve a cadeia de caracteres apontada por str no uxo apontado por parq. O cdigo nulo ao nal da cadeia no copiado para o uxo. O cdigo correspondente EOF ser retornado se ocorrer um erro e um valor no negativo em caso de sucesso. A funo fgets() l uma cadeia de caracteres do uxo especicado por parq at que um caractere de nova linha seja encontrado ou comp-1 caracteres sejam lidos. O caractere de nova linha interrompe a leitura. Observar que diferentemente de gets() o caractere de nova linha encontrado passa a fazer parte da cadeia que recebe um caractere nulo ao seu nal. O ponteiro str retornado caso a leitura ocorra sem erro. No caso de erro o ponteiro str recebe o valor NULL. Se o m do arquivo for encontrado e nenhum caractere foi lido, o contedo de str mantido e um NULL retornado. O exemplo 11.5 mostra um exemplo de uso destas funes para ler e escrever cadeias de caracteres em um arquivo. Listagem 11.5: Exemplo de leitura e escrita de cadeias de caracteres.
#include < stdio .h > #define MAX 80 i n t main ( void ) { char linha [ MAX ]; FILE * pa ; char * nome = " texto . txt " ; i f (( pa = fopen ( nome , " w + " ) ) == NULL ) { printf ( " \ n \ nNao foi possivel abrir o arquivo .\ n ") ;

184

return 1; } fgets ( linha , MAX , stdin ) ; while (! feof ( stdin ) ) { fputs ( linha , pa ) ; fgets ( linha , MAX , stdin ) ; } rewind ( pa ) ; /* volta ao inicio do arquivo */ printf ( " \ nTerminei de escrever , agora vou ler .\ n \ n " ); fgets ( linha , MAX , pa ) ; while (! feof ( pa ) ) { puts ( linha ) ; fgets ( linha , MAX , pa ) ; } fclose ( pa ) ; return 0; }

11.8

Entrada e Sada Formatada

As funes fprintf() e fscanf() so equivalentes as funes printf() e scanf () usadas at agora, sendo a nica modicao o fato de que elas trabalham com uxos de dados (arquivos). Os prottipos das duas funes so os seguintes:
int fprintf(FILE *parq, const char *formatacao, ...); int fscanf(FILE *parq, const char *formatacao, ...);

onde parq um ponteiro de arquivo recebido aps uma chamada a fopen(). Em leituras, a funo retorna o nmero de itens lidos com sucesso. Esta contagem pode igualar o nmero esperado de leituras ou ser menor no caso de falha. Caso ocorra uma falha antes de que uma leitura possa ser feita com sucesso, EOF retornado. Em escritas, caso a operao de escrita tenha sucesso, o nmero total de caracteres escrito retornado. Um nmero negativo retornado em caso de falha. Embora estas duas funes, por sua semelhana com printf() e scanf() , sejam maneiras convenientes de escrever e ler dados de arquivos, elas tm a desvantagem de serem mais lentas do que uso de arquivos binrios. A perda de tempo devido ao fato dos dados serem gravados em ASCII, o que obriga a uma converso dos dados a cada operao realizada. Em alguns casos o fato dos dados serem gravados em ASCII pode ser considerado um vantagem que se sobrepe a desvantagem da reduo de velocidade. Dados gravados em ASCII podem ser facilmente vericados pelos usurios, o que no acontece com dados em binrio. O exemplo 11.6 mostra o uso destas funes para ler e escrever vrios tipos de dados em um arquivo. Listagem 11.6: Exemplo de leitura e escrita de dados formatados.

185

#include < stdio .h > i n t main ( void ) { char palavra [20]; int i; fl oat f; FILE * pa ; char * nome = " format . txt " ; i f (( pa = fopen ( nome , " w + " ) ) == NULL ) { printf ( " \ n \ nNao foi possivel abrir o arquivo .\ n ") ; return 1; } puts ( " Entre com uma palavra . " ) ; scanf ( " % s " , palavra ) ; puts ( " Entre com um numero inteiro . " ) ; scanf ( " % d " , & i ) ; puts ( " Entre com um numero flutuante . " ) ; scanf ( " % f " , & f); /* Escreve os dados no arquivo */ fprintf ( pa , " % s % d % f " , palavra , i , f ) ; rewind ( pa ) ; /* volta ao inicio do arquivo */ printf ( " \ nTerminei de escrever , agora vou ler .\ n ") ; fscanf ( pa , " % s % d % f " , palavra , &i , & f ) ; printf ( " Palavra lida : % s \ n " , palavra ) ; printf ( " Inteiro lido : % d \ n " , i ) ; printf ( " Float lido : % f \ n " , f ) ; fclose ( pa ) ; return 0; }

11.9

Lendo e Escrevendo Arquivos Binrios

As funes fread e fwrite so empregadas para leitura e escrita de dados em modo binrio. Os prottipos das funes so:
size_t fread (void *ptr, size_t size, size_t nmemb, FILE *parq); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *parq);

A funo fread l nmemb objetos, cada um com size bytes de comprimento, do uxo apontado por stream e os coloca na localizao apontada por ptr. A funo retorna o nmero de itens que foram lidos com sucesso. Caso ocorra um erro, ou o m do arquivo foi atingido o valor de retorno menor do que nmemb ou zero. Esta funo no distingue entre um m de arquivo e erro, portanto aconselhvel o uso de feof() ou ferror() para determinar que erro ocorreu. A funo fwrite escreve nmemb elementos de dados, cada um com size bytes de comprimento, para o uxo apontado por stream obtendo-os da localizao apontada por ptr. fwrite retorna o nmero de itens que foram lidos com sucesso. Caso ocorra um erro, ou o m do arquivo foi atingido o valor de retorno menor do que nmemb ou zero. O programa 11.7 ilustra como podemos escrever e ler dados binrios de diferentes tipos em arquivos. Como um dos parmetros da funo o nmero de bytes do dado a ser lido, recomendado o uso de sizeof. 186

Listagem 11.7: Exemplo de leitura e escrita na forma binria.


#include < stdio .h > i n t main ( void ) { i n t inum =10; f l o a t fnum =2.5; double pi =3.141516; char c = 'Z '; FILE * pa ; char * nome = " texto . bin " ; i f (( pa = fopen ( nome , " w + " ) ) == NULL ) { perror ( " fopen : " ) ; return 1; } fwrite (& inum , s i z e o f ( i n t ) , 1 , pa ) ; fwrite (& fnum , s i z e o f ( f l o a t ) , 1 , pa ) ; fwrite (& pi , s i z e o f ( double ) , 1 , pa ) ; fwrite (& c , s i z e o f ( char ) , 1 , pa ) ; rewind ( pa ) ; fread (& inum , s i z e o f ( i n t ) , 1 , pa ) ; fread (& fnum , s i z e o f ( f l o a t ) , 1 , pa ) ; fread (& pi , s i z e o f ( double ) , 1 , pa ) ; fread (& c , s i z e o f ( char ) , 1 , pa ) ; printf ( " %d , %f , %f , % c \ n " , inum , fnum , pi , c ) ; fclose ( pa ) ; return 0; }

Uma das principais aplicaes destas funes a leitura e escrita de estruturas criadas pelos usurios. A gravao em binrio da estrutura permite que o programador ao escrever ou ler do arquivo se preocupe somente com a estrutura como um todo e no com cada elemento que a compe. O programa 11.8 mostra um exemplo onde estruturas so gravadas e lidas de um arquivo. Neste exemplo usado um lao para gravar uma estrutura de cada vez. No entanto, tambm possvel gravar todas as estruturas de uma vez mudando o terceiro parmetro da funo fwrite(). O lao seria substitudo por
fwrite( &turma[i], sizeof (struct pessoa), MAX, pa);

Para testar erro basta vericar o valor retornado pela funo. Caso ela tenha retornado um valor diferente de MAX ocorreu um erro.

187

Listagem 11.8: Exemplo de leitura e escrita de estruturas.


#include < stdio .h > #include < string .h > typedef s t r u c t _PESSOA { char nome [40]; i n t ano ; } PESSOA ; i n t main () { FILE * pa ; char nome [40] , linha [80]; PESSOA turma [4] , back [4]; int i; f o r ( i =0; i <4; i ++) { puts ( " Nome ? " ); fgets ( turma [ i ]. nome , 40 , stdin ) ; turma [ i ]. nome [ strlen ( turma [ i ]. nome ) -1]= ' \0 '; puts ( " Ano ? " ); fgets ( linha , 80 , stdin ) ; sscanf ( linha , " % d " , & turma [ i ]. ano ) ; } puts ( " \ nGravando \ n " ); puts ( " Qual o nome do arquivo ? " ) ; fgets ( nome , 40 , stdin ); nome [ strlen ( nome ) -1]= ' \0 '; i f (( pa = fopen ( nome , " w + " ) ) == NULL ) { puts ( " Arquivo nao pode ser aberto " ) ; return 1; } f o r ( i =0; i <4; i ++) { i f ( fwrite ( & turma [ i ] , s i z e o f ( PESSOA ) , 1 , pa ) != 1) puts ( " Erro na escrita . " ) ; } rewind ( pa ) ; f o r ( i =0; i <4; i ++) { i f ( fread ( & back [ i ] , s i z e o f ( PESSOA ) , 1 , pa ) != 1) { i f ( feof ( pa ) ) break ; puts ( " Erro na leitura . " ) ; } } f o r ( i =0; i <4; i ++) { printf ( " Nome = % s \ n " , back [ i ]. nome ) ; printf ( " Ano = % d \ n \ n " , back [ i ]. ano ) ; } return 0; }

188

11.10

Exerccios

11.1: Escreva um programa que abra um arquivo texto e conte o nmero de caracteres presentes nele. Imprima o nmero de caracteres na tela. 11.2: Considere um arquivo de dados do tipo texto com o seguinte contedo: 3 ZE SA 8.5 10.0 ANTONIO SANTOS 7.5 8.5 SEBASTIAO OLIVEIRA 5.0 6.0 O arquivo acima um exemplo. Considere ento que nestes arquivos a primeira linha contm o nmero de alunos no arquivo. As linhas seguintes contm os seguintes dados: nome do aluno com no mximo 50 caracteres; nota da primeira prova; nota da segunda prova. Escreva um programa que imprima os nomes de todos os alunos que tm a mdia das duas notas menor que 7.0 11.3: Escreva um programa que grave os dados lidos no exerccio anterior em um arquivo do tipo binrio de acesso aleatrio. O nmero que indica quantos alunos devem ser lidos (primeira linha do arquivo) no deve ser gravado no arquivo binrio. Nesta questo o programa deve obrigatoriamente usar um vetor de estruturas do seguinte tipo:
typedef s t r u c t _ALUNO { char nome [81]; f l o a t n1 , n2 ; } ALUNO ;

11.4: Escreva um programa que leia de um arquivo, cujo nome sera fornecido pelo usurio, um conjunto de nmeros reais e armazena em um vetor. O tamanho mximo do vetor e dado pela constante TAM_MAX. A quantidade de nmeros no arquivo varia entre 0 e TAM_MAX. O programa ao nal calcula a media dos nmeros lidos. 11.5: Faa um programa que leia 10 caracteres e armazene em um arquivo 10 cpias de cada um. Exiba o contedo 189

11.6: Crie uma funo que receba duas strings como parmetros, uma com um endereo de arquivo e outra com um texto qualquer, e adicione o texto no m do arquivo. 11.7: Utilizando a funo do exerccio anterior faa um programa que gere 10 arquivos com o nome "Teste"e extenses "01", ..., "10". Cada um contendo o texto "Texto do arquivo [NMERO DO ARQUIVO]". 11.8: Escreva um programa para armazenar o telefone de 5 amigos. O programa deve obrigatoriamente usar a estrutura
typedef s t r u c t _PESSOA { char nome [50]; i n t idade ; f l o a t altura ; char telefone [10]; } PESSOA ;

a ser preenchida pelo usurio antes do armazenamento de cada registro. 11.9: Faa um programa que leia os dados do arquivo gerado no exerccio anterior e salve-os num novo arquivo utilizando uma sada formatada como indicado abaixo. FORMATO: [nome] tem [idade] anos e [altura] de altura Tel.: [telefone] 11.10: Escreva um programa que leia um arquivo texto contendo linhas de dados. Em cada linha do arquivo h o nome de um aluno e duas notas. Estes dados esto separados por ponto e vrgula. Existe um ponto e vrgula ao nal de cada linha. O formato dos dados e o seguinte: ze sa; 10.0; 9.0; antonio silva: 9.0; 7.0; O programa deve ler estes dados e imprimir os valores lidos, a mdia das duas notas e se o aluno foi aprovado ou no (media 5). O formato de sada : ze sa 10.0 8.0 9.0 aprovado antonio silva 9.0 7.0 8.0 aprovado 11.11: Faa um programa que receba o nome de um arquivo e gere uma cpia. 11.12: Escreva um programa que compare dois arquivos especicados pelo usurio e imprima sempre que os caracteres dos dois arquivos coincidirem. Por exemplo: arquivo1.c Ol, pessoal! arquivo2.c Oi, como vai? Neste caso, os caracteres na primeira e dcima primeira posio so iguais nos dois arquivos. A sada do seu programa deve ser algo como:

190

1 - O 11 - a indicando que os primeiros caracteres dos arquivos so iguais (O) bem como o dcimo primeiro (a) 11.13: Um arquivo do tipo texto, chamado numeros.txt contm uma quantidade desconhecida de nmeros reais. Escreva um programa que leia estes nmeros, os coloque em ordem crescente e depois os grave em um arquivo binrio chamado numeros.bin. Observaes: (a) Neste exerccio a quantidade de dados gravados no arquivo do tipo texto desconhecida, portanto, obrigatrio usar um vetor denido com ponteiro. A denio de um vetor com um nmero constante de elementos, mesmo que seja um nmero grande, considerado um erro. (b) Para testar o programa crie o arquivo com um editor simples. 11.14: Um programador escreveu os trechos de programas (I) e (II), mostrados nas listagens 11.9 e 11.10, para ler dados inteiros de um arquivo. O nmero de dados armazenados no arquivo desconhecido. Listagem 11.9: (I) Trecho de programa do problema 14.
while (1) { fscanf (p , " % d " , & i ) ; i f ( feof ( p ) ) break ; printf ( " % d \ n " , i ) ; }

Listagem 11.10: (II) Trecho de programa do problema 14.


fscanf (p , " % d " , & i ) ; while (! feof ( p ) ) { printf ( " % d \ n " , i ) ; fscanf (p , " % d " , & i ) ; }

Qual das opes abaixo verdadeira? (a) Somente o trecho I funciona. (b) Somente o trecho II funciona. (c) Os dois trechos funcionam. (d) Nenhum dos dois trechos funcionam.

191

Captulo 12

Problemas Extras
1a Problema:

Ser que Zeno chega l?


Zeno estava perdido em uma regio desrtica da Terra Mdia, ao norte de Nrnia e a leste do famoso Castelo de Hogwarts. A cidade mais prxima, Forks, cava a vrios dias de caminhada. Sedento e faminto ele j perdia as esperanas, quando avistou, a 1000 metros de onde estava, uma fonte jorrando gua. Zeno comeou a correr, mas chocou-se contra uma barreira mgica que circundava a fonte. No instante em que se chocou contra a barreira ouviu a seguinte mensagem: Forasteiro infeliz, para chegar at a fonte voc deve ter muita pacincia e faa o seguinte: a cada 5 minutos caminhe metade da distncia que ainda falta para chegar at a fonte. Desobedea qualquer uma destas instrues e a morte ser o seu destino. Tarefa A sua tarefa descobrir em quantos minutos Zeno ir chegar a uma distncia da fonte menor do que 103 m. Entrada Este programa no tem entradas. Sada O seu programa deve imprimir em quantos minutos Zeno ir chegar na distncia desejada. 2a Problema:

Estatstica?

192

Uma medida importante em Estatstica o desvio padro representado na equao 12.2 por . Ele d a medida da variabilidade ou disperso de um conjunto de dados. Alm de ser bastante til muito fcil de ser calculado. Considere um conjunto de N nmeros S = {x0 , x1 , x2 , . . . , xN 1 } cuja mdia vale x. Para este conjunto de dados o desvio padro pode ser calculado pela equao 12.2.
N 1 i=0

= =

xi

N 1 N
N 1

(12.1) (xi x)2 (12.2)

i=0

Tarefa A sua tarefa ler um conjunto de dados armazenado em um arquivo do tipo texto, chamado estatistica.txt e calcular a mdia e depois o desvio padro deste conjunto. Observe que o tamanho do conjunto desconhecido e s pode ser descoberto aps a leitura de todos os dados. Neste exerccio obrigatrio o uso de ponteiros para armazenar o vetor. Sada A sada deve informar os valores obtidos para a mdia e o desvio padro. Exemplo de Arquivo de Entrada e da sada Arquivo estatistica.txt: 1 2 3 4 5 6 7 8 9 10 3a Problema: Sada na tela: A media vale 5.500000 O desvio padrao vale 2.872281

Vericando o CPF
Denies O CPF o nmero usado pela Receita Federal no Brasil para identicar os Contribuintes Pessoas Fsicas. O CPF composto por 11 algarismos. Destes 11 algarismos os dois ltimos so usados para vericar se os primeiros 9 foram digitados corretamente. Eles so chamados de algarismos vericadores. Por exemplo, considere o CPF exemplo 12345678909. Este CPF na realidade 193

123456789, os algarismos 09 servem para que os programas da Receita Federal veriquem se os 9 primeiros esto corretos e so gerados automaticamente pelos computadores da Receita quando algum se inscreve. O algoritmo de gerao dos dois ltimos algarismos descrito a seguir. Para o primeiro dgito vericador (v1 ), o 0, no nosso exemplo, o algoritmo o seguinte: multiplique o primeiro algarismo por 10, o segundo por 9, e assim sucessivamente at o nono algarismo do cdigo e some todos estes resultados. Neste exemplo teramos soma1 = (1 10) + (2 9) + (3 8) + + (9 2) Calcule o valor do mdulo 11 de soma1 . Se este valor for 0 ou 1 ento o algarismo v1 0, caso contrrio o algarismo v1 o resultado da subtrao 11 soma1 % 11. Para o segundo dgito vericador (v2 ), no nosso caso o 9, o algoritmo o seguinte: multiplique o primeiro algarismo por 11, o segundo por 10, e assim sucessivamente at o nono algarismo do cdigo e some todos estes resultados. Neste exemplo teramos soma2 = (1 11) + (2 10) + (3 9) + + (9 3) Some este resultado ao dobro do primeiro dgito vericador (soma2 = soma2 + 2 v1 ). Calcule o valor do mdulo 11 desta nova soma2 . Se este valor for 0 ou 1 ento o algarismo v2 0, caso contrrio o algarismo v2 o resultado da subtrao 11 soma2 % 11. Tarefa O programa mostrado na listagem 12.1 faz a vericao de um CPF fornecido pelo usurio. Complete as partes que faltam do programa. Observe que o CPF lido como um vetor de caracteres. Um exemplo de interao do programa com um usurio o seguinte: Entre com o cpf. 12345678909 CPF lido: 12345678909 CPF valido. 4a Problema:

Mais perto, mais longe


Tarefa Z S est planejando a sua prxima viagem por Pindorama. No momento ele gostaria de saber qual so as cidades mais distante e as mais perto da sua. Para fazer estes clculos ele dispe de um arquivo com as coordenadas de sua cidade e das vrias cidades que ele ir visitar Entrada A entrada ser feita a partir de um arquivo texto chamado cidades.txt. A primeira linha deste arquivo um nmero inteiro n dizendo quantas cidades h no arquivo. Considere que o nmero mximo de cidades igual a 50. As n 194

Listagem 12.1: Processando o CPF.


#include < stdio .h > #include < stdlib .h > #include < string .h > #define TAMCPF 11 #define CERTO 1 #define ERRADO 0 int int int int int cpfs ( char *) ; verificaCPF ( char *) ; digito1 ( char *) ; digito2 ( char * , i n t ) ; leCPF ( char *) ;

i n t main ( i n t argc , char * argv []) { char cpfs [ TAMCPF +1]; i n t tam ; tam = leCPF ( cpfs ) ; i f ( tam != TAMCPF ) { puts ( " CPF deve ter 11 digitos . " ) ; return 1; } else { i f ( verificaCPF ( cpfs ) ) { puts ( " CPF valido . " ) ; return 0; } else { puts ( " CPF invalido . " ) ; return 1; } } return 0; } i n t leCPF ( char * cpfs ) { puts ( " Entre com o cpf . " ) ; fgets ( cpfs , TAMCPF +2 , stdin ) ; cpfs [ strlen ( cpfs ) -1] = ' \0 '; printf ( " CPF lido : % s \ n " , cpfs ) ; return strlen ( cpfs ) ; } i n t verificaCPF ( char * cpfs ) { i n t dig1 , dig2 ; dig1 = digito1 ( cpfs ) ; dig2 = digito2 ( cpfs , dig1 ) ; /* AQUI FALTA O FINAL */ } i n t digito1 ( char * cpfs ) /* AQUI FALTA TODA A } i n t digito2 ( char * cpfs , /* AQUI TAMBEM FALTA } { FUNCAO

*/

i n t dig1 ) { TODA A FUNCAO */ 195

linhas seguintes contm pares de nmeros com as coordenadas de cada uma das cidades que Z S ir visitar. O primeiro par de coordenadas pertence a cidade onde Z S vive. Sada A sada composta de trs tipos de linhas e deve ser feita no vdeo. A primeira linha informa as coordenadas da cidade onde Z S vive. Em seguida deve(m) vir as coordenadas da(s) cidade(s) que ca(m) mais perto da cidade de Z S. Aps devem vir a(s) linha(s) que mostra(m) as coordenadas da(s) cidade(s) que ca(m) mais longe da cidade de Z S. O exemplo abaixo mostra o formato do arquivo de entrada e o formato da sada na tela. Exemplo de entrada e sada Arquivo : 8 2.0 2.0 0.0 0.0 1.0 1.0 3.0 3.0 4.0 4.0 0.0 4.0 4.0 0.0 7.0 7.0 5a Problema: Sada na tela: Origem: (2.000000, 2.000000) Mais perto: (1.000000, 1.000000) Mais perto: (3.000000, 3.000000) Mais longe: (7.000000, 7.000000)

Produto Escalar

O produto escalar de dois vetores de n dimenses A = (a1 , a2 , . . . , an ) e B = (b1 , b2 , . . . , bn ), onde (ai , bi ) dado pela equao 12.3 A B = a1 b 1 + a2 b 2 + + an b n Tarefa Escreva um programa que calcule o produto escalar de M pares de vetores, todos em um espao de n dimenses. Neste problema no preciso saber vetores em C. Entrada A entrada consiste das seguintes linhas de dados: 1. A primeira linha contm um nmero inteiro M que informa o nmero de pares de vetores a serem lidos. 2. A segunda linha contm um nmero n que indica a dimenso de cada um dos vetores. 3. As linhas restantes contm as coordenadas dos pares de vetores. Primeiro n linhas com as coordenadas do primeiro par, em seguida n linhas com as coordenadas do segundo par e assim sucessivamente. (12.3)

196

As coordenadas de cada par de vetores so fornecidas da seguinte maneira. Primeiro so fornecidos dois nmeros a1 b1 depois a2 b2 e assim sucessivamente at an bn . Sada Imprimir os M produtos escalares calculados. Exemplo de entrada: 2 4 1 1.5 2 2 3 3.5 4 4 2.0 1.0 2.0 2.0 2.0 3.0 4.0 2.5 6a Problema: Exemplo de sada: 32.000000 22.000000

Desconando do sorteio

H pessoas que desconam de tudo, como h pessoas que acreditam em tudo. Em Pindorama se joga em tudo, Ultrasena, Maxisena, Lotoesportiva etc, desde que seja o governo que recebe todos os lucros. Um dos jogos a Ultrasena, onde os jogadores devem escolher nmeros entre 1 e 60. O jogador que acertar os nmeros sorteados ganha uma frao mnima do total que o governo arrecadou. Um jogador desconado acha que o sorteio viciado e contratou voc para descobrir se isto verdade ou no. Voc deve escrever um programa que leia os N ltimos nmeros inteiros sorteados e conte a frequncia com que cada um dos nmeros foi sorteado. Entrada: Primeiro o programa deve ler o valor de N . Em seguida o programa deve ler a lista de N nmeros inteiros entre 1 e 60. Sada: Imprimir a frequncia com que cada um dos nmeros apareceu. Nmeros com frequncia zero no devem ser impressos. Exemplos de entrada e sada:

197

Exemplo de entrada 12 8 21 14 5 36 21 43 14 6 21 24 43 7a Problema:

Sada para o exemplo de entrada 5 = 1 6 = 1 8 = 1 14 = 2 21 = 3 24 = 1 36 = 1 43 = 2

Convertendo para base 2

Escreva um programa que leia uma sequncia de nmeros inteiros na base 10 e imprima o nmero convertido para base 2 e a maior sequncia de bits 1 que o nmero binrio contm. Considere que o nmero na base 10 menor que 232 1. Entrada: A entrada contm vrios casos de teste. Cada linha de um caso de teste contm um nmero inteiro menor que 232 1 O programa termina quando o usurio fornecer um nmero negativo. Sada: Para cada caso de teste da entrada seu programa deve produzir quatro linhas. Na primeira linha o programa deve imprimir o caso de teste no formato Teste n, onde n nmero do caso de teste comeando em 1. Na segunda linha o nmero convertido para base 2. Zeros esquerda no devem ser impressos. Na terceira linha o seu programa deve imprimir a maior sequncia de 1s do nmero em binrio. A quarta linha deve ser deixada em branco. Exemplos de entrada e sada:

198

Exemplo de entrada 6 25 123456 14 -1

Sada para o exemplo de entrada Teste 1 110 2 Teste 2 11001 2 Teste 3 11110001001000000 4 Teste 4 1110 3

8a Problema:

Calculando reas
Voc foi contratado para escrever um programa que calcula reas de crculos. O programa deve imprimir se um crculo tem rea maior que a rea mdia ou rea menor ou igual a mdia. O seu programa deve usar a estrutura (12.2) para representar cada crculo. Listagem 12.2: Estrutura do problema 8.
typedef s t r u c t _CIRCULO { i n t x , y , raio ; } CIRCULO ;

Entrada Os dados estaro organizados da seguinte maneira. Primeiro, a quantidade N de crculos, em seguida os dados dos N crculos, na seguinte ordem: coordenada x, coordenada y , raio raio. Obs. No possvel assumir um valor mximo para N , aloque o espao de memria necessrio. Sada O programa de imprimir se um dado crculo tem rea maior que a mdia ou rea menor ou igual a mdia. Considerando que o primeiro crculo recebe o nmero um, o segundo o nmero 2 e assim at o crculo N , o formato de sada o seguinte: a palavra Circulo seguida do nmero do crculo e se ele tem rea maior ou menor ou igual. 199

Exemplo de Entrada: 5 1 1 1 1 2 2 3 1 2 2 2 4 1 1 3

Exemplo de Sada: Circulo 1 area menor ou igual Circulo 2 area menor ou igual Circulo 3 area menor ou igual Circulo 4 area maior Circulo 5 area maior

9a Problema:

Lucrando com Aes


Voc foi contratado, pela bolsa de valores de Pindorama, para escrever um programa que imprima as aes com o melhor e o pior desempenho durante o ano de 2325. Entrada A entrada ser lida de um arquivo texto com o nome de acoes.txt. O arquivo consiste de uma srie de linhas. Em cada linha h o nome da ao seguido pelas cotaes no dia 01 de janeiro de 2325 e no do dia 31 de dezembro de 2325. Nenhum nome de empresa ter mais de 20 caracteres. Observe que pode haver mais de uma pior (melhor) ao. Sada A sada dever ser um arquivo do tipo texto, com o nome saida.txt. Neste arquivo voc deve indicar a melhor ao e seu rendimento e a pior ao com sua perda. Observe que pode haver mais de uma melhor (pior) ao. Exemplo de Entrada: lixo 56.00 23.00 bb 100.00 125.00 etai 125.00 110.00 embrair 78.00 156.00 estavel 88.00 88.00 maislixo 56.00 23.00 Exemplo Sada: Pior acao = lixo, variacao -0.59 Melhor acao = embrair, variacao 1.00 Pior acao = maislixo, variacao -0.59

10a Problema:

Somando Linhas
Escreva um programa que leia de um arquivo texto uma matriz quadrada de nmeros reais. O tamanho mximo da matriz 1000 1000. O nome do arquivo de entrada matrizin.txt. O seu programa deve calcular a soma de todos os elementos de cada linha. Em seguida o seu programa deve descobrir qual a maior soma e que linha tem soma igual a maior soma. O programa deve gravar 200

a maior soma e o(s) nmero(s) da(s) linha(s) com soma igual a maior em um arquivo texto chamado matrizout.txt. Entrada Os dados no arquivo de entrada tem o seguinte formato. A primeira linha do arquivo contm o tamanho da matriz (1 N 1000). Em seguida o arquivo contm N N nmeros inteiros em um formato livre, ou seja quantidade de nmeros por linha do arquivo varivel. Sada O arquivo de sada tem o seguinte formato. Primeiro o valor da maior soma das linhas. Em seguida as linhas com soma igual a maior soma. Exemplo de 5 1.0 2.0 3.0 2.0 1.0 2.0 3.0 0.0 0.0 5.0 1.0 0.0 1.0 1.0 1.0 11a Problema: Entrada: 4.0 1.0 0.0 2.0 1.0 2.0 8.0 11.0 6.0 1.0 Exemplo de Sada: 14.000000 1 2 3

Misturando Dados
Uma tarefa muito comum em computao misturar dois vetores dados j ordenados para criar um terceiro tambm ordenado. A sua tarefa escrever um programa que leia os dados de dois vetores e os misture em um terceiro. Considere que o tamanho mximo de cada um dos dois vetores originais desconhecido. Entrada A leitura dos dados vai ser feita da seguinte maneira. Primeiro o programa deve ler o tamanho do primeiro vetor (tam1). Em seguida o programa deve ler tam1 nmeros reais. Aps estas leituras o programa l o tamanho do segundo vetor (tam2). Finalmente, o programa l tam2 nmeros reais. Considerar que os dados do primeiro e do segundo vetor esto em ordem crescente. Sada Aps misturar os dois vetores em um terceiro o programa deve imprimir os tam1 + tam2 nmeros reais armazenados no terceiro vetor. Estes dados devem

201

estar em ordem crescente. Exemplo de Entrada: 5 1.0 4.0 7.0 10.0 12.0 3 1.0 2.0 9.0 Exemplo de Sada: 1.0 1.0 2.0 4.0 7.0 9.0 10.0 12.0

202

Apndice A

Tabela ASCII
A tabela ASCII (American Standard for Information Interchange) usada por grande parte da indstria de computadores para a troca de informaes e armazenamento de caracteres. Cada caractere representado por um cdigo de 8 bits. A Tabela A.1 mostra os cdigos para a tabela ASCII de 7 bits. Existe uma table estendida para 8 bits que inclui os caracteres acentuados. Para saber qual o cdigo de um caractere na base 10 junte o dgito da primeira coluna da tabela com o dgito da primeira linha da tabela. Por exemplo, o cdigo da letra a minscula 97 na base 10. 0 nul nl dc4 rs ( 2 < F P Z d n x 1 soh vt nak us ) 3 = G Q [ e o y 2 stx syn sp * 4 > H R \ f p z 3 etx cr etb ! + 5 ? I S ] g q { 4 eot so can " , 6 @ J T ^ h r | 5 enq si em # 7 A K U _ i s } 6 ack dle sub $ . 8 B L V j t ~ 7 bel dc1 esc % / 9 C M W a k u del 8 bs dc2 fs & 0 : D N X b l v 9 ht dc3 gs 1 ; E O Y c m w

0 1 2 3 4 5 6 7 8 9 10 11 12

Tabela A.1: Conjunto de caracteres ASCII Os caracteres de controle listados acima, servem para comunicao com perifricos e controlar a troca de dados entre computadores. Eles tm o signicado mostrado na Tabela A.2.

203

Carac nul stx eot ack bs lf so dle dc2 dc4 syn can sub fs rs sp

Descrio Caractere nulo Comeo de texto Fim de transmisso Conrmao Volta um caractere Passa para prxima linha Passa para prxima pgina Shift-out Data line escape Controle de dispositivo Controle de dispositivo Synchronous idle Cancela Substitui Separador de arquivo Separador de registro Espao em branco

Carac soh etx enq bel ht vt cr si dc1 dc3 nak etb em esc gs us

Descrio Comeo de cabealho de transmisso Fim de texto Interroga Sinal sonoro Tabulao horizontal Tabulao vertical Passa para incio da linha Shift-in Controle de dispositivo Controle de dispositivo Negativa de conrmao Fim de transmisso de um bloco Fim de meio de transmisso Escape Separador de grupo Separador de unidade

Tabela A.2: Conjunto de cdigos especiais ASCII e seus signicados

204

Apndice B

Palavras Reservadas
Palavras reservadas, tambm as vezes chamadas de palavra chave, servem para propsitos especiais nas linguagens de programao. Servem para declarar tipos de dados ou propriedades de um objeto da linguagem, indicar um comando alm de vrias outras funes. Palavras reservadas no podem ser usadas como nomes de variveis ou funes. asm: Indica que cdigo escrito em assembly ser inserido junto comandos C. auto: Modicador que dene a classe de armazenamento padro. break: Comando usado para sair incondicionalmente dos comandos for, while, switch, and do...while. case: Comando usado dentro do comando switch. char: O tipo de dados mais simples em C, normalmente usado para armazenar caracteres. const: Modicados de dados que impede que uma varivel seja modicada. Esta palavra no existia nas primeiras verses da linguagem C e foi introduzida pelo comit ANSI C. Veja volatile. continue: Comando que interrompe os comandos de repetio for , while , ou do...while e faz que eles passem para a prxima iterao. default: usado dentro do comando switch para aceitar qualquer valor no denido previamente com um comando case. do: Comando de repetio usado em conjunto com o comando while . Pela denio do comando o lao sempre executado pelo menos uma vez. double: Tipo de dados usado para armazenar valores de ponto utuante em preciso dupla. else: Comando que indica um bloco de comandos alternativo que deve ser executado quando a condio testada pelo comando if foi avaliada como FALSA. 205

enum: Tipo denido pelo usurio que permite a denio de variveis que iro aceitar somente certos valores. extern: Modicador de dados que indica que uma varivel ir ser declarada em outra rea do programa. oat: Tipo usado para armazenar valores de ponto utuante. for: Comando de repetio que contm inicializao de variveis, incremento e sees condicionais. Em C o comando for um comando de repetio extremamente exvel, permitindo inmeras possibilidades. goto: Comando que causa um pulo para uma posio do programa marcada com um rtulo. if: Comando de testes usado para mudar o uxo do programa baseada em uma deciso VERDADEIRO/FALSO. int: Tipo de dados usado para armazenar valores inteiros. long: Tipo de dados usado para armazenar valores inteiros com preciso maior do que o tipo int. Nos computadores modernos o tipo long tem a mesma preciso que o tipo int e so usados 4 bytes. register: Especicador de classe de armazenamento que pede que, caso seja possvel, uma varivel deve ser armazenada nos registradores do processador. return: Comando que causa o uxo de instrues do programa abandonar a funo em execuo e retornar para a funo que chamou. Tambm pode ser usado para retornar um nico valor. short: Tipo de dados usado para armazenar valores inteiros em preciso menor do que o tipo int. Neste tipo 2 bytes so usados para armazenar os dados. signed: Modicador usado para indicar que uma varivel pode armazenar tanto valores positivos como negativos. sizeof: Operador que retorna o tamanho em bytes do item fornecido. static: Modicador usado para signicar que o compilador deve preparar o cdigo de forma a reter o valor da varivel. struct: Usado para combinar C variveis de tipos diferentes na mesma estrutura. switch: Comando de desvio usado para permitir que o uxo do programa possa ser mudado para vrias direes diferentes. Usado em conjunto com o comando case. typedef: Modicador usado para criar novos nomes para tipos j existentes. union: Palavra chave usada para permitir mltiplas variveis partilharem o mesmo espao na memria.

206

unsigned: Modicador usado para signicar que uma varivel conter somente valores positivos. void: Palavra usada para signicar que ou a funo no retorna nada ou que um ponteiro deve ser considerado genrico ou ser capaz de apontar para qualquer tipo de dados. volatile: Modicador que signica que uma varivel pode ser alterada. while: Comando de teste que executa uma seo de cdigo enquanto uma condio retorna VERDADEIRO. Em adio a estas as seguintes palavras so reservadas em C++: catch, inline, template, class, new, this, delete, operator, throw, except, private, try, finally, protected, virtual, friend, public. Caso queira escrever programas que possam ser convertidas para a linguagem C++ aconselhvel no us-las.

207

Referncias Bibliogrcas
[Kernighan e Ritchie 1978]KERNIGHAN, B. W.; RITCHIE, D. M. The C Programming Language. Englewood Clis, NJ, USA: Prentice-Hall, Inc, 1978. [Knuth 1973]KNUTH, D. E. The Art of Computer Programming: Fundamental Algorithms - vol. 1. Massachusetts, USA: Addison-Wesley Publishing Company, Inc, 1973. [Schildt 1997]SCHILDT, H. C Completo e Total. So Paulo, Brasil: Makron Books do Brasil Editora, 1997.

208

ndice Remissivo
baco, 21 lgebra booleana, 51 C, 34 abertura de arquivo, 179 algoritmo, 32, 41 Analytical Engine, 23 arquivo, 179 abrir arquivo, 180 fechar arquivo, 181 m de arquivo, 182 assembler, 34 assembly, 33 atribuio, 70 atribuies, 52 Babbage, 22 base 2, 31 Basic, 34 BIOS, 30 bit, 31 byte, 31 C++, 34 cadeia de caractere, 68 char, 61 chipset, 29 circuitos integrados, 26 Cobol, 34 comandos de controle, 53 comandos de repetio, 54 compilador, 35 compiladores, 34 constante caractere, 67 constante em ponto-utuante, 66 constante hexadecimal, 65 constante octal, 64 constantes, 61, 62 declarao de variveis, 69 Delphi, 34 depurao, 35 Dierence Engine, 22 double, 62 DRAM, 30 EDSAC, 26 EEPROM, 30 endereos de memria, 30 ENIAC, 25 EPROM, 30 erros de compilao, 35 Exabyte, 32 nal de linha, 177 Flash memory, 31 uxo de dados, 177 uxo binrio, 178 uxo de texto, 177 Fluxograma, 44, 45 Fortran, 34 gcc, 37 George Boole, 51 Gigabyte, 32 hardware, 27 Harvard Mark I, 24 IDE, 37 int, 61 Integrated Development Environment, 37 Java, 34 Kilobyte, 32 linguagem de alto nvel, 34 linguagem de mquina, 33 linguagem intermediria, 36 Linguagem Natural, 44 linguagem natural, 44 209

linguagens interpretadas, 35 link edition, 35 Lisp, 34 long, 62 Megabyte, 32 memria, 29 memria cache, 29 memria principal, 29 microcomputadores, 28 microprocessador, 28 MINGW, 37 montar, 34 Moore, 19 null, 68 palavra de memria, 31 Pascal, 34 Pascalina, 22 perifricos, 28, 32 Petabyte, 32 pipelining, 27 Prolog, 34 PROM, 30 pseudo-cdigo, 44 Pseudo-linguagem, 44 pseudo-linguagem, 45 RAM, 30 registradores, 29 ROM, 30 signed, 62 sistema operacional, 36 software, 32 soroban, 21 string, 68 Terabyte, 32 text stream, 177 tipos de dados, 61 UCP, 27 Unidade Central de Processamento, 27 Unidade de Controle, 27 Unidade de Entrada e Sada, 27 Unidade Lgica e Aritmtica, 27 unsigned, 62 variveis, 61

varivel, 68 VLSI, 27 void, 62 von Neumann, 26, 47 Z1, 24 Zuze, 24

210

Вам также может понравиться