Vous êtes sur la page 1sur 441

Programao de Computadores I

Faculdade de Computao

UFMS

2009
c 2009, 2008, 2007 Fbio Henrique Viduani Martinez
Copyright
Permission is granted to copy, distribute and/or modify this document under the terms of
the GNU Free Documentation License, Version 1.2 or any later version published by the Free
Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license is included in the section entitled GNU Free Documentation
License.
S UMRIO

0 Boas Vindas! 1

1 Breve Histria do Computador 2


1.1 Pr-histria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Sculo XX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.1 Computadores Eletromecnicos . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.2 Computadores Eletrnicos . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2 Como funciona um computador 19


2.1 Contextualizao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Arquitetura de von Neumann . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3 Algoritmos e programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3 Dicas iniciais 25
3.1 Interface do sistema operacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2 Compilador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.3 Emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

4 Primeiros programas 34
4.1 Digitando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2 Compilando e executando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.3 Olhando o primeiro programa mais de perto . . . . . . . . . . . . . . . . . . . . . 35
4.4 Prximo programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.5 Documentao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5 Entrada e sada 39
5.1 Um exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2 Segundo exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

i
6 Estruturas condicionais 42
6.1 Estrutura condicional simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.2 Estrutura condicional composta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.3 Troca de contedos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

7 Nmeros inteiros 46
7.1 Constantes e variveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.2 Expresses aritmticas com nmeros inteiros . . . . . . . . . . . . . . . . . . . . . 49
7.3 Representao de nmeros inteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

8 Estrutura de repetio while 53


8.1 Motivao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.2 Estrutura de repetio while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

9 Exerccios 57

10 Exerccios 59

11 Expresses com inteiros 62


11.1 Expresses aritmticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
11.2 Expresses relacionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
11.3 Expresses lgicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

12 Estrutura de repetio for 70


12.1 Estruturas de programao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
12.2 Estrutura de repetio for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

13 Estrutura de repetio do-while 76


13.1 Definio e exemplo de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

14 Nmeros com ponto flutuante 79


14.1 Constantes e variveis do tipo ponto flutuante . . . . . . . . . . . . . . . . . . . . 79
14.2 Expresses aritmticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

15 Exerccios 87

16 Exerccios 89

17 Caracteres 91

ii
17.1 Representao grfica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
17.2 Constantes e variveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
17.3 Expresses com caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

18 Tipos de dados bsicos 97


18.1 Tipos inteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
18.2 Nmeros com ponto flutuante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
18.3 Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
18.4 Converso de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
18.5 Tipos de dados definidos pelo programador . . . . . . . . . . . . . . . . . . . . . . 111
18.6 Operador sizeof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
18.7 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

19 Vetores 115
19.1 Motivao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
19.2 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
19.3 Inicializao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
19.4 Exemplo com vetores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
19.5 Macros para constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

20 Invariantes 124
20.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
20.2 Exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

21 Exerccios 129

22 Exerccios 131

23 Cadeias de caracteres 133


23.1 Literais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
23.2 Vetores de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
23.3 Cadeias de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

24 Matrizes 140
24.1 Definio, declarao e uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
24.2 Declarao e inicializao simultneas . . . . . . . . . . . . . . . . . . . . . . . . . 142
24.3 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

iii
25 Exerccios 145

26 Exerccios 147

27 Registros 151
27.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
27.2 Declarao e inicializao simultneas . . . . . . . . . . . . . . . . . . . . . . . . . 154
27.3 Operaes sobre registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
27.4 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

28 Vetores, matrizes e registros 159


28.1 Vetores de registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
28.2 Registros contendo variveis compostas . . . . . . . . . . . . . . . . . . . . . . . . 163

29 Registros com registros 167


29.1 Registros contendo registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
29.2 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

30 Unies e enumeraes 172


30.1 Unies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
30.2 Enumeraes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

31 Depurao de programas 180


31.1 Depurador GDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
31.2 Primeiro contato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
31.3 Sintaxe dos comandos do GDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
31.4 Pontos de parada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
31.5 Programa fonte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
31.6 Verificao de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
31.7 Alterao de dados durante a execuo . . . . . . . . . . . . . . . . . . . . . . . . 186
31.8 Resumo dos comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
31.9 Exemplos de execuo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

32 Eficincia de programas 197


32.1 Programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
32.2 Anlise de algoritmos e programas . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
32.2.1 Ordem de crescimento de funes matemticas . . . . . . . . . . . . . . . 201

iv
32.3 Anlise da ordenao por trocas sucessivas . . . . . . . . . . . . . . . . . . . . . . 202
32.4 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205

33 Introduo s funes 207


33.1 Noes iniciais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
33.2 Definio e chamada de funes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
33.3 Finalizao de programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
33.4 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
33.5 Declarao de funes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

34 Exerccios 222

35 Argumentos e parmetros de funes 225


35.1 Argumentos e parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
35.2 Escopo de dados e de funes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

36 Funes e vetores 231


36.1 Vetores como argumentos de funes . . . . . . . . . . . . . . . . . . . . . . . . . . 231
36.2 Vetores so parmetros passados por referncia . . . . . . . . . . . . . . . . . . . . 232
36.3 Vetores como parmetros com dimenses omitidas . . . . . . . . . . . . . . . . . . 233

37 Funes e matrizes 236


37.1 Matrizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
37.2 Matrizes como parmetros com uma dimenso omitida . . . . . . . . . . . . . . . 237

38 Funes e registros 241


38.1 Tipo registro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
38.2 Registros e passagem por cpia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
38.3 Registros e passagem por referncia . . . . . . . . . . . . . . . . . . . . . . . . . . 245
38.4 Funes que devolvem registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247

39 Recurso 250
39.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
39.2 Exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251

40 Exerccios 255

41 Busca 258

v
41.1 Busca seqencial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
41.2 Busca em um vetor ordenado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259

42 Ordenao: mtodos elementares 266


42.1 Problema da ordenao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
42.2 Mtodo das trocas sucessivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
42.3 Mtodo da seleo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
42.4 Mtodo da insero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268

43 Ordenao por intercalao 270


43.1 Dividir para conquistar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
43.2 Problema da intercalao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
43.3 Ordenao por intercalao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

44 Ordenao por separao 275


44.1 Problema da separao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
44.2 Ordenao por separao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277

45 Biblioteca padro 281


45.1 Qualificadores de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
45.2 Arquivo-cabealho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
45.3 Arquivos-cabealhos da biblioteca padro . . . . . . . . . . . . . . . . . . . . . . . 286
45.3.1 Diagnsticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
45.3.2 Manipulao, teste e converso de caracteres . . . . . . . . . . . . . . . . . 286
45.3.3 Erros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
45.3.4 Caractersticas dos tipos com ponto flutuante . . . . . . . . . . . . . . . . . 288
45.3.5 Localizao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
45.3.6 Matemtica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
45.3.7 Saltos no-locais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
45.3.8 Manipulao de sinais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
45.3.9 Nmero varivel de argumentos . . . . . . . . . . . . . . . . . . . . . . . . 292
45.3.10 Definies comuns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
45.3.11 Entrada e sada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
45.3.12 Utilitrios gerais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
45.3.13 Manipulao de cadeias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
45.3.14 Data e hora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

vi
46 Pr-processador 299
46.1 Funcionamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
46.2 Diretivas de pr-processamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
46.3 Definies de macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
46.4 Incluso de arquivos-cabealhos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
46.5 Compilao condicional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
46.6 Outras diretivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306

47 Programas extensos 308


47.1 Arquivos-fontes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
47.2 Arquivos-cabealhos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
47.3 Diviso de programas em arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 313

48 Operaes sobre bits 316


48.1 Operadores bit a bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
48.2 Trechos de bits em registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

49 Introduo aos apontadores 324


49.1 Variveis apontadoras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
49.2 Operadores de endereamento e de indireo . . . . . . . . . . . . . . . . . . . . . 326
49.3 Apontadores em expresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

50 Apontadores e funes 332


50.1 Parmetros de entrada e sada? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
50.2 Devoluo de apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335

51 Apontadores e vetores 338


51.1 Aritmtica com apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
51.2 Uso de apontadores para processamento de vetores . . . . . . . . . . . . . . . . . 342
51.3 Uso do identificador de um vetor como apontador . . . . . . . . . . . . . . . . . . 343

52 Apontadores e matrizes 349


52.1 Apontadores para elementos de uma matriz . . . . . . . . . . . . . . . . . . . . . 349
52.2 Processamento das linhas de uma matriz . . . . . . . . . . . . . . . . . . . . . . . 350
52.3 Processamento das colunas de uma matriz . . . . . . . . . . . . . . . . . . . . . . 351
52.4 Identificadores de matrizes como apontadores . . . . . . . . . . . . . . . . . . . . 352

vii
53 Apontadores e cadeias de caracteres 354
53.1 Literais e apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
53.2 Vetores de cadeias de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
53.3 Argumentos na linha de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

54 Apontadores e registros 366


54.1 Apontadores para registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
54.2 Registros contendo apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368

55 Uso avanado de apontadores 371


55.1 Alocao dinmica de memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
55.2 Apontadores para apontadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
55.3 Apontadores para funes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377

56 Arquivos 382
56.1 Seqncias de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
56.2 Redirecionamento de entrada e sada . . . . . . . . . . . . . . . . . . . . . . . . . . 383
56.3 Funes de entrada e sada da linguagem C . . . . . . . . . . . . . . . . . . . . . . 384
56.3.1 Funes de abertura e fechamento . . . . . . . . . . . . . . . . . . . . . . . 385
56.3.2 Funes de entrada e sada . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
56.3.3 Funes de controle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
56.3.4 Funes sobre arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
56.3.5 Arquivos do sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
56.4 Exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392

57 Listas lineares 396


57.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
57.2 Busca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
57.3 Insero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
57.4 Remoo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

58 Pilhas 406
58.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
58.2 Operaes bsicas em alocao seqencial . . . . . . . . . . . . . . . . . . . . . . . 406
58.3 Operaes bsicas em alocao encadeada . . . . . . . . . . . . . . . . . . . . . . . 409

viii
59 Filas 412
59.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
59.2 Operaes bsicas em alocao seqencial . . . . . . . . . . . . . . . . . . . . . . . 413
59.3 Operaes bsicas em alocao encadeada . . . . . . . . . . . . . . . . . . . . . . . 415

60 Listas lineares circulares 419


60.1 Alocao encadeada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
60.2 Busca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
60.3 Insero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
60.4 Remoo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421

61 Listas lineares duplamente encadeadas 424


61.1 Definio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
61.2 Busca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
61.3 Insero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
61.4 Remoo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426

ix
A ULA 0

B OAS V INDAS !

Esta aula reservada para ambientar o(a) estudante no laboratrio, fazendo-o(a) logar em
um computador, experimentar o ambiente de trabalho e a ferramenta de suporte a disciplinas
da faculdade (SSD-FACOM), onde dever cadastrar seu perfil de usurio(a) e se cadastrar nas
disciplinas de seu semestre. Essa ser a ferramenta de interface entre o(a) professor(a) e os(as)
estudantes durante todo o curso.
Alm disso, as informaes gerais sobre a disciplina, tais como a forma de avaliao, datas
de provas e trabalhos, critrios de avaliao e mdia final, alm da conduta tica esperada
dos(as) estudantes, so explicitadas nesse ambiente.

1
A ULA 1

B REVE H ISTRIA DO C OMPUTADOR

Nesta aula abordaremos uma pequena histria do computador, desde seu surgimento at
os nossos dias. Sabemos, no entanto, que esta na verdade uma tentativa e que fatos histricos
relevantes podem ter sido excludos involuntariamente. Continuaremos a trabalhar no sentido
de produzir um texto cada vez mais amplo e completo, buscando informaes em outras fontes
ainda desconhecidas.
Este texto baseado principalmente nas pginas1 da Rede Mundial de Computadores [2, 3,
9, 13, 18] e nos livros de Philippe Breton [1] e Russel Shackelford [14].

1.1 Pr-histria

Quando comeou a realizar tarefas mecnicas para sua comodidade e bem estar, o ser hu-
mano predominantemente as mulheres tinha de realizar, hora aps hora, dia aps dia, todos
os clculos repetitivos em tarefas das cincias, do comrcio e da indstria. Tabelas e mais tabe-
las eram preenchidas com os resultados desses clculos, para que no fossem sempre refeitos.
No entanto, erros nesses clculos eram freqentes devido, em especial, ao tdio e descon-
centrao. Alm disso, esses clculos no eram realizados de forma rpida. Assim, muitos
inventores tentaram por centenas de anos construir mquinas que nos ajudassem a realizar
essas tarefas.
O baco2 provavelmente o mais antigo instru-
mento conhecido de auxlio ao ser humano em clcu-
los matemticos. O baco mais antigo foi descoberto
na Babilnia, hoje conhecida como Iraque, e data do
ano de 300 A.C. Seu valor est especialmente no fato
de auxiliar a memria humana durante uma operao
aritmtica. Algum bem treinado no uso de um baco
pode executar adies e subtraes com a mesma velo-
cidade de quem usa uma calculadora eletrnica, mas
outras operaes bsicas so mais lentas. Esse instru-
mento ainda consideravelmente utilizado em pases
Figura 1.1: baco. do Oriente.
1
Essas pginas so muito legais! Vale a pena visit-las!
2
Abacus uma palavra em latim que tem sua origem nas palavras gregas abax ou abakon, que quer dizer tabela e
que, por sua vez, tem sua origem possivelmente na palavra semtica abq, que significa areia (do livro The Universal
History of Numbers de Georges Ifrah, Wiley Press 2000).

2
1.1 P R - HISTRIA 3

John Napier3 , em seu trabalho Mirifici Logarith-


morum Canonis Descriptio de 1614, inventou o logaritmo, uma ferramenta que permitia que
multiplicaes fossem realizadas via adies e divises via subtraes. A mgica consistia na
consulta do logaritmo de cada operando, obtido de uma tabela impressa. A inveno de John
Napier deu origem rgua de clculo, instrumento de preciso construdo inicialmente na In-
glaterra em 1622 por William Oughtred4 e utilizado nos projetos Mercury, Gemini e Apollo da
NASA, que levaram o homem lua.

Figura 1.2: Rgua de clculo.

Leonardo da Vinci5 e Wilhelm Schickard6 , nos sculos XVI e XVII, respectivamente, foram
os primeiros a projetar mquinas de clculo baseadas em engrenagens. A mquina proposta
por da Vinci nunca saiu do papel, mas a mquina de Schickard conhecida como a primeira
calculadora mecnica automtica ou o primeiro computador no programvel. O sucesso de
uma mquina como essa s foi obtido por Blaise Pascal7 em 1642, que aos 19 anos construiu a
Pascaline para ajudar seu pai, um recolhedor de taxas, a somar quantias. A Pascaline realizava
apenas adies e no era muito precisa. Pouco tempo aps Pascal ter proposto sua mquina de
calcular, o alemo Gottfried Leibniz8 , co-inventor do Clculo juntamente com Isaac Newton9 ,
planejou construir uma calculadora10 com as quatro operaes bsicas e que, ao invs de engre-
nagens, empregava cilindros dentados com 10 dentes, possibilitando seu trabalho no sistema
numrico decimal. O sculo seguinte tem poucas inovaes diretamente relacionadas aos com-
putadores, como a escala de temperaturas Fahrenheit11 , o telgrafo e a eletricidade, descoberta
por Benjamin Franklin12 .
Joseph Jacquard13 criou, em 1801, um tear que podia tecer a partir de um padro lido auto-
maticamente de cartes de madeira perfurados e conectados por cordes. Descendentes desses
cartes perfurados so utilizados at hoje em algumas aplicaes. A inveno de Jacquard
incrementou a produtividade de tecidos da poca, mas, em contrapartida, gerou grande de-
3
John Napier ( ), nascido na Esccia, matemtico, fsico, astrnomo e 8o Lorde de Merchistoun.
4
William Oughtred ( ), nascido na Inglaterra, matemtico.
5
Leonardo di ser Piero da Vinci ( ), nascido na Itlia, erudito, arquiteto, anatomista, escultor, enge-
nheiro, inventor, matemtico, msico, cientista e pintor.
6
Wilhelm Schickard ( ), nascido na Alemanha, erudito.
7
Blaise Pascal ( ), nascido na Frana, matemtico, fsico e filsofo.
8
Gottfried Wilhelm Leibniz ( ), nascido na Alemanha, erudito.
9
Sir Isaac Newton ( ), nascido na Inglaterra, fsico, matemtico, astrnomo e filsofo.
10
Ele chamou sua mquina de stepped reckoner.
11
Daniel Gabriel Fahrenheit ( ), nascido na Alemanha, fsico e engenheiro.
12
Benjamin Franklin ( ), nascido nos Estados Unidos, jornalista, editor, autor, filantropo, abolicionista,
funcionrio pblico, cientista, diplomata e inventor. Foi tambm um dos lderes da Revoluo Norte-americana.
13
Joseph Marie Jacquard ( ), nascido na Frana, alfaiate e inventor.

FACOM UFMS
1.1 P R - HISTRIA 4

semprego de operrios da indstria txtil.

Figura 1.3: Cartes perfurados da mquina de Jacquard.

A Mquina de Diferenas 14 , projetada por Charles Babbage15 , era uma mquina de calcular
a vapor e foi criada com o objetivo de atender estratgia expansionista do governo ingls, que
tinha a ambio de se tornar o maior imprio do planeta. Esse foi o projeto mais caro financiado
pelo governo da Inglaterra at ento, mas aps dez anos de tentativas infrutferas, o projeto
ruiu e a mquina no foi terminada. Todavia, Babbage no desistiu e projetou uma segunda
mquina chamada Mquina Analtica 16 que seria alimentada por seis mquinas a vapor, teria
o tamanho de uma casa e era uma mquina de propsito mais geral, j que seria programvel
por meio de cartes perfurados. Alm disso, sua inveno tambm permitiria que os cartes
perfurados, agora de papel, fossem empregados como um dispositivo de armazenamento. Essa
mquina teria ainda um dispositivo que a distinguia das calculadoras e a aproximava do que
conhecemos hoje como computadores: uma sentena condicional.
Ada Byron17 tornou-se amiga de Charles Babbage e ficou fascinada com o as idias da M-
quina Analtica. Apesar de nunca ter sido construda, Ada escreveu diversos programas para
essa mquina e entrou para a histria como a primeira programadora de um computador. Ada
inventou a sub-rotina e reconheceu a importncia do lao como uma estrutura de programao.
Em 1890 o governo norte-americano tinha de realizar o censo da populao dos Estados
Unidos e a previso era de que, da forma como tinha sido realizado o ltimo, o censo de 1890
levaria cerca de 7 anos e meio at ser finalizado. Um prmio foi ento anunciado para o inven-
tor que ajudasse a automatizar o censo de 1890. O prmio foi vencido por Herman Hollerith18 ,
que props a Mesa de Hollerith19 , uma mquina que consistia de um leitor de cartes perfura-
dos que era sensvel aos buracos nos cartes, um mecanismo de engrenagens que podia contar e
um grande expositor de indicadores para mostrar os resultados da computao. Essa mquina
foi projetada com sucesso e o censo de 1890 terminou em somente 3 anos. A partir da Hollerith
fundou uma empresa chamada TABULATING M ACHINE C OMPANY. Em 1911 sua empresa se
une a outras duas e se torna a C OMPUTING TABULATING R ECORDING C ORPORATION CTR.
Sob a presidncia de Thomas Watson20 , a empresa foi renomeada I NTERNATIONAL B USINESS
M ACHINES IBM em 1924.
14
Do ingls Difference Engine.
15
Charles Babbage ( ), nascido na Inglaterra, matemtico, filsofo, engenheiro mecnico e precursor
da Cincia da Computao.
16
Do ingls Analytic Engine.
17
Augusta Ada Byron ( ), nascida na Inglaterra, conhecida como a primeira programadora da histria.
Depois de seu casamento passou a se chamar Augusta Ada King, Condessa de Lovelace.
18
Herman Hollerith ( ), nascido nos Estados Unidos, estatstico e empresrio.
19
Do ingls Hollerith desk.
20
Thomas John Watson ( ), nascido nos Estados Unidos, empresrio.

FACOM UFMS
1.2 S CULO XX 5

Figura 1.4: Mesa de Hollerith.

1.2 Sculo XX

A exploso dos computadores ocorreu no sculo XX. At a metade desse sculo, compu-
tadores eletromecnicos e os primeiros computadores totalmente eletrnicos foram projetados
com fins militares, para realizar clculos balsticos e decifrar cdigos dos inimigos. Eminen-
tes cientistas, que deram origem a quase tudo do que chamamos de Cincia da Computao,
estiveram envolvidos nesses projetos. A partir da segunda metade do sculo, a exploso dos
computadores eletrnicos se deu, quando o computador pessoal passou a fazer parte de nosso
dia a dia.

1.2.1 Computadores Eletromecnicos

No incio do sculo XX a IBM continuava produzindo calculadoras mecnicas que eram


muito utilizadas em escritrios especialmente para realizar somas. No entanto, a demanda por
calculadoras mecnicas que realizassem clculos cientficos comeou a crescer, impulsionada
especialmente pelas foras armadas norte-americanas e pelo seu envolvimento nas Primeira e
Segunda Guerras Mundiais.
Konrad Zuse21 props a srie Z de computadores e, destacadamente, em 1941, o primeiro
computador digital binrio programvel por fita, o Z3, de 22 bits de barramento, relgio interno
com velocidade de 5 Hz e 2.000 rels. O Z4, construdo em 1950, o primeiro computador
comercial, alugado pelo Instituto Federal de Tecnologia da Sua (Eidgenssische Technische
Hochschule Zrich ETH Zrich ). Zuse tambm projetou uma linguagem de programao de
alto nvel, a Plankalkl, publicada em 1948. Devido s circunstncias da Segunda Guerra Mun-
dial, seu trabalho ficou conhecido muito posteriormente nos Estados Unidos e na Inglaterra,
em meados dos anos 60.
O primeiro computador programvel construdo nos Estados Unidos foi o Mark I, proje-
tado pela universidade de Harvard e a IBM em 1944. O Mark I era um computador eletrome-
cnico composto por interruptores, rels, engates e embreagens. Pesava cerca de 5 toneladas,
incorporava mais de 800 quilmetros de fios, media 2,5 metros de altura por 15,5 metros de
21
Konrad Zuse ( ), nascido na Alemanha, engenheiro e pioneiro da computao.

FACOM UFMS
1.2 S CULO XX 6

comprimento e funcionava atravs de um motor de 5 cavalos-vapor. O Mark I podia operar


nmeros de at 23 dgitos. Podia adicionar ou subtrair esses nmeros em 3/10 de segundo,
multiplic-los em 4 segundos e dividi-los em 10 segundos22. Apesar de ser um computador
enorme, com aproximadamente 750 mil componentes, o Mark I podia armazenar apenas 72
nmeros e sua velocidade de armazenamento e recuperao era muito lenta, uma motivao e
um fator preponderante para substituio posterior do computador eletromecnico pelo com-
putador eletrnico. Apesar disso, o Mark I funcionou sem parar por quinze anos.

Figura 1.5: O computador eletromecnico Harvard Mark I.

Uma das primeiras pessoas a programar o Mark I foi uma mulher, Grace Hopper23, que em
1953 inventou a primeira linguagem de programao de alto nvel chamada Flow-matic, vindo
a se tornar posteriormente a linguagem COBOL. Uma linguagem de programao de alto n-
vel projetada com o objetivo de ser mais compreensvel ao ser humano, diferentemente da
linguagem binria, de baixo nvel, compreendida pelo computador. No entanto, uma lingua-
gem de alto nvel necessita de um programa chamado compilador que traduz um programa
em linguagem de alto nvel para um programa em linguagem de baixo nvel, de modo que o
Mark I pudesse execut-lo. Portanto, Grace Hopper tambm construiu o primeiro compilador
conhecido.
22
espantoso observar que aps 45 anos da fabricao do Mark I, um computador podia realizar uma operao
de adio em 1/109 de segundo.
23
Grace Murray Hopper ( ), nascida nos Estados Unidos, cientista da computao e oficial da Marinha
dos Estados Unidos.

FACOM UFMS
1.2 S CULO XX 7

1.2.2 Computadores Eletrnicos

No princpio da era eletrnica, logo no comeo do sculo XX, os computadores substituram


os interruptores e as engrenagens eletromecnicas pelas vlvulas. Em seguida, a revoluo da
microeletrnica permitiu que uma quantidade enorme de fiao produzida de forma artesanal
pudesse ser produzida industrialmente como um circuito integrado. A principal vantagem de
um circuito integrado que milhes de transistores podem ser produzidos e interconectados
em um processo industrial de larga escala. Alm disso, os transistores so minsculos compa-
rados s vlvulas, alm de muito mais confiveis.
As contribuies tambm vieram da teoria. A Cincia da Computao no seria a mesma
no fossem as contribuies de um matemtico ingls nascido em 1912, conhecido como Pai
da Cincia da Computao. No incio do sculo XX, Alan Turing24 formalizou o conceito de al-
goritmo e computao atravs de um modelo terico e formal chamado de mquina de Turing e
formulou a tese que qualquer modelo de computao prtico ou tem capacidades equivalentes
s de uma mquina de Turing ou tem um subconjunto delas25 . Turing, assim como os melhores
crebros da poca, tambm se envolveu com a guerra. Na Segunda Guerra Mundial, no centro
de criptoanlise de Bletchley Park, Inglaterra, trabalhou como criptoanalista na decifragem do
cdigo produzido pela mquina Enigma da Alemanha nazista. Em 1947 participou do desen-
volvimento de um computador eletrnico chamado Manchester Mark I na Universidade de
Manchester, Inglaterra. Em 1952 foi acusado de atos de indecncia nojenta por assumir sua
relao com um homem em Manchester. Turing morreu em 1956 antes de completar 44 anos
aps comer uma ma envenenada. Sua morte foi registrada como suicdio.
Alguns dispositivos da primeira metade do sculo XX reclamam o
ttulo de primeiro computador digital eletrnico. O Z3 de Konrad Zuse,
como j mencionado, era o primeiro computador eletromecnico de pro-
psito geral. Foi o primeiro computador a usar aritmtica binria, era
Turing-completo e era totalmente programvel por fita perfurada. No
entanto, usava rels em seu funcionamento e, portanto, no era eletr-
nico. Entre 1937 e 1942 foi concebido o Computador Atanasoff-Berry
(ABC), projetado por John Atanasoff26 e Clifford Berry27 , que continha
elementos importantes da computao moderna como aritmtica bin-
ria e vlvulas, mas sua especificidade e impossibilidade de armazena-
mento de seus programas o distinguem dos computadores modernos.
O Colossus tambm foi um computador construdo para fins militares
entre os anos de 1943 e 1944. Tommy Flowers28 o projetou para auxiliar
criptoanalistas ingleses a decifrar mensagens criptografadas produzidas
pelas mquinas Lorenz SZ 40 e 42 da Alemanha nazista na Segunda
Guerra Mundial. O Colossus, apesar de utilizar a mais recente tecnolo-
gia eletrnica de sua poca, no era um computador de propsito geral, Figura 1.6: Vlvula.
era programvel de forma limitada e no era Turing-completo.
24
Alan Mathison Turing ( ), nascido na Inglaterra, matemtico, lgico e criptoanalista.
25
Esta afirmao chamada Tese de Church-Turing. Uma linguagem de programao ou um computador abs-
trato Turing-completo se satisfaz essa tese.
26
John Vincent Atanasoff ( ), nascido nos Estados Unidos, fsico.
27
Clifford Edward Berry ( ), nascido nos Estados Unidos, engenheiro eltrico.
28
Thomas Harold Flowers ( ), nascido na Inglaterra, engenheiro.

FACOM UFMS
1.2 S CULO XX 8

De 1940 a 1960

O ttulo de primeiro computador digital de propsito geral e totalmente eletrnico em


geral dado ao ENIAC (Electronic Numerical Integrator and Calculator). Esse computador foi cons-
trudo na Universidade da Pensilvnia entre 1943 e 1945 pelos professores John Mauchly29 e
John Eckert30 obtendo financiamento do departamento de guerra com a promessa de construir
uma mquina que substituiria todos os computadores existentes, em particular as mulheres
que calculavam as tabelas balsticas para as armas da artilharia pesada do exrcito. O ENIAC
ocupava uma sala de 6 por 12 metros, pesava 30 toneladas e usava mais de 18 mil tubos a
vcuo, que eram muito pouco confiveis e aqueciam demasiadamente.

Figura 1.7: O primeiro computador eletrnico ENIAC.

Apesar de suas 18 mil vlvulas, o ENIAC podia armazenar apenas 20 nmeros por vez.
No entanto, graas eliminao de engrenagens, era muito mais rpido que o Mark I. Por
exemplo, enquanto uma multiplicao no Mark I levava 6 segundos, no ENIAC levava 2,8
milsimos de segundo. A velocidade do relgio interno do ENIAC era de 100 mil ciclos por
segundo31. Financiado pelo exrcito dos Estados Unidos, o ENIAC tinha como principal tarefa
verificar a possibilidade da construo da bomba de hidrognio. Aps processar um programa
armazenado em meio milho de cartes perfurados por seis semanas, o ENIAC infelizmente
respondeu que a bomba de hidrognio era vivel.
29
John William Mauchly ( ), nascido nos Estados Unidos, fsico e pioneiro da computao.
30
John Adam Presper Eckert Jr. ( ), nascido nos Estados Unidos, fsico e pioneiro da computao.
31
Diramos que o ENIAC era um computador com velocidade de 100 KHz.

FACOM UFMS
1.2 S CULO XX 9

O ENIAC mostrou-se muito til e vivel economicamente, mas tinha como um de seus
principais defeitos a dificuldade de reprogramao. Isto , um programa para o ENIAC estava
intrinsecamente relacionado a sua parte fsica, em especial a fios e interruptores. O EDVAC
(Electronic Discrete Variable Automatic Computer) foi projetado em 1946 pela equipe de John Mau-
chly e John Eckert, que agregou o matemtico John von Neumann32 e tinha como principais
caractersticas a possibilidade de armazenar um programa em sua memria e de ser um com-
putador baseado no sistema binrio. John von Neumann publicou um trabalho33 descrevendo
uma arquitetura de computadores em que os dados e o programa so mapeados no mesmo
espao de endereos. Essa arquitetura, conhecida como arquitetura de von Neumann ainda
utilizada nos processadores atuais, como por exemplo no Pentium IV.

Figura 1.8: O EDVAC.

Nos anos 50, os computadores eram nada populares e pertenciam a algumas poucas uni-
versidades e ao governo norte-americano. No incio da dcada, John Eckert e John Mauchly
deixam a Universidade de Pensilvnia por problemas de patenteamento de suas invenes e
32
Margittai Neumann Jnos Lajos ( ), nascido na ustria-Hungria, matemtico e erudito. John von
Neumann tem muitas contribuies em diversas reas. Na Cincia da Computao, alm da arquitetura de von
Neumann, props os autmatos celulares. Mas, infelizmente, tambm foi um dos cientistas a trabalhar no Projeto
Manhatan, responsvel pelo desenvolvimento de armas nucleares, tendo sido responsvel pela escolha dos alvos
de Hiroshima e Nagazaki no Japo na Segunda Guerra Mundial, onde explodiriam as primeiras bombas nucleares
da histria e pelo clculo da melhor altura de exploso para que uma maior destruio fosse obtida.
33
First Draft of a Report on the EDVAC.

FACOM UFMS
1.2 S CULO XX 10

fundam sua prpria empresa de computadores, a E CKERT-M AUCHLY C OMPUTER C ORPORA -


TION. Em 1951 projetam o UNIVAC, de UNIVersal Automatic Computer, o primeiro computador
eletrnico comercial produzido em larga escala e que utilizava fita magntica. O primeiro UNI-
VAC foi vendido para o escritrio do censo populacional dos Estados Unidos. Mas devido ao
domnio da IBM, que em 1955 vendia mais computadores que o UNIVAC, a empresa de John
Eckert e John Mauchly passa por muitas dificuldades at ser fechada e vendida.
Em 1955 a B ELL L ABORATORIES, uma empresa de
tecnologia fundada em 1925, produz o primeiro compu-
tador base de transistores. Os transistores eram me-
nores, mais rpidos e aqueciam muito menos que as
vlvulas, o que tornava computadores base de tran-
sistores muito mais eficientes e confiveis. Em 1957 a
IBM anuncia que no usaria mais vlvulas e produz seu
primeiro computador contendo 2.000 transistores. Em
1958, descobertas experimentais que mostravam que
dispositivos semicondutores podiam substituir as vl-
vulas e a possibilidade de produzir tais dispositivo em
Figura 1.9: Um circuito integrado.
larga escala, possibilitaram o surgimento do primeiro
circuito integrado, ou microchip, desenvolvido simul-
taneamente por Jack Kilby34 da T EXAS I NSTRUMENTS e por Robert Noyce35 da FAIRCHILD
S EMICONDUCTOR. Um circuito integrado um circuito eletrnico miniaturizado, consistindo
basicamente de dispositivos semicondutores, produzido na superfcie de um fino substrato de
material semicondutor.

De 1960 a 1980
Nos anos 60, as empresas produziam computado-
res de grande porte ou mainframes. Esses computado-
res eram usados por grandes organizaes para apli-
caes massivas, tipicamente para processamento de
enormes quantidades de dados tais como censo po-
pulacional, estatsticas industriais ou comerciais e pro-
cessamento de transaes financeiras. Faz parte desse
conjunto a srie IBM 1400 da IBM e a srie UNIVAC
1100 da S PERRY-R AND. Naqueles anos, juntamente
com a produo de computadores, dispositivos, perif-
ricos e linguagens de programao foram tambm de-
Figura 1.10: O primeiro mouse. senvolvidos, alm de propostas de padronizao des-
ses novos recursos. Em 1963, Douglas Engelbart36 in-
ventou e patenteou o primeiro mouse de computador. No mesmo ano desenvolvido o cdigo
padro norte-americano para troca de informaes37 para padronizar a troca de dados entre
computadores. Em 1967, a IBM cria o primeiro disco flexvel38 para armazenamento de da-
34
Jack St. Clair Kilby ( ), nascido nos Estados Unidos, engenheiro eltrico. Ganhador do Prmio Nobel
de Fsica em 2000 por sua contribuio na inveno do circuito integrado.
35
Robert Noyce ( ), nascido nos Estados Unidos, fsico. Tambm conhecido como o Mestre do Vale
do Silcio.
36
Douglas C. Engelbart (), nascido nos Estados Unidos, engenheiro eltrico e inventor.
37
Do ingls American Standard Code for Information Interchange ASCII.
38
Disquete ou disco flexvel, do ingls floppy disk.

FACOM UFMS
1.2 S CULO XX 11

dos. Alm disso, as linguagens de programao BASIC, FORTRAN e COBOL foram propostas
nessa dcada. Com o desenvolvimento da indstria de computadores fervilhando, Gordon
Moore39 , atravs de observaes empricas, afirma em 1965 que o nmero de transistores em
um circuito integrado duplicaria a cada 24 meses. Essa afirmao fica conhecida depois como
Lei de Moore. Em 1968, Gordon Moore e Robert Noyce fundam a I NTEL C ORPORATION,
uma empresa fabricante de processadores. Em 1969, um grupo de ex-executivos da FAIRCHILD
S EMICONDUCTOR funda uma empresa de circuitos integrados chamada A DVANCED M ICRO
D EVICES , I NC ., AMD. Em 1969, a AT&T B ELL L ABORATORIES desenvolve o Unix, um exce-
lente sistema operacional utilizado ainda hoje especialmente em servidores. Entre 1969 e 1970,
a primeira impressora matricial e a primeira impressora laser so produzidas.

Figura 1.11: Mquinas perfuradoras de cartes IBM 26.

Como j comentamos, os computadores dos anos 60 e 70 eram computadores de grande


porte estabelecidos em rgos governamentais, universidades e algumas grandes empresas.
Esses computadores podiam ser programados de duas maneiras distintas. A primeira ma-
neira conhecida como compartilhamento de tempo40 onde o computador d a cada usurio
uma fatia de tempo de processamento, comportando, naquela poca, 100 usurios ativos si-
multaneamente. A segunda maneira chamada de modo de processamento em lote41 onde
o computador d ateno total a um programa, mas no h usurios ativos. O programa, no
momento em que o computador se dispe a process-lo, lido a partir de cartes perfurados
que foram previamente preparados em uma mquina perfuradora de cartes.
A dcada de 70 tambm foi muito prolfica no desenvolvimento da computao. Lingua-
39
Gordon Earle Moore (), nascido nos Estados Unidos, qumico e fsico.
40
Do ingls time sharing processing.
41
Do ingls batch processing.

FACOM UFMS
1.2 S CULO XX 12

gens de programao usadas at hoje foram projetadas nessa poca como Pascal, concebida em
1971 por Niklaus Wirth42 , e C, proposta em 1972 por Dennis Ritchie43 . Avanos no projeto de
dispositivos tambm foram obtidos. Datam dessa poca o CD44 , criado em 1972, o primeiro
leitor de discos flexveis, introduzido pela T OSHIBA em 1974, e o primeiro disco flexvel de 5
1/4 produzido em 1978. Ainda, o ano de 1975 marca o nascimento de uma das maiores e
mais bem sucedidas empresas do mundo, a M ICROSOFT, criada por Bill Gates45 e Paul Allen46 .
No mesmo ano, Steve Wozniak47 e Steve Jobs48 fundam a A PPLE C OMPUTERS, tambm uma
das maiores empresas fabricantes de computadores do mundo.
Mas nenhum dos avanos dessa poca foi to
significativo como aquele obtido pela fabricante de
microprocessadores I NTEL, que conseguiu colocar
um computador inteiro em uma nica pastilha
de silcio logo no incio da dcada. Em 1970 a I N -
TEL colocou em produo o microprocessador IN-
TEL 4004, com barramento de 4 bits, velocidade de
108 KHz e 2.300 transistores. A encomenda inicial
partiu da empresa japonesa B USICOM, que tinha a
inteno de utilizar o INTEL 4004 em uma linha
de calculadoras cientficas. Porm, o processador
mais notvel que foi disponibilizado em seguida
pela I NTEL foi o INTEL 8080 em 1974, com bar-
ramento de 8 bits e velocidade de processamento
de 2 MHz. A I NTEL vendia esse processador por
360 dlares como uma brincadeira com a IBM, j
que seu computador de grande porte IBM S/360
custava milhes de dlares. Em 1975, o INTEL
8080 foi empregado no Altair 8800 da M ICRO I NS -
TRUMENTATION AND T ELEMETRY S YSTEMS, con-
siderado o primeiro computador pessoal49 , tinha
1 Kbyte de memria principal e tinha de ser cons-
trudo a partir de um kit de peas que chegavam
pelo correio. Bill Gates, que acabara de ingressar
na Universidade de Harvard, largou seus estudos Figura 1.12: Altair 8800.
para concentrar esforos na escrita de programas
para esse computador.
Em 1976, trs computadores pessoais concorrentes so colocados venda. O primeiro com-
putador pessoal que j vinha montado pela fbrica, o Apple I, lanado pela A PPLE C OMPU -
TERS. Logo em seguida, em 1977, a A PPLE inicia a produo do Apple II, o primeiro computa-
dor pessoal com monitor colorido, projetado com o processador 6502 da MOS T ECHNOLOGY,
de 8 bits de barramento, 1 MHz de velocidade, 4 Mbytes de memria principal e interface de
42
Niklaus E. Wirth (), nascido na Sua, engenheiro eletrnico e cientista da computao.
43
Dennis MacAlistair Ritchie (), nascido nos Estados Unidos, fsico e matemtico.
44
Do ingls compact disk.
45
William Henry Gates III (), nascido nos Estados Unidos, empresrio.
46
Paul Gardner Allen (), nascido nos Estados Unidos, empresrio.
47
Stephan Gary Wozniak (), nascido nos Estados Unidos, engenheiro da computao e filantropo.
48
Steven Paul Jobs (), nascido nos Estados Unidos, empresrio.
49
Do ingls personal computer PC.

FACOM UFMS
1.2 S CULO XX 13

udio-cassete para armazenamento e recuperao de dados em fita cassete. Era o computador


padro empregado na rede de ensino dos Estados Unidos nos anos 80 e 90.

Figura 1.13: O computador pessoal Apple I.

Alm do Apple I, um outro computador pessoal que entrou no mercado de computadores


pessoais a partir de 1976 foi o TRS-80 Model I da TANDY C ORPORATION. Esse computador con-
tinha um microprocessador de 8 bits, o Zilog Z80, de 1,77 MHz de velocidade e tinha memria
de 4 Kbytes.
Tambm em 1976, a I NTEL lana o processador INTEL 8086, ainda conhecido como P1, com
barramento de 16 bits, velocidade de 5 MHz e 29.000 transistores, que d origem arquitetura
de processadores x86. O INTEL 8088 lanado em 1979 baseado no INTEL 8086, mas tem
barramento de dados de 8 bits, permitindo compatibilidade com os processadores anteriores.
Esse processador foi utilizado como o processador padro da linha de computadores pessoais
da IBM, os IBM PCs, a partir de 1981. J em 1980, a IBM contrata Bill Gates e Paul Allen para
desenvolver um sistema operacional para o IBM PC, que eles denominaram Disk Operating
System DOS. O DOS, ou MS-DOS, era um sistema operacional com interface de linha de
comandos.

De 1980 a 2000

Com o mercado de computadores em contnuo crescimento, os anos 80 tambm foram de


muitas inovaes tecnolgicas na rea. O crescimento do nmero de computadores pessoais
nos Estados Unidos nessa poca revela o significado da palavra exploso dos computadores.
Para se ter uma idia, em 1983 o nmero de computadores pessoais em uso nos Estados Unidos
era de 10 milhes, subindo para 30 milhes em 1986 e chegando a mais de 45 milhes em 1988.
Logo em 1982, a A PPLE C OMPUTERS a primeira empresa fabricante de computadores pessoais
a atingir a marca de 1 bilho de dlares em vendas anuais.

FACOM UFMS
1.2 S CULO XX 14

Figura 1.14: O computador pessoal IBM PC.

Processadores, e conseqentemente os computadores pessoais, foram atualizados a passos


largos. A E PSON C ORPORATE H EADQUARTERS, em 1982, introduz no mercado o primeiro
computador pessoal porttil50 . Os processadores da famlia x86 286, 386 e 486 foram lanados
gradativamente pela I NTEL na dcada de 80 e foram incorporados em computadores pessoais
de diversas marcas. O INTEL 80486, por exemplo, era um processador de 32 bits, 50 MHz
e 1,2 milhes de transistores. Processadores compatveis com a famlia x86 da I NTEL foram
produzidos por outras empresas como IBM, T EXAS I NSTRUMENTS, AMD, C YRIX e C HIPS AND
T ECHNOLOGIES. Em 1984, a A PPLE lana com sucesso estrondoso o primeiro MacIntosh, o
primeiro computador pessoal a usar uma interface grfica para interao entre o usurio e a
mquina, conhecido como MacOS. O MacIntosh era equipado com o processador 68000 da
M OTOROLA de 8 MHz e 128 Kbytes de memria. Uma atualizao com expanso de memria
de 1 Mbytes foi feita em 1986 no MacIntosh Plus.
Os programas tambm evoluram na dcada de 80. O sistema operacional MS-DOS partiu
gradativamente da verso 1.0 em 1981 e atingiu a verso 4.01 em 1988. Em 1985, o sistema
operacional Windows 1.0 vendido por 100 dlares pela M ICROSOFT em resposta tendncia
crescente de uso das interfaces grficas de usurios popularizadas pelo MacIntosh. Em 1987 o
Windows 2.0 disponibilizado. Nessa poca a A PPLE trava uma batalha judicial por cpia de
direitos autorais do sistema operacional do MacIntosh contra a M ICROSOFT, pelo sistema ope-
racional Windows 2.03 de 1988, e a H EWLETT-PACKARD, pelo sistema operacional NewWave
de 1989.
Em 1990 Tim Berners-Lee51 prope um sistema de hipertexto que o primeiro impulso da
Rede Mundial de Computadores52 . O primeiro provedor comercial de linha discada da Internet
torna-se ativo em 1991, quando a WWW disponibilizada para o pblico em geral como uma
ferramenta de busca.
50
Tambm conhecido como notebook.
51
Sir Timothy John Berners-Lee (), nascido na Inglaterra, fsico.
52
Do ingls World Wide Web WWW.

FACOM UFMS
1.2 S CULO XX 15

Richard Stallman53 , em 1985, escreve o Manifesto GNU


que apresenta sua motivao para desenvolver o sistema
operacional GNU, o primeiro projeto de software livre pro-
posto. Desde meados dos anos 90, Stallman tem gastado
muito de seu tempo como um ativista poltico defendendo
o software livre, bem como fazendo campanha contra pa-
tentes de software e a expanso das leis de direitos au-
torais. Os mais destacados programas desenvolvidos por
Stallman so o GNU Emacs, o GNU Compiler Collection
(gcc) e o GNU Debugger (gdb). Inspirado pelo Minix, um
sistema operacional baseado no Unix voltado para o ensino,
Linus Torvalds54 projetou em 1991 um sistema operacional
para computadores pessoais chamado Linux. O Linux um
exemplo de destaque do que chamamos de software livre e Figura 1.15: O smbolo do Linux.
de desenvolvimento de cdigo aberto. Seu cdigo fonte, es-
crito na linguagem C, disponibilizado para qualquer pessoa usar, modificar e redistribuir
livremente.
O sistema operacional MS-DOS teve seu fim na dcada de 90, em sua ltima verso co-
mercial 6.22, tendo sido completamente substitudo pelo bem sucedido Windows. A verso
3.0 do Windows vendeu 3 milhes de cpias em 1990. Em 1992, a verso 3.1 vendeu 1 milho
de cpias em apenas 2 meses depois de seu lanamento. J em 1997, aps o lanamento do
Windows 95 em 1995, Bill Gates reconhecido como o homem mais rico do mundo. Nessa
poca de exploso do uso dos sistemas operacionais da M ICROSOFT, os vrus de computador
passaram a infestar cada vez mais computadores e a impor perdas cada vez maiores de tempo,
de recursos e de dinheiro s pessoas e empresas que utilizavam tais sistemas operacionais. Um
dos primeiros e mais famosos vrus de computador o Monkey Virus, um vrus de setor de
boot descoberto no Canad em 1991, que se espalhou muito rapidamente pelos Estados Uni-
dos, Inglaterra e Austrlia. Seguiu-se ao Windows 95 o Windows 98 em 1998 e o Windows ME
em 2000.
A Rede Mundial de Computadores e a Internet tambm mostram um desenvolvimento
destacado nessa poca. O ano de 1993 registra um crescimento espantoso da Internet e 50
servidores WWW j so conhecidos at aquele momento. Em 1994, os estudantes de douto-
rado em engenharia eltrica da Universidade de Stanford Jerry Yang55 e David Filo56 fundam
a Yahoo!, uma empresa de servios de Internet que engloba um portal de Internet, uma ferra-
menta de busca na Internet, servio de e-mail, entre outros. A Yahoo! obtm grande sucesso
entre usurios e em outubro de 2005 sua rede de servios espalhada pelo mundo recebe em
mdia 3,4 bilhes de visitas por dia. Em 1994, o World Wide Web Consortium W3C fundado
por Tim Berners-Lee para auxlio no desenvolvimento de protocolos comuns para avaliao da
Rede Mundial de Computadores. O Wiki foi criado em 1995 pelo Repositrio Padro de Por-
tland, nos Estados Unidos, e um banco de dados aberto edio, permitindo que qualquer
usurio possa atualizar e adicionar informao, criar novas pginas, etc., na Internet. Nesse
mesmo ano, a S UN M ICROSYSTEMS lana a linguagem de programao orientada a objetos
Java, amplamente utilizada hoje em dia para criar aplicaes para a Internet. Os rudimentos
53
Richard Matthew Stallman (), nascido nos Estados Unidos, fsico, ativista poltico e ativista de software.
54
Linus Benedict Torvalds (), nascido na Finlndia, cientista da computao.
55
Jerry Chih-Yuan Yang (), nascido em Taiwan, engenheiro eltrico e empresrio.
56
David Filo (?), nascido nos Estados Unidos, engenheiro da computao e empresrio.

FACOM UFMS
1.2 S CULO XX 16

da ferramenta de busca Google so desenvolvidos em 1996 como um projeto de pesquisa dos


alunos de doutorado Larry Page57 e Sergey Brin58 da Universidade de Stanford. Em 1998 a
pgina do Google disponibilizada na Internet e, atualmente, a ferramenta de busca mais
utilizada na rede. A WebTV tambm disponibilizada em 1996 possibilitando aos usurios
navegar pela Internet a partir de sua TV.
Com relao aos dispositivos eletrnicos, o Barramento Serial Universal USB59 padroni-
zado em 1995 pela I NTEL, C OMPAQ, M ICROSOFT, entre outras. O USB um barramento externo
padronizado que permite transferncia de dados e capaz de suportar at 127 dispositivos pe-
rifricos. Em 1997 o mercado comea a vender uma nova mdia de armazenamento tico de
dados, o Disco Verstil Digital DVD60 , com as mesmas dimenses do CD, mas codificado
em um formato diferente com densidade muito mais alta, o que permite maior capacidade de
armazenamento. Os DVDs so muito utilizados para armazenar filmes com alta qualidade de
som e vdeo. Em 1998 o primeiro tocador de MP3, chamado de MPMan, vendido no Japo
pela empresa S AEHAN.
Os processadores tambm foram vendidos co-
mo nunca nos anos 90. A I NTEL lana em 1993 o
sucessor do INTEL 486, conhecido como Pentium,
ou Pentium I, de 32 bits, 60 MHz e 3,1 milhes de
transistores em sua verso bsica. A partir de 1997,
a I NTEL lana seus processadores seguidamente,
ano aps ano. Em 1997 anuncia a venda dos pro-
cessadores Pentium MMX e Pentium II. O Celeron
produzido a partir de 1998 e em 1999, a I NTEL
anuncia o incio da produo do Pentium III. O
Pentium IV produzido a partir de 2000 e um
processador de 32 bits, 1,4 GHz e 42 milhes de
transistores. A AMD j produzia microprocessa-
dores desde a dcada de 70, mas entrou em forte
concorrncia com a I NTEL a partir dessa poca,
com o lanamento do K5 em 1995, concorrente do
Figura 1.16: Processador Pentium.
Pentium I, de 32 bits, 75 MHz e 4,3 milhes de tran-
sistores em sua primeira verso. Seguiu-se ao K5
o K6 em 1997, de 32 bits, 66 MHZ e 8,8 milhes de transistores, e a srie K7 de 1999, que en-
globa os processadores Athlon e Duron. A partir da dcada de 90, uma fatia considervel das
fbricas produz computadores pessoais com processadores da I NTEL ou da AMD. No incio da
dcada de 90, A PPLE, IBM e M OTOROLA se unem para projetar o processador PowerPC de 32
bits que equipou o PowerMac da A PPLE a partir de 1994. A aceitao desse novo computador
no foi como o esperado e, aps um perodo de baixas, a A PPLE ressurge em 1998 com o iMac,
um computador pessoal com projeto visual arrojado e com a filosofia do tudo-em-um-s. A
dcada de 90 marcada pela facilidade de acesso aos computadores pessoais, j que muitos
deles passam a ser vendidos por menos que 1.000 dlares.
57
Lawrence Edward Page (), nascido nos Estados Unidos, engenheiro da computao e empresrio.
58
Sergey Brin (), nascido na Rssia, matemtico, cientista da computao e empresrio.
59
Do ingls Universal Serial Bus.
60
Do ingls Digital Versatile Disc.

FACOM UFMS
1.2 S CULO XX 17

De 2000 at hoje

O ano de 2000 inicia receoso para governos e grandes empresas pelo medo das conseqn-
cias potencialmente desastrosas da virada do sculo nos sistemas computacionais, o que se
chamou na poca de Bug do Milnio. Alm disso, o juiz Thomas Penfield anuncia a diviso
do imprio M ICROSOFT em duas companhias e Bill Gates deixa o cargo de Chefe-Executivo da
empresa.
A M ICROSOFT lana o Windows XP em 2001 e recentemente o Windows Vista, em janeiro de
2007. A A PPLE disponibiliza o MacOS X 10.0 em 2001 e seguidas novas atualizaes at 2006,
com o MAC OS X 10.5. O Linux tem maior penetrao e aceitao dos usurios de computa-
dores pessoais e diversas distribuies so disponibilizadas como Ubuntu, Mandriva Linux,
Fedora Core e SUSE Linux. Estas distribuies e os projetos comunitrios Debian e Gentoo,
montam e testam os programas antes de disponibilizar sua distribuio. Atualmente, existem
centenas de projetos de distribuio Linux em ativo desenvolvimento, constantemente revi-
sando e melhorando suas respectivas distribuies.
Em 2001 a D ELL passa a ser a maior fabricante de computadores pessoais do mundo. Em
2002, uma empresa de consultoria norte-americana faz uma estimativa que at aquele ano
aproximadamente 1 bilho de computadores pessoais tinham sido vendidos no mundo inteiro
desde sua consolidao.
A I NTEL tem investido mais recentemente sua produo em processadores de 64 bits com
ncleos mltiplos, como por exemplo o Xeon de 2004, o Pentium D de 2005 e o INTEL Core Duo
de 2006. A AMD produz os processadores de 64 bits Duron em 2000 e o Sempron em 2004, am-
bos da srie K7, e o Opteron em 2003 da srie K8, este ltimo um processador de dois ncleos,
para competir com os processadores de mltiplos ncleos, como o Xeon da I NTEL. A A PPLE,
que vinha utilizando o processador PowerPC em sua linha de iMacs, anuncia que a partir de
2006 comear a usar os processadores da I NTEL. A verso de 2006 do iMac usa o processador
INTEL Core 2 Duo. Na lista dos 500 supercomputadores mais rpidos do mundo, a TOP500, de
novembro de 2006, 22,6% dos supercomputadores eram baseados em processadores Opteron
da AMD e 21,6% em processadores Xeon da I NTEL.

Figura 1.17: O processador Opteron da AMD.

FACOM UFMS
1.2 S CULO XX 18

Alguns modelos de computadores experimentais e inovadores tm sido propostos, com


implementaes. O computador de DNA, o computador molecular, o computador qumico e
o computador quntico so exemplos de propostas recentes e promissoras.

Exerccios

1.1 Visite a pgina do Computer History Museum, que contm uma enorme quantidade de
informaes.

1.2 Escolha um ou dois personagens da pr-histria da computao e leia os verbetes sobre


esses personagens na Wikipedia.

1.3 Leia os verbetes associados s duas mulheres Augusta Ada Byron e Grace Murray Hop-
per, que deram origem a muito do que conhecemos como computao.

1.4 Leia o verbete sobre Alan Mathison Turing, o Pai da Cincia da Computao, na Wiki-
pedia.

1.5 Visite a pgina GNU Operating System e leia o verbete GNU na Wikipedia.

1.6 Visite as pgina Free Software Foundation e Fundao Software Livre Amrica Latina.

1.7 Leia o verbete Linux na Wikipedia.

FACOM UFMS
A ULA 2

C OMO FUNCIONA UM COMPUTADOR

Na aula 1 vimos um pequeno histrico sobre os computadores. Nesta aula de hoje, vamos
mencionar brevemente como essas mquinas so organizadas internamente. Como veremos,
John von Neumann foi o responsvel, em 1945, pelo desenvolvimento de um modelo1 de com-
putador que sobrevive at hoje.
Este texto baseado principalmente nas referncias [10, 12].

2.1 Contextualizao

As primeiras mquinas computacionais projetadas tinham programas fixos. Assim, reali-


zavam apenas aquelas tarefas para as quais foram concebidas. Modificar o programa de uma
dessas mquinas significava reestruturar toda a mquina internamente. Mesmo para compu-
tadores como o ENIAC, programar e reprogramar era um processo geralmente laborioso, com
uma quantidade enorme de rearranjo de fiao e de cabos de conexo. Dessa forma, podemos
destacar que a programao dos computadores que foram projetados antes do EDVAC tinha
uma caracterstica curiosa e comum: a programao por meios externos. Ou seja, os programa-
dores, para que essas mquinas executassem seus programas, deviam usar cartes perfurados,
fitas perfuradas, cabos de conexo, entre outros. A pequena disponibilidade de memria para
armazenamento de dados e resultados intermedirios de processamento tambm representava
uma outra dificuldade complicadora importante.
Em 1936, Alan Turing publicou um trabalho [16]2 que descrevia um computador universal
terico, conhecido hoje como mquina universal de Turing. Esse computador tinha capacidade
infinita de armazenamento, onde se poderia armazenar dados e instrues. O termo usado foi o
de computador com armazenamento de programas3 . Sobre esse mesmo conceito, o engenheiro
alemo Konrad Zuse, que projetou a famlia Z de computadores, escreveu um trabalho [19]4
independentemente em 1936. Tambm de forma independente, John Presper Eckert e John
Mauchly, que projetaram o ENIAC na Universidade da Pensilvnia, propuseram um trabalho
em 1943, conforme meno em [11], sobre o conceito de computadores com armazenamento de
programas. Em 1944, no projeto do novo computador EDVAC, John Eckert deixou registrado
que sua equipe estava propondo uma forma inovadora de armazenamento de dados e progra-
mas em um dispositivo de memria enderevel, feito de mercrio, conhecido como linhas de
atraso. John von Neumann, tendo trabalhado com a equipe que construiu o ENIAC, juntou-
1
Veja o verbete von Neumann architecture na Wikipedia.
2
Artigo disponvel na seo Bibliografia complementar na pgina da disciplina no Moodle.
3
Do ingls stored-program computer.
4
Veja mais em The Life and Work of Konrad Zuse, especialmente a parte 10.

19
2.2 A RQUITETURA DE VON N EUMANN 20

se tambm s discusses do projeto desse novo computador e ficou responsvel por redigir o
documento com a descrio dessas idias. Com a publicao desse trabalho [17]5 , divulgado
contendo apenas seu nome, essa organizao interna de um computador passou a ser conhe-
cida como arquitetura de von Neumann, apesar de Turing, Zuse, Eckert e Mauchly terem
contribudo ou j apresentado idias semelhantes. Esse trabalho obteve grande circulao no
meio acadmico e seu sucesso como proposta de implementao de um novo computador foi
imediato. At nossos dias, essa organizao ainda usada nos projetos de computadores mais
conhecidos.

2.2 Arquitetura de von Neumann

As idias publicadas por John von Neumann foram revolucionrias a ponto de estabelecer
um novo paradigma de concepo de computadores para a poca e que permanece vlido at
os nossos dias. Informalmente, a maneira de organizar os componentes que constituem um
computador chamada arquitetura do computador. Assim, como a maioria dos computadores
atuais segue o modelo proposto por von Neumann, veremos aqui uma breve descrio da
assim chamada arquitetura de von Neumann.
Nesse modelo, um computador constitudo por trs componentes principais: (i) a unidade
central de processamento ou UCP6 , (ii) a memria e (iii) os dispositivos de entrada e sada.
A UCP, por sua vez, composta pela unidade lgico-aritmtica ou ULA e pela unidade de
controle ou UC. Esses trs componentes principais esto conectados e se comunicam atravs
de linhas de comunicao conhecidas como barramento do computador.
Podemos ilustrar a arquitetura de von Neumann como na figura 2.1.

11111111111111111
00000000000000000
00000000000000000
11111111111111111
00000000000000000
11111111111111111
memria
00000000000000000
11111111111111111
00000000000000000
11111111111111111
00000000000000000
11111111111111111

111111111
000000000
00
11 000
111
000000000
111111111 UCP
00
11 000
111
000000000
111111111
00
11 000
111
000000000
111111111
00
11
UC
000
111
ULA
000000000
111111111
00
11 000
111
000000000
111111111
000000000
111111111
000000000
111111111

11111
00000
00000
11111 1111
0000
0000
1111
00000
11111 0000
1111
00000
11111
00000
11111
entrada 0000
1111
0000
1111
sada
00000
11111
00000
11111 0000
1111
0000
1111
00000
11111 0000
1111
Figura 2.1: Arquitetura de von Neumann.

A unidade central de processamento responsvel pela execuo das instrues armaze-


nadas, tambm chamadas de programa. Atualmente, o termo UCP quase um sinnimo de
microprocessador. Um microprocessador a implementao de uma UCP atravs de um nico,
5
Artigo disponvel na seo Bibliografia complementar na pgina da disciplina no Moodle.
6
Do ingls central processing unit CPU.

FACOM UFMS
2.2 A RQUITETURA DE VON N EUMANN 21

ou de muito poucos, circuitos integrados. Diversos fabricantes disponibilizam microprocessa-


dores no mercado, como os Core 2 Duo e Core 2 Quad da Intel e os Phenom X3 triple-core e
Phenom X4 quad-core da AMD.
A unidade central de processamento composta tambm pela unidade lgico-aritmtica e
pela unidade de controle. A ULA executa dois tipos de operaes: (i) operaes aritmticas,
como adies e subtraes, e (ii) operaes lgicas ou de comparao, como por exemplo a
verificao se um nmero maior ou igual a outro nmero.
A memria de um computador pode ser vista como uma lista linear de compartimentos ou
clulas. Cada compartimento tem um identificador ou endereo numrico e pode armazenar
uma certa quantidade pequena de informao. A informao armazenada em um comparti-
mento de memria pode ser uma instruo de um programa ou um dado, uma informao
que deve ser processada atravs das instrues. H dois tipos distintos de memria: (i) me-
mrias volteis e (ii) memrias no volteis. As memrias do primeiro tipo necessitam de
uma fonte de energia para que seu contedo seja mantido. As memrias que permitem acesso
aleatrio7 aos seus compartimentos so, em geral, memrias volteis. Esses dispositivos per-
mitem acesso rpido informao armazenada, mas so muito mais caros que os dispositivos
no volteis de memria. O que em geral chamamos de memria principal de um compu-
tador um dispositivo de memria voltil, pois quando desligamos o computador, todas as
informaes armazenadas em seus compartimentos so perdidas. Os dispositivos de armaze-
namento de informaes no volteis so aqueles que, ao contrrio dos primeiros, podem reter
a informao armazenada mesmo sem uma fonte de energia conectada. Como exemplo desses
dispositivos, podemos listar os discos rgidos, discos ticos, fitas magnticas, memrias do tipo
flash, memrias hologrficas, entre outros. Pela forma como em geral so implementados, esses
dispositivos no permitem que as informaes sejam acessadas rapidamente em um compar-
timento qualquer, o que acarreta um tempo maior para busca e recuperao das informaes.
No entanto, o custo de produo de tais dispositivos inferior ao custo dos dispositivos de
memrias volteis.
Se enxergarmos um computador como uma caixa-preta que alimentamos com informaes
e colhemos resultados dessas informaes processadas, podemos associar os seus dispositi-
vos de entrada e sada como aqueles que fazem a comunicao do computador com o mundo
externo. O mundo externo pode ser composto por pessoas ou mesmo outros computadores. Te-
clados, mouses, microfones, cmeras, entre outros, so dispositivos comuns de entrada. Moni-
tores e impressoras so dispositivos de sada. Placas de redes so dispositivos tanto de entrada
como de sada de informaes.
Um computador funciona ento como uma mquina que, a cada passo, carrega uma ins-
truo e dados da memria, executa essa instruo sobre esses dados e armazena os resultados
desse processamento em sua memria. Ento o processo se repete, isto , o computador nova-
mente carrega uma prxima instruo e novos dados da memria, executa essa instruo sobre
os dados e grava os resultados desse processamento em sua memria. Enquanto existirem ins-
trues a serem executadas em um programa, esse processo se repete. Todo computador tem
um sinal de relgio8 que marca esse passo mencionado, que mais conhecido como ciclo do re-
lgio. Dessa forma, o sinal de relgio de um computador usado para coordenar e sincronizar
as suas aes.
7
Do ingls random acess memory RAM.
8
Do ingls clock signal.

FACOM UFMS
2.3 A LGORITMOS E PROGRAMAS 22

Uma outra representao a arquitetura de von Neumann, mais prxima ao que conhecemos
como um computador pessoal, pode ser vista na figura 2.2.

1
0 1
00
1
impressora
1111
0000 0
1 0
10
1 111
000
disco
0000
1111 0
1 0
10
1 000
111
rgido
0000
1111 0
1 0
10
1 000
111
portas
0000
1111 0
1 0
10
1 placa
000
111
mouse
0000
1111 0
1 0
10
1 de disco
000
111
0
1 0
10
1
0
1 0
10
1 DVD-RW
teclado 0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
1111
0000 0
1 0
10
1 111
000
0000
1111 0
1 0
10
1 000
111
0000
1111 0
1 0
10
1 000
111
placa
0000
1111
UCP 0
1 0
10
1 000
111 monitor
0000
1111 0
1 0
10
1 grfica
000
111
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
1111
0000 0
1 0
10
1 111
000
0000
1111 0
1 0
10
1 000
111
placa caixas
memria
0000
1111 0
1 0
10
1 000
111
0000
1111 0
1 0
10
1 de som
000
111 de som
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1
0
1 0
10
1 111
000
0
1 0
10
1 000
111
placa
0
1 0
10
1 000
111
de rede
internet
0
1 0
10
1 000
111
0
1 0
1
01
0
0
1 10
1
barramento

Figura 2.2: Uma outra ilustrao da arquitetura de von Neumann.

2.3 Algoritmos e programas

H uma certa controvrsia sobre como definir de


maneira formal um algoritmo. Informalmente, pode-
mos dizer que um algoritmo uma seqncia precisa,
sistemtica e finita de passos ou instrues para so-
luo de algum problema. Uma tentativa de forma-
lizao do termo teve incio com a tentativa de solu-
o do problema Entscheidungsproblem, ou o problema
da deciso, proposto por David Hilbert em 1928. O
termo algoritmo tem sua origem no cientista persa
Al-Khwarizm9 . A latinizao de seu nome para Al-
goritmi deu origem ao termo algoritmo em portu-
gus e em outras lnguas. O termo algarismo, que
significa dgito numrico, tambm tem origem em seu
nome. Suas principais contribuies incluem inova-
es importantes na matemtica, em especial em l-
gebra e trigonometria. Um de seus mais importantes
livros, Sobre o Clculo com Nmeros Hindus, do ano
de 825, foi responsvel pela disseminao do sistema
Figura 2.3: Al-Khwarizm. hindu de numerao no Oriente Mdio e na Europa.

9
Mohammad ebne Musa Khwarazm, ( ), nascido na Prsia, matemtico, astrnomo e gegrafo.

FACOM UFMS
2.3 A LGORITMOS E PROGRAMAS 23

Um algoritmo ento um conjunto de idias abs-


tratas para soluo de um problema. Lidamos com algoritmos desde nossa infncia em di-
versas situaes. Por exemplo, aprendemos no ensino primrio o algoritmo de Euclides para
obteno do mximo divisor comum entre dois nmeros inteiros. Nesse algoritmo, tomamos
dois nmeros inteiros a e b e se b = 0 ento o mximo divisor comum entre a e b o nmero
a. Caso contrrio, o mximo divisor comum entre a e b dado pelo mximo divisor comum de
b e do resto da diviso de a por b. O processo ento se repete at que b seja igual a 0 (zero).
importante destacar que esse algoritmo sempre obtm um b = 0 em algum dos passos e, por-
tanto, o algoritmo sempre termina. Alm disso, o algoritmo correto, j que sempre produz
uma resposta correta para um par de nmeros inteiros fornecidos como entrada.
Aps o desenvolvimento de um algoritmo para soluo de algum problema, o prximo
passo inform-lo a um computador. No entanto, os computadores no compreendem, e por-
tanto so incapazes de obedecerem e executarem, instrues em uma linguagem natural como
o portugus. Dessa forma, precisamos traduzir nosso algoritmo para um programa em uma
linguagem de programao escolhida. Uma linguagem de programao uma linguagem ar-
tificial projetada para expressar computaes que podem ser executadas por um computador.
Dessa forma, um programa projetado em uma linguagem de programao possui uma sintaxe
e semntica precisas. Alm disso, um programa escrito em uma linguagem de programao
pode ser facilmente traduzido para um programa em linguagem de mquina, que nada mais
que uma seqncia de dgitos binrios (bits) que representam dados e instrues e que produ-
zem o comportamento desejado do computador, em sua arquitetura especfica.
Em geral, trabalhamos com linguagens de programao chamadas de alto nvel, o que
significa que so linguagens de programao mais facilmente compreensveis para os seres hu-
manos. As linguagens C, C++, Java, PHP, entre outras, so linguagens de programao de
alto nvel. Em contraposio, existem linguagens de programao de baixo nvel, que so
muito prximas da arquitetura do computador que usamos. A linguagem assembly uma lin-
guagem de programao de baixo nvel. Infelizmente, programas escritos em uma linguagem
programao qualquer no esto prontos para serem executados por um computador. Ainda
h processos de traduo intermedirios que levam o programa de um ponto compreensvel
por um ser humano e incompreensvel para o computador para o outro extremo, isto , basica-
mente incompreensvel pelo ser humano e completamente compreensvel pelo computador e
sua arquitetura.
Assim, dado um programa em uma linguagem de programao de alto nvel, usamos um
programa especial, chamado compilador, para efetuar a traduo deste programa para um
programa em linguagem assembly, de baixo nvel e associada ao processador do computador
que ir executar as instrues. Um segundo e ltimo processo de traduo ainda realizado,
chamado de montagem, onde h a codificao do programa na linguagem assembly para um
programa em formato binrio, composto por 0s e 1s, e que pode enfim ser executado pela
mquina. Um esquema dos passos desde a concepo de um algoritmo at a obteno de um
programa executvel equivalente em um computador mostrado na figura 2.4.
Modelos conceituais inovadores e alternativos arquitetura de von Neumann tm sido
propostos e alguns deles implementados. O computador de DNA, o computador molecular, o
computador qumico e o computador quntico so exemplos de propostas recentes e promis-
soras.

FACOM UFMS
2.3 A LGORITMOS E PROGRAMAS 24

111111
000000
000000
111111
000000
111111
algoritmo
000000
111111
000000
111111
0
1
000000
111111
0
1
0
1
0
1
0 programao
1
0
1
000000
111111
0
1
000000
111111
000000
111111
programa na
000000
111111
linguagem
000000
111111
000000
111111
000000
111111
X
0
1
0
1
0
1
0 compilao
1
0
1
000000
111111
0
1
000000
111111
000000
111111
000000
111111
programa
000000
111111
assembly
000000
111111
0
1
000000
111111
0
1
0
1
0
1 montagem
0
1
0
1
0
1
000000
111111
000000
111111
programa em
000000
111111
linguagem
000000
111111
000000
111111
de mquina
000000
111111
Figura 2.4: Passos desde a concepo de um algoritmo at o programa executvel por um
computador.

Exerccios

2.1 Leia o verbete Computer na Wikipedia.

2.2 Leia o verbete von Neumann architecture na Wikipedia.

2.3 D uma olhada no trabalho de John von Neumann na referncia [17]10 , sobre a arquite-
tura de von Neumann.

2.4 D uma olhada no trabalho de Alan Turing na referncia [16]11 com a descrio da m-
quina de Turing.

2.5 Visite a pgina sobre a vida e o trabalho de Konrad Zuse.

2.6 Visite as pginas C History e C Programming Language e leia o verbete C (programming


language) na Wikipedia.

10
Artigo disponvel na seo Bibliografia complementar na pgina da disciplina no Moodle.
11
Artigo disponvel na seo Bibliografia complementar na pgina da disciplina no Moodle.

FACOM UFMS
A ULA 3

D ICAS INICIAIS

Nesta aula aprenderemos dicas importantes sobre as ferramentas que mais usaremos du-
rante o desenvolvimento da disciplina. Em particular, veremos a definio de uma interface do
sistema operacional (shell ou Unix shell) e aprenderemos a usar alguns dentre os tantos coman-
dos disponveis para essa interface, aprenderemos alguns comandos e funes do ambiente
de trabalho GNU/Emacs e aprenderemos a usar o compilador da linguagem C da coleo de
compiladores GNU, o gcc.

3.1 Interface do sistema operacional

Podemos definir uma interface de um sistema como um programa que permite a intera-
o entre o sistema e seus usurios. Dessa forma, diversos sistemas contm interfaces desse
tipo. No entanto, esse termo quase que automaticamente associado aos sistemas operacio-
nais, onde uma interface fornecida aos usurios para acesso aos servios do ncleo do sis-
tema operacional. As interfaces dos sistemas operacionais so freqentemente usadas com o
propsito principal de invocar outros programas, mas tambm possuem outras habilidades
adicionais.
H em geral dois tipos de interfaces de sistemas operacionais: as interfaces de linhas de
comando e as interfaces grficas. Neste curso, usaremos as interfaces de linhas de comando
para solicitar servios e executar certos programas nativos do sistema operacional Linux. A
seguir listamos alguns desses comandos.
Antes de mais nada, necessrio saber duas coisas sobre o sistema que temos instalado
em nossos laboratrios. Primeiro, devemos saber que o sistema operacional instalado nesses
computadores o Linux. A distribuio escolhida a Debian, verso Lenny. Essa e uma dis-
tribuio no comercial e livre do GNU/Linux, isto , uma verso gratuita e de cdigo fonte
aberto. O nome Debian vem dos nomes dos seus fundadores, Ian Murdock e de sua esposa,
Deb ra. Diversas distribuies comerciais baseiam-se, ou basearam-se, na distribuio Debian,
como Linspire, Xandros, Kurumin, Debian-BR-CDD, Ubuntu e Libranet. A segunda informa-
o importante sobre a interface grfica do Linux usada nos laboratrios. A interface grfica
completa do ambiente de trabalho a plataforma GNOME. Essa plataforma um projeto co-
laborativo internacional inteiramente baseado em software livre que inclui o desenvolvimento
de arcabouos, de seleo de aplicaes para o ambiente de trabalho e o desenvolvimento de
programas que gerenciam o disparo de aplicaes, manipulao de arquivos, o gerenciamento
de janelas e de tarefas. O GNOME faz parte do Projeto GNU e pode ser usado com diversos
sistemas operacionais do tipo Unix, especialmente aqueles que possuem um ncleo Linux.

25
3.1 I NTERFACE DO SISTEMA OPERACIONAL 26

Isso posto, um dos programas mais comuns disponveis para os usurios do sistema ope-
racional GNU/Linux chamado de terminal, como uma abreviao de terminal de linhas de
comando, que nada mais que uma interface entre o usurio e o sistema operacional. Para
disparar esse programa no gerenciador de janelas GNOME, selecione a opo Aplicaes do
menu principal, depois a opo Acessrios no sub-menu e, em seguida, a opo Terminal.
Um exemplo de uma janela de terminal de linhas de comando do GNOME apresentado na
figura 3.1.

Figura 3.1: Uma interface, ou janela, de linhas de comandos do sistema de janelas GNOME,
chamada no computador Homer pelo usurio fhvm.

Uma janela de linha de comandos possibilita a interao entre o usurio e o computador,


intermediada pelo sistema operacional, onde o primeiro solicita tarefas diretamente ao outro
e, depois de executadas, recebe seus resultados. Daqui por diante, veremos alguns dos co-
mandos e utilitrios que sero mais usados durante o decorrer da disciplina. Alguns deles so
apresentam, at certo ponto, algum grau de dificuldade de compreenso neste momento. To-
davia, importante ressaltar que todos esses comandos e utilitrios sero muito teis a partir
de momentos oportunos, como veremos.
A seguir, apresentamos a tabela 3.1 contendo os principais comandos e utilitrios do sis-
tema operacional, com suas descries breves, os quais mais usaremos no desenvolvimento da
disciplina de Programao de Computadores I. Esta tabela apenas um guia de referncia r-
pida e, apesar da maioria dos comandos e utilitrios serem intuitivos e muito fceis de usar, a
descrio breve contida na tabela infelizmente no nos ensina de fato como fazer uso correto de
cada um deles. Tambm no mostra todas as opes de execuo que em geral todo comando
ou utilitrio possui.

FACOM UFMS
3.1 I NTERFACE DO SISTEMA OPERACIONAL 27

Dessa forma, para aprender gradativa e corretamente a usar os comandos e utilitrios apre-
sentados na tabela 3.1 necessrio: (i) fazer os exerccios ao final desta aula; (ii) usar com
freqncia o utilitrio man ou info , que so interfaces para o manual de referncia dos co-
mandos e ferramentas do sistema1 . Por exemplo, podemos digitar:

prompt$ man comando


ou
prompt$ info comando

onde prompt$ o sinal de pronto do sistema, man e info so utilitrios do sistema e


comando o nome do comando/ferramenta do sistema para o qual informaes do manual
sero exibidas; e (iii) usar a opo --help na chamada de um comando/utilitrio. Essa opo
mostra como us-lo corretamente, listando as opes disponveis. Por exemplo,

prompt$ man --help

Comando Descrio
cat arq mostra o contedo de arq ou da entrada padro
cd dir troca o diretrio atual para o diretrio dir
cmp arq1 arq2 compara arq1 e arq2 byte a byte
cp arq1 arq2 copia o arq1 para o arq2
cp arq(s) dir copia arq(s) para o diretrio dir
date mostra a data e a hora do sistema
diff arq1 arq2 compara arq1 e arq2 linha a linha
echo args imprime args
enscript arq converte arquivos-texto para PostScript, HTML, RTF, ANSI e outros
fmt arq formata arq
gprof arq fornece um perfil de execuo do programa arq
indent arq faz a indentao correta de um programa na linguagem C em arq
info cmd mostra a pgina (mais completa) de manual de cmd
ls arq(s) lista arq(s)
ls dir lista os arquivos no diretrio dir ou no diretrio atual
man cmd mostra a pgina de manual de cmd
mkdir dir(s) cria diretrio(s) dir(s)
mv arq1 arq2 move/renomeia o arq1 para o arq2
mv arq(s) dir move arq(s) para o diretrio dir
pwd mostra o nome do diretrio atual
pr arq converte arq, do tipo texto, para impresso
rm arq(s) remove arq(s)
rmdir dir(s) remove diretrios vazios dir(s)
sort arq(s) ordena as linhas de arq(s) ou da entrada padro
wc arq(s) conta o nmero de linhas, palavras e caracteres em arq(s) ou na entrada padro
who imprime a identificao do usurio do computador

Tabela 3.1: Tabela de comandos e utilitrios mais usados.


1
Esses manuais tambm podem ser encontrados em pginas da internet, como Manpages e SS64.com.

FACOM UFMS
3.1 I NTERFACE DO SISTEMA OPERACIONAL 28

Para aumentar a flexibilidade e facilidade na especificao de argumentos de comandos do


sistema operacional, podemos usar caracteres especiais de substituio, chamados de coringas.
Como veremos, os coringas so muito usados quando os argumentos dos comandos so nomes
de arquivos.
O caractere * (asterisco) um coringa que substitui zero ou mais caracteres. No comando
apresentado a seguir:

prompt$ ls *.c

prompt$ o sinal de pronto do sistema, ls um comando que permite listar os arquivos


de um diretrio e *.c um argumento desse comando que representa todos os arquivos do
diretrio cujos nomes terminam com os caracteres .c .
O caractere ? (ponto de interrogao) um outro coringa muito usado como parte de
argumentos de comandos, representando exatamente um caractere e que pode ser um caractere
qualquer. Por exemplo, no comando abaixo:

prompt$ ls ???.c

prompt$ o sinal de pronto do sistema, ls um comando que permite listar os arquivos


de um diretrio e ???.c um argumento desse comando que representa todos os arquivos
do diretrio cujos nomes contm exatamente 3 caracteres iniciais quaisquer seguidos pelos
caracteres .c .
Os colchetes [ ] tambm so caracteres coringas muito usados nos argumentos de coman-
dos e indicam a substituio de um nico caractere especificado no interior dos colchetes. No
exemplo abaixo:

prompt$ ls *[abc]

o argumento *[abc] representa todos os arquivos do diretrio cujos nomes terminam com o
caractere a , b ou c .
Um intervalo de caracteres pode ser obtido no interior dos colchetes se o caractere -
usado. Por exemplo:

prompt$ ls *[a-z]

onde o argumento representa os nomes dos arquivos que terminam com uma das 26 letras do
alfabeto.
Por fim, importante destacar que muitos comandos do sistema operacional recebem infor-
maes de entrada a partir do terminal e tambm enviam as informaes resultantes de sada
para o terminal de comandos. Um comando em geral l as informaes de entrada de um lo-
cal chamado de entrada padro, que pr-definido como o terminal de linha de comandos, e
escreve sua sada em um local chamado de sada padro, que tambm pr-definido como o
terminal de linhas de comando.
Por exemplo, o comando sort pode ordenar nomes fornecidos linha a linha por um usu-
rio no terminal de comandos, ordenar esses nomes lexicograficamente e apresentar o resultado
no terminal de linha de comandos:

FACOM UFMS
3.1 I NTERFACE DO SISTEMA OPERACIONAL 29

prompt$ sort
Zenaide
Carlos
Giovana
Amanda
Ctrl-d
Amanda
Carlos
Giovana
Zenaide
prompt$

Se dispomos os caracteres > arq ao final de qualquer comando ou utilitrio que normal-
mente envia sua sada para a sada padro, ou seja, para o terminal de linha de comandos,
o sada desse comando ser escrita para o arquivo com nome arq ao invs do terminal. Por
exemplo, a seqncia a seguir:

prompt$ sort > nomes.txt


Zenaide
Carlos
Giovana
Amanda
Ctrl-d
prompt$

faz com que quatro nomes digitados pelo usurio sejam ordenados e, em seguida, armazenados
no arquivo nomes.txt .
Assim como a sada dos comandos e utilitrios pode ser redirecionada para um arquivo, a
entrada tambm pode ser redirecionada a partir de um arquivo. Se, por exemplo, existe um
arquivo temp.txt contendo diversos nomes apresentados linha a linha, ento o comando a
seguir:

prompt$ sort < temp.txt

faz com que todos os nomes contidos no arquivo temp.txt sejam ordenados lexicografica-
mente e posteriormente apresentados seguida na sada padro, isto , na janela do terminal de
linha de comandos.
Podemos ainda redirecionar a entrada e a sada de um comando concomitantemente. Por
exemplo, o comando:

prompt$ sort < temp.txt > nomes.txt

recebe, pelo redirecionamento da entrada, os nomes contidos no arquivo temp.txt , ordena-


os lexicograficamente e os armazena em um novo arquivo chamado nomes.txt , devido ao
redirecionamento da sada.
Dessa forma, temos que os smbolos < e > so os smbolos de redirecionamento de en-
trada e sada, respectivamente.

FACOM UFMS
3.2 C OMPILADOR 30

3.2 Compilador

A coleo de compiladores GNU (GCC) , na verdade, um arcabouo de compiladores para


diversas linguagens de programao. Esse sistema foi desenvolvido pelo Projeto GNU e, por
ter sido adotado como o compilador oficial do sistema operacional GNU, tambm foi adotado
como compilador padro por muitos dos sistemas operacionais derivados do Unix, tais como
o GNU/Linux, a famlia BSD e o Mac OS X. Alm disso, o GCC tem sido traduzido para uma
grande variedade de arquiteturas.
Richard Stallman desenvolveu a primeira verso do GCC em 1987, como uma extenso de
um compilador j existente da linguagem C. Por isso, naquela poca o compilador era conhe-
cido como GNU C Compiler. No mesmo ano, o compilador foi estendido para tambm compilar
programas na linguagem C++. Depois disso, outras extenses foram incorporadas, como For-
tran, Pascal, Objective C, Java e Ada, entre outras. A Fundao para o Software Livre distribui
o GCC sob a Licena Geral Pblica (GNU GPL GNU General Public License). O GCC um
software livre.
Um compilador faz o trabalho de traduzir um programa na linguagem C, ou de outra lin-
guagem de alto nvel qualquer, para um programa em uma linguagem intermediria, que ainda
ser traduzido, ou ligado, para um programa em uma linguagem de mquina. Dessa forma,
um argumento bvio que devemos fornecer ao GCC o nome do arquivo que contm um
programa na linguagem C. Alm disso, podemos informar ao compilador um nome que ser
atribudo ao programa executvel resultante da compilao e ligao. H tambm diversas op-
es disponveis para realizar uma compilao personalizada de um programa e que podem
ser informadas no processo de compilao.
Ento, o formato geral de compilao de um programa dado a seguir:

prompt$ gcc programa.c -o executvel -Wall -ansi -pedantic

onde gcc o programa compilador, programa.c um arquivo com extenso .c que contm
um programa fonte na linguagem C, -o uma opo do compilador que permite fornecer um
nome ao programa executvel resultante do processo ( executvel ), -Wall uma opo que
solicita que o compilador mostre qualquer mensagem de erro que ocorra durante a compilao,
-ansi uma opo que fora o programa fonte a estar escrito de acordo com o padro ANSI
da linguagem C e -pedantic uma opo que deixa o compilador muito sensvel a qualquer
possvel erro no programa fonte.
No momento, ainda no escrevemos um programa na linguagem C e, por isso, no con-
seguimos testar o compilador de forma satisfatria. Esta seo ento apenas um guia de
referncia rpida que ser muito usada daqui por diante.

3.3 Emacs

Em geral, usamos a palavra Emacs para mencionar um editor/processador de textos, mas


Emacs , na verdade, o nome de uma classe de editores/processadores que se caracteriza espe-
cialmente pela sua extensibilidade. O Emacs tem mais de 1.000 comandos de edio, mais do
que qualquer outro editor existente, e ainda permite que um usurio combine esses comandos
em macros para automatizao de tarefas. A primeira verso do Emacs foi escrita em 1976

FACOM UFMS
3.3 E MACS 31

por Richard Stallman. A verso mais popular do Emacs o GNU/Emacs, uma parte do Pro-
jeto GNU. O GNU/Emacs um editor extensvel, configurvel, auto-documentado e de tempo
real. A maior parte do editor escrita na linguagem Emacs Lisp, um dialeto da linguagem de
programao funcional Lisp.
O Emacs considerado por muitos especialistas da rea como o editor/processador de
textos mais poderoso existente nos dias de hoje. Sua base em Lisp permite que se torne con-
figurvel a ponto de se transformar em uma ferramenta de trabalho completa para escritores,
analistas e programadores.
Em modo de edio, o Emacs comporta-se como um editor de texto normal, onde o pressi-
onamento de um caractere alfanumrico no teclado provoca a insero do caractere correspon-
dente no texto, as setas movimentam o ponto ou cursor de edio, a tecla Backspace remove
um caractere, a tecla Insert troca o modo de insero para substituio ou vice-versa, etc. Co-
mandos podem ser acionados atravs do pressionamento de uma combinao de teclas, pres-
sionando a tecla Ctrl e/ou o Meta/Alt juntamente com uma tecla normal. Todo comando
de edio de fato uma chamada de uma funo no ambiente Emacs Lisp. Alguns comandos
bsicos so mostrados na tabela 3.2. Observe que a tecla Ctrl representada por C e a tecla
Alt por M . Um manual de referncia rpida est disponvel na bibliografia complementar na
pgina da disciplina.
Para carregar o Emacs, podemos usar o menu principal do GNOME e escolher a opo Apli-
caes, escolhendo em seguida a opo Acessrios e ento a opo Emacs. Ou ento, de uma
linha de comandos, podemos usar:
prompt$ emacs &

Vejamos a tabela 3.2, que contm um certo nmero de comandos2 importantes.

Teclas Descrio
C-z minimiza a janela do Emacs
C-x C-c finaliza a execuo do Emacs
C-x C-f carrega um arquivo no Emacs
C-x C-s salva o arquivo atual no disco
C-x i insere o contedo de um outro arquivo no arquivo atual
C-x C-w salva o contedo do arquivo em um outro arquivo especificado
C-h r carrega o manual do Emacs
C-h t mostra um tutorial do Emacs
C-g aborta o comando parcialmente informado
C-s busca uma cadeia de caracteres a partir do cursor
C-r busca uma cadeia de caracteres antes do cursor
M-% interativamente, substitui uma cadeia de caracteres

Tabela 3.2: Uma pequena tabela de comandos do Emacs.

importante observar que o Emacs, quando executado em sua interface grfica, tem a
possibilidade de executar muitos de seus comandos atravs de botes do menu ou atravs da
barra de menus. No entanto, usurios avanados preferem usar os atalhos de teclado para esse
tipo de execuo, por terem um acesso mais rpido e mais conveniente depois da memorizao
dessas seqncias de teclas.
2
Veja tambm o carto de referncia do Emacs na seo de bibliografia complementar da disciplina no Moodle.

FACOM UFMS
3.3 E MACS 32

Exerccios

3.1 Abra um terminal. A partir de seu diretrio inicial, chamado de home pelo sistema ope-
racional, faa as seguintes tarefas:

crie trs diretrios com os seguintes nomes: progi, algodadi e temp;


entre no diretrio progi;
crie trs subdiretrios dentro do diretrio progi com os seguintes nomes: aulas,
trabalhos e provas;
entre no diretrio aulas;
usando o comando cat e o smbolo de redirecionamento de sada > , crie um ar-
quivo com nome agenda.txt, que contm linha a linha, nome e telefone de pes-
soas. Adicione pelo menos 10 nomes nesse arquivo;
copie o arquivo agenda.txt para os diretrios algodadi e temp;
verifique o contedo dos diretrios progi/aulas, algodadi e temp;
use o comando sort para ordenar o contedo do arquivo agenda.txt. Execute
o comando duas vezes: na primeira, use-o visualizando o resultado na tela do com-
putador. Na segunda, redirecione a sada desse mesmo comando, criando um novo
arquivo com nome agenda-ordenada.txt;
verifique o contedo do diretrio;
compare os arquivos agenda.txt e agenda-ordenada.txt com os comandos
cmp e diff ;
crie um arquivo com nome frase contendo uma frase qualquer, com pelo menos
20 caracteres. Da mesma forma como antes, use o comandos cat e o smbolo > de
redirecionamento de sada;
conte o nmero de palavras no arquivo frase usando o comando wc ;
neste diretrio, liste todos os arquivos que comeam com a letra a;
neste diretrio, liste todos os arquivos que comeam com a letra f;
neste diretrio, liste todos os arquivos que terminam com a seqncia de caracteres
txt;
repita os trs ltimos comandos, usando no entanto o comando echo ;
liste todos os arquivos contidos neste diretrio, mas redirecione a sada do comando
para um arquivo de nome lista;
execute o comando ls [afl]* e verifique a sada;
execute o comando ls [a-l]* e verifique a sada;
execute o comando ls ?g* e verifique a sada;
execute o comando ls ?[gr]* e verifique a sada;
execute o comando ls ????? e verifique a sada;
execute o comando ls .. e verifique a sada;
execute o comando ls /progi e verifique a sada;
execute o comando ls e verifique a sada;

FACOM UFMS
3.3 E MACS 33

mova os arquivos cujos nomes tm exatamente 5 caracteres neste diretrio para o


diretrio temp;
remova o diretrio temp do seu sistema de arquivos;
remova todos os arquivos criados at aqui, mantendo apenas os diretrios.

3.2 Abra o Emacs. Ento, realize as seguintes tarefas:

digite um texto que resume o que voc aprendeu nas aulas de Programao de Com-
putadores I at aqui;
salve o arquivo no diretrio progi/aulas;
busque as palavras computador, Turing, von Neumann, ENIAC, ls, cp e Emacs
no seu texto;
troque a palavra Emacs no seu texto pela palavra GNU/Emacs.

FACOM UFMS
A ULA 4

P RIMEIROS PROGRAMAS

Nesta aula aprenderemos a codificar nossos primeiros programas na linguagem C.

4.1 Digitando

Abra o seu Emacs, pressione C-x C-f , digite em seguida um nome para seu primeiro
programa, como por exemplo primeiro.c, e ento digite o seguinte cdigo.

Programa 4.1: Primeiro programa.

1 #include <stdio.h>
2 int main(void)
3 {
4 printf("Programar bacana!\n");
5 return 0;
6 }

Depois de ter digitado o programa, pressione as teclas C-x C-s para salv-lo em um dire-
trio adequado da sua rea de trabalho.
Na linguagem C, letras minsculas e maisculas so diferentes. Alm disso, em progra-
mas na linguagem C no h distino de onde voc inicia a digitao das suas linhas e, assim,
usamos essa caracterstica a nosso favor, adicionando espaos em algumas linhas do programa
para facilitar sua leitura. Essa adio de espaos em uma linha chamada indentao ou tabu-
lao. No Emacs, voc pode usar a tecla Tab para adicionar indentaes de forma adequada.
Veremos que a indentao uma prtica de programao muito importante.

4.2 Compilando e executando

Abra um terminal onde possa digitar comandos, solicitando ao sistema operacional que os
execute. Ento, compile o programa 4.1 com o compilador gcc:

prompt$ gcc primeiro.c


prompt$

34
4.3 O LHANDO O PRIMEIRO PROGRAMA MAIS DE PERTO 35

Depois disso, o programa executvel a.out estar disponvel para execuo no mesmo
diretrio. Ento s digitar a.out (ou ./a.out) na linha de comando do terminal para ver o
resultado da execuo:

prompt$ a.out
Programar bacana!
prompt$

O nome a.out um nome padro que o compilador gcc d aos arquivos executveis resul-
tantes de uma compilao. Em geral, atribumos um nome mais significativo a um programa
executvel, como por exemplo o mesmo nome do programa na linguagem C, mas sem a sua
extenso. No exemplo acima, o programa executvel associado tem o nome primeiro. O
compilador gcc pode gerar um executvel dessa forma como segue:

prompt$ gcc primeiro.c -o primeiro


prompt$

4.3 Olhando o primeiro programa mais de perto

A primeira linha do nosso primeiro programa

#include <stdio.h>

ser muito provavelmente includa em todo programa que voc far na linguagem C. Essa
linha fornece informaes ao compilador sobre a funo de sada de dados de nome printf ,
que usada depois no programa.
A segunda linha do programa

int main(void)

informa ao compilador onde o programa inicia de fato. Em particular, essa linha indica que
main , do ingls principal, o incio do programa principal e, mais que isso, que esse trecho do
programa na verdade uma funo da linguagem C que no recebe valores de entrada ( void )
e devolve um valor inteiro ( int ). Esses conceitos de funo, parmetros de entrada de uma
funo e valor de sada sero elucidados oportunamente. Por enquanto, devemos memorizar
que essa linha indica o incio de nosso programa.
A prxima linha contm o smbolo abre-chave { que estabelece o incio do bloco de co-
mandos do programa. Em seguida, a prxima linha contm uma chamada funo printf :

printf("Programar bacana!\n");

Essa funo tem um nico argumento ou parmetro, que a seqncia de smbolos da tabela
ASCII printf("Programar bacana!\n"); . Note que todos os smbolos dessa seqncia
so mostrados na sada, a menos de \n , que tem um significado especial quando sua impres-
so solicitada por printf : saltar para a prxima linha. A funo printf uma rotina

FACOM UFMS
4.4 P RXIMO PROGRAMA 36

da biblioteca stdio.h da linguagem C que mostra o seu argumento na sada padro, que
geralmente o monitor.
Depois disso, adicionamos a linha

return 0;

que termina a execuo do programa. Finalmente o smbolo fecha-chave } indica o final do


bloco de comandos do programa.

4.4 Prximo programa

Vamos digitar um prximo programa.

Programa 4.2: Segundo programa.

1 #include <stdio.h>
2 int main(void)
3 {
4 int num1, num2, soma;
5 num1 = 25;
6 num2 = 30;
7 soma = num1 + num2;
8 printf("A soma de %d e %d %d\n", num1, num2, soma);
9 return 0;
10 }

Este exemplo ligeiramente diferente do primeiro e introduz algumas novidades. Agora, a


primeira linha aps a linha que contm o nome da funo main apresenta uma declarao de
variveis. Trs variveis do tipo inteiro so declaradas: num1 , num2 e soma . Isso significa
que, durante a execuo desse programa, trs compartimentos de memria so reservados pelo
computador para armazenamento de informaes que, neste caso, so nmeros inteiros. Alm
disso, a cada compartimento associado um nome: num1 , num2 e soma . Esses comparti-
mentos de memria so tambm conhecidos como variveis, j que seu contedo pode variar
durante a execuo de um programa.
Depois disso, na prxima linha do nosso programa temos uma atribuio do valor do tipo
inteiro 25 para a varivel num1 . Observe ento que o smbolo de atribuio da linguagem C
= . Na linha seguinte, uma outra atribuio realizada, do nmero 30 para a varivel num2 .
Na prxima linha temos uma atribuio para a varivel soma . No entanto, note que no temos
mais um nmero no lado direito da expresso de atribuio, mas sim a expresso aritmtica
num1 + num2 . Neste caso, durante a execuo dessa linha do programa, o computador con-
sulta o contedo das variveis num1 e num2 , realiza a operao de adio com os dois valores
obtidos dessas variveis e, aps isso, atribui o resultado varivel soma . No nosso exemplo,
o resultado da expresso aritmtica 25 + 30, ou seja, o valor 55, ser atribudo varivel soma .
A linha seguinte de nosso programa contm uma chamada funo printf , mas agora com

FACOM UFMS
4.5 D OCUMENTAO 37

quatro argumentos: o primeiro agora uma cadeia de caracteres de formatao, contendo no


apenas caracteres a serem impressos na sada, mas smbolos especiais, iniciados com % , co-
nhecidos como conversores de tipo da linguagem C. Neste caso, os smbolos %d permitem
que um nmero inteiro seja mostrado na sada. Note que trs conversores %d esto contidos
na seqncia de smbolos de sada e, a cada um deles e na ordem como so apresentados est
associado uma das variveis num1 , num2 e soma , que so os argumentos restantes da funo
printf .

4.5 Documentao

Uma boa documentao de um programa, conforme [4], significa inserir comentrios apro-
priados no cdigo de modo a explicar o que cada uma das funes que compem o programa
faz. A documentao de uma funo um pequeno manual que d instrues precisas e com-
pletas sobre o uso da funo.
Comentrios so ento introduzidos em programas com o objetivo de document-los e de
incrementar a sua legibilidade. O(a) programador(a) responsvel por manter seus cdigos
legveis e bem documentados para que, no futuro, possa retom-los e compreend-los sem
muito esforo e sem desperdcio de tempo. Na linguagem C padro, os comentrios so envol-
vidos pelos smbolos /* e */ . Um comentrio completamente ignorado quando encontrado
pelo compilador e, portanto, no faz diferena alguma no programa executvel produzido pelo
compilador da linguagem.
A seguir, mostramos nosso ltimo programa desta aula com comentrios explicativos adi-
cionados nos pontos onde so realmente necessrios.

Programa 4.3: Segundo programa com comentrios explicativos.

1 #include <stdio.h>
2 /* Esta funo faz a adio de dois nmeros inteiros fixos
3 e mostra o resultado da operao na sada. */
4 int main(void)
5 {
6 int num1, num2, soma;
7 num1 = 25;
8 num2 = 30;
9 soma = num1 + num2;
10 printf("A soma de %d e %d %d\n", num1, num2, soma);
11 return 0;
12 }

Ainda conforme [4], uma boa documentao no se preocupa em explicar como uma funo
faz o que faz, mas sim o que ela faz de fato, informando quais so os valores de entrada da
funo, quais so os valores de sada e quais as relaes que esses valores que entram e saem
da funo e as transformaes pela funo realizadas.

FACOM UFMS
4.5 D OCUMENTAO 38

Exerccios
4.1 Escreva uma programa na linguagem C que escreva a seguinte mensagem na sada pa-
dro:

a) Comentrios na linguagem C iniciam com /* e terminam com */


b) Letras minsculas e maisculas so diferentes na linguagem C
c) A palavra chave main indica o incio do programa na linguagem C
d) Os smbolos { e } envolvem um bloco de comandos na linguagem C
e) Todos os comandos na linguagem C devem terminar com um ponto e vrgula

4.2 Qual a sada esperada para o programa 4.4?

Programa 4.4: Programa do exerccio 4.2.

1 #include <stdio.h>
2 int main(void)
3 {
4 printf("Al! ");
5 printf("Al! ");
6 printf("Tem algum a?");
7 printf("\n");
8 return 0;
9 }

4.3 Escreva um programa na linguagem C que subtraia 14 de 73 e mostre o resultado na sada


padro com uma mensagem apropriada.

4.4 Verifique se o programa 4.5 est correto. Em caso negativo, liste os erros de digitao que
voc encontrou.

Programa 4.5: Programa do exerccio 4.4.

1 include <stdio.h>
2 /* computa a soma de dois nmeros
3 Int main(void)
4 {
5 int resultado;
6 resultado = 13 + 22 - 7
7 printf("O resultado da operao %d\n" resultado);
8 return 0;
9 }

FACOM UFMS
A ULA 5

E NTRADA E SADA

A funo usada para entrada de dados scanf e a funo usada para sada de dados
printf sero estudadas nesta aula. Estas so funes bsicas da linguagem C que fazem
a interao com usurio durante a execuo de um programa, solicitando valores de entrada e
mostrando valores na sada.

5.1 Um exemplo

Vamos ver um primeiro exemplo de programa na linguagem C que usa a funo de leitura
scanf . Abra seu Emacs, pressione C-x C-f , d um nome para seu programa, como por
exemplo soma.c , e digite o cdigo apresentado no programa 5.1.

Programa 5.1: Programa com entrada e sada de dados.

1 #include <stdio.h>
2 /* Programa que l dois nmeros inteiros e mostra
3 o resultado da soma desses dois nmeros */
4 int main(void)
5 {
6 int num1, num2, soma;
7 printf("Informe um nmero: ");
8 scanf("%d", &num1);
9 printf("Informe outro nmero: ");
10 scanf("%d", &num2);
11 soma = num1 + num2;
12 printf("A soma de %d mais %d %d\n", num1, num2, soma);
13 return 0;
14 }

Vamos olhar mais de perto este programa. Observe que, pela primeira vez, usamos a funo
scanf . Desconsiderando o esqueleto do programa, a primeira linha a ser notada a declara-
o de variveis:

int num1, num2, soma;

39
5.2 S EGUNDO EXEMPLO 40

Estamos, com esta linha do programa, reservando trs compartimentos de memria que pode-
ro ser utilizados para armazenamento de nmeros inteiros, cujos nomes desses compartimen-
tos so num1 , num2 e soma .
Em seguida, h uma funo para escrita e uma funo para leitura:

printf("Informe um nmero: ");


scanf("%d", &num1);

A funo para escrita printf j bem conhecida, vista nas aulas anteriores. A funo de
leitura scanf tem dois parmetros: uma cadeia de caracteres de formatao "%d" e uma
varivel num1 correspondente a este formato. Diferentemente da funo printf , a varivel
na funo scanf deve vir precedida com o smbolo & . No comando acima, a cadeia de
caracteres de formatao "%d" indica que o valor informado pelo usurio ser convertido
para um inteiro. Ainda, este valor ser ento armazenado na varivel do tipo inteiro num1 .
Observe, mais uma vez, que esta varivel precedida por um & .
A prxima seqncia de instrues equivalente e solicita ao usurio do programa a en-
trada de um outro nmero, que ser armazenado na varivel num2 :

printf("Informe outro nmero: ");


scanf("%d", &num2) ;

Depois disso, h um comando um pouco mais complexo:

soma = num1 + num2;

Este um comando de atribuio da linguagem C, determinado pelo smbolo = . Um comando


de atribuio tem uma sintaxe bem definida: do lado esquerdo do smbolo de atribuio =
h sempre uma varivel; do lado direito h uma constante, uma varivel ou uma expresso
do mesmo tipo da varivel esquerda. Este comando funciona da seguinte forma: avalia a
expresso do lado direito do smbolo de atribuio e atribui o resultado desta avaliao para
a varivel do lado esquerdo. Em nosso exemplo, o lado direito uma expresso aritmtica de
adio, que soma os contedos das variveis do tipo inteiro num1 e num2 . O resultado da
avaliao dessa expresso um nmero inteiro que ser atribudo varivel soma , do lado
esquerdo do smbolo de atribuio.
Por fim, a chamada funo

printf("A soma de %d mais %d %d\n", num1, num2, soma);

mostra, alm dos valores das duas parcelas num1 e num2 , o resultado dessa soma armazenado
na varivel soma .

5.2 Segundo exemplo

Este segundo exemplo muito semelhante ao exemplo anterior. A diferena apenas a


expresso aritmtica: no primeiro exemplo, uma adio realizada; neste segundo exemplo,
uma multiplicao realizada. O operador aritmtico de multiplicao o smbolo * . In-
forme, compile e execute o seguinte programa.

FACOM UFMS
5.2 S EGUNDO EXEMPLO 41

Programa 5.2: Segundo exemplo.

1 #include <stdio.h>
2 /* A funo main l dois nmeros inteiros e mostra na sada
3 o resultado da multiplicao desses dois nmeros */
4 int main(void)
5 {
6 int num1, num2, produto;
7 printf("Informe um nmero: ");
8 scanf("%d", &num1);
9 printf("Informe outro nmero: ");
10 scanf("%d", &num2);
11 produto = num1 * num2;
12 printf("O produto de %d por %d %d\n", num1, num2, produto);
13 return 0;
14 }

Exerccios

5.1 Faa um programa que leia trs nmeros inteiros a, b e c, calcule a b + c e mostre o
resultado na sada padro para o usurio.

Programa 5.3: Uma soluo possvel para o exerccio 5.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int a, b, c;
5 printf("Informe 3 nmeros inteiros: ");
6 scanf("%d %d %d", &a, &b, &c);
7 printf("A expresso %d*%d+%d tem resultado %d.\n", a, b, c, a*b+c);
8 return 0;
9 }

5.2 Faa um programa que leia um nmero inteiro e mostre o seu quadrado e seu cubo. Por
exemplo, se o nmero de entrada 3, a sada deve ser 9 e 27.

5.3 Faa um programa que leia trs nmeros inteiros e mostre como resultado a soma desses
trs nmeros e tambm a multiplicao desses trs nmeros.

5.4 Escreva um programa que leia um nmero inteiro e mostre o resultado da quociente
(inteiro) da diviso desse nmero por 2 e por 3.

FACOM UFMS
A ULA 6

E STRUTURAS CONDICIONAIS

Nesta aula veremos alguns pequenos exemplos de programas que usam estruturas con-
dicionais. Alm da estrutura seqencial, aquela que tivemos contato nas aulas anteriores, a
estrutura condicional uma ferramenta de programao importante que auxilia o programa-
dor a decidir que trechos de programa devem ser executados ou no, dependendo da avaliao
da condio expressa.
Para maior compreenso das estruturas condicionais certamente necessrio o entendi-
mento dos conceitos de constantes, variveis, expresses aritmticas e expresses lgicas da
linguagem C. Por enquanto, esses conceitos ficaro implcitos, j que vamos estud-los com
bastante detalhe na aula 7.

6.1 Estrutura condicional simples

Como vimos nas aulas anteriores, a linguagem C usada para solicitar que o computador
realize uma seqncia de comandos ou instrues tais como leitura de dados, impresso de
dados e atribuio. Alm de ter esse poder, a linguagem C tambm pode ser usada para tomar
decises. Uma deciso, na linguagem C, tomada com o uso de uma estrutura condicional
simples ou de uma estrutura condicional composta. Uma estrutura condicional simples tem o
seguinte formato:

if (condio) {
comando 1;
comando 2;
...
comando n;
}

para n > 1. No caso em que n = 1, os delimitadores de bloco, isto , as chaves { e } , so


opcionais.
Uma deciso tomada de acordo com a avaliao da condio , que uma expresso
lgica: caso o resultado dessa avaliao seja verdadeiro, o comando ou o bloco de comandos
ser executado; caso contrrio, ser ignorado. Se o nmero de comandos a serem executados
em um bloco interno estrutura condicional pelo menos dois, ento esse bloco tem de vir
envolvido por chaves.

42
6.2 E STRUTURA CONDICIONAL COMPOSTA 43

Vejamos um pequeno exemplo no programa 6.1.

Programa 6.1: Um exemplo contendo uma estrutura condicional simples.

1 #include <stdio.h>
2 int main(void)
3 {
4 int idade;
5 printf("Quantos anos voc tem? ");
6 scanf("%d", &idade);
7 if (idade < 30)
8 printf("Nossa! Voc bem jovem!\n");
9 printf("At breve!\n");
10 return 0;
11 }

Neste programa 6.1, uma nica varivel idade do tipo inteiro declarada no incio do pro-
grama. Em seguida, solicitado ao usurio do programa 6.1 que informe um nmero inteiro,
que ser armazenado na varivel idade . Se o valor informado pelo usurio for um nmero
inteiro menor que 30, ento uma mensagem Nossa! Voc bem jovem! aparecer no mo-
nitor. Por fim, a mensagem At breve! impressa.

6.2 Estrutura condicional composta

Uma estrutura condicional composta tem o seguinte formato geral:

if (condio) {
comando 1;
comando 2;
...
comando m;
}
else {
comando 1;
comando 2;
...
comando n;
}

para m , n > 1. No caso em que m = 1 ou n = 1, os delimitadores de bloco, isto , as chaves


{ e } , so opcionais.
Ento, vejamos um outro exemplo no programa 6.2.

FACOM UFMS
6.3 T ROCA DE CONTEDOS 44

Programa 6.2: Um exemplo usando uma estrutura condicional composta.

1 #include <stdio.h>
2 int main(void)
3 {
4 int idade;
5 printf("Quantos anos voc tem? ");
6 scanf("%d", &idade);
7 if (idade < 30)
8 printf("Nossa! Voc bem jovem!\n");
9 else
10 printf("Puxa! Voc j velhinho!\n");
11 printf("At breve!\n");
12 return 0;
13 }

Observe agora que no programa 6.2 as mensagens que podem ser emitidas para o usurio
so excludentes.

6.3 Troca de contedos

O programa 6.3 l dois nmeros inteiros quaisquer e os imprime em ordem no decrescente.

Programa 6.3: Um exemplo de troca de contedos entre variveis do mesmo tipo.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, y, aux;
5 printf("Informe o valor de x: ");
6 scanf("%d", &x);
7 printf("Informe o valor de y: ");
8 scanf("%d", &y);
9 if (x > y)
10 {
11 aux = x;
12 x = y;
13 y = aux;
14 }
15 printf("%d menor ou igual a %d\n", x, y);
16 return 0;
17 }

FACOM UFMS
6.3 T ROCA DE CONTEDOS 45

Na verdade, o programa 6.3 l dois valores do tipo inteiro e os armazena nas variveis x
e y e, em seguida, armazena o menor desses dois valores em x e o maior em y . Se houver
empate, a mensagem emitida est correta. Suponha que o programa 6.3 est sendo executado e
que os valores de x e y j foram lidos. Observe primeiro que se um usurio desse programa
informa um valor para x e um valor para y de tal forma que o valor de x menor ou igual
ao valor de y ento a avaliao da condio x > y tem resultado falso e o bloco de comandos
interno estrutura condicional no executado. Agora suponha o contrrio, isto , suponha
que o usurio do programa informa um valor para x maior que o valor informado para y .
Ento, a avaliao da condio x > y tem resultado verdadeiro, o bloco de comandos interno
estrutura condicional executado e uma troca ser realizada: o contedo da varivel x
armazenado temporariamente na varivel aux , a varivel x recebe o contedo da varivel y
e, finalmente, a varivel y recebe o contedo da varivel aux . Note que a troca no poderia
ser realizada sem o uso de uma varivel auxiliar. A ltima instruo do programa a chamada
funo de impresso printf para mostrar o contedo das variveis x e y . Por exemplo,
suponha que um usurio do programa 6.3 fornea o valor 2 para x e 5 para y . Ento, a troca
no realizada e a mensagem impressa na sada padro 2 menor ou igual a 5 . Se, ao
contrrio, o usurio fornece 5 para x e 2 para y , a troca realizada e a mensagem na sada
padro tambm 2 menor ou igual a 5 .

Exerccios

6.1 Escreva um programa que receba a temperatura ambiente em graus Clsius e mostre uma
mensagem para o usurio informando se a temperatura est muito quente. Considere
como temperatura limite o valor de 30 graus Clsius.

Programa 6.4: Uma possvel soluo para o exerccio 6.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int temperatura;
5 printf("Informe uma temperatura (em graus Celsius): ");
6 scanf("%d", &temperatura);
7 if (temperatura > 30)
8 printf("Hoj um dia bem quente!\n");
9 else
10 printf("Hoje no est to quente.\n");
11 return 0;
12 }

6.2 Escreva um programa que receba trs valores, armazenando-os nas variveis x, y e z, e or-
dene esses valores de modo que, ao final, o menor valor esteja armazenado na varivel x,
o valor intermedirio esteja armazenado na varivel y e o maior valor esteja armazenado
na varivel z.

FACOM UFMS
A ULA 7

N MEROS INTEIROS

Nas aulas anteriores, nos programas que fizemos, tomamos contato com a estrutura de pro-
gramao seqencial, onde comandos de atribuio, chamadas de funes de entrada e sada
e tambm declaraes de variveis so dispostos linha aps linha em um bloco de comandos
envolvido por { e } . Vimos o uso de funes, com seus argumentos, tais como a funo
para entrada de dados scanf e a funo para sada de dados printf . Tambm vimos as
estruturas condicionais da linguagem C, onde o fluxo de execuo de um programa pode ser
modificado de acordo com a avaliao de uma expresso lgica.
A partir da aula de hoje formalizaremos todos esses conceitos. Iniciamos essa formaliza-
o com os nmeros inteiros, definindo constantes, variveis e expresses que envolvem esses
nmeros. No final desta aula, explicamos tambm como feita a representao de nmeros
inteiros em um computador.

7.1 Constantes e variveis

Como mencionamos nas aulas 1 e 2, os primeiros programadores tinham de programar


diretamente na linguagem que o computador compreende, a linguagem de mquina ou lin-
guagem binria, j que no existia na poca nenhuma linguagem de alto nvel. Isso significa
que instrues em cdigo binrio deviam ser escritas pelos programadores antes que fossem
digitadas nesses computadores. Alm disso, os programadores tinham de referenciar explicita-
mente um valor que representasse um endereo de memria para armazenar valores tempor-
rios. Felizmente, as linguagens de programao de alto nvel permitem que um programador
se concentre muito mais na soluo do problema que em cdigos especficos da mquina e em
endereos de memria.
Uma linguagem de alto nvel permite que um programador use facilmente compartimen-
tos de memria para armazenamento de informaes durante a execuo de um programa.
Como valores podem ser atribudos e sobrescritos nesses compartimentos e, portanto, podem
variar durante a execuo do programa, esses compartimentos de memria so chamados de
variveis do programa. Alm disso, uma linguagem de alto nvel permite que um progra-
mador atribua um nome simblico a um endereo de um compartimento de memria. Esse
nome simblico conhecido como identificador ou nome da varivel. O identificador de uma
varivel pode ser escolhido pelo programador de modo a refletir o seu contedo. Nas aulas
anteriores, temos usado diversas variveis para armazenar valores que so nmeros inteiros,
como num1 , soma e produto , por exemplo. A linguagem C permite o uso de outros tipos de
dados alm de inteiros, como veremos mais adiante.

46
7.1 C ONSTANTES E VARIVEIS 47

H uma regra fundamental sobre variveis e programao na linguagem C: qualquer va-


rivel usada em um programa deve ser previamente declarada. H tambm regras para um
programador determinar os identificadores de variveis na linguagem C. Essas regras so as
apresentadas a seguir:

os smbolos vlidos em um identificador de varivel so os caracteres do alfabeto da


lngua inglesa, minsculos ou maisculos, os dgitos numricos e o sublinhado1 (_);

o identificador de uma varivel deve iniciar com uma letra ou com um sublinhado (_);

aps o primeiro caracter, o identificador de uma varivel determinado por qualquer


seqncia de letras minsculas ou maisculas, de nmeros ou de sublinhados.

Observe que as regras descritas acima excluem quaisquer caracteres que estejam fora do
conjunto { a , . . . , z , A , . . . , Z } e isso significa que caracteres acentuados e cedilhas no po-
dem fazer parte de identificadores de variveis. Dessa forma, os seguintes identificadores so
nomes vlidos de variveis:

soma
num1
i
soma_total
_sistema
A3x3

Por outro lado, os identificadores listados a seguir no so nomes vlidos de variveis na


linguagem C:

preo$
soma total
4quant
int

No primeiro caso, o identificador preo$ contm dois smbolos no vlidos: e $. O se-


gundo identificador tambm tem o mesmo problema, j que um identificador no pode conter
um caracter espao. O terceiro exemplo tem um identificador que inicia com um caracteres no
vlido, um dgio numrico. O ltimo exemplo um identificador no vlido j que int uma
palavra reservada da linguagem C.
Tambm importante destacar que letras minsculas e maisculas na linguagem C so
diferentes. Assim, as variveis soma , Soma e SOMA so diferentes.
A declarao de variveis do tipo inteiro na linguagem C pode ser realizada com um nico
comando int e mais uma lista de identificadores de variveis, separados por um espao e
1
Do ingls underscore ou underline.

FACOM UFMS
7.1 C ONSTANTES E VARIVEIS 48

uma vrgula, e finalizada com um ponto e vrgula; ou ainda, pode ser realizada uma a uma,
com um comando int para cada varivel:

int i, num1, num2, soma, produto, aux;

ou

int i;
int num1;
int num2;
int soma;
int produto;
int aux;

Os identificadores das variveis podem ser to longos quanto se queira, apesar de apenas
os 63 primeiros caracteres serem significativos para o compilador da linguagem C. Um pro-
grama escrito com identificadores de variveis muito longos pode ser difcil de ser escrito. Por
exemplo,

TotalDeTodoDinheiroQueTenhoGuardado = TotalDoDinheiroQueGuardeiNoAnoPassado +
TotalDeDinheiroQueGuardeiEm2009 - TotalDeImpostosEmReais;

bem menos significativo que

TotalGeral = TotalPassado + Total2009 - Impostos;

Em um programa na linguagem C, os nmeros inteiros que ocorrem em expresses so co-


nhecidos como constantes. Por exemplo, o nmero 37 representa um valor inteiro constante.
Expresses aritmticas que se constituem apenas de valores constantes so chamadas de ex-
presses aritmticas constantes. Por exemplo,

781 + 553 - 12 * 44

uma expresso aritmtica constante.

FACOM UFMS
7.2 E XPRESSES ARITMTICAS COM NMEROS INTEIROS 49

7.2 Expresses aritmticas com nmeros inteiros

A linguagem C possui cinco operadores aritmticos binrios que podemos usar com nme-
ros inteiros: + para adio, - para subtrao, * para multiplicao, / para diviso e % para
resto da diviso. Devemos ressaltar que todos esses operadores so binrios, isto , necessitam
de dois operandos para execuo da operao aritmtica correspondente. Esses operandos po-
dem ser constantes, variveis ou expresses aritmticas do tipo inteiro. O programa 7.1 um
exemplo de programa que usa todos esses operadores em expresses aritmticas com nmeros
inteiros.

Programa 7.1: Programa contendo expresses aritmticas com nmeros inteiros.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, y, z, resultado;
5 x = 25;
6 y = 10;
7 resultado = x + y;
8 printf("A soma de %d e %d %d\n", x, y, resultado);
9 x = 38;
10 y = 29;
11 resultado = x - y;
12 printf("A substrao de %d e %d %d\n", x, y, resultado);
13 x = 51;
14 y = 17;
15 resultado = x * y;
16 printf("A multiplicao de %d por %d %d\n", x, y, resultado);
17 x = 100;
18 y = 25;
19 resultado = x / y;
20 printf("A diviso de %d por %d %d\n", x, y, resultado);
21 x = 17;
22 y = 3;
23 resultado = x % y;
24 printf("O resto da diviso de %d por %d %d\n", x, y, resultado);
25 x = -7;
26 resultado = -x;
27 printf("%d com sinal trocado %d\n", x, resultado);
28 x = 10;
29 y = 4;
30 z = 15;
31 resultado = x + y * z;
32 printf("A expresso %d + %d * %d tem resultado %d\n", x, y, z, resultado);
33 return 0;
34 }

FACOM UFMS
7.3 R EPRESENTAO DE NMEROS INTEIROS 50

H ainda um operador unrio - na linguagem C, que trabalha sobre um nico valor e


que troca o seu sinal, isto , troca o sinal de uma constante, uma varivel ou uma expresso
aritmtica do tipo inteiro. No exemplo acima, o comando de atribuio x = -7; na linha 25 e
o comando de atribuio resultado = -x; na linha 26 fazem com que o valor armazenado
na varivel resultado seja 7 .
Observe finalmente que o resultado da avaliao da expresso aritmtica 10 + 4 * 15
no 14 15 = 210, mas sim 10 + 60 = 70. Os operadores aritmticos na linguagem C
tm precedncias uns sobre os outros. O operador de menos unrio precede todos os outros.
Multiplicaes, divises e restos de divises tm precedncia sobre a adio e subtrao e, por
isso, o resultado da operao

resultado = x + y * z;

dado primeiro pela avaliao da multiplicao e depois pela avaliao da adio. Parnteses
so utilizados para modificar a ordem das operaes. Por exemplo,

resultado = (x + y) * z;

teria como resultado da avaliao o valor 210.


Segue um resumo das precedncias dos operadores aritmticos binrios sobre nmeros in-
teiros:
Operador Descrio Precedncia
* / % Multiplicao, diviso e resto da diviso 1 (mxima)
+ - Adio e subtrao 2 (mnima)

7.3 Representao de nmeros inteiros

Esta seo completamente inspirada no apndice C do livro [4].


A memria do computador uma seqncia de bytes. Um byte consiste em 8 bits, onde bit
significa a menor unidade de informao que pode ser armazenada na memria e pode assumir
apenas dois valores possveis: 0 ou 1. Assim, um byte pode assumir 256 valores possveis:
00000000, 00000001, . . . , 11111111. De fato, o conjunto de todas as seqncias de k bits
representa os nmeros naturais de 0 a 2k 1.
Uma seqncia de bits chamada de um nmero binrio ou nmero na base 2. Por exem-
plo, 010110 um nmero binrio representado por uma seqncia de 6 bits. Podemos conver-
ter um nmero binrio em um nmero decimal, ou nmero na base 10, que nada mais que
um nmero natural. Por exemplo, o nmero binrio 010110 representa o nmero decimal 22,
pois 0 25 + 1 24 + 0 23 + 1 22 + 1 21 + 0 20 = 22.
Na linguagem C, os nmeros inteiros positivos e negativos so conhecidos como inteiros
com sinal. Para declarar uma varivel i do tipo inteiro com sinal, temos de digitar

FACOM UFMS
7.3 R EPRESENTAO DE NMEROS INTEIROS 51

int i;

Cada varivel do tipo int armazenada em b bytes consecutivos na memria, onde b


dependente da arquitetura do computador. A maioria dos computadores pessoais atuais tm
b = 4 e assim cada varivel do tipo int representada por uma seqncia de 8 4 = 32 bits,
perfazendo um total de 232 valores possveis, contidos no conjunto

231 , . . . , 1, 0, 1, . . . , 231 1

ou
2147483648, . . . , 1, 0, 1, . . . , 2147483647 .

Cada seqncia de 32 bits que comea com 0 representa um int no-negativo em notao
binria. Cada seqncia de 32 bits que comea com 1 representa o inteiro negativo k 232 ,
onde k o valor da seqncia em notao binria. Esta representao de nmeros inteiros
negativos
 31 31 chamada
 de complemento-de-dois. Nmeros inteiros que estejam fora do intevalo
2 , 2 1 so representados mdulo 232 .
Ressaltamos ainda que todos os valores acima podem ser obtidos usando frmulas gerais
em funo do nmero de bytes consecutivos usados para representar um int .

Exerccios

7.1 Escreva um programa que receba um nmero inteiro x e avalie o polinmio

p(x) = 3x3 5x2 + 2x 1 .

Programa 7.2: Possvel soluo para o exerccio 7.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, p;
5 printf("Informe x: ");
6 scanf("%d", &x);
7 p = 3 * x * x * x - 5 * x * x + 2 * x - 1;
8 printf("p(%d) = %d.\n", x, p);
9 return 0;
10 }

7.2 Para transformar um nmero inteiro i no menor inteiro m maior que i e mltiplo de um
nmero inteiro j, a seguinte frmula pode ser utilizada:

m = i + j i mod j ,

FACOM UFMS
7.3 R EPRESENTAO DE NMEROS INTEIROS 52

onde o operador mod o operador de resto de diviso inteira na notao matemtica


usual, que corresponde ao operador % na linguagem C.
Por exemplo, suponha que usamos i = 256 dias para alguma atividade e queremos saber
qual o total de dias m que devemos ter de forma que esse nmero seja divisvel por j = 7,
para termos uma idia do nmero de semanas que usaremos na atividade. Ento, pela
frmula acima, temos que

m = 256 + 7 256 mod 7


= 256 + 7 4
= 259 .

Escreva um programa que receba dois nmeros inteiros positivos i e j e devolva o menor
inteiro m maior que i e mltiplo de j.

FACOM UFMS
A ULA 8

E STRUTURA DE REPETIO while

Estruturas de repetio so, juntamente com a estrutura seqencial e as estruturas condici-


onais, elementos fundamentais em algoritmos e programao. So essas as estruturas bsicas
que permitem que um programador consiga resolver centenas e centenas de problemas de ma-
neira efetiva. Nesta aula motivaremos o estudo das estruturas de repetio e apresentaremos a
estrutura de repetio while da linguagem C.

8.1 Motivao

Suponha que algum queira escrever um programa para imprimir os dez primeiros nme-
ros inteiros positivos. Este problema, apesar de muito simples e pouco til primeira vista,
motiva a introduo das estruturas de repetio, como veremos adiante. Com o que aprende-
mos at o momento, bem fcil escrever um programa como esse.

Programa 8.1: Um programa para imprimir os 10 primeiros inteiros positivos.

1 #include <stdio.h>
2 int main(void)
3 {
4 printf("1\n");
5 printf("2\n");
6 printf("3\n");
7 printf("4\n");
8 printf("5\n");
9 printf("6\n");
10 printf("7\n");
11 printf("8\n");
12 printf("9\n");
13 printf("10\n");
14 return 0;
15 }

Alm de bem simples, o programa 8.1 parece estar realmente correto, isto , parece realizar
o propsito de imprimir os dez primeiros nmeros inteiros positivos de forma correta. Mas e
se quisssemos imprimir os 1.000 primeiros nmeros inteiros positivos? Um programa seme-

53
8.2 E STRUTURA DE REPETIO while 54

lhante ao programa acima ficaria um pouco mais complicado e montono de escrever, j que
teria muitas e muitas linhas de cdigo.
Uma das propriedades fundamentais dos computadores que eles possuem a capacidade
de executar repetitivamente um conjunto de comandos. Essa capacidade de repetio permite
que um programador escreva programas mais concisos que contenham processos repetitivos
que poderiam necessitar de centenas ou milhares de linhas se no fossem assim descritos. A
linguagem de programao C tem trs estruturas de repetio diferentes. A seguir veremos a
estrutura while .

8.2 Estrutura de repetio while

Vamos fazer um programa que mostra os primeiros 100 nmeros na sada padro, linha
aps linha, usando uma estrutura de repetio. Veja o programa 8.2.

Programa 8.2: Um programa para imprimir os inteiros de 1 a 100.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero;
5 numero = 1;
6 while (numero <= 100) {
7 printf("%d\n", numero);
8 numero = numero + 1;
9 }
10 return 0;
11 }

A novidade neste programa a estrutura de repetio while . O programa tem incio com
a declarao da varivel numero que pode armazenar nmeros inteiros. A linha 5 contm um
comando de atribuio que faz com que a constante numrica 1 seja armazenada na varivel
numero . Depois disso, na linha 6 temos a estrutura de repetio while .
A estrutura de repetio while composta por um conjunto de elementos: (i) inicializao
da varivel que controla a estrutura de repetio; (ii) condio, ou expresso lgica, que controla
a repetio do bloco de comandos da estrutura; (iii) seqncia de comandos, que formam o bloco
de comandos, a menos da ltima linha, da estrutura de repetio e que sero executados; e
(iv) passo, que , em geral, a ltima linha do bloco de comandos da estrutura de repetio
e determina a freqncia com que o bloco de comandos ser executado. No programa 8.2, a
inicializao realizada antes da estrutura de repetio while , como mostrado na linha 5, com
o comando de atribuio numero = 1; . A condio, ou expresso lgica, que sempre vem
envolvida por parnteses e aparece logo aps o comando while , dada numero <= 100 . A
seqncia de comandos composta apenas pelo comando printf("%d\n", numero); . E o
passo vem, em geral, aps a seqncia de comandos. No caso do nosso programa o passo um
incremento, numero = numero + 1 .

FACOM UFMS
8.2 E STRUTURA DE REPETIO while 55

O funcionamento da estrutura de repetio while dado da seguinte forma. Sempre que


um programa encontra o comando while a expresso lgica envolvida por parnteses que
vem logo a seguir avaliada. Se o resultado da avaliao for verdadeiro, o bloco de comandos
interno a essa estrutura ser executado. Ao final da seqncia de comandos, o fluxo de execu-
o volta ao comando while para nova avaliao da sua expresso lgica. Se novamente o
resultado da avaliao dessa expresso for verdadeiro, seu bloco de comandos repetido. Esse
processo termina quando o resultado da avaliao da expresso lgica falso. Quando isso
acontece, o fluxo de execuo do programa salta para a prxima linha aps o bloco de coman-
dos da estrutura de repetio while . No caso do nosso programa, a linha alcanada por esse
salto a linha 10.
Vamos fazer agora um programa que soma os 100 primeiros nmeros inteiros positivos.
Veja o programa 8.3.

Programa 8.3: Programa que soma os 100 primeiros nmeros inteiros positivos.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero, soma;
5 soma = 0;
6 numero = 1;
7 while (numero <= 100) {
8 soma = soma + numero;
9 numero = numero + 1;
10 }
11 printf("A soma dos 100 primeiros inteiros positivos %d\n", soma);
12 return 0;
13 }

Exerccios

8.1 Dado um inteiro positivo n, somar os n primeiros inteiros positivos.

8.2 Dado n, imprimir os n primeiros naturais mpares.


Exemplo:

Para n = 4 a sada dever ser 1, 3, 5, 7.

8.3 Dado um inteiro positivo n, calcular n!.

8.4 Dado n, imprimir as n primeiras potncias de 2.


Exemplo:

Para n = 5 a sada dever ser 1, 2, 4, 8, 16.

8.5 Dados x inteiro e n um natural, calcular xn .

FACOM UFMS
8.2 E STRUTURA DE REPETIO while 56

Programa 8.4: Programa para o exerccio 8.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, numero, soma;
5 printf("Informe n: ");
6 scanf("%d", &n);
7 soma = 0;
8 numero = 1;
9 while (numero <= n) {
10 soma = soma + numero;
11 numero = numero + 1;
12 }
13 printf("O resultado da soma dos %d primeiros inteiros %d\n", n, soma);
14 return 0;
15 }

8.6 Dado um inteiro positivo n e uma seqncia de n inteiros, somar esses n nmeros.

FACOM UFMS
A ULA 9

E XERCCIOS

Nesta aula faremos exerccios para treinar a estrutura de repetio while .

Enunciados

9.1 Dado um inteiro positivo n e uma seqncia de n nmeros inteiros, determinar a soma
dos nmeros inteiros positivos da seqncia.
Exemplo:

Se n = 7 e a seqncia de nmeros inteiros 6, 2, 7, 0, 5, 8, 4 a sada deve ser


19.

Programa 9.1: Soluo para o exerccio 9.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, conta, num, soma;
5 printf("Informe n: ");
6 scanf("%d", &n);
7 soma = 0;
8 conta = 1;
9 while (conta <= n) {
10 printf("Informe um nmero: ");
11 scanf("%d", &num);
12 if (num > 0)
13 soma = soma + num;
14 conta = conta + 1;
15 }
16 printf("A soma dos nmeros positivos da seqncia %d.\n", soma);
17 return 0;
18 }

9.2 Dado um inteiro positivo n e uma seqncia de n inteiros, somar os nmeros pares e os
nmeros mpares.

57
58

9.3 Durante os 31 dias do ms de maro foram tomadas as temperaturas mdias dirias de


Campo Grande, MS. Determinar o nmero de dias desse ms com temperaturas abaixo
de zero.

9.4 Dado um inteiro positivo n e uma seqncia de n inteiros, determinar quantos nmeros
da seqncia so positivos e quantos so no-positivos. Um nmero no-positivo se
negativo ou se igual a 0 (zero).

9.5 Dado um inteiro positivo n e uma seqncia de n inteiros, determinar quantos nmeros
da seqncia so pares e quantos so mpares.

9.6 Uma loja de discos anota diariamente durante o ms de abril a quantidade de discos ven-
didos. Determinar em que dia desse ms ocorreu a maior venda e qual foi a quantidade
de discos vendida nesse dia.

9.7 Dados o nmero n de estudantes de uma turma de Programao de Computadores I e


suas notas de primeira prova, determinar a maior e a menor nota obtidas por essa turma,
onde a nota mnima 0 e a nota mxima 100.

FACOM UFMS
A ULA 10

E XERCCIOS

Enunciados

10.1 Dado um nmero inteiro positivo n e dois nmeros naturais no nulos i e j, imprimir em
ordem crescente os n primeiros naturais que so mltiplos de i ou de j ou de ambos.
Exemplo:

Para n = 6, i = 2 e j = 3 a sada dever ser 0, 2, 3, 4, 6, 8.

Programa 10.1: Soluo para o exerccio 10.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, i, j, numero, contador;
5 printf("Informe os nmeros n, i e j: ");
6 scanf("%d %d %d", &n, &i, &j);
7 numero = 0;
8 contador = 1;
9 while (contador <= n) {
10 if (numero % i == 0) {
11 printf("%d ", numero);
12 contador = contador + 1;
13 }
14 else {
15 if (numero % j == 0) {
16 printf("%d ", numero);
17 contador = contador + 1;
18 }
19 }
20 numero = numero + 1;
21 }
22 printf("\n");
23 return 0;
24 }

59
60

Programa 10.2: Outra soluo para o exerccio 10.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, i, j, numero, contador;
5 printf("Informe o nmero n: ");
6 scanf("%d", &n);
7 printf("Informe os nmeros i e j: ");
8 scanf("%d %d", &i, &j);
9 numero = 0;
10 contador = 1;
11 while (contador <= n) {
12 if ((numero % i == 0) || (numero % j == 0)) {
13 printf("%d ", numero);
14 contador = contador + 1;
15 }
16 numero = numero + 1;
17 }
18 printf("\n");
19 return 0;
20 }

10.2 Dizemos que um nmero natural triangular se produto de trs nmeros naturais
consecutivos.
Exemplo:

120 triangular, pois 4 5 6 = 120.

Dado n natural, verificar se n triangular.

10.3 Dados dois nmeros inteiros positivos, determinar o mximo divisor comum entre eles
utilizando o algoritmo de Euclides.
Exemplo:

1 1 1 2
24 15 9 6 3 = mdc(24,15)
9 6 3 0

10.4 Dados dois nmeros inteiros positivos a e b, representando a frao a/b, escreva um pro-
grama que reduz a/b para uma frao irredutvel.
Exemplo:

Se a entrada 9/12 a sada tem de ser 3/4.

FACOM UFMS
61

10.5 Dados a quantidade de dias de um ms e o dia da semana em que o ms comea, escreva


um programa que imprima os dias do ms por semana, linha a linha. Considere o dia
da semana 1 como domingo, 2 como segunda-feira, e assim por diante, at o dia 7 como
sbado.
Exemplo:

Se a entrada 31 e 3 ento a sada deve ser

1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

FACOM UFMS
A ULA 11

E XPRESSES COM INTEIROS

Nesta aula veremos expresses aritmticas, relacionais e lgicas com nmeros inteiros. Em
aulas anteriores j tomamos contato com essas expresses, mas pretendemos formaliz-las
aqui. A compreenso destas expresses com nmeros inteiros fundamental para que pos-
samos escrever programas mais eficientes e poderosos e tambm porque podemos estender
esse entendimento a outros tipos de variveis.

11.1 Expresses aritmticas

Uma expresso aritmtica qualquer seqncia de smbolos formada apenas por cons-
tantes numricas, variveis numricas, operadores aritmticos e parnteses. Como j vimos,
uma constante numrica do tipo inteiro qualquer nmero inteiro digitado em seu programa,
como 34 ou 7. Uma varivel numrica do tipo inteiro aquela que foi declarada com um co-
mando int no incio do programa. Os operadores aritmticos so divididos em duas classes:
operadores aritmticos unrios e operadores aritmticos binrios. S conhecemos, at agora,
um operador aritmtico unrio: o - , que troca o sinal da expresso aritmtica que o sucede.
Existe tambm um outro operador aritmtico unrio, o + que no faz nada alm de enfatizar
que uma constante numrica positiva. Os operadores aritmticos binrios so aqueles que
realizam as operaes bsicas sobre dois inteiros: + para adio, - para subtrao, * para
multiplicao, / para diviso e % para o resto da diviso.
As expresses aritmticas formadas por operaes binrias envolvendo operandos e opera-
dores tm precedncias umas sobre as outras: as operaes de multiplicao, diviso e resto da
diviso tm prioridade sobre as operaes de adio e a subtrao. Caso haja necessidade de
modificar a prioridade de uma operao em uma expresso, parnteses devem ser utilizados.
Expresses envolvidas por parnteses tm maior prioridade sobre expresses que esto fora
desses parnteses.
Suponha, por exemplo, que tenhamos declarado as variveis x , y , a , soma e parcela
do tipo inteiro:

int x, y, a, soma, parcela;

Suponha ainda que os seguintes comandos de atribuio tenham sido executados sobre essas
variveis:

62
11.2 E XPRESSES RELACIONAIS 63

x = 1;
y = 2;
a = 5;
soma = 100;
parcela = 134;

As sentenas a seguir so exemplos de expresses aritmticas:

2 * x * x + 5 * -x - +4
soma + parcela % 3
4 * 1002 - 4412 % 11 * -2 + a
(((204 / (3 + x)) * y) - ((y % x) + soma))

De acordo com os valores das inicializaes das variveis envolvidas e o resultado da avaliao
das prprias expresses, a avaliao de cada uma das linhas acima tem valores -7, 102, 4037 e
202, respectivamente.
A tabela abaixo, contendo as prioridades dos operadores aritmticos unrios e binrios, j
foi exibida em parte na aula 7.

Operadores Tipo Descrio Precedncia


+ - unrios constante positiva, troca de sinal 1 (mxima)
* / % binrios multiplicao, diviso e resto da diviso 2
+ - binrios adio e subtrao 3 (mnima)

11.2 Expresses relacionais

Uma expresso relacional, ou simplesmente uma relao, uma comparao entre dois va-
lores do mesmo tipo bsico. Esses valores so representados na relao atravs de constantes,
variveis ou expresses aritmticas. Note que, at o momento, conhecemos apenas o tipo b-
sico int , mas essa definio bastante abrangente e engloba todos os tipos bsicos que ainda
veremos.
Os operadores relacionais da linguagem C, que indicam a comparao a ser realizada entre
os termos da relao, so os seguintes:

Operador Descrio
== igual a
!= diferente de
< menor que
> maior que
<= menor que ou igual a
>= maior que ou igual a

FACOM UFMS
11.3 E XPRESSES LGICAS 64

O resultado da avaliao de uma expresso relacional sempre um valor lgico, isto ,


verdadeiro ou falso. Assim, supondo que temos declarado trs variveis a , b e c do tipo
inteiro como a seguir,

int a, b, c;

e as seguintes atribuies realizadas

a = 2;
b = 3;
c = 4;

ento as expresses relacionais

a == 2
a > b + c
b + c <= 5 - a
b != 3

tm valores verdadeiro, falso, falso e falso, respectivamente.


Uma observao importante neste momento que o tipo lgico ou booleano no existe na
linguagem C. Isso significa que literalmente no existem constantes lgicas e variveis lgicas.
O resultado da avaliao de uma expresso relacional ou lgica , na verdade, um valor nu-
mrico do tipo inteiro. Se este um valor diferente de zero, ento a expresso considerada
ter valor verdadeiro. Caso contrrio, isto , se este valor igual a zero, ento a expresso tem
valor falso. Dessa forma, representaremos em geral o valor 1 (um) para a constante lgica com
valor verdadeiro e 0 (zero) para a constante lgica com valor falso. Lembrando apenas que, na
verdade, qualquer valor diferente de 0 (zero) avaliado como verdadeiro em uma expresso
lgica.

11.3 Expresses lgicas

Uma proposio qualquer sentena que pode ser valorada com o valor verdadeiro ou
falso. Traduzindo essa definio para a linguagem C, uma proposio qualquer sentena que
pode ser valorada com um valor inteiro. Com o que aprendemos at agora, uma proposio
uma relao, uma varivel do tipo inteiro ou uma constante do tipo inteiro. Uma expresso
condicional ou lgica, ou ainda booleana1 , formada por uma ou mais proposies. Neste
1
Devido a George Lawlor Boole ( ), nascido na Inglaterra, matemtico e filsofo.

FACOM UFMS
11.3 E XPRESSES LGICAS 65

ltimo caso, relacionamos as proposies atravs de operadores lgicos. Os operadores lgicos


da linguagem C utilizados como conectivos nas expresses lgicas so apresentados a seguir.

Operador Descrio
&& conjuno
|| disjuno
! negao

Duas proposies p e q podem ser combinadas pelo conectivo && para formar uma nica
proposio denominada conjuno das proposies originais: p && q e lemos p e q . O
resultado da avaliao da conjuno de duas proposies verdadeiro se e somente se ambas
as proposies tm valor verdadeiro, como mostra a tabela a seguir.

p q p && q
1 1 1
1 0 0
0 1 0
0 0 0

Duas proposies p e q podem ser combinadas pelo conectivo || para formar uma nica
proposio denominada disjuno das proposies originais: p || q e lemos p ou q ,
com sentido de e/ou. O resultado da avaliao da disjuno de duas proposies verdadeiro
se pelo menos uma das proposies tem valor verdadeiro, como mostra a tabela a seguir.

p q p || q
1 1 1
1 0 1
0 1 1
0 0 0

Dada uma proposio p , uma outra proposio, chamada negao de p , pode ser obtida
atravs da insero do smbolo ! antes da proposio: !p e lemos no p . Se a proposio
p tem valor verdadeiro, ento !p tem valor falso e se p tem valor falso, ento a proposio
!p tem valor verdadeiro, como mostra a tabela a seguir.

p !p
1 0
0 1

Exemplos de expresses lgicas so mostrados a seguir. Inicialmente, suponha que decla-


ramos as variveis do tipo inteiro a seguir:

int a, b, c, x;

e as seguintes atribuies so realizadas

FACOM UFMS
11.3 E XPRESSES LGICAS 66

a = 2;
b = 3;
c = 4;
x = 1;

ento as expresses lgicas a seguir

5 a == 2 && a < b + c
a b + c >= 5 - a || b != 3
!x a + b > c || 2 * x == b && 4 < x

tm valores verdadeiro, verdadeiro, falso na primeira coluna e verdadeiro, verdadeiro e falso


na segunda coluna.
Observe que avaliamos as expresses lgicas em uma ordem: primeiro avaliamos as ex-
presses aritmticas, depois as expresses relacionais e, por fim, as expresses lgicas em si.
Por exemplo,

a == 2 && a + x > b + c
2 == 2 && 3 > 3 + 4
2 == 2 && 3 > 7
1 && 0
1

A tabela abaixo ilustra a prioridade de todos os operadores aritmticos, relacionais e lgi-


cos, unrios e binrios, vistos at aqui.

Operador Tipo Precedncia


+ - unrios 1 (mxima)
* / % binrios 2
+ - binrios 3
== != >= <= > < binrios 4
! unrio 5
&& binrio 6
|| binrio 7 (mnima)

Observe que o operador de negao ! um operador unrio, ou seja, necessita de apenas


um operando como argumento da operao. Os outros operadores so todos binrios.
Lembre-se tambm que para modificar a precedncia de alguma expresso necessrio o
uso de parnteses, como j vimos nas expresses aritmticas.

FACOM UFMS
11.3 E XPRESSES LGICAS 67

Dessa forma, se considerarmos as mesmas variveis e atribuies acima, a expresso lgica


abaixo seria avaliada da seguinte forma:

x + c >= a + b || 2 * x < b && a > b + x


1 + 4 >= 2 + 3 || 2 * 1 < 3 && 2 > 3 + 1
5 >= 5 || 2 < 3 && 2 > 4
1 || 1 && 0
1 || 0
1

Observe que, como o operador lgico de conjuno && tem prioridade sobre o operador
lgico de disjuno || , o resultado da expresso acima verdadeiro. Se tivssemos realizado
a disjuno primeiramente, como poderamos intuitivamente supor devido ao posicionamento
mais esquerda do operador de disjuno, o resultado da expresso seria diferente.

Exerccios

Alguns exerccios desta aula ensinam um importante truque de programao que , na ver-
dade, o uso de uma varivel que simula um tipo lgico e que indica que algo ocorreu durante
a execuo do programa. Essa varivel chamada de indicadora de passagem.

11.1 Dado p inteiro, verificar se p primo.

11.2 Dado um nmero inteiro positivo n e uma seqncia de n nmeros inteiros, verificar se a
seqncia est em ordem crescente.

11.3 Dados um nmero inteiro n > 0 e um dgito d, com 0 6 d 6 9, determinar quantas vezes
o dgito d ocorre no nmero n.

11.4 Dado um nmero inteiro positivo n, verificar se este nmero contm dois dgitos conse-
cutivos iguais.

11.5 Dado um nmero inteiro positivo n, verificar se o primeiro e o ltimo dgito deste nmero
so iguais.

FACOM UFMS
11.3 E XPRESSES LGICAS 68

Programa 11.1: Uma soluo para o exerccio 11.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int p, divisor;
5 printf("Informe um nmero: ");
6 scanf("%d", &p);
7 divisor = 2;
8 while (divisor <= p/2) {
9 if (p % divisor == 0)
10 divisor = p;
11 else
12 divisor = divisor + 1;
13 }
14 if (divisor == p/2 + 1)
15 printf("%d primo\n", p);
16 else
17 printf("%d no primo\n", p);
18 return 0;
19 }

Programa 11.2: Soluo para o exerccio 11.1 usando uma varivel indicadora de passagem.

1 #include <stdio.h>
2 int main(void)
3 {
4 int p, divisor, primo;
5 printf("Informe um nmero: ");
6 scanf("%d", &p);
7 divisor = 2;
8 primo = 1;
9 while (divisor <= p/2 && primo == 1) {
10 if (p % divisor == 0)
11 primo = 0;
12 else
13 divisor = divisor + 1;
14 }
15 if (primo == 1)
16 printf("%d primo\n", p);
17 else
18 printf("%d no primo\n", p);
19 return 0;
20 }

FACOM UFMS
11.3 E XPRESSES LGICAS 69

Programa 11.3: Terceira soluo para o exerccio 11.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int p, divisor, primo;
5 printf("Informe um nmero: ");
6 scanf("%d", &p);
7 divisor = 2;
8 primo = 1;
9 while (divisor <= p/2 && primo) {
10 if (! (p % divisor))
11 primo = 0;
12 else
13 divisor = divisor + 1;
14 }
15 if (primo)
16 printf("%d primo\n", p);
17 else
18 printf("%d no primo\n", p);
19 return 0;
20 }

FACOM UFMS
A ULA 12

E STRUTURA DE REPETIO for

Relembrando, as estruturas de repetio so estruturas de programao fundamentais tanto


para construo de algoritmos como de programas. Todas as linguagens de programao de
alto nvel apresentam uma ou mais estruturas de repetio. De fato, so essas as estruturas
bsicas que permitem que um programador resolva centenas e centenas de problemas. Nesta
aula, alm de uma formalizao sobre as estruturas de programao, apresentaremos a estru-
tura de repetio for da linguagem C.

12.1 Estruturas de programao

Como j mencionamos, as estruturas bsicas de programao so a estrutura seqencial,


as estruturas condicionais e as estruturas de repetio. Na estrutura seqencial, a mais bsica
das estruturas de programao, os comandos seguem-se uns aos outros, linha aps linha, cada
uma delas terminada por um ponto e vrgula ( ; ). Esses comandos devem fazer parte de um
bloco de comandos, envolvidos pelos smbolos { e } .
Vejamos um exemplo da estrutura seqencial de programao no programa 12.1, uma res-
posta ao exerccio 5.2.

Programa 12.1: Um exemplo de um pequeno programa contendo apenas a estrutura seqencial


de programao.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, quadrado, cubo;
5 printf("Informe um nmero: ");
6 scanf("%d", &x);
7 quadrado = x * x;
8 cubo = quadrado * x;
9 printf("O quadrado de %d %d\n", x, quadrado);
10 printf("O cubo de %d %d\n", x, cubo);
11 return 0;
12 }

70
12.1 E STRUTURAS DE PROGRAMAO 71

Observe que o programa 12.1 contm uma nica estrutura de programao, onde as linhas
so dispostas uma aps outra e finalizadas com um ponto e vrgula ( ; ). Ou seja, aps a linha
que determina o incio do programa principal, a nica estrutura de programao apresentada
neste programa a estrutura seqencial.
Tambm j vimos as estruturas condicionais, simples e compostas, de programao. Uma
estrutura condicional realiza um desvio no fluxo de execuo de um programa, condicionado
ao valor resultante de uma expresso lgica. Vejamos um exemplo do uso de uma estrutura
condicional no programa 12.2.

Programa 12.2: Um exemplo de um programa contendo uma estrutura condicional composta.


Note que a estrutura condicional est inserida na estrutura seqencial da funo main . E,
alm disso, no interior da estrutura condicional h estruturas seqenciais.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, y, z;
5 printf("Informe um nmero: ");
6 scanf("%d", &x);
7 printf("Informe outro nmero: ");
8 scanf("%d", &y);
9 if (x > y) {
10 z = x * x;
11 printf("O quadrado de %d %d\n", x, z);
12 }
13 else {
14 z = y * y * y;
15 printf("O cubo de %d %d\n", y, z);
16 }
17 return 0;
18 }

Aps o incio do programa 12.2, seguem-se cinco linhas, na estrutura seqencial de pro-
gramao, contendo primeiro a declarao das variveis e depois funes de entrada e sada.
Depois disso, uma estrutura condicional composta apresentada e o fluxo de execuo do pro-
grama fica sujeito expresso lgica u > v . Caso o resultado da avaliao dessa expresso
seja verdadeiro, o bloco de comandos que vem logo a seguir ser executado. Caso contrrio,
isto , se o resultado for falso, o bloco de comandos aps a palavra reservada else ser exe-
cutado.
Observe ainda que as estruturas de programao podem estar encaixadas umas dentro das
outras, como pode ser observado no programa 12.2: o bloco de comandos aps o comando
if , contendo duas linhas, apresentado na estrutura seqencial, assim como o bloco de co-
mandos aps o comando else , que tambm contm duas linhas na estrutura seqencial de
programao. Aps essa estrutura condicional, o programa finalizado com o ltimo comando
( return 0; ).

FACOM UFMS
12.2 E STRUTURA DE REPETIO for 72

E, finalmente, como vimos nas aulas anteriores mais recentes, as estruturas de repetio
fazem com que um bloco de comandos seja executado enquanto uma condio satisfeita.
Quando essa condio deixa de ser satisfeita, o fluxo de execuo salta para a prxima linha
aps este bloco. Este o caso da estrutura de repetio while que vimos nas ltimas au-
las. Nesta aula, aprenderemos a estrutura de repetio for , muito semelhante estrutura de
repetio while .

12.2 Estrutura de repetio for

Retomando o programa 8.2, isto , o primeiro exemplo com uma estrutura de repetio
que vimos at agora, vamos refazer o programa que mostra os primeiros cem nmeros intei-
ros positivos na sada padro, mas agora usando a estrutura de repetio for . Vejamos o
programa 12.3 a seguir.

Programa 12.3: Programa que mostra os 100 primeiros inteiros positivos, usando a estrutura
de repetio for.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero;
5 for (numero = 1; numero <= 100; numero = numero + 1)
6 printf("%d\n", numero);
7 return 0;
8 }

A novidade neste programa a estrutura de repetio for . Um esqueleto da estrutura de


repetio for dado a seguir:

for (inicializao; condio; passo) {


seqncia de comandos
}

A estrutura de repetio for dispe a inicializao, a condio e o passo todos na mesma


linha, logo aps o comando for , envolvidos por parnteses. O funcionamento deste comando
dado da seguinte forma. Sempre que um comando for encontrado, a inicializao exe-
cutada. Em seguida, a condio verificada. Se o resultado da avaliao desta condio
verdadeiro, ento o fluxo de execuo desviado para a seqncia de comandos no bloco de
comandos interno estrutura for . Finalizada a seqncia de comandos, o passo realizado
e o fluxo de execuo desviado para a linha do comando for , onde novamente a condio
ser testada e o processo todo repetido, at que esta condio tenha como resultado de sua

FACOM UFMS
12.2 E STRUTURA DE REPETIO for 73

avaliao o valor falso e o fluxo da execuo ento desviado para o prximo comando aps o
bloco de comandos do comando for .
As semelhanas entre a estrutura de repetio for e while so destacadas a seguir.

inicializao
while (condio) { for (inicializao; condio; passo) {
seqncia de comandos seqncia de comandos
passo }
}

Vamos fazer um outro exemplo de programa, que tambm j fizemos antes no programa 8.3,
que soma os 100 primeiros nmeros inteiros positivos e utiliza a estrutura de repetio for .
Vejamos o programa 12.4.

Programa 12.4: Programa que soma os 100 primeiros inteiros positivos usando a estrutura de
repetio for.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero, soma;
5 soma = 0;
6 for (numero = 1; numero <= 100; numero = numero + 1)
7 soma = soma + numero;
8 printf("A soma dos 100 primeiros nmeros inteiros %d\n", soma);
9 return 0;
10 }

Aqui talvez seja um bom momento para entrarmos em contato com outros dois operadores
aritmticos da linguagem C. Os operadores unrios de incremento ++ e de decremento --
tm como funo adicionar ou subtrair uma unidade ao valor armazenado em seu operando,
respectivamente. Mas infelizmente, a compreenso das formas de uso desses operadores pode
ser facilmente prejudicada. Isso porque, alm de modificar os valores de seus operandos, ++
e -- podem ser usados como operadores prefixos e tambm como operadores posfixos. Por
exemplo, o trecho de cdigo abaixo

int cont;
cont = 1;
printf("cont vale %d\n", ++cont);
printf("cont vale %d\n", cont);

FACOM UFMS
12.2 E STRUTURA DE REPETIO for 74

imprime cont vale 2 e cont vale 2 na sada. Por outro lado, o trecho de cdigo abaixo

int cont;
cont = 1;
printf("cont vale %d\n", cont++);
printf("cont vale %d\n", cont);

imprime cont vale 1 e cont vale 2 na sada.


Podemos pensar que a expresso ++cont significa incremente cont imediatamente en-
quanto que a expresso cont++ significa use agora o valor de cont e depois incremente-o.
O depois nesse caso depende de algumas questes, mas seguramente a varivel cont ser
incrementada antes da prxima sentena ser executada.
O operador -- tem as mesmas propriedades do operador ++ .
importante observar que os operadores de incremento e decremento, se usados como
operadores posfixos, tm maior prioridade que os operadores unrios + e - . Se so usados
como operadores prefixos, ento tm a mesma prioridade dos operadores + e - .
O programa 12.4, que soma os 100 primeiros nmeros inteiros positivos, pode ser reescrito
como no programa 12.5.

Programa 12.5: Programa que realiza a mesma tarefa do programa 12.4, mas usando o operador
aritmtico unrio de incremento.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero, soma;
5 soma = 0;
6 for (numero = 1; numero <= 100; ++numero)
7 soma = soma + numero;
8 printf("O resultado da soma dos 100 primeiros inteiros %d\n", soma);
9 return 0;
10 }

Exerccios

12.1 Dado um nmero natural na base binria, transform-lo para a base decimal.
Exemplo:

Dado 10010 a sada ser 18, pois 1 24 + 0 23 + 0 22 + 1 21 + 0 20 = 18.

FACOM UFMS
12.2 E STRUTURA DE REPETIO for 75

Programa 12.6: Uma soluo do exerccio 12.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int bin, dec, pot2;
5 printf("Informe um nmero na base binria: ");
6 scanf("%d", &bin);
7 pot2 = 1;
8 dec = 0;
9 for ( ; bin != 0; bin = bin/10) {
10 dec = dec + bin % 10 * pot2;
11 pot2 = pot2 * 2;
12 }
13 printf("%d\n", dec);
14 return 0;
15 }

12.2 Dado um nmero natural na base decimal, transform-lo para a base binria.
Exemplo:

Dado 18 a sada dever ser 10010.

12.3 Dado um nmero inteiro positivo n que no contm um dgito 0, imprimi-lo na ordem
inversa de seus dgitos.
Exemplo:

Dado 26578 a sada dever ser 87562.

12.4 Qualquer nmero natural de quatro algarismos pode ser dividido em duas dezenas for-
madas pelos seus dois primeiros e dois ltimos dgitos.
Exemplos:

1297: 12 e 97.
5314: 53 e 14.

Escreva um programa que imprima todos os nmeros de quatro algarismos cuja raiz
quadrada seja a soma das dezenas formadas pela diviso acima.
Exemplo:

9801 = 99 = 98 + 01.
Portanto, 9801 um dos nmeros a ser impresso.

FACOM UFMS
A ULA 13

E STRUTURA DE REPETIO do-while

Nesta aula veremos uma terceira, e ltima, estrutura de repetio da linguagem C, a estru-
tura do-while . Diferentemente das estruturas de repetio while e for que estabelecem
seus critrios de repetio logo no incio, a estrutura de repetio do-while tem um critrio
de repetio ao final do bloco de comandos associado a elas. Isso significa, entre outras coi-
sas, que o bloco de comandos associado estrutura de repetio do-while executado pelo
menos uma vez, diferentemente das outras duas estruturas de repetio, que podem ter seus
blocos de comandos nunca executados.

13.1 Definio e exemplo de uso

Suponha, por exemplo, que seja necessrio resolver um problema simples, muito seme-
lhante aos problemas iniciais que resolvemos usando uma estrutura de repetio. O enunciado
do problema o seguinte: dada uma seqncia de nmeros inteiros terminada com 0, calcular
a soma desses nmeros.
Com o que aprendemos sobre programao at o momento, este problema parece bem sim-
ples de ser resolvido. Vejamos agora uma soluo um pouco diferente, dada no programa 13.1,
contendo a estrutura de repetio do-while que ainda no conhecamos.

Programa 13.1: Um programa que usa a estrutura de repetio do-while.

1 #include <stdio.h>
2 int main(void)
3 {
4 int numero, soma;
5 soma = 0;
6 do {
7 printf("Informe um nmero: ");
8 scanf("%d", &numero);
9 soma = soma + numero;
10 } while (numero != 0);
11 printf("A soma dos nmeros informados %d\n", soma);
12 return 0;
13 }

76
13.1 D EFINIO E EXEMPLO DE USO 77

O programa 13.1 muito semelhante aos demais programas que vimos construindo at o
momento. A nica diferena significativa o uso da estrutura de repetio do-while . Quando
o programa encontra essa estrutura de programao pela primeira vez, com uma linha con-
tendo a palavra reservada do seguida de um smbolo de incio de bloco de comandos { , o
fluxo de programao segue para o primeiro comando deste bloco. Os comandos desse bloco
so ento executados um a um, at que o smbolo de fim do bloco de comandos } seja encon-
trado. Logo em seguida, encontramos a palavra reservada while e, depois, uma expresso
lgica envolvida por parnteses. Se o resultado da avaliao dessa expresso lgica for verda-
deiro, ento o fluxo de execuo do programa desviado para o primeiro comando do bloco de
comandos desta estrutura de repetio e todos os comandos so novamente executados. Caso
contrrio, o fluxo de execuo desviado para o prximo comando aps a linha contendo o
final desta estrutura de repetio, isto , a linha que contm a palavra reservada while .
Um esqueleto da estrutura de repetio do-while mostrado a seguir, onde a seqncia
de comandos composta por quaisquer comandos da linguagem C ou qualquer outra estrutura
de programao:

do {
seqncia de comandos
} while (condio);

Observe que, assim como em qualquer outra estrutura de programao, o bloco de co-
mandos da estrutura de repetio do-while pode conter um nico comando e, por isso, os
smbolos de delimitao de um bloco { e } so dispensveis neste caso.
Note ainda que o teste de continuidade desta estrutura de repetio realizado sempre no
final, aps todos os comandos de seu bloco interno de comandos terem sido executados. Isso
significa que esse bloco de comandos ser executado ao menos uma vez. Neste sentido, esta es-
trutura de repetio do-while difere bastante das estruturas de repetio while e for que
vimos anteriormente, j que nesses dois ltimos casos o resultado da avaliao da expresso
condicional associada a essas estruturas pode fazer com que seus blocos de comandos respec-
tivos no sejam executados nem mesmo uma nica vez.

Exerccios

13.1 Dado um nmero inteiro no-negativo n, escreva um programa que determine quantos
dgitos o nmero n possui.

13.2 Solucione o exerccio 12.3 usando a estrutura de repetio do-while .

13.3 Dizemos que um nmero natural n com pelo menos 2 algarismos palndromo se

o primeiro algarismo de n igual ao seu ltimo algarismo;


o segundo algarismo de n igual ao se penltimo algarismo;
e assim sucessivamente.

Exemplos:

FACOM UFMS
13.1 D EFINIO E EXEMPLO DE USO 78

Programa 13.2: Uma soluo para o exerccio 13.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, digitos;
5 printf("Informe n: ");
6 scanf("%d", &n);
7 digitos = 0;
8 do {
9 n = n / 10;
10 digitos++;
11 } while (n > 0);
12 printf("O nmero tem %d dgitos\n", digitos);
13 return 0;
14 }

567765 palndromo;
32423 palndromo;
567675 no palndromo.

Dado um nmero natural n, n > 10, verificar se n palndromo.

13.4 Dados um nmero inteiro n > 0 e uma seqncia de n nmeros inteiros, determinar
quantos segmentos de nmeros iguais consecutivos compem essa seqncia.
Exemplo:
z}|{ z }| { z }| { z}|{
Para n = 9, a seqncia 5 , 2, 2, 4, 4, 4, 4, 1, 1 formada por 4 segmentos
de nmeros iguais.

13.5 Dados um nmero inteiro n > 0 e uma seqncia de n nmeros inteiros, determinar o
comprimento de um segmento crescente de comprimento mximo.
Exemplos:
z }| {
Na seqncia 5, 10, 6, 2, 4, 7, 9, 8, 3 o comprimento do segmento crescente m-
ximo 4.
Na seqncia 10, 8, 7, 5, 2 o comprimento do segmento crescente mximo 1.

FACOM UFMS
A ULA 14

N MEROS COM PONTO FLUTUANTE

Dadas as dificuldades inerentes da representao de nmeros reais nos computadores, as


linguagens de programao de alto nvel procuram super-las abstraindo essa representao
atravs do uso de nmeros com ponto flutuante. Na linguagem C, nmeros com ponto flutu-
ante podem ser manipulados como constantes ou variveis do tipo ponto flutuante. O armaze-
namento destes nmeros em memria se d de forma distinta do armazenamento de nmeros
inteiros e necessita de mais espao, como poderamos supor.
Nesta aula veremos como manipular nmeros de ponto flutuante, formalizando as defini-
es de constantes e variveis envolvidas, alm de aprender as regras de uso desses elementos
em expresses aritmticas.

14.1 Constantes e variveis do tipo ponto flutuante

Nmeros inteiros no so suficientes ou adequados para solucionar todos os problemas.


Muitas vezes so necessrias variveis que possam armazenar nmeros com dgitos aps a
vrgula, nmeros muito grandes ou nmeros muito pequenos. A vrgula, especialmente em
pases de lngua inglesa, substituda pelo ponto decimal. A linguagem C permite que va-
riveis sejam declaradas de forma a poderem armazenar nmeros de ponto flutuante, com os
tipos bsicos float e double .
Uma constante do tipo ponto flutuante se distingue de uma constante do tipo inteiro pelo
uso do ponto decimal. Dessa forma, 3.0 uma constante do tipo ponto flutuante, assim como
1.125 e -765.567 . Podemos omitir os dgitos antes do ponto decimal ou depois do ponto
decimal, mas obviamente no ambos. Assim, 3. e -.1115 so constantes do tipo ponto
flutuante vlidas.
Na linguagem C, uma varivel do tipo ponto flutuante pode armazenar valores do tipo
ponto flutuante e deve ser declarada com a palavra reservada float ou double . Por exem-
plo, a declarao

float x, y;
double arco;

realiza a declarao das variveis x e y como variveis do tipo ponto flutuante.

79
14.1 C ONSTANTES E VARIVEIS DO TIPO PONTO FLUTUANTE 80

A distino entre os tipos de ponto flutuante float e double se d pela quantidade de


preciso necessria. Quando a preciso no crtica, o tipo float adequado. O tipo double
fornece preciso maior.
A tabela a seguir mostra as caractersticas dos tipos de ponto flutuante quando implemen-
tados de acordo com o padro IEEE1 . Em computadores que no seguem o padro IEEE, esta
tabela pode no ser vlida.

Tipo Menor valor (positivo) Maior valor Preciso


float 1.17549 1038 3.40282 1038 6 dgitos
double 2.22507 10308 1.79769 10308 15 dgitos

O padro IEEE 754 estabelece dois formatos primrios para nmeros de ponto flutuante:
o formato de preciso simples, com 32 bits, e o formato de preciso dupla, com 64 bits. Os
nmeros de ponto flutuante so armazenados no formato de notao cientfica, composto por
trs partes: um sinal, um expoente e uma frao. O nmero de bits reservado para representar o
expoente determina quo grande o nmero pode ser, enquanto que o nmero de bits da frao
determina sua preciso.
Um exemplo de um programa que usa constantes e variveis do tipo float apresentado
no programa 14.1. Neste programa, uma seqncia de cem nmeros do tipo ponto flutuante
informada pelo usurio. Em seguida, a mdia aritmtica desses nmeros calculada e, por
fim, mostrada na sada padro.

Programa 14.1: Calcula a mdia de 100 nmeros do tipo ponto flutuante.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i;
5 float numero, soma, media;
6 soma = 0.0;
7 for (i = 1; i <= 100; i++) {
8 printf("Informe um nmero: ");
9 scanf("%f", &numero);
10 soma = soma + numero;
11 }
12 media = soma / 100;
13 printf("A mdia dos 100 nmeros %f\n", media);
14 return 0;
15 }

Observe que a cadeia de caracteres de formatao contm um especificador de converso


para um nmero de ponto flutuante %f , diferentemente do especificador de converso para
nmeros inteiros ( %d ) que vimos anteriormente.
1
Instituto de Engenheiros Eltricos e Eletrnicos, do ingls Institute of Electrical and Eletronics Engineers (IEEE).

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 81

14.2 Expresses aritmticas

Na linguagem C existem regras de converso implcita de valores do tipo inteiro e do tipo


ponto flutuante. A regra principal diz que a avaliao de uma expresso aritmtica, isto , seu
resultado, ser do tipo ponto flutuante caso algum de seus operandos resultante da avaliao
de uma expresso aritmtica seja tambm do tipo ponto flutuante. Caso contrrio, isto , se
todos os operandos so valores do tipo inteiro, o resultado da expresso aritmtica ser um
valor do tipo inteiro. Vejamos um exemplo no programa 14.2.

Programa 14.2: Programa que apresenta valores do tipo inteiro e do tipo ponto flutuante.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i1, i2;
5 float f1, f2;
6 i1 = 190;
7 f1 = 100.5;
8 i2 = i1 / 100;
9 printf("i2 = %d\n", i2);
10 f2 = i1 / 100;
11 printf("f2 = %f\n", f2);
12 f2 = i1 / 100.0;
13 printf("f2 = %f\n", f2);
14 f2 = f1 / 100;
15 printf("f2 = %f\n", f2);
16 return 0;
17 }

O resultado da execuo desse programa apresentado a seguir.

i2 = 1
f2 = 1.000000
f2 = 1.900000
f2 = 1.005000

O primeiro valor um nmero do tipo inteiro que representa o quociente da diviso de


dois nmeros do tipo inteiro 190 e 100 . J tnhamos trabalhado com expresses aritmticas
semelhantes em aulas anteriores. No segundo valor temos de olhar para a expresso aritm-
tica direita do comando de atribuio. Esta expresso uma expresso aritmtica que s
contm operandos do tipo inteiro. Por isso, o resultado o nmero do tipo inteiro 1 , que
atribudo varivel f2 . O comando printf mostra o contedo da varivel f2 como um
nmero do tipo ponto flutuante e assim a sada f2 = 1.000000 . Em seguida, nas prximas
duas impresses, as expresses aritmticas correspondentes contm operandos do tipo ponto

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 82

flutuante: a constante do tipo ponto flutuante 100.0 na primeira e a varivel do tipo ponto
flutuante f1 na segunda. Por isso, as expresses aritmticas tm como resultados nmeros do
tipo ponto flutuante e a sada mostra os resultados esperados.
Suponha, no entanto, que um programa semelhante, o programa 14.3, tenha sido digitado,
salvo, compilado e executado.

Programa 14.3: Outro programa que apresenta valores do tipo inteiro e de ponto flutuante.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i1, i2;
5 float f1;
6 i1 = 3;
7 i2 = 2;
8 f1 = i1 / i2;
9 printf("f1 = %f\n", f1);
10 return 0;
11 }

O resultado da execuo deste programa :

f1 = 1.000000

Mas e se quisssemos que a diviso i1/ i2 tenha como resultado um valor do tipo ponto
flutuante? Neste caso, devemos usar um operador unrio chamado operador conversor de
tipo, do ingls type cast operator, sobre alguma das variveis da expresso. No caso acima,
poderamos usar qualquer uma das atribuies abaixo:

f1 = (float) i1 / (float) i2;


f1 = (float) i1 / i2;
f1 = i1 / (float) i2;

e o resultado e a sada do programa seria ento:

f1 = 1.500000

O operador (float) assim chamado de operador conversor do tipo ponto flutuante.

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 83

Um outro operador unrio, que faz o inverso do operador conversor do tipo ponto flutu-
ante, o operador conversor do tipo inteiro, denotado por (int) . Esse operador conversor
do tipo inteiro converte o valor de uma expresso aritmtica para um valor do tipo inteiro.
Vejamos o programa 14.4.

Programa 14.4: Mais um programa que apresenta valores do tipo inteiro e de ponto flutuante.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i1, i2, i3, i4;
5 float f1, f2;
6 f1 = 10.8;
7 f2 = 1.5;
8 i1 = f1 / f2;
9 printf("i1 = %d\n", i1);
10 i2 = (int) f1 / f2;
11 printf("i2 = %d\n", i2);
12 i3 = f1 / (int) f2;
13 printf("i3 = %d\n", i3);
14 i4 = (int) f1 / (int) f2;
15 printf("i4 = %d\n", i4);
16 return 0;
17 }

A sada do programa 14.4

i1 = 7
i2 = 6
i3 = 10
i4 = 10

As primeiras duas atribuies fazem com que as variveis f1 e f2 recebam as constantes de


ponto flutuante 10.8 e 1.5 . Em seguida, a atribuio i1 = f1 / f2; faz com que o resul-
tado da expresso aritmtica direita do comando de atribuio seja atribudo varivel i1
do tipo inteiro. A expresso aritmtica devolve um valor de ponto flutuante: 10.8 / 1.5
= 7.2 . Porm, como do lado esquerdo da atribuio temos uma varivel do tipo inteiro, a
parte fracionria deste resultado descartada e o valor 7 ento armazenado na varivel i1
e mostrado com o comando printf . Na prxima linha, a expresso aritmtica do lado di-
reito da atribuio (int) f1 / f2 que avaliada como (int) 10.8 / 1.5 = 10 / 1.5
= 6.666666 e, como do lado direito do comando de atribuio temos uma varivel do tipo
inteiro i2 , o valor 6 armazenado nesta varivel e mostrado na sada atravs do comando
printf na linha seguinte. Na prxima linha, a expresso aritmtica f1 / (int) f2 ava-
liada como 10.8 / (int) 1.5 = 10.8 / 1 = 10.8 e, de novo, como do lado direito do

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 84

comando de atribuio temos uma varivel do tipo inteiro i3 , o valor armazenado nesta va-
rivel 10 , que tambm mostrado na sada atravs do comando printf logo a seguir. E,
por fim, a expresso aritmtica seguinte (int) f1 / (int) f2 e sua avaliao dada por
(int) 10.8 / (int) 1.5 = 10 / 1 = 10 e portanto este valor atribudo varivel i4
e apresentado na sada pelo comando printf .
Um compilador da linguagem C considera qualquer constante com ponto flutuante como
sendo uma constante do tipo double , a menos que o programador explicitamente diga o
contrrio, colocando o smbolo f no final da constante. Por exemplo, a constante 3.1415f
uma constante com ponto flutuante do tipo float , ao contrrio da constante 55.726 , que
do tipo double .
Uma constante de ponto flutuante tambm pode ser expressa em notao cientfica. O valor
1.342e-3 um valor de ponto flutuante e representa o valor 1,342103 ou 0,001324. O valor
antes do smbolo e chamado mantissa e o valor aps esse smbolo chamado expoente do
nmero de ponto flutuante.
Para mostrar um nmero com ponto flutuante do tipo double na sada padro podemos
usar a mesmo caracter de converso de tipo %f que usamos para mostrar um nmero com
ponto flutuante do tipo float . Para realizar a leitura de um nmero de ponto flutuante que
ser armazenado em uma varivel do tipo double usamos os caracteres %lf como caracteres
de converso de tipo.
Um outro exemplo do uso de constantes e variveis do tipo ponto flutuante ( float e
double ) mostrado no programa 14.5, que computa a rea de um crculo cujo valor do raio
informado pelo usurio em radianos.

Programa 14.5: Programa para clculo da rea do crculo.

1 #include <stdio.h>
2 int main(void)
3 {
4 float pi;
5 double raio, area;
6 pi = 3.141592f;
7 printf("Digite o valor do raio: ");
8 scanf("%lf", &raio);
9 area = (double)pi * raio * raio;
10 printf("A rea do crculo %f\n", area);
11 return 0;
12 }

Exerccios

14.1 Uma pessoa aplicou um capital de x reais a juros mensais de y% durante 1 ano. Determi-
nar o montante de cada ms durante este perodo.

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 85

Programa 14.6: Uma soluo para o exerccio 14.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int mes;
5 float x, y;
6 printf("Informe o capital inicial: ");
7 scanf("%d", &x);
8 printf("Informe a taxa de juros: ");
9 scanf("%d", &y);
10 for (mes = 1; mes <= 12; mes++) {
11 x = x * (1 + y / 100);
12 printf("Ms: %d Montante: %f\n", mes, x);
13 }
14 return 0;
15 }

14.2 Os pontos (x, y) que pertencem figura H (veja a figura 14.1) so tais que x > 0, y > 0 e
x2 + y 2 6 1. Dados n pontos reais (x, y), verifique se cada ponto pertence ou no a H.

1111111111
0000000000
y
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
H
0000000000
1111111111
0000000000
1111111111
0000000000x
1111111111
Figura 14.1: rea H de um quarto de um crculo.

14.3 Considere o conjunto H = H1 H2 de pontos reais, onde

H1 = {(x, y)|x 6 0, y 6 0, y + x2 + 2x 3 6 0}
H2 = {(x, y)|x > 0, y + x2 2x 3 6 0}

Dado um nmero inteiro n > 0, leia uma seqncia de n pontos reais (x, y) e verifique se
cada ponto pertence ou no ao conjunto H, contando o nmero de pontos da seqncia
que pertencem a H.

14.4 Dado um natural n, determine o nmero harmnico Hn definido por


n
X 1
Hn = .
k
k=1

FACOM UFMS
14.2 E XPRESSES ARITMTICAS 86

14.5 Para n > 0 alunos de uma determinada turma so dadas 3 notas de provas. Calcular a
mdia aritmtica das provas de cada aluno, a mdia da classe, o nmero de aprovados e
o nmero de reprovados, onde o critrio de aprovao mdia > 5.0.

14.6 Dados nmeros reais a, b e c, calcular as razes de uma equao do 2o grau da forma
ax2 + bx + c = 0. Imprimir a soluo em uma das seguintes formas:

a. DUPLA b. REAIS DISTINTAS c. COMPLEXAS


raiz raiz 1 parte real
raiz 2 parte imaginria

FACOM UFMS
A ULA 15

E XERCCIOS

Nesta aula faremos mais exerccios usando nmeros com ponto flutuante.

Enunciados

15.1 Dado um inteiro positivo n, calcular e imprimir o valor da seguinte soma


1 2 3 n
+ + + ... + .
n n1 n2 1

Programa 15.1: Uma soluo para o exerccio 15.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, numerador, denominador;
5 float soma;
6 printf("Informe o valor de n: ");
7 scanf("%d", &n);
8 numerador = 1;
9 denomidador = n;
10 soma = 0.0;
11 for (i = 1; i <= n; i++) {
12 soma = soma + (float) numerador / denominador;
13 numerador++;
14 denominador--;
15 }
16 printf("Soma = %f", soma);
17 return 0;
18 }

15.2 Faa um programa que calcula a soma


1 1 1 1 1
1 + + ... +
2 3 4 9999 10000
pelas seguintes maneiras:

87
88

(a) adio dos termos da direita para a esquerda;


(b) adio dos termos da esquerda para a direita;
(c) adio separada dos termos positivos e dos termos negativos da esquerda para a
direita;
(d) adio separada dos termos positivos e dos termos negativos da direita para a es-
querda;
(e) frmula de recorrncia;
(f) frmula do termo geral.

15.3 Uma maneira de calcular o valor do nmero utilizar a seguinte srie:


4 4 4 4 4
=4 + + + ...
3 5 7 9 11
Fazer um programa para calcular e imprimir o valor de atravs da srie acima, com
preciso de 4 casas decimais. Para obter a preciso desejada, adicionar apenas os termos
cujo valor absoluto seja maior ou igual a 0,0001.

15.4 Dado um nmero real x tal que 0 6 x 6 1, calcular uma aproximao do arco tangente
de x em radianos atravs da srie infinita:
x3 x5 x7
arctan x = x + + ...
3 5 7
2k+1
incluindo todos os termos da srie at | x2k+1 | < 0.0001, com k > 0.

15.5 Uma maneira de calcular o valor de ex utilizar a seguinte srie:

x1 x2 x3
e x = x0 + + + + ...
1! 2! 3!
k
Dados dois nmeros reais x e , calcular o valor de ex incluindo todos os termos Tk = xk! ,
com k > 0, at que Tk < . Mostre na sada o valor computado e o nmero total de termos
usados na srie.

15.6 Dados x real e n natural, calcular uma aproximao para cos x, onde x dado em radia-
nos, atravs dos n primeiros termos da seguinte srie:

x2 x4 x6 x2k
cos x = 1 + + . . . + (1)k + ...
2! 4! 6! (2k)!
com k > 0. Compare com os resultados de sua calculadora.

15.7 Dados x e reais, > 0, calcular uma aproximao para sen x, onde x dado em radianos,
atravs da seguinte srie infinita

x x3 x5 x2k+1
sen x = + . . . + (1)k + ...
1! 3! 5! (2k + 1)!
x2k+1
incluindo todos os termos Tk = (2k+1)! , com k > 0, at que Tk < . Compare com os
resultados de sua calculadora.

FACOM UFMS
A ULA 16

E XERCCIOS

Nesta aula faremos exerccios com estruturas de repetio encaixadas.

Enunciados

16.1 Dados um nmero inteiro n e n seqncias de nmeros inteiros, cada qual terminada por
0, determinar a soma dos nmeros pares de cada seqncia.

Programa 16.1: Uma soluo para o exerccio 16.1.

1 #include <stdio.n>
2 int main(void)
3 {
4 int n, num, soma;
5 printf("Informe o valor de n: ");
6 scanf("%d", &n);
7 for (i = 1; i <= n; i++) {
8 soma = 0;
9 do {
10 printf("(Sequncia %d) Informe um nmero: ", i);
11 scanf("%d", &num);
12 if (!(num % 2))
13 soma = soma + num;
14 } while (num != 0);
15 printf("(Sequncia %d) Soma = %d\n", i, soma);
16 }
17 return 0;
18 }

16.2 Dados um nmero inteiro n > 0 e uma seqncia de n nmeros inteiros positivos deter-
minar o fatorial de cada nmero da seqncia.

16.3 Dados n nmeros inteiros positivos, calcular a soma dos que so primos.

16.4 Dado um nmero inteiro positivo n, determinar todos os inteiros entre 1 e n que so
comprimento de hipotenusa de um tringulo retngulo com catetos inteiros.

89
90

16.5 Dados dois naturais m e n, determinar, entre todos os pares de nmeros naturais (x, y)
tais que x 6 m e y 6 n, um par para o qual o valor da expresso xy x2 + y seja mximo
e calcular tambm esse mximo.

16.6 Sabe-se que um nmero da forma n3 igual soma de n nmeros mpares consecutivos.
Exemplo:

13 =1
23 =3+5
33 = 7 + 9 + 11
43 = 13 + 15 + 17 + 19
..
.

Dado m, determine os mpares consecutivos cuja soma igual a n3 para n assumindo


valores de 1 a m.

16.7 Dado um nmero inteiro positivo, determine a sua decomposio em fatores primos,
calculando tambm a multiplicidade de cada fator.
Exemplo:

Se n = 600 a sada deve ser


fator 2 multiplicidade 3
fator 3 multiplicidade 1
fator 5 multiplicidade 2

16.8 Dados n inteiros positivos, determinar o mximo divisor comum entre eles.

FACOM UFMS
A ULA 17

C ARACTERES

Nesta aula vamos estudar um novo tipo bsico de dados, o tipo caracter. Na linguagem C
existem dois tipos de caracteres: caracter com sinal e caracter sem sinal. Esses dois tipos so
apenas interpretaes diferentes do conjunto de todas as seqncias de 8 bits, sendo que essa
diferena irrelevante na prtica. Esta aula baseada especialmente no livro [4] e tambm no
livro [7].

17.1 Representao grfica

Na prtica, em todas as linguagens de programao de alto nvel, incluindo a lingua-


gem C, cada caracter armazenado em um nico byte na memria do computador, ou
seja, em 8 bits. Conseqentemente, na linguagem C um caracter sem sinal um n-
mero do conjunto {0, 1, . . . , 254, 255} e um caracter com sinal um nmero do conjunto
{128, . . . , 1, 0, 1, . . . , 127}. Ou seja, um caracter uma seqncia de 8 bits, dentre as 256
seqncias de 8 bits possveis.
A impresso de um caracter na sada padro a sua representao como um smbolo gr-
fico. Por exemplo, o smbolo grfico do caracter 97 a . Alguns caracteres tm representaes
grficas especiais, como o caracter 10 que representado por uma mudana de linha.
Os smbolos grficos dos caracteres 0 a 127 , de 7 bits, foram codificados e padronizados
pelo Cdigo Padro Americano para Troca de Informaes (do ingls American Standard Code for
Information Interchange). Por conta disso, essa representao grfica dos caracteres conhecida
como tabela ASCII. Essa codificao foi desenvolvida em 1960 e tem como base o alfabeto da
lngua inglesa. Muitas das codificaes de caracteres mais modernas herdaram como base a
tabela ASCII.
As limitaes do conjunto de caracteres da tabela ASCII, e tambm da tabela EBCDIC, logo
mostraram-se aparentes e outros mtodos foram desenvolvidos para estend-los. A necessi-
dade de incorporar mltiplos sistemas de escrita, incluindo a famlia dos caracteres do leste
asitico, exige suporte a um nmero bem maior de caracteres. Por exemplo, o repertrio com-
pleto do UNICODE compreende mais de cem mil caracteres. Outros repertrios comuns incluem
ISO 8859-1 bastante usado nos alfabetos latinos e, por isso, tambm conhecido como ISO Latin1.
Dos 128 caracteres padronizados da tabela ASCII e de seus smbolos grficos correspon-
dentes, 33 deles so no-imprimveis e os restantes 95 so imprimveis. Os caracteres no-
imprimveis so caracteres de controle, criados nos primrdios da computao, quando se usa-
vam mquinas teletipo e fitas de papel perfurado, sendo que atualmente grande parte deles
esto obsoletos.

91
17.1 R EPRESENTAO GRFICA 92

Na tabela a seguir, apresentamos alguns desses caracteres no-imprimveis. Muitos deles


no so mostrados por opo ou por terem cado em desuso.

Bin Dec Significado


0000 0000 00 Nulo
0000 0111 07 Campainha
0000 1001 09 Tabulao horizontal
0000 1010 10 Mudana de linha
0000 1011 11 Tabulao vertical
0000 1100 12 Quebra de pgina
0000 1101 13 Retorno do carro/cursor
0111 1111 127 Delete

A tabela a seguir mostra os 95 caracteres imprimveis da tabela ASCII.

Bin Dec Sim Bin Dec Sim Bin Dec Sim


0010 0000 32 0100 0000 64 @ 0110 0000 96
0010 0001 33 ! 0100 0001 65 A 0110 0001 97 a
0010 0010 34 " 0100 0010 66 B 0110 0010 98 b
0010 0011 35 # 0100 0011 67 C 0110 0011 99 c
0010 0100 36 $ 0100 0100 68 D 0110 0100 100 d
0010 0101 37 % 0100 0101 69 E 0110 0101 101 e
0010 0110 38 & 0100 0110 70 F 0110 0110 102 f
0010 0111 39 0100 0111 71 G 0110 0111 103 g
0010 1000 40 ( 0100 1000 72 H 0110 1000 104 h
0010 1001 41 ) 0100 1001 73 I 0110 1001 105 i
0010 1010 42 * 0100 1010 74 J 0110 1010 106 j
0010 1011 43 + 0100 1011 75 K 0110 1011 107 k
0010 1100 44 , 0100 1100 76 L 0110 1100 108 l
0010 1101 45 - 0100 1101 77 M 0110 1101 109 m
0010 1110 46 . 0100 1110 78 N 0110 1110 110 n
0010 1111 47 / 0100 1111 79 O 0110 1111 111 o
0011 0000 48 0 0101 0000 80 P 0111 0000 112 p
0011 0001 49 1 0101 0001 81 Q 0111 0001 113 q
0011 0010 50 2 0101 0010 82 R 0111 0010 114 r
0011 0011 51 3 0101 0011 83 S 0111 0011 115 s
0011 0100 52 4 0101 0100 84 T 0111 0100 116 t
0011 0101 53 5 0101 0101 85 U 0111 0101 117 u
0011 0110 54 6 0101 0110 86 V 0111 0110 118 v
0011 0111 55 7 0101 0111 87 W 0111 0111 119 w
0011 1000 56 8 0101 1000 88 X 0111 1000 120 x
0011 1001 57 9 0101 1001 89 Y 0111 1001 121 y
0011 1010 58 : 0101 1010 90 Z 0111 1010 122 z
0011 1011 59 ; 0101 1011 91 [ 0111 1011 123 {
0011 1100 60 < 0101 1100 92 \ 0111 1100 124 |
0011 1101 61 = 0101 1101 93 ] 0111 1101 125 }
0011 1110 62 > 0101 1110 94 ^ 0111 1110 126 ~
0011 1111 63 ? 0101 1111 95 _

FACOM UFMS
17.2 C ONSTANTES E VARIVEIS 93

Os caracteres com sinal, quando dispostos em ordem crescente, apresentam-se de tal forma
que as letras acentuadas precedem as no-acentuadas. Os caracteres sem sinal, ao contrrio, em
ordem crescente apresentam-se de forma que as letras no-acentuadas precedem as acentuadas.
Essa a nica diferena entre caracteres com e sem sinal.

17.2 Constantes e variveis

Uma varivel do tipo caracter com sinal pode ser declarada na linguagem C com a palavra
reservada char . Uma varivel do tipo caracter sem sinal pode ser declarada com a mesma
palavra reservada char , mas com o especificador de tipo unsigned a precedendo. Especifi-
cadores de tipos bsicos sero estudados em detalhes na aula 18. Dessa forma,

char c, d, e;
unsigned char f, g, h;

so declaraes vlidas de variveis do tipo caracter.


Uma constante do tipo caracter um nmero no intervalo de 0 a 255 ou de 128 a 127. Por
exemplo, para as variveis c , d e e declarada acima, podemos fazer

c = 122;
d = 59;
e = 51;

Mais comum e confortavelmente, podemos especificar uma constante do tipo caracter atra-
vs da representao grfica de um caracter, envolvendo-o por aspas simples. Por exemplo,
z , ; e 3 so exemplos de constantes do tipo caracter. Assim, bem mais cmodo
fazer as atribuies

c = z;
d = ;;
e = 3;

que, na prtica, so idnticas s anteriores.


Alguns caracteres produzem efeitos especiais tais como acionar um som de campainha ou
realizar uma tabulao horizontal. Para representar um caracter como esse na linguagem C
usamos uma seqncia de dois caracteres consecutivos iniciada por uma barra invertida. Por
exemplo, \n o mesmo que 10 e representa uma mudana de linha. A tabela a seguir
mostra algumas constantes do tipo caracter.

FACOM UFMS
17.2 C ONSTANTES E VARIVEIS 94

caracter constante smbolo grfico


0 \0 caracter nulo
9 \t tabulao horizontal
10 \n mudana de linha
11 \v tabulao vertical
12 \f quebra de pgina
13 \r retorno do carro/cursor
32 espao
55 7 7
92 \\ \
97 a a

Na linguagem C, um branco (do ingls whitespace) definido como sendo um caracter que
uma tabulao horizontal, uma mudana de linha, uma tabulao vertical, uma quebra de
pgina, um retorno de carro/cursor e um espao. Ou seja, os caracteres 9, 10, 11, 12, 13 e 32
so brancos e as constantes correspondentes so \t , \n , \v , \f , \r e .
A funo scanf trata todos os brancos como se fossem , assim como outras funes da
linguagem C tambm o fazem.
Para imprimir um caracter na sada padro com a funo printf devemos usar o especi-
ficador de converso de tipo caracter %c na seqncia de caracteres de formatao. Para ler
um caracter a partir da entrada padro com a funo scanf tambm devemos usar o mesmo
especificador de converso de tipo caracter %c na seqncia de caracteres de formatao. Um
exemplo de uso do tipo bsico caracter mostrado no programa 17.1.

Programa 17.1: Um programa usando o tipo char .

1 #include <stdio.h>
2 int main(void)
3 {
4 char c;
5 c = a;
6 printf("%c\n", c);
7 c = 97;
8 printf("%c\n", c);
9 printf("Informe um caracter: ");
10 scanf("%c", &c);
11 printf("%c = %d\n", c, c);
12 return 0;
13 }

Como vimos at aqui nesta seo, por enquanto no h muita utilidade para constantes e
variveis do tipo caracter. Mas em breve iremos us-las para construir um tipo de dados muito
importante chamado de cadeia de caracteres.

FACOM UFMS
17.3 E XPRESSES COM CARACTERES 95

17.3 Expresses com caracteres

Como caracteres so implementados como nmeros inteiros em um byte, as expresses


envolvendo caracteres herdam todas as propriedades das expresses envolvendo nmeros in-
teiros. Em particular, as operaes aritmticas envolvendo variveis do tipo unsigned char
e char so executadas em aritmtica int .
Dessa forma, considere o trecho de cdigo a seguir:

unsigned char x, y, z;
x = 240;
y = 65;
z = x + y;

Na ltima linha o resultado da expresso x + y 305 . No entanto, atribuies de n-


meros inteiros a variveis do tipo caracter so feitas mdulo 256. Por isso, a varivel z acima
receber o valor 49 .
Do mesmo modo, aps a execuo do trecho de cdigo a seguir:

unsigned char x;
char c;
x = 256;
c = 136;

a varivel x conter o valor 0 e a varivel c conter o valor -120 .


Expresses relacionais podem naturalmente envolver caracteres. No trecho de cdigo a
seguir, algumas expresses lgicas envolvendo caracteres so mostradas:

char c;
c = a;
if (a <= c && c <= z)
c = c - ;
printf("%c = %d\n", c, c);
if (65 <= c && c <= 122)
c = c + a;
printf("%c = %d\n", c, c);

Qual a sada gerada pelas duas chamadas da funo printf no trecho de cdigo acima?

FACOM UFMS
17.3 E XPRESSES COM CARACTERES 96

Exerccios

17.1 Verifique se o programa 17.2 est correto.

Programa 17.2: O que faz este programa?

1 #include <stdio.h>
2 int main(void)
3 {
4 char c;
5 for (c = 0; c < 128; c++)
6 printf(".");
7 printf("\ntchau!\n");
8 return 0;
9 }

17.2 Escreva um programa que imprima todas as letras minsculas e todas as letras maiscu-
las do alfabeto.

17.3 Escreva um programa que traduz um nmero de telefone alfabtico de 8 dgitos em um


nmero de telefone na forma numrica. Suponha que a entrada sempre dada em carac-
teres maisculos.
Exemplo:

Se a entrada URGENCIA a sada deve ser 87436242 . Se a entrada


1111FOGO a sada deve ser 11113646 .

Se voc no possui um telefone, ento as letras que correspondem s teclas so as seguin-


tes: 2=ABC, 3=DEF, 4=GHI, 5=JKL, 6=MNO, 7=PQRS, 8=TUV e 9=WXYZ.

17.4 Scrabble um jogo de palavras em que os jogadores formam palavras usando pequenos
quadrados que contm uma letra e um valor. O valor varia de uma letra para outra, ba-
seado na sua raridade. Os valores so os seguintes: AEILNORSTU=1, DG=2, BCMP=3,
FHVWY=4, K=5, JX=8 e QZ=10.
Escreva um programa que receba uma palavra e compute o seu valor, somando os va-
lores de suas letras. Seu programa no deve fazer distino entre letras maisculas e
minsculas.
Exemplo:

Se a palavra de entrada programa a sada tem de ser 13 .

17.5 Escreva um programa que receba dois nmeros inteiros a e b e um caracter op, tal que op
pode ser um dos cinco operadores aritmticos disponveis na linguagem C ( + , - , * ,
/ , % ), realize a operao a op b e mostre o resultado na sada.

FACOM UFMS
A ULA 18

T IPOS DE DADOS BSICOS

Como vimos at esta aula, a linguagem C suporta fundamentalmente dois tipos de dados
numricos: tipos inteiros e tipos com ponto flutuante. Valores do tipo inteiro so nmeros
inteiros em um dado intervalo finito e valores do tipo ponto flutuante so tambm dados em
um intervalo finito e podem ter uma parte fracionria. Alm desses, o tipo caracter tambm
suportado pela linguagem e um valor do tipo caracter tambm um nmero inteiro em um
intervalo finito mais restrito que o dos tipos inteiros.
Nesta aula faremos uma reviso dos tipos de dados bsicos da linguagem C e veremos os
especificadores desses tipos, seus especificadores de converso para entrada e sada de dados,
as converses entre valores de tipos distintos, alm de definir nossos prprios tipos de dados.
Esta aula uma reviso das aulas 7, 14 e 17 e tambm uma extenso desses conceitos, podendo
ser usada como uma referncia para tipos de dados bsicos. As referncias usadas nesta aula
so [4, 7].

18.1 Tipos inteiros

Valores do tipo inteiro na linguagem C so nmeros inteiros em um intervalo bem definido.


Os tipos de dados inteiros na linguagem C tm tamanhos diferentes. O tipo int tem geral-
mente 32 bits: o bit mais significativo reservado para o sinal do nmero inteiro. Por isso, o
tipo int chamado de tipo inteiro com sinal. A palavra reservada signed pode ser usada
em conjunto com int para declarar uma varivel do tipo inteiro com sinal, embora signed
seja completamente dispensvel. Uma varivel do tipo inteiro sem sinal pode ser declarada
com a palavra unsigned precedendo a palavra reservada int .
Alm disso, programas podem necessitar de armazenar nmeros inteiros grandes e pode-
mos declarar variveis do tipo inteiro para armazenar tais nmeros com a palavra reservada
long . Do mesmo modo, quando h necessidade de economia de espao em memria, pode-
mos declarar uma varivel para armazenar nmeros inteiros menores com a palavra reservada
short precedendo a palavra reservada int .
Dessa forma, podemos construir um tipo de dados inteiro que melhor represente nossa ne-
cessidade usando as palavras reservadas signed , unsigned , short e long . Essas palavras
reservadas so chamadas de especificadores de tipos, do ingls type specifiers, da linguagem C.
Podemos combinar os especificadores de tipos descritos acima na declarao de uma vari-
vel do tipo inteiro. No entanto, apenas seis combinaes desses especificadores e da palavra
reservada int produzem tipos de dados diferentes:

97
18.1 T IPOS INTEIROS 98

short int
unsigned short int
int
unsigned int
long int
unsigned long int

Qualquer outra combinao de especificadores na declarao de uma varivel equi-


valente a uma das combinaes acima. A ordem que os especificadores ocorrem no tem
influncia sobre o resultado final: declaraes feitas com unsigned short int ou com
short unsigned int tm o mesmo resultado. Quando no estritamente necessrio, a
linguagem C permite que a palavra reservada int seja omitida, como por exemplo em
unsigned short e long .
O intervalo de valores representado por cada um dos seis tipos inteiros varia de uma m-
quina para outra. Os compiladores, entretanto, devem obedecer algumas regras fundamentais.
Em especial, o padro especifica que o tipo int no seja menor que o tipo short int e que
long int no seja menor que int .
As mquinas atuais, em sua grande maioria, so de 32 bits e a tabela abaixo mostra os
intervalos de cada um dos possveis tipos inteiros. Observe que os tipos int e long int tm
intervalos idnticos nas mquinas de 32 bits. Em mquinas de 64 bits, h diferenciao entre
esses dois tipos.

Tipo Menor valor Maior valor


short int 32.768 32.767
unsigned short int 0 65.535
int 2.147.483.648 2.147.483.647
unsigned int 0 4.294.967.294
long int 2.147.483.648 2.147.483.647
unsigned long int 0 4.294.967.294

Constantes numricas, como vimos, so nmeros que ocorrem no cdigo dos programas,
em especial em expresses aritmticas. As constantes, assim como as variveis, podem ser
nmeros inteiros ou nmeros com ponto flutuante.
Na linguagem C, as constantes numricas do tipo inteiro podem ser descritas em decimal
ou base 10, octal ou base 8 ou ainda hexadecimal ou base 16. Constantes decimais contm
dgitos de 0 (zero) a 9 (nove), mas no devem iniciar com 0 (zero). Exemplos de constantes
decimais so mostrados a seguir:

75 -264 32767

Constantes octais contm somente dgitos entre 0 (zero) e 7 (sete) e devem comear necessaria-
mente com o dgito 0 (zero). Exemplos de constantes octais so mostrados a seguir:

FACOM UFMS
18.1 T IPOS INTEIROS 99

075 0367 07777

E constante hexadecimais contm dgitos entre 0 (zero) e 9 (nove) e letras entre a e f, e sempre
devem iniciar com os caracteres 0x. As letras podem ser maisculas ou minsculas. Exemplos
de constantes hexadecimais so mostrados a seguir:

0xf 0x8aff 0X12Acf

importante destacar que a descrio de constantes como nmeros na base octal e hexade-
cimal apenas uma forma alternativa de escrever nmeros que no tem efeito na forma como
so armazenados na memria, j que sempre so armazenados na base binria. Alm disso,
essas notaes so mais usadas quando trabalhamos com programas de baixo nvel.
No h necessidade de se convencionar uma nica notao para escrever constantes e po-
demos trocar de uma notao para outra a qualquer momento. Na verdade, podemos inclusive
misturar essas notaes em expresses aritmticas como a seguir:

117 + 077 + 0xf0

e o resultado dessa expresso 420 na base decimal.


O tipo de uma constante que um nmero inteiro na base decimal normalmente int .
Se o valor da constante for muito grande para armazen-la como um int , a constante ser
armazenada como um long int . Se mesmo nesse caso ainda no for possvel armazenar
tal constante, o compilador tenta, como um ltimo recurso, armazenar o valor como um
unsigned long int . Constantes na base octal ou hexadecimal so armazenadas de ma-
neira semelhante, com o compilador tentando armazen-la progressivamente como um int ,
unsigned int , long int ou unsigned long int .
Para forar que o compilador trate uma constante como um nmero inteiro grande, deve-
mos adicionar a letra L ou l ao final da constante. Como exemplo, as constantes abaixo so
do tipo long int :

22L 055l 0xffffL

Para indicar que uma constante no tem sinal, devemos adicionar a letra U ou u ao final
da constante. Por exemplo, as constantes abaixo so do tipo unsigned int :

FACOM UFMS
18.1 T IPOS INTEIROS 100

22u 072U 0xa09DU

Podemos indicar que uma constante um nmero inteiro grande e sem sinal usando uma
combinao das letras acima, em qualquer ordem. Por exemplo, as constantes abaixo so do
tipo unsigned long int :

22ul 07777UL 0xFFFFAALU

A funo printf usada para imprimir, entre outros, nmeros inteiros. Relembrando, a
funo printf tem o seguinte formato:

printf(cadeia, expresso 1, expresso 2, . . .);

onde cadeia a cadeia de caracteres de formatao. Quando a funo printf chamada,


a impresso da cadeia de caracteres de formatao ocorre na sada. Essa cadeia pode conter ca-
racteres usuais, que sero impressos diretamente, e tambm especificaes de converso, cada
uma iniciando com o caracter % e representando um valor a ser preenchido durante a impres-
so. A informao que sucede o caracter % especifica como o valor deve ser convertido
a partir de sua representao interna binria para uma representao imprimvel. Caracteres
usuais na cadeia de caracteres de formatao so impressos na sada exatamente como ocor-
rem na cadeia enquanto que as especificaes de converso so trocadas por valores a serem
impressos.
A tabela abaixo mostra tipos e especificadores de tipos e tambm especificadores de con-
verso para cadeias de caracteres de formatao da funo printf , especificamente para tra-
tamento de nmeros inteiros.

Especificador e tipo Especificador de converso


short int %hd ou %hi
unsigned short int %hu
int %d ou %i
unsigned int %u
long int %ld ou %li
unsigned long int %lu

A forma geral de um especificador de converso para nmeros inteiros em uma cadeia de


caracteres de formatao da funo printf a seguinte:

%[flags][comprimento][.preciso][hl]conversor

Os campos opcionais so mostrados entre colchetes. Ou seja, apenas conversor obri-


gatrio, que chamamos de especificador de converso. A tabela a seguir mostra os possveis
flags para um especificador de converso de uma cadeia de caracteres de formatao da funo
printf .

FACOM UFMS
18.1 T IPOS INTEIROS 101

Flag Significado
- Valor alinhado esquerda
+ Valor precedido por + ou -
espaos Valor positivo precedido por espaos
0 Nmero preenchido com zeros esquerda

Os especificadores de converso o e x (ou X ) permitem ainda que um nmero inteiro


a ser mostrado na sada o seja na base octal ou hexadecimal, respectivamente. Se o nmero
inteiro pequeno, grande, com ou sem sinal, essas opes tambm podem ser descritas no
especificador.
O programa 18.1 usa os tipos bsicos conhecidos para nmeros inteiros e seus especifica-
dores, juntamente com caracteres especificadores de converso nas cadeias de caracteres de
formatao das chamadas da funo printf .

Programa 18.1: Exemplo de tipos e especificadores de tipos inteiros e tambm de formatao


de impresso com especificadores de converso.

1 #include <stdio.h>
2 int main(void)
3 {
4 short int i1;
5 unsigned short int i2;
6 int i3;
7 unsigned int i4;
8 long int i5;
9 unsigned long int i6;
10 i1 = 159;
11 i2 = 630u;
12 i3 = -991023;
13 i4 = 98979U;
14 i5 = 8393202L;
15 i6 = 34268298UL;
16 printf("%hd %d %ho\n", i1, i1, i1);
17 printf("%hu %i %hx\n", i2, i2, i2);
18 printf("%+d % d %09d\n", i3, i3, i3);
19 printf("%X %d %u\n", i4, i4, i4);
20 printf("%+ld %ld %10ld\n", i5, i5, i5);
21 printf("%lu %10.8lu %-lu\n", i6, i6, i6);
22 return 0;
23 }

Assim como para a funo printf , outras opes de formatao tambm esto disponveis
para a funo scanf , mais que aquelas vistas at esta aula. Neste caso tambm vale o conceito
de especificadores de converso. Quando uma funo scanf executada, especificadores de
converso so procurados na cadeia de caracteres de formatao (de leitura), aps o smbolo
usual % .

FACOM UFMS
18.1 T IPOS INTEIROS 102

Os especificadores de converso h e l podem ser usados em uma cadeia de caracteres de


formatao de leitura, na funo scanf , com o mesmo sentido que na cadeia de caracteres de
formatao de escrita, na funo printf , isto , para indicar que a leitura ser feita como um
nmero inteiro curto ou longo, respectivamente. Um comprimento tambm pode ser usado,
indicando o comprimento mximo do valor a ser armazenado na varivel correspondente.

Especificador Significado
d valor a ser lido expresso em notao decimal; o argumento correspondente do tipo
int, a menos que os modificadores de converso h ou l tenham sido usados, casos
em que o valor ser short int ou long int, respectivamente

i como %d, exceto nmeros na base octal (comeando com 0) ou hexadecimal


(comeando com 0x ou 0X,) que tambm podem ser lidos

u valor a ser lido um inteiro e o argumento correspondente do tipo unsigned int

o valor a ser lido expresso na notao octal e pode opcionalmente ser precedido por 0;
o argumento correspondente um int, a menos que os modificadores de converso
h ou l tenham sido usados, casos em que short int ou long int,
respectivamente

x valor a ser lido expresso na notao hexadecimal e pode opcionalmente ser precedido
por 0x ou 0X; o argumento correspondente um unsigned int, a menos que os
modificadores de converso h ou l tenham sido usados, casos em que short int
ou long int, respectivamente

A funo scanf executada primeiro esperando por um valor a ser informado pelo usu-
rio e, depois, formatando este valor atravs do uso da cadeia de caracteres de formatao e do
especificador de converso.
A funo scanf finaliza a leitura de dados sempre que o usurio informa um branco, que
o caracter 32 (espao ou ), o caracter 9 (tabulao horizontal ou \t ), o caracter 10
(tabulao vertical ou \v ), o caracter 11 (retorno de carro/cursor ou \r ), o caracter 12
(mudana de linha ou \n ) ou o caracter 13 (avano de pgina ou \f ).
Nesse sentido, considere uma entrada como no trecho de cdigo abaixo:

scanf("%d%d", &a, &b);

Neste caso, se o usurio informar

prompt$ ./a.out
4 7

ou

FACOM UFMS
18.2 N MEROS COM PONTO FLUTUANTE 103

prompt$ ./a.out
4
7

ou ainda

prompt$ ./a.out
4 7

o resultado ser o mesmo, ou seja, os valores 4 e 7 sero armazenados nas variveis a e b ,


respectivamente.

18.2 Nmeros com ponto flutuante

Como apenas nmeros inteiros so incapazes de auxiliar na solues de diversos proble-


mas, valores reais so necessrios mais especificamente para descrever nmeros imensamente
grandes ou pequenos. Nmeros reais so armazenados na linguagem C como nmeros com
ponto flutuante.
A linguagem C fornece trs tipos de dados numricos com ponto flutuante:
float Ponto flutuante de preciso simples
double Ponto flutuante de preciso dupla
long double Ponto flutuante de preciso estendida
O tipo float usado quando a necessidade de preciso no crtica. O tipo double for-
nece maior preciso, suficiente para a maioria dos problemas. E o tipo long double fornece
a maior preciso possvel e raramente usado.
A linguagem C no estabelece padro para a preciso dos tipos com ponto flutuante, j
que diferentes computadores podem armazenar nmeros com ponto flutuante de formas dife-
rentes. Os computadores mais recentes seguem, em geral, as especificaes do Padro 754 da
IEEE. A tabela abaixo, mostrada na aula 14, mostra as caractersticas dos tipos numricos com
ponto flutuante quando implementados sob esse padro.

Tipo Menor valor (positivo) Maior valor Preciso


float 1.17549 1038 3.40282 1038 6 dgitos
double 2.22507 10308 1.79769 10308 15 dgitos

Constantes com ponto flutuante podem ser descritas de diversas formas. Por exemplo, as
constantes abaixo so formas vlidas de escrever o nmero 391,0:

391.0 391. 391.0e0 391E0 3.91e+2 .391e3 3910.e-1

FACOM UFMS
18.3 C ARACTERES 104

Uma constante com ponto flutuante deve conter um ponto decimal e/ou um expoente, que
uma potncia de 10 pela qual o nmero multiplicado. Se um expoente descrito, ele deve
vir precedido da letra e ou E . Opcionalmente, um sinal + ou - pode aparecer logo aps a
letra e ou E .
Por padro, constantes com ponto flutuante so armazenadas como nmeros de ponto flu-
tuante de preciso dupla. Isso significa que, quando um compilador C encontra em um pro-
grama uma constante 391.0 , por exemplo, ele armazena esse nmero na memria no mesmo
formato de uma varivel do tipo double . Se for necessrio, podemos forar o compilador a
armazenar uma constante com ponto flutuante no formato float ou long double . Para in-
dicar preciso simples basta adicionar a letra f ou F ao final da constante, como por exemplo
391.0f . Para indicar preciso estendida necessrio adicionar a letra l ou L ao final da
constante, como por exemplo 391.0L .
Os especificadores de converso para nmeros com ponto flutuante de preciso simples so
%e , %f e %g , tanto para escrita como para leitura. O formato geral de um especificador de
converso para nmeros com ponto flutuante parecido com aquele descrito na seo anterior,
como podemos observar abaixo:

%[flags][comprimento][.preciso][lL]conversor

O especificador de converso %e mostra/solicita um nmero com ponto flutuante no for-


mato exponencial ou notao cientfica. A preciso indica quantos dgitos aps o ponto sero
mostrados/solicitados ao usurio, onde o padro 6. O especificador de converso %f mos-
tra/solicita um nmero com ponto flutuante no formato com casas decimais fixas, sem expo-
ente. A preciso indica o mesmo que para %e . E o especificador de converso %g mostra o
nmero com ponto flutuante no formato exponencial ou com casas decimais fixas, dependendo
de seu tamanho. Diferentemente dos especificadores anteriores, a preciso neste caso indica o
nmero mximo de dgitos significativos a ser mostrado/solicitado.
Ateno deve ser dispensada com pequenas diferenas na escrita e na leitura de nmeros
com ponto flutuante de preciso dupla e estendida. Quando da leitura de um valor do tipo
double necessrio colocar a letra l precedendo e , f ou g . Esse procedimento neces-
srio apenas na leitura e no na escrita. Quando da leitura de um valor do tipo long double
necessrio colocar a letra L precedendo e , f ou g .

18.3 Caracteres

Como mencionamos na aula 17, na linguagem C cada caracter armazenado em um nico


byte na memria do computador. Assim, um caracter sem sinal um nmero do conjunto
{0, . . . , 255} e um caracter com sinal um nmero do conjunto {128, . . . , 1, 0, 1, . . . , 127}.
Ou seja, um caracter uma seqncia de 8 bits, dentre as 256 seqncias de 8 bits possveis. A
impresso de um caracter na sada padro a sua representao como um smbolo grfico. Por
exemplo, o smbolo grfico do caracter 97 a . Alguns caracteres tm representaes grficas
especiais, como o caracter 10 que representado por uma mudana de linha.
Uma varivel do tipo caracter (com sinal) pode ser declarada na linguagem C com a palavra
reservada char . Uma varivel do tipo caracter sem sinal pode ser declarada com a mesma
palavra reservada char , mas com o especificador de tipo unsigned a precedendo. Assim,

FACOM UFMS
18.3 C ARACTERES 105

char c, d, e;
unsigned char f, g, h;

so declaraes vlidas de variveis do tipo caracter.


Uma constante do tipo caracter sem sinal um nmero no intervalo de 0 a 255 e uma
constante do tipo caractere com sinal uma constante no intervalo de 128 a 127. Por exemplo,
para as variveis c , d e e declaradas acima, podemos fazer

c = 122;
d = 59;
e = 51;

Mais comum e confortavelmente, podemos especificar uma constante do tipo caracter atra-
vs da representao grfica de um caracter, envolvendo-o por aspas simples. Por exemplo,
z , ; e 3 so exemplos de constantes do tipo caracter. Assim, bem mais cmodo
fazer as atribuies

c = z;
d = ;;
e = 3;

que, na prtica, so idnticas.


Alguns caracteres produzem efeitos especiais tais como acionar um som de campainha ou
realizar uma tabulao horizontal. Para representar um caracter como esse na linguagem C
usamos uma seqncia de dois caracteres consecutivos iniciada por uma barra invertida. Por
exemplo, \n o mesmo que 10 e representa uma mudana de linha. A tabela a seguir
mostra algumas constantes do tipo caracter.

caracter constante smbolo grfico


0 \0 caracter nulo
9 \t tabulao horizontal
10 \n mudana de linha
11 \v tabulao vertical
12 \f quebra de pgina
13 \r retorno do carro/cursor
32 espao
55 7 7
92 \\ \
97 a a

FACOM UFMS
18.3 C ARACTERES 106

Na linguagem C, um branco (do ingls whitespace) definido como sendo um caracter que
uma tabulao horizontal, uma mudana de linha, uma tabulao vertical, uma quebra de
pgina, um retorno de carro/cursor ou um espao. Ou seja, os caracteres 9, 10, 11, 12, 13 e 32
so brancos e as constantes correspondentes so \t , \n , \v , \f , \r e . A
funo scanf trata todos os brancos como se fossem , assim como outras funes tambm
o fazem.
O especificador de converso %c permite que as funes scanf e printf de entrada
e sada de dados, respectivamente, possam ler ou escrever um nico caracter. importante
reiterar que a funo scanf no salta caracteres brancos antes de ler um caracter. Se o prximo
caracter a ser lido foi digitado como um espao ou uma mudana de linha, ento a varivel
correspondente conter um espao ou uma mudana de linha. Para forar a funo scanf
desconsiderar espaos em branco antes da leitura de um caracter, necessrio adicionar um
espao na cadeia de caracteres de formatao de leitura antes do especificador de formatao
%c , como a seguir:

scanf(" %c", &c);

Um espao na cadeia de caracteres de formatao de leitura significa que haver um salto de


zero ou mais caracteres brancos.
O programa 18.2 mostra um exemplo de uso da funo scanf e de uma cadeia de caracte-
res de formatao contendo um especificador de tipo %c .

Programa 18.2: Conta o nmero de vogais minsculas na frase digitada pelo usurio.

1 #include <stdio.h>
2 int main(void)
3 {
4 char c;
5 int conta;
6 printf("Digite uma frase: ");
7 conta = 0;
8 do {
9 scanf("%c", &c);
10 if (c == a || c == e || c == i || c == o || c == u)
11 conta++;
12 } while (c != \n);
13 printf("A frase tem %d vogais minsculas\n", conta);
14 return 0;
15 }

A funo scanf tem compreenso dificultada em alguns casos, especialmente quando


queremos ralizar a entrada de caracteres ou ainda quando misturamos a entrada de nmeros
e caracteres em um programa. A funo scanf essencialmente uma funo de casamento

FACOM UFMS
18.3 C ARACTERES 107

de cadeias de caracteres que tenta emparelharar grupos de caracteres com especificaes de


converso. Essa funo tambm controlada por uma cadeia de caracteres de formatao e,
quando chamada, comea o processamento da informao nessa cadeia a partir do caracter
mais a esquerda. Para cada especificao de converso na cadeia de caracteres de formatao,
a funo scanf tenta localizar um item do tipo apropriado na cadeia de entrada, saltando
brancos se necessrio. scanf l ento esse item, parando quando encontra um caracter que
que no pode pertencer quele item. Se o item foi lido corretamente, a funo scanf continua
processando o restante da cadeia de caracteres de formatao. Se qualquer item no pode ser
lido com sucesso, a funo scanf pra imediatamente sem olhar para o resto da cadeia de
caracteres de formatao e o restante da cadeia de entrada.
Considere, por exemplo, que temos duas variveis do tipo int com identificadores a e
b e duas variveis do tipo float com identificadores x e y . Considere a seguinte chamada
funo scanf :

scanf("%d%d%f%f", &a, &b, &x, &y);

Suponha que o usurio informou os valores correspondentes a essas variveis da seguinte


forma:

prompt$ ./a.out
-10
2 0.33
27.8e3

Como, nesse caso, a leitura busca por nmeros, a funo ignora os brancos. Dessa forma, os
nmeros acima podem ser colocados todos em uma linha, separados por espaos, ou em vrias
linhas, como mostramos acima. A funo scanf enxerga a entrada acima como uma cadeia
de caracteres como abaixo:
-102 0.33 27.8e3
onde representa o caracter espao e representa o caracter de mudana de linha (ou a tecla
Enter ). Como a funo salta os brancos, ento os nmeros so lidos de forma correta.
Se a cadeia de caracteres de formatao de leitura contm caracteres usuais alm dos es-
pecificadores de tipo, ento o casamento dessa cadeia de caracteres e da cadeia de caracteres
de entrada se d de forma ligeiramente diferente. O processamento de um caracter usual na
cadeia de caracteres de formatao de uma funo scanf depende se o caracter um branco
ou no. Ou seja,

um ou mais brancos consecutivos na cadeia de caracteres de formatao faz com que a


funo scanf leia repetidamente brancos na entrada at que um caracter no branco seja
lido; o nmero de brancos na cadeia de caracteres de formatao irrelevante;

FACOM UFMS
18.3 C ARACTERES 108

outros caracteres na cadeia de caracteres de formatao, no brancos, faz com que a fun-
o scanf o compare com o prximo caracter da entrada; se os dois caracteres so idn-
ticos, a funo scanf descarta o caracter de entrada e continua o processamento da
cadeia de caracteres de formatao; seno, a funo scanf aborta sua execuo no pro-
cessando o restante da cadeia de caracteres de formatao e os caracteres de entrada.

Por exemplo, suponha que temos uma leitura como abaixo:

scanf("%d/%d", &dia, &mes);

Suponha que a entrada seja


2/ 33

ento, a funo scanf salta o primeiro espao, associa %d com 2 , casa / e / , salta o espao
e associa %d com 33 . Por outro lado, se a entrada
2 / 33

a funo scanf salta o espao, associa %d com 2 , tenta casar o caracter / da cadeia de
caracteres de formatao com um espao da entrada e como esses caracteres no casam, a
funo termina e o restante / 33 da entrada permanecer armazenado na memria at que
uma prxima chamada funo scanf seja realizada.
Por convenincia e simplicidade, muitas vezes preferimos usar funes mais simples de
entrada e sada de caracteres. As funes getchar e putchar so funes da biblioteca
padro de entrada e sada da linguagem C e so usadas exclusivamente com caracteres. A
funo getchar l um caracter da entrada e devolve esse caracter. A funo putchar toma
um caracter como parmetro e o exibe na sada. Dessa forma, se c uma varivel do tipo
char as linhas a seguir:

scanf("%c", &c);
printf("%c", c);

so equivalentes s linhas abaixo:

c = getchar();
putchar(c);

Observe que a funo getchar no tem parmetros de entrada, mas, por ser uma funo,
carrega os parnteses como sufixo. Alm disso, essa funo devolve um caracter, que pode
ser usado em uma expresso. Nas linhas acima, getchar usada em uma atribuio. Por
outro lado, a funo putchar tem como parmetro uma expresso do tipo caracter que, aps
avaliada, seu valor ser exibido na sada. A funo putchar no devolve qualquer valor.

FACOM UFMS
18.4 C ONVERSO DE TIPOS 109

18.4 Converso de tipos

As arquiteturas dos computadores em geral restringem as operaes em expresses para


que sejam realizadas apenas com operandos de mesmo comprimento, isto , mesmo nmero
de bytes. A linguagem C, por outro lado, menos restritiva nesse sentido e permite que va-
lores de tipos bsicos sejam misturados em expresses. Isso acarreta um maior trabalho para
o compilador, j que necessria a converso de alguns operandos da expresso. Como essas
converses so realizadas pelo compilador, elas so chamadas de converses implcitas. A
linguagem C tambm permite que o programador faa suas prprias converses, chamadas
de converses explcitas, usando operadores de converso de tipo, como vimos na aula 14 e
revisaremos adiante.
Converses implcitas so realizadas nas seguintes situaes:

quando os operandos em uma expresso no so do mesmo tipo;

quando o tipo do valor resultante da avaliao de uma expresso ao lado direito de um


comando de atribuio no compatvel com o tipo da varivel do lado esquerdo de um
comando de atribuio;

quando o tipo de um argumento em uma chamada de uma funo no compatvel com


o tipo do parmetro correspondente;

quando o tipo do valor resultante de uma expresso de devoluo de uma funo, junto
da palavra reservada return , no compatvel com o tipo da funo.

Nesta aula, veremos os dois primeiros casos, sendo que os dois ltimos sero vistos quando
tratarmos de funes, a partir da aula 33.
As converses usuais em expresses so aplicadas aos operandos de todos os operadores
binrios aritmticos, relacionais e lgicos. A linguagem C faz a converso dos operandos de
maneira que os fiquem mais seguramente acomodados. Isso significa que o menor nmero
de bits que mais seguramente acomodar os operandos ser usado nessa converso, se uma
converso for de fato necessria. As regras de converso de tipos podem ento ser divididas
em dois tipos:

o tipo de pelo menos um dos operandos de ponto flutuante: se um dos operandos do tipo
long double , ento o outro operando ser convertido para o tipo long double . Caso
contrrio, se um dos operandos do tipo double , ento o outro operando ser con-
vertido para o tipo double . Seno, um dos operandos do tipo float e o outro ser
convertido para esse mesmo tipo;

nenhum dos operandos do tipo ponto flutuante: primeiro, se qualquer um dos operandos
do tipo char ou short int , ser convertido para o tipo int ; depois, se um dos ope-
randos do tipo unsigned long int , ento o outro operando ser convertido para o
tipo unsigned long int . Caso contrrio, se um dos operandos do tipo long int ,
ento o outro operando ser convertido para o tipo long int . Ainda, se um dos ope-
randos do tipo unsigned int , ento o outro operando ser convertido para o tipo
unsigned int . Por fim, um dos operandos do tipo int e o outro ser convertido
para esse mesmo tipo.

FACOM UFMS
18.4 C ONVERSO DE TIPOS 110

importante alertar para um caso em que uma operao envolve um dos operandos com
sinal e o outro sem sinal. Pelas regras acima, o operando com sinal convertido para um ope-
rando sem sinal. Um exemplo simples dessa regra comentado a seguir. Se um dos operandos
do tipo int e contm um nmero negativo e o outro operando do tipo unsigned int
e contm um nmero positivo, ento uma operao envolvendo esses dois operandos deve
ser realizada cuidadosamente. Nesse caso, o valor do tipo int ser promovido para o tipo
unsigned int , mas a converso ser feita usando a frmula k + 232 , onde k o valor com
sinal. Esse problema uma das causas de erro mais difceis de depurar. Veja o programa 18.3.

Programa 18.3: Dificuldade na converso de valores em expresses.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i;
5 unsigned int j;
6 i = -1;
7 j = 1;
8 printf("i = %+d i = %u\n", i, (unsigned int) i);
9 printf("j = %+d j = %u\n", (int) j, j);
10 if (i < j)
11 printf("i < j\n");
12 else
13 printf("j < i\n");
14 return 0;
15 }

A sada do programa 18.3 mostrada a seguir.

prompt$ ./a.out
i = -1 i = 4294967295
j = +1 j = 1
j < i

As converses implcitas da linguagem C so bastante convenientes, como pudemos perce-


ber at aqui. No entanto, muitas vezes necessrio que o programador tenha maior controle
sobre as converses a serem realizadas, podendo assim realizar converses explcitas. Dessa
forma, a linguagem C possui operadores de converso de tipo, ou casts, que j tomamos con-
tato especialmente na aula 14. Um operador de converso de tipo um operador unrio que
tem o seguinte formato geral:

(nome-do-tipo) expresso

FACOM UFMS
18.5 T IPOS DE DADOS DEFINIDOS PELO PROGRAMADOR 111

onde nome-do-tipo o nome de qualquer tipo bsico de dados que ser usado para converter
o valor da expresso , aps sua avaliao.
Por exemplo, se f e frac so variveis do tipo float , ento o trecho de cdigo a seguir:

f = 123.4567f;
frac = f - (int) f;

far com que a varivel frac contenha a parte fracionria da varivel f .

18.5 Tipos de dados definidos pelo programador

A linguagem C possui poucos tipos de dados bsicos, mas permite que um programador
possa definir seus prprios tipos de dados. Para tanto, devemos usar a palavra reservada
typedef no seguinte formato geral:

typedef tipo-bsico tipo-novo;

onde tipo-bsico o nome de um dos tipos de dados bsicos da linguagem C e tipo-novo


o nome do novo tipo de dados definido pelo programador. Um exemplo de definio e uso
de um tipo apresentado a seguir:

typedef int Logic;


Logic primo, crescente;

Na primeira linha, typedef uma palavra reservada da linguagem C, int o tipo bsico
de dados para nmeros inteiros e Logic o nome do novo tipo de dados criado pelo progra-
mador. Na segunda linha, ocorre a declarao de duas variveis do tipo Logic : a varivel
primo e a varivel crescente . O nome Logic desse novo tipo foi descrito com a primeira
letra maiscula, mas isso no uma obrigatoriedade. O novo tipo Logic criado a partir de
typedef faz com que o compilador o adicione a sua lista de tipos conhecidos. Isso significa
que podemos, a partir de ento, declarar variveis com esse tipo novo, us-lo como operador
conversor de tipo, etc. O compilador trata Logic como um sinnimo para int . Dessa forma,
as variveis primo e crescente so, na verdade, variveis do tipo int .
Definies de tipos podem fazer com que o cdigo seja mais compreensvel, mais fcil de
modificar e mais fcil de transportar de uma arquitetura para outra.

FACOM UFMS
18.6 O PERADOR sizeof 112

18.6 Operador sizeof

O operador unrio sizeof permite que se determine quanto de memria necessrio para
armazenar valores de um tipo qualquer. A expresso abaixo:

sizeof (nome-do-tipo)

determina um valor que um inteiro sem sinal representando o nmero de bytes necessrios
para armazenar um valor do tipo dado por nome-do-tipo .
Veja o programa 18.4, que mostra as quantidades de bytes necessrios para armazenamento
de valores dos tipos bsicos da linguagem C.

Programa 18.4: Exemplo do uso do operador unrio sizeof.

1 #include <stdio.h>
2 int main(void)
3 {
4 printf("Caracter: %d\n", sizeof(char));
5 printf("Inteiros:\n");
6 printf(" short: %d\n", sizeof(short int));
7 printf(" int: %d\n", sizeof(int));
8 printf(" long int: %d\n", sizeof(long int));
9 printf("Nmeros de ponto flutuante:\n");
10 printf(" float: %d\n", sizeof(float));
11 printf(" double: %d\n", sizeof(double));
12 printf(" long double: %d\n", sizeof(long double));
13 return 0;
14 }

A sada do programa 18.4, executado em uma mquina de 32 bits, apresentada abaixo:

Caracter: 1
Inteiros:
short: 2
int: 4
long int: 4
Nmeros de ponto flutuante:
float: 4
double: 8
long double: 12

FACOM UFMS
18.7 E XERCCIOS 113

18.7 Exerccios

Exerccios para treinar os conceitos aprendidos nesta aula. Faa muitos testes com suas
entradas e sadas.

18.1 Dadas n triplas compostas por um smbolo de operao aritmtica (+, , ou /) e dois
nmeros reais, calcule o resultado ao efetuar a operao indicada para os dois nmeros.
Faremos esse exerccio usando a estrutura condicional switch , que compara uma ex-
presso com uma seqncia de valores. Essa estrutura tem o seguinte formato:

switch (expresso) {
case constante:
seqncia de comandos
break;
case constante:
seqncia de comandos
break;
...
default:
seqncia de comandos
break;
}

Veja o programa 18.5. Observe especialmente a leitura dos dados.


18.2 Um matemtico italiano da idade mdia conseguiu modelar o ritmo de crescimento da
populao de coelhos atravs de uma seqncia de nmeros naturais que passou a ser co-
nhecida como seqncia de Fibonacci. A seqncia de Fibonacci descrita pela seguinte
frmula de recorrncia:

F1 = 1
F2 = 1

Fi = Fi1 + Fi2 , para i > 3.

Escreva um programa que dado n > 1 calcule e exiba Fn .


18.3 Os babilnios descreveram a mais de 4 mil anos um mtodo para calcular a raiz quadrada
de um nmero. Esse mtodo ficou posteriormente conhecido como mtodo de Newton.
Dado um nmero x, o mtodo parte de um chute inicial y para o valor da raiz quadrada
de x e sucessivamente encontra aproximaes desse valor calculando a mdia aritmtica
de y e de x/y. O exemplo a seguir mostra o mtodo em funcionamento para o clculo da
raiz quadrada de 3, com chute inicial 1:

x y x/y (y + x/y)/2
3 1 3 2
3 2 1.5 1.75
3 1.75 1.714286 1.732143
3 1.732143 1.731959 1.732051
3 1.732051 1.732051 1.732051

FACOM UFMS
18.7 E XERCCIOS 114

Programa 18.5: Soluo do exerccio 18.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 char operador;
5 int n;
6 float op1, op2, result;
7 printf("Informe n: ");
8 scanf("%d", &n);
9 for ( ; n > 0; n--) {
10 printf("Informe a expresso (ex.: 5.3 * 3.1): ");
11 scanf("%f %c%f", &op1, &operador, &op2);
12 switch (operador) {
13 case +:
14 result = op1 + op2;
15 break;
16 case -:
17 result = op1 - op2;
18 break;
19 case *:
20 result = op1 * op2;
21 break;
22 case /:
23 result = op1 / op2;
24 break;
25 default:
26 break;
27 }
28 printf("%f %c %f = %f\n", op1, operador, op2, result);
29 }
30 return 0;
31 }

Escreva um programa que receba um nmero real positivo x e um nmero real e calcule
a raiz quadrada de x usando o mtodo de Newton, at que o valor absoluto da diferena
entre dois valores consecutivos de y seja menor que . Mostre tambm na sada a quanti-
dade de passos realizados para obteno da raiz de x.

FACOM UFMS
A ULA 19

V ETORES

At o momento, estudamos os tipos de dados bsicos ou elementares da linguagem C de


programao: char , int , float e double . Os tipos de dados bsicos se caracterizam pelo
fato que seus valores no podem ser decompostos. Por outro lado, se os valores de um tipo
de dados podem ser decompostos ou subdivididos em valores mais simples, ento o tipo de
dados chamado de complexo, composto ou estruturado. A organizao desses valores e
as relaes estabelecidas entre eles determinam o que conhecemos como estrutura de dados.
Estudaremos algumas dessas estruturas de dados em nossas aulas futuras, como por exemplo
as listas lineares, pilhas e filas.
Nesta aula iniciaremos o estudo sobre variveis compostas, partindo de uma varivel co-
nhecida como varivel composta homognea unidimensional ou simplesmente vetor. Carac-
tersticas especficas da linguagem C no tratamento de vetores tambm sero abordadas.

19.1 Motivao

Como um exemplo da necessidade do uso da estrutura de dados conhecida como vetor,


considere o seguinte problema:

Dadas cinco notas de uma prova dos(as) estudantes de uma disciplina, calcular a
mdia das notas da prova e a quantidade de estudantes que obtiveram nota maior
que a mdia e a quantidade de estudantes que obtiveram nota menor que a mdia.

Uma tentativa natural e uma idia inicial para solucionar esse problema consiste no uso
de uma estrutura de repetio para acumular o valor das cinco notas informadas e o clculo
posterior da mdia destas cinco notas. Esses passos resolvem o problema inicial do clculo da
mdia das provas dos estudantes. Mas e a computao das quantidades de alunos que obti-
veram nota maior e menor que a mdia j computada? Observe que aps lidas as cinco notas
e processadas para o clculo da mdia em uma estrutura de repetio, a tarefa de encontrar
as quantidades de estudantes que obtiveram nota superior e inferior mdia impossvel, j
que as notas dos estudantes no esto mais disponveis na memria. Isto , a menos que o(a)
programador(a) pea ao usurio para informar as notas dos(as) estudantes novamente, no h
como computar essas quantidades.
Apesar disso, ainda podemos resolver o problema usando uma estrutura seqencial em
que todas as cinco notas informadas pelo usurio ficam armazenadas na memria e assim po-
demos posteriormente computar as quantidades solicitadas consultando estes valores. Veja o
programa 19.1.

115
19.1 M OTIVAO 116

Programa 19.1: Um programa para resolver o problema da seo 19.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int menor, maior;
5 float nota1, nota2, nota3, nota4, nota5, media;
6 printf("Informe as notas dos alunos: ");
7 scanf("%f%f%f%f%f%f", &nota1, &nota2, &nota3, &nota4, &nota5);
8 media = (nota1 + nota2 + nota3 + nota4 + nota5) / 5;
9 printf("\nMedia das provas: %2.2f\n", media);
10 menor = 0;
11 if (nota1 < media)
12 menor++;
13 if (nota2 < media)
14 menor++;
15 if (nota3 < media)
16 menor++;
17 if (nota4 < media)
18 menor++;
19 if (nota5 < media)
20 menor++;
21 maior = 0;
22 if (nota1 > media)
23 maior++;
24 if (nota2 > media)
25 maior++;
26 if (nota3 > media)
27 maior++;
28 if (nota4 > media)
29 maior++;
30 if (nota5 > media)
31 maior++;
32 printf("\nQuantidade de estudantes com nota inferior mdia: %d\n", menor);
33 printf("\nQuantidade de estudantes com nota superior mdia: %d\n", maior);
34 return 0;
35 }

A soluo que apresentamos no programa 19.1 de fato uma soluo para o problema. Isto
, dadas como entrada as cinco notas dos(as) estudantes em uma prova, o programa computa
de forma correta as sadas que esperamos, ou seja, as quantidades de estudantes com nota in-
ferior e superior mdia da prova. No entanto, o que aconteceria se a sala de aula tivesse mais
estudantes, como por exemplo 100? Ou 1.000 estudantes? Certamente, a estrutura seqencial
no seria apropriada para resolver esse problema, j que o programador teria de digitar cente-
nas ou milhares de linhas repetitivas, incorrendo inclusive na possibilidade de propagao de
erros e na dificuldade de encontr-los. E assim como esse, outros problemas no podem ser
resolvidos sem uma extenso na maneira de armazenar e manipular as entradas de dados.

FACOM UFMS
19.2 D EFINIO 117

19.2 Definio

Uma varivel composta homognea unidimensional, ou simplesmente um vetor, uma


estrutura de armazenamento de dados que se dispe de forma linear na memria e usada
para armazenar valores de um mesmo tipo. Um vetor ento uma lista de clulas na memria
de tamanho fixo cujos contedos so do mesmo tipo bsico. Cada uma dessas clulas arma-
zena um, e apenas um, valor. Cada clula do vetor tem um endereo ou ndice atravs do
qual podemos referenci-la. O termo varivel composta homognea unidimensional bem
explcito e significa que temos uma: (i) varivel, cujos valores podem ser modificados durante
a execuo de um programa; (ii) composta, j que h um conjunto de valores armazenado
na varivel; (iii) homognea, pois os valores armazenados na varivel composta so todos de
um mesmo tipo bsico; e (iv) unidimensional, porque a estrutura de armazenamento na vari-
vel composta homognea linear. No entanto, pela facilidade, usamos o termo vetor com o
mesmo significado.
A forma geral de declarao de um vetor dada a seguir:

tipo identificador[tamanho];

onde:

tipo um tipo de dados conhecido ou definido pelo(a) programador(a);

identificador o nome do vetor, fornecido pelo(a) programador(a); e

tamanho a quantidade de clulas a serem disponibilizadas para uso no vetor.

Por exemplo, a declarao a seguir

float nota[100];

faz com que 100 clulas contguas de memria sejam reservadas, cada uma delas podendo
armazenar nmeros de ponto flutuante do tipo float . A referncia a cada uma dessas clulas
realizada pelo identificador do vetor nota e por um ndice. Na figura 19.1 mostramos o
efeito da declarao do vetor nota na memria de um computador.

0 1 2 98 99

memria

nota

Figura 19.1: Vetor com 100 posies.

FACOM UFMS
19.3 I NICIALIZAO 118

importante observar que, na linguagem C, a primeira clula de um vetor tem ndice 0,


a segunda clula tem ndice 1, a terceira tem ndice 2 e assim por diante. Para referenciar
o contedo da clula 0 do vetor nota , devemos usar o identificador do vetor e o ndice 0,
envolvido por colchetes, isto , nota[0] . Assim, o uso de nota[35] em uma expresso
qualquer referencia o trigsimo sexto elemento do vetor nota . Podemos, tambm, usar uma
varivel do tipo inteiro como ndice de um vetor ou ainda uma expresso aritmtica do tipo
inteiro. Por exemplo, nota[i] acessa a (i + 1)-sima clula do vetor nota , supondo que a
varivel i do tipo inteiro. Ou ainda, podemos fazer nota[2*i+j] para acessar a posio
de ndice 2i + j do vetor nota , supondo que i e j so variveis do tipo inteiro contm
valores compatveis de tal forma que o resultado da expresso aritmtica 2i+j seja um valor no
intervalo de ndices vlido para o vetor nota . O compilador da linguagem C no verifica de
antemo se os limites dos ndices de um vetor esto corretos, trabalho que deve ser realizado
pelo(a) programador(a).
Um erro comum, aparentemente inocente, mas que pode ter causas desastrosas, mostrado
no trecho de cdigo abaixo:

int A[10], i;
for (i = 1; i <= 10; i++)
A[i] = 0;

Alguns compiladores podem fazer com que a estrutura de repetio for acima seja execu-
tada infinitamente. Isso porque quando a varivel i atinge o valor 10 , o programa armazena
o valor 0 em A[10] . Observe, no entanto, que A[10] no existe e assim 0 armazenado
no compartimento de memria que sucede A[9] . Se a varivel i ocorre na memria logo
aps A[9] , como bem provvel, ento i receber o valor 0 fazendo com que o lao inicie
novamente.

19.3 Inicializao

Podemos atribuir valores iniciais a quaisquer variveis de qualquer tipo bsico no momento
de suas respectivas declaraes. At o momento, no havamos usado declaraes e inicializa-
es em conjunto. O trecho de cdigo abaixo mostra declaraes e inicializaes simultneas
de variveis de tipos bsicos:

char c = a;
int num, soma = 0;
float produto = 1.0, resultado;

No exemplo acima, as variveis c , soma e produto so inicializadas no momento da


declarao, enquanto que num e resultado so apenas declaradas.

FACOM UFMS
19.3 I NICIALIZAO 119

Apesar de, por alguns motivos, no termos usado declaraes e inicializaes simultneas
com variveis de tipos bsicos, essa caracterstica da linguagem C muito favorvel quando
tratamos de variveis compostas. Do mesmo modo, podemos atribuir um valor inicial a um
vetor no momento de sua declarao. As regras para declarao e atribuio simultneas para
vetores so um pouco mais complicadas, mas veremos as mais simples no momento. A forma
mais comum de se fazer a inicializao de um vetor atravs de uma lista de expresses cons-
tantes envolvidas por chaves e separadas por vrgulas, como no exemplo abaixo:

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

Se o inicializador tem menos elementos que a capacidade do vetor, os elementos restantes


so inicializados com o valor 0 (zero), como abaixo:

int A[10] = {1, 2, 3, 4};

O resultado na memria equivalente a termos realizado a inicializao abaixo:

int A[10] = {1, 2, 3, 4, 0, 0, 0, 0, 0, 0};

No entanto, no permitido que o inicializador tenha mais elementos que a quantidade de


compartimentos do vetor.
Podemos ento facilmente inicializar um vetor todo com zeros da seguinte maneira:

int A[10] = {0};

Se um inicializador est presente em conjunto com a declarao de um vetor, ento o seu


tamanho pode ser omitido, como mostrado a seguir:

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

O compilador ento interpreta que o tamanho do vetor A determinado pela quantidade


de elementos do seu inicializador. Isso significa que, aps a execuo da linha do exemplo
de cdigo acima, o vetor A tem 10 compartimentos de memria, do mesmo modo como se
tivssemos especificado isso explicitamente, como no primeiro exemplo.

FACOM UFMS
19.4 E XEMPLO COM VETORES 120

19.4 Exemplo com vetores

Vamos agora resolver o problema da seo 19.1 do clculo das mdias individuais de estu-
dantes e da verificao da quantidade de estudantes com mdia inferior e tambm superior
mdia da classe. Solucionaremos esse problema construindo um programa que usa vetores e
veremos como nossa soluo se mostra muito mais simples, mais eficiente e facilmente exten-
svel para qualquer quantidade de estudantes que se queira. Vejamos ento o programa 19.2 a
seguir.

Programa 19.2: Soluo do problema proposto na seo 19.1 usando um vetor.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, menor, maior;
5 float nota[5], soma, media;
6 for (i = 0; i < 5; i++) {
7 printf("Informe a nota do(a) estudante %d: ", i+1);
8 scanf("%f", &nota[i]);
9 }
10 soma = 0.0;
11 for (i = 0; i < 5; i++)
12 soma = soma + nota[i];
13 media = soma / 5;
14 menor = 0;
15 maior = 0;
16 for (i = 0; i < 5; i++) {
17 if (nota[i] < media)
18 menor++;
19 if (nota[i] > media)
20 maior++;
21 }
22 printf("\nMedia das provas: %2.2f\n", media);
23 printf("Quantidade de alunos com nota inferior mdia: %d\n", menor);
24 printf("Quantidade de alunos com nota superior mdia: %d\n", maior);
25 return 0;
26 }

Observe que o programa 19.2 mais conciso e de mais fcil compreenso que o pro-
grama 19.1. O programa tambm mais facilmente extensvel, no sentido que muito poucas
modificaes so necessrias para que esse programa possa solucionar problemas semelhan-
tes com outras quantidades de estudantes. Isto , se a turma tem 5, 10 ou 100 estudantes, ou
ainda um nmero desconhecido n que ser informado pelo usurio durante a execuo do pro-
grama, uma pequena quantidade de esforo ser necessria para alterao de poucas linhas do
programa.

FACOM UFMS
19.5 M ACROS PARA CONSTANTES 121

19.5 Macros para constantes

Em geral, quando um programa contm constantes, uma boa idia dar nomes a essas
constantes. Podemos atribuir um nome ou identificador a uma constante usando a definio
de uma macro. Uma macro definida atravs da diretiva de pr-processador #define e tem
o seguinte formato geral:

#define identificador constante

onde #define uma diretiva do pr-processador da linguagem C e identificador um


nome associado constante que vem logo a seguir. Observe que, por ser uma diretiva do
pr-processador, a linha contendo uma definio de uma macro no finalizada por ; . Em ge-
ral, a definio de uma macro ocorre logo no incio do programa, aps as diretivas #include
para incluso de cabealhos de bibliotecas de funes. Alm disso, o identificador de uma
macro , preferencial mas no obrigatoriamente, descrito em letras maisculas.
Exemplos de macros so apresentados a seguir:

#define CARAC a
#define NUMERADOR 4
#define MIN -10000
#define TAXA 0.01567

Quando um programa compilado, o pr-processador troca cada macro definida no c-


digo pelo valor que ela representa. Depois disso, um segundo passo de compilao execu-
tado. O programa 14.5 pode ser reescrito com o uso de uma macro, como podemos ver no
programa 19.3, onde uma macro com identificador PI definida e usada no cdigo.

Programa 19.3: Clculo da rea do crculo.

1 #include <stdio.h>
2 #define PI 3.141592f
3 int main(void)
4 {
5 double raio, area;
6 printf("Digite o valor do raio: ");
7 scanf("%lf", &raio);
8 area = PI * raio * raio;
9 printf("A rea do crculo de raio %f %f\n", raio, area);
10 return 0;
11 }

FACOM UFMS
19.5 M ACROS PARA CONSTANTES 122

O uso de macros com vetores bastante til porque, como j vimos, um vetor faz alo-
cao esttica da memria, o que significa que em sua declarao ocorre uma reserva prvia
de um nmero fixo de compartimentos de memria. Por ser uma alocao esttica, no h
possibilidade de aumento ou diminuio dessa quantidade aps a execuo da linha de c-
digo contendo a declarao do vetor. O programa 19.2 pode ser ainda modificado como no
programa 19.4 com a incluso de uma macro que indica a quantidade de notas a serem proces-
sadas.

Programa 19.4: Soluo do problema proposto na seo 19.1 usando uma macro e um vetor.

1 #include <stdio.h>
2 #define MAX 5
3 int main(void)
4 {
5 int i, menor, maior;
6 float nota[MAX], soma, media;
7 for (i = 0; i < MAX; i++) {
8 printf("Informe a nota do(a) estudante %d: ", i+1);
9 scanf("%f", &nota[i]);
10 }
11 soma = 0.0;
12 for (i = 0; i < MAX; i++)
13 soma = soma + nota[i];
14 media = soma / MAX;
15 menor = 0;
16 maior = 0;
17 for (i = 0; i < MAX; i++) {
18 if (nota[i] < media)
19 menor++;
20 if (nota[i] > media)
21 maior++;
22 }
23 printf("\nMedia das provas: %2.2f\n", media);
24 printf("Quantidade de estudantes com nota inferior mdia: %d\n", menor);
25 printf("Quantidade de estudantes com nota superior mdia: %d\n", maior);
26 return 0;
27 }

A macro MAX usada quatro vezes no programa 19.4: na declarao do vetor nota , nas
expresses relacionais das duas estruturas de repetio for e no clculo da mdia. A van-
tagem de se usar uma macro que, caso seja necessrio modificar a quantidade de notas do
programa por novas exigncias do usurio, isso pode ser feito rpida e facilmente em uma
nica linha do cdigo, onde ocorre a definio da macro.
Voltaremos a discutir mais sobre macros, sobre a diretiva #define e tambm sobre outras
diretivas do pr-processador na aula 46.

FACOM UFMS
19.5 M ACROS PARA CONSTANTES 123

Exerccios

19.1 Dada uma seqncia de n nmeros inteiros, com 1 6 n 6 100, imprimi-la em ordem
inversa de leitura.

Programa 19.5: Programa para solucionar o exerccio 19.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int i, n, A[MAX];
6 printf("Informe n: ");
7 scanf("%d", &n);
8 for (i = 0; i < n; i++) {
9 printf("Informe o nmero %d: ", i+1);
10 scanf("%d", &A[i]);
11 }
12 printf("Nmeros na ordem inversa da leitura:\n");
13 for (i = n-1; i >= 0; i--)
14 printf("%d ", A[i]);
15 printf("\n");
16 return 0;
17 }

19.2 Uma prova consta de 30 questes, cada uma com cinco alternativas identificadas pelas
letras A, B, C, D e E. Dado o carto gabarito da prova e o carto de respostas de n estu-
dantes, com 1 6 n 6 100, computar o nmero de acertos de cada um dos estudantes.

19.3 Tentando descobrir se um dado era viciado, um dono de cassino o lanou n vezes. Dados
os n resultados dos lanamentos, determinar o nmero de ocorrncias de cada face.

19.4 Um jogador viciado de cassino deseja fazer um levantamento estatstico simples sobre
uma roleta. Para isso, ele fez n lanamentos nesta roleta. Sabendo que uma roleta contm
37 nmeros (de 0 a 36), calcular a freqncia de cada nmero desta roleta nos n lana-
mentos realizados.

FACOM UFMS
A ULA 20

I NVARIANTES

Algoritmos e programas podem conter um ou mais processos iterativos, que so contro-


lados por estruturas de repetio. Na linguagem C, como sabemos, as estruturas de repetio
so while , for e do-while . Processos iterativos podem ser documentados com invariantes,
que nos ajudam a entender os motivos que o algoritmo e/ou programa est correto. Nesta aula
definiremos invariantes, mostraremos exemplos de invariantes e, atravs deles, provaremos
que os programas apresentados esto corretos.

20.1 Definio

Um invariante de um processo iterativo uma relao entre os valores das variveis que
vale no incio de cada iterao desse processo. Os invariantes explicam o funcionamento do
processo iterativo e permitem provar por induo que ele tem o efeito desejado.
Devemos provar trs elementos sobre um invariante de um processo iterativo:

Inicializao: verdadeiro antes da primeira iterao da estrutura de repetio;


Manuteno: se verdadeiro antes do incio de uma iterao da estrutura de repetio, ento
permanece verdadeiro antes da prxima iterao;
Trmino: quando a estrutura de repetio termina, o invariante nos d uma propriedade til
que nos ajuda a mostrar que o algoritmo ou programa est correto.

Quando as duas primeiras propriedades so satisfeitas, o invariante verdadeiro antes de


toda iterao da estrutura de repetio. Como usamos invariantes para mostrar a corretude de
um algoritmo e/ou programa, a terceira propriedade a mais importante, aquela que permite
mostrar de fato a sua corretude.
Dessa forma, os invariantes explicam o funcionamento dos processos iterativos e permitem
provar, por induo, que esses processos tm o efeito desejado.

20.2 Exemplos

Nesta seo veremos exemplos do uso dos invariantes para mostrar a corretude de progra-
mas. O primeiro exemplo bem simples e o programa contm apenas variveis do tipo inteiro
e, obviamente, uma estrutura de repetio. O segundo exemplo um programa que usa um
vetor para soluo do problema.

124
20.2 E XEMPLOS 125

Considere ento o programa 20.1, que realiza a soma de n nmeros inteiros fornecidos pelo
usurio. O programa 20.1 simples e no usa um vetor para solucionar esse problema.

Programa 20.1: Soma n inteiros fornecidos pelo usurio.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, i, num, soma;
5 printf("Informe n: ");
6 scanf("%d", &n);
7 soma = 0;
8 for (i = 1; i <= n; i++) {
9 /* a varivel soma contm a soma dos primeiros
10 (i - 1) nmeros fornecidos pelo usurio */
11 printf("Informe um nmero: ");
12 scanf("%d", &num);
13 soma = soma + num;
14 }
15 printf("Soma dos %d nmeros %d\n", n, soma);
16 return 0;
17 }

importante destacar o comentrio nas linhas 9 e 10, as primeiras do bloco de execuo da


estrutura de repetio for do programa 20.1: este o invariante desse processo iterativo. E
como Feofiloff destaca em [4], o enunciado de um invariante , provavelmente, o nico tipo de
comentrio que vale a pena inserir no corpo de um algoritmo, programa ou funo.
Ento, podemos provar a seguinte proposio.
Proposio 20.1. O programa 20.1 realiza a soma de n > 0 nmeros inteiros fornecidos pelo usurio.

Demonstrao.

Por convenincia na demonstrao usaremos o modo matemtico para expressar as


variveis do programa: n ser denotada por n, i por i, soma por soma e num
por num. Quando nos referirmos ao i-simo nmero inteiro fornecido pelo usurio
e armazenado na varivel num , usaremos a notao numi .
Provar que o programa 20.1 est correto significa mostrar que para qualquer va-
lor de n e qualquer seqncia de n nmeros, a varivel soma conter, ao final do
processo iterativo, o valor
Xn
soma = numi .
i=1

Vamos mostrar que o invariante vale no incio da primeira iterao do processo


iterativo. Como soma contm o valor 0 (zero) e i contm 1, verdade que a varivel
soma contm a soma dos i 1 primeiros nmeros fornecidos pelo usurio.

FACOM UFMS
20.2 E XEMPLOS 126

Suponha agora que o invariante valha no incio da i-sima iterao, com 1 < i < n.
Vamos mostrar que o invariante vale no incio da ltima iterao, quando i contm
o valor n. Por hiptese de induo, a varivel soma contm o valor
n1
X
= numi .
i=1

Ento, no decorrer dessa n-sima iterao, o usurio deve informar um nmero que
ser armazenado na varivel num e, ento, a varivel soma conter o valor

soma = + numn
n1
!
X
= numi + numn
i=1
n
X
= numi .
i=1

Portanto, isso mostra que o programa 20.1 de fato realiza a soma dos n nmeros
inteiros fornecidos pelo usurio.

O prximo exemplo dado da seguinte forma: dado um vetor com n nmeros inteiros
fornecidos pelo usurio, encontrar o valor mximo armazenado nesse vetor. O programa 20.2
bem simples e se prope a solucionar esse problema.

Programa 20.2: Encontra o maior valor em um vetor com n nmeros inteiros fornecidos pelo
usurio.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, vet[100], i, max;
5 printf("Informe n: ");
6 scanf("%d", &n);
7 for (i = 0; i < n; i++) {
8 printf("vet[%d]=", i);
9 scanf("%d", &vet[i]);
10 }
11 max = vet[0];
12 for (i = 1; i < n; i++) {
13 /* max um elemento mximo em vet[0..i-1] */
14 if (vet[i] > max)
15 max = vet[i];
16 }
17 printf("O elemento de mximo valor no vetor %d\n", max);
18 return 0;
19 }

FACOM UFMS
20.2 E XEMPLOS 127

Proposio 20.2. O programa 20.2 encontra o elemento mximo de um conjunto de n nmeros forne-
cidos pelo usurio.

Demonstrao.

Novamente, por convenincia na demonstrao usaremos o modo matemtico para


expressar as variveis do programa: n ser denotada por n, i por i, vet por vet
e max por max.
Provar que o programa 20.2 est correto significa mostrar que para qualquer valor
de n e qualquer seqncia de n nmeros fornecidos pelo usurio e armazenados
em um vetor vet, a varivel max conter, ao final do processo iterativo, o valor do
elemento mximo em vet[0..n 1].
Vamos mostrar que o invariante vale no incio da primeira iterao do processo
iterativo. Como max contm o valor armazenado em vet[0] e, em seguida, a varivel
i inicializada com o valor 1, ento verdade que a varivel max contm o elemento
mximo em vet[0..i 1].
Suponha agora que o invariante valha no incio da i-sima iterao, com 1 < i < n.
Vamos mostrar que o invariante vale no incio da ltima iterao, quando i contm o
valor n 1. Por hiptese de induo, no incio desta iterao a varivel max contm
o valor o elemento mximo de vet[0..n 2]. Ento, no decorrer dessa iterao, o
valor vet[n 1] comparado com max e dois casos devem ser avaliados:

(i) vet[n 1] > max


Isso significa que o valor vet[n 1] maior que qualquer valor armazenado
em vet[0..n 2]. Assim, na linha 15 a varivel max atualizada com vet[n 1]
e portanto a varivel max conter, ao final desta ltima iterao, o elemento
mximo da seqncia em vet[0..n 1].
(ii) vet[n 1] 6 max
Isso significa que existe pelo menos um valor em vet[0..n 2] que maior ou
igual a vet[n 1]. Por hiptese de induo, esse valor est armazenado em
max. Assim, ao final desta ltima iterao, a varivel max conter o elemento
mximo da seqncia em vet[0..n 1].

Portanto, isso mostra que o programa 20.2 de fato encontra o elemento mximo em
uma seqncia de n nmeros inteiros armazenados em um vetor.

Exerccios

20.1 O programa 20.3 recebe um nmero inteiro n > 0, uma seqncia de n nmeros inteiros,
um nmero inteiro x e verifica se x pertence seqncia de nmeros.
Mostre que o programa 20.3 est correto.

20.2 Dado n > 0 e uma seqncia de n nmeros inteiros em um vetor, escreva um programa
que inverta os elementos dessa seqncia armazenando-os no mesmo vetor.

FACOM UFMS
20.2 E XEMPLOS 128

Programa 20.3: Verifica se x pertence uma seqncia de n nmeros.

1 #include <stdio.h>
2 int main(void)
3 {
4 int n, A[100], i, x;
5 printf("Informe um valor para n: ");
6 scanf("%d", &n);
7 for (i = 0; i < n; i++) {
8 printf("A[%d]=", i);
9 scanf("%d", &A[i]);
10 }
11 printf("Informe x: ");
12 scanf("%d", &x);
13 i = 0;
14 while (i < n && A[i] != x) {
15 /* x no pertence A[0..i] */
16 i++;
17 }
18 if (i < n)
19 printf("%d o %d-simo elemento do vetor\n", x, i);
20 else
21 printf("%d no se encontra no vetor\n", x);
22 return 0;
23 }

20.3 O piso de um nmero x o nico inteiro i tal que i 6 x < i+1. O piso de x denotado por
x. Escreva um programa que receba um nmero inteiro positivo n e compute log2 n.
Segue uma amostra de valores:

n 15 16 31 32 63 64 127 128 255 256 511 512


log2 n 3 4 4 5 5 6 6 7 7 8 8 9

Documente seu programa enunciando o invariante do processo iterativo. Prove que seu
programa est correto.

FACOM UFMS
A ULA 21

E XERCCIOS

Faremos mais exerccios sobre vetores nesta aula.

Enunciados

21.1 Dados dois vetores x e y, ambos com n elementos, 1 6 n 6 100, determinar o produto
escalar desses vetores.

Programa 21.1: Programa para solucionar o exerccio 21.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, n;
5 float prod, x[100], y[100];
6 printf("Informe n: ");
7 scanf("%d", &n);
8 for (i = 0; i < n; i++) {
9 printf("Informe x[%d]: ", i+1);
10 scanf("%f", &x[i]);
11 }
12 for (i = 0; i < n; i++) {
13 printf("Informe y[%d]: ", i+1);
14 scanf("%f", &y[i]);
15 }
16 prod = 0.0;
17 for (i = 0; i < n; i++)
18 prod = prod + x[i] * y[i];
19 printf("Produto escalar dos vetores x e y %f\n", prod);
20 return 0;
21 }

21.2 So dadas as coordenadas reais x e y de um ponto, um nmero natural n e as coordenadas


reais de n pontos, com 1 6 n 6 100. Deseja-se calcular e imprimir sem repetio os
raios das circunferncias centradas no ponto (x, y) que passem por pelo menos um dos n
pontos dados.

129
130

Exemplo:

(x, y) = (1.0, 1.0)
n=5

Pontos: (1.0, 1.2), (1.5, 2.0), (0.0, 2.0), (0.0, 0.5), (4.0, 2.0)
Nesse caso h trs circunferncias de raios 1.12, 2.01 e 3.162.
Observaes:
p
(a) A distncia entre os pontos (a, b) e (c, d) (a c)2 + (b d)2 .
(b) Dois pontos esto na mesma circunferncia se esto mesma distncia do centro.

21.3 Dadas duas seqncias de caracteres (uma contendo uma frase e outra contendo uma
palavra), determine o nmero de vezes que a palavra ocorre na frase. Considere que
essas seqncias tm no mximo 100 caracteres cada uma.
Exemplo:

Para a palavra ANA e a frase:


ANA E MARIANA GOSTAM DE BANANA.
Temos que a palavra ocorre 4 vezes na frase.

21.4 Calcule o valor do polinmio p(x) = a0 + a1 x + . . . + an xn em k pontos distintos. So


dados os valores de n (grau do polinmio), com 1 6 n 6 100, de a0 , a1 , . . . an (coeficientes
reais do polinmio), de k e dos pontos x1 , x2 , . . . , xk .

21.5 Dado o polinmio p(x) = a0 + a1 x + . . . an xn , isto , os valores de n e de a0 , a1 , . . . , an ,


com 1 6 n 6 100 determine os coeficientes reais da primeira derivada de p(x).

21.6 Dados dois polinmios reais

p(x) = a0 + a1 x + . . . + an xn e q(x) = b0 + b1 x + . . . + bm xm

determinar o produto desses dois polinmios. Suponha que 1 6 m, n 6 100.

21.7 Dadas duas seqncias com n nmeros inteiros entre 0 e 9, interpretadas como dois n-
meros inteiros de n algarismos, 1 6 n 6 100, calcular a seqncia de nmeros que repre-
senta a soma dos dois inteiros.
Exemplo:

n = 8,
1a seqncia 8 2 4 3 4 2 5 1
2a seqncia + 3 3 7 5 2 3 3 7
1 1 6 1 8 6 5 8 8

FACOM UFMS
A ULA 22

E XERCCIOS

Mais exerccios sobre vetores.

Enunciados

22.1 Dada uma seqncia de n nmeros inteiros, com 1 6 n 6 100, imprimi-la em ordem no
decrescente de seus valores. Escreva trs solues usando o mtodo da bolha, o mtodo
da insero e o mtodo da seleo em cada uma delas.

Programa 22.1: Programa para solucionar o exerccio 22.1 usando o mtodo da bolha.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j, n, aux, A[100];
5 printf("Informe n: ");
6 scanf("%d", &n);
7 for (i = 0; i < n; i++) {
8 printf("Informe A[%d]: ", i+1);
9 scanf("%d", &A[i]);
10 }
11 for (i = 0; i < n - 1; i++)
12 for (j = 0; j < n - 1 - i; j++)
13 if (A[j] > A[j + 1]) {
14 aux = A[j];
15 A[j] = A[j + 1];
16 A[j + 1] = aux;
17 }
18 for (i = 0; i < n; i++)
19 printf("%d ", A[i]);
20 printf("\n");
21 return 0;
22 }

Escreva os programas para soluo deste exerccio usando o mtodo da insero e o m-


todo da seleo.

131
132

22.2 Dizemos que uma seqncia de n elementos, com n par, balanceada se as seguintes
somas so todas iguais:

a soma do maior elemento com o menor elemento;


a soma do segundo maior elemento com o segundo menor elemento;
a soma do terceiro maior elemento com o terceiro menor elemento;
e assim por diante . . .

Exemplo:

2 12 3 6 16 15 uma seqncia balanceada, pois 16 + 2 = 15 + 3 = 12 + 6.

Dados n (n par e 0 6 n 6 100) e uma seqncia de n nmeros inteiros, verificar se essa


seqncia balanceada.

22.3 Dados dois nmeros naturais m e n, com 1 6 m, n 6 100, e duas seqncias ordenadas
com m e n nmeros inteiros, obter uma nica seqncia ordenada contendo todos os
elementos das seqncias originais sem repetio.

22.4 Dada uma seqncia x1 , x2 , . . . , xk de nmeros inteiros, com 1 6 k 6 100, verifique se


existem dois segmentos consecutivos iguais nesta seqncia, isto , se existem i e m tais
que
xi , xi+1 , . . . , xi+m1 = xi+m , xi+m+1 , . . . , xi+2m1 .
Imprima, caso existam, os valores de i e m.
Exemplo:

Na seqncia 7, 9, 5, 4, 5, 4, 8, 6 existem i = 3 e m = 2.

FACOM UFMS
A ULA 23

C ADEIAS DE CARACTERES

Veremos nesta aula uma estrutura que possui tratamento especial na linguagem C: a cadeia
de caracteres. Esta estrutura similar a um vetor de caracteres, diferenciando-se apenas por
conter um caracter especial no final, aps o ltimo caracter vlido. Essa caracterstica evita, em
muitos casos, que tenhamos de manter uma varivel que contenha o comprimento do vetor
para saber o nmero de caracteres contidos nele. Outras caractersticas e diferenas importan-
tes sero vistas a seguir.
Na aula 45 veremos funes da biblioteca string.h . Essa biblioteca da linguagem C con-
tm diversas funes teis para manipulao de cadeias de caracteres.

23.1 Literais

Vimos tomando contato com cadeias de caracteres desde quando escrevemos nosso pri-
meiro programa na linguagem C. Por exemplo, na sentena abaixo:

printf("Programar bacana!\n");

o nico argumento passado para a funo printf a cadeia de caracteres (de formatao)
"Programar bacana!\n" . As aspas duplas so usadas para delimitar uma constante do
tipo cadeia de caracteres, que pode conter qualquer combinao de letras, nmeros ou caracte-
res especiais que no sejam as aspas duplas. Mesmo assim, possvel inserir as aspas duplas
no interior de uma constante cadeia de caracteres, inserindo a seqncia \" nessa cadeia. Na
linguagem C, uma constante do tipo cadeia de caracter chamada de literal.
Quando estudamos o tipo de dados char , aprendemos que uma varivel deste tipo pode
conter apenas um nico caracter. Para atribuir um caracter a uma varivel, o caracter deve ser
envolvido por aspas simples. Dessa forma, o trecho de cdigo a seguir:

char sinal;
sinal = +;

133
23.2 V ETORES DE CARACTERES 134

tem o efeito de atribuir o caractere cuja constante + para a varivel sinal . Alm disso,
aprendemos que existe uma distino entre as aspas simples e as aspas duplas, sendo que no
primeiro caso elas servem para definir constantes do tipo caracter e no segundo para definir
constantes do tipo cadeia de caracteres. Assim, o seguinte trecho de cdigo:

char sinal;
sinal = "+";

no est correto, j que que a varivel sinal foi declarada do tipo char , podendo conter um
nico caracter. Lembre-se que na linguagem C as aspas simples e as aspas duplas so usadas
para definir dois tipos de constantes diferentes.
Usamos literais especialmente quando chamamos as funes printf e scanf em um
programa, ou seja, quando descrevemos cadeias de caracteres de formatao. Essencialmente,
a linguagem C trata as literais como cadeias de caracteres. Quando o compilador da linguagem
C encontra uma literal com n caracteres em um programa, ele reserva n + 1 compartimentos de
memria para armazenar a cadeia de caracteres correspondente. Essa rea na memria conter
os caracteres da cadeia mais um caractere extra, o caractere nulo, que registra o final da cadeia.
O caractere nulo um byte cujos bits so todos 0 (zeros) e representado pela seqncia \0 .
importante destacar a diferena entre o caractere nulo e o caractere zero: o primeiro um
caractere no-imprimvel, tem valor decimal 0 e constante \0 ; o segundo um caractere
imprimvel, tem valor 48, smbolo grfico 0 e constante 0 .
A literal "abc" armazenada como um vetor de quatro caracteres na memria, como
mostra a figura 23.1.

a b c \0

Figura 23.1: Armazenamento de uma literal na memria.

Por outro lado, uma literal tambm pode ser vazia. A literal "" uma literal vazia, repre-
sentada na memria como na figura 23.2.

\0

Figura 23.2: Literal vazia na memria.

23.2 Vetores de caracteres

Se queremos trabalhar com variveis que comportam mais que um nico caracter, temos
de trabalhar com vetores de caracteres. No exerccio 21.3, definimos dois vetores palavra e
frase do tipo caracter, da seguinte forma:

FACOM UFMS
23.3 C ADEIAS DE CARACTERES 135

char palavra[MAX+1], frase[MAX+1];

Para ler, por exemplo, o contedo da varivel palavra , produzimos o seguinte trecho de
programa:

printf("Informe a palavra: ");


i = 0;
do {
scanf("%c", &palavra[i]);
i++;
} while(palavra[i-1] != \n);
m = i-1;

Para imprimir o contedo dessa mesma varivel palavra , devemos escrever um trecho de
programa da seguinte forma:

printf("Palavra: ");
for (i = 0; i < m; i++)
printf("%c", palavra[i]);
printf("\n");

Note que sempre necessitamos de uma varivel adicional para controlar o comprimento
de um vetor de caracteres quando da sua leitura. A varivel m faz esse papel no primeiro
trecho de programa acima. Alm disso, tanto para leitura como para escrita de um vetor de
caracteres, precisamos de uma estrutura de repetio para processar os caracteres um a um.
Com as cadeias de caracteres, evitamos esta sobrecarga de trabalho para o programador, como
veremos daqui por diante.

23.3 Cadeias de caracteres

Algumas linguagens de programao de alto nvel oferecem ao() programador(a) um tipo


de dados especial para que variveis do tipo cadeia de caracteres possam ser declaradas. Por
outro lado, na linguagem C no h um tipo de dados como esse e, assim, qualquer vetor de
caracteres pode ser usado para armazenar uma cadeia de caracteres. A diferena, nesse caso,
que uma cadeia de caracteres sempre terminada por um caractere nulo. Uma vantagem dessa
estratgia que no h necessidade de se manter o comprimento da cadeia de caracteres asso-
ciado a ela. Por outro lado, esse mesmo comprimento, se necessrio, s pode ser encontrado
atravs de uma varredura no vetor.

FACOM UFMS
23.3 C ADEIAS DE CARACTERES 136

Suponha que necessitamos de uma varivel capaz de armazenar uma cadeia de caracteres
de at 50 caracteres. Como a cadeia de caracteres necessitar de um caractere nulo no final,
ento a cadeia de caracteres tm de ser declarada com 51 compartimentos para armazenar
valores do tipo char , como mostrado a seguir:

#define MAX 50
char cadeia[MAX+1];

Na declarao de uma varivel que pode armazenar uma cadeia de caracteres, sempre de-
vemos reservar um compartimento a mais para que o caractere nulo possa ser armazenado.
Como diversas funes da linguagem C supem que as cadeias de caracteres so terminadas
com o caractere nulo, se isso no ocorre em algum caso, o comportamento do programa passa
a ser imprevisvel.
A declarao de um vetor de caracteres com dimenso MAX+1 no quer dizer que ele sem-
pre conter uma cadeia de caracteres com MAX caracteres. O comprimento de uma cadeia de
caracteres depende da posio do caractere nulo na cadeia, no do comprimento do vetor onde
a cadeia est armazenada.
Como em qualquer vetor, uma cadeia de caracteres tambm pode ser declarada e iniciali-
zada simultaneamente. Por exemplo,

char cidade[13] = "Campo Grande";

faz com que o compilador insira seqencialmente os caracteres da cadeia de caracteres


"Campo Grande" no vetor cidade e ento adicione o caractere nulo ao final da cadeia. Ape-
sar de "Campo Grande" parecer uma literal, na realidade, a linguagem C a enxerga como uma
abreviao para um inicializador de um vetor, que poderia ter sido escrito equivalentemente
como abaixo:

char cidade[13] = {C,a,m,p,o, ,G,r,a,n,d,e,\0};

Se um inicializador tem menor comprimento que o comprimento do vetor, o compilador


preencher os caracteres restantes do vetor com o caractere nulo. Se, por outro lado, o inicia-
lizador tem maior comprimento que a capacidade de armazenamento do vetor associado, os
caracteres iniciais do inicializador sero armazenados no vetor, sem que o ltimo deles seja o
caractere nulo, impedindo assim que essa varivel seja usada como uma legtima cadeia de
caracteres.
Vejamos agora o programa 23.1 que determina o comprimento de uma varivel que uma
cadeia de caracteres. Qual ser a sada deste programa?

FACOM UFMS
23.3 C ADEIAS DE CARACTERES 137

Programa 23.1: Programa que determina o comprimento de uma cadeia de caracteres.

1 #include <stdio.h>
2 int main(void)
3 {
4 char palavra[10] = "Ola!";
5 int n;
6 n = 0;
7 while(palavra[n] != \0)
8 n++;
9 printf("O comprimento da palavra %d\n", n);
10 return 0;
11 }

H uma forma de ler e escrever cadeias de caracteres na linguagem C que facilita o trabalho
de um(a) programador(a), evitando que sempre lance mo de uma estrutura de repetio para
realizar uma dessas duas tarefas. O especificador de converso %s no interior de uma cadeia
de caracteres de formatao de entrada pode ser usado para mostrar um vetor de caracteres
que terminado por um caracter nulo, isto , uma cadeia de caracteres. Assim se palavra
um vetor de caracteres terminado com o caractere nulo, a chamada da funo abaixo:

printf("%s\n", palavra);

pode ser usada para mostrar o contedo completo da cadeia de caracteres palavra na sada
padro. Quando a funo printf encontra o especificador de converso %s , supe que o
argumento correspondente uma cadeia de caracteres, isto , um vetor de caracteres terminado
por um caractere nulo.
Podemos tambm usar a mesma cadeia de caracteres de formatao "%s" para leitura de
uma cadeia de caracteres. A funo scanf pode ser usada com o especificador de converso
%s para ler uma cadeia de caracteres at que a leitura de um branco seja realizada. Assim, a
chamada da funo scanf abaixo:

scanf("%s", palavra);

tem o efeito de ler uma cadeia de caracteres digitada pelo usurio e de armazen-la no vetor
de caracteres palavra .
muito importante ressaltar que, ao contrrio das chamadas anteriores da funo scanf ,
no caso de leitura de cadeias de caracteres, o smbolo & no adicionado como prefixo do
identificador da varivel. Veremos o porqu disto a partir da aula 49, quando aprenderemos
apontadores.

FACOM UFMS
23.3 C ADEIAS DE CARACTERES 138

Se na execuo da funo scanf anterior um(a) usurio(a) digita os caracteres abcdefg , a


cadeia de caracteres "abcdefg" armazenada no vetor palavra . Se, diferentemente, um(a)
usurio(a) digita os caracteres Campo Grande , ento apenas a cadeia de caracteres "Campo"
armazenada no vetor palavra , devido ao branco ( ). Os caracteres restantes da cadeia di-
gitada ficaro disponveis no buffer de entrada at que uma prxima chamada funo scanf
seja realizada.
Para evitar os brancos na leitura de uma cadeia de caracteres, usamos o especificador de
converso %[...] , que tambm usado na leitura de cadeias de caracteres, delimitando, den-
tro dos colchetes, quais so os caracteres permitidos em uma leitura. Qualquer outro caractere
diferente dos especificados dentro dos colchetes finalizam a leitura. Alm disso, podemos in-
verter essas permisses, indicando o caractere ^ como o primeiro caractere dentro dos colche-
tes. Por exemplo,

scanf("%[^\n]", palavra);

realiza a leitura de uma cadeia de caracteres, armazenando seu contedo no vetor de carac-
teres palavra . O caracter que finaliza a leitura o \n . Qualquer outro caracter ser lido e
armazenado no vetor palavra .
muito importante destacar que a funo scanf termina automaticamente uma cadeia de
caracteres que lida com o especificador de converso "%s" ou "%[...]" com um caracter
nulo, fazendo assim que o vetor de caracteres se torne de fato uma cadeia de caracteres aps
sua leitura.
Veja um exemplo simples do uso dos conceitos de entrada e sada de cadeias de caracteres
no programa 23.2.

Programa 23.2: Entrada e sada de uma cadeia de caracteres.

1 #include <stdio.h>
2 #define MAX 20
3 int main(void)
4 {
5 char palavra[MAX];
6 int n;
7 printf("Informe uma palavra (com at %d caracteres): ", MAX);
8 scanf("%s", palavra);
9 n = 0;
10 while (palavra[n] != \0)
11 n++;
12 printf("A palavra [%s] tem %d caracteres\n", palavra, n);
13 return 0;
14 }

FACOM UFMS
23.3 C ADEIAS DE CARACTERES 139

Exerccios

23.1 Dada uma frase com no mximo 100 caracteres, determinar quantos caracteres espao a
frase contm.

Programa 23.3: Soluo do exerccios 23.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 char frase[MAX+1];
6 int esp, i;
7 printf("Informe uma frase: ");
8 scanf("%[^\n]", frase);
9 esp = 0;
10 for (i = 0; frase[i] != \0; i++)
11 if (frase[i] == )
12 esp++;
13 printf("Frase tem %d espaos\n", esp);
14 return 0;
15 }

23.2 Dada uma cadeia de caracteres com no mximo 100 caracteres, contar a quantidade de
letras minsculas, letras maisculas, dgitos, espaos e smbolos de pontuao que essa
cadeia possui.

23.3 Dadas duas cadeias de caracteres cadeia1 e cadeia2 , concatenar cadeia2 no final
de cadeia1 , colocando o caracter nulo no final da cadeia resultante. A cadeia resultante
a ser mostrada deve estar armazenada em cadeia1 . Suponha que as cadeias sejam
informadas com no mximo 100 caracteres.

23.4 Dada uma cadeia de caracter cadeia com no mximo 100 caracteres e um caractere c ,
buscar a primeira ocorrncia de c em cadeia . Se c ocorre em cadeia , mostrar a
posio da primeira ocorrncia; caso contrrio, mostrar o valor -1 .

23.5 Dadas duas cadeias de caracteres cadeia1 e cadeia2 , cada uma com no mximo 100
caracteres, compar-las e devolver um valor menor que zero se cadeia1 lexicografica-
mente menor que cadeia2 , o valor zero se cadeia1 igual ou tem o mesmo contedo
que cadeia2 , ou um valor maior que zero se cadeia1 lexicograficamente maior que
cadeia2 .

FACOM UFMS
A ULA 24

M ATRIZES

Nas aulas 19, 20, 21 e 22, tivemos contato com vetores ou variveis compostas homogneas
unidimensionais. No entanto, uma varivel composta homognea pode ter qualquer nmero
de dimenses. A partir desta aula, aprenderemos a trabalhar com as estruturas denominadas
de matrizes ou variveis compostas homogneas bidimensionais.
Do mesmo modo, as matrizes so variveis compostas porque representam a composio
de um conjunto de valores indivisveis, homogneas porque esses valores so de um mesmo
tipo de dados e bidimensionais porque, ao contrrio dos vetores que so lineares ou tm uma
nica dimenso, estas estruturas tm duas dimenses. Nesta aula aprenderemos a declarar
matrizes, a declarar e inicializar simultaneamente as matrizes e tambm a usar matrizes para
solucionar problemas.

24.1 Definio, declarao e uso

Em matemtica, uma matriz uma tabela ou um quadro contendo m linhas e n colunas e


usada, entre outros usos, para a resoluo de sistemas de equaes lineares e transformaes
lineares.
Uma matriz com m linhas e n colunas chamada de uma matriz m por n e denota-se m n.
Os valores m e n so chamados de dimenses, tipo ou ordem da matriz.
Um elemento de uma matriz A que est na i-sima linha e na j-sima coluna chamado
de elemento i, j ou (i, j)-simo elemento de A. Este elemento denotado por Ai,j ou A[i, j].
Observe que estas definies so matemticas e que, na linguagem C, temos um deslocamento
dos elementos da matriz devido aos ndices das linhas e colunas iniciarem, como acontece nos
vetores, pelo 0 (zero).
Uma matriz onde uma de suas dimenses igual a 1 geralmente chamada de vetor. Uma
matriz de dimenses 1 n, contendo uma linha e n colunas, chamada de vetor linha ou
matriz linha, e uma matriz de dimenses m 1, contendo m linhas e uma coluna, chamada
de vetor coluna ou matriz coluna.
Na linguagem C, as matrizes so declaradas similarmente aos vetores. Um tipo de dados
usado para a declarao, em seguida um identificador ou nome da varivel matriz e, ainda,
dois nmeros inteiros envolvidos individualmente por colchetes, indicando as dimenses da
matriz, isto , seu nmero de linhas e seu nmero de colunas.
A forma geral da declarao de uma matriz dada a seguir:

140
24.1 D EFINIO , DECLARAO E USO 141

tipo identificador[dimenso1][dimenso2];

onde tipo um dos tipos de dados da linguagem C ou um tipo definido pelo(a) progra-
mador(a), identificador o nome da varivel matriz fornecido pelo(a) programador(a) e
dimenso1 e dimenso2 determinam a quantidade de linhas e colunas, respectivamente, a
serem disponibilizadas para uso na matriz. Por exemplo, a declarao a seguir

int A[20][30];

faz com que 600 clulas de memria sejam reservadas, cada uma delas podendo armazenar
valores do tipo int . A referncia a cada uma dessas clulas realizada pelo identificador
da matriz A e por dois ndices, o primeiro que determina a linha e o segundo que determina
a coluna da matriz. Na figura 24.1 mostramos um esquema de como a matriz A disposta
quando declarada desta forma.

A 0 1 2 28 29

18

19

Figura 24.1: Matriz A com 20 linhas e 30 colunas.

Na linguagem C, a primeira linha de uma matriz tem ndice 0, a segunda linha tem ndice
1, e assim por diante. Do mesmo modo, a primeira coluna da matriz tem ndice 0, a segunda
tem ndice 1 e assim por diante. Para referenciar o valor da clula da linha 0 e da coluna 3 da
matriz A , devemos usar o identificador da varivel e os ndices 0 e 3 envolvidos por colchetes,
ou seja, A[0][3] .
Apesar de visualizarmos uma matriz na forma de uma tabela bidimensional, essa no a
forma de armazenamento dessa varivel na memria. A linguagem C armazena uma matriz
na memria linha a linha, como mostrado na figura 24.2 para a matriz A .

FACOM UFMS
24.2 D ECLARAO E INICIALIZAO SIMULTNEAS 142

memria linha 0 linha 1 linha 19

A[0][0] A[0][29] A[1][0] A[1][29] A[19][0] A[19][29]

Figura 24.2: Matriz A com 20 linhas e 30 colunas disposta na memria.

24.2 Declarao e inicializao simultneas

Da mesma forma como com os vetores, uma matriz pode ser inicializada no momento de
sua declarao. Na verdade, variveis compostas de qualquer dimenso podem ser iniciali-
zadas seguindo as mesmas regras. Para uma matriz, a declarao e inicializao simultneas
deve ser realizada agrupando os inicializadores de uma dimenso como abaixo:

int A[4][7] = {{0, 1, 1, 0, 0, 0, 2},


{1, 2, 0, 1, 5, 7, 1},
{2, 2, 2, 1, 1, 2, 1},
{3, 7, 9, 6, 2, 1, 0}};

Na declarao e inicializao acima, cada inicializador fornece valores para uma linha da ma-
triz. A linguagem C possui algumas pequenas regras para declarar e inicializar matrizes ou
variveis compostas homogneas de qualquer dimenso:

se um inicializador no grande o suficiente para inicializar um varivel composta ho-


mognea, ento o restante dos elementos sero inicializados com 0 (zero). Por exemplo,

int A[4][7] = {{0, 1, 1, 0, 0, 0, 2},


{3, 7, 9, 6, 2, 1, 0}};

inicializa as duas primeiras linhas da matriz A . As duas ltimas linhas sero inicializa-
das com 0 (zero);
se um inicializador mais interno no longo o suficiente para inicializar uma linha, ento
o restante dos elementos na linha inicializado com 0 (zero). Por exemplo,

int A[4][7] = {{0, 1, 1},


{1, 2, 0, 1, 5, 7, 1},
{2, 2, 2, 1, 1, 2},
{3, 7, 9, 6, 2, 1, 0}};

as chaves internas, que determinam as inicializaes das linhas, podem ser omitidas.
Neste caso, uma vez que o compilador tenha lido elementos suficientes para preencher
uma linha, ele o faz e inicia o preenchimento da prxima linha. Por exemplo,

FACOM UFMS
24.3 E XEMPLO 143

int A[4][7] = {0, 1, 1, 0, 0, 0, 2,


1, 2, 0, 1, 5, 7, 1,
2, 2, 2, 1, 1, 2, 1,
3, 7, 9, 6, 2, 1, 0};

24.3 Exemplo

Apresentamos a seguir o programa 24.1, que resolve o seguinte problema:

Dada uma matriz real B, de 5 linhas e 10 colunas, escrever um programa que calcule
o somatrio dos elementos da oitava coluna e que calcule o somatrio da terceira
linha.

Programa 24.1: Um programa que exemplifica o uso de matrizes.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j;
5 float soma_c8, soma_l3, B[5][10];
6 for (i = 0; i < 5; i++) {
7 for (j = 0; j < 10; j++) {
8 printf("Informe B[%d][%d] = ", i, j);
9 scanf("%f", &B[i][j]);
10 }
11 }
12 soma_c8 = 0.0;
13 for (i = 0; i < 5; i++)
14 soma_c8 = soma_c8 + B[i][7];
15 printf("Valor da soma da oitava coluna %4.4f\n", soma_c8);
16 soma_l3 = 0.0;
17 for (j = 0; j < 10; j++)
18 soma_l3 = soma_l3 + B[2][j];
19 printf("Valor da soma da terceira linha %4.4f\n", soma_l3);
20 return 0;
21 }

Exerccios

24.1 Dadas duas matrizes de nmeros inteiros A e B, de dimenses mn, com 1 6 m, n 6 100,
fazer um programa que calcule a matriz Cmn = A + B.

FACOM UFMS
24.3 E XEMPLO 144

Programa 24.2: Soluo do exerccio 24.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int m, n, i, j,
6 A[MAX][MAX], B[MAX][MAX], C[MAX][MAX];
7 printf("Informe as dimenses (m,n) das matrizes: ");
8 scanf("%d%d", &m, &n);
9 for (i = 0; i < m; i++)
10 for (j = 0; j < n; j++) {
11 printf("Informe A[%2d][%2d]: ", i, j);
12 scanf("%d", &A[i][j]);
13 }
14 for (i = 0; i < m; i++)
15 for (j = 0; j < n; j++) {
16 printf("Informe B[%2d][%2d]: ", i, j);
17 scanf("%d", &B[i][j]);
18 }
19 for (i = 0; i < m; i++)
20 for (j = 0; j < n; j++)
21 C[i][j] = A[i][j] + B[i][j];
22 for (i = 0; i < m; i++) {
23 for (j = 0; j < n; j++)
24 printf("%2d ", C[i][j]);
25 printf("\n");
26 }
27 return 0;
28 }

24.2 Fazer um programa que dada uma matriz de nmeros reais Amn , determine At . Supo-
nha que 1 6 m, n 6 100.

24.3 Dada uma matriz de nmeros reais A com m linhas e n colunas, 1 6 m, n 6 100, e um
vetor de nmeros reais v com n elementos, determinar o produto de A por v.

24.4 Um vetor de nmeros reais x com n elementos apresentado como resultado de um


sistema de equaes lineares Ax = b, cujos coeficientes so representados em uma matriz
de nmeros reais Amn e o lados direitos das equaes em um vetor de nmeros reais
b de m elementos. Dados A, x e b, verificar se o vetor x realmente soluo do sistema
Ax = b, supondo que 1 6 m, n 6 100.

FACOM UFMS
A ULA 25

E XERCCIOS

Enunciados

25.1 Dadas duas matrizes de nmeros reais Amn e Bnp , com 1 6 m, n, p 6 100, calcular o
produto de A por B.

25.2 Dada uma matriz de nmeros inteiros Amn , com 1 6 m, n 6 100, imprimir o nmero de
linhas e o nmero de colunas nulas da matriz.
Exemplo:

Se a matriz A tem m = 4 linhas, n = 4 colunas e contedo



1 0 2 3
4 0 5 6

0 0 0 0
0 0 0 0
ento A tem 2 linhas nulas e 1 coluna nula.

25.3 Dizemos que uma matriz de nmeros inteiros Ann uma matriz de permutao se em
cada linha e em cada coluna houver n 1 elementos nulos e um nico elemento 1.
Exemplo:

A matriz abaixo de permutao



0 1 0 0
0 0 1 0
1 0 0 0 .

0 0 0 1
Observe que

2 1 0
1 2 0
0 0 1
no de permutao.

Dada uma matriz de nmeros inteiros Ann , , com 1 6 n 6 100, verificar se A de


permutao.

25.4 Dada uma matriz de nmeros reais Amn , com 1 6 m, n 6 100, verificar se existem
elementos repetidos em A.

145
146

Programa 25.1: Soluo do exerccio 25.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int m, n, p, i, j, k;
6 float A[MAX][MAX], B[MAX][MAX], C[MAX][MAX];
7 printf("Informe as dimenses (m,n) da matriz A: ");
8 scanf("%d%d", &m, &n);
9 printf("Informe o nmero de colunas da matriz B: ");
10 scanf("%d", &p);
11 for (i = 0; i < m; i++)
12 for (j = 0; j < n; j++) {
13 printf("Informe A[%2d][%2d]: ", i, j);
14 scanf("%f", &A[i][j]);
15 }
16 for (i = 0; i < n; i++)
17 for (j = 0; j < p; j++) {
18 printf("Informe B[%2d][%2d]: ", i, j);
19 scanf("%f", &B[i][j]);
20 }
21 for (i = 0; i < m; i++)
22 for (j = 0; j < p; j++) {
23 C[i][j] = 0.0;
24 for (k = 0; k < n; k++)
25 C[i][j] = C[i][j] + A[i][k] + B[k][j];
26 }
27 for (i = 0; i < m; i++) {
28 for (j = 0; j < p; j++)
29 printf("%2.2f ", C[i][j]);
30 printf("\n");
31 }
32 return 0;
33 }

FACOM UFMS
A ULA 26

E XERCCIOS

Enunciados

26.1 Dizemos que uma matriz quadrada de nmeros inteiros distintos um quadrado m-
gico1 se a soma dos elementos de cada linha, a soma dos elementos de cada coluna e a
soma dos elementos da diagonal principal e secundria so todas iguais.
Exemplo:

A matriz

8 0 7
4 5 6
3 10 2
um quadrado mgico.

Dada uma matriz quadrada de nmeros inteiros Ann , com 1 6 n 6 100, verificar se A
um quadrado mgico.

26.2 (a) Imprimir as n primeiras linhas do tringulo de Pascal2 , com 1 6 n 6 100.


1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
..
.
(b) Imprimir as n primeiras linhas do tringulo de Pascal usando apenas um vetor.

26.3 Um jogo de palavras cruzadas pode ser representado por uma matriz Amn onde cada
posio da matriz corresponde a um quadrado do jogo, sendo que 0 indica um quadrado
branco e 1 indica um quadrado preto. Indicar em uma dada matriz Amn de 0 e 1,
com 1 6 m, n 6 100, as posies que so incio de palavras horizontais e/ou verticais nos
quadrados correspondentes (substituindo os zeros), considerando que uma palavra deve
ter pelo menos duas letras. Para isso, numere consecutivamente tais posies.
Exemplo:
1
O primeiro registro conhecido de um quadrado mgico vem da China e data do ano de 650 a.c.
2
Descoberto em 1654 pelo matemtico francs Blaise Pascal.

147
148

Programa 26.1: Soluo do exerccio 26.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int n, i, j, A[MAX][MAX], soma, soma_aux, magico;
6 printf("Informe a dimenso n da matriz: ");
7 scanf("%d", &n);
8 for (i = 0; i < n; i++)
9 for (j = 0; j < n; j++) {
10 printf("Informe A[%2d][%2d]: ", i, j);
11 scanf("%d", &A[i][j]);
12 }
13 soma = 0;
14 for (j = 0; j < n; j++)
15 soma = soma + A[0][j];
16 magico = 1;
17 for (i = 1; i < n && magico; i++) {
18 soma_aux = 0;
19 for (j = 0; j < n; j++)
20 soma_aux = soma_aux + A[i][j];
21 if (soma_aux != soma)
22 magico = 0;
23 }
24 for (j = 0; j < n && magico; j++) {
25 soma_aux = 0;
26 for (i = 0; i < n; i++)
27 soma_aux = soma_aux + A[i][j];
28 if (soma_aux != soma)
29 magico = 0;
30 }
31 soma_aux = 0;
32 for (i = 0; i < n && magico; i++)
33 soma_aux = soma_aux + A[i][i];
34 if (soma_aux != soma)
35 magico = 0;
36 soma_aux = 0;
37 for (i = 0; i < n; i++)
38 soma_aux = soma_aux + A[i][n-i-1];
39 if (soma_aux != soma)
40 magico = 0;
41 if (magico)
42 printf("Quadrado mgico de ordem %d\n", n);
43 else
44 printf("Matriz no quadrado mgico\n");
45 return 0;
46 }

FACOM UFMS
149

Dada a matriz

0 1 0 1 1 0 1 0
0
0 0 0 1 0 0 0

0
0 1 1 0 0 1 0

1 0 0 0 0 1 0 0
0 0 1 0 0 0 1 1
a sada dever ser

1 1 2 1 1 3 1 4
5
6 0 0 1 7 0 0

8 0 1 1 9 0 1 0 .

1 10 0 11 0 1 12 0
13 0 1 14 0 0 1 1

26.4 Os elementos aij de uma matriz de nmeros inteiros Ann representam os custos de trans-
porte da cidade i para a cidade j. Dados uma matriz Ann , com 1 6 n 6 100, um nmero
inteiro m > 0 representando a quantidade de itinerrios, um nmero k representando o
nmero de cidades em cada um dos itinerrios, calcular o custo total para cada itinerrio.
Exemplo:

Dado
4 1 2 3
5 2 1 400
A=
2

1 3 8
7 1 2 5
m = 1, k = 8 e o itinerrio 0 3 1 3 3 2 1 0, o custo total desse itinerrio

a03 + a31 + a13 + a33 + a32 + a21 + a10 = 3 + 1 + 400 + 5 + 2 + 1 + 5 = 417 .

26.5 Dados um caa-palavra, representado por uma matriz A de letras de dimenso m n,


com 1 6 m, n 6 50, e uma lista de k > 0 palavras, encontrar a localizao (linha e coluna)
no caa-palavras em que cada uma das palavras pode ser encontrada.

a c a t g u d k h
q y u s z j l l c
b m a m o r a w e
p f a x v n e t q
o c g j u a t d k
h j o a q y s c z amora
z l c a d b m o r
o b g j h c t a w
t s y z l o k e u
q b r s o a e p h
r o d o m a y s l
m u l q c k a z g

Uma palavra encontrada no caa-palavras se uma seqncia ininterrupta de letras na


tabela coincide com a palavra. Considere que as letras so apenas as minsculas. A busca
por uma palavra deve ser feita em qualquer das oito direes no caa-palavras.

FACOM UFMS
150

26.6 Considere n cidades numeradas de 0 a n 1 que esto interligadas por uma srie de
estradas de mo nica, com 1 6 n 6 100. As ligaes entre as cidades so representadas
pelos elementos de uma matriz quadrada Ann , cujos elementos aij assumem o valor 1 ou
0, conforme exista ou no estrada direta que saia da cidade i e chegue cidade j. Assim,
os elementos da coluna j indicam as estradas que chegam cidade j. Por conveno
aii = 1.
A figura abaixo mostra um exemplo para n = 4.

0 1 2 3

A 0 1 2 3
0 1 1 1 0

1 1 1 1 0

2 1 0 1 0

3 0 1 1 1

(a) Dado k, determinar quantas estradas saem e quantas chegam cidade k.


(b) A qual das cidades chega o maior nmero de estradas?
(c) Dado k, verificar se todas as ligaes diretas entre a cidade k e outras so de mo
dupla.
(d) Relacionar as cidades que possuem sadas diretas para a cidade k.
(e) Relacionar, se existirem:
i. As cidades isoladas, isto , as que no tm ligao com nenhuma outra;
ii. As cidades das quais no h sada, apesar de haver entrada;
iii. As cidades das quais h sada sem haver entrada.
(f) Dada uma seqncia de m inteiros cujos valores esto entre 0 e n 1, verificar se
possvel realizar o roteiro correspondente. No exemplo dado, o roteiro representado
pela seqncia 2 0 1 2 3, com m = 5, impossvel.
(g) Dados k e p, determinar se possvel ir da cidade k para a cidade p pelas estradas
existentes. Voc consegue encontrar o menor caminho entre as duas cidades?

FACOM UFMS
A ULA 27

R EGISTROS

Nas aulas 19 a 26 aprendemos a trabalhar com variveis compostas homogneas unidimen-


sionais e bidimensionais ou os vetores e as matrizes , que permitem que um programador
agrupe valores de um mesmo tipo em uma nica entidade lgica. Como mencionamos antes,
a dimenso de uma varivel composta homognea no fica necessariamente restrita a uma ou
duas. Isto , tambm podemos declarar e usar variveis compostas homogneas com trs ou
mais dimenses. Importante relembrar tambm que, para referenciar um elemento em uma
varivel composta homognea so necessrios o seu identificador e um ou mais ndices. A
linguagem C dispe tambm de uma outra forma para agrupamento de dados, chamada va-
rivel composta heterognea, registro ou estrutura1 . Nelas, diferentemente do que nas vari-
veis compostas homogneas, podemos armazenar sob uma mesma entidade lgica valores de
tipos diferentes. Alm disso, ao invs de ndices ou endereos usados nas variveis compostas
homogneas para acesso a um valor, especificamos o nome de um campo para selecionar um
campo particular do registro. Os registros so os objetos de estudo desta aula.

27.1 Definio

Uma varivel composta heterognea ou registro uma estrutura onde podemos armaze-
nar valores de tipos diferentes sob uma mesma entidade lgica. Cada um desses possveis
valores armazenado em um compartimento do registro denominado campo do registro, ou
simplesmente campo. Um registro composto pelo seu identificador e pelos seus campos.
Suponha que queremos trabalhar com um agrupamento de valores que representam uma de-
terminada mercadoria de uma loja, cujas informaes relevantes so o cdigo do produto e seu
valor. Na linguagem C, podemos ento declarar um registro com identificador produto da
seguinte forma:

struct {
int codigo;
int quant;
float valor;
} produto;

1
struct e member (of a structure) so jarges comuns na linguagem C. A traduo literal dessas palavras pode nos
confundir com outros termos j usados durante o curso. Por isso, escolhemos registro e campo (do registro) como
tradues para o portugus.

151
27.1 D EFINIO 152

A figura 27.1 mostra a disposio do registro produto e de seus campos na memria do


computador.

produto

memria

cdigo quant valor

Figura 27.1: Representao do registro produto na memria.

A declarao de um registro sempre inicia com a palavra reservada struct . Em seguida,


um bloco de declaraes iniciado com o smbolo { . Depois disso, podemos declarar os cam-
pos do registro, linha aps linha. Cada linha deve conter o tipo e o identificador do campo do
registro. Finalizamos ento as declaraes dos campos do registro com o smbolo } e, depois
dele, realizamos de fato a declarao do registro, digitando o seu identificador. A varivel do
tipo registro com identificador horario declarada acima contm trs campos, dois do tipo
inteiro com identificadores horas e quant , e outro do tipo ponto flutuante valor .
Podemos declarar outras variveis do tipo registro com os mesmos campos da varivel
produto da seguinte forma:

struct {
int codigo;
int quant;
float valor;
} produto, estoque, baixa;

Na linguagem C, uma forma alternativa de declarao dos campos de um registro declarar


campos de um mesmo tipo em uma nica linha de cdigo. Por exemplo,

struct {
char sala, turma;
int horas_inicio, minutos_inicio, horas_fim, minutos_fim;
float dimensoes_sala;
} aula;

declara um registro com identificador aula com seis campos, dois campos do tipo char ,
quatro outros campos do tipo int e um campo do tipo float . Apesar dessa declarao
estar correta e de economizar algumas linhas de cdigo, sempre optamos pela declarao dos
campos separadamente, linha a linha, para que os campos fiquem bem destacados e, assim,
facilitem sua identificao rpida no cdigo. Dessa forma, em geral optamos pela seguinte
declarao do registro aula :

FACOM UFMS
27.1 D EFINIO 153

struct {
char sala;
char turma;
int horas_inicio;
int minutos_inicio;
int horas_fim;
int minutos_fim;
float dimensoes_sala;
} aula;

Diferentemente da atribuio de um valor a uma varivel ou a um compartimento de uma


varivel composta homognea, a atribuio de um valor a um campo de uma varivel do tipo
registro realizada atravs do acesso a esse campo, especificando o identificador do registro,
um ponto e o identificador do campo.
Por exemplo, para atribuir os valores 12, 5 e 34.5 aos campos codigo , quant e valor ,
respectivamente, da varivel do tipo registro produto declarada anteriormente, devemos fa-
zer como abaixo:

produto.codigo = 12;
produto.quant = 5;
produto.valor = 34.5;

Observe acima que, quando referenciamos um campo de uma varivel do tipo registro, no
so permitidos espaos entre o identificador do registro, o ponto e o identificador do campo.
Tambm podemos usar o valor de um campo de um registro em quaisquer expresses. Por
exemplo, a expresso relacional abaixo correta:

if (produto.valor > 150.0)


printf("Acima do preo de mercado!\n");

Declaraes de registros diferentes podem conter campos com mesmo identificador. Por
exemplo,

struct {
char tipo;
char fatorRH;
int idade;
float altura;
} coleta;

FACOM UFMS
27.2 D ECLARAO E INICIALIZAO SIMULTNEAS 154

struct {
char codigo;
int tipo;
int idade;
} certidao;

so declaraes vlidas na linguagem C. O acesso aos campos dessas variveis se diferencia jus-
tamente pelo identificador dos registros. Isto , a partir das declaraes dos registros coleta
e certidao , as atribuies abaixo esto corretas:

coleta.tipo = O;
certidao.tipo = 0;
coleta.idade = 29;
certidao.idade = coleta.idade + 2;

27.2 Declarao e inicializao simultneas

Declarao e inicializao simultneas tambm so vlidas quando tratamos com regis-


tros. As regras so idnticas s declaraes e inicializaes simultneas para os vetores. Por
exemplo, o registro produto que declaramos acima pode ter sua inicializao realizada no
momento de sua declarao da seguinte forma:

struct {
int codigo;
int quant;
float valor;
} produto = {1, 5, 34.5};

Neste caso, o campo codigo inicializado com o valor 1 , o campo quant inicializado
com o valor 5 e o campo valor com 34.5 . Como mencionado, as regras de declarao e
inicializao simultneas para registros so equivalentes quelas para vetores. Assim,

struct {
int codigo;
int quant;
float valor;
} produto = {0};

far a inicializao dos campos codigo e quant com 0 e do campo valor com 0.0 .

FACOM UFMS
27.3 O PERAES SOBRE REGISTROS 155

27.3 Operaes sobre registros

Variveis compostas heterogneas possuem uma caracterstica importante, que no ob-


servada em variveis compostas homogneas.
Se queremos copiar o contedo de uma varivel composta homognea para outra varivel
composta homognea, necessrio realizar a cpia elemento a elemento, escrevendo uma ou
mais estruturas de repetio para que essa cpia seja efetuada com sucesso. Isto , se A e B
so, por exemplo, vetores de mesmo tipo de dados e mesma dimenso, errado tentar fazer
uma atribuio como abaixo:

A = B;

para tentar copiar os valores do vetor B no vetor A .


O correto, neste caso, fazer a cpia elemento a elemento de uma varivel para outra, como
mostrado a seguir:

for (i = 0; i < n; i++)


A[i] = B[i];

supondo que n a dimenso dos vetores A e B .


Quando tratamos de registros, no entanto, podemos fazer uma atribuio direta e realizar
a cpia de todos os seus campos nessa nica atribuio. O trecho de cdigo a seguir mostra
um exemplo com a declarao de dois registros com mesmos campos, a atribuio de valores
ao primeiro registro e uma cpia completa de todos os campos do primeiro registro para o
segundo:

struct {
char tipo;
int codigo;
int quant;
float valor;
} mercadoria1, mercadoria2;
mercadoria1.tipo = A;
mercadoria1.codigo = 10029;
mercadoria1.quant = 62;
mercatoria1.valor = 10.32 * TAXA + 0.53;
mercadoria2 = mercadoria1;

FACOM UFMS
27.4 E XEMPLO 156

27.4 Exemplo

O programa 27.1 recebe um horrio no formato de horas, minutos e segundos, com as horas
no intervalo de 0 a 23, e atualiza este horrio em um segundo.

Programa 27.1: Exemplo do uso de registros.

1 #include <stdio.h>
2 int main(void)
3 {
4 struct {
5 int horas;
6 int minutos;
7 int segundos;
8 } agora, prox;
9 printf("Informe o horrio atual (hh:mm:ss): ");
10 scanf("%d:%d:%d", &agora.horas, &agora.minutos, &agora.segundos);
11 prox = agora;
12 prox.segundos++;
13 if (prox.segundos == 60) {
14 prox.segundos = 0;
15 prox.minutos++;
16 if (prox.minutos == 60) {
17 prox.minutos = 0;
18 prox.horas++;
19 if (prox.horas == 24)
20 prox.horas = 0;
21 }
22 }
23 printf("Prximo horrio %02d:%02d:%02d\n",
24 prox.horas, prox.minutos, prox.segundos);
25 return 0;
26 }

Exerccios

27.1 Dada uma data, escreva um programa que mostre a prxima data, isto , a data que
representa o dia seguinte data fornecida. No esquea dos anos bissextos. Lembre-se
que um ano bissexto se divisvel por 400 ou, em caso negativo, se divisvel por 4 mas
no por 100.

27.2 Dados dois horrios de um mesmo dia expressos no formato hh:mm:ss, calcule o tempo
decorrido entre estes dois horrios, apresentando o resultado no mesmo formato.

27.3 Dadas duas datas, calcule o nmero de dias decorridos entre estas duas datas.

FACOM UFMS
27.4 E XEMPLO 157

Programa 27.2: Soluo do exerccio 27.1.

1 #include <stdio.h>
2 int main(void)
3 {
4 struct {
5 int dia;
6 int mes;
7 int ano;
8 } data, prox;
9 scanf("%d/%d/%d", &data.dia, &data.mes, &data.ano);
10 prox = data;
11 prox.dia++;
12 if (prox.dia > 31 ||
13 (prox.dia == 31 && (prox.mes == 4 || prox.mes == 6 ||
14 prox.mes == 9 || prox.mes == 11)) ||
15 (prox.dia >= 30 && prox.mes == 2) ||
16 (prox.dia == 29 && prox.mes == 2 && (prox.ano % 400 != 0 &&
17 prox.ano % 4 != 0)) ) {
18 prox.dia = 1;
19 prox.mes++;
20 if (prox.mes > 12) {
21 prox.mes = 1;
22 prox.ano++;
23 }
24 }
25 printf("%02d/%02d/%02d\n", prox.dia, prox.mes, prox.ano);
26 return 0;
27 }

Uma maneira provavelmente mais simples de computar essa diferena usar a fr-
mula 27.1 para calcular um nmero de dias N baseado em uma data:
   
1461 f (ano, ms) 153 g(ms)
N= + + dia (27.1)
4 5

onde 
ano 1, se ms 6 2 ,
f (ano, ms) =
ano, caso contrrio
e 
ms + 13, se ms 6 2 ,
g(ms) =
ms + 1, caso contrrio .

Podemos calcular o valor N1 para a primeira data informada, o valor N2 para a segunda
data informada e a diferena N2 N1 o nmero de dias decorridos entre estas duas
datas informadas.

FACOM UFMS
27.4 E XEMPLO 158

27.4 Seja N computado como na equao 27.1. Ento, o valor

D = (N 621049) mod 7

um nmero entre 0 e 6 que representa os dias da semana, de domingo a sbado. Por


exemplo, para a data de 21/06/2007 temos
   
1461 f (2007, 6) 153 g(6)
N= + + 21
4 5
   
1461 2007 153 7
= + + 21
4 5
= 733056 + 214 + 21
= 733291

e ento

D = (733291 621049) mod 7


= 112242 mod 7
=4.

Dada uma data fornecida pelo usurio no formato dd/mm/aaaa, determine o dia da
semana para esta data.

FACOM UFMS
A ULA 28

V ETORES , MATRIZES E REGISTROS

Nesta aula, vamos trabalhar com uma extenso natural do uso de registros, declarando e
usando variveis compostas homogneas de registros como, por exemplo, vetores de registros
ou matrizes de registros. Por outro lado, estudaremos tambm registros contendo variveis
compostas homogneas como campos. Veremos que combinaes dessas declaraes tambm
podem ser usadas de modo a representar e organizar os dados em memria e solucionar pro-
blemas.

28.1 Vetores de registros

Como vimos nas aulas 19 e 24, podemos declarar variveis compostas homogneas a partir
de qualquer tipo bsico ou ainda de um tipo definido pelo(a) programador(a). Ou seja, pode-
mos declarar, por exemplo, um vetor do tipo inteiro, uma matriz do tipo ponto flutuante, uma
varivel composta homognea de k dimenses do tipo caracter e etc. A partir de agora, podere-
mos tambm declarar variveis compostas homogneas, de quaisquer dimenses, de registros.
Por exemplo, podemos declarar um vetor com identificador cronometro como mostrado a
seguir:

struct {
int horas;
int minutos;
int segundos;
} cronometro[10];

ou ainda declarar uma matriz com identificador agenda como abaixo:

struct {
int horas;
int minutos;
int segundos;
} agenda[10][30];

159
28.1 V ETORES DE REGISTROS 160

O vetor cronometro declarado anteriormente contm 10 compartimentos de memria,


sendo que cada um deles do tipo registro. Cada registro contm, por sua vez, os campos
horas , minutos e segundos , do tipo inteiro. J a matriz com identificador agenda uma
matriz contendo 10 linhas e 30 colunas, onde cada compartimento contm um registro com os
mesmos campos do tipo inteiro horas , minutos e segundos . Essas duas variveis com-
postas homogneas tambm poderiam ter sido declaradas em conjunto, como apresentado a
seguir:

struct {
int horas;
int minutos;
int segundos;
} cronometro[10], agenda[10][30];

A declarao do vetor cronometro tem um efeito na memria que pode ser ilustrado como
na figura 28.1.

0 1 2 9

cronometro

horas
minutos
segundos

Figura 28.1: Efeito da declarao do vetor cronometro na memria.

Atribuies de valores do tipo inteiro aos campos do registro do primeiro compartimento


deste vetor cronometro podem ser feitas da seguinte forma:

cronometro[0].horas = 20;
cronometro[0].minutos = 39;
cronometro[0].segundos = 18;

Alm disso, como j mencionamos na aula 27, podemos fazer a atribuio direta de registros
para registros. Assim, por exemplo, se declaramos as variveis cronometro e aux como
abaixo:

FACOM UFMS
28.1 V ETORES DE REGISTROS 161

struct {
int horas;
int minutos;
int segundos;
} cronometro[10], aux;

ento as atribuies a seguir so vlidas e realizam a troca dos contedos das posies i e j
do vetor de registros cronometro :

aux = cronometro[i];
cronometro[i] = cronometro[j];
cronometro[j] = aux;

importante observar novamente que todos os campos de um registro so atualizados


automaticamente quando da atribuio de um registro a outro registro, no havendo a neces-
sidade da atualizao campo a campo. Ou seja, podemos fazer:

cronometro[i] = cronometro[j];

ao invs de

cronometro[i].horas = cronometro[j].horas;
cronometro[i].minutos = cronometro[j].minutos;
cronometro[i].segundos = cronometro[j].segundos;

As duas formas acima esto corretas, apesar da primeira forma ser mais prtica e direta.
Nesse contexto, considere o seguinte problema:

Dado um nmero inteiro n, com 1 6 n 6 100, e n medidas de tempo dadas em


horas, minutos e segundos, distintas duas a duas, ordenar essas medidas de tempo
em ordem crescente.

O programa 28.1 soluciona o problema acima usando o mtodo de ordenao das trocas
sucessivas ou mtodo da bolha.

FACOM UFMS
28.1 V ETORES DE REGISTROS 162

Programa 28.1: Um programa usando um vetor de registros.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j, n;
5 struct {
6 int horas;
7 int minutos;
8 int segundos;
9 } cron[100], aux;
10 printf("Informe a quantidade de medidas de tempo: ");
11 scanf("%d", &n);
12 printf("\n");
13 for (i = 0; i < n; i++) {
14 printf("Informe uma medida de tempo (hh:mm:ss): ");
15 scanf("%d:%d:%d", &cron[i].horas, &cron[i].minutos, &cron[i].segundos);
16 }
17 for (i = n-1; i > 0; i--)
18 for (j = 0; j < i; j++)
19 if (cron[j].horas > cron[j+1].horas) {
20 aux = cron[j];
21 cron[j] = cron[j+1];
22 cronometro[j+1] = aux;
23 }
24 else if (cron[j].horas == cron[j+1].horas)
25 if (cron[j].minutos > cron[j+1].minutos) {
26 aux = cron[j];
27 cron[j] = cron[j+1];
28 cron[j+1] = aux;
29 }
30 else if (cron[j].minutos == cron[j+1].minutos)
31 if (cron[j].segundos > cron[j+1].segundos) {
32 aux = cron[j];
33 cron[j] = cron[j+1];
34 cron[j+1] = aux;
35 }
36 printf("\nHorrios em ordem crescente\n");
37 for (i = 0; i < n; i++)
38 printf("%d:%d:%d\n", cron[i].horas, cron[i].minutos, cron[i].segundos);
39 return 0;
40 }

FACOM UFMS
28.2 R EGISTROS CONTENDO VARIVEIS COMPOSTAS 163

28.2 Registros contendo variveis compostas

Na aula 27 definimos registros que continham campos de tipos bsicos ou de tipos definidos
pelo(a) programador(a). Na seo 28.1, estudamos vetores no mais de um tipo bsico, mas de
registros, isto , vetores contendo registros. Podemos tambm declarar registros que contm
variveis compostas como campos. Um exemplo bastante comum a declarao de um vetor
de caracteres, ou uma cadeia de caracteres, dentro de um registro:

struct {
int dias;
char nome[3];
} mes;

A declarao do registro mes permite o armazenamento de um valor do tipo inteiro no


campo dias , que pode representar, por exemplo, a quantidade de dias de um ms, e de trs
valores do tipo caracter no campo vetor nome , que podem representar os trs primeiros ca-
racteres do nome de um ms do ano. Assim, se declaramos as variveis mes e aux como a
seguir:

struct {
int dias,
char nome[3];
} mes, aux;

podemos fazer a seguinte atribuio vlida ao registro mes :

mes.dias = 31;
mes.nome[0] = J;
mes.nome[1] = a;
mes.nome[2] = n;

Os efeitos da declarao da varivel mes na memria e da atribuio de valores acima podem


ser vistos na figura 28.2.
importante salientar mais uma vez que as regras para cpias de registros permanecem as
mesmas, mesmo que um campo de um desses registros seja uma varivel composta. Assim, a
cpia abaixo perfeitamente vlida:

aux = mes;

FACOM UFMS
28.2 R EGISTROS CONTENDO VARIVEIS COMPOSTAS 164

nome
dias

mes 31 J a n

0 1 2

Figura 28.2: Varivel mes na memria.

Suponha agora que temos o seguinte problema.

Dadas duas descries de tarefas e seus horrios de incio no formato hh:mm:ss,


escreva um programa que verifica qual das duas tarefas ser iniciada antes. Consi-
dere que a descrio de uma tarefa tenha no mximo 50 caracteres.

Uma soluo para esse problema apresentada no programa 28.2.

Programa 28.2: Exemplo do uso de um vetor como campo de um registro.

1 #include <stdio.h>
2 int main(void)
3 {
4 int tempo1, tempo2;
5 struct {
6 int horas;
7 int minutos;
8 int segundos;
9 char descricao[51];
10 } t1, t2;
11 printf("Informe a descrio da primeira atividade: ");
12 scanf("%[^\n]", t1.descricao);
13 printf("Informe o horrio de incio dessa atividade (hh:mm:ss): ");
14 scanf("%d:%d:%d", &t1.horas, &t1.minutos, &t1.segundos);
15 printf("Informe a descrio da segunda atividade: ");
16 scanf(" %[^\n]", t2.descricao);
17 printf("Informe o horrio de incio dessa atividade (hh:mm:ss): ");
18 scanf("%d:%d:%d", &t2.horas, &t2.minutos, &t2.segundos);
19 tempo1 = t1.horas * 3600 + t1.minutos * 60 + t1.segundos;
20 tempo2 = t2.horas * 3600 + t2.minutos * 60 + t2.segundos;
21 if (tempo1 <= tempo2)
22 printf("%s ser realizada antes de %s\n", t1.descricao, t2.descricao);
23 else
24 printf("%s ser realizada antes de %s\n", t2.descricao, t1.descricao);
25 return 0;
26 }

FACOM UFMS
28.2 R EGISTROS CONTENDO VARIVEIS COMPOSTAS 165

Exerccios

28.1 Dadas n datas, com 1 6 n 6 100, e uma data de referncia d, verifique qual das n datas
mais prxima data d.
Podemos usar a frmula do exerccio 27.3 para solucionar esse exerccio mais facilmente.

28.2 Dadas trs fichas de produtos de um supermercado, contendo as informaes de seu


cdigo, sua descrio com at 50 caracteres e seu preo unitrio, orden-las em ordem
alfabtica de seus nomes.

28.3 Dados um nmero inteiro n > 1 e mais n fichas de doadores de um banco de sangue, con-
tendo o cdigo do doador, seu nome, seu tipo sangneo e seu fator Rhesus, escreva um
programa que lista os doadores do banco das seguintes formas: (i) em ordem crescente
de cdigos de doadores; (ii) em ordem alfabtica de nomes de doadores e (iii) em ordem
alfabtica de tipos sangneos e fatores Rhesus.

FACOM UFMS
28.2 R EGISTROS CONTENDO VARIVEIS COMPOSTAS 166

Programa 28.3: Soluo do exerccio 28.1.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int dif[MAX], menor;
6 struct {
7 int dia;
8 int mes;
9 int ano;
10 } d, data[MAX];
11 printf("Informe uma data de referncia (dd/mm/aa): ");
12 scanf("%d/%d/%d", &d.dia, &d.mes, &d.ano);
13 scanf("%d", &n);
14 printf("Informe a quantidade de datas: ");
15 scanf("%d", &n);
16 for (i = 0; i < n; i++) {
17 printf("[%03d] Informe uma data (dd/mm/aa): ", i+1);
18 scanf("%d/%d/%d", &data[i].dia, &data[i].mes, &data[i].ano);
19 }
20 if (d.mes <= 2)
21 N1 = (1461*(d.ano-1))/4 + ((153*d.mes+13)/5) + d.dia;
22 else
23 N1 = (1461*d.ano)/4 + ((153*d.mes+1)/5) + d.dia;
24 for (i = 0; i < n; i++) {
25 if (data[i].mes <= 2)
26 N2 = (1461*(data[i].ano-1))/4 + ((153*data[i].mes+13)/5) + data[i].dia;
27 else
28 N2 = (1461*data[i].ano)/4 + ((153*data[i].mes+1)/5) + data[i].dia;
29 if (N1 >= N2)
30 dif[i] = N1 - N2;
31 else
32 dif[i] = N2 - N1;
33 }
34 menor = 0;
35 for (i = 1; i < n; i++)
36 if (dif[i] < dif[menor])
37 menor = i;
38 printf("Data mais prxima de %2d/%2d/%2d %2d/%2d/%d\n",
39 d.dia, d.mes, d.ano, data[menor].dia, data[menor].mes, data[menor].ano);
40 return 0;
41 }

FACOM UFMS
A ULA 29

R EGISTROS COM REGISTROS

Assim como na aula 28, a aula de hoje tambm trata da declarao de registros mais com-
plexos, mas agora seus campos podem ser variveis no somente de tipos bsicos, de tipos
definidos pelo usurio, ou ainda variveis compostas homogneas, mas tambm de variveis
compostas heterogneas ou registros.
Uma varivel que um registro e que, por sua vez, contm como campos um ou mais
registros, fornece ao() programador(a) uma liberdade e flexibilidade na declarao de qual-
quer estrutura para armazenamento de informaes que lhe seja necessria na soluo de um
problema computacional, especialmente daqueles problemas mais complexos.

29.1 Registros contendo registros

importante observar que a declarao de um registro pode conter um outro registro como
um campo, em seu interior. Ou seja, uma varivel composta heterognea pode conter campos
de tipos bsicos, iguais ou distintos, campos de tipos definidos pelo usurio, campos que so
variveis compostas homogneas, ou ainda campos que se constituem tambm como variveis
compostas heterogneas.
Como um exemplo, podemos declarar uma varivel do tipo registro com identificador
estudante contendo um campo do tipo inteiro rga , um campo do tipo vetor de caracteres
nome , com 51 posies e um campo do tipo registro nascimento , contendo por sua vez trs
campos do tipo inteiro com identificadores dia , mes e ano . Ou seja, o registro estudante
pode ser declarado como a seguir:

struct {
int rga;
char nome[51];
struct {
int dia;
int mes;
int ano;
} nascimento;
} estudante;

A figura 29.1 mostra o efeito da declarao da varivel estudante na memria.

167
29.2 E XEMPLO 168

estudante
dia mes ano

rga nome nasc

0 1 48 49

Figura 29.1: Efeitos da declarao do registro estudante na memria.

Observe que a varivel estudante um registro que mistura campos de um tipo bsico
o campo rga que do tipo inteiro , um campo que um vetor de um tipo bsico o campo
nome do tipo vetor de caracteres e um campo do tipo registro o campo nascimento do
tipo registro, contendo, por sua vez, trs campos do tipo inteiro: os campos dia , mes e ano .
Um exemplo do uso do registro estudante e de seus campos dado a seguir atravs de
atribuies de valores a cada um de seus campos:

estudante.rga = 200790111;
estudante.nome[0] = J;
estudante.nome[1] = o;
estudante.nome[2] = s;
estudante.nome[3] = e;
estudante.nome[4] = \0;
estudante.nasc.dia = 22;
estudante.nasc.mes = 2;
estudante.nasc.ano = 1988;

29.2 Exemplo

O programa 29.1 implementa uma agenda de telefones simples, ilustrando um exemplo de


uso de um vetor que contm registros, onde cada registro contm tambm um registro como
campo. A agenda contm trs informaes para cada amigo(a) includo(a): nome, telefone e
data de aniversrio. Alm de armazenar essa agenda na memria, em um vetor de registros, o
programa ainda faz com que uma data seja fornecida pelo(a) usurio(a) e todas as pessoas que
fazem aniversrio nessa data so mostradas na sada.
Podemos destacar novamente, assim como fizemos nas aulas 27 e 28, que registros podem
ser atribudos automaticamente para registros, no havendo necessidade de faz-los campo a
campo. Por exemplo, se temos declarados os registros:

FACOM UFMS
29.2 E XEMPLO 169

Programa 29.1: Um exemplo de uso de registros contendo registros.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, n;
5 struct {
6 char nome[51];
7 int telefone;
8 struct {
9 int dia;
10 int mes;
11 int ano;
12 } aniver;
13 } agenda[100];
14 struct {
15 int dia;
16 int mes;
17 } data;
18 printf("Informe a quantidade de amigos: ");
19 scanf("%d", &n);
20 for(i = 0; i < n; i++) {
21 printf("\nAmigo(a): %3d\n", i+1);
22 printf(" Nome : ");
23 scanf(" %[^\n]", agenda[i].nome);
24 printf(" Telefone : ");
25 scanf("%d", &agenda[i].telefone);
26 printf(" Aniversrio: ");
27 scanf("%d/%d/%d", &agenda[i].aniver.dia, &agenda[i].aniver.mes,
28 &agenda[i].aniver.ano);
29 }
30 printf("\nInforme uma data (dd/mm): ");
31 scanf("%d/%d", &data.dia, &data.mes);
32 i = 0;
33 while (i < n) {
34 if (agenda[i].aniver.dia == data.dia && agenda[i].aniver.mes == data.mes)
35 printf("%-50s %8d\n", agenda[i].nome, agenda[i].telefone);
36 i++;
37 }
38 return 0;
39 }

FACOM UFMS
29.2 E XEMPLO 170

struct {
int rga;
char nome[51];
struct {
int dia;
int mes;
int ano;
} nascimento;
} estudante1, estudante2, aux;

ento, as atribuies a seguir so perfeitamente vlidas e realizam corretamente a troca de


contedos dos registros estudante1 e estudante2 :

aux = estudante1;
estudante1 = estudante2;
estudante2 = aux;

Exerccios

29.1 Suponha que em um determinado galpo estejam armazenados os materiais de constru-


o de uma loja que vende tais materiais. Este galpo quadrado e mede 2020 = 400m2
e a cada 22 = 4m2 h uma certa quantidade de um material armazenado. O encarregado
do setor tem uma tabela de 10 linhas por 10 colunas, representando o galpo, contendo,
em cada clula, o cdigo do material, sua descrio e sua quantidade. O cdigo do ma-
terial um nmero inteiro, a descrio o material contm no mximo 20 caracteres e a
quantidade do material um nmero de ponto flutuante.
Escreva um programa que receba as informaes armazenadas na tabela do encarrerado
e liste cada material e a sua quantidade disponvel no galpo. Observe que um mesmo
material pode encontrar-se em mais que um local no galpo.

29.2 Escreva um programa que receba o nome, o telefone e a data de nascimento de n pessoas,
com 1 6 n 6 100, e implemente uma agenda telefnica com duas listagens possveis: (i)
uma lista dos nomes e telefones das pessoas em ordem alfabtica de nomes e (ii) uma lista
dos nomes e telefones das pessoas em ordem de datas de aniversrios das pessoas.

FACOM UFMS
29.2 E XEMPLO 171

Programa 29.2: Soluo do exerccio 29.1.

1 #include <stdio.h>
2 #define MAX 10
3 int main(void)
4 {
5 int i, j, k, l, m;
6 float soma;
7 struct {
8 int codigo;
9 char descricao[21];
10 float quant;
11 int marca;
12 } galpao[MAX][MAX], resumo[MAX*MAX];
13 for(i = 0; i < MAX; i++)
14 for (j = 0; j < MAX; j++) {
15 scanf("%d %[\^n]%f", &galpao[i][j].codigo, galpao[i][j].descricao,
16 &galpao[i][j].quant);
17 galpao[i][j].marca = 0;
18 }
19 for (m = 0, i = 0; i < MAX; i++)
20 for (j = 0; j < MAX; j++)
21 if (!galpao[i][j].marca) {
22 if (j < MAX - 1) {
23 k = i;
24 l = j + 1;
25 }
26 else {
27 k = i + 1;
28 l = 0;
29 }
30 soma = galpao[i][j].quant;
31 for ( ; k < MAX; k++, l = 0)
32 for ( ; l < MAX; l++)
33 if (galpao[k][l].codigo == galpao[i][j].codigo) {
34 soma = soma + galpao[k][l].quant;
35 galpao[k][l].marca = 1;
36 }
37 resumo[m] = galpao[i][j];
38 resumo[m].quant = soma;
39 m++;
40 galpao[i][j].marca = 1;
41 }
42 for (i = 0; i < m; i++)
43 printf("%d %s %5.2f\n", resumo[i].codigo, resumo[i].descricao,
44 resumo[i].quant);
45 return 0;
46 }

FACOM UFMS
A ULA 30

U NIES E ENUMERAES

Complementando o contedo apresentado at aqui sobre variveis compostas, a linguagem


C ainda oferece dois tipos delas: as unies e as enumeraes. Uma unio um registro que
armazena apenas um dos valores de seus campos e, portanto, apresenta economia de espao.
Uma enumerao ajuda o(a) programador(a) a declarar e usar uma varivel que representa
intuitivamente uma seqncia de nmeros naturais.

30.1 Unies

Como os registros, as unies tambm so compostas por um ou mais campos, possivel-


mente de tipos diferentes. No entanto, o compilador aloca espao suficiente apenas para o
maior dos campos de uma unio. Esses campos compartilham ento o mesmo espao na me-
mria. Dessa forma, a atribuio de um valor a um dos campos da unio tambm altera os
valores dos outros campos.
Vamos declarar a unio u com dois campos, um do tipo caractere com sinal e outro do tipo
inteiro com sinal, como abaixo:

union {
char c;
int i;
} u;

Observe que a declarao de uma unio idntica declarao de um registro que apren-
demos nas aulas 27, 28 e 29, a menos da palavra reservada union que substitui a palavra
reservada struct . Por exemplo, podemos declarar o registro r com os mesmos dois campos
da unio u como a seguir:

struct {
char c;
int i;
} r;

172
30.1 U NIES 173

A diferena entre a unio u e o registro r concentra-se apenas no fato que os campos de


u esto localizados no mesmo endereo de memria enquanto que os campos de r esto loca-
lizados em endereos diferentes. Considerando que um caractere ocupa um byte na memria
e um nmero inteiro ocupa quatro bytes, a figura 30.1 apresenta uma ilustrao do que ocorre
na memria na declarao das variveis u e r .

c i

Figura 30.1: Unio e registro representados na memria.

No registro r , os campos c e i ocupam diferentes posies da memria. Assim, o total


de memria necessrio para armazenamento do registro r na memria de cinco bytes. Na
unio u , os campos c e i compartilham a mesma posio da memria isto , os campos
c e i de u tm o mesmo endereo de memria e, portanto, o total de memria necessrio
para armazenamento de u de quatro bytes.
Campos de uma unio so acessados da mesma forma que os campos de um registro. Por
exemplo, podemos fazer a atribuio a seguir:

u.c = a;

Tambm podemos fazer a atribuio de um nmero inteiro para o outro campo da unio
como apresentado abaixo:

u.i = 3894;

Como o compilador superpe valores nos campos de uma unio, a alterao de um dos
campos implica na alterao de qualquer valor previamente armazenado nos outros campos.
Assim, se armazenamos um valor no campo u.c , qualquer valor previamente armazenado
no campo u.i ser perdido. Do mesmo modo, alterar o valor do campo u.i altera o valor
do campo u.c . Assim, podemos pensar na unio u como um local na memria para arma-
zenar um caractere em u.c OU ou um nmero em u.i , mas no ambos. O registro r pode
armazenar valores em r.c E em r.i .

FACOM UFMS
30.1 U NIES 174

Como com registros, unies podem ser copiadas diretamente usando o comando de atri-
buio = , sem a necessidade de faz-lo campo a campo. Declaraes e inicializaes tambm
so feitas similarmente. No entanto, somente o primeiro campo de uma unio pode ter um
valor atribudo no inicializador. Por exemplo, podemos fazer

union {
char c;
int i;
} u = {\0};

Usamos unies freqentemente como uma forma para economizar espao em registros.
Tambm, usamos unies quando queremos criar estruturas de armazenamento de dados que
contenham uma mistura de diferentes tipos de dados.
Como um exemplo do segundo caso, suponha que temos uma coleo de nmeros inteiros
e de ponto flutuante que queremos armazenar em um nico vetor. Como os elementos de um
vetor tm de ser do mesmo tipo, devemos usar unies para implementar um vetor com essa
caracterstica. A declarao de um vetor como esse dada a seguir:

union {
int i;
double d;
} vetor[100];

Cada compartimento do vetor pode armazenar um valor do tipo int ou um valor do tipo
double , o que possibilita armazenar uma mistura de valores do tipo int e double no
vetor . Por exemplo, para as atribuies abaixo atribuem nmeros inteiros e nmeros de
ponto flutuante para as posies 0 e 1 do vetor :

vetor[0].i = 5;
vetor[1].d = 7.647;

Suponha que temos ento o seguinte problema que precisamos construir um programa para
solucion-lo:

Dada uma seqncia n de nmeros, inteiros e reais, com 1 6 n 6 100, realizar a


adio de todos os nmeros inteiros e o produto de todos os nmeros reais.

O programa 30.1 mostra um exemplo do uso de um vetor de registros onde um de seus


campos uma unio.

FACOM UFMS
30.1 U NIES 175

Programa 30.1: Um exemplo de uso de registros e unies.

1 #include <stdio.h>
2 #define TIPO_INT 0
3 #define TIPO_DOUBLE 1
4 #define MAX 100
5 int main(void)
6 {
7 int i, n, soma, inteiro, real;
8 double produto;
9 struct {
10 int tipo;
11 union {
12 int i;
13 double d;
14 } u;
15 } numero[MAX];
16 printf("Informe n: ");
17 scanf("%d", &n);
18 for (i = 0; i < n; i++) {
19 printf("Informe o tipo do nmero (0: inteiro, 1: real): ");
20 scanf("%d", &numero[i].tipo);
21 printf("Informe o nmero: ");
22 if (numero[i].tipo == TIPO_INT)
23 scanf("%d", &numero[i].u.i);
24 else
25 scanf("%lf", &numero[i].u.d);
26 }
27 soma = 0;
28 produto = 1.0;
29 inteiro = 0;
30 real = 0;
31 for (i = 0; i < n; i++)
32 if (numero[i].tipo == TIPO_INT) {
33 soma = soma + numero[i].u.i;
34 inteiro = 1;
35 }
36 else {
37 produto = produto * numero[i].u.d;
38 real = 1;
39 }
40 if (inteiro)
41 printf("Soma dos nmeros inteiros: %d\n", soma);
42 else
43 printf("No h nmeros inteiros na entrada\n");
44 if (real)
45 printf("Produto dos nmeros reais: %g\n", produto);
46 else
47 printf("No h nmeros reais na entrada\n");
48 return 0;
49 }

FACOM UFMS
30.2 E NUMERAES 176

30.2 Enumeraes

Muitas vezes precisamos de variveis que contero, durante a execuo de um programa,


somente um pequeno conjunto de valores. Por exemplo, variveis lgicas ou booleanas deve-
ro conter somente dois valores possveis: verdadeiro e falso. Uma varivel que armazena
os naipes das cartas de um baralho dever conter apenas quatro valores potenciais: paus,
copas, espadas e ouros. Uma maneira natural de tratar esses valores atravs da de-
clarao de uma varivel do tipo inteiro que pode armazenar valores que representam esses
naipes. Por exemplo, 0 representa o naipe paus, 1 o naipe copas e assim por diante. As-
sim, podemos por exemplo declarar uma varivel naipe e atribuir valores a essa varivel da
seguinte forma:

int n;
n = 0;

Apesar dessa tcnica funcionar, algum que precisa compreender esse programa pode no
ser capaz de saber que a varivel n pode assumir apenas quatro valores durante a execuo
do programa e o significado do valor 0 no imediatamente aparente.
Uma estratgia melhor que a anterior o uso de macros para definir um tipo naipe e
tambm para definir nomes para os diversos naipes existentes. Ento, podemos fazer:

#define NAIPE int


#define PAUS 0
#define COPAS 1
#define ESPADAS 2
#define OUROS 3

O exemplo anterior fica ento bem mais fcil de ler:

NAIPE n;
n = PAUS;

Apesar de melhor, essa estratgia ainda possui restries. Por exemplo, algum que neces-
sita compreender o programa no tem a viso imediata que as macros representam valores do
mesmo tipo. Alm disso, se o nmero de valores possveis um pouco maior, a definio de
uma macro para cada valor pode se tornar uma tarefa tediosa. Por fim, os identificadores das
macros sero removidos pelo pr-processador e substitudos pelos seus valores respectivos e
isso significa que no estaro disponveis para a fase de depurao do programa. Depurao
uma atividade muito til para programao a medida que nossos programas ficam maiores e
mais complexos, como veremos na aula 31.

FACOM UFMS
30.2 E NUMERAES 177

A linguagem C possui um tipo especial especfico para variveis que armazenam um con-
junto pequeno de possveis valores. Um tipo enumerado um tipo cujos valores so listados
ou enumerados pelo(a) programador(a) que deve criar um nome, chamado de uma constante
da enumerao, para cada um dos valores. O exemplo a seguir enumera os valores PAUS ,
COPAS , ESPADAS e OUROS que podem ser atribudos s variveis n1 e n2 :

enum {PAUS, COPAS, ESPADAS, OUROS} n1, n2;


n1 = PAUS;
n2 = n1;

Apesar de terem pouco em comum com registros e unies, as enumeraes so declaradas


de modo semelhante. Entretanto, diferentemente dos campos dos registros e unies, os nomes
das constantes da enumerao devem ser diferentes de outros nomes de outras enumeraes
declaradas.
A linguagem C trata variveis que so enumeraes e constantes da enumerao como
nmeros inteiros. Por padro, o compilador atribui os inteiros 0, 1, 2, . . . para as constantes da
enumerao na declarao de uma varivel qualquer do tipo enumerao. No exemplo acima,
PAUS , COPAS , ESPADAS e OUROS representam os valores 0, 1, 2 e 3, respectivamente.
Podemos escolher valores diferentes dos acima para as constantes da enumerao. Por
exemplo, podemos fazer a seguinte declarao:

enum {PAUS = 1, COPAS = 2, ESPADAS = 3, OUROS = 4} n;

Os valores das constantes da enumerao podem ser valores inteiros arbitrrios, listados
sem um ordem particular, como por exemplo:

enum {DCT = 17, DCH = 2, DEC = 21, DMT = 6} depto;

Se nenhum valor especificado para uma constante da enumerao, o valor atribudo


constante um maior que o valor da constante anterior. A primeira constante da enumerao
tem o valor padro 0 (zero). Na enumerao a seguir, PRETO tem o valor 0, CINZA_CLARO tem
o valor 7, CINZA_ESCURO temo o valor 8 e BRANCO tem o valor 15:

enum {PRETO, CINZA_CLARO = 7, CINZA_ESCURO, BRANCO = 15} coresEGA;

Como as constantes de uma enumerao so, na verdade, nmeros inteiros, a linguagem C


permite que um(a) programador(a) use-as em expresses com inteiros. Por exemplo, o trecho
de cdigo a seguir vlido em um programa:

FACOM UFMS
30.2 E NUMERAES 178

int i;
enum {PAUS, COPAS, ESPADAS, OUROS} n;
i = COPAS;
n = 0;
n++;
i = n + 2;

Apesar da convenincia de podermos usar uma constante de uma enumerao como um


nmero inteiro, perigoso usar um nmero inteiro como um valor de uma enumerao. Por
exemplo, podemos acidentalmente armazenar o nmero 4, que no corresponde a qualquer
naipe, na varivel n .
Vejamos o programa 30.2 com um exemplo do uso de enumeraes, onde contamos o n-
mero de cartas de cada naipe de um conjunto de cartas fornecido como entrada. Uma entrada
uma cadeia de caracteres, dada como 4pAp2c1o7e, onde p significa o naipe de paus, c
copas, e espadas e o ouros.

Exerccios

30.1 Escreva um programa que receba uma coleo de n nmeros, com 1 6 n 6 100, que
podem ser inteiros pequenos de 1 (um) byte, inteiros maiores de 4 (quatro) bytes ou n-
meros reais, e receba ainda um nmero x e verifique se x pertence a esse conjunto. Use
uma forma de armazenamento que minimize a quantidade de memria utilizada. Use
um vetor de registros tal que cada clula contm um campo que uma enumerao, para
indicar o tipo do nmero armazenado nessa clula, e um campo que uma unio, para
armazenar um nmero.

30.2 Dados os nmeros inteiros m e n e uma matriz A de nmeros inteiros, com 1 6 m, n 6


100, calcular a quantidade de linhas que contm o elemento 0 (zero) e a quantidade de
colunas. Use uma varivel indicadora de passagem, lgica, declarada como uma enume-
rao.

FACOM UFMS
30.2 E NUMERAES 179

Programa 30.2: Uso de enumeraes.

1 #include <stdio.h>
2 int main(void)
3 {
4 char entrada[109];
5 int i, j, conta[4] = {0};
6 struct {
7 char valor[2];
8 enum {PAUS, COPAS, ESPADAS, OUROS} naipe;
9 } mao[52];
10 printf("Informe uma mo: ");
11 scanf("%s", entrada);
12 for (i = 0, j = 0; entrada[i]; j++) {
13 mao[j].valor[0] = entrada[i];
14 i++;
15 if (entrada[i] == 0) {
16 mao[j].valor[1] = entrada[i];
17 i++;
18 }
19 switch (entrada[i]) {
20 case p:
21 mao[j].naipe = PAUS;
22 break;
23 case c:
24 mao[j].naipe = COPAS;
25 break;
26 case e:
27 mao[j].naipe = ESPADAS;
28 break;
29 case o:
30 mao[j].naipe = OUROS;
31 break;
32 default:
33 break;
34 }
35 i++;
36 }
37 for (j--; j >= 0; j--)
38 conta[mao[j].naipe]++;
39 printf("Cartas:\n");
40 printf(" %d de paus\n", conta[PAUS]);
41 printf(" %d de copas\n", conta[COPAS]);
42 printf(" %d de espadas\n", conta[ESPADAS]);
43 printf(" %d de ouros\n", conta[OUROS]);
44 return 0;
45 }

FACOM UFMS
A ULA 31

D EPURAO DE PROGRAMAS

medida que temos resolvido problemas maiores e mais complexos, natural e conseqen-
temente nossos programas tm ganhado complexidade e extenso. Uso correto de estruturas
de programao, identificadores de variveis significativos, indentao, comentrios, tudo isso
tem sido feito na tentativa de escrever programas corretamente. Ou seja, nosso desejo sempre
escrever programas eficientes, coesos e corretos.
Infelizmente, nem sempre isso possvel. Por falta de ateno, de experincia, de disciplina,
ou mesmo pela complexidade do problema que precisamos resolver, no incomum o desen-
volvimento de um programa que contm erros. Nesta aula, aprenderemos a usar um poderoso
depurador de programas interativo, chamado GDB, como aliado na construo de programas
corretos. Veremos algumas de suas principais caractersticas, talvez as mais teis. Interessa-
dos(as) em mais informaes e caractersticas avanadas desta ferramenta devem consultar a
pgina e o manual da mesma.

31.1 Depurador GDB

Um depurador um programa que permite a um programador visualizar o que acontece


no interior de um outro programa durante sua execuo. Neste sentido, podemos dizer que
um depurador uma poderosa ferramenta de auxlio programao. O depurador GDB, do
Projeto GNU, projetado inicialmente por Richard Stallman1 , usado com freqncia para de-
purar programas compilados com o compilador GCC, tambm do projeto GNU. Para auxiliar
um programador na procura por erros em um programa, o GDB pode agir de uma das quatro
principais formas abaixo:

iniciar a execuo de um programa, especificando qualquer coisa que possa afetar seu
comportamento;

interromper a execuo de um programa sob condies estabelecidas;

examinar o que aconteceu quando a execuo do programa foi interrompida;

modificar algo no programa, permitindo que o programador corrija um erro e possa con-
tinuar a investigar outros erros no mesmo programa.

O depurador GDB pode ser usado para depurar programas escritos nas linguagens de pro-
gramao C, C++, Objective-C, Modula-2, Pascal e Fortran.
1
Richard Matthew Stallman (), nascido nos Estados Unidos, fsico, ativista poltico e ativista de software.

180
31.2 P RIMEIRO CONTATO 181

GDB um software livre, protegido pela Licena Pblica Geral (GPL) da GNU . A GPL d a
seu usurio a liberdade para copiar ou adaptar um programa licenciado, mas qualquer pessoa
que faz uma cpia tambm deve ter a liberdade de modificar aquela cpia o que significa que
deve ter acesso ao cdigo fonte , e a liberdade de distribuir essas cpias. Empresas de software
tpicas usam o copyright para limitar a liberdade dos usurios; a Fundao para o Software Livre
(Free Software Foundation) usa a GPL para preservar essas liberdades. Fundamentalmente, a
Licena Pblica Geral (GPL) uma licena que diz que todas as pessoas tm essas liberdades e
que ningum pode restringi-las.
Para executar um programa com auxlio do depurador GDB, o programa fonte na lingua-
gem C deve ser compilado com o compilador GCC usando a diretiva de compilao ou opo
-g . Essa diretiva de compilao faz com que o compilador adicione informaes extras no
programa executvel que sero usadas pelo GDB.

31.2 Primeiro contato

De uma janela de comandos, voc pode chamar o depurador GDB, como a seguir:

$ gdb
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
(gdb)

Em geral, quando queremos depurar um programa, digitamos o comando gdb programa


em uma janela de comandos, onde programa um programa executvel compilado e gerado
pelo compilador gcc usando a diretiva de compilao -g . Dessa forma,

$ gdb ./consecutivos
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)

FACOM UFMS
31.3 S INTAXE DOS COMANDOS DO GDB 182

faz a chamada do depurador GDB com a carga do programa executvel consecutivos , que
foi previamente gerado pelo processo de compilao, usando o compilador GCC com a diretiva
de compilao -g .
Para sair do GDB e retornar janela de comandos basta digitar o comando quit , como a
seguir:

(gdb) quit
$

31.3 Sintaxe dos comandos do GDB

Um comando do GDB composto por uma linha de entrada, contendo o nome do comando
seguido de zero ou mais argumentos. O nome de um comando do GDB pode ser truncado, caso
essa abreviao no seja ambga. O usurio tambm pode usar a tecla Tab para fazer com que
o GDB preencha o resto do nome do comando ou para mostrar as alternativas disponveis, caso
exista mais que uma possibilidade. Uma linha em branco como entrada significa a solicitao
para que o GDB repita o ltimo comando fornecido pelo usurio.
Podemos solicitar ao GDB informaes sobre os seus comandos usando o comando help .
Atravs do comando help , que pode ser abreviado por h , sem argumentos, obtemos uma
pequena lista das classes de comandos do GDB:

List of classes of commands:

aliases -- Aliases of other commands


breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
(gdb)

FACOM UFMS
31.4 P ONTOS DE PARADA 183

Usando o nome de uma das classes gerais de ajuda como um argumento do comando
help , podemos obter uma lista dos comandos desta classe. Por exemplo,

(gdb) help status


Status inquiries.

List of commands:

info -- Generic command for showing things about the program being debugged
macro -- Prefix for commands dealing with C preprocessor macros
show -- Generic command for showing things about the debugger

Type "help" followed by command name for full documentation.


Command name abbreviations are allowed if unambiguous.
(gdb)

Tambm podemos usar o comando help diretamente com o nome de um comando que
queremos ajuda. Por exemplo,

(gdb) help help


Print list of commands.
(gdb)

Para colocar um programa executvel sob execuo no GDB necessrio usar o comando
run , que pode ser abreviado por r . Este programa executvel deve ter sido usado como um
parmetro na chamada do depurador ou ento deve ser carregado no GDB usando o comando
file ou exec-file .

31.4 Pontos de parada

Uma das principais vantagens no uso de depuradores de programas a possibilidade de


interromper a execuo de um programa antes de seu trmino. Sob o GDB, um programa pode
ser interrompido intencionalmente por diversas razes. Neste ponto de interrupo, podemos
examinar e modificar o contedo de variveis, criar outros pontos de parada, ou remover anti-
gos, e continuar a execuo do programa. Usualmente, as mensagens emitidas pelo GDB forne-
cem explicaes satisfatrias sobre o estado do programa, mas podemos tambm solicitar essas
informaes explicitamente a qualquer momento, atravs do comando info program . Esse
comando apresenta o estado do programa, sua identificao de processo, se est sob execuo
ou no e, neste ltimo caso, os motivos pelos quais sua execuo foi interrompida.
Um ponto de parada interrompe a execuo de um programa sempre que um determi-
nado ponto do cdigo fonte alcanado e/ou uma determinada condio verificada. No

FACOM UFMS
31.4 P ONTOS DE PARADA 184

GDB existem trs tipos de pontos de parada: breakpoints, watchpoints e catchpoints. Nesta aula
aprenderemos a usar dois deles, os dois primeiros.
O GDB atribui um nmero para cada ponto de parada quando da sua criao. Esses nme-
ros so nmeros inteiros sucessivos comeando com 1. Em diversos comandos para controle
de pontos de parada, podemos usar seu nmero seqencial para referenci-lo. Assim que
criado, um ponto de parada est habilitado. No entanto, durante a execuo de um programa,
podemos desabilit-lo de acordo com nossa convenincia.
Um breakpoint um ponto de parada que interrompe a execuo de um programa em um
dado ponto especificado do cdigo fonte. Seu uso mais freqente com o argumento do ponto
de execuo como sendo o nmero da linha do cdigo fonte. Por exemplo, breakpoint 5
estabelece um ponto de parada na linha 5 do programa. O ponto de parada interrom-
per o programa antes da execuo do cdigo localizado na linha especificada. O comando
breakpoint pode ser abreviado por br . Outra forma de uso de um breakpoint atravs
de sua associao a uma expresso lgica, estabelecendo um ponto de parada em uma dada
localizao do cdigo fonte, caso uma determinada condio seja satisfeita. Por exemplo,
breakpoint 13 if ant == prox estabelece um ponto de parada na linha 13, caso a expres-
so lgica descrita aps a palavra reservada if seja avaliada com valor verdadeiro. O ponto de
parada interromper ento o programa antes da execuo do cdigo na linha 22. Por outro lado,
um breakpoint tambm pode ser criado atravs do comando breakpoint identificador ,
onde o argumento identificador o nome de uma funo. At o momento, projetamos
programas apenas com a funo com identificador main para solucionar problemas propos-
tos, mas em breve projetaremos outras funes e este comando pode nos ajudar a adicionar
pontos de parada no incio de outras funes de um programa.
Podemos tambm usar um ponto de parada na execuo de um programa que suspende sua
execuo sempre que o valor de uma determinada expresso se modifica, sem ter de predizer
um ponto particular no cdigo fonte onde isso acontece. Esse ponto de parada chamado
de watchpoint do GDB. Por exemplo, watch iguais estabelece um ponto de parada que ir
suspender a execuo do programa quando o valor da expresso descrita que no caso contm
apenas a varivel iguais for modificado. O comando watch pode ser abreviado por wa .
O watchpoint s pode ser adicionado durante a execuo de um programa.
Como j mencionado, o GDB atribui um nmero seqencial, iniciando com 1, para cada
ponto de parada, quando da sua criao. Podemos imprimir uma tabela com informaes
bsicas sobre os pontos de parada associados ao programa em execuo com o comando
info breakpoints [n] , que pode ser abreviado por info break [n] . O argumento opci-
onal n mostra as informaes apenas do ponto de parada especificado. Por exemplo,

(gdb) info break


Num Type Disp Enb Address What
1 breakpoint keep y 0x080483f5 in main at consecutivos.c:5
breakpoint already hit 1 time
2 breakpoint keep y 0x0804848e in main at consecutivos.c:13
stop only if ant == prox
3 hw watchpoint keep y iguais
(gdb)

FACOM UFMS
31.5 P ROGRAMA FONTE 185

Muitas vezes, queremos eliminar um ponto de parada que j cumpriu o seu papel na de-
purao do programa e no queremos mais que o programa seja interrompido naquele ponto
previamente especificado. Podemos usar os comandos clear ou delete para remover um
ou mais pontos de parada de um programa. Os comandos clear e delete , com um ar-
gumento numrico, removem um ponto de parada especificado. Por exemplo, os comandos
clear 1 e delete 2 removem os pontos de parada identificados pelos nmeros 1 e 2, res-
pectivamente. A diferena entre esses comandos se d quando so usados sem argumentos:
o comando clear remove o ponto de parada da linha de cdigo em execuo, se existir al-
gum, e o comando delete sem argumentos remove todos os pontos de parada existentes no
programa.
Ao invs de remover definitivamente um ponto de parada, pode ser til para o processo de
depurao apenas desabilit-lo temporariamente e, se for necessrio, reabilit-lo depois. O co-
mando disable desabilita todos os pontos de parada de um programa. Podemos desabilitar
um ponto de parada especfico atravs do uso de seu nmero como argumento desse comando
disable . Por exemplo, disable 3 desabilita o ponto de parada de nmero 3. Podemos
tambm reabilitar um ponto de parada atravs do comando enable , que reabilita todos os
pontos de parada desabilitados anteriormente, ou enable n , que reabilita o ponto de parada
de nmero n .
Aps verificar o que aconteceu com o programa em um dado ponto de parada, podemos
retomar a execuo do programa a partir deste ponto de dois modos diferentes: atravs do
comando continue , que pode ser abreviado por c , ou atravs do comando step , que pode
ser abreviado por s . No primeiro comando, a execuo retomada e segue at o final do
programa ou at um outro ponto de parada ser encontrado. No segundo, apenas a prxima
sentena do programa em geral, a prxima linha executada e o controle do programa
novamente devolvido ao GDB. O comando step pode vir seguido por um argumento num-
rico, indicando quantas sentenas do cdigo fonte queremos progredir na sua execuo. Por
exemplo, step 5 indica que queremos executar as prximas 5 sentenas do cdigo. Caso um
ponto de parada seja encontrado antes disso, o programa interrompido e o controle repassado
ao GDB.
Comumente, adicionamos um ponto de parada no incio de uma funo onde acreditamos
que exista um problema, um erro, executamos o programa at que ele atinja este ponto de
parada e ento usamos o comando step nessa rea suspeita, examinando o contedo das
variveis envolvidas, at que o problema se revele.

31.5 Programa fonte

O GDB pode imprimir partes do cdigo fonte do programa sendo depurado. Quando um
programa tem sua execuo interrompida por algum evento, o GDB mostra automaticamente o
contedo da linha do cdigo fonte onde o programa parou. Para ver outras pores do cdigo,
podemos executar comandos especficos do GDB.
O comando list mostra ao() programador(a) algumas linhas do cdigo fonte carregado
previamente. Em geral, mostra 10 linhas em torno do ponto onde a execuo do programa
se encontra. Podemos explicitamente solicitar ao GDB que mostre um intervalo de linhas do
cdigo fonte, como por exemplo, list 1,14 . Neste caso, as primeiras 14 linhas do cdigo
fonte do programa carregado no GDB sero mostradas.

FACOM UFMS
31.6 V ERIFICAO DE DADOS 186

(gdb) list 1,14


1 #include <stdio.h>
2 int main(void)
3 {
4 int num, ant, prox, iguais;
5 printf("\nInforme um nmero: ");
6 scanf("%d", &num);
7 prox = num % 10;
8 iguais = 0;
9 while (num != 0 && !iguais) {
10 num = num / 10;
11 ant = prox;
12 prox = num % 10;
13 if (ant == prox)
14 iguais = 1;
(gdb)

31.6 Verificao de dados

Uma forma usual de examinar informaes em nosso programa atravs do comando


print , que pode ser abreviado por p . Esse comando avalia uma expresso e mostra seu
resultado. Por exemplo, print 2*a+3 mostra o resultado da avaliao da expresso 2*a+3 ,
supondo que a uma varivel do nosso programa. Sem argumento, esse comando mostra o
ltimo resultado apresentado pelo comando print .
Uma facilidade que o GDB nos oferece mostrar o valor de uma expresso freqente-
mente. Dessa forma, podemos verificar como essa expresso se comporta durante a execu-
o do programa. Podemos criar uma lista de impresso automtica que o GDB mostra cada
vez que o programa tem sua execuo interrompida. Podemos usar o comando display
para adicionar uma expresso lista de impresso automtica de um programa. Por exemplo,
display iguais adiciona a varivel iguais a essa lista e faz com que o GDB imprima o con-
tedo dessa verivel sempre que a execuo do programa for interrompida por algum evento.
Aos elementos inseridos nessa lista so atribudos nmeros inteiros consecutivos, iniciando
com 1. Para remover uma expresso dessa lista, basta executar o comando delete display
n, onde n o nmero da expresso que desejamos remover. Para visualizar todas as expresses
nesta lista necessrio executar o comando info display .
Todas as variveis internas de uma funo, e seus contedos, podem ser visualizados atra-
vs do comando info locals .

31.7 Alterao de dados durante a execuo

Podemos ainda alterar contedos de variveis durante a execuo de um programa no GDB.


Em qualquer momento da execuo, podemos usar o comando set var para alterar o con-

FACOM UFMS
31.8 R ESUMO DOS COMANDOS 187

tedo de uma varivel. Por exemplo, set var x = 2 modifica o contedo da varivel x
para 2. O lado direito aps o smbolo = no comando set var pode conter uma constante,
uma varivel ou uma expresso vlida. Essa caracterstica do GDB permite que um(a) progra-
mador(a) possa alterar os contedos de variveis durante a execuo de um programa e, com
isso, possa controlar seu fluxo de execuo e corrigir possveis erros.

31.8 Resumo dos comandos

Apresentamos agora uma relao dos comandos que aprendemos, com uma explicao
breve sobre cada um. A inteno agrupar os comandos em um lugar s, para que a relao
nos sirva de referncia. Vale reiterar que o GDB possui muitos comandos no cobertos aqui.
Comando Significado

P ROGRAMA F ONTE
list [n] Mostra linhas em torno da linha n ou as prximas 10 linhas, se no
especificada
list m,n Mostra linhas entre m e n

VARIVEIS E E XPRESSES
print expr Imprime expr
display expr Adiciona expr lista de impresso automtica
info display Mostra a lista de impresso automtica
delete display n Remove a expresso de nmero n da lista de impresso automtica
info locals Mostra o contedo de todas as variveis locais na funo corrente
set var var = expr Atribui expr para a varivel var

P ONTOS DE PARADA
break n Estabelece um breakpoint na linha n
break n if expr Estabelece um breakpoint na linha n se o valor de expr for verdadeiro
break func Estabelece um breakpoint no incio da funo func
watch expr Estabelece um watchpoint na expresso expr
clear [n] Remove o ponto de parada na linha n ou na prxima linha, se no
especificada
delete [n] Remove o ponto de parada de nmero n ou todos os pontos de parada,
se no especificado
enable [n] Habilita todos os pontos de parada suspensos ou o ponto de parada n
disable [n] Desabilita todos os pontos de parada ou o ponto de parada n
info break Mostra todos os pontos de parada

E XECUO DO P ROGRAMA
file [prog] Carrega o programa executvel prog e sua tabela de smbolos ou
libera o GDB da execuo corrente, se no especificado
exec-file [prog] Carrega o programa executvel prog ou libera o GDB da execuo
corrente, se no especificado
run Inicia a execuo do programa
continue Continua a execuo do programa
step [n] Executa a prxima sentena do programa ou as prximas n sentenas
info program Mostra o estado da execuo do programa
quit Encerra a execuo do GDB

A JUDA
help Mostra as classes de ajuda de comandos do GDB
help classe Mostra uma ajuda sobre a classe de comandos
help comando Mostra uma ajuda sobre o comando

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 188

31.9 Exemplos de execuo

Vamos usar o GDB sobre o programa 31.1 que soluciona o exerccio 11.3. No exerccio 11.3
temos de verificar se um nmero inteiro fornecido como entrada contm dois dgitos consecu-
tivos e iguais.

Programa 31.1: Soluo do exerccio 11.3.

1 #include <stdio.h>
2 int main(void)
3 {
4 int num, ant, prox, iguais;
5 printf("Informe um nmero: ");
6 scanf("%d", &num);
7 prox = num % 10;
8 iguais = 0;
9 while (num != 0 && !iguais) {
10 num = num / 10;
11 ant = prox;
12 prox = num % 10;
13 if (ant == prox)
14 iguais = 1;
15 }
16 if (iguais)
17 printf("Nmero tem dois dgitos consecutivos iguais.\n");
18 else
19 printf("Nmero no tem dois dgitos consecutivos iguais.\n");
20 return 0;
21 }

Iniciamos o processo compilando o programa 31.1 adequadamente e carregando o pro-


grama executvel gerado no GDB:

$ gcc consecutivos.c -o consecutivos -Wall -ansi -pedantic -g


$ gdb ./consecutivos
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 189

Para verificar se o programa foi de fato carregado na chamada do GDB, podemos usar o
comando list para ver seu cdigo fonte ou ento o comando info program , para visualizar
o estado do programa:

(gdb) list 1,12


1 #include <stdio.h>
2 int main(void)
3 {
4 int num, ant, prox, iguais;
5 printf("Informe um nmero: ");
6 scanf("%d", &num);
7 prox = num % 10;
8 iguais = 0;
9 while (num != 0 && !iguais) {
10 num = num / 10;
11 ant = prox;
12 prox = num % 10;
(gdb)

ou

(gdb) info program


The program being debugged is not being run.
(gdb)

O programa pode ser executado neste momento, atravs do comando run :

(gdb) run
Starting program: ~/ensino/disciplinas/progi/2009/programas/consecutivos

Informe um nmero: 12233


Nmero tem dois dgitos consecutivos iguais.

Program exited normally.


(gdb)

A execuo acima foi realizada dentro do GDB, sem a adio de qualquer ponto de parada.
Como o programa ainda continua carregado no GDB, podemos execut-lo quantas vezes qui-
sermos. Antes de iniciar a prxima execuo, vamos adicionar alguns pontos de parada no
programa:

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 190

(gdb) break 5
Breakpoint 1 at 0x8048416: file consecutivos.c, line 5.
(gdb)

Note que no iniciamos a execuo do programa ainda. Ento,

(gdb) run
Starting program: ~/ensino/disciplinas/progi/2009/programas/consecutivos

Breakpoint 1, main () at consecutivos.c:5


5 printf("Informe um nmero: ");
(gdb)

O ponto de parada estabelecido na linha 5 do programa foi encontrado e o GDB interrompeu


a execuo do programa neste ponto. Podemos visualizar todas as variveis, e seus contedos,
dentro da funo main usando o comando info locals :

(gdb) info locals


num = -1209114636
ant = 134518608
prox = -1075753928
iguais = 134513993
(gdb)

Observe que todas as variveis declaradas constam da relao apresentada pelo GDB e que seus
contedos so valores estranhos, j que nenhuma dessas variveis foi inicializada at a linha 5
do programa.
Vamos adicionar mais pontos de parada neste programa, um deles um breakpoint associado
a uma expresso lgica e o outro um watchpoint associado a uma varivel:

(gdb) break 13 if ant == prox


Breakpoint 2 at 0x80484ce: file consecutivos.c, line 13.
(gdb) watch iguais
Hardware watchpoint 3: iguais
(gdb)

Agora, podemos visualizar todos os pontos de parada associados a este programa com o
comando info break :

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 191

(gdb) info break


Num Type Disp Enb Address What
1 breakpoint keep y 0x08048416 in main at consecutivos.c:5
breakpoint already hit 1 time
2 breakpoint keep y 0x080484ce in main at consecutivos.c:13
stop only if ant == prox
3 hw watchpoint keep y iguais
(gdb)

Podemos continuar a execuo do programa usando o comando continue :

(gdb) continue
Continuing.

Informe um nmero: 55123


Hardware watchpoint 3: iguais

Old value = 134513993


New value = 0
0x08048471 in main () at consecutivos.c:8
8 iguais = 0;
(gdb)

Observe que na linha 8 do programa a varivel iguais inicializada, substituindo ento


um valor no vlido, um lixo que iguais continha aps sua declarao, pelo valor 0 (zero).
Assim, o GDB interrompe a execuo do programa nessa linha, devido ao watchpoint de nmero
3, e mostra a mudana de valores que ocorreu nessa varivel.
Vamos ento continuar a execuo do programa:

(gdb) continue
Continuing.

Breakpoint 2, main () at consecutivos.c:13


13 if (ant == prox)
(gdb)

O GDB ento interrompeu a execuo do programa devido ao breakpoint 2, na linha 13 do pro-


grama. Neste ponto, deve ter ocorrido o evento em que os contedos das variveis ant e
prox coincidem. Para verificar se esse evento de fato ocorreu, podemos usar o comando
print para verificar os contedos dessas variveis:

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 192

(gdb) print ant


$1 = 5
(gdb) print prox
$2 = 5
(gdb)

Vamos continuar a execuo deste programa:

(gdb) continue
Continuing.
Hardware watchpoint 3: iguais

Old value = 0
New value = 1
main () at consecutivos.c:9
9 while (num != 0 && !iguais) {
(gdb)

Observe que o programa foi interrompido pelo watchpoint, j que o contedo da varivel
iguais foi modificado de 0 para 1 na linha 14 do programa. Assim, a prxima linha a ser
executada a linha 9, como mostrado pelo GDB.
Vamos dar uma olhada no contedo das variveis da funo main :

(gdb) info locals


num = 5
ant = 5
prox = 5
iguais = 1
(gdb)

O programa ento est quase no fim. Vamos continuar sua execuo:

(gdb) continue
Continuing.
Nmero tem dois dgitos consecutivos iguais.

Watchpoint 3 deleted because the program has left the block in


which its expression is valid.
0xb7f08250 in ?? () from /lib/ld-linux.so.2
(gdb)

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 193

Mais uma anlise e mais alguns testes sobre o programa 31.1 nos permitem afirmar com
alguma convico que este programa est correto e que soluciona o exerccio 11.3. O fato novo
que essa afirmao foi feita tambm com o GDB nos auxiliando. Para provar formalmente que
o programa 31.1 est correto, devemos descrever algum invariante do processo iterativo das
linhas 9 at 15 do programa e ento provar por induo a sua validade, conforme a aula 20.
No prximo exemplo vamos executar um programa sob o controle do GDB que no soluci-
ona o problema associado, isto , contm algum erro que, primeira vista, no conseguimos
corrigir. O programa 31.2 se prope a implementar a ordenao por seleo.

Programa 31.2: Uma verso de um programa que deveria realizar a ordenao por seleo.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j, n, menor, indice, aux, A[10];
5 scanf("%d", &n);
6 for (i = 0; i < n; i++)
7 scanf("%d", &A[i]);
8 for (i = 0; i < n-1; i++) {
9 menor = A[i];
10 indice = i;
11 for (j = n-1; j > i; j++) {
12 if (A[j] < menor) {
13 menor = A[j];
14 indice = j;
15 }
16 }
17 aux = A[i];
18 A[i] = A[indice];
19 A[indice] = aux;
20 }
21 for (i = 0; i < n; i++)
22 printf("%d ", A[i]);
23 printf("\n");
24 return 0;
25 }

Em uma tentativa de execuo do programa 31.2, ocorre o seguinte erro:

$ ./ord-selecao
8
7 5 4 1 8 6 2 3
Falha de segmentao
$

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 194

Esse erro no nos d nenhuma dica de onde procur-lo. E se j nos debruamos sobre o
cdigo fonte e no conseguimos encontr-lo, a melhor idia e usar um depurador para nos
ajudar. Neste exemplo, mostraremos o uso do GDB sem muitas interrupes no texto, a menos
que necessrias. Ento, segue uma depurao do programa 31.2.

$ gcc ord-selecao.c -o ord-selecao -Wall -ansi -pedantic -g


$ gdb ./ord-selecao
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) list 8,15
8 for (i = 0; i < n-1; i++) {
9 menor = A[i];
10 indice = i;
11 for (j = n-1; j > i; j++) {
12 if (A[j] < menor) {
13 menor = A[j];
14 indice = j;
15 }
(gdb) break 8
Breakpoint 1 at 0x804845c: file ord-selecao.c, line 8.
(gdb) run
Starting program: ~/ensino/disciplinas/progi/2009/programas/ord-selecao
8
7 5 4 1 8 6 2 3

Breakpoint 1, main () at ord-selecao.c:8


8 for (i = 0; i < n-1; i++) {

(gdb) step 4
12 if (A[j] < menor) {
(gdb) info locals
i = 0
j = 7
n = 8
menor = 7
indice = 0
aux = -1208630704
A = {7, 5, 4, 1, 8, 6, 2, 3, 134518484, -1075407768}
(gdb) display A[j]
1: A[j] = 3
(gdb) display j
2: j = 7
(gdb) display i
3: i = 0

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 195

(gdb) watch menor


Hardware watchpoint 2: menor
(gdb) continue
Continuing.
Hardware watchpoint 2: menor

Old value = 7
New value = 3
main () at ord-selecao.c:14
14 indice = j;
3: i = 0
2: j = 7
1: A[j] = 3

(gdb) continue
Continuing.
Hardware watchpoint 2: menor

Old value = 3
New value = -1075407768
main () at ord-selecao.c:14
14 indice = j;
3: i = 0
2: j = 9
1: A[j] = -1075407768
(gdb) c
Continuing.
Hardware watchpoint 2: menor

Old value = -1075407768


New value = -1208630704
main () at ord-selecao.c:14
14 indice = j;
3: i = 0
2: j = 15
1: A[j] = -1208630704
(gdb) step 100
Hardware watchpoint 2: menor

Old value = -1208630704


New value = -1210084267
main () at ord-selecao.c:14
14 indice = j;
3: i = 0
2: j = 18
1: A[j] = -1210084267
(gdb) set var j=-1
(gdb) set var i=n
(gdb) continue
Continuing.

FACOM UFMS
31.9 E XEMPLOS DE EXECUO 196

7 5 4 1 8 6 2 3

Watchpoint 2 deleted because the program has left the block in


which its expression is valid.
0xb7f5c251 in ?? () from /lib/ld-linux.so.2
(gdb)

Encontramos um erro na linha 11 do programa, no passo da estrutura de repetio for .


Substituindo j++ por j-- nessa linha, temos um programa corrigido que realiza uma orde-
nao por seleo de n nmeros inteiros.

FACOM UFMS
A ULA 32

E FICINCIA DE PROGRAMAS

Medir a eficincia de um programa significa tentar predizer os recursos necessrios para seu
funcionamento. O recurso que temos mais interesse o tempo embora a memria, a comunica-
o e as portas lgicas tambm podem ser de interesse. Na anlise de programas alternativos
para soluo de um mesmo problema, aqueles mais eficientes, isto , que usam a menor quan-
tidade de tempo, so em geral escolhidos como melhores programas. Nesta aula faremos uma
discusso inicial sobre eficincia de programas.

32.1 Programas

Um programa, como j sabemos, uma seqncia bem definida de passos descritos em


uma linguagem de programao especfica que transforma um conjunto de valores, chamado
de entrada, e produz um conjunto de valores, chamado de sada. Dessa forma, um programa
tambm uma ferramenta para solucionar um problema computacional bem definido.
Suponha que temos um problema computacional bem definido, o problema da busca, que
j vimos antes. A descrio do problema dada a seguir:

Dado um nmero intiro n, com 1 6 n 6 100, um conjunto C de n nmeros inteiros


e um nmero inteiro x, verificar se x se encontra no conjunto C.

O problema da busca um problema computacional bsico que surge em diversas apli-


caes prticas. A busca uma operao bsica em computao e, por isso, vrios bons pro-
gramas que a realizam foram desenvolvidos. A escolha do melhor programa para uma dada
aplicao depende de alguns fatores como a quantidade de elementos no conjunto C e da com-
plexidade da estrutura em que C est armazenado.
Como j vimos, se para qualquer entrada um programa pra com a resposta correta, en-
to dizemos que o mesmo est correto. Assim, um programa correto soluciona o problema
computacional associado. Um programa incorreto pode sequer parar, para alguma entrada, ou
pode parar mas com uma resposta indesejada.
O programa 32.1 implementa uma busca de um nmero inteiro x em um conjunto de n
nmeros inteiros C armazenado na memria como um vetor. A busca se d seqencialmente
no vetor C, da esquerda para direita, at que um elemento com ndice i no vetor C contenha o
valor x, quando o programa responde que o elemento foi encontrado, mostrando sua posio.
Caso contrrio, o vetor C todo percorrido e o programa informa que o elemento x no se
encontra no vetor C.

197
32.2 A NLISE DE ALGORITMOS E PROGRAMAS 198

Programa 32.1: Busca de x em C.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int n, i, C[MAX], x;
6 printf("Informe n: ");
7 scanf("%d", &n);
8 for (i = 0; i < n; i++) {
9 printf("Informe um elemento: ");
10 scanf("%d", &C[i]);
11 }
12 printf("Informe x: ");
13 scanf("%d", &x);
14 for (i = 0; i < n && C[i] != x; i++)
15 ;
16 if (i < n)
17 printf("%d est na posio %d de C\n", x, i);
18 else
19 printf("%d no pertence ao conjunto C\n", x);
20 return 0;
21 }

32.2 Anlise de algoritmos e programas

Antes de analisar um programa, devemos conhecer o modelo da tecnologia de computao


usada na mquina em que implementamos o programa, para estabelecer os custos associados
aos recursos que o programa usa. Na anlise de algoritmos e programas, consideramos regu-
larmente um modelo de computao genrico chamado de mquina de acesso aleatrio (do
ingls random acess machine RAM) com um processador. Nesse modelo, as instrues so exe-
cutadas uma aps outra, sem concorrncia. Modelos de computao paralela e distribuda, que
usam concorrncia de instrues, so modelos investigativos que vm se tornando realidade
recentemente. No entanto, nosso modelo para anlise de algoritmos e programas no leva em
conta essas premissas.
A anlise de um programa pode ser uma tarefa desafiadora envolvendo diversas ferramen-
tas matemticas como combinatria discreta, teoria das probabilidades, lgebra e etc. Como
o comportamento de um programa pode ser diferente para cada entrada possvel, precisamos
de uma forma para resumir esse comportamento em frmulas matemticas simples e de fcil
compreenso.
Mesmo com a conveno de um modelo fixo de computao para analisar um programa,
ainda precisamos fazer muitas escolhas para decidir como expressar nossa anlise. Um dos
principais objetivos encontrar uma forma de expresso que simples de escrever e manipu-
lar, que mostra as caractersticas mais importantes das necessidades do programa e exclui os
detalhes mais tediosos.

FACOM UFMS
32.2 A NLISE DE ALGORITMOS E PROGRAMAS 199

No difcil notar que a anlise do programa 32.1 depende do nmero de elementos for-
necidos na entrada. Ou seja, procurar um elemento x em um conjunto C com milhares de
elementos gasta mais tempo que procur-lo em um conjunto C com trs elementos. Alm
disso, o programa 32.1 gasta diferentes quantidades de tempo para buscar um elemento em
conjuntos de mesmo tamanho, dependendo de como os elementos nesses conjuntos esto dis-
postos. Em geral, o tempo gasto por um programa cresce com o tamanho da entrada e assim
comum descrever o tempo de execuo de um programa como uma funo do tamanho de sua
entrada.
Por tamanho da entrada queremos dizer, em geral, o nmero de itens na entrada. Por
exemplo, o vetor de nmeros com n nmeros inteiros onde a busca de um elemento ser reali-
zada. Em outros casos, como na multiplicao de dois nmeros inteiros, a melhor medida para
o tamanho da entrada o nmero de bits necessrios para representar essa entrada na notao
binria. O tempo de execuo de um programa sobre uma entrada particular o nmero de
operaes primitivas ou passos executados por ele. Quanto mais independente da mquina
a definio de um passo, mais conveniente para anlise de tempo dos programas.
Considere ento que uma quantidade constante de tempo necessria para executar cada
linha do programa. Uma linha pode gastar uma quantidade de tempo diferente de outra linha,
mas consideramos que cada execuo da i-sima linha gasta tempo ci , onde ci uma constante.
Iniciamos a anlise do programa 32.1 destacando que o aspecto mais importante para sua
anlise o tempo gasto com a busca do elemento x no vetor C, descrita nas linhas 14 e 15. As
linhas restantes contm diretivas de pr-processador, cabealho da funo main , a entrada de
dados nas linhas 6 a 13, a sada nas linhas 16 a 19 e a sentena return que finaliza a funo
main na linha 20. Isto , queremos saber o tempo gasto no processamento, na transformao,
dos dados de entrada nos dados de sada.

Custo Vezes
#include <stdio.h> c1 1
#define MAX 100 c2 1
int main(void) c3 1
{ 0 1
int n, i, C[MAX], x; c4 1
printf("Informe n: "); c5 1
scanf("%d", &n); c6 1
for (i = 0; i < n; i++) { c7 n+1
printf("Informe um elemento: "); c8 n
scanf("%d", &C[i]); c9 n
} 0 n
printf("Informe x: "); c10 1
scanf("%d", &x); c11 1
for (i = 0; i < n && C[i] != x; i++) c12 ti
; 0 ti 1
if (i < n) c13 1
printf("%d est na posio %d de C\n", x, i); c14 1
else c15 1
printf("%d no pertence ao conjunto C\n", x); c16 1
return 0; c17 1
} 0 1

FACOM UFMS
32.2 A NLISE DE ALGORITMOS E PROGRAMAS 200

O tempo de execuo do programa 32.1 dado pela soma dos tempos para cada sentena
executada. Uma sentena que gasta ci passos e executada n vezes contribui com ci n no tempo
de execuo do programa. Para computar T (n), o tempo de execuo do programa 32.1, deve-
mos somar os produtos das colunas Custo e Vezes , obtendo:

T (n) = c1 + c2 + c3 + c4 + c5 + c6 + c7 (n + 1) + c8 n + c9 n+
c10 + c11 + c12 ti + c13 + c14 + c15 + c16 + c17 .

Observe que, mesmo para entradas de um mesmo tamanho, o tempo de execuo de um


programa pode depender de qual entrada desse tamanho fornecida. Por exemplo, no pro-
grama 32.1, o melhor caso ocorre se o elemento x encontra-se na primeira posio do vetor C.
Assim, ti = 1 e o tempo de execuo do melhor caso dado por:

T (n) = c1 + c2 + c3 + c4 + c5 + c6 + c7 (n + 1) + c8 n + c9 n+
c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17
= (c7 + c8 + c9 )n+
(c1 + c2 + c3 + c4 + c5 + c6 + c7 + c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17 ) .

Esse tempo de execuo pode ser expresso como an+b para constantes a e b, que dependem
dos custos ci das sentenas do programa. Assim, o tempo de execuo dado por uma funo
linear em n. No entanto, observe que as constantes c7 , c8 e c9 que multiplicam n na frmula de
T (n) so aquelas que tratam somente da entrada de dados. Evidentemente que para armazenar
n nmeros inteiros em um vetor devemos gastar tempo an, para alguma constante a. Dessa
forma, se consideramos apenas a constante c12 na frmula, que trata propriamente da busca, o
tempo de execuo de melhor caso, quando ti = 1, dado por:

T (n) = c12 ti = c12 .

Por outro lado, se o elemento x no se encontra no conjunto C, temos ento o pior caso do
programa. Alm de comparar i com n, comparamos tambm o elemento x com o elemento C[i]
para cada i, 0 6 i 6 n 1. Uma ltima comparao ainda realizada quando i atinge o valor
n. Assim, ti = n + 1 e o tempo de execuo de pior caso dado por:

T (n) = c12 ti = c12 (n + 1) = c12 n + c12 .

Esse tempo de execuo de pior caso pode ser expresso como uma funo linear an + b para
constantes a e b que dependem somente da constante c12 , responsvel pelo trecho do programa
que realiza o processamento.
Na anlise do programa 32.1, estabelecemos os tempos de execuo do melhor caso, quando
encontramos o elemento procurado logo na primeira posio do vetor que representa o con-
junto, e do pior caso, quando no encontramos o elemento no vetor. No entanto, estamos em
geral interessados no tempo de execuo de pior caso de um programa, isto , o maior tempo
de execuo para qualquer entrada de tamanho n.
Como o tempo de execuo de pior caso de um programa um limitante superior para seu
tempo de execuo para qualquer entrada, temos ento uma garantia que o programa nunca
vai gastar mais tempo que esse estabelecido. Alm disso, o pior caso ocorre muito freqente-
mente nos programas em geral, como no caso do problema da busca.

FACOM UFMS
32.2 A NLISE DE ALGORITMOS E PROGRAMAS 201

32.2.1 Ordem de crescimento de funes matemticas

Acabamos de usar algumas convenes que simplificam a anlise do programa 32.1. A


primeira abstrao que fizemos foi ignorar o custo real de uma sentena do programa, usando
as constantes ci para representar esses custos. Depois, observamos que mesmo essas constantes
nos do mais detalhes do que realmente precisamos: o tempo de execuo de pior caso do
programa 32.1 an + b, para constantes a e b que dependem dos custos ci das sentenas. Dessa
forma, ignoramos no apenas o custo real das sentenas mas tambm os custos abstratos ci .
Na direo de realizar mais uma simplificao, estamos interessados na taxa de cresci-
mento, ou ordem de crescimento, da funo que descreve o tempo de execuo de um al-
goritmo ou programa. Portanto, consideramos apenas o maior termo da frmula, como por
exemplo an, j que os termos menores so relativamente insignificantes para n grande. Tam-
bm ignoramos o coeficiente constante do maior termo, j que fatores constantes so menos
significativos que a taxa de crescimento no clculo da eficincia computacional para entradas
grandes. Dessa forma, dizemos que o programa 32.1, por exemplo, tem tempo de execuo de
pior caso O(n).
Usualmente, consideramos um programa mais eficiente que outro se seu tempo de exe-
cuo de pior caso tem ordem de crescimento menor. Essa avaliao pode ser errnea para
pequenas entradas mas, entradas para suficientemente grandes, um programa que tem tempo
de execuo de pior caso O(n) executar mais rapidamente no pior caso que um programa que
tem tempo de execuo de pior caso O(n2 ).
Quando olhamos para entradas cujos tamanhos so grandes o suficiente para fazer com que
somente a taxa de crescimento da funo que descreve o tempo de execuo de um programa
seja relevante, estamos estudando na verdade a eficincia assinttica de um algoritmo ou pro-
grama. Isto , concentramo-nos em saber como o tempo de execuo de um programa cresce
com o tamanho da entrada no limite, quando o tamanho da entrada cresce ilimitadamente. Usu-
almente, um programa que assintoticamente mais eficiente ser a melhor escolha para todas
as entradas, excluindo talvez algumas entradas pequenas.
As notaes que usamos para descrever o tempo de execuo assinttico de um programa
so definidas em termos de funes matemticas cujos domnios so o conjunto dos nmeros
naturais N = {0, 1, 2, . . .}. Essas notaes so convenientes para descrever o tempo de execuo
de pior caso T (n) que usualmente definido sobre entradas de tamanhos inteiros.
No incio desta seo estabelecemos que o tempo de execuo de pior caso do programa 32.1
T (n) = O(n). Vamos definir formalmente o que essa notao significa. Para uma dada funo
g(n), denotamos por O(g(n)) o conjunto de funes
O(g(n)) = {f (n) : existem constantes positivas c e n0 tais que
0 6 f (n) 6 cg(n) para todo n > n0 } .

A funo f (n) pertence ao conjunto O(g(n)) se existe uma constante positiva c tal que f (n)
no seja maior que cg(n), para n suficientemente grande. Dessa forma, usamos a notao
O para fornecer um limitante assinttico superior sobre uma funo, dentro de um fator cons-
tante. Apesar de O(g(n)) ser um conjunto, escrevemos f (n) = O(g(n)) para indicar que f (n)
um elemento de O(g(n)), isto , que f (n) O(g(n)).
A figura 32.1 mostra a intuio por trs da notao O. Para todos os valores de n direita
de n0 , o valor da funo f (n) est sobre ou abaixo do valor da funo g(n).

FACOM UFMS
32.3 A NLISE DA ORDENAO POR TROCAS SUCESSIVAS 202

cg(n)

f (n)

n
n0

Figura 32.1: f (n) = O(g(n)).

Dessa forma, podemos dizer, por exemplo, que 4n + 1 = O(n). Isso porque existem cons-
tantes positivas c e n0 tais que
4n + 1 6 cn
para todo n > n0 . Tomando c = 5 e n0 = 1, essa desigualdade satisfeita para todo n > n0 .
Certamente, existem outras escolhas para as constantes c e n0 , mas o mais importante que
existe alguma escolha. Observe que as constantes dependem da funo 4n + 1. Uma funo
diferente que pertence a O(n) provavelmente necessita de outras constantes.
A definio de O(g(n)) requer que toda funo pertencente a O(g(n)) seja assintoticamente
no-negativa, isto , que f (n) seja no-negativa sempre que n seja suficientemente grande.
Conseqentemente, a prpria funo g(n) deve ser assintoticamente no-negativa, caso contr-
rio o conjunto O(g(n)) vazio. Portanto, vamos considerar que toda funo usada na notao
O assintoticamente no-negativa.
A ordem de crescimento do tempo de execuo de um programa ou algoritmo pode ser
denotada atravs de outras notaes assintticas, tais como as notaes , , o e . Essas
notaes so especficas para anlise do tempo de execuo de um programa atravs de outros
pontos de vista. Nesta aula, ficaremos restritos apenas notao O.

32.3 Anlise da ordenao por trocas sucessivas

Vamos fazer a anlise de um outro programa que j conhecemos bem e que soluciona o pro-
blema da ordenao. O programa 32.2 faz a ordenao de um vetor de n elementos fornecidos
como entrada usando o mtodo das trocas sucessivas ou o mtodo da bolha.
O problema computacional que o programa 32.2 soluciona descrito formalmente a seguir:

Dado um nmero inteiro positivo n, com 1 6 n 6 100, e uma seqncia de n nme-


ros inteiros, determinar a ordenao desses n nmeros em ordem no-decrescente.

FACOM UFMS
32.3 A NLISE DA ORDENAO POR TROCAS SUCESSIVAS 203

Programa 32.2: Mtodo de ordenao atravs de trocas sucessivas.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 int i, j, n, aux, A[MAX];
6 scanf("%d", &n);
7 for (i = 0; i < n; i++)
8 scanf("%d", &A[i]);
9 for (i = n-1; i > 0; i--)
10 for (j = 0; j < i; j++)
11 if (A[j] > A[j+1]) {
12 aux = A[j];
13 A[j] = A[j+1];
14 A[j+1] = aux;
15 }
16 for (i = 0; i < n; i++)
17 printf("%d ", A[i]);
18 printf("\n");
19 return 0;
20 }

Conforme j fizemos antes, a anlise linha a linha do programa 32.2 descrita abaixo.

Custo Vezes
#include <stdio.h> c1 1
#define MAX 100 c2 1
int main(void) c3 1
{ 0 1
int i, j, n, aux, A[MAX]; c4 1
scanf("%d", &n); c5 1
for (i = 0; i < n; i++) c6 n+1
scanf("%d", &A[i]); c7 n
for (i = n-1; i > 0; i--) c8 n
Pn1
for (j = 0; j < i; j++) c9 i=1 (i + 1)
Pn1
if (A[j] > A[j+1]) { c10 i=1 i
Pn1
aux = A[j]; c11 ti
Pi=1
n1
A[j] = A[j+1]; c12 ti
Pi=1
n1
A[j+1] = aux; c13 ti
Pi=1
n1
} 0 i=1 i
for (i = 0; i < n; i++) c14 n+1
printf("%d ", A[i]); c15 n
printf("\n"; c16 1
return 0; c17 1
} 0 1

FACOM UFMS
32.3 A NLISE DA ORDENAO POR TROCAS SUCESSIVAS 204

Podemos ir um passo adiante na simplificao da anlise de um algoritmo ou programa se


consideramos que cada linha tem custo de processamento 1. Na prtica, isso no bem verdade
j que h sentenas mais custosas que outras. Por exemplo, o custo para executar uma sentena
que contm a multiplicao de dois nmeros de ponto flutuante de preciso dupla maior que
o custo de comparar dois nmeros inteiros. No entanto, ainda assim vamos considerar que o
custo para processar qualquer linha de um programa constante e, mais ainda, que tem custo
1. Dessa forma, a coluna com rtulo Vezes nas anlises acima representam ento o custo de
execuo de cada linha do programa.
Para anlise do programa 32.2 vamos considerar apenas o trecho onde ocorre o proces-
samento, isto , as linhas de 9 a 15. O melhor caso para o programa 32.2 ocorre quando a
seqncia de entrada com n nmeros inteiros fornecida em ordem no-decrescente. Observe
que, nesse caso, as linhas 12, 13 e 14 no sero executadas sequer uma vez. Ou seja, ti = 0 para
todo i. Ento, o tempo de execuo no melhor caso dado pela seguinte expresso:
n1
X n1
X n1
X n1
X n1
X
T (n) = n + (i + 1) + i+ ti + ti + ti
i=1 i=1 i=1 i=1 i=1
n1
X n1
X n1
X
=n+2 i+ 1+3 ti
i=1 i=1 i=1
n1
X n1
X n1
X
=n+2 i+ 1+3 0
i=1 i=1 i=1
n(n 1)
=n+2 +n1
2
= n2 + n 1 .

Ento, escolhendo c = 2 e n0 = 1 temos que T (n) = O(n2 ). Ou seja, o tempo de execuo


do melhor caso do programa 32.2 que implementa o mtodo da bolha quadrtico no tamanho
da entrada.
Para definir a entrada que determina o pior caso para o programa 32.2 devemos notar que
quando o vetor contm os n elementos em ordem decrescente, o maior nmero de trocas
realizado. Assim, ti = i para todo i e o tempo de execuo de pior caso dado pela seguinte
expresso:
n1
X n1
X n1
X
T (n) = n + (i + 1) + i+3 ti
i=1 i=1 i=1
n1
X n1
X n1
X
=n+ (i + 1) + i+3 i
i=1 i=1 i=1
n1
X n1
X
=n+5 i+ 1
i=1 i=1
n(n 1)
=n+5 +n1
2
5 5
= n2 n + 2n 1
2 2
5 2 1
= n n1.
2 2

FACOM UFMS
32.4 R ESUMO 205

Ento, escolhendo c = 5/2 e n0 = 1 temos que T (n) = O(n2 ). Ou seja, o tempo de execuo
do pior caso do programa 32.2 quadrtico no tamanho da entrada. Note tambm que ambos
os tempos de execuo de melhor e de pior caso do programa 32.2, que implementa o mtodo
da ordenao da bolha, tm o mesmo desempenho assinttico.

32.4 Resumo

Um bom algoritmo ou programa como uma faca afiada: faz o que suposto fazer com a
menor quantidade de esforo aplicada. Usar um programa errado para resolver um problema
como tentar cortar um bife com uma chave de fenda: podemos eventualmente obter um
resultado digervel, mas gastaremos muito mais energia que o necessrio e o resultado no
dever ser muito agradvel esteticamente.
Programas alternativos projetados para resolver um mesmo problema em geral diferem
dramaticamente em eficincia. Essas diferenas podem ser muito mais significativas que a di-
ferena de tempos de execuo em um supercomputador e em um computador pessoal. Como
um exemplo, suponha que temos um supercomputador executando o programa que imple-
menta o mtodo da bolha de ordenao, com tempo de execuo de pior caso O(n2 ), contra um
pequeno computador pessoal executando o mtodo da intercalao. Esse ltimo mtodo de
ordenao tem tempo de execuo de pior caso O(n log n) para qualquer entrada de tamanho
n e ser visto na aula 43. Suponha que cada um desses programas deve ordenar um vetor de
um milho de nmeros. Suponha tambm que o supercomputador capaz de executar 100
milhes de operaes por segundo enquanto que o computador pessoal capaz de executar
apenas um milho de operaes por segundo. Para tornar a diferena ainda mais dramtica,
suponha que o mais habilidoso dos programadores do mundo codificou o mtodo da bolha na
linguagem de mquina do supercomputador e o programa usa 2n2 operaes para ordenar n
nmeros inteiros. Por outro lado, o mtodo da intercalao foi programado por um progra-
mador mediano usando uma linguagem de programao de alto nvel com um compilador
ineficiente e o cdigo resultante gasta 50n log n operaes para ordenar os n nmeros. Assim,
para ordenar um milho de nmeros o supercomputador gasta

2 (106 )2 operaes
= 20.000 segundos 5,56 horas,
108 operaes/segundo

enquanto que o computador pessoal gasta

50 106 log 106 operaes


1.000 segundos 16,67 minutos.
106 operaes/segundo

Usando um programa cujo tempo de execuo tem menor taxa de crescimento, mesmo com
um compilador pior, o computador pessoal 20 vezes mais rpido que o supercomputador!
Esse exemplo mostra que os algoritmos e os programas, assim como os computadores, so
uma tecnologia. O desempenho total do sistema depende da escolha de algoritmos e progra-
mas eficientes tanto quanto da escolha de computadores rpidos. Assim como rpidos avanos
esto sendo feitos em outras tecnologias computacionais, eles esto sendo feitos em algoritmos
e programas tambm.

FACOM UFMS
32.4 R ESUMO 206

Exerccios

32.1 Qual o menor valor de n tal que um programa com tempo de execuo 100n2 mais
rpido que um programa cujo tempo de execuo 2n , supondo que os programas foram
implementados no mesmo computador?
32.2 Suponha que estamos comparando as implementaes dos mtodos de ordenao da bo-
lha e da intercalao em um mesmo computador. Para entradas de tamanho n, o mtodo
da bolha gasta 8n2 passos enquanto que o mtodo da intercalao gasta 64n log n passos.
Para quais valores de n o mtodo da bolha melhor que o mtodo da intercalao?
32.3 Expresse a funo n3 /1000 100n2 100n + 3 na notao O.
32.4 Para cada funo f (n) e tempo t na tabela abaixo determine o maior tamanho n de um
problema que pode ser resolvido em tempo t, considerando que o programa soluciona o
problema em f (n) microssegundos.

1 1 1 1 1 1 1
segundo minuto hora dia ms ano sculo
log n

n
n
n log n
n2
n3
2n
n!

32.5 verdade que 2n+1 = O(2n )? E verdade que 22n = O(2n )?


32.6 Considere o problema de computar o valor de um polinmio em um ponto. Dados n
Pn1
coeficientes a0 , a1 , . . . , an1 e um nmero real x, queremos computar i=0 ai xi .
(a) Escreva um programa simples com tempo de execuo de pior caso O(n2 ) para so-
lucionar este problema.
(b) Escreva um programa com tempo de execuo de pior caso O(n) para solucionar
este problema usando o mtodo chamado de regra de Horner para reescrever o po-
linmio:
n1
X
ai xi = ( (an1 x + an2 )x + + a1 )x + a0 .
i=1

32.7 Seja A[0..n 1] um vetor de n nmeros inteiros distintos dois a dois. Se i < j e A[i] > A[j]
ento o par (i, j) chamado uma inverso de A.
(a) Liste as cinco inverses do vetor A = h2, 3, 8, 6, 1i.
(b) Qual vetor com elementos no conjunto {1, 2, . . . , n} tem a maior quantidade de in-
verses? Quantas so?
(c) Escreva um programa que determine o nmero de inverses em qualquer permuta-
o de n elementos em tempo de execuo de pior caso O(n log n).

FACOM UFMS
A ULA 33

I NTRODUO S FUNES

Se concordamos que uma grande tarefa pode ser solucionada atravs de sua diviso em
sub-tarefas e da combinao de suas solues parciais, ento podemos ter nosso trabalho facili-
tado focando na construo de solues para essas sub-tarefas. Em programao essas solues
menores so chamadas de mdulos de programao e fazem parte de todo processo de cons-
truo de programas para soluo de problemas reais.
Os mdulos so uma ferramenta da programao estruturada que fornecem ao() progra-
mador(a) um mecanismo para construir programas que so fceis de escrever, ler, compreen-
der, corrigir, modificar e manter. Na linguagem C, essa caracterstica em um cdigo obtida
atravs do uso de funes. Na verdade, todos os programas que codificamos at o momento
de alguma forma j fizeram uso de funes. Por exemplo, scanf e printf so funes de
entrada e sada de dados da linguagem C que j usamos muitas e muitas vezes. Alm disso,
temos construdo nossas prprias funes main para solucionar todos os problemas vistos at
aqui. Apesar do termo funo vir da matemtica, uma funo da linguagem C nem sempre
se parece com uma funo matemtica, j que pode no ter argumentos nem computar um
valor. Cada funo na linguagem C pode ser vista como um pequeno programa com suas pr-
prias declaraes de variveis e sentenas de programao. Alm de facilitar a compreenso
e a modificao de um programa e evitar a duplicao de cdigo usado mais que uma vez, as
funes podem ser usadas no s no programa para o qual foram projetadas, mas tambm em
outros programas.

33.1 Noes iniciais

Antes de aprender as regras formais para definir uma funo, vejamos trs pequenos exem-
plos de programas que definem funes.
Suponha que computamos freqentemente a mdia de dois valores do tipo double . A
biblioteca de funes da linguagem C no tem uma funo que computa a mdia, mas podemos
escrever facilmente a nossa prpria funo. Vejamos a seguir:

double media(double a, double b)


{
return (a + b) / 2;
}

207
33.1 N OES INICIAIS 208

A palavra reservada double na primeira linha o tipo do valor devolvido da funo


media , que o tipo do valor devolvido pela funo cada vez que ela chamada. Os identifi-
cadores a e b , chamados de parmetros da funo, representam os dois nmeros que sero
fornecidos quando a funo media chamada. Assim como uma varivel, um parmetro
deve ter um tipo. No exemplo, ambos os parmetros a e b so do tipo double . Um par-
metro de uma funo essencialmente uma varivel cujo valor inicial fornecido quando da
sua chamada.
Toda funo tem um trecho executvel, chamado de corpo, envolvido por abre e fecha
chaves. O corpo de funo media consiste de uma nica sentena return . A execuo
dessa sentena faz com que a funo regresse para o ponto de onde foi chamada. O valor
(a + b) / 2 ser devolvido pela funo.
Para chamar uma funo, escrevemos o nome da funo seguido por uma lista de argu-
mentos. Por exemplo, media(x, y) uma chamada da funo media . Os argumentos so
usados para fornecer informaes para a funo, como nesse caso, em que a funo media
necessita de dois valores para calcular a mdia. O efeito da chamada media(x, y) primeiro
copiar os valores de x e y nos parmetros a e b e, em seguida, executar o corpo de media .
Um argumento no tem de ser necessariamente uma varivel. Qualquer expresso de um tipo
compatvel pode ser um argumento, o que nos permite escrever media(5.1, 8.9) ou ainda
media(x/2, y/3) .

Devemos fazer uma chamada funo media no ponto do cdigo onde usamos o seu valor
devolvido. Por exemplo, para computar a mdia de x e y , e escrever o resultado na sada,
poderamos escrever:

printf("Mdia: %g\n", media(x, y));

A sentena acima tem o seguinte efeito:

1. a funo media chamada com argumentos x e y ;

2. x e y so copiados em a e b ;

3. a funo media executa sua sentena return , devolvendo a mdia de a e b ;

4. a funo printf imprime a cadeia de caracteres e o valor que media devolve, sendo
que esse valor devolvido torna-se um argumento da funo printf .

Observe que o valor devolvido pela funo media no salvo em lugar algum. A sentena
imprime o valor e ento o descarta. Se h necessidade de usar o valor devolvido posterior-
mente, podemos captur-lo em uma varivel, como abaixo:

m = media(x, y);

FACOM UFMS
33.1 N OES INICIAIS 209

A sentena acima chama a funo media e ento salva o valor devolvido na varivel m .
Vamos usar a funo media em um programa. O programa 33.1 tem trs valores de entrada
e computa a mdia desses valores dois a dois. Entre outras coisas, este programa mostra que
uma funo pode ser chamada tantas vezes quanto necessrio.

Programa 33.1: Primeiro exemplo do uso de funo.

1 #include <stdio.h>
2 double media(double a, double b)
3 {
4 return (a + b) / 2;
5 }
6 int main(void)
7 {
8 double x, y, z;
9 printf("Informe trs valores: ");
10 scanf("%lf%lf%lf", &x, &y, &z);
11 printf("Mdia de %g e %g %g\n", x, y, media(x, y));
12 printf("Mdia de %g e %g %g\n", x, z, media(x, z));
13 printf("Mdia de %g e %g %g\n", y, z, media(y, z));
14 return 0;
15 }

Observe que a definio da funo media vem antes da definio da funo main , j que
teramos problemas de compilao se fizssemos o contrrio.
Nem toda funo devolve um valor como a funo media faz. Por exemplo, uma funo
cujo trabalho enviar informaes para a sada muito provavelmente no devolve valor algum.
Para indicar que uma funo no tem valor devolvido, especificamos que seu valor devolvido
do tipo void , onde void um tipo sem valor. Considere o seguinte exemplo, que imprime
na sada a mensagem "n e contando..." , onde n um valor fornecido quando a funo
chamada:

void imprimeContador(int n)
{
printf("%d e contando...", n);
return;
}

A funo imprimeContador tem um nico parmetro n do tipo int e no devolve valor


algum. Nesse caso, especificamos void como o tipo do valor a ser devolvido e podemos
opcionalmente omitir a sentena return . Dessa forma, a funo imprimeContador tambm
poderia ser descrita corretamente como abaixo:

FACOM UFMS
33.1 N OES INICIAIS 210

void imprimeContador(int n)
{
printf("%d e contando...\n", n);
}

Como a funo imprimeContador no devolve um valor, no podemos cham-la da


mesma maneira que chamamos a funo media no exemplo anterior. Ao contrrio, qual-
quer chamada da funo imprimeContador deve aparecer como uma nica sentena, como
no exemplo a seguir:

imprimeContador(i);

O programa 33.2 chama a funo imprimeContador 10 vezes em uma estrutura de repeti-


o for .

Programa 33.2: Segundo exemplo do uso de funo.

1 #include <stdio.h>
2 void imprimeContador(int n)
3 {
4 printf("%d e contando...\n", n);
5 return;
6 }
7 int main(void)
8 {
9 int i;
10 for (i = 10; i > 0; i--)
11 imprimeContador(i);
12 return 0;
13 }

Inicialmente, a varivel i tem o valor 10. Quando a funo imprimeContador chamada


pela primeira vez, o valor em i copiado no parmetro n e a partir de ento n tambm con-
tm o valor 10. Como resultado, a primeira chamada da funo imprimeContador imprimir
a seguinte mensagem:

10 e contando...

FACOM UFMS
33.1 N OES INICIAIS 211

A funo imprimeContador regressa ento para o ponto em que foi chamada, o que acon-
tece no corpo de uma estrutura de repetio for . A estrutura for continua sua execuo
decrementando i para 9 e verificando se seu valor maior que 0. Como esse novo valor
maior que 0, imprimeContador chamada novamente e imprime a mensagem abaixo:

9 e contando...

Cada vez que imprimeContador chamada, o valor armazenado na varivel i dife-


rente e, assim, imprimeContador imprimir 10 mensagens diferentes na sada.
importante salientar ainda que algumas funes podem tambm no ter parmetros.
Considere a funo imprimeMsg , que imprime uma mensagem cada vez que chamada:

void imprimeMsg(void)
{
printf("Programar bacana!\n");
return;
}

A palavra reservada void entre parnteses aps o identificador da funo indica que
imprimeMsg no tem argumentos. Para chamar uma funo sem argumentos, devemos es-
crever o nome da funo seguido obrigatoriamente por parnteses, como mostramos abaixo:

imprimeMsg();

O programa 33.3 mostra o uso da funo imprimeMsg .

Programa 33.3: Terceiro exemplo do uso de funo.

1 #include <stdio.h>
2 void imprimeMsg(void)
3 {
4 printf("Programar bacana!\n");
5 return;
6 }
7 int main(void)
8 {
9 imprimeMsg();
10 return 0;
11 }

FACOM UFMS
33.2 D EFINIO E CHAMADA DE FUNES 212

A execuo do programa 33.3 comea com a primeira sentena na funo main , que a
chamada funo imprimeMsg . Quando imprimeMsg comea sua execuo, ela chama a
funo printf para mostrar uma mensagem na sada. Quando printf termina sua execu-
o, imprimeMsg termina sua execuo e volta para main .

33.2 Definio e chamada de funes

Uma funo da linguagem C um trecho de cdigo que pode receber um ou mais valores de
entrada armazenados em variveis, chamados de parmetros (de entrada), que realiza algum
processamento especfico e que pode devolver um nico valor de sada. Em geral, construmos
funes para resolver pequenos problemas bem especficos e, em conjunto com outras funes,
resolver problemas maiores e mais complexos. A definio de uma funo na linguagem C
fornece quatro informaes importantes ao compilador:

1. quem pode cham-la;

2. o tipo do valor que a funo devolve;

3. seu identificador;

4. seus parmetros de entrada.

Uma funo composta por:

uma interface, que faz a comunicao entre a funo e o meio exterior; a interface de-
finida pela primeira linha da funo, onde especificamos o tipo do valor devolvido da
funo, seu identificador e a lista de parmetros de entrada separados por vrgulas e en-
volvidos por um par de parnteses; e

um corpo, que realiza o processamento sobre os valores armazenados nos parmetros de


entrada, e tambm nas variveis declaradas internamente funo, e devolve um valor
na sada; o corpo de uma funo composto pela declarao de variveis locais da funo
e sua lista de comandos.

Depois de ter visto vrios exemplos de funes na seo anterior, a forma geral de uma
definio de uma funo dada a seguir:

tipo identificador(parmetros)
{
declarao de variveis
sentenas
}

A primeira linha apresentada a interface da funo e as linhas seguintes, envolvidas por


chaves, compem o corpo da funo. A interface da funo inicia com um tipo , que especifica

FACOM UFMS
33.2 D EFINIO E CHAMADA DE FUNES 213

o tipo do valor a ser devolvido pela funo. Funes no podem devolver variveis compostas
homogneas, mas no h qualquer outra restrio quanto ao tipo de valor a ser devolvido. No
entanto, especificar que o valor devolvido do tipo void indica que a funo no devolve
um valor. Depois, a interface contm o identificador da funo, que seu nome e que a
identifica e diferencia de outras funes. E, finalmente, a interface contm os parmetros , que
armazenam valores a serem recebidos como entrada pela funo, envolvidos por parnteses. A
lista de parmetros composta por tipos e identificadores de variveis, separados por vrgulas.
Um tipo deve ser especificado para cada parmetro, mesmo que vrios parmetros sejam do
mesmo tipo. Por exemplo, errado escrever a funo media como abaixo:

double media(double a, b)
{
return (a + b) / 2;
}

Se a funo no tem parmetros, a palavra reservada void deve ocorrer entre os parnte-
ses.
Aps a interface, a funo contm um corpo, que uma seqncia de comandos da lingua-
gem C envolvida por chaves. O corpo de uma funo pode incluir declaraes e sentenas. Por
exemplo, a funo media poderia ser escrita como abaixo:

double media(double a, double b)


{
double soma;
soma = a + b;
return soma / 2;
}

A declarao de variveis de uma funo deve vir primeiro, antes de qualquer sentena do
corpo da funo. As variveis declaradas no corpo de uma funo pertencem exclusivamente
quela funo e no podem ser examinadas ou modificadas por outras funes.
Uma chamada de funo consiste de um identificador da funo seguido por uma lista de
argumentos entre parnteses. Por exemplo, abaixo temos trs chamadas de funes:

media(x, y)
imprimeContador(i)
imprimeMsg()

Uma chamada de uma funo com valor devolvido void sempre seguida por um ;
para torn-la uma sentena. Por exemplo:

FACOM UFMS
33.2 D EFINIO E CHAMADA DE FUNES 214

imprimeContador(i);
imprimeMsg();

Por outro lado, uma chamada de uma funo com valor devolvido diferente de void pro-
duz um valor que pode ser armazenado em uma varivel ou usado em uma expresso aritm-
tica, relacional ou lgica. Por exemplo,

if (media(x, y) > 0)
printf("Mdia positiva\n");
printf("A mdia %g\n", media(x, y));

O valor devolvido por uma funo que devolve um valor diferente de void sempre pode
ser descartado, como podemos ver na chamada a seguir:

media(x, y);

Esta chamada funo media um exemplo de uma sentena que avalia uma expresso mas
descarta o resultado. Apesar de estranho na chamada funo media , ignorar o valor devol-
vido pode fazer sentido em alguns casos. Por exemplo, a funo printf sempre devolve o
nmero de caracteres impressos na sada. Dessa forma, aps a chamada abaixo, a varivel nc
ter o valor 19:

nc = printf("Programar bacana\n");

Como em geral no estamos interessados no nmero de caracteres impressos, normalmente


descartamos o valor devolvido pela funo printf e fazemos:

printf("Programar bacana\n");

Uma funo que devolve um valor diferente de void deve usar a sentena return para
especificar qual valor ser devolvido. A sentena return tem a seguinte forma geral:

return expresso;

FACOM UFMS
33.3 F INALIZAO DE PROGRAMAS 215

A expresso em geral uma constante ou uma varivel, como abaixo:

return 0;
return estado;

Expresses mais complexas so possveis, como por exemplo:

return (a + b) / 2;
return a*a*a + 2*a*a - 3*a - 1;

Quando uma das sentenas acima executada, primeiramente a avaliao da expresso rea-
lizada e o valor obtido ento devolvido pela funo.
Se o tipo da expresso na sentena return diferente do tipo devolvido pela funo, a
expresso ser implicitamente convertida para o tipo da funo.

33.3 Finalizao de programas

Como main uma funo, ela deve ter um tipo de valor de devoluo. Normalmente, o
tipo do valor a ser devolvido pela funo main int e por isso que temos definido main
da forma como fizemos at aqui.
O valor devolvido pela funo main um cdigo de estado do programa que, em alguns
sistemas operacionais, pode ser verificado quando o programa termina. A funo main deve
devolver 0 se o programa termina normalmente ou um valor diferente de 0 para indicar um
trmino anormal. Na verdade, no h uma regra para o uso do valor devolvido pela funo
main e, assim, podemos usar esse valor para qualquer propsito. uma boa prtica de pro-
gramao fazer com que todo programa na linguagem C devolva um cdigo de estado, mesmo
se no h planos de us-lo, j que um(a) usurio(a) pode decidir verific-lo.
Executar a sentena return na funo main uma forma de terminar um programa.
Outra maneira chamar a funo exit que pertence biblioteca stdlib . O argumento
passado para exit tem o mesmo significado que o valor a ser devolvido por main , isto
, ambos indicam o estado do programa ao seu trmino. Para indicar um trmino normal,
passamos 0 (zero) como argumento, como abaixo:

exit(0);

Como o argumento 0 no muito significativo, a linguagem C nos permite usar o argu-


mento EXIT_SUCCESS , que tem o mesmo efeito:

FACOM UFMS
33.4 E XEMPLO 216

exit(EXIT_SUCCESS);

O argumento EXIT_FAILURE indica trmino anormal:

exit(EXIT_FAILURE);

EXIT_SUCCESS e EXIT_FAILURE so macros definidas na stdlib.h . Os valores associa-


dos so definidos pela arquitetura do computador em uso, mas em geral so 0 e 1 , respecti-
vamente.
Como mtodos para terminar um programa, return e exit so muito semelhantes. Ou
seja, a sentena

return expresso;

na funo main equivalente sentena

exit(expresso);

A diferena entre return e exit que exit causa o trmino de um programa inde-
pendente do ponto em que se encontra essa chamada, isto , no considerando qual funo
chamou a funo exit . A sentena return causa o trmino de um programa apenas se
aparece na funo main . Alguns(mas) programadores(as) usam exit exclusivamente para
depurar programas.

33.4 Exemplo

Vamos resolver agora o seguinte problema:

Construa uma funo que receba dois inteiros positivos e devolva o mximo divisor
comum entre eles.

J resolvemos o problema de encontrar o mximo divisor comum de dois nmeros inteiros


positivos antes, usando o algoritmo de Euclides, mas sem o uso de uma funo. Uma possvel
funo que soluciona o problema acima mostrada a seguir.

FACOM UFMS
33.5 D ECLARAO DE FUNES 217

int mdc(int a, int b)


{
int aux;
while (b != 0) {
aux = a % b;
a = b;
b = aux;
}
return a;
}

Um exemplo de um programa principal que faz uma chamada funo mdc descrita acima
mostrado no programa 33.4.

Programa 33.4: Exemplo de um programa com uma funo mdc .

1 #include <stdio.h>
2 int mdc(int a, int b)
3 {
4 int aux;
5 while (b != 0) {
6 aux = a % b;
7 a = b;
8 b = aux;
9 }
10 return a;
11 }
12 int main(void)
13 {
14 int x, y;
15 printf("Informe dois valores: ");
16 scanf("%d%d", &x, &y);
17 printf("O mdc entre %d e %d %d.", x, y, mdc(x, y));
18 return 0;
19 }

33.5 Declarao de funes

Nos programas que vimos nas sees 33.1 e 33.4 a definio de cada funo sempre foi
feita acima do ponto onde ocorrem suas chamadas. Ou seja, se a funo main faz chamada
a uma funo escrita pelo programador, a definio dessa funo tem de ocorrer antes dessa
chamada, acima da prpria funo main . No entanto, a linguagem C na verdade no obriga
que a definio de uma funo preceda suas chamadas.

FACOM UFMS
33.5 D ECLARAO DE FUNES 218

Suponha que rearranjamos o programa 33.1 colocando a definio da funo media depois
da definio da funo main , como abaixo:

1 #include <stdio.h>
2 int main(void)
3 {
4 double x, y, z;
5 printf("Informe trs valores: ");
6 scanf("%lf%lf%lf", &x, &y, &z);
7 printf("Mdia de %g e %g %g\n", x, y, media(x, y));
8 printf("Mdia de %g e %g %g\n", x, z, media(x, z));
9 printf("Mdia de %g e %g %g\n", y, z, media(y, z));
10 return 0;
11 }
12 double media(double a, double b)
13 {
14 return (a + b) / 2;
15 }

Quando o compilador encontra a primeira chamada da funo media na linha 7 da funo


main , ele no tem informao alguma sobre media : no conhece quantos parmetros media
tem, quais os tipos desses parmetros e qual o tipo do valor que media devolve. Ao invs de
uma mensagem de erro, o compilador faz uma tentativa de declarao, chamada de declarao
implcita, da funo media e, em geral, nos passos seguintes da compilao, um ou mais erros
decorrem dessa declarao: um erro do tipo do valor devolvido, do nmero de parmetros ou
do tipo de cada parmetro.
Uma forma de evitar erros de chamadas antes da definio de uma funo dispor o pro-
grama de maneira que a definio da funo preceda todas as suas chamadas. Infelizmente,
nem sempre possvel arranjar um programa dessa maneira e, mesmo quando possvel, pode
tornar mais difcil sua compreenso j que as definies das funes sero dispostas em uma
ordem pouco natural.
Felizmente, a linguagem C oferece uma soluo melhor, com a declarao de uma funo
antes de sua chamada. A declarao de uma funo fornece ao compilador uma viso inicial
da funo cuja declarao completa ser dada posteriormente. A declarao de uma funo
composta exatamente pela primeira linha da definio de uma funo com um ponto e vrgula
adicionado no final:

tipo identificador(parmetros);

Desnecessrio dizer que a declarao de uma funo tem de ser consistente com a defi-
nio da mesma funo. A seguir mostramos como nosso programa ficaria com a adio da
declarao de media :

FACOM UFMS
33.5 D ECLARAO DE FUNES 219

1 #include <stdio.h>
2 double media(double a, double b); /* declarao */
3 int main(void)
4 {
5 double x, y, z;
6 printf("Informe trs valores: ");
7 scanf("%lf%lf%lf", &x, &y, &z);
8 printf("Mdia de %g e %g %g\n", x, y, media(x, y));
9 printf("Mdia de %g e %g %g\n", x, z, media(x, z));
10 printf("Mdia de %g e %g %g\n", y, z, media(y, z));
11 return 0;
12 }
13 double media(double a, double b) /* definio */
14 {
15 return (a + b) / 2;
16 }

A declarao de uma funo tambm conhecida como prottipo da funo. Um prot-


tipo de uma funo fornece ao compilador uma descrio completa de como chamar a funo:
quantos argumentos fornecer, de quais tipos esses argumentos devem ser e qual o tipo do re-
sultado a ser devolvido.

Exerccios

33.1 (a) Escreva uma funo com a seguinte interface:

double areaTriangulo(double base, double altura)

que receba dois nmeros de ponto flutuante que representam a base e a altura de
um tringulo e compute e devolva a rea desse tringulo.
(b) Escreva um programa que receba uma seqncia de n pares de nmeros de ponto
flutuante, onde cada par representa a base e a altura de um tringulo, e calcule e
escreva, para cada par, a rea do tringulo correspondente. Use a funo descrita no
item (a).

FACOM UFMS
33.5 D ECLARAO DE FUNES 220

Programa 33.5: Soluo do exerccio 33.1.

1 #include <stdio.h>
2 double areaTriangulo(double base, double altura)
3 {
4 return (base * altura) / 2;
5 }
6 int main(void)
7 {
8 int i, n;
9 double b, a;
10 printf("Informe n: ");
11 scanf("%d", &n);
12 for (i = 0; i < n; i++) {
13 printf("Informe a base e a altura do tringulo: ");
14 scanf("%lf%lf", &b, &a);
15 printf("rea do tringulo: %g\n", areaTriangulo(b, a));
16 }
17 return 0;
18 }

33.2 (a) Escreva uma funo com a seguinte interface:

int mult(int a, int b)

que receba dois nmeros inteiros positivos a e b e determine e devolva um valor que
representa o produto desses nmeros, usando o seguinte mtodo de multiplicao:
i. dividir, sucessivamente, o primeiro nmero por 2, at que se obtenha 1 como
quociente;
ii. em paralelo, dobrar, sucessivamente, o segundo nmero;
iii. somar os nmeros da segunda coluna que tenham um nmero mpar na pri-
meira coluna; o total obtido o produto procurado.
Por exemplo, para os nmeros 9 e 6, temos que 9 6
9 6 6
4 12
2 24
1 48 48
54
(b) Escreva um programa que leia n > 1 pares de nmeros e calcule os respectivos
produtos desses pares, usando a funo do item (a).
33.3 Para determinar o nmero de lmpadas necessrias para cada aposento de uma residn-
cia, existem normas que fornecem o mnimo de potncia de iluminao exigida por metro
quadrado (m2 ) conforme o uso desse ambiente. Suponha que s temos lmpadas de 60
watts para uso.

FACOM UFMS
33.5 D ECLARAO DE FUNES 221

Seja a seguinte tabela de informaes sobre possveis aposentos de uma residncia:

Utilizao Classe Potncia/m2 (W)


quarto 1 15
sala de TV 1 15
salas 2 18
cozinha 2 18
varandas 2 18
escritrio 3 20
banheiro 3 20

(a) Escreva uma funo com a seguinte interface:

int numLampadas(int classe, double a, double b)

que receba um nmero inteiro representando a classe de iluminao de um aposento


e dois nmeros com ponto flutuante representando suas duas dimenses e devolva
um nmero inteiro representando o nmero de lmpadas necessrias para iluminar
adequadamente o aposento.
(b) Escreva um programa que receba uma seqncia de informaes contendo o nome
do aposento da residncia, sua classe de iluminao e as suas dimenses e, usando
a funo numLampadas , imprima a rea de cada aposento, sua potncia de ilumi-
nao e o nmero total de lmpadas necessrias para o aposento. Alm disso, seu
programa deve calcular o total de lmpadas necessrias e a potncia total necessria
para a residncia toda.
Suponha que o trmino se d quando o nome do aposento informado fim .

FACOM UFMS
A ULA 34

E XERCCIOS

34.1 (a) Escreva uma funo com interface

int mdc(int a, int b)

que receba dois nmeros inteiros positivos a e b e calcule e devolva o mximo divisor
comum entre eles.
(b) Usando a funo do item anterior, escreva um programa que receba n > 1 nmeros
inteiros positivos e calcule o mximo divisor comum entre todos eles.

Programa 34.1: Soluo do exerccio 34.1.

1 #include <stdio.h>
2 int mdc(int a, int b)
3 {
4 int aux;
5 while (b != 0) {
6 aux = a % b;
7 a = b;
8 b = aux;
9 }
10 return a;
11 }
12 int main(void)
13 {
14 int n, i, num, result;
15 scanf("%d", &n);
16 scanf("%d", &result);
17 for (i = 2; i <= n; i++) {
18 scanf("%d", &num);
19 result = mdc(result, num);
20 }
21 printf("%d\n", result);
22 return 0;
23 }

222
223

34.2 (a) Escreva uma funo com interface

int verificaPrimo(int p)

que receba um nmero inteiro positivo p e verifique se p primo, devolvendo 1 em


caso positivo e 0 em caso negativo.
(b) Usando a funo do item anterior, escreva um programa que receba n > 1 nmeros
inteiros positivos e calcule a soma dos que so primos.
34.3 Um nmero inteiro a dito ser permutao de um nmero inteiro b se os dgitos de a
formam uma permutao dos dgitos de b.
Exemplo:
5412434 uma permutao de 4321445, mas no uma permutao de 4312455.
Observao: considere que o dgito 0 (zero) no ocorre nos nmeros.

(a) Escreva uma funo com interface

int contaDigitos(int n, int d)

que receba dois nmeros inteiros n e d, com 0 < d 6 9, devolva um valor que
representa o nmero de vezes que o dgito d ocorre no nmero n.
(b) Usando a funo do item anterior, escreva um programa que leia dois nmeros in-
teiros positivos a e b e responda se a permutao de b.

34.4 (a) Escreva uma funo com interface

int sufixo(int a, int b)

que receba dois nmeros inteiros a e b e verifique se b um sufixo de a. Em caso


positivo, a funo deve devolver 1; caso contrrio, a funo deve devolver 0.
Exemplo:
a b
567890 890 sufixo
1234 1234 sufixo
2457 245 no sufixo
457 2457 no sufixo
(b) Usando a funo do item anterior, escreva um programa que leia dois nmeros in-
teiros a e b e verifique se o menor deles subseqncia do outro.
Exemplo:
a b
567890 678 b subseqncia de a
1234 2212345 a subseqncia de b
235 236 um no subseqncia do outro

FACOM UFMS
224

34.5 Uma seqncia de n nmeros inteiros no nulos dita m-alternante se constituda por
m segmentos: o primeiro com um elemento, o segundo com dois elementos e assim por
diante at o m-simo, com m elementos. Alm disso, os elementos de um mesmo seg-
mento devem ser todos pares ou todos mpares e para cada segmento, se seus elementos
forem todos pares (mpares), os elementos do segmento seguinte devem ser todos mpa-
res (pares).
Por exemplo:

A seqncia com n = 10 elementos: 8 3 7 2 10 4 5 13 9 11 4-alternante.


A seqncia com n = 3 elementos: 7 2 8 2-alternante.
A seqncia com n = 8 elementos: 1 12 4 3 13 5 8 6 no alternante, pois o ltimo
segmento no tem tamanho 4.

(a) Escreva uma funo com interface

int bloco(int m)

que receba como parmetro um inteiro m e leia m nmeros inteiros, devolvendo um


dos seguintes valores:
0, se os m nmeros lidos forem pares;
1, se os m nmeros lidos forem mpares;
1, se entre os m nmeros lidos h nmeros com paridades diferentes.
(b) Usando a funo do item anterior, escreva um programa que, dados um inteiro n,
com n > 1, e uma seqncia de n nmeros inteiros, verifica se a seqncia m-
alternante. O programa deve imprimir o valor de m ou exibir uma mensagem infor-
mando que a seqncia no alternante.

FACOM UFMS
A ULA 35

A RGUMENTOS E PARMETROS DE
FUNES

Como vimos at aqui, a interface de uma funo define muito do que queremos saber sobre
ela: o tipo de valor que a funo devolve, seu identificador e seus parmetros. Isso implica na
conseqente elevao do nvel de abstrao dos nossos programas, j que podemos entender o
que um programa faz sem a necessidade de examinar os detalhes de suas funes. Caso alguns
detalhes sejam de nosso interesse, tambm h a vantagem de que sabemos onde examin-los.
Nesta aula focaremos nos argumentos e parmetros das funes e no escopo de seus dados.

35.1 Argumentos e parmetros

Uma diferena importante entre um parmetro e um argumento deve ser destacada aqui.
Parmetros aparecem na definio de uma funo e so nomes que representam valores a serem
fornecidos quando a funo chamada. J os argumentos so expresses que aparecem nas
chamadas das funes.
Conceitualmente, existem trs tipos de parmetros:

de entrada, que permitem que valores sejam passados para a funo;


de sada, que permite que um valor seja devolvido da funo;
de entrada e sada, que permitem que valores sejam passados para a funo e devolvidos
da funo.

At o momento, entramos em contato com parmetros de entrada e parmetros de sada


nos programas que j fizemos. Os valores dos parmetros de entrada so passados para uma
funo atravs de um mecanismo denominado passagem por cpia ou passagem por valor.
Por exemplo, na chamada

x = quadrado(num);

o valor da expresso num um argumento da funo quadrado e atribudo ao parmetro


correspondente da funo. A passagem desse valor feita por cpia, ou seja, o valor copiado
no parmetro correspondente da funo quadrado .

225
35.1 A RGUMENTOS E PARMETROS 226

J os valores dos parmetros de entrada e sada so passados/devolvidos por um meca-


nismo chamado referncia. Nesse caso, temos uma varivel especificada na chamada da fun-
o e um parmetro especificado na interface da funo que compartilham a mesma rea de
armazenamento na memria e isso significa que qualquer alterao realizada no contedo do
parmetro dentro da funo acarreta alterao no contedo da varivel que o argumento da
chamada. Vejamos um exemplo no programa 35.1.

Programa 35.1: Exemplo de passagem de parmetros por referncia.

1 #include <stdio.h>
2 void decompoe(float x, int *parte_int, float *parte_frac)
3 {
4 *parte_int = (int) x;
5 *parte_frac = x - *parte_int;
6 return;
7 }
8 int main(void)
9 {
10 float num, b;
11 int a;
12 printf("Informe um nmero de ponto flutuante: ");
13 scanf("%f", &num);
14 decompoe(num, &a, &b);
15 printf("Nmero: %f\n", num);
16 printf("Parte inteira: %d\n", a);
17 printf("Parte fracionria: %f\n", b);
18 return 0;
19 }

Uma primeira observao importante sobre a interface da funo decompoe :

void decompoe(float x, int *parte_int, float *parte_frac)

Note que os parmetros parte_int e parte_frac vem precedidos do smbolo * . Essa


a forma que definimos parmetros de entrada e sada na linguagem C. Observe agora o corpo
da funo decompoe :

{
*parte_int = (int) x;
*parte_frac = x - *parte_int;
return;
}

FACOM UFMS
35.2 E SCOPO DE DADOS E DE FUNES 227

Os parmetros de entrada e sada so usados como de costume no corpo de uma funo, isto
, como vimos usando os parmetros de entrada. No entanto, os parmetros de entrada e sada
sempre devem vir precedidos pelo smbolo * no corpo de uma funo, como mostrado acima.
E por ltimo, a chamada da funo decompoe na funo main ligeiramente diferente, como
destacado abaixo:

decompoe(num, &a, &b);

Observe que os argumentos a e b so precedidos pelo smbolo & , indicando que as vari-
veis a e b da funo main , de tipos inteiro e ponto flutuante respectivamente, so passadas
como parmetros de entrada e sada para a funo decompoe , ou seja, so passadas por refe-
rncia, e qualquer alterao realizada nos parmetros correspondentes da funo refletiro nos
argumentos da chamada.

35.2 Escopo de dados e de funes

O escopo de uma funo e das variveis de uma funo o trecho, ou os trechos, de cdigo
em que a funo ou as variveis pode(m) ser acessada(s). Em particular, o escopo de uma
funo determina quais funes podem cham-la e quais ela pode chamar.
Variveis definidas internamente nas funes, bem como os parmetros de uma funo, so
chamadas de variveis locais da mesma, ou seja, o escopo dessas variveis circunscreve-se ao
trecho de cdigo definido pelo corpo da funo. Isso significa que elas so criadas durante a
execuo da funo, manipuladas e destrudas ao trmino de sua execuo, quando o comando
return encontrado.
As funes de um programa so organizadas por nveis. No primeiro nvel temos apenas a
funo principal main , aquela pela qual o programa ser iniciado. As funes que so chama-
das pela funo main so ditas estarem no segundo nvel. No terceiro nvel esto as funes
que so chamadas pelas funes do segundo nvel. E assim sucessivamente. Veja a figura 35.1.

main

Figura 35.1: Um esquema de um programa contendo funes distribudas em trs nveis.

FACOM UFMS
35.2 E SCOPO DE DADOS E DE FUNES 228

Exerccios

35.1 (a) Escreva uma funo com a seguinte interface:

void troca(int *a, int *b)

que receba dois nmeros inteiros a e b e troque os seus contedos.


(b) Usando a funo troca definida acima, escreva um programa que leia um vetor
contendo n nmeros inteiros, com 1 6 n 6 100, ordene seus elementos em ordem
no decrescente usando o mtodo das trocas sucessivas e imprima o vetor resultante
na sada.
(c) Repita a letra (b) implementando o mtodo da seleo.
(d) Repita a letra (b) implementando o mtodo da insero.

Programa 35.2: Soluo do exerccio 35.1(b).

1 #include <stdio.h>
2 #define MAX 100
3 void troca(int *a, int *b)
4 {
5 int aux;
6 aux = *a;
7 *a = *b;
8 *b = aux;
9 return;
10 }
11 int main(void)
12 {
13 int i, j, n, A[MAX];
14 scanf("%d", &n);
15 for (i = 0; i < n; i++)
16 scanf("%d", &A[i]);
17 for (i = n-1; i > 0; i--)
18 for (j = 0; j < i; j++)
19 if (A[j] > A[j+1])
20 troca(&A[j], &A[j+1]);
21 for (i = 0; i < n; i++)
22 printf("%d ", A[i]);
23 printf("\n");
24 return 0;
25 }

FACOM UFMS
35.2 E SCOPO DE DADOS E DE FUNES 229

35.2 Em um dado pas a moeda corrente possui apenas quatro cdulas de papel: $1, $5, $10 e
$20.

(a) Escreva uma funo com a seguinte interface:

void cedulas(int valor, int *um, int *cinco, int *dez, int *vinte)

que receba um valor em dinheiro e determine a menor quantidade de cdulas de 1,


5, 10 e 20 necessrias para pagar o valor especificado.
(b) Escreva um programa que dado um valor na moeda corrente determine a menor
quantidade de cdulas para pagar esse valor. Use a funo do item (a).

35.3 Dizemos que um nmero natural n palndromo se lido da esquerda para direita e da
direita para esquerda o mesmo nmero.
Exemplos:

567765 palndromo.
32423 palndromo.
567675 no palndromo.

(a) Escreva uma funo com a seguinte interface:

void quebra(int n, int *prim, int *ult, int *miolo)

que receba um nmero do tipo inteiro n > 0 e devolva trs valores do tipo inteiro:
o primeiro dgito de n, o ltimo dgito de n e um inteiro que represente o nmero n
sem seu primeiro e ltimo dgitos.
Exemplo:
valor inicial de n primeiro dgito ltimo dgito miolo de n
732 7 2 3
14738 1 8 473
78 7 8 0
7 7 7 0
(b) Usando a funo do item (a), escreva um programa que receba um inteiro positivo
n > 0 e verifique se n palndromo. Suponha que n no contm o dgito 0.

35.4 (a) Escreva uma funo com a seguinte interface:

int divisao(int *m, int *n, int d)

que receba trs valores positivos do tipo inteiro m, n e d e devolva 1 se d divide


m, n ou ambos, e 0, caso contrrio. Alm disso, em caso positivo, a funo deve
devolver um valor que representa o quociente da diviso de m por d e outro valor
que representa o quociente da diviso de n por d.

FACOM UFMS
35.2 E SCOPO DE DADOS E DE FUNES 230

(b) Escreva um programa que leia dois inteiros positivos m e n e calcule, usando a fun-
o do item (a), o mnimo mltiplo comum entre m e n.

35.5 (a) Escreva uma funo com a seguinte interface:

int somabit(int b1, int b2, int *vaium)

que receba trs valores inteiros e devolva um valor do tipo inteiro representando a
soma dos trs bits, e devolva tambm um outro valor do tipo inteiro representando
o valor do vai um.
(b) Escreva um programa que leia dois nmeros no sistema binrio de numerao e,
usando a funo do item (a), calcule um nmero em binrio que a soma dos dois
nmeros dados.

FACOM UFMS
A ULA 36

F UNES E VETORES

Nas aulas 33, 34 e 35 vimos funes, argumentos de funes e passagem de parmetros


para funes na linguagem C, onde trabalhamos apenas com argumentos de tipos bsicos. Ob-
serve que chegamos a passar o valor de um elemento de um vetor como um argumento para
uma funo, por cpia e por referncia. Nesta aula veremos como realizar passagem de par-
metros de tipos complexos, mais especificamente de parmetros que so variveis compostas
homogneas unidimensionais ou vetores.

36.1 Vetores como argumentos de funes

Assim como fizemos com variveis de tipos bsicos, possvel passar um vetor todo como
argumento em uma chamada de uma funo. O parmetro correspondente deve ser um vetor
com a mesma dimenso. Vejamos um exemplo no programa 36.1.

Programa 36.1: Um programa com uma funo que tem um vetor como parmetro.

1 #include <stdio.h>
2 int minimo(int A[10])
3 {
4 int i, min;
5 min = A[0];
6 for (i = 1; i < 10; i++)
7 if (A[i] < min)
8 min = A[i];
9 return min;
10 }
11 int main(void)
12 {
13 int i, vet[10];
14 for (i = 0; i < 10; i++)
15 scanf("%d", &vet[i]);
16 printf("O menor valor do conjunto %d\n", minimo(vet));
17 return 0;
18 }

231
36.2 V ETORES SO PARMETROS PASSADOS POR REFERNCIA 232

Observe a interface da funo minimo do programa acima. Essa interface indica que a fun-
o devolve um valor do tipo inteiro, tem identificador minimo e tem como seu argumento um
vetor contendo 10 elementos do tipo inteiro. Referncias feitas ao parmetro A , no interior da
funo minimo , acessam os elementos apropriados dentro do vetor que foi passado funo.
Para passar um vetor inteiro para uma funo necessrio apenas descrever o identificador
do vetor, sem qualquer referncia a ndices, na chamada da funo. Essa situao pode ser
visualizada na ltima linha do programa principal, na chamada da funo minimo(pontos) .
importante destacar tambm que a nica restrio de devoluo de uma funo relativa
s variveis compostas homogneas. De fato, um valor armazenado em uma varivel de qual-
quer tipo pode ser devolvido por uma funo, excluindo variveis compostas homogneas.

36.2 Vetores so parmetros passados por referncia

Nesta aula, o que temos visto at o momento so detalhes de como passar vetores como
parmetros para funes, detalhes na chamada e na interface de uma funo que usa vetores
como parmetros. No entanto, esses elementos por si s no so a principal peculiaridade dos
vetores neste contexto. No programa 36.2 a seguir iremos perceber a verdadeira diferena do
uso de vetores como parmetros em funes.

Programa 36.2: Um vetor sempre um parmetro passado por referncia para uma funo.

1 #include <stdio.h>
2 void dobro(int vetor[100], int n)
3 {
4 int i;
5 for (i = 0; i < n; i++)
6 vetor[i] = vetor[i] * 2;
7 return;
8 }
9 int main(void)
10 {
11 int i, n, valor[100];
12 printf("Informe a quantidade de elementos: ");
13 scanf("%d", &n);
14 for (i = 0; i < n; i++) {
15 printf("Informe o %d-simo valor: ", i+1);
16 scanf("%d", &valor[i]);
17 }
18 dobro(valor, n);
19 printf("Resultado: ");
20 for (i = 0; i < n; i++)
21 printf("%d ", valor[i]);
22 printf("\n");
23 return 0;
24 }

FACOM UFMS
36.3 V ETORES COMO PARMETROS COM DIMENSES OMITIDAS 233

Perceba que a funo dobro do programa acima modifica os valores do vetor valor , uma
varivel local do programa principal que passada como parmetro para a funo dobro na
varivel vetor , uma varivel local da funo dobro . importante observar que, quando
usamos vetores como argumentos, uma funo que modifica o valor de um elemento do ve-
tor, modifica tambm o vetor original que foi passado como parmetro para a funo. Esta
modificao tem efeito mesmo aps o trmino da execuo da funo.
Nesse sentido, podemos dizer que um vetor, uma matriz ou uma varivel composta ho-
mognea de qualquer dimenso sempre um parmetro de entrada e sada, isto , sempre
passado por referncia a uma funo.
No entanto, lembre-se que a modificao de elementos de um vetor em uma funo se
aplica somente quando o vetor completo passado como parmetro funo e no elementos
individuais do vetor. Esse ltimo caso j foi tratado em aulas anteriores.

36.3 Vetores como parmetros com dimenses omitidas

Na definio de uma funo, quando um de seus parmetros um vetor, seu tamanho


pode ser deixado sem especificao. Essa prtica de programao freqente e permite que
a funo torne-se ainda mais geral. Dessa forma, a funo dobro , por exemplo, definida na
seo anterior, poderia ser reescrita como a seguir:

void dobro(int vetor[], int n)


{
int i;
for (i = 0; i < n; i++)
vetor[i] = vetor[i] * 2;
return;
}

Observe que a dimenso do parmetro vetor da funo dobro , que um vetor, foi omi-
tida. A duas definies da funo dobro , nesta seo e na seo anterior, so equivalentes. No
entanto, a definio com a dimenso omitida permite que a funo dobro possa ser chamada
com vetores de quaisquer dimenses como argumentos.
Por exemplo, se temos dois vetores de inteiros A e B, com dimenses 50 e 200, respecti-
vamente, a funo dobro pode ser chamada para computar o dobro de cada coordenada do
vetor A e, do mesmo modo, tambm pode se chamada para computar o dobro de cada coor-
denada do vetor B. Ou seja, a funo dobro pode ser chamada com vetores de dimenses
diferentes como parmetros.

dobro(A, 50);
dobro(B, 200);

FACOM UFMS
36.3 V ETORES COMO PARMETROS COM DIMENSES OMITIDAS 234

Exerccios

36.1 (a) Escreva uma funo com a seguinte interface:

int subconjunto(int A[MAX], int m, int B[MAX], int n)

que receba como parmetros um vetor A de nmeros inteiros com m elementos e


um vetor B de nmeros inteiros com n elementos, ambos representando conjuntos,
e verifica se A est contido em B (A B).
(b) Escreva um programa que receba dois vetores de nmeros inteiros U e W , com u e
w elementos respectivamente, 1 6 u, w 6 100, e verifique se os dois conjuntos so
iguais (U = W se e somente se U W e W U ). Use a funo do item (a).

Programa 36.3: Soluo do exerccio 36.1.

1 #include <stdio.h>
2 #define MAX 100
3 int subconjunto(int A[MAX], int m, int B[MAX], int n)
4 {
5 int i, j, contido;
6 contido = 1;
7 for (i = 0; i < m && contido; i++) {
8 for (j = 0; j < n && B[j] != A[i]; j++)
9 ;
10 if (j == n)
11 contido = 0;
12 }
13 return contido;
14 }
15 int main(void)
16 {
17 int tamU, i, U[MAX], tamW, j, W[MAX];
18 scanf("%d", &tamU);
19 for (i = 0; i < tamU; i++)
20 scanf("%d", &U[i]);
21 scanf("%d", &tamW);
22 for (j = 0; j < tamW; j++)
23 scanf("%d", &W[j]);
24 if (subconjunto(U, tamU, W, tamW) && subconjunto(W, tamW, U, tamU))
25 printf("U == W\n");
26 else
27 printf("U != W\n");
28 return 0;
29 }

FACOM UFMS
36.3 V ETORES COMO PARMETROS COM DIMENSES OMITIDAS 235

36.2 Em um programa na linguagem C, um conjunto pode ser representado por um vetor da


seguinte forma: V[0] contm o nmero de elementos do conjunto; V[1], V[2], ...
so os elementos do conjunto, sem repeties.

(a) Escreva uma funo com a seguinte interface:

void intersec(int A[MAX+1], int B[MAX+1], int C[MAX+1])

que dados dois conjuntos de nmeros inteiros A e B, construa um terceiro conjunto


C tal que C = A B. Lembre-se de que em C[0] a sua funo deve colocar o
tamanho da interseco.
(b) Escreva um programa que leia um nmero inteiro n > 2 e uma seqncia de n
conjuntos de nmeros inteiros, cada um com no mximo 100 elementos, e construa
e imprima um vetor que representa a interseco dos n conjuntos.

Observao: no leia todos os conjuntos de uma s vez. Leia os dois primeiros conjuntos e
calcule a primeira interseco. Depois, leia o prximo conjunto e calcule uma nova inter-
seco entre esse conjunto lido e o conjunto da interseco anterior, e assim por diante.

36.3 (a) Escreva uma funo com a seguinte interface:

void ordena_insercao(int A[MAX], int m)

que receba um vetor A de m nmeros inteiros distintos, com 1 6 m 6 100, e ordene


os elementos desse vetor em ordem crescente usando o mtodo da insero.
(b) Escreva uma funo com a seguinte interface:

void intercala(int A[MAX], int m, int B[MAX], int n,


int C[2*MAX], int *k)

que receba um vetor A de nmeros inteiros distintos e ordenados em ordem cres-


cente de dimenso m e um vetor B de nmeros inteiros distintos e ordenados em
ordem crescente de dimenso m e compute um vetor C contendo os elementos de A
e de B sem repetio e em ordem crescente.
(c) Escreva um programa que receba dois conjuntos de nmeros inteiros e distintos X e
Y , com no mximo 100 elementos, ordene cada um deles usando a funo do item (a)
e intercale esses dois vetores usando a funo do item (b), obtendo como resultado
um vetor ordenado de nmeros inteiros distintos dois a dois.

FACOM UFMS
A ULA 37

F UNES E MATRIZES

Na aula 36 vimos funes e vetores e destacamos como estas estruturas so tratadas de


forma diferenciada neste caso. Em especial, muito importante lembrar que vetores so sem-
pre parmetros passados por referncia s funes na linguagem C. Como veremos adiante,
isso vale para qualquer varivel composta homognea, isto , para variveis compostas homo-
gneas de qualquer dimenso.

37.1 Matrizes

Um elemento de uma matriz pode ser passado como parmetro para uma funo assim
como fizemos com uma varivel de um tipo bsico ou como um elemento de um vetor. Ou
seja, a sentena

p = paridade(matriz[i][j]);

chama a funo paridade passando o valor contido em matriz[i][j] como um argumento


para a funo.
Uma matriz toda pode ser passada a uma funo da mesma forma que um vetor todo pode
ser passado, bastando listar o identificador da matriz. Por exemplo, se a matriz A declarada
como uma matriz bidimensional de nmeros inteiros, a sentena

multiplicaEscalar(A, escalar);

pode ser usada para chamar uma funo que multiplica cada elemento de uma matriz A pelo
valor armazenado na varivel escalar . Assim como com vetores, uma modificao realizada
no corpo de uma funo sobre qualquer elemento de uma matriz que um parmetro dessa
funo provoca uma modificao no argumento da chamada da funo, ou seja, na matriz que
foi passada funo durante sua chamada.
O programa 37.1 recebe uma matriz A44 de nmeros inteiros e um escalar e realiza o
produto da matriz por esse escalar.

236
37.2 M ATRIZES COMO PARMETROS COM UMA DIMENSO OMITIDA 237

Programa 37.1: Uma funo com um parmetro que uma matriz.

1 #include <stdio.h>
2 void multiplicaEscalar(int A[4][4], int escalar)
3 {
4 int i, j;
5 for(i = 0; i < 4; i++)
6 for(j = 0; j < 4; j++)
7 A[i][j] = A[i][j] * escalar;
8 return;
9 }
10 void imprimeMatriz(int A[4][4])
11 {
12 int i, j;
13 for(i = 0; i < 4; i++) {
14 for(j = 0; j < 4; j++)
15 printf("%3d ", A[i][j]);
16 printf("\n");
17 }
18 return;
19 }
20 int main(void)
21 {
22 int i, j, esc, matriz[4][4];
23 scanf("%d", &esc);
24 for(i = 0; i < 4; i++)
25 for(j = 0; j < 4; j++)
26 scanf("%d", &matriz[i][j]);
27 imprimeMatriz(matriz);
28 multiplicaEscalar(matriz, esc);
29 imprimeMatriz(matriz);
30 return 0;
31 }

37.2 Matrizes como parmetros com uma dimenso omitida

Se um parmetro de uma funo uma varivel composta homognea k-dimensional, com


k > 2, ento somente o tamanho da primeira dimenso pode ser omitida quando o parmetro
declarado. Por exemplo, na funo multiplicaEscalar da seo anterior, onde A um de
seus parmetros e uma matriz, o nmero de colunas de A deve ser espeficiado, embora seu
nmero de linhas no seja necessrio. Assim, na definio da funo, poderamos escrever:

void multiplicaEscalar(int A[][4], int escalar)

FACOM UFMS
37.2 M ATRIZES COMO PARMETROS COM UMA DIMENSO OMITIDA 238

Exerccios

37.1 (a) Escreva uma funo com a seguinte interface:

void troca(int *a, int *b)

que receba dois nmeros inteiros a e b e troque os seus contedos.


(b) Usando a funo anterior, escreva um programa que receba uma matriz de nmeros
inteiros A, de dimenso m n, com 1 6 m, n 6 100, e dois nmeros inteiros i e j,
troque os contedos das linhas i e j da matriz A e imprima a matriz resultante.

Programa 37.2: Soluo do exerccio 37.1.

1 #include <stdio.h>
2 #define MAX 100
3 void troca(int *a, int *b)
4 {
5 int aux;
6 aux = *a;
7 *a = *b;
8 *b = aux;
9 return;
10 }
11 int main(void)
12 {
13 int m, n, i, j, k, A[MAX][MAX];
14 scanf("%d%d", &m, &n);
15 for(i = 0; i < m; i++)
16 for(j = 0; j < n; j++)
17 scanf("%d", &A[i][j]);
18 scanf("%d%d", &i, &j);
19 for (k = 0; k < n; k++)
20 troca(&A[i][k], &A[j][k]);
21 for (i = 0; i < m; i++) {
22 for (j = 0; j < n; j++)
23 printf("%d ", A[i][j]);
24 printf("\n");
25 }
26 return 0;
27 }

37.2 A k-sima potncia de uma matriz quadrada Ann , denotada por Ak , a matriz obtida a
partir da matriz A da seguinte forma:
k
z }| {
k
A = In A A A ,

FACOM UFMS
37.2 M ATRIZES COMO PARMETROS COM UMA DIMENSO OMITIDA 239

onde In a matriz identidade de ordem n.


Para facilitar a computao de Ak , podemos usar a seguinte frmula:

k In , se k = 0 ,
A = k1
A A, se k > 0 .

(a) Escreva uma funo com a seguinte interface:

void identidade(int I[MAX][MAX], int n)

que receba uma matriz I e uma dimenso n e preencha essa matriz com os valores
da matriz identidade de ordem n.
(b) Escreva uma funo com a seguinte interface:

void multMat(int C[MAX][MAX],


int A[MAX][MAX], int B[MAX][MAX], int n)

que receba as matrizes A, B e C, todas de ordem n, e compute C = A B.


(c) Escreva uma funo com a seguinte interface:

void copia(int A[MAX][MAX], int B[MAX][MAX], int n)

que receba as matrizes A e B de ordem n, e copie os elementos da matriz B na matriz


A.
(d) Escreva um programa que, usando as funes em (a) e (b), receba uma matriz de
nmeros inteiros A de dimenso n e um inteiro k e compute e imprima Ak .

37.3 Dizemos que uma matriz Ann um quadrado latino de ordem n se em cada linha e
em cada coluna aparecem todos os inteiros 1, 2, 3, . . . , n, ou seja, cada linha e coluna
permutao dos inteiros 1, 2, . . . , n.
Exemplo:

1 2 3 4
2 3 4 1

4 1 2 3
3 4 1 2

A matriz acima um quadrado latino de ordem 4.

(a) Escreva uma funo com a seguinte interface:

int linha(int A[MAX][MAX], int n, int i)

FACOM UFMS
37.2 M ATRIZES COMO PARMETROS COM UMA DIMENSO OMITIDA 240

que receba como parmetros uma matriz Ann de nmeros inteiros e um ndice i, e
verifique se na linha i de A ocorrem todos os nmeros inteiros de 1 a n, devolvendo
1 em caso positivo e 0 caso contrrio.
(b) Escreva uma funo com a seguinte interface:

int coluna(int A[MAX][MAX], int n, int j)

que receba como parmetros uma matriz Ann de nmeros inteiros e um ndice j, e
verifique se na coluna j de A ocorrem todos os nmeros inteiros de 1 a n, devolvendo
1 em caso positivo e 0 caso contrrio.
(c) Usando as funes dos itens (a) e (b), escreva um programa que verifique se uma
dada matriz Ann de nmeros inteiros, com 1 6 n 6 100, um quadrado latino de
ordem n.

FACOM UFMS
A ULA 38

F UNES E REGISTROS

J vimos funes com parmetros de tipos bsicos e de tipos complexos, como vetores e
matrizes. Nesta aula, aprenderemos como comportam-se os registros no contexto das funes.
Como veremos, um registro, diferentemente das variveis compostas homogneas, comporta-
se como uma varivel de um tipo bsico qualquer quando trabalhamos com funes. Ou seja,
um registro comporta-se como qualquer outra varivel excluindo as compostas homogneas,
isto , um registro sempre um argumento passado por cpia a uma funo. Se quisermos
que um registro seja um argumento passado por referncia a uma funo, devemos explici-
tamente indicar essa opo usando o smbolo & no argumento e o smbolo * no parmetro
correspondente.

38.1 Tipo registro

Antes de estudar diretamente o funcionamento das funes com registros, precisamos


aprender algo mais sobre esses ltimos. Primeiro, importante notar que apesar das aulas
anteriores mostrarem como declarar variveis que so registros, no discutimos em detalhes
o que um tipo registro. Ento, suponha que um programa precisa declarar diversas vari-
veis que so registros com mesmos campos. Se todas as variveis podem ser declaradas ao
mesmo tempo, no h problemas. Mas se for necessrio declarar essas variveis em diferentes
pontos do programa, ento temos uma situao mais complicada. Para super-la, precisamos
saber definir um nome que representa um tipo registro, no uma varivel registro particular.
A linguagem C fornece duas boas maneiras para realizar essa tarefa: declarar uma etiqueta de
registro ou usar typedef para criar um tipo registro.
Uma etiqueta de registro um nome usado para identificar um tipo particular de registro.
Essa etiqueta no reserva compartimentos na memria para armazenamento dos campos e do
registro, mas sim adiciona informaes a uma tabela usada pelo compilador para que variveis
registros possam ser declaradas a partir dessa etiqueta. A seguir temos a declarao de uma
etiqueta de registro com nome cadastro :

struct cadastro {
int codigo;
char nome[MAX+1];
int fone;
};

241
38.1 T IPO REGISTRO 242

Observe que o caractere ponto e vrgula ( ; ) segue o caractere fecha chaves } . O ; deve
estar presente neste ponto para terminar a declarao da etiqueta do registro.
Uma vez que criamos a etiqueta cadastro , podemos us-la para declarar variveis, como
a seguir:

struct cadastro ficha1, ficha2;

Infelizmente, no podemos abreviar a declarao acima removendo a palavra reservada


struct , j que cadastro no o nome de um tipo. Isso significa que sem a palavra reservada
struct a declarao acima no tem sentido.
Tambm, a declarao de uma etiqueta de um registro pode ser combinada com a declara-
o de variveis registros, como mostrado a seguir:

struct cadastro {
int codigo;
char nome[MAX+1];
int fone;
} ficha1, ficha2;

Ou seja, acima temos a declarao da etiqueta de registro cadastro e a declarao de duas


variveis registros ficha1 e ficha2 .
Todas as variveis registros declaradas com o tipo struct cadastro so compatveis en-
tre si e, portanto, podemos atribuir uma a outra sem problemas, com mostramos abaixo:

struct cadastro ficha1 = {10032, "Sir Lancelot", 77165115};


struct cadastro ficha2;
ficha2 = ficha1;

Uma alternativa para declarao de etiquetas de registros o uso da palavra reservada


typedef para definir o nome de um tipo genuno. Por exemplo, poderamos definir um tipo
com nome Cadastro da seguinte forma:

typedef struct {
int codigo;
char nome[MAX+1];
int fone;
} Cadastro;

FACOM UFMS
38.2 R EGISTROS E PASSAGEM POR CPIA 243

Observe que o nome do tipo, Cadastro , deve ocorrer no final, no aps a palavra reser-
vada struct . Alm disso, como Cadastro um nome de um tipo definido por typedef ,
no permitido escrever struct Cadastro . Todas as variveis do tipo Cadastro so com-
patveis, desconsiderando onde foram declaradas. Assim, a declarao de registros com essa
definio pode ser dada como abaixo:

Cadastro ficha1, ficha2;

Quando precisamos declarar um nome para um registro, podemos escolher entre decla-
rar uma etiqueta de registro ou definir um tipo registro com a palavra reservada typedef .
Entretanto, em geral preferimos usar etiquetas de registros por diversos motivos que sero
justificados posteriormente.

38.2 Registros e passagem por cpia

Um campo de um registro pode ser passado como parmetro para uma funo assim como
fizemos com uma varivel de um tipo bsico ou com uma clula de um vetor ou matriz. Ou
seja, a sentena

x = raizQuadrada(poligono.diagonal);

chama a funo raizQuadrada com argumento poligono.diagonal , isto , com o valor


armazenado no campo diagonal do registro poligono .
Um registro todo pode ser argumentos de funes da mesma forma que um vetor ou uma
matriz podem ser, bastando listar o identificador do registro. Por exemplo, se temos um registro
tempo , contendo os campos hora , minutos e segundos , ento a sentena

s = totalSegundos(tempo);

pode ser usada para chamar a funo totalSegundos que provavelmente calcula o total de
segundos de uma medida de tempo armazenada no registro tempo .
Diferentemente das variveis compostas homogneas, uma modificao realizada em qual-
quer campo de um registro que um parmetro de uma funo no provoca uma modificao
no contedo do campo correspondente que um argumento da funo. Nesse caso, a princpio,
um argumento que um registro em uma chamada de uma funo tem o mesmo comporta-
mento de um argumento que uma varivel de um tipo bsico qualquer, ou seja, uma expres-
so que um argumento de uma chamada de uma funo e que, na verdade um registro,
de fato um parmetro de entrada da funo, passado por cpia.

FACOM UFMS
38.2 R EGISTROS E PASSAGEM POR CPIA 244

Um exemplo mostrado no programa 38.1.

Programa 38.1: Um programa com uma funo com parmetro do tipo registro.

1 #include <stdio.h>
2 struct tipo_reg {
3 int hh;
4 int mm;
5 int ss;
6 };
7 int converteSegundos(struct tipo_reg tempo)
8 {
9 return tempo.hh * 3600 + tempo.mm * 60 + tempo.ss;
10 }
11 int main(void)
12 {
13 int segundos;
14 struct tipo_reg horario;
15 printf("Informe um horario (hh:mm:ss): ");
16 scanf("%d:%d:%d", &horario.hh, &horario.mm, &horario.ss);
17 segundos = converteSegundos(horario);
18 printf("Passaram-se %d segundo(s) neste dia.\n", segundos);
19 return 0;
20 }

Devemos destacar pontos importantes neste programa. O primeiro, e talvez mais impor-
tante, a definio da etiqueta de registro tipo_reg . Em nenhuma outra oportunidade ante-
rior havamos nos desenvolvido um programa que continha uma declarao de uma etiqueta
de registro. Observe ainda que a declarao da etiqueta tipo_reg do programa 38.1 foi feita
fora dos corpos das funes converteSegundos e main . Em todos os programas descritos at
aqui, no temos um exemplo de uma declarao de uma etiqueta de registro, ou mesmo uma
declarao de uma varivel de qualquer tipo, fora do corpo de qualquer funo. A declarao
da etiqueta tipo_reg realizada dessa maneira se deve ao fato que a mesma usada nas fun-
es converteSegundos e main , obrigando que essa declarao seja realizada globalmente,
ou seja, fora do corpo de qualquer funo.
Os outros pontos de destaque do programa 38.1 so todos relativos ao argumento da cha-
mada da funo converteSegundos e do parmetro da mesma, descrito em sua interface.
Note que essa funo tem como parmetro o registro tempo do tipo struct tipo_reg . O
argumento da funo, na chamada dentro da funo main o registro horario de mesmo
tipo struct tipo_reg .
Finalmente, importante observar mais uma vez que a passagem de um registro para
uma funo feita por cpia, ou seja, o parmetro correspondente um parmetro de en-
trada e, por isso, qualquer alterao realizada em um ou mais campos do registro dentro da
funo no afeta o contedo dos campos do registro que um argumento na sua chamada.
No exemplo acima no h alteraes nos campos do registro tempo no corpo da funo

FACOM UFMS
38.3 R EGISTROS E PASSAGEM POR REFERNCIA 245

converteSegundos , mas, caso houvesse, isso no afetaria qualquer um dos campos do re-
gistro horario , o argumento da chamada da funo converteSegundos no corpo da funo
main .

38.3 Registros e passagem por referncia

Se precisamos de uma funo com um parmetro de entrada e sada, isto , um parmetro


passado por referncia, devemos fazer como usualmente fazemos com qualquer varivel de
um tipo bsico e usar os smbolos usuais & e * . O tratamento dos campos do registro em
questo tem uma peculiaridade que precisamos observar com cuidado e se refere ao uso do
smbolo * no parmetro passado por referncia no corpo da funo. Vejamos um exemplo no
programa 38.2.

Programa 38.2: Funo com um registro como parmetro de entrada e sada.

1 #include <stdio.h>
2 struct tipo_reg {
3 int hh;
4 int mm;
5 int ss;
6 };
7 void adicionaSegundo(struct tipo_reg *tempo)
8 {
9 (*tempo).ss++;
10 if ((*tempo).ss == 60) {
11 (*tempo).ss = 0;
12 (*tempo).mm++;
13 if ((*tempo).mm == 60) {
14 (*tempo).mm = 0;
15 (*tempo).hh++;
16 if ((*tempo).hh == 24)
17 (*tempo).hh = 0;
18 }
19 }
20 return;
21 }
22 int main(void)
23 {
24 struct tipo_reg horario;
25 printf("Informe um horario (hh:mm:ss): ");
26 scanf("%d:%d:%d", &horario.hh, &horario.mm, &horario.ss);
27 adicionaSegundo(&horario);
28 printf("Novo horrio %02d:%02d:%02d.\n", horario.hh, horario.mm, horario.ss);
29 return 0;
30 }

FACOM UFMS
38.3 R EGISTROS E PASSAGEM POR REFERNCIA 246

Uma observao importante sobre o programa 38.2 a forma como um registro que um
parmetro passado por referncia apresentado no corpo da funo. Como o smbolo . que
seleciona um campo de um registro tem prioridade sobre o smbolo * , que indica que o par-
metro foi passado por referncia, os parnteses envolvendo o o smbolo * e o identificador do
registro so essenciais para evitar erros. Dessa forma, uma expresso envolvendo, por exem-
plo, o campo ss do registro tempo , escrita como *tempo.ss , est incorreta e no realiza a
seleo do campo do registro que tencionamos.
Alternativamente, podemos usar os smbolos -> para indicar o acesso a um campo de um
registro que foi passado por referncia a uma funo. O uso desse smbolo simplifica bastante
a escrita do corpo das funes que tm parmetros formais que so registros passados por
referncia. Dessa forma, o programa 38.2 pode ser reescrito como o programa 38.3. Observe
que esses dois programas tm a mesma finalidade, isto , dada uma entrada, realizam o mesmo
processamento e devolvem as mesmas sadas.

Programa 38.3: Uso do smbolo -> para registros passados por referncia.

1 #include <stdio.h>
2 struct tipo_reg {
3 int hh;
4 int mm;
5 int ss;
6 };
7 void adicionaSegundo(struct tipo_reg *tempo)
8 {
9 tempo->ss++;
10 if (tempo->ss == 60) {
11 tempo->ss = 0;
12 tempo->mm++;
13 if (tempo->mm == 60) {
14 tempo->mm = 0;
15 tempo->hh++;
16 if (tempo->hh == 24)
17 tempo->hh = 0;
18 }
19 }
20 return;
21 }
22 int main(void)
23 {
24 struct tipo_reg horario;
25 printf("Informe um horario (hh:mm:ss): ");
26 scanf("%d:%d:%d", &horario.hh, &horario.mm, &horario.ss);
27 adicionaSegundo(&horario);
28 printf("Novo horrio %02d:%02d:%02d.\n", horario.hh, horario.mm, horario.ss);
29 return 0;
30 }

FACOM UFMS
38.4 F UNES QUE DEVOLVEM REGISTROS 247

Observe que o argumento passado por referncia para a funo adicionaSegundo idn-
tico nos programas 38.2 e 38.3. Mais que isso, a interface da funo adicionaSegundo idn-
tica nos dois casos e o parmetro declarado como struct tipo_reg *tempo . No corpo
desta funo do programa 38.3 que usamos o smbolo -> como uma abreviao, ou uma
simplificao, da notao usada no corpo da funo adicionaSegundo do programa 38.2. As-
sim, por exemplo, a expresso (*tempo).ss no programa 38.2 reescrita como tempo->ss
no programa 38.3.

38.4 Funes que devolvem registros

Uma funo pode devolver valores de tipos bsicos, mas tambm pode devolver um va-
lor de um tipo complexo como um registro. Observe que variveis compostas homogneas,
pelo fato de sempre poderem ser argumentos que correspondem a parmetros de entrada e
sada, isto , de sempre poderem ser passadas por referncia, nunca so devolvidas explicita-
mente por uma funo, atravs do comando return . O nico valor de um tipo complexo que
podemos devolver um valor do tipo registro.
No problema da seo anterior, em que dado um horrio e queremos atualiz-lo em um se-
gundo, usamos um registro como parmetro de entrada e sada na funo adicionaSegundo .
Podemos fazer com que a funo apenas receba uma varivel do tipo registro contendo um ho-
rrio como parmetro de entrada e devolva um registro com o horrio atualizado como sada.
Assim, os programas 38.2 e 38.3 poderiam ser reescritos como no programa 38.4.
Observe que a interface da funo adicionaSegundo diferente nesse caso. O tipo do
valor devolvido o tipo struct tipo_reg , indicando que a funo devolver um registro.
O corpo dessa funo tambm diferente das anteriores e h a declarao de uma varivel
atualizado do tipo struct tipo_reg que ao final conter o horrio do registro tempo ,
parmetro de entrada da funo, atualizado em um segundo. Na funo principal, a chamada
da funo adicionaSegundo tambm diferente, j que descrita do lado direito de um
comando de atribuio, indicando que o registro devolvido pela funo ser armazenado no
registro descrito do lado esquerdo da atribuio: novo = adicionaSegundo(agora); .

FACOM UFMS
38.4 F UNES QUE DEVOLVEM REGISTROS 248

Programa 38.4: Um programa com uma funo que devolve um registro.

1 #include <stdio.h>
2 struct tipo_reg {
3 int hh;
4 int mm;
5 int ss;
6 };
7 struct tipo_reg adicionaSegundo(struct tipo_reg tempo)
8 {
9 struct tipo_reg atualizado;
10 atualizado.ss = tempo.ss + 1;
11 if (atualizado.ss == 60) {
12 atualizado.ss = 0;
13 atualizado.mm = tempo.mm + 1;
14 if (atualizado.mm == 60) {
15 atualizado.mm = 0;
16 atualizado.hh = tempo.hh + 1;
17 if (atualizado.hh == 24)
18 atualizado.hh = 0;
19 }
20 else
21 atualizado.hh = tempo.hh;
22 }
23 else {
24 atualizado.mm = tempo.mm;
25 atualizado.hh = tempo.hh;
26 }
27 return atualizado;
28 }
29 int main(void)
30 {
31 struct tipo_reg agora, novo;
32 printf("Informe um horario (hh:mm:ss): ");
33 scanf("%d:%d:%d", &agora.hh, &agora.mm, &agora.ss);
34 novo = adicionaSegundo(agora);
35 printf("Novo horrio %02d:%02d:%02d.\n", novo.hh, novo.mm, novo.ss);
36 return 0;
37 }

Exerccios

38.1 Este exerccio tem o mesmo objetivo do exerccio 27.1.

(a) Escreva uma funo com a seguinte interface:

FACOM UFMS
38.4 F UNES QUE DEVOLVEM REGISTROS 249

int bissexto(struct data d)

que receba uma data e verifique se o ano bissexto, devolvendo 1 em caso positivo
e 0 caso contrrio. Um ano bissexto se divisvel por 4 e no por 100 ou divisvel
por 400.
(b) Escreva uma funo com a seguinte interface:

int diasMes(struct data d)

que receba uma data e devolva o nmero de dias do ms em questo.


Exemplo:
Se a funo recebe a data 10/04/1992 deve devolver 30 e se recebe a data
20/02/2004 deve devolver 29 .
(c) Escreva uma funo com a seguinte interface:

struct data diaSeguinte(struct data d)

que receba uma data e devolva a data que representa o dia seguinte. Use as funes
dos itens (a) e (b).
(d) Escreva um programa que receba uma data e imprima a data que representa o dia
seguinte. Use as funes dos itens anteriores.

38.2 Reescreva a funo do item (c) do exerccio 38.1, fazendo com que a funo diaSeguinte
tenha o registro d como um parmetro de entrada e sada. Isto , a interface da funo
deve ser:

void diaSeguinte(struct data *d)

38.3 (a) Escreva uma funo com a seguinte interface:

struct tempo tempoDecorr(struct tempo t1, struct tempo t2)

receba duas medidas de tempo no formato hh:mm:ss e calcule o tempo decorrido


entre essas duas medidas de tempo. Tenha cuidado com um par de medidas de
tempo que cruzam a meia noite.
(b) Escreva um programa que receba dois horrios e calcule o tempo decorrido entre
esses dois horrios. Use a funo anterior.

FACOM UFMS
A ULA 39

R ECURSO

O conceito de recurso tem importncia fundamental em computao. Na linguagem C,


uma funo recursiva aquela que contm em seu corpo uma ou mais chamadas a si mesma.
Naturalmente, uma funo deve possuir pelo menos uma chamada proveniente de uma outra
funo externa. Uma funo no recursiva, em contrapartida, aquela para qual todas as
suas chamadas so externas. Nesta aula construiremos funes recursivas para solues de
problemas.

39.1 Definio

Em muitos problemas computacionais encontramos a seguinte propriedade: cada entrada


do problema contm uma entrada menor do mesmo problema. Dessa forma, dizemos que
esses problemas tm uma estrutura recursiva. Para resolver um problema como esse, usamos
em geral a seguinte estratgia:
se a entrada do problema pequena ento
resolva-a diretamente;
seno,
reduza-a a uma entrada menor do mesmo problema,
aplique este mtodo entrada menor
e volte entrada original.
A aplicao dessa estratgia produz um algoritmo recursivo, que implementado na lingua-
gem C torna-se um programa recursivo ou um programa que contm uma ou mais funes
recursivas.
Uma funo recursiva aquela que possui, em seu corpo, uma ou mais chamadas a si
mesma. Uma chamada de uma funo a si mesma dita uma chamada recursiva. Como
sabemos, uma funo deve possuir ao menos uma chamada proveniente de uma outra funo,
externa a ela. Se a funo s possui chamadas externas a si, ento chamada de funo no
recursiva. At a aula de hoje construimos apenas funes no recursivas.
Em geral, a toda funo recursiva corresponde uma outra no recursiva que executa exa-
tamente a mesma computao. Como alguns problemas possuem estrutura recursiva natural,
funes recursivas so facilmente construdas a partir de suas definies. Alm disso, a de-
monstrao da corretude de um programa recursivo facilitada pela relao direta entre sua
estrutura e a induo matemtica. Outras vezes, no entanto, a implementao de um programa
recursivo demanda um gasto maior de memria, j que durante seu processo de execuo mui-
tas informaes devem ser guardadas na sua pilha de execuo.

250
39.2 E XEMPLOS 251

39.2 Exemplos

Um exemplo clssico de uma funo recursiva aquela que computa o fatorial de um n-


mero inteiro n > 0. A idia da soluo atravs de uma recursiva baseada na frmula mostrada
a seguir: 
1, se n 6 1 ,
n! =
n (n 1)! , caso contrrio .

A funo recursiva fat , que calcula o fatorial de um dado nmero inteiro no negativo n,
mostrada a seguir:

int fat(int n)
{
int result;
if (n <= 1)
result = 1;
else
result = n * fat(n-1);
return result;
}

A sentena return pode aparecer em qualquer ponto do corpo de uma funo e por isso
podemos escrever a funo fat como a seguir:

int fat(int n)
{
if (n == 0)
return 1;
else
return n * fat(n-1);
}

Geralmente, preferimos a primeira verso implementada acima, onde existe apenas uma
sentena com a palavra reservada return posicionada no final do corpo da funo fat . Essa
maneira de escrever funes recursivas evita confuso e nos permite seguir o fluxo de execuo
dessas funes mais naturalmente. No entanto, a segunda verso da funo fat apresentada
acima equivalente primeira e isso significa que tambm vlida. Alm disso, essa segunda
soluo mais compacta e usa menos memria, j que evita o uso de uma varivel. Por tudo
isso, essa segunda forma de escrever funes recursivas muito usada por programadores(as)
mais experientes.
A execuo da funo fat se d da seguinte forma. Imagine que uma chamada fat(3)
foi realizada. Ento, temos ilustrativamente a seguinte situao:

FACOM UFMS
39.2 E XEMPLOS 252

fat(3)
p
fat(2)
p
fat(1)
p
devolve 1
x
devolve 2 1
x
devolve 3 2
x
Repare nas indentaes que ilustram as chamadas recursivas.
Vamos ver um prximo exemplo. Considere o problema de determinar um valor mximo
de um vetor v com n elementos. O tamanho de uma entrada do problema n > 1. Se
n = 1 ento v[0] o nico elemento do vetor e portanto v[0] mximo. Se n > 1 ento
o valor que procuramos o maior dentre o mximo do vetor v[0..n-2] e o valor armaze-
nado em v[n-1] . Dessa forma, a entrada v[0..n-1] do problema fica reduzida entrada
v[0..n-2] . A funo maximo a seguir implementa essa idia.

/* esta funo recebe um vetor de nmeros inteiros v e um


nmero inteiro n >= 1 e devolve um elemento mximo no vetor v[0..n-1] */
int maximo(int v[MAX], int n)
{
int aux;
if (n == 1)
return v[0];
else {
aux = maximo(v, n-1);
if (aux > v[n-1])
return aux;
else
return v[n-1];
}
}

Para verificar que uma funo recursiva est correta, devemos seguir o seguinte roteiro:

Passo 1: escreva o que a funo deve fazer;

Passo 2: verifique se a funo de fato faz o que deveria fazer quando a entrada pequena;

Passo 3: imagine que a entrada grande e suponha que a funo far a coisa certa para entra-
das menores; sob essa hiptese, verifique que a funo faz o que dela se espera.

FACOM UFMS
39.2 E XEMPLOS 253

Exerccios

39.1 (a) Escreva uma funo no recursiva com a seguinte interface:

int pot(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn .


(b) Escreva uma funo recursiva com a seguinte interface:

int potR(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn .


Como no primeiro exemplo da seo anterior quando computamos o fatorial de um
nmero, o valor de xn pode ser computado recursivamente basicamente da mesma
forma, observando a seguinte frmula:

n 1, se n = 0 ,
x = n1
xx , se n > 1 .

(c) Escreva um programa que receba dois nmeros inteiros x e n, com n > 0, e devolva
xn . Use as funes em (a) e (b) para mostrar os dois resultados.

39.2 O que faz a funo abaixo?

void ib(int n)
{
if (n != 0) {
ib(n / 2);
printf("%c", 0 + n % 2);
}
return;
}

Escreva um programa para testar a funo ib .

39.3 (a) Escreva uma funo recursiva que implementa o algoritmo de Euclides. Veja o exer-
ccio 10.3.
(b) Escreva um programa que receba dois nmeros inteiros e calcule o mximo divisor
comum entre eles. Use a funo do item (a).

FACOM UFMS
39.2 E XEMPLOS 254

Programa 39.1: Soluo do exerccio 39.1.

1 #include <stdio.h>
2 int pot(int x, int n)
3 {
4 int i, result;
5 result = 1;
6 for (i = 1; i <= n; i++)
7 result = result * x;
8 return result;
9 }
10 int potR(int x, int n)
11 {
12 if (n == 0)
13 return 1;
14 else
15 return x * potR(x, n-1);
16 }
17 int main(void) {
18 int x, n;
19 scanf("%d%d", &x, &n);
20 printf("No resursiva: %d^%d = %d\n", x, n, pot(x, n));
21 printf("Resursiva : %d^%d = %d\n", x, n, potR(x, n));
22 return 0;
23 }

FACOM UFMS
A ULA 40

E XERCCIOS

40.1 (a) Escreva uma funo recursiva com a seguinte interface:

float soma(float v[MAX], int n)

que receba um vetor v e um nmero inteiro n, tal que v contm n > 1 nmeros com
ponto flutuante, e calcule e devolva a soma desses n nmeros.
(b) Usando a funo do item anterior, escreva um programa que receba um nmero
inteiro n, com n > 1, e mais n nmeros reais e calcule a soma desses nmeros.

Programa 40.1: Soluo do exerccio 40.1.

1 #include <stdio.h>
2 #define MAX 100
3 float soma(float v[MAX], int n)
4 {
5 if (n == 1)
6 return v[0];
7 else
8 return soma(v, n-1) + v[n-1];
9 }
10 int main(void)
11 {
12 int n, i;
13 float V[MAX];
14 scanf("%d", &n);
15 for (i = 0; i < n; i++)
16 scanf("%f", &V[i]);
17 printf("%g\n", soma(V, n));
18 return 0;
19 }

255
256

40.2 (a) Escreva uma funo recursiva com a seguinte interface:

int somaDigitos(int n)

que receba um nmero inteiro positivo n e devolva a soma de seus dgitos.


(b) Escreva um programa que receba um nmero inteiro n e imprima a soma de seus
dgitos. Use a funo do item (a).
40.3 Como no exerccio 18.2, neste exerccio temos de imprimir a seqncia de Fibonacci. Essa
seqncia dada pela seguinte frmula:

F1 = 1
F = 1
2
Fi = Fi1 + Fi2 , para i > 3.

(a) Escreva uma funo recursiva com a seguinte interface:

int Fib(int i)

que receba um nmero inteiro positivo i e devolva o i-simo termo da seqncia de


Fibonacci, isto , Fi .
(b) Escreva um programa que receba um nmero inteiro i > 1 e imprima o termo Fi da
seqncia de Fibonacci. Use a funo do item (a).

40.4 Como no exerccio 20.3, neste exerccio temos computar a funo log2 n. Lembre-se que
o piso de um nmero x o nico inteiro i tal que i 6 x < i + 1. O piso de x denotado
por x.
Segue uma amostra de valores da funo log2 n:

n 15 16 31 32 63 64 127 128 255 256 511 512


log2 n 3 4 4 5 5 6 6 7 7 8 8 9

(a) Escreva uma funo recursiva com a seguinte interface:

int log2(int n)

que receba um nmero inteiro positivo n e devolva log2 n.


(b) Escreva um programa que receba um nmero inteiro n > 1 e imprima log2 n. Use
a funo do item (a).

40.5 Considere o seguinte processo para gerar uma seqncia de nmeros. Comece com um
inteiro n. Se n par, divida por 2. Se n mpar, multiplique por 3 e some 1. Repita esse
processo com o novo valor de n, terminando quando n = 1. Por exemplo, a seqncia de
nmeros a seguir gerada para n = 22:

22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

FACOM UFMS
257

conjecturado que esse processo termina com n = 1 para todo inteiro n > 0. Para uma
entrada n, o comprimento do ciclo de n o nmero de elementos gerados na seqncia.
No exemplo acima, o comprimento do ciclo de 22 16.

(a) Escreva uma funo no recursiva com a seguinte interface:

int ciclo(int n)

que receba um nmero inteiro positivo n, mostre a seqncia gerada pelo processo
descrito acima na sada e devolva o comprimento do ciclo de n.
(b) Escreva uma verso recursiva da funo do item (a) com a seguinte interface:

int cicloR(int n)

que receba um nmero inteiro positivo n, mostre a seqncia gerada pelo processo
descrito acima na sada e devolva o comprimento do ciclo de n.
(c) Escreva um programa que receba um nmero inteiro n > 1 e determine a seqncia
gerada por esse processo e tambm o comprimento do ciclo de n. Use as funes em
(a) e (b) para testar.

40.6 Podemos calcular a potncia xn de uma maneira mais eficiente. Observe primeiro que se
n uma potncia de 2 ento xn pode ser computada usando seqncias de quadrados.
Por exemplo, x4 o quadrado de x2 e assim x4 pode ser computado usando somente duas
multiplicaes ao invs de trs. Esta tcnica pode ser usada mesmo quando n no uma
potncia de 2, usando a seguinte frmula:

1, se n = 0,
n
x = n/2 2
(x ) , se n par, (40.1)

x xn1 , se n mpar.

(a) Escreva uma funo com interface

int potencia(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn usando a fr-


mula (40.1).
(b) Escreva um programa que receba dois nmeros inteiros a e b e imprima o valor de
ab .

FACOM UFMS
A ULA 41

B USCA

O problema da busca de um elemento em um conjunto uma operao bsica em com-


putao. A maneira como esse conjunto armazenado na memria do computador permite
que algumas estratgias possam ser usadas para realizar a tarefa da busca. Na aula de hoje
revemos os processos de busca que vimos usando corriqueiramente at o momento como uma
sub-tarefa realizada na soluo de diversos problemas prticos. A busca ser fixada, como an-
tes, com os dados envolvidos como sendo nmeros inteiros. O conjunto de nmeros inteiros
onde a busca se dar armazenado em um vetor. Alm de rever essa estratgia, chamada de
busca seqencial, vemos ainda uma estratgia nova e muito eficiente de busca em um vetor
ordenado, chamada de busca binria.

41.1 Busca seqencial

Vamos fixar o elemento a ser buscado e o conjunto a ser varrido como sendo constitudos de
nmeros inteiros, observando que o problema da busca no se modifica essencialmente se esse
tipo de dados for alterado. Assim, dado um nmero inteiro x e um vetor de nmeros inteiros
v[0..n 1], considere o problema de encontrar um ndice k tal que v[k] = x. O problema faz
sentido com qualquer n > 0. Se n = 0, o vetor vazio e portanto essa entrada do problema no
tem soluo.
preciso comear com uma deciso de projeto: que fazer se x no estiver no vetor? Adota-
remos a conveno de devolver 1 nesse caso. A conveno razovel pois 1 no pertence
ao conjunto {0, . . . , n 1} de ndices vlidos. Para implementar essa conveno, convm varrer
o vetor do fim para o comeo, como mostra a funo busca abaixo:

/* Recebe um vetor v[0..n-1], com n >= 0, e um nmero x


e devolve k no intervalo [0, n-1] tal que v[k] == x.
Se tal k no existe, devolve -1. */
int busca(int v[MAX], int n, int x)
{
int k;
for (k = n - 1; k >= 0 && v[k] != x; k--)
;
return k;
}

258
41.2 B USCA EM UM VETOR ORDENADO 259

Observe como a funo eficiente e elegante, funcionando corretamente mesmo quando o


vetor est vazio, isto , quando n vale 0.
Um exemplo de uma chamada funo busca apresentado abaixo:

if (busca(v, n, x) >= 0)
printf("Encontrei!\n");

A funo busca pode ser escrita em verso recursiva. A idia do cdigo simples: se
n = 0 ento o vetor vazio e portanto x no est em v[0..n 1]; se n > 0 ento x est em
v[0..n 1] se e somente se x = v[n 1] ou x est no vetor v[0..n 2]. A verso recursiva ento
mostrada abaixo:

/* Recebe um vetor v[0..n-1], com n >= 0, e um nmero x e devolve k


tal que 0 <= k < n e v[k] == x. Se tal k no existe, devolve -1. */
int buscaR(int v[MAX], int n, int x)
{
if (n == 0)
return -1;
else
if (x == v[n - 1])
return n - 1;
else
return buscaR(v, n - 1, x);
}

A corretude da funo busca foi mostrada no exerccio 20.1. Seu tempo de execuo
linear no tamanho da entrada, isto , O(n), como mostrado na aula 32. A funo buscaR tem
corretude e anlise de eficincia equivalentes.

41.2 Busca em um vetor ordenado

Dizemos que um vetor de nmeros inteiros v[0..n 1] crescente se v[0] 6 v[1] 6 6


v[n 1] e decrescente se v[0] > v[1] > > v[n 1]. Dizemos ainda que o vetor ordenado se
crescente ou decrescente. Nesta seo, vamos focar no problema da busca de um elemento x
em um vetor ordenado v[0..n 1].
Agora, em lugar de perguntar em que posio x est no vetor v[0..n 1], mais til e con-
veniente perguntar em que posio x deveria estar. Assim, o problema pode ser reformulado
da seguinte maneira: dado um nmero inteiro x e um vetor de nmeros inteiros crescente
v[0..n 1], encontrar um ndice k tal que

v[k 1] < x 6 v[k] . (41.1)

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 260

De posse de tal ndice k, muito fcil resolver o problema da busca, bastando comparar x com
v[k].
Observe ainda que qualquer valor de k no intervalo [0, n] pode ser uma soluo do pro-
blema da busca. Nos dois extremos do intervalo, a condio (41.1) deve ser interpretada ade-
quadamente. Isto , se k = 0, a condio se reduz apenas a x 6 v[0], pois v[1] no faz sentido.
Se k = n, a condio se reduz somente a v[n 1] < x, pois v[n] no faz sentido. Tudo se passa
como se o vetor v tivesse um componente imaginrio v[1] com valor e um componente
imaginrio v[n] com valor +. Tambm, para simplificar um pouco o raciocnio, suporemos
que n > 1.
Isso posto, observe ento que uma busca seqencial, recursiva ou no, pode ser realizada
para resolver o problema, conforme apresentado na seo anterior. Vejamos ento a funo
buscaOrd .

/* Recebe um vetor crescente v[0..n-1] com n >= 1 e um inteiro x e


devolve um ndice k em [0, n] tal que v[k-1] < x <= v[k]. */
int buscaOrd(int v[MAX], int n, int x)
{
int k;
for (k = 0; k < n && v[k] < x; k++)
;
return k;
}

Uma chamada funo buscaOrd mostrada a seguir:

ind = buscaOrd(v, n, x);

Se aplicamos a estratgia da busca seqencial, isto , se aplicamos qualquer das duas fun-
es apresentadas na seo anterior, para soluo do problema da busca sobre um vetor de
entrada que se encontra ordenado, ento certamente obtemos uma resposta correta, porm
ineficiente do ponto de vista de seu consumo de tempo. Isso porque, no pior caso, a busca
seqencial realiza a comparao do elemento x com cada um dos elementos do vetor v de
entrada. Ou seja, o problema da busca resolvido em tempo proporcional ao nmero de ele-
mentos do vetor de entrada v, deixando de explorar sua propriedade especial de se encontrar
ordenado.
Com uma busca binria, podemos fazer o mesmo trabalho de forma bem mais eficiente.
A busca binria se baseia no mtodo que usamos s vezes para encontrar uma palavra no
dicionrio: abrimos o dicionrio ao meio e comparamos a primeira palavra desta pgina com
a palavra buscada. Se a primeira palavra menor que a palavra buscada, jogamos fora a
primeira metade do dicionrio e repetimos a mesma estratgia considerando apenas a metade
restante. Se, ao contrrio, a primeira palavra maior que a palavra buscada, jogamos fora a
segunda metade do dicionrio e repetimos o processo. A funo buscaBin abaixo implementa
essa idia.

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 261

/* Recebe um vetor crescente v[0..n-1] com n >= 1 e um inteiro x e


devolve um ndice k em [0, n] tal que v[k-1] < x <= v[k]. */
int buscaBin(int v[MAX], int n, int x)
{
int e, d, m;
e = -1;
d = n;
while (e < d - 1) {
m = (e + d) / 2;
if (v[m] < x)
e = m;
else
d = m;
}
return d;
}

Um exemplo de chamada funo buscaBin mostrado abaixo:

k = buscaBin(v, n, x);

Para provar a correo da funo buscaBin , basta verificar o seguinte invariante:

no incio de cada repetio while , imediatamente antes da comparao de e com


d - 1 , vale a relao v[e] < x <= v[d] .

Com esse invariante em mos, podemos usar a estratgia que aprendemos na aula 20 para
mostrar finalmente que essa funo est de fato correta.
Quantas iteraes a funo buscaBin executa? Essa conta revela o valor aproximado que
representa o consumo de tempo dessa funo em um dado vetor de entrada. Observe que em
cada iterao, o tamanho do vetor v dado por d e 1 . No incio da primeira iterao,
o tamanho do vetor n. No incio da segunda iterao, o tamanho do vetor aproximadamente
n/2. No incio da terceira, aproximadamente n/4. No incio da (k+1)-sima, aproximadamente
n/2k . Quando k > log2 n, temos n/2k < 1 e a execuo da funo termina. Assim, o nmero
de iteraes aproximadamente log2 n. O consumo de tempo da funo proporcional ao
nmero de iteraes e portanto proporcional a log2 n. Esse consumo cresce com n muito mais
lentamente que o consumo da busca seqencial.
Uma soluo recursiva para o problema da busca em um vetor ordenado apresentada a se-
guir. Antes, necessrio reformular o problema ligeiramente. A funo recursiva buscaBinR
procura o elemento x no vetor crescente v[e..d], supondo que o valor x est entre os extremos
v[e] e v[d].

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 262

/* Recebe um vetor crescente v[e..d] e um inteiro x tais que


v[e] < x <= v[d] e devolve um ndice k em [e+1, d]
tal que v[k-1] < x <= v[k]. */
int buscaBinR(int v[MAX], int e, int d, int x)
{
int m;
if (e == d - 1)
return d;
else {
m = (e + d) / 2;
if (v[m] < x)
return BuscaBinR(v, m, d, x);
else
return BuscaBinR(v, e, m, v);
}
}

Uma chamada da funo buscaBinR pode ser realizada da seguinte forma:

k = buscaBinR(v, -1, n);

Quando a funo buscaBinR chamada com argumentos (v, 1, n, x), ela chama a si mesma
cerca de log2 n vezes. Este nmero de chamadas a profundidade da recurso.

Exerccios

41.1 Tome uma deciso de projeto diferente daquela da seo 41.1: se x no estiver em v[0..n
1], a funo deve devolver n. Escreva a verso correspondente da funo busca . Para
evitar o grande nmero de comparaes de k com n, coloque uma sentinela em v[n].

1 /* Recebe um vetor v[0..n-1], com n >= 0, e um nmero x


2 e devolve k no intervalo [0, n-1] tal que v[k] == x.
3 Se tal k no existe, devolve n. */
4 int buscaS(int v[MAX], int n, int x)
5 {
6 int k;
7 v[n] = x;
8 for (k = 0; v[k] != x; k++)
9 ;
10 return k;
11 }

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 263

41.2 Considere o problema de determinar o valor de um elemento mximo de um vetor


v[0..n 1]. Considere a funo maximo abaixo.

1 int maximo(int v[MAX], int n)


2 {
3 int i, x;
4 x = v[0];
5 for (i = 1; i < n; i++)
6 if (x < v[i])
7 x = v[i];
8 return x;
9 }

(a) A funo maximo acima resolve o problema?


(b) Faz sentido trocar x = v[0] pode x = 0 ?
(c) Faz sentido trocar x = v[0] pode x = INT_MIN 1 ?
(d) Faz sentido trocar x < v[i] por x <= v[i] ?

41.3 O autor da funo abaixo afirma que ela decide se x est no vetor v[0..n 1]. Critique seu
cdigo.

1 int busc(int v[MAX], int n, int x)


2 {
3 if (v[n-1] == x)
4 return 1;
5 else
6 return busc(v, n-1, x);
7 }

41.4 A operao de remoo consiste de retirar do vetor v[0..n 1] o elemento que tem ndice
k e fazer com que o vetor resultante tenha ndices 0, 1, . . . , n 2. Essa operao s faz
sentido se 0 6 k < n.

(a) Escreva uma funo iterativa com a seguinte interface:

int remove(int v[MAX], int n, int k)

que remove o elemento de ndice k do vetor v[0..n 1] e devolve o novo valor de n,


supondo que 0 6 k < n.
(b) Escreva uma funo recursiva para a remoo com a seguinte interface:
1
INT_MIN o valor do menor nmero do tipo int .

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 264

int removeR(int v[MAX], int n, int k)

41.5 A operao de insero consiste em introduzir um novo elemento y entre a posio de


ndice k 1 e a posio de ndice k no vetor v[0..n 1], com 0 6 k 6 n.

(a) Escreva uma funo iterativa com a seguinte interface:

int insere(int v[MAX], int n, int k, int y)

que insere o elemento y entre as posies k 1 e k do vetor v[0..n 1] e devolve o


novo valor de n, supondo que 0 6 k 6 n.
(b) Escreva uma funo recursiva para a insero com a seguinte interface:

int insereR(int v[MAX], int n, int k, int x)

41.6 Na busca binria, suponha que v[i] = i para todo i.

(a) Execute a funo buscaBin com n = 9 e x = 3;


(b) Execute a funo buscaBin com n = 14 e x = 7;
(c) Execute a funo buscaBin com n = 15 e x = 7.

41.7 Execute a funo buscaBin com n = 16. Quais os possveis valores de m na primeira
iterao? Quais os possveis valores de m na segunda iterao? Na terceira? Na quarta?

41.8 Confira a validade da seguinte afirmao: quando n + 1 uma potncia de 2, o valor da


expresso (e + d) divisvel por 2 em todas as iteraes da funo buscaBin , quaisquer
que sejam v e x.

41.9 Responda as seguintes perguntas sobre a funo buscaBin .

(a) Que acontece se while (e < d - 1) for substitudo por while (e < d) ?
(b) Que acontece se if (v[m] < x) for substitudo por if (v[m] <= x) ?
(c) Que acontece se e = m for substitudo por e = m+1 ou por e = m - 1 ?
(d) Que acontece se d = m for substitudo por d = m + 1 ou por d = m - 1 ?

41.10 Se t segundos so necessrios para fazer uma busca binria em um vetor com n elemen-
tos, quantos segundos sero necessrios para fazer uma busca em n2 elementos?

41.11 Escreva uma verso da busca binria para resolver o seguinte problema: dado um inteiro
x e um vetor decrescente v[0..n 1], encontrar k tal que v[k 1] > x > v[k].

FACOM UFMS
41.2 B USCA EM UM VETOR ORDENADO 265

41.12 Suponha que cada elemento do vetor v[0..n 1] uma cadeia de caracteres (ou seja,
temos uma matriz de caracteres). Suponha tambm que o vetor est em ordem lexicogr-
fica. Escreva uma funo eficiente, baseada na busca binria, que receba uma cadeia de
caracteres x e devolva um ndice k tal que x igual a v[k]. Se tal k no existe, a funo
deve devolver 1.

41.13 Suponha que cada elemento do vetor v[0..n 1] um registro com dois campos: o nome
do(a) estudante e o nmero do(a) estudante. Suponha que o vetor est em ordem cres-
cente de nmeros. Escreva uma funo de busca binria que receba o nmero de um(a)
estudante e devolva seu nome. Se o nmero no estiver no vetor, a funo deve devolver
a cadeia de caracteres vazia.

41.14 Escreva uma funo que receba um vetor estritamente crescente v[0..n 1] de nmeros
inteiros e devolva um ndice i entre 0 e n 1 tal que v[i] = i. Se tal i no existe, a
funo deve devolver 1. A sua funo no deve fazer mais que log2 n comparaes
envolvendo os elementos de v.

FACOM UFMS
A ULA 42

O RDENAO : MTODOS ELEMENTARES

Alm da busca, a ordenao outra operao elementar em computao. Nesta aula re-
visaremos os mtodos de ordenao elementares que j tivemos contato em aulas anteriores:
o mtodo das trocas sucessivas, da seleo e da insero. Esses mtodos foram projetados a
partir de idias simples e tm, como caracterstica comum, tempo de execuo de pior caso
quadrtico no tamanho da entrada.

42.1 Problema da ordenao

Um vetor v[0..n 1] crescente se v[0] 6 v[1] 6 6 v[n 1]. O problema da ordenao


de um vetor consiste em rearranjar, ou permutar, os elementos de um vetor v[0..n 1] de tal
forma que se torne crescente. Nesta aula ns discutimos trs funes simples para soluo
desse problema. Nas aulas 43 e 44 examinaremos funes mais sofisticadas e eficientes.

42.2 Mtodo das trocas sucessivas

O mtodo das trocas sucessivas, popularmente conhecido como mtodo da bolha, um m-


todo simples de ordenao que, a cada passo, posiciona o maior elemento de um subconjunto
de elementos do vetor de entrada na sua localizao correta neste vetor. A funo ordTrocas
implementa esse mtodo.

1 void ordTrocas(int v[], int n)


2 {
3 int i, j;
4 for (i = n - 1; i > 0; i--)
5 for (j = 0; j < i; j++)
6 if (v[j] > v[j+1])
7 troca(&v[j], &v[j+1]);
8 }

Um exemplo de chamada da funo ordTrocas dado a seguir:

266
42.3 M TODO DA SELEO 267

ordTrocas(v, n);

Para entender a funo ordTrocas basta observar que no incio de cada repetio do for
externo vale que:

o vetor v[0..n 1] uma permutao do vetor original,

o vetor v[i + 1..n 1] crescente e

v[j] 6 v[i + 1] para j = 0, 1, . . . , i.

Alm disso, o consumo de tempo da funo ordTrocas proporcional ao nmero de


execues da comparao v[j] > v[j + 1], que proporcional a n2 no pior caso.

42.3 Mtodo da seleo

O mtodo de ordenao por seleo baseado na idia de escolher um menor elemento


do vetor, depois um segundo menor elemento e assim por diante. A funo ordSelecao
implementa esse mtodo.

1 void ordSelecao(int v[], int n)


2 {
3 int i, j, min;
4 for (i = 0; i < n-1; i++) {
5 min = i;
6 for (j = i+1; j < n; j++)
7 if (v[j] < v[min])
8 min = j;
9 troca(&v[i], &v[min]);
10 }
11 }

Um exemplo de chamada da funo ordSelecao dado a seguir:

ordSelecao(v, n);

Para entender como e por que o a funo ordSelecao funciona, basta observar que no
incio de cada repetio do for externo valem os seguintes invariantes:

o vetor v[0..n 1] uma permutao do vetor original,

FACOM UFMS
42.4 M TODO DA INSERO 268

o vetor v[0..i 1] est em ordem crescente e

v[i 1] 6 v[j] para j = i, i + 1, . . . , n 1.

O terceiro invariante pode ser assim interpretado: v[0..i 1] contm todos os elementos
pequenos do vetor original e v[i..n 1] contm todos os elementos grandes. Os trs inva-
riantes garantem que no incio de cada iterao os elementos v[0], . . . , v[i 1] j esto em suas
posies definitivas.
Uma anlise semelhante que fizemos para a funo ordTrocas mostra que o mtodo
da insero implementado pela funo ordSelecao consome n2 unidades de tempo no pior
caso.

42.4 Mtodo da insero

O mtodo da ordenao por insero muito popular. freqentemente usado para colocar
em ordem um baralho de cartas. A funo ordInsercao implementa esse mtodo.

1 void ordInsercao(int v[], int n)


2 {
3 int i, j, x;;
4 for (i = 1; i < n; i++) {
5 x = v[i];
6 for (j = i-1; j >= 0 && v[j] > x; j--)
7 v[j+1] = v[j];
8 v[j+1] = x;
9 }
10 }

Um exemplo de chamada da funo ordInsercao dado a seguir:

ordInsercao(v, n);

Para entender a funo ordInsercao basta observar que no incio de cada repetio do
for externo, valem os seguintes invariantes:

o vetor v[0..n 1] uma permutao do vetor original e

o vetor v[0..i 1] crescente.

O consumo de tempo da funo ordInsercao proporcional ao nmero de execues da


comparao v[j] > x, que proporcional a n2 .

FACOM UFMS
42.4 M TODO DA INSERO 269

Exerccios

42.1 Escreva uma funo que verifique se um dado vetor v[0..n 1] crescente.

1 int verifOrd(int v[], int n)


2 {
3 for (i = 0; i < n-1; i++)
4 if (v[i] > v[i+1])
5 return 0;
6 return 1;
7 }

42.2 Que acontece se trocarmos i > 0 por i >= 0 no cdigo da funo ordTrocas ? Que
acontece se trocarmos j < i por j <= i ?

42.3 Troque v[j] > v[j+1] por v[j] >= v[j+1] no cdigo da funo ordTrocas . A
nova funo continua produzindo uma ordenao crescente de v[0..n 1]?

42.4 Escreva uma verso recursiva do mtodo de ordenao por trocas sucessivas.

42.5 Que acontece se trocarmos i = 0 por i = 1 no cdigo da funo ordSelecao ? Que


acontece se trocarmos i < n-1 por i < n ?

42.6 Troque v[j] < v[min] por v[j] <= v[min] no cdigo da funo ordSelecao . A
nova funo continua produzindo uma ordenao crescente de v[0..n 1]?

42.7 Escreva uma verso recursiva do mtodo de ordenao por seleo.

42.8 No cdigo da funo ordInsercao , troque v[j] > x por v[j] >= x . A nova funo
continua produzindo uma ordenao crescente de v[0..n 1]?

42.9 No cdigo da funo ordInsercao , que acontece se trocarmos i = 1 por i = 0 ? Que


acontece se trocarmos v[j+1] = x por v[j] = x ?

42.10 Escreva uma verso recursiva do mtodo de ordenao por insero.

42.11 Escreva uma funo que rearranje um vetor v[0..n 1] de modo que ele fique em ordem
estritamente crescente.

42.12 Escreva uma funo que permute os elementos de um vetor v[0..n 1] de modo que eles
fiquem em ordem decrescente.

FACOM UFMS
A ULA 43

O RDENAO POR INTERCALAO

Na aula 42 revimos os mtodos de ordenao mais bsicos, que so todos iterativos, simples
e tm tempo de execuo de pior caso proporcional a n2 , onde n o tamanho da entrada. M-
todos mais eficientes de ordenao so baseados em recurso, tcnica introduzida na aula 39.
Nesta aula, estudamos o mtodo da ordenao por intercalao, conhecido como mergesort.
A ordenao por intercalao, que veremos nesta aula, e a ordenao por separao, que
veremos na aula 44, so mtodos eficientes baseados na tcnica recursiva chamada dividir para
conquistar, onde quebramos o problema em vrios sub-problemas de menor tamanho que
so similares ao problema original, resolvemos esses sub-problemas recursivamente e ento
combinamos essas solues para produzir uma soluo para o problema original.

43.1 Dividir para conquistar

A tcnica de dividir para conquistar uma tcnica geral de construo de algoritmos e


programas, tendo a recurso como base, que envolve trs passos em cada nvel da recurso:

Dividir o problema em um nmero de sub-problemas;

Conquistar os sub-problemas solucionando-os recursivamente. No entanto, se os tamanhos


dos sub-problemas so suficientemente pequenos, resolva os sub-problemas de uma ma-
neira simples;

Combinar as solues dos sub-problemas na soluo do problema original.

Como mencionamos na aula 41, o algoritmo da busca binria um mtodo de busca que usa
a tcnica de dividir para conquistar na soluo do problema da busca. O mtodo de ordenao
por intercalao, que veremos nesta aula, e o mtodo da ordenao por separao, que veremos
na aula 44, tambm so algoritmos baseados nessa tcnica.

43.2 Problema da intercalao

Antes de apresentar o mtodo da ordenao por intercalao, precisamos resolver um pro-


blema anterior, que auxilia esse mtodo, chamado de problema da intercalao. O problema da
intercalao pode ser descrito de forma mais geral como a seguir: dados dois conjuntos cres-
centes A e B, com m e n elementos respectivamente, obter um conjunto crescente C a partir
de A e B. Variantes sutis desse problema geral podem ser descritas como no caso em que se

270
43.2 P ROBLEMA DA INTERCALAO 271

permite ou no elementos iguais nos dois conjuntos de entrada, isto , conjuntos de entrada A
e B tais que A B 6= ou A B = .
O problema da intercalao que queremos resolver aqui mais especfico e pode ser as-
sim descrito: dados dois vetores crescentes v[p..q 1] e v[q..r 1], rearranjar v[p..r 1] em
ordem crescente. Isso significa que queremos de alguma forma intercalar os vetores v[0..q 1] e
v[q..r 1]. Nesse caso, a primeira vista parece que os vetores de entrada pode ter elementos em
comum. Entretanto, este no o caso, j que estamos considerando o mesmo conjunto inicial
de elementos armazenados no vetor v.
Uma maneira fcil de resolver o problema da intercalao usar um dos mtodos de orde-
nao da aula 42 tendo como entrada o vetor v[p..r 1]. Essa soluo, no entanto, tem consumo
de tempo de pior caso proporcional ao quadrado do nmero de elementos do vetor e inefici-
ente por desconsiderar as caractersticas dos vetores v[p..q 1] e v[q..r 1]. Uma soluo mais
eficiente, que usa um vetor auxiliar, mostrada a seguir.

1 /* Recebe os vetores crescentes v[p..q-1] e v[q..r-1] e


2 rearranja v[p..r-1] em ordem crescente */
3 void intercala(int v[MAX], int p, int q, int r)
4 {
5 int w[MAX], i, j, k;
6 i = p;
7 j = q;
8 k = 0;
9 while (i < q && j < r) {
10 if (v[i] < v[j]) {
11 w[k] = v[i];
12 i++;
13 }
14 else {
15 w[k] = v[j];
16 j++;
17 }
18 k++;
19 }
20 while (i < q) {
21 w[k] = v[i];
22 i++;
23 k++;
24 }
25 while (j < r) {
26 w[k] = v[j];
27 j++;
28 k++;
29 }
30 for (i = p; i < r; i++)
31 v[i] = w[i-p];
32 }

FACOM UFMS
43.3 O RDENAO POR INTERCALAO 272

A funo intercala tem tempo de execuo de pior caso proporcional ao nmero de


comparaes entre os elementos do vetor, isto , r p. Assim, podemos dizer que o consumo
de tempo no pior caso da funo intercala proporcional ao nmero de elementos do vetor
de entrada.

43.3 Ordenao por intercalao

Com o problema da intercalao resolvido, podemos agora descrever uma funo que im-
plementa o mtodo da ordenao por intercalao. Nesse mtodo, dividimos ao meio um vetor
v com r p elementos, ordenamos recursivamente essas duas metades de v e ento as interca-
lamos. A funo mergesort a seguir recursiva e a base da recurso ocorre quando p > r 1,
quando no necessrio qualquer processamento.

1 /* recebe um vetor v[p..r-1] e o rearranja em ordem crescente */


2 void mergesort(int v[MAX], int p, int r)
3 {
4 int q;
5 if (p < r - 1) {
6 q = (p + r) / 2;
7 mergesort(v, p, q);
8 mergesort(v, q, r);
9 intercala(v, p, q, r);
10 }
11 }

Como a expresso (p + q)/2 da funo mergesort do tipo inteiro, observe que seu resul-
tado , na verdade, avaliado como p+q
2 .
Observe tambm que para ordenar um vetor v[0..n 1] basta chamar a funo mergesort
com os seguintes argumentos:

mergesort(v, 0, n);

Vejamos um exemplo de execuo da funo mergesort na figura 43.1, para um vetor de


entrada v[0..7] = {4, 6, 7, 3, 5, 1, 2, 8} e chamada

mergesort(v, 0, 8);

Observe que as chamadas recursivas so realizadas at a linha divisria imaginria ilus-


trada na figura, quando p > r 1. A partir desse ponto, a cada volta de um nvel de recurso,

FACOM UFMS
43.3 O RDENAO POR INTERCALAO 273

uma intercalao realizada. No final, uma ltima intercalao realizada e o vetor original
torna-se ento um vetor crescente com os mesmos elementos de entrada.

4 6 7 3 5 1 2 8

4 6 7 3 5 1 2 8

4 6 7 3 5 1 2 8

4 6 7 3 5 1 2 8

4 6 3 7 1 5 2 8

3 4 6 7 1 2 5 8

1 2 3 4 5 6 7 8

Figura 43.1: Exemplo de execuo da ordenao por intercalao.

Qual o desempenho da funo mergesort quando queremos ordenar um vetor v[0..n1]?


Suponha, para efeito de simplificao, que n uma potncia de 2. Se esse no o caso, pode-
mos examinar duas potncias de 2 consecutivas, justamente aquelas tais que 2k1 < n 6 2k ,
para algum k > 0. Observe ento que o nmero de elementos do vetor diminudo a aproxi-
madamente metade a cada chamada da funo mergesort . Ou seja, o nmero aproximado
de chamadas proporcional a log2 n. Na primeira vez, o problema original reduzido a dois
sub-problemas onde necessrio ordenar os vetores v[0.. n2 1] e v[ n2 ..n 1]. Na segunda vez,
cada um dos sub-problemas so ainda divididos em mais dois sub-problemas cada, gerando
quatro sub-problemas no total, onde necessrio ordenar os vetores v[0.. n4 1], v[ n4 .. n2 1],
v[ n2 .. 3n 3n
4 1] e v[ 4 ..n 1]. E assim por diante. Alm disso, como j vimos, o tempo total que a
funo intercala gasta proporcional a n. Portanto, a funo mergesort consome tempo
proporcional a n log2 n.

FACOM UFMS
43.3 O RDENAO POR INTERCALAO 274

Exerccios

43.1 Simule detalhadamente a execuo da funo mergesort sobre o vetor de entrada


v[0..7] = {3, 41, 52, 26, 38, 57, 9, 49}.

43.2 A funo intercala est correta nos casos extremos p = q e q = r?

43.3 Um algoritmo de intercalao estvel se no altera a posio relativa dos elementos que
tm um mesmo valor. Por exemplo, se o vetor tiver dois elementos de valor 222, um
algoritmo de intercalao estvel manter o primeiro 222 antes do segundo. A funo
intercala estvel? Se a comparao v[i] <= v[j] for trocada por v[i] < v[j]
a funo fica estvel?

43.4 O que acontece se trocarmos (p + r)/2 por (p + r - 1)/2 no cdigo da funo


mergesort ? Que acontece se trocarmos (p + r)/2 por (p + r + 1)/2 ?

43.5 Escreva uma verso da ordenao por intercalao que rearranje um vetor v[p..r 1] em
ordem decrescente.

43.6 Escreva uma funo eficiente que receba um conjunto S de n nmeros reais e um nmero
real x e determine se existe um par de elementos em S cuja soma exatamente X.

43.7 Seja A um vetor de n nmeros inteiros distintos. Se i < j e A[i] > A[j] ento o par (i, j)
chamado uma inverso de A.

(a) Liste as cinco inverses do vetor {2, 3, 8, 6, 1}.


(b) Qual vetor com elementos do conjunto {1, 2, . . . , n} tem o maior nmero de inver-
ses? Quantas so?
(c) Qual a relao entre o tempo de execuo da ordenao por insero e o nmero de
inverses em um vetor de entrada? Justifique sua resposta.
(d) Modificando a ordenao por intercalao, escreva uma funo eficiente que deter-
mine o nmero de inverses em uma permutao de n elementos.

43.8 Escreva um programa para comparar experimentalmente o desempenho da funo


mergesort com o das funes ordTrocas , ordSelecao e ordInsercao da aula 42.
Use um vetor aleatrio para fazer os testes.

43.9 Veja animaes dos mtodos de ordenao que j vimos nas seguintes pginas:

Sorting Algorithms de J. Harrison;


Sorting Algorithms de P. Morin;
Sorting Algorithms Animations de D. R. Martin.

FACOM UFMS
A ULA 44

O RDENAO POR SEPARAO

O mtodo de ordenao por separao resolve o problema da ordenao descrito na aula 42,
rearranjando um vetor v[0..n 1] de modo que se torne crescente. Em geral, este mtodo
muito mais rpido que os mtodos elementares vistos na aula 42, mas pode ser to lento
quanto aqueles para certas entradas especiais do problema.
O mtodo da ordenao por separao recursivo e tambm se baseia na estratgia de
dividir para conquistar. O mtodo comumente conhecido como quisksort. freqentemente
usado na prtica para ordenao j que destacadamente rpido na mdia. Apenas para
algumas entradas especiais o mtodo to lento quanto os mtodos elementares de ordenao.

44.1 Problema da separao

O problema da separao o ncleo do mtodo da ordenao por separao, que descrito


de forma propositalmente vaga abaixo:

rearranjar um vetor v[p..r] de modo que os elementos pequenos fiquem todos do


lado esquerdo e os grandes fiquem todos do lado direito.

Gostaramos que os dois lados tivessem aproximadamente o mesmo nmero de elementos,


mas estamos dispostos a aceitar resultados menos equilibrados. De todo modo, importante
que a separao no resulte degenerada, deixando um dos lados vazio. A dificuldade est em
construir uma funo que resolva o problema de maneira rpida e no use um vetor auxiliar.
O problema da separao que estamos interessados pode ser formulado concretamente da
seguinte maneira:

rearranjar o vetor v[p..r] de modo que tenhamos

v[p..q] 6 v[q + 1..r]

para algum q em p..r 1.

A funo separa abaixo soluciona o problema da separao. No incio, um piv x esco-


lhido e, a partir de ento, o significado de pequeno e grande passa a ser associado a esse piv:
os elementos do vetor que forem maiores que x sero considerados grandes e os demais sero
considerados pequenos.

275
44.1 P ROBLEMA DA SEPARAO 276

1 int separa(int v[], int p, int r)


2 {
3 int x, i, j;
4 x = v[p];
5 i = p - 1;
6 j = r + 1;
7 while (1) {
8 do
9 j--;
10 while (v[j] > x);
11 do
12 i++;
13 while (v[i] < x);
14 if (i < j)
15 troca(&v[i], &v[j]);
16 else
17 return j;
18 }
19 }

Um exemplo de execuo da funo separa apresentado na figura 44.1.

x 5

v[p..r]

5 3 2 6 4 1 3 7 5 3 2 6 4 1 3 7

i j i j
(a) (b)

3 3 2 6 4 1 5 7 3 3 2 6 4 1 5 7

i j i j
(c) (d)
v[p..q] v[q + 1..r]

3 3 2 1 4 6 5 7

j i
(e)

Figura 44.1: Uma execuo da funo separa .

FACOM UFMS
44.2 O RDENAO POR SEPARAO 277

O corpo da estrutura de repetio while da funo separa repetido at que i > j e,


neste ponto, o vetor v[p..r] acabou de ser separado em v[p..q] e v[q + 1..r], com p 6 q < r, tal que
nenhum elemento de v[p..q] maior que um elemento de v[q + 1..r]. O valor q = j devolvido
ento pela funo separa .
Em outras palavras, podemos dizer que no incio de cada iterao valem os seguintes inva-
riantes:

v[p..r] uma permutao do vetor original,

v[p..i] 6 x 6 v[j..r] e
p 6 i 6 j 6 r.

O nmero de iteraes que a funo realiza proporcional a r p + 1, isto , proporcional


ao nmero de elementos do vetor.

44.2 Ordenao por separao

Com uma funo que soluciona o problema da separao, podemos descrever agora o m-
todo da ordenao por separao. Esse mtodo tambm usa a estratgia de dividir para con-
quistar e o faz de maneira semelhante ao mtodo da ordenao por intercalao, mas com
chamadas invertidas.

1 void quicksort(int v[], int p, int r)


2 {
3 int q;
4 if (p < r) {
5 q = separa(v, p, r);
6 quicksort(v, p, q);
7 quicksort(v, q+1, r);
8 }
9 }

Uma chamada da funo quicksort para ordenao de um vetor v[0..n 1] deve ser feita
como a seguir:

quicksort(v, 0, n-1);

Observe ainda que a funo quicksort est correta mesmo quando p > r, isto , quando
o vetor est vazio.
O consumo de tempo do mtodo de ordenao por separao proporcional ao nmero
de comparaes realizadas entre os elementos do vetor. Se o ndice devolvido pela funo

FACOM UFMS
44.2 O RDENAO POR SEPARAO 278

separa sempre tiver valor mais ou menos mdio de p e r, ento o nmero de comparaes
ser aproximadamente n log2 n. Caso contrrio, o nmero de comparaes ser da ordem de n2 .
Observe que isso ocorre, por exemplo, quando o vetor j estiver ordenado ou quase-ordenado.
Portanto, o consumo de tempo de pior caso da ordenao por separao no melhor que o dos
mtodos elementares vistos na aula 42. Felizmente, o pior caso para a ordenao por separao
raro. Dessa forma, o consumo de tempo mdio da funo quicksort proporcional a
n log2 n.

Exerccios

44.1 Ilustre a operao da funo separa sobre o vetor v que contm os elementos do con-
junto {13, 19, 9, 5, 12, 8, 7, 4, 11, 2, 6, 21}.

44.2 Qual o valor de q a funo separa devolve quando todos os elementos no vetor v[p..r]
tm o mesmo valor?

44.3 A funo separa produz o resultado correto quando p = r?

44.4 Escreva uma funo que rearranje um vetor v[p..r] de nmeros inteiros de modo que os
elementos negativos e nulos fiquem esquerda e os positivos fiquem direita. Em outras
palavras, rearranje o vetor de modo que tenhamos v[p..q 1] 6 0 e v[q..r] > 0 para algum
q em p..r + 1. Procure escrever uma funo eficiente que no use um vetor auxiliar.

44.5 Digamos que um vetor v[p..r] est arrumado se existe q em p..r que satisfaz

v[p..q 1] 6 v[q] < v[q + 1..r] .

Escreva uma funo que decida se v[p..r] est arrumado. Em caso afirmativo, sua funo
deve devolver o valor de q.

44.6 Que acontece se trocarmos if (p < r) por if (p != r) no corpo da funo


quicksort ?

44.7 Compara as funes quicksort e mergesort . Discuta as semelhanas e diferenas.

44.8 Como voc modificaria a funo quicksort para ordenar elementos em ordem decres-
cente?

44.9 Os bancos freqentemente gravam transaes sobre uma conta corrente na ordem das
datas das transaes, mas muitas pessoas preferem receber seus extratos bancrios em
listagens ordenadas pelo nmero do cheque emitido. As pessoas em geral emitem che-
ques na ordem da numerao dos cheques, e comerciantes usualmente descontam estes
cheques com rapidez razovel. O problema de converter uma lista ordenada por data de
transao em uma lista ordenada por nmero de cheques portanto o problema de orde-
nar uma entrada quase j ordenada. Argumente que, neste caso, o mtodo de ordenao
por insero provavelmente se comportar melhor do que o mtodo de ordenao por
separao.

44.10 Fornea um argumento cuidadoso para mostrar que a funo separa correta. Prove o
seguinte:

FACOM UFMS
44.2 O RDENAO POR SEPARAO 279

(a) Os ndices i e j nunca referenciam um elemento de v fora do intervalo [p..r];


(b) O ndice j no igual a r quando separa termina (ou seja, a partio sempre no
trivial);
(c) Todo elemento de v[p..j] menor ou igual a todo elemento de v[j + 1..r] quando a
funo separa termina.

44.11 Escreva uma verso da funo quicksort que coloque um vetor de cadeias de caracteres
em ordem lexicogrfica.

44.12 Considere a seguinte soluo do problema da separao, devido a N. Lomuto. Para sepa-
rar v[p..r], esta verso incrementa duas regies, v[p..i] e v[i + 1..j], tal que todo elemento
na primeira regio menor ou igual a x = v[r] e todo elemento na segunda regio maior
que x.

1 int separaLomuto(int v[], int p, int r)


2 {
3 int x, i, j;
4 x = v[r];
5 i = p - 1;
6 for (j = p; j <= r; j++)
7 if (v[j] <= x) {
8 i++;
9 troca(&v[i], &v[j]);
10 }
11 if (i < r)
12 return i;
13 else
14 return i - 1;
15 }

(a) Ilustre a operao da funo separaLomuto sobre o vetor v que contm os elemen-
tos {13, 19, 9, 5, 12, 8, 7, 4, 11, 2, 6, 21}.
(b) Argumente que separaLomuto est correta.
(c) Qual o nmero mximo de vezes que um elemento pode ser movido pelas funes
separa e separaLomuto ? Justifique sua resposta.
(d) Argumente que separaLomuto , assim como separa , tem tempo de execuo pro-
porcional a n sobre um vetor de n = r p + 1 elementos.
(e) Como a troca da funo separa pela funo separaLomuto afeta o tempo de exe-
cuo do mtodo da ordenao por separao quando todos os valores de entrada
so iguais?

44.13 A funo quicksort contm duas chamadas recursivas para ela prpria. Depois da cha-
mada da separa , o sub-vetor esquerdo ordenado recursivamente e ento o sub-vetor
direito ordenado recursivamente. A segunda chamada recursiva no corpo da funo
quicksort no realmente necessria; ela pode ser evitada usando uma estrutura de

FACOM UFMS
44.2 O RDENAO POR SEPARAO 280

controle iterativa. Essa tcnica, chamada recurso de cauda, fornecida automaticamente


por bons compiladores. Considere a seguinte verso da ordenao por separao , que
simula a recurso de cauda.

1 void quicksort2(int v[], int p, int r)


2 {
3 while (p < r) {
4 q = separa(v, p, r);
5 quicksort2(v, p, q);
6 p = q + 1;
7 }
8 }

Ilustre a operao da funo quicksort2 sobre o vetor v que contm os elementos


{21, 7, 5, 11, 6, 42, 13, 2}. Use a chamada quicksort2(v, 0, n-1) . Argumente que a
funo quicksort2 ordena corretamente o vetor v.

44.14 Um famoso programador props o seguinte mtodo de ordenao de um vetor v[0..n 1]


de nmeros inteiros:

1 void ordPateta(int v[], int i, int j)


2 {
3 int k;
4 if (v[i] > v[j])
5 troca(&v[i], &v[j]);
6 if (i + 1 < j) {
7 k = (j - i + 1) / 3;
8 ordPateta(v, i, j-k);
9 ordPateta(v, i+k, j);
10 ordPateta(v, i, j-k);
11 }
12 }

Ilustre a operao desse novo mtodo de ordenao sobre o vetor v que contm os ele-
mentos {21, 7, 5, 11, 6, 42, 13, 2}. Use a chamada ordPateta(v, 0, n-1) . Argumente
que a funo ordPateta ordena corretamente o vetor v.

44.15 Veja animaes dos mtodos de ordenao que j vimos nas seguintes pginas:

Sorting Algorithms de J. Harrison;


Sorting Algorithms de P. Morin;
Sorting Algorithms Animations de D. R. Martin.

44.16 Familiarize-se com a funo qsort da biblioteca stdlib da linguagem C.

FACOM UFMS
A ULA 45

B IBLIOTECA PADRO

Nas ltimas aulas aprendemos a construir nossas prprias funes. Os benefcios dessa
prtica so muitos, como percebemos. Entre eles, podemos destacar abstrao, organizao e
reaproveitamento de cdigo, por exemplo. Felizmente, no sempre que precisamos de um
trecho de cdigo que realiza um determinado processamento que temos necessariamente de
programar esse trecho, construindo uma funo. A linguagem C fornece uma biblioteca pa-
dro de funes que nos ajuda em muitas tarefas importantes, desde funes de entrada e
sada de dados, manipulao de cadeias de caracteres at funes matemticas. Nesta aula, ve-
remos algumas funes importantes, destacando que muitas outras no sero cobertas aqui. Os
interessados em mais informaes devem buscar a pgina do guia de referncia da biblioteca
da linguagem C. Nesta aula, cobrimos com algum detalhe os arquivos que contm definies
de macros, tipos e os prottipos das funes da biblioteca padro de funes da linguagem C.
Esses arquivos so tambm conhecidos como arquivos-cabealhos e tm extenso .h, do ingls
header.
Iniciamos com uma viso sobre os qualificadores de tipos existentes na linguagem C, funda-
mentais especialmente na declarao de parmetros sensveis alterao, tais como os vetores
e as matrizes, que so sempre parmetros por referncia. Em seguida, definimos arquivos-
cabealhos para, por fim, cobrir os arquivos-cabealhos da biblioteca padro da linguagem C.
Esta aula um guia de referncia da biblioteca padro da linguagem C, suas constantes, tipos
e funes principais. Algumas funes, por serem muito especficas, no foram listadas aqui.

45.1 Qualificadores de tipos

Um qualificador de tipo fornece informao adicional sobre um tipo de uma varivel ao


compilador. Na linguagem C, um qualificador de tipo informa o compilador que a varivel,
ou as clulas de memrias que so reservadas para seu uso, tem alguma propriedade especial,
a saber, um valor que no deve ser modificado pelo(a) programador(a) durante a execuo do
programa ou ainda um valor que potencialmente se modifica durante a execuo do programa
sem que haja controle do(a) programador(a) sobre o processo.
Os qualificadores de tipo const e volatile so, respectivamente, os dois qualificadores
da linguagem C que correspondem a essas descries. O segundo, por tratar de programao
de baixo nvel, ser descrito apropriadamente na aula 48.
O qualificador de tipo const usado para declarar objetos que se parecem com variveis
mas so variveis apenas de leitura, isto , um programa pode acessar seu valor, mas no
pode modific-lo. Por exemplo, a declarao abaixo:

281
45.2 A RQUIVO - CABEALHO 282

const int n = 10;

cria um objeto const do tipo int com identificador n cujo valor 10 . A declarao abaixo:

const int v[] = {71, 13, 67, 88, 4, 52};

cria um vetor const de nmeros inteiros e identificador v .


Muitas vantagens podem ser destacadas quando declaramos um objeto do tipo const ,
como por exemplo auto-documentao e verificao pelo compilador de tentativas alterao.
A primeira vista, pode parecer que o qualificador de tipo const tem o mesmo papel da
diretiva #define , que foi usada em aulas anteriores para definir nomes para constantes ou
macros. Existem diferenas significativas entre as duas, que so listadas abaixo:

podemos usar #define para criar um nome par uma constante numrica, caractere ou
cadeia de caracteres; por outro lado, podemos usar const para criar objetos somente
para leitura de qualquer tipo, incluindo vetores, estruturas, unies, apontadores, etc;

objetos criados com const esto sujeitos s mesmas regras de escopo de qualquer vari-
vel, mas constantes criadas com #define no;

o valor de um objeto criado com const pode ser visto em um depurador de programas;
o valor de uma macro no pode;

objetos criados com const no podem ser usados em expresses constantes; as constan-
tes criadas com #define podem;

podemos aplicar o operador de endereamento & sobre um objeto const , j que ele
possui um endereo; uma macro no tem um endereo.

No h uma regra bem definida que determina o uso de #define ou const na decla-
rao de constantes. Em geral, usamos a diretiva #define mais especificamente para criar
constantes que representam nmeros ou caracteres.

45.2 Arquivo-cabealho

A diretiva #include ordena o pr-processador a abrir um arquivo especificado e a inse-


rir seu contedo no arquivo atual. Assim, se queremos que vrios arquivos de cdigo fonte
tenham acesso mesma informao, devemos colocar essa informao em um arquivo e en-
to usar a diretiva #include para trazer o contedo do arquivo em cada arquivo de cdigo
fonte. Arquivos que so includos dessa forma so chamados de arquivos-cabealhos, do in-
gls header files ou tambm include files. Por conveno, um arquivo como esse tem a extenso
.h .

FACOM UFMS
45.2 A RQUIVO - CABEALHO 283

A diretiva #include pode ser usada de duas formas. A primeira forma usada para
arquivos-cabealhos que pertencem biblioteca padro da linguagem C:

#include <arquivo.h>

A segunda forma usada para todos os outros arquivos-cabealhos, incluindo aqueles que
so escritos por programadores(as):

#include "arquivo.h"

A diferena entre as duas formas se d pela maneira como o compilador busca o arquivo-
cabealho. Na primeira, o compilador busca o arquivo-cabealho no(s) diretrio(s) em que os
arquivos-cabealhos do sistema se encontram. Nos sistemas baseados no UNIX, como o LINUX,
os arquivos-cabealhos so mantidos usualmente no diretrio /usr/include . Na segunda
forma, a busca realizada no diretrio corrente e, em seguida, no(s) diretrio(s) em que os
arquivos-cabealhos do sistema se encontram.
Arquivos-cabealhos auxiliam no compartilhamento de definies de macros, de tipos e de
prottipos de funes por dois ou mais arquivos de cdigo fonte.
Abaixo apresentamos o arquivo-cabealho completo assert.h da biblioteca padro da
linguagem C.

/* Copyright (C) 1991,1992,1994-2001,2003,2004,2007


Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or


modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

The GNU C Library is distributed in the hope that it will be useful,


but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */

/*
* ISO C99 Standard: 7.2 Diagnostics <assert.h>

FACOM UFMS
45.2 A RQUIVO - CABEALHO 284

*/

#ifdef _ASSERT_H

# undef _ASSERT_H
# undef assert
# undef __ASSERT_VOID_CAST

# ifdef __USE_GNU
# undef assert_perror
# endif

#endif /* assert.h */

#define _ASSERT_H 1
#include <features.h>

#if defined __cplusplus && __GNUC_PREREQ (2,95)


# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif

/* void assert (int expression);

If NDEBUG is defined, do nothing.


If not, and EXPRESSION is zero, print an error message and abort. */

#ifdef NDEBUG

# define assert(expr) (__ASSERT_VOID_CAST (0))

/* void assert_perror (int errnum);

If NDEBUG is defined, do nothing. If not, and ERRNUM is not zero, print an


error message with the error text for ERRNUM and abort.
(This is a GNU extension.) */

# ifdef __USE_GNU
# define assert_perror(errnum) (__ASSERT_VOID_CAST (0))
# endif

#else /* Not NDEBUG. */

#ifndef _ASSERT_H_DECLS
#define _ASSERT_H_DECLS
__BEGIN_DECLS

/* This prints an "Assertion failed" message and aborts. */


extern void __assert_fail (__const char *__assertion, __const char *__file,
unsigned int __line, __const char *__function)
__THROW __attribute__ ((__noreturn__));

FACOM UFMS
45.2 A RQUIVO - CABEALHO 285

/* Likewise, but prints the error text for ERRNUM. */


extern void __assert_perror_fail (int __errnum, __const char *__file,
unsigned int __line,
__const char *__function)
__THROW __attribute__ ((__noreturn__));

/* The following is not at all used here but needed for standard
compliance. */
extern void __assert (const char *__assertion, const char *__file, int __line)
__THROW __attribute__ ((__noreturn__));

__END_DECLS
#endif /* Not _ASSERT_H_DECLS */

# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))

# ifdef __USE_GNU
# define assert_perror(errnum) \
(!(errnum) \
? __ASSERT_VOID_CAST (0) \
: __assert_perror_fail ((errnum), __FILE__, __LINE__, __ASSERT_FUNCTION))
# endif

/* Version 2.4 and later of GCC define a magical variable __PRETTY_FUNCTION__


which contains the name of the function currently being defined.
This is broken in G++ before version 2.6.
C9x has a similar variable called __func__, but prefer the GCC one since
it demangles C++ function names. */
# if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4)
# define __ASSERT_FUNCTION __PRETTY_FUNCTION__
# else
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __ASSERT_FUNCTION __func__
# else
# define __ASSERT_FUNCTION ((__const char *) 0)
# endif
# endif

#endif /* NDEBUG. */

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 286

45.3 Arquivos-cabealhos da biblioteca padro

45.3.1 Diagnsticos

assert.h o arquivo-cabealho que contm informaes sobre a biblioteca de diagnsti-


cos. Contm uma nica macro, assert , que permite aos programadores inserirem pontos de
verificao nos programas. Se uma verificao falha, o programa termina sua execuo.
No exemplo de trecho de cdigo a seguir, a macro assert monitora o valor do ndice i do
vetor v. Se esse valor no est no intervalo especificado, o programa terminado.

assert(0 <= i && i <= 9);


v[i] = 0;

45.3.2 Manipulao, teste e converso de caracteres

A biblioteca de manipulao, teste e converso de caracteres pode ser acessada atravs das
informaes contidas no arquivo-cabealho ctype.h.

Funes is...

Uma funo cujo prottipo se encontra no arquivo-cabealho ctype.h com identificador da


forma is... verifica alguma propriedade de um caractere, argumento na chamada da funo, e
devolve um valor diferente de zero, indicando verdadeiro, isto , que o caractere tem a propri-
edade, ou um valor igual a zero, indicando falso ou que o caractere no possui a propriedade.
Listamos abaixo as interfaces das funes da forma is... em ctype.h, acompanhadas de
explicaes:

int isalnum(int c)
Devolve verdadeiro se o contedo do parmetro c uma letra (de A a Z ou de a a
z) ou um dgito (de 0 a 9);

int isalpha(int c)
Devolve verdadeiro se o contedo do parmetro c uma letra (de A a Z ou de a a
z);

int iscntrl(int c)
Devolve verdadeiro se o contedo do parmetro c um caractere de controle (de 0 a 31
ou 127);

int isdigit(int c)
Devolve verdadeiro se o contedo do parmetro c um dgito (de 0 a 9);

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 287

int isgraph(int c)
Devolve verdadeiro se o contedo do parmetro c um caractere imprimvel, exceto o
espao em branco, isto , caracteres 33 a 126);
int islower(int c)
Devolve verdadeiro se o contedo do parmetro c uma letra minscula (de a a z);
int isprint(int c)
Devolve verdadeiro se o contedo do parmetro c um caractere imprimvel, isto , ca-
racteres de 32 a 126;
int ispunct(int c)
Devolve verdadeiro se o contedo do parmetro c um caractere de pontuao;
int isspace(int c)
Devolve verdadeiro se o contedo do parmetro c um branco (whitespace);
int isupper(int c)
Devolve verdadeiro se o contedo do parmetro c uma letra maiscula (de A a Z);
int isxdigit(int c)
Devolve verdadeiro se o contedo do parmetro c um dgito em hexadecimal (de 0 a
9, ou
de a a f, ou ainda de A a F).

Funes to...

Uma funo em ctype.h com identificador da forma to... faz a converso de um carac-
tere. Se o caractere atende a uma condio, ento convertido. Caso contrrio, o caractere
devolvido sem modificaes.
Listamos abaixo as interfaces das funes da forma to... em ctype.h, acompanhadas de
explicaes:

int tolower(int c)
Se o parmetro c uma letra maiscula (de A a Z), ento o caractere convertido para
uma letra minscula e devolvido;
int toupper(int c)
Se o parmetro c uma letra minscula (de a a z), ento o caractere convertido para
uma letra maiscula e devolvido.

45.3.3 Erros

A biblioteca de monitoramento de erros acessada por meio do arquivo-cabealho errno.h.


Algumas funes da biblioteca padro da linguagem C indicam ocorrncia de uma falha atra-
vs do armazenamento de um cdigo de erro, um nmero inteiro positivo, em errno , uma
varivel do tipo int declarada em errno.h. O trecho de cdigo a seguir mostra o uso da
varivel errno :

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 288

errno = 0;
y = sqrt(x);
if (errno != 0)
exit(EXIT_FAIULURE);

Nesse trecho de cdigo, observe que inicializamos a varivel errno declarada na biblioteca
errno.h. Se o valor dessa varivel diferente de zero aps a execuo da funo sqrt , isso
significa que um erro ocorreu em sua execuo, como por exemplo, quando o valor de x
negativo.
O valor armazenado na varivel errno , em geral, EDOM ou ERANGE , ambas macros
definidas em errno.h. Essas macros representam os dois tipos de erros que podem ocorrer
quando uma funo matemtica chamada: erro de domnio ou erro de intervalo, respectiva-
mente. No exemplo acima, se x tem valor negativo, ento o valor de EDOM armazenado na
varivel errno . Por outro lado, se o valor devolvido por uma funo muito grande, ultra-
passando a capacidade de representao do tipo do valor a ser devolvido pela funo, ento
o valor ERANGE armazenado em errno . Por exemplo, se 1000 argumento da funo exp,
ento isso provoca um erro de intervalo j que e1000 muito grande para ser representado por
um double .

45.3.4 Caractersticas dos tipos com ponto flutuante

Caractersticas dos tipos com ponto flutuante podem ser obtidas atravs da incluso do
arquivo-cabealho float.h. Esse arquivo fornece apenas macros que definem o intervalo e a
preciso dos tipos float , double e long double . No h variveis, tipos ou funes em
float.h.

Duas macros se aplicam a todos os tipos de ponto flutuante: FLT_ROUNDS e FLT_RADIX .


A primeira descreve a direo do arredondamento. A segunda especifica o comprimento da
representao do expoente.
As macros restantes descrevem as caractersticas dos tipos especficos de ponto flutuante.

45.3.5 Localizao

locale.h o arquivo-cabealho que contm as informaes da biblioteca de funes que


ajudam um programa a adaptar seu comportamento a um pas ou a uma regio geogrfica.
O comportamento especfico de um local inclui a forma como os nmeros so impressos, o
formato dos valores monetrios, o conjunto de caracteres e a aparncia da data e da hora.
Com a mudana de um local, um programa pode adaptar seu comportamento para uma
rea diferente do mundo. Mas essa mudana pode afetar muitas partes da biblioteca, algumas
das quais podemos preferir no alterar.

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 289

45.3.6 Matemtica

A biblioteca matemtica da linguagem C pode ser acessada atravs do arquivo-cabealho


math.h, que contm os prottipos de diversas funes matemticas teis, agrupadas em fun-
es trigonomtricas, funes logartmicas, de exponenciao e de potenciao, alm de outras
funes.
Quando inclumos o arquivo-cabealho math.h em um programa e usamos suas funes,
importante destacar que h necessidade de adicionar a diretiva de compilao -lm no pro-
cesso de compilao deste programa, na chamada do gcc . Caso contrrio, um erro de compi-
lao ser emitido pelo compilador da linguagem C.

Constantes

Uma nica macro definida neste arquivo-cabealho: HUGE_VAL . Essa macro indica que
o valor devolvido por uma funo muito grande para ser representado como um nmero de
preciso dupla. HUGE_VAL do tipo double e pode ser entendido como infinito.

Erros

Todas as funes da biblioteca matemtica da linguagem C manipulam erros de forma si-


milar. No caso em que o argumento passado funo excede o intervalo permitido, ento a
varivel errno recebe o valor EDOM . O valor devolvido pela funo depende da mquina. No
caso do valor devolvido ser muito grande para ser representado como um nmero de preciso
dupla, ento a funo devolve a macro HUGE_VAL e atualiza a varivel errno para ERANGE .
Se o valor muito pequeno para ser representado como um nmero de preciso dupla, ento
a funo devolve zero. Nesse caso, se a varivel errno recebe ERANGE ou no uma deciso
dependente da mquina.

Funes trigonomtricas

Listamos abaixo as interfaces das funes trigonomtricas no arquivo-cabealho math.h,


acompanhadas de explicaes:

double cos(double x)
Devolve o cosseno de um ngulo x dado em radianos. O valor devolvido est no intervalo
[1, +1];

double sin(double x)
Devolve o seno de um ngulo x dado em radianos. O valor devolvido est no intervalo
[1, +1];

double tan(double x)
Devolve a tangente de um ngulo x dado em radianos;

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 290

double acos(double x)
Devolve o arco-cosseno de x em radianos. O valor de x deve estar no intervalo [1, +1].
O valor devolvido est no intervalo [0, ];

double asin(double x)
Devolve o arco-seno de x em radianos. O valor de x deve estar no intervalo [1, +1]. O
valor devolvido est no intervalo [/2, +/2];

double atan(double x)
Devolve o arco-tangente de x em radianos. O valor devolvido est no intervalo
[/2, +/2];

double atan2(double y, double x)


Devolve o arco-tangente em radianos de y/x baseado nos sinais de ambos os valores para
determinar o quadrante correto. O valor devolvido est no intervalo [/2, /2];

double cosh(double x)
Devolve o cosseno hiperblico de x;

double sinh(double x)
Devolve o seno hiperblico de x;

double tanh(double x)
Devolve a tangente hiperblica de x. O valor devolvido est no intervalo [1, +1].

Funes logartmicas, de exponenciao e de potenciao

Listamos abaixo as interfaces das funes logartmicas, de exponenciao e de potenciao


em math.h, acompanhadas de explicaes:

double exp(double x)
Devolve o valor de e elevado x-sima potncia;

double frexp(double x, int *expoente)


O nmero com ponto flutuante x subdividido em uma mantissa e um expoente. O valor
devolvido pela funo a mantissa e o parmetro de entrada e sada expoente contm o
expoente. Observe que
expoente
x = matissa 2 .
A mantissa deve estar no intervalo [0.5, 1.0].

double ldexp(double x, int expoente)


Devolve x multiplicado por 2 elevado potncia expoente, isto , x 2expoente .

double log(double x)
Devolve o logaritmo natural de x, isto , o logaritmo na base e;

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 291

double log10(double x)
Devolve o logaritmo de x na base 10;

double modf(double x, double *inteiro)


Subdivide o nmero com ponto flutuante x nas partes inteira e fracionria. O valor devol-
vido pela funo a parte fracionria, depois do ponto decimal, e o parmetro de entrada
e sada inteiro contm a parte inteira de x;

double pow(double x, double y)


Devolve x elevado potncia y. O valor de x no pode ser negativo se y um valor
fracionrio. x no pode ser zero se y menor ou igual a zero.

double sqrt(double x)
Devolve a raiz quadrada de x. O valor armazenado em x no pode ser negativo.

Outras funes

Listamos abaixo as interfaces de outras funes matemticas em math.h, acompanhadas de


explicaes:

double ceil(double x)
Devolve o menor inteiro que maior ou igual a x;

double floor(double x)
Devolve o maior inteiro que menor ou igual a x;

double fabs(double x)
Devolve o valor absoluto de x. Isto , se x um valor negativo, a funo devolve o valor
positivo. Caso contrrio, devolve o valor de x;

double fmod(double x, double y)


Devolve o resto de x dividido por y. O valor de y deve ser diferente de zero.

45.3.7 Saltos no-locais

setjmp.h o arquivo-cabealho que contm informaes sobre saltos no-locais de baixo


nvel para controle de chamadas de funes e desvio de fluxo de execuo das mesmas. Duas
funes constam desta biblioteca, uma delas que marca um ponto no programa e outra que
pode ser usada para fazer com que o programa tenha seu fluxo de execuo desviado para
um dos pontos marcados. Isso significa que possvel, com o uso das funes desta biblioteca,
uma funo saltar diretamente para uma outra funo sem que tenha encontrado uma sentena
return . Esses recursos so usados especialmente para depurao de erros.

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 292

45.3.8 Manipulao de sinais

Arquivo-cabealho signal.h que contm informaes sobre a biblioteca de manipulao de


sinais signal.h. Contm funes que tratam de condies excepcionais, incluindo interrupes
e erros de tempo de execuo. Alguns sinais so assncronos e podem acontecer a qualquer
instante durante a execuo de um programa, no apenas em certos pontos do cdigos que so
conhecidos pelo programador.

45.3.9 Nmero varivel de argumentos

Biblioteca de ferramentas para tratamento de funes que possuem um nmero varivel


de argumentos acessada pelo arquivo-cabealho stdarg.h. Funes como printf e scanf
permitem um nmero varivel de argumentos e programadores tambm podem construir suas
prprias funes com essa caracterstica.

45.3.10 Definies comuns

O arquivo-cabealho stddef.h contm informaes sobre definies de tipos e macros que


so usadas com freqncia nos programas. No h funes declaradas neste arquivo.

45.3.11 Entrada e sada

A biblioteca padro de entrada e sada da linguagem C acessada atravs do arquivo-


cabealho stdio.h e contm tipos, macros e funes para efetuar entrada e sada de dados,
incluindo operaes sobre arquivos. Listamos a seguir apenas alguns desses elementos.

Entrada e sada formatadas

Listamos abaixo as interfaces das funes de entrada e sada formatadas em stdio.h, acom-
panhadas de explicaes:

int printf(const char *formato, ...)


Imprime informaes na sada padro, tomando uma cadeia de caracteres de formatao
especificada pelo argumento formato e aplica para cada argumento subseqente o espe-
cificador de converso na cadeia de caracteres de formatao, da esquerda para direita.
Veja a aula 18 para informaes sobre especificadores de converso.
O nmero de caracteres impressos na sada padro devolvido. Se um erro ocorreu na
chamada da funo, ento o valor 1 devolvido.

int scanf(const char *formato, ...)


L dados de uma forma que especificada pelo argumento formato e e armazenada cada
valor lido nos argumentos subseqentes, da esquerda para direita. Cada entrada for-
matada definida na cadeia de caracteres de formatao, atravs de um especificador
de converso precedido pelo smbolo % que especifica como uma entrada deve ser ar-
mazenada na varivel respectiva. Outros caracteres listados na cadeia de caracteres de

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 293

formatao especificam caracteres que devem corresponder na entrada mas no armaze-


nados nos argumentos da funo. Um branco pode corresponder a qualquer outro branco
ou ao prximo caractere incompatvel. Mais informaes sobre entrada formatada, veja a
aula 18.
Se a execuo da funo obteve sucesso, o nmero de valores lidos, convertidos e arma-
zenados nas variveis devolvido.

Entrada e sada de caracteres

Listamos abaixo as interfaces das funes de entrada e sada de caracteres em stdio.h,


acompanhadas de explicaes:

int getchar(void)
L um caractere (um unsigned char) da entrada padro. Se a leitura tem sucesso, o carac-
tere devolvido;

int putchar(int caractere)


Imprime um caractere (um unsigned char) especificado pelo argumento caractere na
sada padro. Se a impresso tem sucesso, o caractere devolvido.

45.3.12 Utilitrios gerais

A biblioteca de utilitrios gerais da linguagem C pode ser acessada pelo arquivo-cabealho


stdlib.h. uma miscelnea de definies de tipos, macros e funes que no se encaixam nos
outros arquivos-cabealhos.

Constantes

Listamos abaixo as macros definidas em stdlib.h, acompanhadas de explicaes:

NULL
Valor de um apontador nulo;

EXIT_FAILURE e EXIT_SUCCESS
Valores usados pela funo exit para devolver o estado do trmino da execuo de um
programa;

RAND_MAX
Valor mximo devolvido pela funo rand .

Funes sobre cadeias de caracteres

Listamos abaixo as interfaces das funes de cadeias de caracteres em stdlib.h, acompa-


nhadas de explicaes:

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 294

double atof(const char *cadeia)


A cadeia de caracteres cadeia convertida e devolvida como um nmero de ponto
flutuante do tipo double . Brancos iniciais so ignorados. O nmero pode conter um
sinal opcional, uma seqncia de dgitos com um caractere de ponto decimal opcional,
mais uma letra e ou E opcional seguida por um expoente opcionalmente sinalizado. A
converso pra quando o primeiro caractere no reconhecvel encontrado.
Se a converso foi realizada com sucesso, o nmero devolvido. Caso contrrio, zero
devolvido;

int atoi(const char *cadeia)


A cadeia de caracteres cadeia convertida e devolvida como um nmero inteiro do tipo
int . Bancos iniciais so ignorados. O nmero pode conter um sinal opcional, mais uma
seqncia de dgitos. A converso pra quando o primeiro caractere no reconhecvel
encontrado.
Se a converso foi realizada com sucesso, o nmero devolvido. Caso contrrio, zero
devolvido;

long int atol(const char *cadeia)


A cadeia de caracteres cadeia convertida e devolvida como um nmero inteiro do
tipo long int . Brancos iniciais so ignorados. O nmero pode conter um sinal opcio-
nal, mais uma seqncia de dgitos. A converso pra quando o primeiro caractere no
reconhecvel encontrado.
Se a converso foi realizada com sucesso, o nmero devolvido. Caso contrrio, zero
devolvido.

Funes de ambiente

Listamos abaixo as interfaces das funes de ambiente em stdlib.h, acompanhadas de ex-


plicaes:

void abort(void)
Finaliza o programa anormalmente, sinalizando o trmino sem sucesso para o ambiente;

void exit(int estado)


Finaliza o programa normalmente, sinalizando para o ambiente o valor armazenado em
estado , que pode ser EXIT_FAILURE ou EXIT_SUCCESS .

Funes matemticas

Listamos abaixo as interfaces das funes matemticas em stdlib.h, acompanhadas de ex-


plicaes:

int abs(int x)
Devolve o valor absoluto de x ;

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 295

long int labs(long int x)


Devolve o valor absoluto de x ;

int rand(void)
Devolve um nmero pseudo-aleatrio no intervalo [0, RAND_MAX ];

void srand(unsigned int semente)


Inicializa o gerador de nmero pseudo-aleatrios usado pela funo rand . Inicializar
srand com o mesmo valor de semente faz com que a funo rand devolva a mesma
seqncia de nmeros pseudo-aleatrios. Se srand no for executada, rand age como
se srand(1) tivesse sido executada.

45.3.13 Manipulao de cadeias

A biblioteca de manipulao de cadeias de caracteres pode ser acessada atravs do arquivo-


cabealho string.h.

Constantes e tipos

A macro NULL definida em string.h, para denotar uma cadeia de caracteres vazia, assim
como o tipo typedef size_t, que permite a manipulao de variveis que contm comprimen-
tos de cadeias de caracteres. Ou seja, esse tipo na verdade um tipo int .

Funes

Listamos abaixo as principais funes em string.h, acompanhadas de explicaes:

char *strcat(char *cadeia1, const char *cadeia2)


Concatena a cadeia de caracteres cadeia2 no final da cadeia de caracteres cadeia1 .
O caractere nulo \0 da cadeia1 sobrescrito. A concatenao termina quando o
caractere nulo da cadeia2 copiado. Devolve um apontador para a cadeia1 ;

char *strncat(char *cadeia1, const char *cadeia2, size_t n)


Concatena at n caracteres da cadeia de caracteres cadeia2 no final da cadeia de ca-
racteres cadeia1 . O caractere nulo \0 da cadeia1 sobrescrito. O caractere nulo
sempre adicionado na cadeia1 . Devolve um apontador para a cadeia1 ;

int strcmp(const char *cadeia1, const char *cadeia2)


Compara as cadeias de caracteres cadeia1 e cadeia2 . Devolve zero se cadeia1 e
cadeia2 so iguais. Devolve um valor menor que zero ou maior que zero se cadeia1
menor que ou maior que cadeia2 , respectivamente;

int strncmp(const char *cadeia1, const char *cadeia2, unsigned int n)

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 296

Compara no mximo os primeiros n caracteres de cadeia1 e cadeia2 . Finaliza a


comparao aps encontrar um caractere nulo. Devolve zero se os n primeiros caracte-
res, ou os caracteres at alcanar o caractere nulo, de cadeia1 e cadeia2 so iguais.
Devolve um valor menor que zero ou maior que zero se cadeia1 menor que ou maior
que cadeia2 , respectivamente;
char *strcpy(char *cadeia1, const char *cadeia2)
Copia a cadeia de caracteres cadeia2 na cadeia de caracteres cadeia1 . Copia at o
caractere nulo de cadeia2 , inclusive. Devolve um apontador para cadeia1 ;
char *strncpy(char *cadeia1, const char *cadeia2, unsigned int n)
Copia at n caracteres de cadeia2 em cadeia1 . A cpia termina quando n caracteres
so copiados ou quando o caractere nulo em cadeia2 alcanado. Se o caractere nulo
alcanado, os caracteres nulos so continuamente copiados para cadeia1 at que n
caracteres tenham sido copiados;
size_t strlen(const char *cadeia)
Computa o comprimento da cadeia , no incluindo o caractere nulo. Devolve o nmero
de caracteres da cadeia ;
char *strpbrk(const char *cadeia1, const char *cadeia2)
Procura o primeiro caractere na cadeia1 que corresponde a algum caractere especifi-
cado na cadeia2 . Se encontrado, um apontador para a localizao deste caractere
devolvido. Caso contrrio, o apontador para nulo devolvido;
char *strchr(const char *cadeia, int c)
Busca pela primeira ocorrncia do caractere c na cadeia de caracteres cadeia . Devolve
um apontador para a posio que contm o primeiro caractere na cadeia que corres-
ponde a c ou nulo se c no ocorre em cadeia ;
char *strrchr(const char *cadeia, int c)
Busca pela ltima ocorrncia do caractere c na cadeia de caracteres cadeia . Devolve
um apontador para a posio que contm o ltimo caracter na cadeia que corresponde
c ou nulo se c no ocorre em cadeia ;

size_t strspn(const char *cadeia1, const char *cadeia2)


Encontra a primeira seqncia de caracteres na cadeia1 que contm qualquer carac-
ter da cadeia2 . Devolve o comprimento desta primeira seqncia de caracteres que
corresponde cadeia2 ;
size_t strcspn(const char *cadeia1, const char *cadeia2)
Encontra a primeira seqncia de caracteres na cadeia1 que no contm qualquer ca-
racter da cadeia2 . Devolve o comprimento desta primeira seqncia de caracteres que
no corresponde cadeia2 ;
char *strstr(const char *cadeia1, const char *cadeia2)
Encontra a primeira ocorrncia da cadeia2 na cadeia1 . Devolve um apontador para
a localizao da primeira ocorrncia da cadeia2 na cadeia1 . Se no h correspon-
dncia, o apontador nulo devolvido. Se cadeia2 contm uma cadeia de caracteres de
comprimento zero, ento cadeia1 devolvida.

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 297

45.3.14 Data e hora

A biblioteca que contm funes para determinar, manipular e formatar horrios e datas
pode ser acessada atravs do arquivo-cabealho time.h.

Exerccios

45.1 (a) Escreva uma funo com a seguinte interface

double cosseno(double x, double epsilon)

que receba um nmero real x e um nmero positivo real > 0, e calcule uma apro-
ximao para cos x, onde x dado em radianos, atravs da seguinte srie:

x2 x4 x6 x2k
cos x = 1 + + . . . + (1)k + ...
2! 4! 6! (2k)!
x2k
incluindo todos os termos Tk = (2k)! , at que Tk < .
(b) Escreva uma funo com a seguinte interface:

double seno(double x, double epsilon)

que receba um nmero real x e um nmero real > 0, e calcule uma aproximao
para sen x, onde x dado em radianos, atravs da seguinte srie:

x x3 x5 x2k+1
sen x = + . . . + (1)k + ...
1! 3! 5! (2k + 1)!
x2k+1
incluindo todos os termos Tk = (2k+1)! , at que Tk < .
(c) Escreva um programa que receba um nmero real x representando um ngulo em
radianos e um nmero real > 0 representando uma preciso, e calcule o valor de
tan x de duas formas: usando as funes dos itens acima e usando a funo tan da
biblioteca math . Compare os resultados.

45.2 (a) Escreva uma funo com a seguinte interface:

int ocorre(char palavra[], char frase[], int pos)

que receba duas cadeias de caracteres palavra e frase e um inteiro pos e veri-
fique se a cadeia de caracteres palavra ocorre na posio pos da cadeia de carac-
teres frase . Em caso positivo, a funo deve devolver 1. Em caso negativo, deve
devolver zero.

FACOM UFMS
45.3 A RQUIVOS - CABEALHOS DA BIBLIOTECA PADRO 298

(b) Escreva um programa que receba duas cadeias de caracteres padrao e texto ,
com m e n caracteres respectivamente e m 6 n, e imprima o nmero de vezes que
padrao ocorre em texto .
Use todas as funes da biblioteca string que puder.

FACOM UFMS
A ULA 46

P R - PROCESSADOR

O pr-processador um mdulo da linguagem C que edita um programa antes de sua com-


pilao. uma ferramenta poderosa e que diferencia a linguagem C das demais linguagens de
programao do alto nvel. As diretivas #include e #define que usamos em aulas anterio-
res so manipuladas pelo pr-processador, assim como outras que veremos nesta aula. Apesar
de poderoso, o mal uso do pr-processador pode produzir programas praticamente inintelig-
veis e/ou com erros muito difceis de encontrar.

46.1 Funcionamento

As diretivas de pr-processamento controlam o comportamento do pr-processador. Uma


diretiva do pr-processador um comando que inicia com o caractere # . At o momento,
vimos duas das diretivas do pr-processador da linguagem C: #include e #define . Revisa-
remos essas diretivas adiante.
A figura 46.1 ilustra o papel do pr-processador durante o processo de compilao.

programa em C

pr-processador

programa em C modificado

compilador

cdigo objeto

Figura 46.1: O papel do pr-processador durante a compilao.

299
46.2 D IRETIVAS DE PR - PROCESSAMENTO 300

A entrada para o pr-processador um programa escrito na linguagem C, possivelmente


contendo diretivas. O pr-processador executa ento estas diretivas, eliminando-as durante
este processo. A sada produzida pelo pr-processador um outro programa na linguagem
C, que representa uma verso editada do programa original, sem diretivas. A sada do pr-
processador a entrada para o compilador, que verifica erros no programa e realiza sua tradu-
o para o cdigo objeto, que contm apenas instrues diretas para a mquina.

46.2 Diretivas de pr-processamento

A maioria das diretivas de pr-processamento pertencem a uma das trs categorias a seguir:

definio de macros: a diretiva #define define uma macro, tambm chamada de cons-
tante simblica; a diretiva #undef remove a definio de uma macro;

incluso de arquivos: a diretiva #include faz com que o contedo de um arquivo es-
pecificado seja includo em um programa;

compilao condicional: as diretivas #if , #ifdef , #ifndef , #elif , #else e


#endif permitem que trechos de cdigo seja includos ou excludos do programa, de-
pendendo das condies verificadas pelo pr-processador.

As outras diretivas #error , #line e #pragma so mais especficas e usadas com menos
freqncia. Antes de descrev-las com mais detalhes, listamos abaixo as regras que se aplicam
a todas elas:

Diretivas sempre iniciam com o smbolo # . No necessrio que o smbolo # seja o


primeiro caractere da linha. No entanto, apenas espaos podem preced-lo. Aps # o
identificador de uma diretiva deve ser descrito, seguido por qualquer outra informao
que a diretiva necessite.

Qualquer nmero de espaos e caracteres de tabulao horizontal podem separar os


itens em uma diretiva. Por exemplo, a diretiva a seguir est correta:

# define MAX 100

Diretivas sempre terminam no primeiro caractere de mudana de linha, a menos que a


continuao seja explcita. Para continuar uma diretiva em uma prxima linha, devemos
finalizar a linha atual com o caractere \ . Por exemplo, a diretiva abaixo define uma
macro que representa a capacidade de um disco medida em bytes:

#define CAPACIDADE_DISCO (LADOS * \


TRILHAS_POR_LADO * \
SETORES_POR_TRILHA * \
BYTES_POR_SETOR)

FACOM UFMS
46.3 D EFINIES DE MACROS 301

Diretivas podem aparecer em qualquer lugar em um programa. Apesar de usualmente


colocarmos as diretivas #include e #define no comeo de um arquivo, outras direti-
vas so mais provveis de ocorrer em outros pontos do programa.

Comentrios podem ocorre na mesma linha de uma diretiva. Em geral, colocamos um


comentrio no final da definio de uma macro para explicar seu significado. Por exem-
plo:

#define TAMANHO 100 /* Dimenso dos vetores */

46.3 Definies de macros

O uso mais freqente da diretiva #define do pr-processador da linguagem C a atri-


buio de nomes simblicos para constantes. Macros como essas so conhecidas como macros
simples. O pr-processador tambm suporta definio de macros parametrizadas. Nesta aula
veremos apenas as macros simples.
A definio de uma macro simples tem a seguinte forma:

#define identificador lista-de-troca

onde lista-de-troca uma seqncia qualquer de itens. Essa lista pode incluir identifica-
dores, palavras reservadas, constantes numricas, constantes de caracteres, literais, operadores
e pontuao. Quando encontra uma definio de uma macro, o pr-processador toma nota
que o identificador representa a lista-de-troca . Sempre que identificador ocorre
posteriormente no arquivo, o pr-processador o substitui pela lista-de-troca .
Um erro freqente na definio de macros a incluso de smbolos extras, que conseqen-
temente faro parte da lista de troca. Abaixo so mostrados dois exemplos de erros como esse.

#define TAM = 100


#define N 10;
...
int u[TAM];
double v[N];

A traduo do trecho de cdigo acima realizada pelo pr-processador gera o seguinte trecho
de cdigo modificado, correspondente s duas ltima linhas:

FACOM UFMS
46.4 I NCLUSO DE ARQUIVOS - CABEALHOS 302

int u[= 100];


double v[10;];

certo que o compilador acusar erros nessa duas linhas do programa.


Podemos usar macros para dar nomes a valores numricos, caracteres e literais, como ilus-
trado abaixo:

#define COMPRIMENTO 80
#define VERDADEIRO 1
#define FALSO 0
#define PI 3.141592
#define ENTER \n
#define ERRO "Erro: memria insuficiente\n"

Usar a diretiva #define para criar nomes para constantes tem diversas vantagens, como
tornar os programas mais fceis de ler e de modificar, ajudar a evitar inconsistncias e erros
tipogrficos, permitir pequenas mudanas na sintaxe da linguagem, renomear tipos e controlar
a compilao condicional.

46.4 Incluso de arquivos-cabealhos

Vamos recordar a aula 45. A diretiva #include ordena o pr-processador a abrir um ar-
quivo especificado e a inserir seu contedo no arquivo atual. Assim, se queremos que vrios
arquivos de cdigo fonte tenham acesso mesma informao, devemos colocar essa infor-
mao em um arquivo e ento usar a diretiva #include para trazer o contedo do arquivo
em cada arquivo de cdigo fonte. Arquivos que so includos dessa forma so chamados de
arquivos-cabealhos, do ingls header files ou tambm include files. Por conveno, um arquivo
como esse tem a extenso .h .
A diretiva #include pode ser usada de duas formas. A primeira forma usada para
arquivos-cabealhos que pertencem biblioteca padro da linguagem C:

#include <arquivo.h>

A segunda forma usada para todos os outros arquivos-cabealhos, incluindo aqueles que
so escritos por programadores(as):

#include "arquivo.h"

FACOM UFMS
46.4 I NCLUSO DE ARQUIVOS - CABEALHOS 303

Programa 46.1: Um programa curioso.

1 #include <stdio.h>
2

3 #define PAR 0
4 #define IMPAR 1
5 #define Programa main
6 #define inicio {
7 #define fim }
8 #define escreva printf
9 #define leia scanf
10 #define se if
11 #define senao else
12 #define enquanto while
13 #define devolva return
14

15 int verificaParidade(int numero)


16 inicio
17 int paridade;
18 se (numero % 2 == 0)
19 paridade = PAR;
20 senao
21 paridade = IMPAR;
22 devolva paridade;
23 fim
24

25 int Programa(void)
26 inicio
27 int i, n, numero, somapar, somaimpar;
28 escreva("Informe a quantidade de elementos: ");
29 leia("%d", &n);
30 somapar = 0;
31 somaimpar = 0;
32 i = 1;
33 enquanto (i <= n)
34 inicio
35 escreva("Informe um nmero: ");
36 leia("%d", &numero);
37 se (verificaParidade(numero) == PAR)
38 somapar = somapar + numero;
39 senao
40 somaimpar = somaimpar + numero;
41 i++;
42 fim
43 escreva("\nSoma dos nmeros pares: %d\n", somapar);
44 escreva("Soma dos nmeros mpares: %d\n", somaimpar);
45 devolva 0;
46 fim

FACOM UFMS
46.5 C OMPILAO CONDICIONAL 304

A diferena entre as duas formas se d pela maneira como o compilador busca o arquivo-
cabealho. Na primeira, o compilador busca o arquivo-cabealho no(s) diretrio(s) em que os
arquivos-cabealhos do sistema se encontram. Nos sistemas baseados no UNIX, como o LINUX,
os arquivos-cabealhos so mantidos usualmente no diretrio /usr/include . Na segunda
forma, a busca realizada no diretrio corrente e, em seguida, no(s) diretrio(s) em que os
arquivos-cabealhos do sistema se encontram.
Arquivos-cabealhos auxiliam no compartilhamento de definies de macros, de tipos e de
prottipos de funes por dois ou mais arquivos de cdigo fonte. Na aula 47, quando apresen-
tamos formas de tratar programas muito grandes divididos em arquivos diferentes, menciona-
mos novamente a diretiva #include , destacando formas de seu uso no compartilhamento de
definies de macros, de tipos e de prottipos de funes.

46.5 Compilao condicional

O pr-processador da linguagem C reconhece diretivas que permitem a incluso ou exclu-


so de um trecho de programa dependendo do resultado da avaliao de um teste executado
pelo pr-processador. Esse processo chamado de compilao condicional. Em geral, as dire-
tivas de compilao condicional so muito usadas para auxiliar na depurao dos programas.
As diretivas #if e #endif , por exemplo, so usadas em conjunto para verificar se um trecho
de cdigo ser ou no executado, dependendo da condio a ser avaliada. O formato geral da
diretiva #if

#if expresso-constante

e da diretiva #endif simplesmente

#endif

Quando o pr-processador encontra a diretiva #if , ele avalia a expresso-constante .


Se o resultado da avaliao igual a zero, as linhas entre #if e #endif sero removidas do
programa durante o pr-processamento. Caso contrrio, as linhas entre #if e #endif sero
mantidas e processadas pelo compilador. O seguinte trecho de cdigo exemplifica o uso dessas
diretivas e permite verificar seu uso no auxlio do processo de depurao.

#define DEBUG 1
...
#if DEBUG
printf("Valor de i = %d\n", i);
printf("Valor de j = %d\n", j);
#endif

FACOM UFMS
46.5 C OMPILAO CONDICIONAL 305

Durante o pr-processamento, a diretiva #if verifica o valor de DEBUG . Como seu va-
lor diferente de zero, o pr-processador mantm as duas chamadas funo printf no
programa, mas as linhas contendo as diretivas #if e #endif sero eliminadas aps o pr-
processamento. Se modificamos o valor de DEBUG para zero e recompilamos o programa,
ento o pr-processador remove todas as 4 linhas do programa. importante notar que a di-
retiva #if trata identificadores no definidos como macros que tm o valor zero. Assim, se
esquecemos de definir DEBUG , o teste

#if DEBUG

ir falhar, mas no produzir uma mensagem de erro. O teste

#if !DEBUG

produzir o resultado verdadeiro, nesse caso.


Nesse sentido, h duas outras diretivas para verificar se um identificador definido ou no
como uma macro naquele ponto do programa: #ifdef e #ifndef .
O formato geral dessas diretivas

#ifdef identificador
Linhas a serem includas se o identificador est definido como uma macro
#endif

#ifndef identificador
Linhas a serem includas se o identificador no est definido como uma macro
#endif

As diretivas #elif e #else podem ser usadas em conjunto com as diretivas #if ,
#ifdef e #ifndef quando aninhamento de blocos so necessrios. O formato geral dessas
diretivas apresentado abaixo:

#elif expresso-constante

FACOM UFMS
46.6 O UTRAS DIRETIVAS 306

#else

Por exemplo, podemos verificar uma srie de condies como a seguir:

#if expresso1
Linhas a serem includas se expresso1 diferente de zero
#elif expresso2
Linhas a serem includas se expresso2 diferente de zero
#else
Linhas a serem includas em caso contrrio
#endif

A compilao condicional, alm de auxiliar na depurao de programas, tambm tem mui-


tos outros usos como na construo de programas que podem ser transportados de uma m-
quina para outra ou de um sistema operacional para outro, na construo de programas que
podem ser compilados por compiladores diferentes, no suporte definies padronizadas de
macros, entre outros.

46.6 Outras diretivas

As diretivas #error , #line e #pragma so mais especficas e por isso menos usadas.
A diretiva #error faz com que o pr-processador imprima uma mensagem de erro na
sada padro. A diretiva #error tem o seguinte formato geral:

#error mensagem

onde mensagem uma seqncia qualquer de caracteres. Um exemplo de uso dessa diretiva
apresentado no trecho de cdigo a seguir:

#ifdef LINUX
...
#else
#error Sistema operacional no especificado
#endif

FACOM UFMS
46.6 O UTRAS DIRETIVAS 307

Se o pr-processador encontra uma diretiva #error , isso sinal de que uma falha grave
ocorreu no programa e alguns compiladores terminam imediatamente a compilao.
A diretiva #line usada para alterar a forma como as linhas do programa so numeradas.
O formato geral dessa diretiva apresentado abaixo:

#line n

#line n "arquivo"

No primeiro formato, as linhas do programa so numeradas a partir do nmero n . No se-


gundo, as linhas do programa no arquivo so numeradas a partir do nmero n . Muitos
compiladores usam essa informao quando precisam gerar mensagens de erro.
Por fim, a diretiva #pragma permite que uma diretiva seja criada pelo(a) programador(a).
O formato geral dessa diretiva dado abaixo:

#pragma diretiva

FACOM UFMS
A ULA 47

P ROGRAMAS EXTENSOS

Nesta aula aprenderemos como dividir e distribuir nossas funes em vrios arquivos. Esta
possibilidade uma caracterstica importante da linguagem C, permitindo que o(a) programa-
dor(a) possa ter sua prpria biblioteca de funes e possa us-la em conjunto com um ou mais
programas.
Os programas que escrevemos at o momento so bem simples e, por isso mesmo peque-
nos, com poucas linhas de cdigo. No entanto, programas pequenos so uma exceo.
medida que os problemas tm maior complexidade, os programas para solucion-los tm, em
geral, proporcionalmente mais linhas de cdigo. Por exemplo, a verso 2.6.25 do ncleo do
sistema operacional LINUX, de abril de 2008, tem mais de nove milhes de linhas de cdigo
na linguagem C e seria impraticvel mant-las todas no mesmo arquivo1 . Nesta aula vere-
mos que um programa na linguagem C consiste de vrios arquivos-fontes e tambm de alguns
arquivos-cabealhos. aprenderemos a dividir nossos programas em mltiplos arquivos.

47.1 Arquivos-fontes

At a ltima aula, sempre consideramos que um programa na linguagem C consiste de um


nico arquivo. Na verdade, um programa pode ser dividido em qualquer nmero de arquivos-
fontes que, por conveno, tm a extenso .c . Cada arquivo-fonte contm uma parte do pro-
grama, em geral definies de funes e variveis. Um dos arquivos-fontes de um programa
deve necessariamente conter uma funo main , que o ponto de incio do programa. Quando
dividimos um programa em arquivos, faz sentido colocar funes relacionadas e variveis em
um mesmo arquivo-fonte. A diviso de um programa em arquivos-fontes mltiplos tem van-
tagens significativas:

agrupar funes relacionadas e variveis em um nico arquivo ajuda a deixar clara a


estrutura do programa;

cada arquivo-fonte pode ser compilado separadamente, com uma grande economia de
tempo se o programa grande modificado muitas vezes;

funes so mais facilmente re-usadas em outros programas quando agrupadas em


arquivos-fontes separados.
1
O sistema operacional LINUX, por ser livre e de cdigo aberto, permite que voc possa consultar seu cdigo
fonte, alm de modific-lo a seu gosto.

308
47.2 A RQUIVOS - CABEALHOS 309

47.2 Arquivos-cabealhos

Nas aulas 45 e 46 abordamos com algum detalhe os arquivos-cabealhos. Quando dividi-


mos um programa em vrios arquivos-fonte, como uma funo definida em um arquivo pode
chamar uma outra funo definida em outro arquivo? Como dois arquivos podem comparti-
lhar a definio de uma mesma macro ou a definio de um tipo? A diretiva #include nos
ajuda a responder a essas perguntas, permitindo que essas informaes possam ser comparti-
lhadas entre arquivos-fontes.
Muitos programas grandes contm definies de macros e definies de tipos que necessi-
tam ser compartilhadas por vrios arquivos-fontes. Essas definies devem ser mantidas em
arquivos-cabealhos.
Por exemplo, suponha que estamos escrevendo um programa que usa macros com nomes
LOGIC , VERDADEIRO e FALSO . Ao invs de repetir essas macros em cada arquivo-fonte do
programa que necessita delas, faz mais sentido colocar as definies em um arquivo-cabealho
com um nome como logico.h tendo as seguintes linhas:

#define VERDADEIRO 1
#define FALSO 0
#define LOGIC int

Qualquer arquivo-fonte que necessite dessas definies deve conter simplesmente a linha a
seguir:

#include "logico.h"

Definies de tipos tambm so comuns em arquivos-cabealhos. Por exemplo, ao invs de


definir a macro LOGIC acima, podemos usar typedef para criar um tipo logic . Assim, o
arquivo logico.h ter as seguintes linhas:

#define VERDADEIRO 1
#define FALSO 0
typedef logic int;

A figura 47.1 mostra um exemplo de dois arquivos-fontes que incluem o arquivo-cabealho


logico.h .
Colocar definies de macros e tipos em um arquivo-cabealho tem algumas vantagens. Pri-
meiro, economizamos tempo por no ter de copiar as definies nos arquivos-fontes onde so
necessrias. Segundo, o programa torna-se muito mais fcil de modificar, j que a modificao

FACOM UFMS
47.2 A RQUIVOS - CABEALHOS 310

logic.h

#define VERDADEIRO 1
#define FALSO 0
#define LOGIC int

#include "logic.h" #include "logic.h"

Figura 47.1: Incluso de um arquivo-cabealho em dois arquivos-fontes.

da definio de uma macro ou de um tipo necessita ser feita em um nico arquivo-cabealho. E


terceiro, no temos de nos preocupar com inconsistncias em conseqncia de arquivos-fontes
contendo definies diferentes da mesma macro ou tipo.
Suponha agora que um arquivo-fonte contm uma chamada a uma funo soma que est
definida em um outro arquivo-fonte, com nome calculos.c . Chamar a funo soma sem sua
declarao pode ocasionar erros de execuo. Quando chamamos uma funo que est definida
em outro arquivo, sempre importante ter certeza que o compilador viu sua declarao, isto ,
seu prottipo, antes dessa chamada.
Nosso primeiro impulso declarar a funo soma no arquivo-fonte onde ela foi chamada.
Isso resolve o problema, mas pode criar imensos problemas de gerenciamento. Suponha que
a funo chamada em cinqenta arquivos-fontes diferentes. Como podemos assegurar que
os prottipos das funes soma so idnticos em todos esses arquivos? Como podemos ga-
rantir que todos esses prottipos correspondem definio de soma em calculos.c ? Se
soma deve ser modificada posteriormente, como podemos encontrar todos os arquivos-fontes
onde ela usada? A soluo evidente: coloque o prottipo da funo soma em um arquivo-
cabealho e inclua ento esse arquivo-cabealho em todos os lugares onde soma chamada.
Como soma definida em calculos.c , um nome natural para esse arquivo-cabealho
calculos.h . Alm de incluir calculos.h nos arquivos-fontes onde soma chamada, pre-
cisamos inclu-lo em calculos.c tambm, permitindo que o compilador verifique que o pro-
ttipo de soma em calculos.h corresponde sua definio em calculos.c . regra sem-
pre incluir o arquivo-cabealho que declara uma funo em um arquivo-fonte que contm a
definio dessa funo. No faz-lo pode ocasionar erros difceis de encontrar.
Se calculos.c contm outras funes, muitas delas devem ser declaradas no mesmo
arquivo-cabealho onde foi declarada a funo soma . Mesmo porque, as outras funes em

FACOM UFMS
47.2 A RQUIVOS - CABEALHOS 311

calculos.c so de alguma forma relacionadas com soma . Ou seja, qualquer arquivo que
contenha uma chamada soma provavelmente necessita de alguma das outras funes em
calculos.c . Funes cuja inteno seja us-las apenas como suporte dentro de calculos.c
no devem ser declaradas em um arquivo-cabealho.
Para ilustrar o uso de prottipos de funes em arquivos-cabealhos, vamos supor que
queremos manter diversas definies de funes relacionadas a clculos geomtricos em um
arquivo-fonte geometricas.c . As funes so as seguintes:

double perimetroQuadrado(double lado)


{
return 4 * lado;
}
double perimetroTriangulo(double lado1, double lado2, double lado3)
{
return lado1 + lado2 + lado3;
}
double perimetroCirculo(double raio)
{
return 2 * PI * raio;
}
double areaQuadrado(double lado)
{
return lado * lado;
}
double areaTriangulo(double base, double altura)
{
return base * altura / 2;
}
double areaCirculo(double raio)
{
return PI * raio * raio;
}
double volumeCubo(double lado)
{
return lado * lado * lado;
}
double volumeTetraedro(double lado, double altura)
{
return (double)1/3 * areaTriangulo(lado, lado * sqrt(altura) / 2) * altura;
}
double volumeEsfera(double raio)
{
return (double)4/3 * PI * raio * raio;
}

Os prottipos dessas funes, alm de uma definio de uma macro, sero mantidos em
um arquivo-cabealho com nome geometricas.h :

FACOM UFMS
47.2 A RQUIVOS - CABEALHOS 312

double perimetroQuadrado(double lado);


double perimetroCirculo(double raio);
double perimetroTriangulo(double lado1, double lado2, double lado3);
double areaQuadrado(double lado);
double areaCirculo(double raio);
double areaTriangulo(double base, double altura);
double volumeCubo(double lado);
double volumeEsfera(double raio);
double volumeTetraedro(double lado, double altura);

Alm desses prottipos, a macro PI tambm deve ser definida neste arquivo. Ento, um
arquivo-fonte calc.c que calcula medidas de figuras geomtricas e que contm a funo
main pode ser construdo. A figura 47.2 ilustra essa diviso.

geometricas.h
#define PI 3.141592

double perimetroQuadrado(double lado);

double perimetroCirculo(double raio);

double volumeTetraedro(double lado, double altura);

#include "geometricas.h" #include "geometricas.h"

int main(void) double perimetroQuadrado(double lado)


{ {

}
x = areaCirculo(r);

double perimetroCirculo(double raio)


{

} }

calc.c

geometricas.c

Figura 47.2: Relao entre prottipos, arquivo-fonte e arquivo-cabealho.

FACOM UFMS
47.3 D IVISO DE PROGRAMAS EM ARQUIVOS 313

47.3 Diviso de programas em arquivos

Como vimos na seo anterior, podemos dividir nossos programas em diversos arquivos
que possuem uma conexo lgica entre si. Usaremos agora o que conhecemos sobre arquivos-
cabealhos e arquivos-fontes para descrever uma tcnica simples de dividir um programa em
arquivos. Consideramos aqui que o programa j foi projetado, isto , j decidimos quais fun-
es o programa necessita e como arranj-las em grupos logicamente relacionados.
Cada conjunto de funes relacionadas ser colocado em um arquivo-fonte em separado.
geometricas.c da seo anterior um exemplo de arquivo-fonte desse tipo. Alm disso,
criamos um arquivo-cabealho com o mesmo nome do arquivo-fonte, mas com extenso
.h . O arquivo-cabealho geometricas.h da seo anterior um exemplo. Nesse arquivo-
cabealho, colocamos os prottipos das funes includas no arquivo-fonte, lembrando que
as funes que so projetadas somente para dar suporte s funes do arquivo-fonte no de-
vem ter seus prottipos descritos no arquivo-cabealho. Ento, devemos incluir o arquivo-
cabealho em cada arquivo-fonte que necessite chamar uma funo definida no arquivo-fonte.
Alm disso, inclumos o arquivo-cabealho no prprio arquivo-fonte para que o compilador
possa verificar que os prottipos das funes no arquivo-cabealho so consistentes com as
definies do arquivo-fonte. Esse o caso do exemplo da seo anterior, com arquivo-fonte
geometricas.c e arquivo-cabealho geometricas.h . Veja novamente a figura 47.2.

A funo principal main deve ser includa em um arquivo-fonte que tem um nome re-
presentando o nome do programa. Por exemplo, se queremos que o programa seja conhecido
como calc ento a funo main deve estar em um arquivo-fonte com nome calc.c .
possvel que existam outras funes no mesmo arquivo-fonte onde main se encontra, funes
essas que no so chamadas em outros arquivos-fontes do programa.
Para gerar um arquivo-executvel de um programa dividido em mltiplos arquivos-fontes,
os mesmos passos bsicos que usamos para um programa em um nico arquivo-fonte so
necessrios:

compilao: cada arquivo-fonte do programa deve ser compilado separadamente; para


cada arquivo-fonte, o compilador gera um arquivo contendo cdigo objeto, que tm ex-
tenso .o ;

ligao: o ligador combina os arquivos-objetos criados na fase compilao, juntamente


com cdigo das funes da biblioteca, para produzir um arquivo-executvel.

Muitos compiladores nos permitem construir um programa em um nico passo. Com o


compilador GCC, usamos o seguinte comando para construir o programa calc da seo ante-
rior:

gcc -o calc calc.c geometricas.c

Os dois arquivos-fontes so primeiro compilados em arquivos-objetos. Esse arquivos-


objetos so automaticamente passados para o ligador que os combina em um nico arquivo. A
opo -o especifica que queremos que nosso arquivo-executvel tenha o nome calc .

FACOM UFMS
47.3 D IVISO DE PROGRAMAS EM ARQUIVOS 314

Digitar os nomes de todos os arquivos-fontes na linha de comando de uma janela de um


terminal logo torna-se uma tarefa tediosa. Alm disso, podemos desperdiar uma quantidade
de tempo quando reconstrumos um programa se sempre recompilamos todos os arquivos-
fontes, no apenas aqueles que so afetados pelas nossas modificaes mais recentes.
Para facilitar a construo de grandes programas, o conceito de makefiles foi proposto nos
primrdios da criao do sistema operacional UNIX. Um makefile um arquivo que contm
informao necessria para construir um programa. Um makefile no apenas lista os arquivos
que fazem parte do programa, mas tambm descreve as dependncias entre os arquivos. Por
exemplo, da seo anterior, como calc.c inclui o arquivo geometricas.h , dizemos que
calc.c depende de geometricas.h , j que uma mudana em geometricas.h far com
que seja necessria a recompilao de calc.c .
Abaixo, listamos um makefile para o programa calc .

calc: calc.o geometricas.o


gcc -o calc calc.o geometricas.o -lm
calc.o: calc.c geometricas.h
gcc -c calc.c -lm
geometricas.o: geometricas.c geometricas.h
gcc -c geometricas.c -lm

No arquivo acima, existem 3 grupos de linhas. Cada grupo conhecido como um regra.
A primeira linha em cada regra fornece um arquivo-alvo, seguido pelos arquivos dos quais
ele depende. A segunda linha um comando a ser executado se o alvo deve ser reconstrudo
devido a uma alterao em um de seus arquivos de dependncia.
Na primeira regra, calc o alvo:

calc: calc.o geometricas.o


gcc -o calc calc.o geometricas.o -lm

A primeira linha dessa regra estabelece que calc depende dos arquivos calc.o e
geometricas.o . Se qualquer um desses dois arquivos foi modificado desde da ltima cons-
truo do programa, ento calc precisa ser reconstrudo. O comando na prxima linha indica
como a reconstruo deve ser feita, usando o GCC para ligar os dois arquivos-objetos.
Na segunda regra, calc.o o alvo:

calc.o: calc.c geometricas.h


gcc -c calc.c -lm

FACOM UFMS
47.3 D IVISO DE PROGRAMAS EM ARQUIVOS 315

A primeira linha indica que calc.o necessita ser reconstrudo se ocorrer uma alterao
em calc.c ou geometricas.h . A prxima linha mostra como atualizar calc.o atravs da
recompilao de calc.c . A opo -c informa o compilador para compilar calc.c em um
arquivo-objeto, sem lig-lo.
Tendo criado um makefile para um programa, podemos usar o utilitrio make para cons-
truir ou reconstruir o programa. verificando a data e a hora associada com cada arquivo do
programa, make determina quais arquivos esto desatualizados. Ento, ele invoca os coman-
dos necessrios para reconstruir o programa.
Algumas dicas para criar makefiles seguem abaixo:

cada comando em um makefile deve ser precedido por um caractere de tabulao horizon-
tal T A B ;
um makefile armazenado em um arquivo com nome Makefile ; quando o utilitrio
make usado, ele automaticamente verifica o contedo do diretrio atual buscando por
esse arquivo;
use

make alvo

onde alvo um dos alvos listados no makefile; se nenhum alvo especificado, make
construir o alvo da primeira regra.

O utilitrio make complicado o suficiente para existirem dezenas de livros e manuais que
nos ensinam a us-lo. Com as informaes desta aula, temos as informaes bsicas necess-
rias para us-lo na construo de programas extensos divididos em diversos arquivos. Mais
informaes sobre o utilitrio make devem ser buscadas no manual do GNU/Make.

Exerccios

47.1 (a) Escreva uma funo com a seguinte interface:

void preencheAleatorio(int v[], int n)

que receba um vetor v de nmeros inteiros e um nmero inteiro n e gere n nmeros


inteiros aleatrios armazenando-os em v. Use a funo rand da biblioteca stdlib .
(b) Crie um arquivo-fonte com todas as funes de ordenao que vimos nas aulas 42, 43
e 44. Crie tambm um arquivo-cabealho correspondente.
(c) Escreva um programa que receba um inteiro n, com 1 6 n 6 10000, gere um seqn-
cia de n nmeros aleatrios e execute os mtodos de ordenao que conhecemos so-
bre esse vetor, medindo seu tempo de execuo. Use as funes clock e difftime
da biblioteca time .
(d) Crie um makefile para compilar e ligar seu programa.

FACOM UFMS
A ULA 48

O PERAES SOBRE BITS

A linguagem C possui muitas caractersticas de uma linguagem de programao de alto n-


vel e isso significa que podemos escrever programas que so independentes das mquinas que
iro execut-los. A maioria das aplicaes possui naturalmente essa propriedade, isto , pode-
mos propor solues computacionais programas escritos na linguagem C que no tm de se
adaptar s mquinas em que sero executados. Por outro lado, alguns programas mais especfi-
cos, como compiladores, sistemas operacionais, cifradores, processadores grficos e programas
cujo tempo de execuo e/ou uso do espao de armazenamento so crticos, precisam executar
operaes no nvel dos bits.
Outra caracterstica importante da linguagem C, que a diferencia de outras linguagens de
programao alto nvel, que ela permite o uso de operaes sobre bits especficos e sobre
trechos de bits, e ainda permite o uso de estruturas de armazenamento para auxiliar nas ope-
raes de baixo nvel. Nesta aula, tratamos com algum detalhe de cada um desses aspectos da
linguagem C.

48.1 Operadores bit a bit

A linguagem C possui seis operadores bit a bit, que operam sobre dados do tipo inteiro e do
tipo caractere no nvel de seus bits. A tabela abaixo mostra esses operadores e seus significados.

Operador Significado
~ complemento
& E
^ OU exclusivo
| OU inclusivo
<< deslocamento esquerda
>> deslocamento direita

O operador de complemento ~ o nico operador unrio dessa tabela, os restantes todos


so binrios. Os operadores ~, &, ^ e | executam operaes booleanas sobre os bits de seus
operandos.
O operador ~ gera o complemento de seu operando, isto , troca os bits 0 por 1 e os bits 1
por 0. O operador & executa um E booleano bit a bit nos seus operandos. Os operadores ^ e |
executam um OU booleano bit a bit nos seus operandos, sendo que o operador ^ produz 0 caso
os dois bits correspondentes dos operandos sejam 1 e, nesse mesmo caso, o operador | produz

316
48.1 O PERADORES BIT A BIT 317

1. Os operadores de deslocamento << e >> so operadores binrios que deslocam os bits de seu
operando esquerda um nmero de vezes representado pelo operando direita. Por exemplo,
i << j produz como resultado o valor de i deslocado de j posies esquerda. Para cada
bit que deslocado esquerda para fora do nmero, um bit 0 adicionado direita desse
nmero. O mesmo vale inversamente para o operador de deslocamento direita >>.
O programa 48.1 mostra um exemplo simples do uso de todos os operadores sobre bits.

Programa 48.1: Uso de operadores sobre bits.

1 #include <stdio.h>
2 void imprime(unsigned short int n, char msg[])
3 {
4 char binario[17] = {0,0,0,0,0,0,0,0,
5 0,0,0,0,0,0,0,0,\0};
6 unsigned short int m;
7 int i;
8 m = n;
9 for (i = 15; n != 0; i--, n = n / 2)
10 binario[i] = 0 + n % 2;
11 printf("%9s %5d (%s)\n", msg, m, binario);
12 }
13 int main(void)
14 {
15 unsigned short int i, j, k;
16 i = 51;
17 j = 15;
18 imprime(i, "i =");
19 imprime(j, "j =");
20 k = ~i;
21 imprime(k, "~i =");
22 k = i & j;
23 imprime(k, "i & j =");
24 k = i ^ j;
25 imprime(k, "i ^ j =");
26 k = i | j;
27 imprime(k, "i | j =");
28 k = i << 4;
29 imprime(k, "i << 4 =");
30 k = j >> 2;
31 imprime(k, "j >> 2 =");
32 return 0;
33 }

A execuo do programa 48.1 provoca a seguinte sada:

FACOM UFMS
48.1 O PERADORES BIT A BIT 318

i = 51 (0000000000110011)
j = 15 (0000000000001111)
~i = 65484 (1111111111001100)
i & j = 3 (0000000000000011)
i ^ j = 60 (0000000000111100)
i | j = 63 (0000000000111111)
i << 4 = 816 (0000001100110000)
j >> 2 = 3 (0000000000000011)

Os operadores sobre bits tm precedncias distintas uns sobre os outros, como mostra a
tabela abaixo:

Operador Precedncia
~ 1 (maior)
& << >> 2
^ 3
| 4 (menor)

Observe ainda que a precedncia dos operadores sobre bits menor que precedncia dos
operadores relacionais. Isso significa que um sentena como a seguir

if (status & 0x4000 != 0)

tem o significado a seguir

if (status & (0x4000 != 0))

o que muito provavelmente no a inteno do(a) programador(a) neste caso. Dessa forma,
para que a inteno se concretize, devemos escrever:

if ((status & 0x4000) != 0)

Muitas vezes em programao de baixo nvel, queremos acessar bits especficos de uma
seqncia de bits. Por exemplo, quando trabalhamos com computao grfica, queremos co-
locar dois ou mais pixeis em um nico byte. Podemos extrair e/ou modificar dados que so
armazenados em um nmero pequeno de bits com o uso dos operadores sobre bits.

FACOM UFMS
48.1 O PERADORES BIT A BIT 319

Suponha que i uma varivel do tipo unsigned short int , ou seja, um compartimento
de memria de 16 bits que armazena um nmero inteiro. As operaes mais freqentes sobre
um nico bit que podem ser realizadas sobre a varivel i so as seguintes:

Ligar um bit: suponha que queremos ligar o bit 4 de i , isto o quinto bit menos signifi-
cativo, o quinto da direita para a esquerda. A forma mais fcil de ligar o bit 4 executar
um OU inclusivo entre i e a constante 0x0010 :

i = i | 0x0010;

Mais geralmente, se a posio do bit que queremos ligar est armazenada na varivel j ,
podemos usar o operador de deslocamento esquerda e executar a seguinte expresso,
seguida da atribuio:

i = i | 1 << j;

Desligar um bit: para desligar o bit 4 de i , devemos usar uma mscara com um bit 0 na
posio 4 e o bit 1 em todas as outras posies:

i = 0x00ff;
i = i & ~0x0010;

Usando a mesma idia, podemos escrever uma sentena que desliga um bit cuja posio
est armazenada em uma varivel j :

i = i & ~(1 << j);

Testando um bit: o trecho de cdigo a seguir verifica se o bit 4 da varivel i est ligado:

if (i & 0x0010)

Para testar se o bit j est ligado, temos de usar a seguinte sentena:

if (i & 1 << j)

FACOM UFMS
48.1 O PERADORES BIT A BIT 320

Para que o trabalho com bits torne-se um pouco mais fcil, freqentemente damos nomes
s posies dos bits de interesse. Por exemplo, se os bits das posies 1, 2 e 4 correspondem s
cores azul, verde e vermelho, podemos definir macros que representam essas trs posies dos
bits:

#define AZUL 1
#define VERDE 2
#define VERMELHO 4

Ligar, desligar e testar o bit AZUL pode ser feito como abaixo:

i = i | AZUL;
i = i & ~AZUL;
if (i & AZUL) ...

Podemos ainda ligar, desligar e testar vrios bits ao mesmo tempo:

i = i | AZUL | VERDE;
i = i & ~(AZUL | VERMELHO);
if (i & (VERDE | VERMELHO)) ...

Trabalhar com um grupo de vrios bits consecutivos, chamados de trecho de bits1 um


pouco mais complicado que trabalhar com um nico bit. As duas operaes mais comuns
sobre trechos de bits so as seguintes:

Modificar um trecho de bits: para alterar um trecho de bits necessrio inicialmente


limp-lo usando a operao E bit a bit e posteriormente armazenar os novos bits no trecho
de bits com a operao de OU inclusivo. A operao a seguir mostra como podemos
armazenar o valor binrio 101 nos bits 46 da varivel i :

i = i & ~0x0070 | 0x0050;

Suponha ainda que a varivel j contm o valor a ser armazenado nos bits 46 de i .
Ento, podemos executar a seguinte sentena:
1
Traduo livre de bit-field.

FACOM UFMS
48.2 T RECHOS DE BITS EM REGISTROS 321

i = i & ~0x0070 | j << 4;

Recuperar um trecho de bits: quando o trecho de bits atinge o bit 0 da varivel, a recupe-
rao de seu valor pode ser facilmente obtida da seguinte forma. Suponha que queremos
recuperar os bits 02 da varivel i . Ento, podemos fazer:

j = i & 0x0007;

Se o trecho de bits no tem essa propriedade, ento podemos deslocar o trecho de bits
direita para depois extra-lo usando a operao E. Para extrair, por exemplo, os bits 46
de i , podemos usar a seguinte sentena:

j = (i >> 4) & 0x0007;

48.2 Trechos de bits em registros

Alm das tcnicas que vimos na seo anterior para trabalhar com trechos de bits, a lin-
guagem C fornece como alternativa a declarao de registros cujos campos so trechos de bits.
Essa caracterstica pode ser bem til especialmente em casos em que a complexidade de pro-
gramao envolvendo trechos de bits gera arquivos-fontes complicados e confusos.
Suponha que queremos armazenar uma data na memria. Como os nmeros envolvidos
so relativamente pequenos, podemos pensar em armazen-la em 16 bits, com 5 bits para o dia,
4 bits para o ms e 7 bits para o ano, como mostra a figura 48.1.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
ano ms dia

Figura 48.1: A forma de armazenamento de uma data.

Usando trechos de bits, podemos definir um registro na linguagem C com o mesmo for-
mato:

struct data {
unisgned int dia: 5;
unisgned int mes: 4;
unisgned int ano: 7;
};

FACOM UFMS
48.2 T RECHOS DE BITS EM REGISTROS 322

O nmero aps cada campo indica o comprimento em bits desse campo. O tipo de um
trecho de bits pode ser um de dois possveis: int ( signed int ) e unsigned int . O me-
lhor declarar todos os trechos de bits que so campos de um registro como signed int ou
unsigned int .
Podemos usar os trechos de bits exatamente como usamos outros campos de um registro,
como mostra o exemplo a seguir:

struct data data_arquivo;


data_arquivo.dia = 28;
data_arquivo.mes = 12;
data_arquivo.ano = 8;

Podemos considerar que um valor armazenado no campo ano seja relativo ao ano de
19802 . Ou seja a atribuio do valor 8 ao campo ano , como feita acima, tem significado 1988.
Depois dessas atribuies, a varivel data_arquivo tem a aparncia mostrada na figura 48.2.

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

Figura 48.2: Varivel data_arquivo com valores armazenados.

Poderamos usar operadores sobre bits para obter o mesmo resultado, talvez at mais rapi-
damente. No entanto, escrever um programa legvel usualmente mais importante que ganhar
alguns microssegundos.
Trechos de bits tm uma restrio que no se aplica a outros campos de um registro. Como
os trechos de bits no tm um endereo no sentido usual, a linguagem C no nos permite aplicar
o operador de endereo & para um trecho de bits. Por conta disso, funes como scanf no
podem armazenar valores diretamente em um campo de um registro que um trecho de bits.
claro que podemos usar scanf para ler uma entrada em uma varivel e ento atribui seu
valor a um campo de um registro que um trecho de bits.

Exerccios

48.1 Uma das formas mais simples de criptografar dados usar a operao de OU exclusivo
sobre cada caractere com uma chave secreta. Suponha, por exemplo, que a chave secreta
o caractere &. Supondo que usamos a tabela ASCII, se fizermos um OU exclusivo do
caractere z com a chave, obtemos o caractere \:

00100110 cdigo binrio ASCII para &


XOR 01111010 cdigo binrio ASCII para z
01011100 cdigo binrio ASCII para \
2
1980 o ano em que o mundo comeou, segundo a Microsoft. O registro data a forma como o sistema
operacional MSDOS armazena a data que um arquivo foi criado ou modificado.

FACOM UFMS
48.2 T RECHOS DE BITS EM REGISTROS 323

Para decifrar uma mensagem, aplicamos a mesma idia. Cifrando uma mensagem pre-
viamente cifrada, obtemos a mensagem original. Se executamos um OU exclusivo entre o
caractere & e o caractere \, por exemplo, obtemos o caractere original z:

00100110 cdigo binrio ASCII para &


XOR 01011100 cdigo binrio ASCII para \
01111010 cdigo binrio ASCII para z

Escreva um programa que receba uma cadeia de caracteres e cifre essa mensagem usando
a tcnica previamente descrita.

Programa 48.2: Soluo do exerccio 48.1.

1 #include <ctype.h>
2 #include <stdio.h>
3 #define CHAVE &
4 int main(void)
5 {
6 int c_orig, c_novo;
7 c_orig = getchar();
8 while (c_orig != EOF) {
9 c_novo = c_orig ^ CHAVE;
10 if (isprint(c_orig) && isprint(c_novo))
11 putchar(c_novo);
12 else
13 putchar(c_orig);
14 c_orig = getchar();
15 }
16 return 0;
17 }

A mensagem original pode ser digitada pelo(a) usurio ou lida a partir de um arquivo
com redirecionamento de entrada. A mensagem cifrada pode ser vista na sada padro
(monitor) ou pode ser armazenada em um arquivo usando redirecionamento de sada.
Suponha que o programa acima tenha sido salvo como cript.c e que um executvel
tenha sido gerado com nome cript . Ento, para cifrar uma mensagem (texto) digitada
em um arquivo msg e armazenar o resultado no arquivo novo , podemos fazer:

prompt$ ./cript < msg > novo

Para recuperar a mensagem original e visualiz-la no monitor, podemos fazer:

prompt$ ./cript < novo

FACOM UFMS
A ULA 49

I NTRODUO AOS APONTADORES

Apontadores ou ponteiros so certamente uma das caractersticas mais destacveis da lin-


guagem de programao C. Os apontadores agregam poder e flexibilidade linguagem de
maneira a diferenci-la de outras linguagens de programao de alto nvel, permitindo a repre-
sentao de estruturas de dados complexas, modificao de valores passados como argumen-
tos a funes, alocao dinmica de espaos na memria, entre outros destaques. Nesta aula
iniciaremos o contato com esses elementos.

49.1 Variveis apontadoras

Para bem compreender os apontadores precisamos inicialmente compreender a idia de


indireo. Conceitualmente, um apontador permite acesso indireto a um valor armazenado
em algum ponto da memria. Esse acesso realizado justamente porque um apontador
uma varivel que armazena um valor especial, que um endereo de memria, e por isso nos
permite acessar indiretamente o valor armazenado nesse endereo.
A memria de um computador pode ser vista como constituda de muitas posies (ou com-
partimentos ou clulas), dispostas continuamente, cada qual podendo armazenar um valor, na
base binria. Ou seja, a memria nada mais do que um grande vetor que pode armazenar
valores na base binria e que, por sua vez, esse valores podem ser interpretados como valores
de diversos tipos. Os ndices desse vetor, numerados seqencialmente a partir de 0 (zero), so
chamados de endereos de memria. Quando escrevemos um programa em uma linguagem
de programao de alto nvel, o nome ou identificador de uma varivel associado a um n-
dice desse vetor, isto , a um endereo da memria. A traduo do nome da varivel para um
endereo de memria, e vice-versa, feita de forma automtica e transparente pelo compilador
da linguagem de programao.
Em geral, a memria de um computador dividida em bytes, com cada byte sendo capaz
de armazenar 8 bits de informao. Cada byte tem um nico endereo que o distingue de
outros bytes da memria. Se existem n bytes na memria, podemos pensar nos endereos
como nmeros de intervalo de 0 a n 1, como mostra a figura 49.1.
Um programa executvel constitudo por trechos de cdigo e dados, ou seja, por instru-
es de mquina que correspondem s sentenas no programa original na linguagem C e de
variveis no programa original. Cada varivel do programa ocupa um ou mais bytes na me-
mria. O endereo do primeiro byte de uma varivel dito ser o endereo da varivel. Na
figura 49.2, a varivel i ocupa os bytes dos endereos 2000 e 2001. Logo, o endereo da vari-
vel i 2000.

324
49.1 VARIVEIS APONTADORAS 325

endereo contedo

0 00010011

1 11010101

2 00111000

3 10010010

n1 00001111

Figura 49.1: Uma ilustrao da memria e de seus endereos.

2000
i
2001

Figura 49.2: Endereo da varivel i .

Os apontadores tm um papel importante em toda essa histria. Apesar de os endereos


serem representados por nmeros, como ilustrado nas figuras 49.1 e 49.2, o intervalo de valores
que esse objetos podem assumir diferente do intervalo que os nmeros inteiros, por exemplo,
podem assumir. Isso significa, entre outras coisas, que no podemos armazenar endereos em
variveis do tipo inteiro. Endereos so armazenados em variveis especiais, chamadas de
variveis apontadoras.
Quando armazenamos o endereo de uma varivel i em uma varivel apontadora p ,
dizemos que p aponta para i . Em outras palavras, um apontador nada mais que um
endereo e uma varivel apontadora uma varivel que pode armazenar endereos.
Ao invs de mostrar endereos como nmeros, usaremos uma notao simplificada de tal
forma que, para indicar que uma varivel apontadora p armazena o endereo de uma varivel
i , mostraremos o contedo de p um endereo como uma flecha orientada na direo de
i , como mostra a figura 49.3.

p i

Figura 49.3: Representao de uma varivel apontadora.

FACOM UFMS
49.2 O PERADORES DE ENDEREAMENTO E DE INDIREO 326

Uma varivel apontadora pode ser declarada da mesma forma que uma varivel qualquer,
mas com um asterisco precedendo seu identificador. Por exemplo,

int *p;

Essa declarao indica que p uma varivel apontadora capaz de apontar para objetos do
tipo int . A linguagem C obriga que toda varivel apontadora aponte apenas para objetos de
um tipo particular, chamado de tipo referenciado.
Diante do exposto at este ponto, daqui por diante no mais distinguiremos os termos apon-
tador e varivel apontadora, ficando ento subentendido valor (contedo) e varivel.

49.2 Operadores de endereamento e de indireo

A linguagem C possui dois operadores para uso especfico com apontadores. Para obter
o endereo de uma varivel, usamos o operador de endereamento (ou de endereo) & . Se
v uma varivel, ento &v seu endereo na memria. Para ter acesso ao objeto que um
apontador aponta, temos de usar o operador de indireo . Se p um apontador, ento *p
representa o objeto para o qual p aponta no momento.
Declarar uma varivel apontadora reserva um espao na memria para um apontador mas
no a faz apontar para um objeto. crucial inicializar p antes de us-lo. Uma forma de
inicializar um apontador atribuir-lhe o endereo de alguma varivel usando o operador & :

int i, *p;
p = &i;

Atribuir o endereo da varivel i para a varivel p faz com que p aponte para i , como
ilustra-a figura 49.4.

p ? i

Figura 49.4: Varivel apontadora p contendo o endereo da varivel i .

possvel inicializar uma varivel apontadora no momento de sua declarao, como


abaixo:

int i, *p = &i;

FACOM UFMS
49.2 O PERADORES DE ENDEREAMENTO E DE INDIREO 327

Uma vez que uma varivel apontadora aponta para um objeto, podemos usar o operador
de indireo * para acessar o valor armazenado no objeto. Se p aponta para i , por exemplo,
podemos imprimir o valor de i como segue:

printf("%d\n", *p);

Observe que a funo printf mostrar o valor de i e no o seu endereo. Observe tambm
que aplicar o operador & a uma varivel produz um apontador para a varivel e aplicar o
operador * para um apontador retoma o valor original da varivel:

j = *&i;

Na verdade, a atribuio acima idntica seguinte:

j = i;

Enquanto dizemos que p aponta para i , dizemos tambm que *p um apelido para i .
No apenas *p tem o mesmo valor que i , mas alterar o valor de *p altera o valor de i .
Uma observao importante que auxilia a escrever e ler programas com apontadores sem-
pre traduzir os operadores unrios de endereo & e indireo * para endereo da varivel e con-
tedo da varivel apontada por, respectivamente. Sempre que usamos um desses operadores no
sentido de estabelecer indireo e apontar para valores, importante traduzi-los desta forma.

Programa 49.1: Um exemplo do uso de apontadores.

1 #include <stdio.h>
2 int main(void)
3 {
4 char c, *p;
5 p = &c;
6 c = a;
7 printf("c=%c, *p=%c\n", c, *p);
8 c = /;
9 printf("c=%c, *p=%c\n", c, *p);
10 *p = Z;
11 printf("c=%c, *p=%c\n", c, *p);
12 return 0;
13 }

FACOM UFMS
49.2 O PERADORES DE ENDEREAMENTO E DE INDIREO 328

Observe que no programa 49.1, aps a declarao das variveis c e p , temos a iniciali-
zao do apontador p , que recebe o endereo da varivel c , sendo essas duas variveis do
mesmo tipo char . importante sempre destacar que o valor, ou contedo, de um apontador
na linguagem C no tem significado at que contenha, ou aponte, para algum endereo vlido.
A primeira chamada da funo printf no programa 49.1 apenas mostra o contedo da
varivel c , que foi inicializada com o caractere a , e tambm o contedo da varivel apon-
tada por p . Como a varivel p aponta para a varivel c , o valor apresentado na sada
tambm aquele armazenado na varivel c , isto , o caractere a . A segunda chamada da
funo printf precedida pela alterao do contedo da varivel c e, como a varivel p
mantm-se apontando para a varivel c , a chamada da funo printf faz com que o ca-
ractere / seja apresentado na sada. importante notar que, a menos que o contedo da
varivel p seja modificado, a expresso *p sempre acessa o contedo da varivel c . Por fim,
a ltima chamada funo printf precedida de uma atribuio que modifica o contedo
da varivel apontada por p , isto , a atribuio a seguir:

*p = Z;

faz tambm com que a varivel c receba o caractere Z .


A linguagem C permite ainda que o operador de atribuio copie apontadores, supondo
que possuam o mesmo tipo. Suponha que a seguinte declarao tenha sido feita:

int i, j, *p, *q;

A sentena

p = &i;

um exemplo de atribuio de um apontador, onde o endereo de i copiado em p . Um


outro exemplo de atribuio de apontador dado a seguir:

q = p;

Essa sentena copia o contedo de p , o endereo de i , para q , fazendo com que q aponte
para o mesmo lugar que p aponta, como podemos visualizar na figura 49.5.
Ambos os apontadores p e q apontam para i e, assim, podemos modificar o contedo
de i indiretamente atravs da atribuio de valores para *p e *q .

FACOM UFMS
49.3 A PONTADORES EM EXPRESSES 329

? i

Figura 49.5: Dois apontadores para um mesmo endereo.

49.3 Apontadores em expresses

Apontadores podem ser usados em expresses aritmticas de mesmo tipo que seus tipos
referenciados. Por exemplo, um apontador para uma varivel do tipo inteiro pode ser usado
em uma expresso aritmtica envolvendo nmeros do tipo inteiro, supondo o uso correto do
operador de indireo * .
importante observar tambm que os operadores & e * , por serem operadores unrios,
tm precedncia sobre os operadores binrios das expresses aritmticas em que se envolvem.
Por exemplo, em uma expresso aritmtica envolvendo nmeros do tipo inteiro, os operadores
binrios + , - , * e / tm menor prioridade que o operador unrio de indireo * .
Vejamos a seguir, como um exemplo simples, o programa 49.2 que usa um apontador como
operando em uma expresso aritmtica.

Programa 49.2: Outro exemplo do uso de apontadores.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j, *apt1, *apt2;
5 apt1 = &i;
6 i = 5;
7 j = 2 * *apt1 + 3;
8 apt2 = apt1;
9 printf("i=%d, j=%d, *apt1=%d, *apt2=%d\n", i, j, *apt1, *apt2);
10 return 0;
11 }

Exerccios

49.1 Se i uma varivel e p uma varivel apontadora que aponta para i , quais das
seguintes expresses so apelidos para i ?

(a) *p
(b) &p

FACOM UFMS
49.3 A PONTADORES EM EXPRESSES 330

(c) *&p
(d) &*p
(e) *i
(f) &i
(g) *&i
(h) &*i
49.2 Se i uma varivel do tipo int e p e q so apontadores para int , quais das seguin-
tes atribuies so corretas?
(a) p = i;
(b) *p = &i;
(c) &p = q;
(d) p = &q;
(e) p = *&q;
(f) p = q;
(g) p = *q;
(h) *p = q;
(i) *p = *q;
49.3 Entenda o que o programa 49.3 faz, simulando sua execuo passo a passo. Depois disso,
implemente-o.

Programa 49.3: Programa do exerccio 49.3.

1 #include <stdio.h>
2 int main(void)
3 {
4 int a, b, *apt1, *apt2;
5 apt1 = &a;
6 apt2 = &b;
7 a = 1;
8 (*apt1)++;
9 b = a + *apt1;
10 *apt2 = *apt1 * *apt2;
11 printf("a=%d, b=%d, *apt1=%d, *apt2=%d\n", a, b, *apt1, *apt2);
12 return 0;
13 }

49.4 Entenda o que o programa 49.4 faz, simulando sua execuo passo a passo. Depois disso,
implemente-o.
49.5 Entenda o que o programa 49.5 faz, simulando sua execuo passo a passo. Depois disso,
implemente-o.

FACOM UFMS
49.3 A PONTADORES EM EXPRESSES 331

Programa 49.4: Programa do exerccio 49.4.

1 #include <stdio.h>
2 int main(void)
3 {
4 int a, b, c, *apt;
5 a = 3;
6 b = 7;
7 printf("a=%d, b=%d\n", a, b);
8 apt = &a;
9 c = *apt;
10 apt = &b;
11 a = *apt;
12 apt = &c;
13 b = *apt;
14 printf("a=%d, b=%d\n", a, b);
15 return 0;
16 }

Programa 49.5: Programa do exerccio 49.5.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i, j, *p, *q;
5 p = &i;
6 q = p;
7 *p = 1;
8 printf("i=%d, *p=%d, *q=%d\n", i, *p, *q);
9 q = &j;
10 i = 6;
11 *q = *p;
12 printf("i=%d, j=%d, *p=%d, *q=%d\n", i, j, *p, *q);
13 return 0;
14 }

FACOM UFMS
A ULA 50

A PONTADORES E FUNES

Nesta aula revemos apontadores e funes na linguagem C. At o momento, aprendemos


algumas regras de como construir funes que tm parmetros de entrada e sada ou argu-
mentos passados por referncia. Esses argumentos/parmetros, como veremos daqui por di-
ante, so na verdade apontadores. Um endereo de uma varivel passado como argumento
para uma funo. O parmetro correspondente que recebe o endereo ento um apontador.
Qualquer alterao realizada no contedo do parmetro tem reflexos externos funo, no
argumento correspondente.

50.1 Parmetros de entrada e sada?

A abstrao que fizemos antes para compreendermos o que so parmetros de entrada


e sada na linguagem C ser revista agora e revelar algumas novidades. Vamos tomar um
exemplo, que de alguma forma j vimos antes, no programa 50.1.

Programa 50.1: Exemplo de parmetros de entrada e sada e apontadores.

1 #include <stdio.h>
2 void troca(int *a, int *b)
3 {
4 int aux;
5 aux = *a;
6 *a = *b;
7 *b = aux;
8 }
9 int main(void)
10 {
11 int x, y;
12 printf("Informe dois valores: ");
13 scanf("%d%d", &x, &y);
14 printf("Antes da troca : x = %d e y = %d\n", x, y);
15 troca(&x, &y);
16 printf("Depois da troca: x = %d e y = %d\n", x, y);
17 return 0;
18 }

332
50.1 PARMETROS DE ENTRADA E SADA ? 333

Agora que entendemos os conceitos bsicos que envolvem os apontadores, podemos olhar
o programa 50.1 e compreender o que est acontecendo, especialmente no que se refere ao
uso de apontadores como argumentos de funes. Suponha que algum est executando esse
programa. A execuo inicia na linha 11 da funo main e seu efeito ilustrado na figura 50.1.

? x y ?

Figura 50.1: Execuo da linha 11.

Em seguida, uma mensagem emitida na sada aps a execuo da linha 12. Depois disso,
na linha 13, suponha que o(a) usurio(a) do programa informe dois valores quaisquer do tipo
inteiro como, por exemplo, 3 e 8. Isso se reflete na memria como na figura 50.2.

3 x y 8

Figura 50.2: Execuo da linha 13.

Na linha 14 da funo main , a execuo da funo printf permite que o(a) usurio(a) ve-
rifique na sada os contedos das variveis que acabou de informar. Na linha 15 do programa,
a funo troca chamada com os endereos das variveis x e y como argumentos. Isto
, esses endereos so copiados nos argumentos correspondentes que compem a interface da
funo. O fluxo de execuo do programa ento desviado para o trecho de cdigo da funo
troca . Os parmetros da funo troca so dois apontadores para valores do tipo inteiro
com identificadores a e b . Esses dois parmetros recebem os endereos das variveis x e y
da funo main , respectivamente, como se pode observar na figura 50.3.

a 3 x y 8 b

Figura 50.3: Execuo da linha 15.

A seguir, na linha 4 do programa, a primeira linha do corpo da funo troca , ocorre a


declarao da varivel aux e um espao identificado por aux reservado na memria, como
vemos na figura 50.4.

a 3 x y 8 b

aux ?

Figura 50.4: Execuo da linha 4.

FACOM UFMS
50.1 PARMETROS DE ENTRADA E SADA ? 334

Depois da declarao da varivel aux , a execuo da linha 5 faz com que o contedo da
varivel apontada por a seja armazenado na varivel aux . Veja a figura 50.5.

a 3 x y 8 b

aux 3

Figura 50.5: Execuo da linha 5.

Na execuo da linha 6, o contedo da varivel apontada por a recebe o contedo da


varivel apontada por b , como mostra a figura 50.6.

a 8 x y 8 b

aux 3

Figura 50.6: Execuo da linha 6.

Por fim, na execuo da linha 7, o contedo da varivel apontada por b recebe o contedo
da varivel aux , conforme a figura 50.7.

a 8 x y 3 b

aux 3

Figura 50.7: Execuo da linha 7.

Aps o trmino da funo troca , seus parmetros e variveis locais so destrudos e o


fluxo de execuo volta para a funo main , no ponto logo aps onde foi feita a chamada da
funo troca , isto , na linha 16. A memria neste momento encontra-se no estado ilustrado
na figura 50.8.
Na linha 17 da funo main a chamada da funo printf , que mostra os contedo das
variveis x e y , que foram trocados, conforme j constatado e ilustrado na figura 50.8. O
programa ento salta para a prxima linha e chega ao fim de sua execuo.
Esse exemplo destaca que so realizadas cpias do valores dos argumentos que nesse

FACOM UFMS
50.2 D EVOLUO DE APONTADORES 335

8 x y 3

Figura 50.8: Estado da memria aps o trmino da funo troca.

caso so endereos das variveis da funo main para os parmetros respectivos da funo
troca . No corpo dessa funo, sempre que usamos o operador de indireo para acessar
algum valor, estamos na verdade acessando o contedo da varivel correspondente dentro da
funo main , que chamou a funo troca . Isso ocorre com as variveis x e y da funo
main , quando copiamos seus endereos nos parmetros a e b da funo troca .
Cpia? Como assim cpia? Ns aprendemos que parmetros passados desta mesma forma
so parmetros de entrada e sada, ou seja, so parmetros passados por referncia e no por
cpia. Esse exemplo mostra uma caracterstica muito importante da linguagem C, que ficou
dissimulada nas aulas anteriores: s h passagem de argumentos por cpia na linguagem C,
ou ainda, no h passagem de argumentos por referncia na linguagem C. O que fazemos de
fato simular a passagem de um argumento por referncia usando apontadores. Assim, pas-
sando (por cpia) o endereo de uma varivel como argumento para uma funo, o parmetro
correspondente deve ser um apontador e, mais que isso, um apontador para a varivel cor-
respondente cujo endereo foi passado como argumento. Dessa forma, qualquer modificao
indireta realizada no corpo dessa funo usando esse apontador ser realizada na verdade no
contedo da varivel apontada pelo parmetro, que simplesmente o contedo da varivel
passada como argumento na chamada da funo.
No h nada de errado com o que aprendemos nas aulas anteriores sobre argumentos de
entrada e sada, isto , passagem de argumentos por referncia. No entanto, vale ressaltar que
passagem de argumentos por referncia um tpico conceitual quando falamos da linguagem
de programao C. O correto repetir que s h passagem de argumentos por cpia na lingua-
gem C.

50.2 Devoluo de apontadores

Alm de passar apontadores para funes tambm podemos faz-las devolver apontadores.
Funes como essas so comuns, por exemplo, quando tratamos de cadeias de caracteres.
A funo abaixo recebe dois apontadores para inteiros e devolve um apontador para o
maior dos nmeros inteiros, isto , devolve o endereo onde se encontra o maior dos nmeros.

1 int *max(int *a, int *b)


2 {
3 if (*a > *b)
4 return a;
5 else
6 return b;
7 }

FACOM UFMS
50.2 D EVOLUO DE APONTADORES 336

Quando chamamos a funo max , passamos apontadores para duas variveis do tipo int
e armazenamos o resultado em uma varivel apontadora:

int i, j, *p;
...
p = max(&i, &j);

Na execuo da funo max , temos que *a um apelido para i e *b um apelido para


j . Se i tem valor maior que j , ento max devolve o endereo de i . Caso contrrio, max
devolve o endereo de j . Depois da chamada, p aponta para i ou para j .
No possvel que uma funo devolva o endereo de uma varivel local sua, j que ao
final de sua execuo, essa varivel ser destruda.

Exerccios

50.1 (a) Escreva uma funo com a seguinte interface:

void minmax(int v[], int n, int *max, int *min)

que receba um vetor v com n > 0 nmeros inteiros e devolva um maior e um menor
dos elementos desse vetor.
(b) Escreva um programa que receba n > 0 nmeros inteiros, armazene-os em um vetor
e, usando a funo do item (a), mostre na sada um maior e um menor elemento
desse conjunto.
Simule no papel a execuo de seu programa antes de implement-lo.

50.2 (a) Escreva uma funo com a seguinte interface:

void doisMaiores(int v[], int n, int *p_maior, int *s_maior)

que receba um vetor v com n > 0 nmeros inteiros e devolva um maior e um se-
gundo maior elementos desse vetor.
(b) Escreva um programa que receba n > 0 nmeros inteiros, armazene-os em um vetor
e, usando a funo do item (a), mostre na sada um maior e um segundo maior
elemento desse conjunto.
Simule no papel a execuo de seu programa antes de implement-lo.

FACOM UFMS
50.2 D EVOLUO DE APONTADORES 337

50.3 (a) Escreva uma funo com a seguinte interface:

void somaprod(int a, int b, int *soma, int *prod)

que receba dois nmeros inteiros a e b e devolva a soma e o produto destes dois
nmeros.
(b) Escreva um programa que receba n nmeros inteiros, com n > 0 par, calcule a soma
e o produto deste conjunto usando a funo do item (a) e determine quantos deles
so maiores que esta soma e quantos so maiores que o produto. Observe que os
nmeros na entrada podem ser negativos.
Simule no papel a execuo de seu programa antes de implement-lo.

50.4 (a) Escreva uma funo com a seguinte interface:

int *maximo(int v[], int n)

que receba um vetor v de n nmeros inteiros e devolva o endereo do elemento de v


onde reside um maior elemento de v.
(b) Escreva um programa que receba n > 0 nmeros inteiros, armazene-os em um vetor
e, usando a funo do item (a), mostre na sada um maior elemento desse conjunto.
Simule no papel a execuo de seu programa antes de implement-lo.

FACOM UFMS
A ULA 51

A PONTADORES E VETORES

Nas aulas 49 e 50 aprendemos o que so os apontadores e tambm como so usados como


parmetros de funes e devolvidos de funes. Nesta aula veremos outra aplicao para os
apontadores. A linguagem C nos permite usar expresses aritmticas de adio e subtrao
com apontadores que apontam para elementos de vetores. Essa uma forma alternativa de
trabalhar com vetores e seus ndices. Para nos tornarmos melhores programadores da lingua-
gem C necessrio conhecer bem essa relao ntima entre apontadores e vetores. Alm disso,
o uso de apontadores para trabalhar com vetores vantajoso em termos de eficincia do pro-
grama executvel resultante.

51.1 Aritmtica com apontadores

Nas aulas 49 e 50 vimos que apontadores podem apontar para elementos de um vetor.
Suponha, por exemplo, que temos declaradas as seguintes variveis:

int v[10], *p;

Podemos fazer o apontador p apontar para o elemento v[0] do vetor fazendo a seguinte
atribuio, como mostra a figura 51.1:

p = &v[0];

0 1 2 3 4 5 6 7 8 9

Figura 51.1: p = &v[0];

338
51.1 A RITMTICA COM APONTADORES 339

Podemos acessar v[0] atravs de p , como ilustrado na figura 51.2.

v 5

0 1 2 3 4 5 6 7 8 9

Figura 51.2: *p = 5;

Podemos ainda executar aritmtica com apontadores ou aritmtica com endereos sobre
p e assim acessamos outros elementos do vetor v . A linguagem C possibilita trs formas de
aritmtica com apontadores: (i) adicionar um nmero inteiro a um apontador; (ii) subtrair um
nmero inteiro de um apontador; e (iii) subtrair um apontador de outro apontador.
Vamos olhar para cada uma dessas operaes. Suponha que temos declaradas as seguintes
variveis:

int v[10], *p, *q, i;

Adicionar um inteiro j a um apontador p fornece um apontador para o elemento posi-


cionado j posies aps p . Mais precisamente, se p aponta para o elemento v[i] , ento
p+j aponta para v[i+j] . A figura 51.3 ilustra essa idia.

Do mesmo modo, se p aponta para o elemento v[i] , ento p-j aponta para v[i-j] ,
como ilustrado na figura 51.4.
Ainda, quando um apontador subtrado de outro, o resultado a distncia, medida em
elementos do vetor, entre os apontadores. Dessa forma, se p aponta para v[i] e q aponta
para v[j] , ento p q igual a i j . A figura 51.5 ilustra essa situao.
Podemos comparar variveis apontadoras entre si usando os operadores relacionais usuais
( < , <= , > , >= , == e != ). Usar os operadores relacionais para comparar dois apontadores
que apontam para um mesmo vetor uma tima idia. O resultado da comparao depende
das posies relativas dos dois elementos do vetor. Por exemplo, depois das atribuies dadas
a seguir:

p = &v[5];
q = &v[1];

o resultado da comparao p <= q falso e o resultado de p >= q verdadeiro.

FACOM UFMS
51.1 A RITMTICA COM APONTADORES 340

0 1 2 3 4 5 6 7 8 9

p q

0 1 2 3 4 5 6 7 8 9

q p

0 1 2 3 4 5 6 7 8 9

Figura 51.3: (a) p = &v[2]; (b) q = p + 3; (c) p = p + 6;

FACOM UFMS
51.1 A RITMTICA COM APONTADORES 341

0 1 2 3 4 5 6 7 8 9

q p

0 1 2 3 4 5 6 7 8 9

p q

0 1 2 3 4 5 6 7 8 9

Figura 51.4: (a) p = &v[8]; (b) q = p - 3; (c) p = p - 6;

FACOM UFMS
51.2 U SO DE APONTADORES PARA PROCESSAMENTO DE VETORES 342

q p

0 1 2 3 4 5 6 7 8 9

Figura 51.5: p = &v[5]; e q = &v[1]; A expresso p - q tem valor 4 e a expresso


q - p tem valor 4.

51.2 Uso de apontadores para processamento de vetores

Usando aritmtica de apontadores podemos visitar os elementos de um vetor atravs da


atribuio de um apontador para seu incio e do seu incremento em cada passo, como mostrado
no trecho de cdigo abaixo:

#define DIM 100


...
int v[DIM], soma, *p;
...
soma = 0;
for (p = &v[0]; p < &v[DIM]; p++)
soma = soma + *p;

A condio p <&v[DIM] na estrutura de repetio for necessita de ateno especial. Ape-


sar de estranho, possvel aplicar o operador de endereo para v[DIM] , mesmo sabendo que
este elemento no existe no vetor v . Usar v[DIM] dessa maneira perfeitamente seguro, j
que a sentena for no tenta examinar o seu valor. O corpo da estrutura de repetio for
ser executado com p igual a &v[0] , &v[1] , . . ., &v[DIM-1] , mas quando p igual a
&v[DIM] a estrutura de repetio termina.
Como j vimos, podemos tambm combinar o operador de indireo * com operadores
de incremento ++ ou decremento -- em sentenas que processam elementos de um vetor.
Considere inicialmente o caso em que queremos armazenar um valor em um vetor e ento
avanar para o prximo elemento. Usando um ndice, podemos fazer diretamente:

v[i++] = j;

Se p est apontando para um elemento de um vetor, a sentena correspondente usando


esse apontador seria:

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 343

*p++ = j;

Devido precedncia do operador ++ sobre o operador * , o compilador enxerga essa


sentena como

*(p++) = j;

O valor da expresso *p++ o valor de *p , antes do incremento. Depois que esse valor
devolvido, a sentena incrementa p .
A expresso *p++ no a nica combinao possvel dos operadores * e ++ . Podemos
escrever (*p)++ para incrementar o valor de *p . Nesse caso, o valor devolvido pela ex-
presso tambm *p , antes do incremento. Em seguida, a sentena incrementa *p . Ainda,
podemos escrever *++p ou ainda ++*p . No primeiro caso, incrementa p e o valor da expres-
so *p , depois do incremento. No segundo, incrementa *p e o valor da expresso *p ,
depois do incremento.
O trecho de cdigo acima, que realiza a soma dos elementos do vetor v usando aritm-
tica com apontadores, pode ento ser reescrito como a seguir, usando uma combinao dos
operadores * e ++ .

soma = 0;
p = &v[0];
while (p < &v[DIM])
soma = soma + *p++;

51.3 Uso do identificador de um vetor como apontador

Apontadores e vetores esto intimamente relacionados. Como vimos nas sees anteriores,
usamos aritmtica de apontadores para trabalhar com vetores. Mas essa no a nica relao
entre eles. Outra relao importante entre apontadores e vetores fornecida pela linguagem C
que o identificador de um vetor pode ser usado como um apontador para o primeiro ele-
mento do vetor. Essa relao simplifica a aritmtica com apontadores e estabelece ganho de
versatilidade em ambos, apontadores e vetores.
Por exemplo, suponha que temos o vetor v declarado como abaixo:

int v[10];

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 344

Usando v como um apontador para o primeiro elemento do vetor, podemos modificar o


contedo de v[0] da seguinte forma:

*v = 7;

Podemos tambm modificar o contedo de v[1] atravs do apontador v+1 :

*(v+1) = 12;

Em geral, v+i o mesmo que &v[i] e *(v+i) equivalente a v[i] . Em outras palavras,
ndices de vetores podem ser vistos como uma forma de aritmtica de apontadores.
O fato de que o identificador de um vetor pode servir como um apontador facilita nossa
programao de estruturas de repetio que percorrem vetores. Considere a estrutura de repe-
tio do exemplo dado na seo anterior:

soma = 0;
for (p = &v[0]; p < &v[DIM]; p++)
soma = soma + *p;

Para simplificar essa estrutura de repetio, podemos substituir &v[0] por v e &v[DIM]
por v+DIM , como mostra o trecho de cdigo abaixo:

soma = 0;
for (p = v; p < v+DIM; p++)
soma = soma + *p;

Apesar de podermos usar o identificador de um vetor como um apontador, no possvel


atribuir-lhe um novo valor. A tentativa de faz-lo apontar para qualquer outro lugar um erro,
como mostra o trecho de cdigo abaixo:

while (*v != 0)
v++;

O programa 51.1 mostra um exemplo do uso desses conceitos, realizando a impresso dos
elementos de um vetor na ordem inversa da qual forma lidos.

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 345

Programa 51.1: Imprime os elementos na ordem inversa da de leitura.

1 #include <stdio.h>
2 #define N 10
3 int main(void)
4 {
5 int v[N], *p;
6 printf("Informe %d nmeros: ", N);
7 for (p = v; p < v+N; p++)
8 scanf("%d", p);
9 printf("Em ordem reversa: ");
10 for (p = v+N-1; p >= v; p--)
11 printf(" %d", *p);
12 printf("\n");
13 return 0;
14 }

Outro uso do identificador de um vetor como um apontador quando um vetor um argu-


mento em uma chamada de funo. Nesse caso, o vetor sempre tratado como um apontador.
Considere a seguinte funo que recebe um vetor de n nmeros inteiros e devolve um maior
elemento nesse vetor.

int max(int v[], int n)


{
int i, maior;
maior = v[0];
for (i = 1; i < n; i++)
if (v[i] > maior)
maior = v[i];
return maior;
}

Suponha que chamamos a funo max da seguinte forma:

M = max(U, N);

Essa chamada faz com que o endereo do primeiro compartimento do vetor U seja atri-
budo v . O vetor U no de fato copiado.
Para indicar que no queremos que um parmetro que um vetor no seja modificado,
podemos incluir a palavra reservada const precedendo a sua declarao.

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 346

Quando uma varivel simples passada para uma funo, isto , quando um argumento
de uma funo, seu valor copiado no parmetro correspondente. Ento, qualquer alterao no
parmetro correspondente no afeta a varivel. Em contraste, um vetor usado como um argu-
mento no est protegido contra alteraes, j que no ocorre uma cpia do vetor todo. Desse
modo, o tempo necessrio para passar um vetor a uma funo independe de seu tamanho. No
h perda por passar vetores grandes, j que nenhuma cpia do vetor realizada. Alm disso,
um parmetro que um vetor pode ser declarado como um apontador. Por exemplo, a funo
max descrita acima pode ser declarada como a seguir:

int max(int *v, int n)


{
...
}

Neste caso, declarar v como sendo um apontador equivalente a declarar v como sendo um
vetor. O compilador trata ambas as declaraes como idnticas.
Apesar de a declarao de um parmetro como um vetor ser equivalente declarao do
mesmo parmetro como um apontador, o mesmo no vale para uma varivel. A declarao a
seguir:

int v[10];

faz com que o compilador reserve espao para 10 nmeros inteiros. Por outro lado, a declarao
abaixo:

int *v;

faz o compilador reservar espao para uma varivel apontadora. Nesse ltimo caso, v no
um vetor e tentar us-lo como tal pode causar resultados desastrosos. Por exemplo, a atribui-
o:

*v = 7;

armazena o valor 7 onde v est apontando. Como no sabemos para onde v est apontando,
o resultado da execuo dessa linha de cdigo imprevisvel.
Do mesmo modo, podemos usar uma varivel apontadora, que aponta para uma posio
de um vetor, como um vetor. O trecho de cdigo a seguir ilustra essa afirmao.

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 347

#define DIM 100


...
int v[DIM], soma, *p;
...
soma = 0;
p = v;
for (i = 0; i < DIM; i++)
soma = soma + p[i];

O compilador trata p[i] como *(p+i) , que uma forma possvel de usar aritmtica com
apontadores. Essa possibilidade de uso, que parece um tanto estranha primeira vista, muito
til em alocao dinmica de memria, como veremos em breve.

Exerccios

51.1 Suponha que as seguintes declaraes foram realizadas:

int v[] = {5, 15, 34, 54, 14, 2, 52, 72};


int *p = &v[1], *q = &v[5];

(a) Qual o valor de *(p + 3) ?


(b) Qual o valor de *(q - 3) ?
(c) Qual o valor de q - p) ?
(d) A expresso p < q tem valor verdadeiro ou falso?
(e) A expresso *p < *q tem valor verdadeiro ou falso?

51.2 Qual o contedo do vetor v aps a execuo do seguinte trecho de cdigo?

#define N 10
int v[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = &v[0], *q = &v[N-1], temp;
while (p < q) {
temp = *p;
*p++ = *q;
*q-- = temp;
}

51.3 Suponha que v um vetor e p um apontador. Considere que a atribuio p = v; foi


realizada previamente. Quais das expresses abaixo no so permitidas? Das restantes,
quais tm valor verdadeiro?

FACOM UFMS
51.3 U SO DO IDENTIFICADOR DE UM VETOR COMO APONTADOR 348

(a) p == v[0]
(b) p == &v[0]
(c) *p == v[0]
(d) p[0] == v[0]

51.4 Escreva um programa que leia uma mensagem e a imprima em ordem reversa. Use a
funo getchar para ler caractere por caractere, armazenando-os em um vetor. Pare
quando encontrar um caractere de mudana de linha \n . Faa o programa de forma
a usar um apontador, ao invs de um ndice como um nmero inteiro, para controlar a
posio corrente no vetor.

FACOM UFMS
A ULA 52

A PONTADORES E MATRIZES

Na aula 51, trabalhamos com apontadores e vetores. Nesta aula veremos as relaes entre
apontadores e matrizes. Neste caso, necessrio estender o conceito de indireo (simples)
para indireo dupla. Veremos tambm como estender esse conceito para indireo mltipla e
suas relaes com variveis compostas homogneas multi-dimensionais.

52.1 Apontadores para elementos de uma matriz

Sabemos h muito tempo que a linguagem C armazena matrizes, que so objetos represen-
tados de forma bidimensional, como uma seqncia contnua de compartimentos de memria,
com demarcaes de onde comea e onde termina cada uma de suas linhas. Ou seja, uma ma-
triz armazenada como um vetor na memria, com marcas especiais em determinados pontos
regularmente espaados: os elementos da linha 0 vm primeiro, seguidos pelos elementos da
linha 1, da linha 2, e assim por diante. Uma matriz com n linhas tem a aparncia ilustrada
como na figura 24.2.

linha 0 linha 1 linha n 1

Figura 52.1: Representao da alocao de espao na memria para uma matriz.

Essa representao pode nos ajudar a trabalhar com apontadores e matrizes. Se fazemos
um apontador p apontar para a primeira clula de uma matriz, isto , o elemento na posi-
o (0,0), podemos ento visitar todos os elementos da matriz incrementando o apontador p
repetidamente.
Por exemplo, suponha que queremos inicializar todos os elementos de uma matriz de n-
meros inteiros com 0. Suponha que temos a declarao da seguinte matriz:

int A[LINHAS][COLUNAS];

A forma que aprendemos inicializar uma matriz pode ser aplicada matriz A declarada
acima e ento o seguinte trecho de cdigo realiza a inicializao desejada:

349
52.2 P ROCESSAMENTO DAS LINHAS DE UMA MATRIZ 350

int i, j;
...
for (i = 0; i < LINHAS; i++)
for (j = 0; j < COLUNAS; j++)
A[i][j] = 0;

Mas se vemos a matriz A da forma como armazenada na memria, isto , como um vetor
unidimensional, podemos trocar o par de estruturas de repetio por uma nica estrutura de
repetio:

int *p;
...
for (p = &A[0][0]; p <= &A[LINHAS-1][COLUNAS-1]; p++)
*p = 0;

A estrutura de repetio acima inicia com p apontando para A[0][0] . Os incrementos


sucessivos de p fazem-no apontar para A[0][1] , A[0][2] , e assim por diante. Quando p
atinge A[0][COLUNAS-1] , o ltimo elemento da linha 0, o prximo incremento faz com que
p aponte para A[1][0] , o primeiro elemento da linha 1. O processo continua at que p
ultrapasse o elemento A[LINHAS-1][COLUNAS-1] , o ltimo elemento da matriz.

52.2 Processamento das linhas de uma matriz

Podemos processar uma linha de uma matriz ou seja, percorr-la, visitar os contedos
dos compartimentos, usar seus valores, modific-los, etc tambm usando apontadores. Por
exemplo, para visitar os elementos da linha i de uma matriz A podemos usar um apontador
p apontar para o elemento da linha i e da coluna 0 da matriz A , como mostrado abaixo:

p = &A[i][0];

ou poderamos fazer simplesmente

p = A[i];

j que, para qualquer matriz A , a expresso A[i] um apontador para o primeiro elemento
da linha i .

FACOM UFMS
52.3 P ROCESSAMENTO DAS COLUNAS DE UMA MATRIZ 351

A justificativa para essa afirmao vem da aritmtica com apontadores. Lembre-se que para
um vetor A , a expresso A[i] equivalente a *(A + i) . Assim, &A[i][0] o mesmo que
&(*(A[i] + 0)) , que equivalente a &*A[i] e que, por fim, equivalente a A[i] . No
trecho de cdigo a seguir usamos essa simplificao para inicializar com zeros a linha i da
matriz A :

int A[LINHAS][COLUNAS], *p, i;


...
for (p = A[i]; p < A[i] + COLUNAS; p++)
*p = 0;

Como A[i] um apontador para a linha i da matriz A , podemos passar A[i] para um
funo que espera receber um vetor como argumento. Em outras palavras, um funo que foi
projetada para trabalhar com um vetor tambm pode trabalhar com uma linha de uma matriz.
Dessa forma, a funo max da aula 51 pode ser chamada com a linha i da matriz A como
argumento:

M = max(A[i], COLUNAS);

52.3 Processamento das colunas de uma matriz

O processamento dos elementos de uma coluna de uma matriz no to simples como o


processamento dos elementos de uma linha, j que a matriz armazenada linha por linha na
memria. A seguir, mostramos uma estrutura de repetio que inicializa com zeros a coluna
j da matriz A :

int A[LINHAS][COLUNAS], (*p)[COLUNAS], j;


...
for (p = &A[0]; p < A[LINHAS]; p++)
(*p)[j] = 0;

Nesse exemplo, declaramos p como um apontador para um vetor de dimenso COLUNAS ,


cujos elementos so nmeros inteiros. Os parnteses envolvendo *p so necessrios, j que
sem eles o compilador trataria p como um vetor de apontadores em vez de um apontador
para um vetor. A expresso p++ avana p para a prxima linha. Na expresso (*p)[j] ,
*p representa uma linha inteira de A e assim (*p)[j] seleciona o elemento na coluna j
da linha. Os parnteses so essenciais na expresso (*p)[i] , j que, sem eles, o compilador
interpretaria a expresso como *(p[i]) .

FACOM UFMS
52.4 I DENTIFICADORES DE MATRIZES COMO APONTADORES 352

52.4 Identificadores de matrizes como apontadores

Assim como o identificador de um vetor pode ser usado como um apontador, o identifi-
cador de uma matriz tambm pode e, na verdade, de qualquer o identificador de qualquer
varivel composta homognea pode. No entanto, alguns cuidados so necessrios quando ul-
trapassamos a barreira de duas ou mais dimenses.
Considere a declarao da matriz a seguir:

int A[LINHAS][COLUNAS];

Neste caso, A no um apontador para A[0][0] . Ao contrrio, um apontador para A[0] .


Isso faz mais sentido se olharmos sob o ponto de vista da linguagem C, que considera A no
como uma matriz bidimensional, mas como um vetor. Quando usado como um apontador, A
tem tipo int (*)[COLUNAS] , um apontador para um vetor de nmeros inteiros de tamanho
COLUNAS .
Saber que A aponta para A[0] til para simplificar estruturas de repetio que proces-
sam elementos de uma matriz. Por exemplo, ao invs de escrever:

for (p = &A[0]; p < A[LINHAS]; p++)


(*p)[j] = 0;

para inicializar a coluna j da matriz A , podemos escrever

for (p = A; p < A + LINHAS; p++)


(*p)[j] = 0;

Com essa idia, podemos fazer o compilador acreditar que uma varivel composta homo-
gnea multi-dimensional unidimensional, isto , um vetor. Por exemplo, podemos passar a
matriz A como argumento para a funo max da aula 51 da seguinte forma:

M = max(A[0], LINHAS*COLUNAS);

j que A[0 aponta para o elemento na linha 0 e coluna 0 e tem tipo int * e assim essa
chamada ser executada corretamente.

FACOM UFMS
52.4 I DENTIFICADORES DE MATRIZES COMO APONTADORES 353

Exerccios

52.1 Escreva uma funo que preencha uma matriz quadrada de dimenso n com a matriz
identidade In . Use um nico apontador que percorra a matriz.

52.2 Reescreva a funo abaixo usando aritmtica de apontadores em vez de ndices de matri-
zes. Em outras palavras, elimine as variveis i e j e todos os [] . Use tambm uma
nica estrutura de repetio.

int somaMatriz(const int A[DIM][DIM], int n)


{
int i, j, soma = 0;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
soma = soma + A[i][j];
return soma;
}

FACOM UFMS
A ULA 53

A PONTADORES E CADEIAS DE
CARACTERES

Nas aulas 51 e 52 estudamos formas de trabalhar com variveis compostas homogneas


e apontadores para seus elementos. No entanto, importante ainda estudar a relao entre
apontadores e as cadeias de caracteres que, como j vimos, so vetores especiais que contm
caracteres. Nesta aula aprenderemos algumas particularidades de apontadores para elemen-
tos de cadeias de caracteres na linguagem C, alm de estudar a relao entre apontadores e
constantes que so cadeias de caracteres.

53.1 Literais e apontadores

Devemos relembrar que uma literal uma seqncia de caracteres envolvida por aspas
duplas. Um exemplo de uma literal apresentado a seguir1 :

"O usurio mdio de computador possui o crebro de um macaco-aranha"

Nosso primeiro contato com literais foi ainda na aula 4. Literais ocorrem com freqncia
na chamada das funes printf e scanf . Mas quando chamamos uma dessas funes e
fornecemos uma literal como argumento, o que de fato estamos passando? Em essncia, a
linguagem C trata literais como cadeias de caracteres. Quando o compilador encontra uma
literal de comprimento n em um programa, ele reserva um espao de n + 1 bytes na memria.
Essa rea de memria conter os caracteres da literal mais o caracter nulo que indica o final da
cadeia. O caracteres nulo um byte cujos bits so todos zeros e representado pela seqncia
de caracteres \0 . Por exemplo, a literal "abc" armazenada como uma cadeia de quatro
caracteres, como mostra a figura 53.1.

a b c \0

Figura 53.1: Representao de uma literal na memria.

Literais podem ser vazias. A literal "" armazenada como um nico caractere nulo.
1
Frase de Bill Gates, dono da Micro$oft, em uma entrevista para Computer Magazine.

354
53.1 L ITERAIS E APONTADORES 355

Como uma literal armazenada em um vetor, o compilador a enxerga como um aponta-


dor do tipo char * . As funes printf e scanf , por exemplo, esperam um valor do tipo
char * como primeiro argumento. Se, por exemplo, fazemos a seguinte chamada:

printf("abc");

o endereo da literal "abc" passado como argumento para a funo printf , isto , o ende-
reo de onde se encontra o caractere a na memria.
Em geral, podemos usar uma literal sempre que a linguagem C permita o uso de um aponta-
dor do tipo char * . Por exemplo, uma literal pode ocorrer do lado direito de uma atribuio,
como mostrado a seguir:

char *p;
p = "abc";

Essa atribuio no copia os caracteres de "abc" , mas faz o apontador p apontar para o
primeiro caractere da literal, como mostra a figura 53.2.

apt

a b c \0

Figura 53.2: Representao da atribuio de uma literal a um apontador.

Observe ainda que no permitido alterar uma literal durante a execuo de um programa.
Isso significa que a tentativa de modificar uma literal pode causar um comportamento indefi-
nido do programa.
Relembrando, uma varivel cadeia de caracteres um vetor do tipo char que necessaria-
mente deve reservar espao para o caractere nulo. Quando declaramos um vetor de caracteres
que ser usado para armazenar cadeias de caracteres, devemos sempre declarar esse vetor com
uma posio a mais que a mais longa das cadeias de caracteres possveis, j que por conven-
o da linguagem C, toda cadeia de caracteres finalizada com um caractere nulo. Veja, por
exemplo, a declarao a seguir:

char cadeia[TAM+1];

FACOM UFMS
53.1 L ITERAIS E APONTADORES 356

onde TAM uma macro definida com o tamanho da cadeia de caracteres mais longa que pode
ser armazenada na varivel cadeia .
Lembrando ainda, podemos inicializar um cadeia de caracteres no momento de sua decla-
rao, como mostra o exemplo abaixo:

char data[13] = "9 de outubro";

O compilador ento coloca os caracteres de "9 de outubro" no vetor data e adiciona


o caractere nulo ao final para que data possa ser usada como uma cadeia de caracteres. A
figura 53.3 ilustra essa situao.

data 9 d e o u t u b r o \0

Figura 53.3: Declarao e inicializao de uma cadeia de caracteres.

Apesar de "9 de outubro" se parecer com uma literal, a linguagem C de fato a v como
uma abreviao para um inicializador de um vetor. Ou seja, a declarao e inicializao acima
enxergada pelo compilador como abaixo:

char data[13] = {9, ,d,e, ,o,u,t,u,b,r,o,\0};

No caso em que o inicializador menor que o tamanho definido para a cadeia de caracteres,
o compilador preencher as posies finais restantes da cadeia com o caractere nulo. Por outro
lado, sempre importante garantir que o inicializador tenha menor comprimento que o tama-
nho do vetor declarado. Tambm, podemos omitir o tamanho do vetor em uma declarao e
inicializao simultneas, caso em que o vetor ter o tamanho equivalente ao comprimento do
inicializador mais uma unidade, que equivale ao caractere nulo.
Agora, vamos comparar a declarao abaixo:

char data[] = "9 de outubro";

que declara uma vetor data , que uma cadeia de caracteres, com a declarao a seguir:

char *data = "9 de outubro";

FACOM UFMS
53.1 L ITERAIS E APONTADORES 357

que declara data como um apontador. Devido relao estrita entre vetores e apontadores,
podemos usar as duas verses da declarao de data como uma cadeia de caractere. Em
particular, qualquer funo que receba um vetor de caracteres ou um apontador para caracteres
aceita qualquer uma das verses da declarao da varivel data apresentada acima.
No entanto, devemos ter cuidado para no cometer o erro de acreditar que as duas verses
da declarao de data so equivalentes e intercambiveis. Existem diferenas significativas
entre as duas, que destacamos abaixo:

na verso em que a varivel declarada como um vetor, os caracteres armazenados em


data podem ser modificados, como fazemos com elementos de qualquer vetor; na ver-
so em que a varivel declarada como um apontador, data aponta para uma literal
que, como j vimos, no pode ser modificada;

na verso com vetor, data um identificador de um vetor; na verso com apontador,


data uma varivel que pode, inclusive, apontar para outras cadeias de caracteres du-
rante a execuo do programa.

Se precisamos que uma cadeia de caracteres seja modificada, nossa responsabilidade de-
clarar um vetor de caracteres no qual ser armazenado essa cadeia. Declarar um apontador
no suficiente, neste caso. Por exemplo, a declarao abaixo:

char *p;

faz com que o compilador reserve espao suficiente para uma varivel apontadora. Infeliz-
mente, o compilador no reserva espao para uma cadeia de caracteres, mesmo porque, no h
indicao alguma de um possvel comprimento da cadeia de caracteres que queremos armaze-
nar. Antes de usarmos p como uma cadeia de caracteres, temos de faz-la apontar para um
vetor de caracteres. Uma possibilidade fazer p apontar para uma varivel que uma cadeia
de caracteres, como mostramos a seguir:

char cadeia[TAM+1], *p;


p = cadeia;

Com essa atribuio, p aponta para o primeiro caractere de cadeia e assim podemos usar
p como uma cadeia de caracteres. Outra possibilidade fazer p apontar para uma cadeia de
caracteres dinamicamente alocada, como veremos na aula 55.
Ainda poderamos discorrer sobre processos para leitura e escrita de cadeias de caracteres,
sobre acesso aos caracteres de uma cadeia de caracteres e tambm sobre o uso das funes da
biblioteca da linguagem C que trabalham especificamente com cadeias de caracteres, o que j
fizemos nas aulas 23 e 45. Ainda veremos a seguir dois tpicos importantes sobre cadeias de
caracteres: vetores de cadeias de caracteres e argumentos de linha de comando.

FACOM UFMS
53.2 V ETORES DE CADEIAS DE CARACTERES 358

53.2 Vetores de cadeias de caracteres

Uma forma de armazenar em memria um vetor de cadeias de caracteres atravs da cri-


ao de uma matriz de caracteres e ento armazenar as cadeias de caracteres uma a uma. Por
exemplo, podemos fazer como a seguir:

char planetas[][9] = {"Mercurio", "Venus", "Terra",


"Marte", "Jupiter", "Saturno",
"Urano", "Netuno", "Plutao"};

Observe que estamos omitindo o nmero de linhas da matriz, que fornecido pelo inicializa-
dor, mas a linguagem C exige que o nmero de colunas seja especificado, conforme fizemos na
declarao.
A figura 53.4 ilustra a declarao e inicializao da varivel planetas . Observe que to-
das as cadeias cabem nas colunas da matriz e, tambm, que h um tanto de compartimentos
desperdiados na matriz, preenchidos com o caractere \0 , j que nem todas as cadeias so
compostas por 8 caracteres.

0 1 2 3 4 5 6 7 8

0 M e r c u r i o \0

1 V e n u s \0 \0 \0 \0

2 T e r r a \0 \0 \0 \0

3 M a r t e \0 \0 \0 \0

4 J u p i t e r \0 \0

5 S a t u r n o \0 \0

6 U r a n o \0 \0 \0 \0

7 N e t u n o \0 \0 \0

8 P l u t a o \0 \0 \0

Figura 53.4: Matriz de cadeias de caracteres planetas .

A ineficincia de armazenamento aparente nesse exemplo comum quando trabalhamos


com cadeias de caracteres, j que colees de cadeias de caracteres sero, em geral, um misto
entre curtas e longas cadeias. Uma possvel forma de sanar esse problema usar um vetor
cujos elementos so apontadores para cadeias de caracteres, como podemos ver na declarao
abaixo:

FACOM UFMS
53.2 V ETORES DE CADEIAS DE CARACTERES 359

char *planetas[] = {"Mercurio", "Venus", "Terra",


"Marte", "Jupiter", "Saturno",
"Urano", "Netuno", "Plutao"};

Note que h poucas diferenas entre essa declarao e a declarao anterior da varivel
planetas : removemos um par de colchetes com um nmero no interior deles e colocamos
um asterisco precedendo o identificador da varivel. No entanto, o efeito dessa declarao na
memria muito diferente, como podemos ver na figura 53.5.

planetas

0 M e r c u r i o \0

1 V e n u s \0

2 T e r r a \0

3 M a r t e \0

4 J u p i t e r \0

5 S a t u r n o \0

6 U r a n o \0

7 N e t u n o \0

8 P l u t a o \0

Figura 53.5: Vetor planetas de apontadores para cadeias de caracteres.

Cada elemento do vetor planetas um apontador para uma cadeia de caracteres, ter-
minada com um caractere nulo. No h mais desperdcio de compartimentos nas cadeias de
caracteres, apesar de termos de alocar espao para os apontadores no vetor planetas . Para
acessar um dos nomes dos planetas necessitamos apenas do ndice do vetor. Para acessar um
caractere do nome de um planeta devemos fazer da mesma forma como acessamos um ele-
mento em uma matriz. Por exemplo, para buscar cadeias de caracteres no vetor planetas
que iniciam com a letra M, podemos usar o seguinte trecho de cdigo:

for (i = 0; i < 9; i++)


if (planetas[i][0] == M)
printf("%s comea com M\n", planetas[i]);

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 360

53.3 Argumentos na linha de comandos

Quando executamos um programa, em geral, devemos fornecer a ele alguma informao


como por exemplo um nome de um arquivo, uma opo que modifica seu comportamento, etc.
Por exemplo, considere o comando UNIX ls . Se executamos esse comando como abaixo:

prompt$ ls

em uma linha de comando, o resultado ser uma listagem de nomes dos arquivos no diretrio
atual. Se digitamos o comando seguido de uma opo, como abaixo:

prompt$ ls -l

ento o resultado uma listagem detalhada2 que nos mostra o tamanho de cada arquivo, seu
proprietrio, a data e hora em que houve a ltima modificao no arquivo e assim por diante.
Para modificar ainda mais o comportamento do comando ls podemos especificar que ele
mostre detalhes de apenas um arquivo, como mostrado abaixo:

prompt$ ls -l exerc1.c

Nesse caso, o comando ls mostrar informaes detalhadas sobre o arquivo exerc1.c .


Informaes de comando de linha esto disponveis para todos os programas, no ape-
nas para comandos do sistema operacional. Para ter acesso aos argumentos de linha de co-
mando, chamados de parmetros do programa na linguagem C padro, devemos definir a
funo main como uma funo com dois parmetros que costumeiramente tm identificado-
res argc e argv . Isto , devemos fazer como abaixo:

int main(int argc, char *argv[])


{
...
}

O parmetro argc , abreviao de contador de argumentos, o nmero de argumentos


de linha de comando, incluindo tambm o nome do programa. O parmetro argv , abrevi-
ao de vetor de argumentos, um vetor de apontadores para os argumentos da linha de
2
l do ingls long.

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 361

comando, que so armazenados como cadeias de caracteres. Assim, argv[0] aponta para o
nome do programa, enquanto que argv[1] at argv[argc-1] apontam para os argumentos
da linha de comandos restantes. O vetor argv tem um elemento adicional argv[argc] que
sempre um apontador nulo, um apontador especial que aponta para nada, representado pela
macro NULL .
Se um(a) usurio(a) digita a linha de comando abaixo:

prompt$ ls -l exerc1.c

ento argc conter o valor 3, argv[0] apontar para a cadeia de caracteres com o nome
do programa, argv[1] apontar para a cadeia de caracteres "-l" , argv[2] apontar para
a cadeia de caracteres exerc1.c e argv[3] apontar para nulo. A figura 53.6 ilustra essa
situao. Observe que o nome do programa no foi listado porque pode incluir o nome do
diretrio ao qual o programa pertence ou ainda outras informaes que dependem do sistema
operacional.
argv

1 l \0

2 e x e r c 1 . c \0

Figura 53.6: Representao de argv .

Como argv um vetor de apontadores, o acesso aos argumentos da linha de comandos


realizado, em geral, como mostrado na estrutura de repetio a seguir:

int i;
for (i = 1; i < argc; i++)
printf("%s\n", argv[i]);

O programa 53.1 ilustra como acessar os argumentos de uma linha de comandos. O pro-
grama projetado para verificar se as cadeias de caracteres fornecidas na linha de comandos
so nomes de planetas do sistema solar.
Se o programa 53.1 tem o nome planetas.c e seu executvel correspondente tem nome
planetas , ento podemos executar esse programa com uma seqncia de cadeias de caracte-
res, como mostramos no exemplo abaixo:

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 362

Programa 53.1: Verifica nomes de planetas.

1 #include <stdio.h>
2 #include <string.h>
3 #define NUM_PLANETAS 9
4 int main(int argc, char *argv[])
5 {
6 char *planetas[] = {"Mercurio", "Venus", "Terra",
7 "Marte", "Jupiter", "Saturno",
8 "Urano", "Netuno", "Plutao"};
9 int i, j, achou;
10 for (i = 1; i < argc; i++) {
11 achou = 0;
12 for (j = 0; j < NUM_PLANETAS && !achou; j++)
13 if (strcmp(argv[i], planetas[j]) == 0)
14 achou = 1;
15 if (achou)
16 printf("%s o planeta %d\n", argv[i], j+1);
17 else
18 printf("%s no um planeta\n", argv[i]);
19 }
20 return 0;
21 }

prompt$ ./planetas Jupiter venus Terra Joaquim

O resultado da execuo dessa execuo dado a seguir:

Jupiter o planeta 5
venus no um planeta
Terra o planeta 3
Joaquim no um planeta

Exerccios

53.1 As chamadas de funes abaixo supostamente escrevem um caractere de mudana de


linha na sada, mas algumas delas esto erradas. Identifique quais chamadas no funcio-
nam e explique o porqu.

(a) printf("%c", \n);

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 363

(b) printf("%c", "\n");


(c) printf("%s", \n);
(d) printf("%s", "\n");
(e) printf(\n);
(f) printf("\n");
(g) putchar(\n);
(h) putchar("\n");

53.2 Suponha que declaramos um apontador p como abaixo:

char *p = "abc";

Quais das chamadas abaixo esto corretas? Mostre a sada produzida por cada chamada
correta e explique por que a(s) outra(s) no est(o) correta(s).

(a) putchar(p);
(b) putchar(*p);
(c) printf("%s", p);
(d) printf("%s", *p);

53.3 Suponha que declaramos as seguintes variveis:

char s[MAX+1];
int i, j;

Suponha tambm que a seguinte chamada foi executada:

scanf("%d%s%d", &i, s, &j);

Se o usurio digita a seguinte entrada:

12abc34 56def78

qual sero os valores de i , j e s depois dessa chamada?

53.4 A funo abaixo supostamente cria uma cpia idntica de uma cadeia de caracteres. O
que h de errado com a funo?

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 364

char *duplica(const char *p)


{
char *q;
strcpy(q, p);
return q;
}

53.5 O que imprime na sada o programa abaixo?

#include <stdio.h>
int main(void)
{
char s[] = "Dvmuvsb", *p;
for (p = s; *p; p++)
--*p;
printf("%s\n", s);
return 0;
}

53.6 (a) Escreva uma funo com a seguinte interface:

void maiuscula(char cadeia[])

que receba uma cadeia de caracteres (terminada com um caractere nulo) contendo
caracteres arbitrrios e substitua os caracteres que so letras minsculas nessa cadeia
por letras maisculas. Use cadeia apenas como vetor, juntamente com os ndices
necessrios.
(b) Escreva uma funo com a seguinte interface:

void maiuscula(char *cadeia)

que receba uma cadeia de caracteres (terminada com um caractere nulo) contendo
caracteres arbitrrios e substitua os caracteres que so letras minsculas nessa cadeia
por letras maisculas. Use apenas apontadores e aritmtica com apontadores.

53.7 (a) Escreva uma funo que receba uma cadeia de caracteres e devolva o nmero total
de caracteres que ela possui.
(b) Escreva uma funo que receba uma cadeia de caracteres e devolva o nmero de
vogais que ela possui.
(c) Escreva uma funo que receba uma cadeia de caracteres e devolva o nmero de
consoantes que ela possui.

FACOM UFMS
53.3 A RGUMENTOS NA LINHA DE COMANDOS 365

(d) Escreva um programa que receba diversas cadeias de caracteres e faa a mdia do
nmero de vogais, de consoantes e de smbolos de pontuao que elas possuem.
Use apenas apontadores nas funes em (a), (b) e (c).
53.8 Escreva um programa que encontra a maior e a menor palavra de uma seqncia de
palavras informadas pelo(a) usurio(a). O programa deve terminar se uma palavra de
quatro letras for fornecida na entrada. Considere que nenhuma palavra tem mais que 20
letras.
Um exemplo de entrada e sada do programa pode ser assim visualizado:

Informe uma palavra: laranja


Informe uma palavra: melao
Informe uma palavra: tomate
Informe uma palavra: cereja
Informe uma palavra: uva
Informe uma palavra: banana
Informe uma palavra: maca

Maior palavra: laranja


Menor Palavra: uva

53.9 Escreva um programa com nome reverso.c que mostra os argumentos da linha de
comandos em ordem inversa. Por exemplo, executando o programa da seguinte forma:

prompt$ ./reverso garfo e faca

deve produzir a seguinte sada:

faca e garfo

53.10 Escreva um programa com nome soma.c que soma todos os argumentos informados
na linha de comandos, considerando que todos eles so nmeros inteiros. Por exemplo,
executando o programa da seguinte forma:

prompt$ ./soma 81 25 2

deve produzir a seguinte sada:

108

FACOM UFMS
A ULA 54

A PONTADORES E REGISTROS

Nesta aula trabalharemos com apontadores e registros. Primeiro, veremos como declarar
e usar apontadores para registros. Essas tarefas so equivalentes as que j fizemos quando
usamos apontadores para nmeros inteiros, por exemplo. Alm disso, vamos adicionar tam-
bm apontadores como campos de registros. Quando registros contm apontadores podemos
us-los como estruturas de dados poderosas tais como listas encadeadas, rvores, etc, como
veremos daqui por diante.

54.1 Apontadores para registros

Suponha que definimos uma etiqueta de registro data como a seguir:

struct data {
int dia;
int mes;
int ano;
};

A partir dessa definio, podemos declarar variveis do tipo struct data , como abaixo:

struct data hoje;

E ento, assim como fizemos com apontadores para inteiros, caracteres e nmeros de ponto
flutuante, podemos declarar um apontador para o registro data da seguinte forma:

struct data *p;

Podemos, a partir dessa declarao, fazer uma atribuio varivel p como a seguir:

366
54.1 A PONTADORES PARA REGISTROS 367

p = &hoje;

Alm disso, podemos atribuir valores aos campos do registro de forma indireta, como faze-
mos abaixo:

(*p).dia = 11;

Essa atribuio tem o efeito de armazenar o nmero inteiro 11 no campo dia da varivel
hoje , indiretamente atravs do apontador p no entanto. Nessa atribuio, os parnteses
envolvendo *p so necessrios porque o operador . , de seleo de campo de um registro,
tem maior prioridade que o operador * de indireo. importante relembrar tambm que
essa forma de acesso indireto aos campos de um registro pode ser substituda, e tem o mesmo
efeito, pelo operador -> como mostramos no exemplo abaixo:

p->dia = 11;

O programa 54.1 ilustra o uso de apontadores para registros.

Programa 54.1: Uso de um apontador para um registro.

1 #include <stdio.h>
2 struct data {
3 int dia;
4 int mes;
5 int ano;
6 };
7 int main(void)
8 {
9 struct data hoje, *p;
10 p = &hoje;
11 p->dia = 13;
12 p->mes = 9;
13 p->ano = 2007;
14 printf("A data de hoje %d/%d/%d\n", hoje.dia, hoje.mes, hoje.ano);
15 return 0;
16 }

Na linha 9 h a declarao de duas variveis: um registro com identificador hoje e um


apontador para registros com identificador p . Na linha 10, p recebe o endereo da varivel

FACOM UFMS
54.2 R EGISTROS CONTENDO APONTADORES 368

hoje . Observe que a varivel hoje do tipo struct data , isto , a varivel hoje do
mesmo tipo da varivel p e, portanto, essa atribuio vlida. Em seguida, valores do tipo
inteiro so armazenados na varivel hoje , mas de forma indireta, com uso do apontador p .
Por fim, os valores atribudos so impressos na sada. A figura 54.1 mostra as variveis hoje
e p depois das atribuies realizadas durante a execuo do programa 54.1.

dia mes ano


p 13 9 2004

hoje

Figura 54.1: Representao do apontador p e do registro hoje .

54.2 Registros contendo apontadores

Podemos tambm usar apontadores como campos de registros. Por exemplo, podemos
definir uma etiqueta de registro como abaixo:

struct reg_apts {
int *apt1;
int *apt2;
};

A partir dessa definio, podemos declarar variveis (registros) do tipo struct reg_apts
como a seguir:

struct reg_apts bloco;

Em seguida, a varivel bloco pode ser usada como sempre fizemos. Note apenas que
bloco no um apontador, mas um registro que contm dois campos que so apontadores.
Veja o programa 54.2, que mostra o uso dessa varivel.
Observe atentamente a diferena entre (*p).dia e *reg.apt1 . No primeiro caso, p
um apontador para um registro e o acesso indireto a um campo do registro, via esse apontador,
tem de ser feito com a sintaxe (*p).dia , isto , o contedo do endereo contido em p
um registro e, portanto, a seleo do campo descrita fora dos parnteses. No segundo caso,
reg um registro e no um apontador para um registro e como contm campos que so
apontadores, o acesso ao contedo dos campos realizado atravs do operador de indireo
* . Assim, *reg.apt1 significa que queremos acessar o contedo do endereo apontado por
reg.apt1 . Como o operador de seleo de campo . de um registro tem prioridade pelo
operador de indireo * , no h necessidade de parnteses, embora pudssemos us-los da
forma *(reg.apt1) . A figura 54.2 ilustra essa situao.

FACOM UFMS
54.2 R EGISTROS CONTENDO APONTADORES 369

Programa 54.2: Uso de um registro que contm campos que so apontadores.

1 #include <stdio.h>
2 struct apts_int {
3 int *apt1;
4 int *apt2;
5 };
6 int main(void)
7 {
8 int i1, i2;
9 struct apts_int reg;
10 i2 = 100;
11 reg.apt1 = &i1;
12 reg.apt2 = &i2;
13 *reg.apt1 = -2;
14 printf("i1 = %d, *reg.apt1 = %d\n", i1, *reg.apt1);
15 printf("i2 = %d, *reg.apt2 = %d\n", i2, *reg.apt2);
16 return 0;
17 }

apt1 apt2

i1 2 100 i2

reg

Figura 54.2: Representao do registro reg contendo dois campos apontadores.

Exerccios

54.1 Qual a sada do programa descrito abaixo?

1 #include <stdio.h>
2 struct dois_valores {
3 int vi;
4 float vf;
5 };
6 int main(void)
7 {
8 struct dois_valores reg1 = {53, 7.112}, reg2, *p = &reg1;
9 reg2.vi = (*p).vf;
10 reg2.vf = (*p).vi;
11 printf("1: %d %f\n2: %d %f\n", reg1.vi, reg1.vf, reg2.vi, reg2.vf);
12 return 0;
13 }

FACOM UFMS
54.2 R EGISTROS CONTENDO APONTADORES 370

54.2 Simule a execuo do programa descrito abaixo.

1 #include <stdio.h>
2 struct apts {
3 char *c;
4 int *i;
5 float *f;
6 };
7 int main(void)
8 {
9 char caractere;
10 int inteiro;
11 float real;
12 struct apts reg;
13 reg.c = &caractere;
14 reg.i = &inteiro;
15 reg.f = &real;
16 scanf("%c%d%f", reg.c, reg.i, reg.f);
17 printf("%c\n%d\n%f\n", caractere, inteiro, real);
18 return 0;
19 }

54.3 Simule a execuo do programa descrito abaixo.

1 #include <stdio.h>
2 struct celula {
3 int valor;
4 struct celula *prox;
5 };
6 int main(void)
7 {
8 struct celula reg1, reg2, *p;
9 scanf("%d%d", &reg1.valor, &reg2.valor);
10 reg1.prox = &reg2;
11 reg2.prox = NULL;
12 for (p = &reg1; p != NULL; p = p->prox)
13 printf("%d ", p->valor);
14 printf("\n");
15 return 0;
16 }

FACOM UFMS
A ULA 55

U SO AVANADO DE APONTADORES

Nas aulas 49 a 54 vimos formas importantes de uso de apontadores: como parmetros de


funes simulando passagem por referncia e como elementos da linguagem C que podem
acessar indiretamente outros compartimentos de memria, seja uma varivel, uma clula de
um vetor ou de uma matriz ou ainda um campo de um registro, usando, inclusive uma aritm-
tica especfica para tanto.
Nesta aula veremos outros usos para apontadores: como auxiliares na alocao dinmica de
espaos de memria, como apontadores para funes e como apontadores para outros aponta-
dores.

55.1 Alocao dinmica de memria

As estruturas de armazenamento de informaes na memria principal da linguagem C


tm, em geral, tamanho fixo. Por exemplo, uma vez que um programa foi compilado, a quan-
tidade de elementos de um vetor ou de uma matriz fixa. Isso significa que, para alterar a
capacidade de armazenamento de uma estrutura de tamanho fixo, necessrio alterar seu ta-
manho no arquivo-fonte e compilar esse programa novamente. Felizmente, a linguagem C
permite alocao dinmica de memria, que a habilidade de reservar espaos na memria
principal durante a execuo de um programa. Usando alocao dinmica, podemos projetar
estruturas de armazenamento que crescem ou diminuem quando necessrio durante a execu-
o do programa. Apesar de disponvel para qualquer tipo de dados, a alocao dinmica
usada em geral com variveis compostas homogneas e heterogneas.
Aprendemos at aqui a declarar uma varivel composta homognea especificando um iden-
tificador e sua(s) dimenso(es). Por exemplo, veja as declaraes a seguir:

int vet[100];
float mat[40][60];

Nesse caso, temos a declarao de um vetor com identificador vet e 100 posies de memria
que podem armazenar nmeros inteiros e uma matriz com identificador mat de 40 linhas e
60 colunas que so compartimentos de memria que podem armazenar nmeros de ponto flu-
tuante. Todos os compartimentos dessas variveis compostas homogneas ficam disponveis
para uso durante a execuo do programa.

371
55.1 A LOCAO DINMICA DE MEMRIA 372

Em diversas aplicaes, para que os dados de entrada sejam armazenados em variveis


compostas homogneas com dimenso(es) adequadas, necessrio saber antes essas dimen-
so(es), o que ento solicitado a um(a) usurio(a) do programa logo de incio. Por exemplo,
o(a) usurio(a) da aplicao pode querer informar a quantidade de elementos que ser arma-
zenada no vetor vet , um valor que ser mantido no programa para verificao do limite de
armazenamento e que no deve ultrapassar o limite mximo de 100 elementos. Note que a
previso de limitante mximo deve sempre ser especificada tambm. Do mesmo modo, o(a)
usurio(a) pode querer informar, antes de usar essa estrutura, quantas linhas e quantas colunas
da matriz mat sero usadas, sem ultrapassar o limite mximo de 40 linhas e 60 colunas. Se o(a)
usurio(a), por exemplo, usar apenas 10 compartimentos do vetor vet ou apenas 3 3 com-
partimentos da matriz mat durante a execuo do programa, os compartimentos restantes no
sero usados, embora tenham sido alocados na memria durante suas declaraes, ocupando
espao desnecessrio na memria do sistema computacional.
Essa alocao que acabamos de descrever e que conhecemos bem chamada de alocao
esttica de memria, o que significa que, antes da execuo, o compilador, quando encontra
uma declarao como essa, reserva na memria um nmero fixo de compartimentos corres-
pondentes declarao. Esse espao fixo e no pode ser alterado durante a execuo do
programa.
Alocao esttica e compartimentos no usados na memria podem no ter impacto signi-
ficativo em programas pequenos, que fazem pouco uso da memria, como os nossos progra-
mas desenvolvidos at aqui. No entanto, devemos sempre ter em mente que a memria um
recurso limitado e que em programas maiores e que armazenam muitas informaes em me-
mria temos, de alguma forma, de us-la de maneira eficiente, economizando compartimentos
sempre que possvel.
Nesse sentido, se pudssemos declarar variveis compostas homogneas com o nmero
exato de compartimentos que sero de fato usados durante a execuo do programa, ento no
haveria esse desperdcio mencionado. No exemplo da declarao de vet e mat , economi-
zaramos espao significativo de memria, caso necessitssemos apenas de 10 compartimentos
no vetor vet e 9 compartimentos na matriz mat . No entanto, em outra execuo subseqente
do mesmo programa que declara essas variveis, podem ser necessrias capacidades diferentes
para ambas as variveis. Dessa forma, fixar um valor torna-se invivel e o que melhor podemos
fazer em alocao esttica de memria e prever um limitante mximo para esssas quantidades,
conforme vimos fazendo at aqui.
Felizmente para ns, programadores e programadoras da linguagem C, possvel alocar
dinamicamente um ou mais blocos de memria na linguagem C. Alocao dinmica de me-
mria significa que um programa solicita ao sistema computacional, durante a sua execuo,
blocos da memria principal que estejam disponveis para uso. Caso haja espao suficiente dis-
ponvel na memria principal, a solicitao atendida e o espao solicitado fica reservado na
memria para aquele uso especfico. Caso contrrio, isto , se no h como atender a solicita-
o de espao, uma mensagem de erro em tempo de execuo emitida e o(a) programador(a)
tem de estar preparado para esse tipo de situao, incluindo testes de verificao de situaes
como essa no arquivo-fonte, informando ao() usurio(a) a impossibilidade de uso do espao
solicitado de memria, solicitando ainda alguma deciso do(a) usurio(a) e, provavelmente,
encerrando a execuo do programa.
Vejamos um exemplo no programa 55.1 que faz alocao dinmica de um vetor.

FACOM UFMS
55.1 A LOCAO DINMICA DE MEMRIA 373

Programa 55.1: Um exemplo de alocao dinmica de memria.

1 #include <stdio.h>
2 #include <stdlib.h>
3 int main(void)
4 {
5 int i, n;
6 int *vetor, *apt;
7 printf("Informe a dimenso do vetor: ");
8 scanf("%d", &n);
9 vetor = (int *) malloc(n * sizeof(int));
10 if (vetor != NULL) {
11 for (i = 0; i < n; i++) {
12 printf("Informe o elemento %d: ", i+1);
13 scanf("%d", (vetor+i));
14 }
15 printf("\nVetor : ");
16 for (apt = vetor; apt < (vetor+n); apt++)
17 printf("%d ", *apt);
18 printf("\nVetor invertido: ");
19 for (i = n-1; i >= 0; i--)
20 printf("%d ", vetor[i]);
21 printf("\n");
22 }
23 else
24 printf("Impossvel alocar o espao requisitado\n");
25 return 0;
26 }

Na segunda linha do programa 55.1, inclumos o arquivo-cabealho stdlib.h , que con-


tm a declarao da funo malloc , usada na linha 9 desse programa. A funo malloc tem
sua interface apresentada a seguir:

void *malloc(size_t tamanho)

A funo malloc reserva uma certa quantidade especfica de memria e devolve um apon-
tador do tipo void . No programa acima, reservamos n compartimentos contnuos que podem
armazenar nmeros inteiros, o que se reflete na expresso n * sizeof(int) . Essa expresso,
na verdade, reflete o nmero de bytes que sero alocados continuamente na memria, que de-
pende do sistema computacional onde o programa compilado. O operador unrio sizeof
devolve como resultado o nmero de bytes dados pelo seu operando, que pode ser um tipo de
dados ou uma expresso. Nesse caso, nas mquinas que usamos no laboratrio, a expresso
sizeof(int) devolve 4 bytes. Esse nmero multiplicado por n, o nmero de comparti-
mentos que desejamos para armazenar nmeros inteiros. O endereo da primeira posio de

FACOM UFMS
55.1 A LOCAO DINMICA DE MEMRIA 374

memria onde encontram-se esses compartimentos devolvido pela funo malloc . Essa fun-
o devolve um apontador do tipo void . Por isso, usamos o modificador de tipo (int *)
para indicar que o endereo devolvido de fato um apontador para um nmero inteiro. Por
fim, esse endereo armazenado em vetor , que foi declarado como um apontador para n-
meros inteiros. A partir da, podemos usar cmdprgvet da forma como preferirmos, como um
apontador ou como um vetor.
O programa 55.2 um exemplo de alocao dinmica de memria de uma matriz de nme-
ros inteiros. Esse programa um pouco mais complicado que o programa 55.1, devido ao uso
distinto que faz da funo malloc .

Programa 55.2: Um exemplo de alocao dinmica de uma matriz.

1 #include <stdio.h>
2 #include <stdlib.h>
3 int main(void)
4 {
5 int i, j, m, n, **matriz, **apt;
6 printf("Informe a dimenso da matriz: ");
7 scanf("%d %d", &m, &n);
8 matriz = (int **) malloc(m * sizeof(int *));
9 if (matriz == NULL) {
10 printf("No h espao suficiente na memria\n");
11 return 0;
12 }
13 for (apt = matriz, i = 0; i < m; i++, apt++) {
14 *apt = (int *) malloc(n * sizeof(int));
15 if (*apt == NULL) {
16 printf("No h espao suficiente na memria\n");
17 return 0;
18 }
19 }
20 for (i = 0; i < m; i++)
21 for (j = 0; j < n; j++) {
22 printf("Informe o elemento (%d,%d): ", i+1, j+1);
23 scanf("%d", &matriz[i][j]);
24 }
25 printf("\nMatriz:\n");
26 apt = matriz;
27 for (i = 0; i < m; i++) {
28 for (j = 0; j < n; j++)
29 printf("%d ", *(*(apt+i)+j));
30 printf("\n");
31 }
32 printf("\n");
33 return 0;
34 }

FACOM UFMS
55.1 A LOCAO DINMICA DE MEMRIA 375

Observe por fim que as linhas em que ocorrem a alocao dinmica de memria no pro-
grama 55.2 podem ser substitudas de forma equivalente pelas linhas a seguir:

matriz = (int **) malloc(m * sizeof(int *));


for (i = 0; i < m; i++)
matriz[i] = (int *) malloc(n * sizeof(int));

Alm da funo malloc existem duas outras funes para alocao de memria na
stdlib.h : calloc e realloc , mas a primeira mais freqentemente usada que essas ou-
tras duas. Essas funes solicitam blocos de memria de um espao de armazenamento conhe-
cido como heap1 ou ainda lista de espaos disponveis. A chamada freqente dessas funes
pode exaurir o heap do sistema, fazendo com que essas funes devolvam um apontador nulo.
Pior ainda, um programa pode alocar blocos de memria e perd-los de algum modo, gastando
espao desnecessrio. Considere o exemplo a seguir:

p = malloc(...);
q = malloc(...);
p = q;

Aps as duas primeiras sentenas serem executadas, p aponta para um bloco de memria e
q aponta para um outro. No entanto, aps a atribuio de q para p na ltima sentena, as
duas variveis apontam para o mesmo bloco de memria, o segundo bloco, sem que nenhuma
delas aponte para o primeiro. Alm disso, no poderemos mais acessar o primeiro bloco, que
ficar perdido na memria, ocupando espao desnecessrio. Esse bloco chamado de lixo.
A funo free usada para ajudar os programadores da linguagem C a resolver o pro-
blema de gerao de lixo na memria durante a execuo de programas. Essa funo tem a
seguinte interface na stdlib.h :

void free(void *apt)

Para usar a funo free corretamente, devemos lhe passar um apontador para um bloco
de memria que no mais necessitamos, como fazemos abaixo:

p = malloc(...);
q = malloc(...);
free(p);
p = q;

1
Uma traduo possvel do ingls pode ser amontoado ou coleo de coisas dispostas juntas.

FACOM UFMS
55.2 A PONTADORES PARA APONTADORES 376

A chamada funo free devolve o bloco de memria apontado por p para o heap, que fica
disponvel para uso em chamadas subseqentes das funes de alocao de memria.
O argumento da funo free deve ser um apontador que foi previamente devolvido por
uma funo de alocao de memria.

55.2 Apontadores para apontadores

Como vimos at aqui, um apontador uma varivel cujo contedo um endereo. Dessa
forma, podemos acessar o contedo de uma posio de memria atravs de um apontador de
forma indireta. Temos visto exemplos de apontadores e suas aplicaes em algumas das aulas
anteriores e tambm na seo anterior.
Por outro lado, imagine por um momento que uma varivel de um tipo bsico qualquer
contm um endereo de uma posio de memria que, por sua vez, tambm contm um ende-
reo de uma outra posio de memria. Ento, essa varivel definida como um apontador
para um apontador, isto , um apontador para um compartimento de memria que contm um
apontador. Apontadores para apontadores tm diversas aplicaes na linguagem C, especial-
mente no uso de matrizes, como veremos adiante.
O conceito de indireo dupla ento introduzido neste caso: uma varivel que contm
um endereo de uma posio de memria, isto , um apontador, que, por sua vez, contm um
endereo para uma outra posio de memria, ou seja, um outro apontador. Certamente, po-
demos estender indirees com a multiplicidade que desejarmos como, por exemplo, indireo
dupla, indireo tripla, indireo qudrupla, etc. No entanto, a compreenso de um programa
fica gradualmente mais difcil medida que indirees mltiplas vo sendo utilizadas. Enten-
der bem um programa com apontadores para apontadores, ou apontadores para apontadores
para apontadores, e assim por diante, bastante complicado e devemos usar esses recursos de
forma criteriosa.
Considere agora o programa 55.3.
Inicialmente, o programa 55.3 faz a declarao de seis variveis do tipo inteiro: x e y , que
armazenam valores desse tipo; apt1 e apt2 , que so apontadores; e aptapt1 e aptapt2
que so apontadores para apontadores. O smbolo ** antes do identificador das variveis
aptapt1 e aptapt2 significa que as variveis so apontadores para apontadores, ou seja, po-
dem armazenar um endereo onde se encontra um outro endereo onde, por sua vez, encontra-
se um valor, um nmero inteiro nesse caso.
Aps essas declaraes, o programa segue com atribuies de valores s variveis x e y
e com atribuies dos endereos das variveis x e y para os apontadores apt1 e apt2 ,
respectivamente, como j vimos em outros exemplos.
Note ento que as duas atribuies abaixo:

aptapt1 = &apt1;
aptapt2 = &apt2;

FACOM UFMS
55.3 A PONTADORES PARA FUNES 377

Programa 55.3: Um exemplo de indireo dupla.

1 #include <stdio.h>
2 int main(void)
3 {
4 int x, y, *apt1, *apt2, **aptapt1, **aptapt2;
5 x = 1;
6 y = 4;
7 printf("x=%d y=%d\n", x, y);
8 apt1 = &x;
9 apt2 = &y;
10 printf("*apt1=%d *apt2=%d\n", *apt1, *apt2);
11 aptapt1 = &apt1;
12 aptapt2 = &apt2;
13 printf("**aptapt1=%d **aptapt2=%d\n", **aptapt1, **aptapt2);
14 return 0;
15 }

fazem das variveis aptapt1 e aptapt2 apontadores para apontadores. Ou seja, aptapt1
contm o endereo da varivel apt1 e aptapt2 contm o endereo da varivel apt2 . Por
sua vez, a varivel apt1 contm o endereo da varivel x e a varivel apt2 contm o en-
dereo da varivel y , o que caracteriza as variveis aptapt1 e aptapt2 como apontadores
para apontadores.
Observe finalmente que para acessar o contedo do endereo apontado pelo endereo apon-
tado por aptapt1 temos de usar o smbolo de indireo dupla ** , como pode ser verificado
na ltima chamada funo printf do programa.
Veja a figura 55.1 para um exemplo esquemtico do que ocorre na memria quando apon-
tadores para apontadores so usados em um programa.

1 x y 4

aptapt1 apt1 apt2 aptapt2

Figura 55.1: Exemplo esquemtico de indireo dupla.

55.3 Apontadores para funes

Como vimos at aqui, apontadores podem conter endereos de variveis de tipos bsicos,
de elementos de vetores e matrizes, de campos de registros ou de registros inteiros. Um apon-
tador pode ainda ser usado para alocao dinmica de memria. No entanto, a linguagem C
no requer que apontadores contenham endereos apenas de dados. possvel ter em um pro-
grama apontadores para funes, j que as funes ocupam posies de memria e, por isso,
possuem um endereo na memria, assim como todas as variveis.

FACOM UFMS
55.3 A PONTADORES PARA FUNES 378

Podemos usar apontadores para funes assim como usamos apontadores para variveis.
Em particular, passar um apontador para uma funo como um argumento de outra funo
bastante comum na linguagem C. Suponha que estamos escrevendo a funo integral que
integra uma funo matemtica f entre os pontos a e b . Gostaramos de fazer a funo
integral to geral quanto possvel, passando a funo f como um argumento seu. Isso
possvel na linguagem C pela definio de f como um apontador para uma funo. Conside-
rando que queremos integrar funes que tm um parmetro do tipo double e que devolvem
um valor do tipo double , uma possvel interface da funo integral apresentada a seguir:

double integral(double (*f)(double), double a, double b)

Os parnteses em torno de *f indicam que f um apontador para uma funo, no uma


funo que devolve um apontador. Tambm permitido definir f como se fosse uma funo:

double integral(double f(double), double a, double b)

Do ponto de vista do compilador, as interfaces acima so idnticas.


Quando chamamos a funo integral devemos fornecer um nome de uma funo como
primeiro argumento. Por exemplo, a chamada a seguir integra a funo seno de 0 a /2:

result = integral(sin, 0.0, PI / 2);

O argumento sin e o nome da funo seno includa em math.h .


Observe que no h parnteses aps sin . Quando o nome de uma funo no seguido
por parnteses, o compilador produz um apontador para a funo em vez de gerar cdigo para
uma chamada da funo. No exemplo acima, no h uma chamada funo sin . Ao invs
disso, estamos passando para a funo integral um apontador para a funo sin . Podemos
pensar em apontadores para funes como pensamos com apontadores para vetores e matrizes.
Relembrando, se, por exemplo, v o identificador de um vetor, ento v[i] representa um
elemento do vetor enquanto que v representa um apontador para o vetor, ou melhor, para
o primeiro elemento do vetor. Da forma similar, se f o identificador de uma funo, a
linguagem C trata f(x) como uma chamada da funo, mas trata f como um apontador
para a funo.
Dentro do corpo da funo integral podemos chamar a funo apontada por f da se-
guinte forma:

y = (*f)(x);

FACOM UFMS
55.3 A PONTADORES PARA FUNES 379

Nessa chamada, *f representa a funo apontada por f e x o argumento dessa chamada.


Assim, durante a execuo da chamada integral(sin, 0.0, PI / 2) , cada chamada de
*f , na verdade, uma chamada de sin .
Tambm podemos armazenar apontadores para funes em variveis ou us-los como ele-
mentos de um vetor, de uma matriz, de um campo de um registro. Podemos ainda escrever
funes que devolvem apontadores para funes. Como um exemplo, declaramos abaixo uma
varivel que pode armazenar um apontador para uma funo:

void (*aptf)(int);

O apontador aptf pode apontar para qualquer funo que tenha um nico parmetro do tipo
int e que devolva um valor do tipo void . Se f uma funo com essas caractersticas,
podemos fazer aptf apontar para f da seguinte forma:

aptf = f;

Observe que no h operador de endereamento antes de f . Uma vez que pf aponta para
f , podemos chamar f indiretamente atravs de aptf como a seguir:

(*aptf)(i);

O programa 55.4 imprime uma tabela mostrando os valores das funes seno, cosseno e
tangente no intervalo e incremento escolhidos pelo(a) usurio(a). O programa usa as funes
sin , cos e tan de math.h .

FACOM UFMS
55.3 A PONTADORES PARA FUNES 380

Programa 55.4: Um exemplo de apontador para funo.

1 #include <math.h>
2 #include <stdio.h>
3 void tabela(double (*f)(double), double a, double b, double incr)
4 {
5 int i, num_intervalos;
6 double x;
7 num_intervalos = ceil((b - a) / incr);
8 for (i = 0; i <= num_intervalos; i++) {
9 x = a + i * incr;
10 printf("%11.6f %11.6f\n", x, (*f)(x));
11 }
12 }
13 int main(void)
14 {
15 double inicio, fim, incremento;
16 printf("Informe um intervalo [a, b]: ");
17 scanf("%lf%lf", &inicio, &fim);
18 printf("Informe o incremento: ");
19 scanf("%lf", &incremento);
20 printf("\n x cos(x)"
21 "\n --------- ---------\n");
22 tabela(cos, inicio, fim, incremento);
23 printf("\n x sen(x)"
24 "\n --------- ---------\n");
25 tabela(sin, inicio, fim, incremento);
26 printf("\n x tan(x)"
27 "\n --------- ---------\n");
28 tabela(tan, inicio, fim, incremento);
29 return 0;
30 }

FACOM UFMS
55.3 A PONTADORES PARA FUNES 381

Exerccios

55.1 Refaa o exerccio 21.1 usando alocao dinmica de memria.

55.2 Refaa o exerccio 22.2 usando alocao dinmica de memria.

55.3 Refaa o exerccio 23.2 usando alocao dinmica de memria.

55.4 Refaa o exerccio 24.3 usando alocao dinmica de memria.

55.5 Refaa o exerccio 26.1 usando alocao dinmica de memria.

55.6 Simule a execuo do programa a seguir.

1 #include <stdio.h>
2 int f1(int (*f)(int))
3 {
4 int n = 0;
5 while ((*f)(n))
6 n++;
7 return n;
8 }
9 int f2(int i)
10 {
11 return i * i + i - 12;
12 }
13 int main(void)
14 {
15 printf("Resposta: %d\n", f1(f2));
16 return 0;
17 }

55.7 Escreva uma funo com a seguinte interface:

int soma(int (*f)(int), int inicio, int fim)

Uma chamada soma(g, i, j) deve devolver g(i) + ... + g(j) .

FACOM UFMS
A ULA 56

A RQUIVOS

Nos programas que fizemos at aqui, a entrada e a sada sempre ocorreram em uma janela
de terminal ou console do sistema operacional. Na linguagem C, no existem palavras-chaves
definidas para tratamento de operaes de entrada e sada. Essas tarefas so realizadas atravs
de funes. Pouco usamos outras funes de entrada e sada da linguagem C alm das funes
scanf e printf , localizadas na biblioteca padro de entrada e sada, com arquivo-cabealho
stdio.h . Nesta aula, aprenderemos funes que realizam tarefas de entrada e sada em ar-
quivos.

56.1 Seqncias de caracteres

Na linguagem C, o termo seqncia de caracteres, do ingls stream, significa qualquer fonte


de entrada ou qualquer destinao para sada. Os programas que produzimos at aqui sempre
obtiveram toda sua entrada a partir de uma seqncia de caracteres, em geral associada ao
teclado, e escreveram sua sada em outra seqncia de caracteres, associada com o monitor.
Programas maiores podem necessitar de seqncias de caracteres adicionais, associadas a
arquivos armazenados em uma variedade de meios fsicos tais como discos e memrias ou
ainda portas de rede e impressoras. As funes de entrada e sada mantidas em stdio.h
trabalham do mesmo modo com todas as seqncias de caracteres, mesmo aquelas que no
representam arquivos fsicos.
O acesso a uma seqncia de caracteres na linguagem C se d atravs de um apontador de
arquivo, cujo tipo FILE * , declarado em stdio.h . Algumas seqncias de caracteres so
representadas por apontadores de arquivos com nomes padronizados. Podemos ainda declarar
apontadores de arquivos conforme nossas necessidades, como fazemos abaixo:

FILE *apt1, *apt2;

Veremos mais sobre arquivos do sistema na seo 56.3.5.


A biblioteca representada pelo arquivo-cabealho stdio.h suporta dois tipos de arquivos:
texto e binrio. Os bytes em um arquivo-texto representam caracteres, fazendo que seja poss-
vel examinar e editar o seu contedo. O arquivo-fonte de um programa na linguagem C, por
exemplo, armazenado em um arquivo-texto. Por outro lado, os bytes em um arquivo-binrio
no representam necessariamente caracteres. Grupos de bytes podem representar outros tipos

382
56.2 R EDIRECIONAMENTO DE ENTRADA E SADA 383

de dados tais como inteiros e nmeros com ponto flutuante. Um programa executvel, por
exemplo, armazenado em um arquivo-binrio.
Arquivos-texto possuem duas principais caractersticas que os diferem dos arquivos-
binrios: so divididos em linhas e podem conter um marcador especial de fim de arquivo.
Cada linha do arquivo-texto normalmente termina com um ou dois caracteres especiais, de-
pendendo do sistema operacional.

56.2 Redirecionamento de entrada e sada

Como j fizemos nos trabalhos da disciplina e em algumas aulas, a leitura e escrita em


arquivos podem ser facilmente executadas nos sistemas operacionais em geral. Como perce-
bemos, nenhum comando especial teve de ser adicionado aos nossos programas na linguagem
C para que a leitura e a escrita fossem executadas de/para arquivos. O que fizemos at aqui
foi redirecionar a entrada e/ou a sada de dados do programa. Como um exemplo simples,
vejamos o programa 56.1, uma soluo para o exerccio 12.2, onde um nmero inteiro na base
decimal fornecido como entrada e na sada apresentado o mesmo nmero na base binria.

Programa 56.1: Uma soluo para o exerccio 12.2.

1 #include <stdio.h>
2 int main(void)
3 {
4 int pot10, numdec, numbin;
5 scanf("%d", &numdec);
6 pot10 = 1;
7 numbin = 0;
8 while (numdec > 0) {
9 numbin = numbin + (numdec % 2) * pot10;
10 numdec = numdec / 2;
11 pot10 = pot10 * 10;
12 }
13 printf("%d\n", numbin);
14 return 0;
15 }

Supondo que o programa 56.1 tenha sido armazenado no arquivo-fonte decbin.c e o


programa executvel equivalente tenha sido criado aps a compilao com o nome decbin ,
ento, se queremos que a sada do programa executvel decbin seja armazenada no arquivo
resultado , ento podemos digitar em uma linha de terminal o seguinte:

prompt$ ./decbin > resultado

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 384

Esse comando instrui o sistema operacional a executar o programa decbin redirecio-


nando a sada, que normalmente seria apresentada no terminal, para um arquivo com nome
resultado . Dessa forma, qualquer informao a ser apresentada por uma funo de sada,
como printf , no ser mostrada no terminal, mas ser escrita no arquivo resultado .
Por outro lado, podemos redirecionar a entrada de um programa executvel, de tal forma
que chamadas a funes que realizam entrada de dados, no mais solicitem essas informaes
ao usurio a partir do terminal, mas as obtenha a partir de um arquivo. Por exemplo, o pro-
grama 56.1 usa a funo scanf para ler um nmero inteiro a partir de um terminal. Podemos
redirecionar a entrada do programa decbin quando est sendo executado, fazendo que essa
entrada seja realizada a partir de um arquivo. Por exemplo, se temos um arquivo com nome
numero que contm um nmero inteiro, podemos digitar o seguinte em uma linha de termi-
nal:

prompt$ ./decbin < numero

Com esse redirecionamento, o programa 56.1, que solicita um nmero a ser informado pelo
usurio, no espera at que um nmero seja digitado. Ao contrrio, pelo redirecionamento, a
entrada do programa tomada do arquivo numero . Ou seja, a chamada funo scanf tem
o efeito de ler um valor do arquivo numero e no do terminal, embora a funo scanf no
saiba disso.
Podemos redirecionar a entrada e a sada de um programa simultaneamente da seguinte
maneira:

prompt$ ./decbin < numero > resultado

Esse comando faz com que o programa decbin seja executado tomando a entrada de da-
dos a partir do arquivo resultado e escrevendo a sada no arquivo resultado .
O redirecionamento de entrada e sada uma ferramenta til, j que, podemos manter
um arquivo de entradas e realizar diversos testes sobre um programa executvel a partir desse
arquivo de entradas. Alm disso, se temos, por exemplo, um arquivo alvo que contm solues
correspondentes s entradas, podemos comparar esse arquivo de solues com as sadas de
nosso programa, que tambm podem ser armazenadas em um arquivo.

56.3 Funes de entrada e sada da linguagem C

At esta aula, excluindo o redirecionamento de entrada e sada, tnhamos sempre arma-


zenado quaisquer informaes na memria principal do nosso sistema computacional. Como
j mencionado, uma grande quantidade de problemas pode ser resolvida com as operaes de
entrada e sada que conhecemos e com o redirecionamento de entrada e sada que acabamos de
aprender. Entretanto, existem problemas onde h necessidade de obter, ou armazenar, dados

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 385

de/para dois ou mais arquivos. Os arquivos so mantidos em um dispositivo do sistema com-


putacional conhecido como memria secundria, que pode ser implementado como um disco
rgido, um disquete, um disco compacto (CD), um disco verstil digital (DVD), um carto de
memria, um disco amovvel (USB flash memory), entre outros. A linguagem C tem um con-
junto de funes especficas para tratamento de arquivos que se localiza na biblioteca padro
de entrada e sada, cujo arquivo-cabealho stdio.h .

56.3.1 Funes de abertura e fechamento

Para que se possa realizar qualquer operao sobre um arquivo necessrio, antes de tudo,
de abrir esse arquivo. Como um programa pode ter de trabalhar com diversos arquivos, ento
todos eles devero estar abertos durante a execuo desse programa. Dessa forma, h neces-
sidade de identificao de cada arquivo, o que implementado na linguagem C com o uso de
um apontador para arquivo.
A funo fopen da biblioteca padro da linguagem C uma funo que realiza a abertura
de um arquivo no sistema. A funo recebe como parmetros duas cadeias de caracteres: a
primeira o nome do arquivo a ser aberto e a segunda determina o modo no qual o arquivo
ser aberto. O nome do arquivo deve constar no sistema de arquivos do sistema operacional. A
funo fopen devolve um apontador nico para o arquivo que pode ser usado para identificar
esse arquivo a partir desse ponto do programa. Esse aponador posicionado no incio ou no
final do arquivo, dependendo do modo como o arquivo foi aberto. Se o arquivo no puder ser
aberto por algum motivo, a funo devolve o valor NULL . Um apontador para um arquivo
deve ser declarado com um tipo pr-definido FILE , tambm incluso no arquivo-cabealho
stdio.h . A interface da funo fopen ento descrita da seguinte forma:

FILE *fopen(char *nome, char *modo)

As opes para a cadeia de caracteres modo so descritas na tabela a seguir:

modo Descrio
r modo de leitura de texto
w modo de escrita de texto
a modo de adicionar texto
r+ modo de leitura e escrita de texto
w+ modo de leitura e escrita de texto
a+ modo de leitura e escrita de texto
rb modo de leitura em binrio
wb modo de escrita em binrio
ab modo de adicionar em binrio
r+b ou rb+ modo de leitura e escrita em binrio
w+b ou wb+ modo de leitura e escrita em binrio
a+b ou ab+ modo de leitura e escrita em binrio

onde:

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 386

trunca o arquivo existente com tamanho 0 ou cria novo arquivo;


abre ou cria o arquivo e posiciona o apontador no final do arquivo.

Algumas observaes sobre os modos de abertura de arquivos se fazem necessrias. Pri-


meiro, observe que se o arquivo no existe e aberto com o modo de leitura ( r ) ento a
abertura falha. Tambm, se o arquivo aberto com o modo de adicionar ( a ), ento todas as
operaes de escrita ocorrem o final do arquivo, desconsiderando a posio atual do apontador
do arquivo. Por fim, se o arquivo aberto no modo de atualizao ( + ) ento a operao de
escrita no pode ser imediatamente seguida pela operao de leitura, e vice-versa, a menos que
uma operao de reposicionamento do apontador do arquivo seja executada, tal como uma
chamada a qualquer uma das funes fseek , fsetpos , rewind ou fflush .
Por exemplo, o comando de atribuio a seguir

aptarq = fopen("entrada", "r");

tem o efeito de abrir um arquivo com nome entrada no modo de leitura. A chamada
funo fopen devolve um identificador para o arquivo aberto que atribudo ao apontador
aptarq do tipo FILE . Ento, esse apontador posicionado no primeiro caracter do arquivo.
A declarao prvia do apontador aptarq deve ser feita da seguinte forma:

FILE *aptarq;

A funo fclose faz o oposto que a funo fopen faz, ou seja, informa o sistema que
o programador no necessita mais usar o arquivo. Quando um arquivo fechado, o sistema
realiza algumas tarefas importantes, especialmente a escrita de quaisquer dados que o sistema
possa ter mantido na memria principal para o arquivo na memria secundria, e ento disso-
cia o identificador do arquivo. Depois de fechado, no podemos realizar tarefas de leitura ou
escrita no arquivo, a menos que seja reaberto. A funo fclose tem a seguinte interface:

int fclose(FILE *aptarq)

Se a operao de fechamento do arquivo apontado por aptarq obtm sucesso, a funo


fclose devolve o valor 0 (zero). Caso contrrio, o valor EOF devolvido.

56.3.2 Funes de entrada e sada

A funo fgetc da biblioteca padro de entrada e sada da linguagem C permite que um


nico caracter seja lido de um arquivo. A interface dessa funo apresentada a seguir:

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 387

int fgetc(FILE *aptarq)

A funo fgetc l o prximo caracter do arquivo apontado por aptarq , avanando esse
apontador em uma posio. Se a leitura realizada com sucesso, o caracter lido devolvido
pela funo. Note, no entanto, que a funo, ao invs de especificar o valor de devoluo como
sendo do tipo unsigned char , especifica-o como sendo do tipo int . Isso se deve ao fato de
que a leitura pode falhar e, nesse caso, o valor devolvido o valor armazenado na constante
simblica EOF , definida no arquivo-cabealho stdio.h . O valor correspondente constante
simblica EOF obviamente um valor diferente do valor de qualquer caracter e, portanto, um
valor negativo. Do mesmo modo, se o fim do arquivo encontrado, a funo fgetc tambm
devolve EOF .
A funo fputc da biblioteca padro de entrada e sada da linguagem C permite que um
nico caracter seja escrito em um arquivo. A interface dessa funo apresentada a seguir:

int fputc(int caracter, FILE *aptarq)

Se a funo fputc tem sucesso, o apontador aptarq incrementado e o caracter escrito


devolvido. Caso contrrio, isto , se ocorre um erro, o valor EOF devolvido.
Existe outro par de funes de leitura e escrita em arquivos com identificadores fscanf e
fprintf . As interfaces dessas funes so apresentadas a seguir:

int fscanf(FILE *aptarq, char *formato, ...)

int fprintf(FILE *aptarq, char *formato, ...)

Essas duas funes so semelhantes s respectivas funes scanf e printf que conhe-
cemos bem, a menos de um parmetro a mais que informado, justamente o primeiro, que
o apontador para o arquivo que ser quer realizar as operaes de entrada e sada formatadas.
Dessa forma, o exemplo de chamada a seguir

fprintf(aptaqr, "O nmero %d primo.\n", numero);

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 388

realiza a escrita no arquivo apontado por aptarq da mensagem entre aspas duplas, subs-
tituindo o valor numrico correspondente armazenado na varivel numero . O nmero de
caracteres escritos no arquivo devolvido pela funo fprintf . Se um erro ocorrer, ento o
valor -1 devolvido.
Do mesmo modo,

fscanf(aptaqr, "%d", &numero);

realiza a leitura de um valor que ser armazenado na varivel numero a partir de um arquivo
identificado pelo apontador aptarq . Se a leitura for realizada com sucesso, o nmero de
valores lidos pela funo fscanf devolvido. Caso contrrio, isto , se houver falha na leitura,
o valor EOF devolvido.
H outras funes para entrada e sada de dados a partir de arquivos, como as funes
fread e fwrite , que no sero cobertas nesta aula. O leitor interessado deve procurar as
referncias bibliogrficas do curso.

56.3.3 Funes de controle

Existem diversas funes de controle que do suporte a operaes sobre os arquivos. Dentre
as mais usadas, listamos uma funo que descarrega o espao de armazenamento temporrio
da memria principal para a memria secundria e as funes que tratam do posicionamento
do apontador do arquivo.
A funo fflush faz a descarga de qualquer informao associada ao arquivo que esteja
armazenada na memria principal para o dispositivo de memria secundria associado. A
funo fflush tem a seguinte interface:

int fflush(FILE *aptarq)

onde aptarq o apontador para um arquivo. Se o apontador aptarq contm um valor


nulo, ento todos espaos de armazenamento temporrios na memria principal de todos os
arquivos abertos so descarregados nos dispositivos de memria secundria. Se a descarga
realizada com sucesso, a funo devolve o valor 0 (zero). Caso contrrio, a funo devolve o
valor EOF .
Sobre as funes que tratam do posicionamento do apontador de um arquivo, existe uma
funo especfica da biblioteca padro da linguagem C que realiza um teste de final de arquivo.
Essa funo tem identificador feof e a seguinte interface:

int feof(FILE *aptarq)

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 389

O argumento da funo feof um apontador para um arquivo do tipo FILE . A funo


devolve um valor inteiro diferente de 0 (zero) se o apontador aptarq est posicionado no final
do arquivo. Caso contrrio, a funo devolve o valor 0 (zero).
A funo fgetpos determina a posio atual do apontador do arquivo e tem a seguinte
interface:

int fgetpos(FILE *aptarq, fpos_t *pos)

onde aptarq o apontador associado a um arquivo e pos uma varivel que, aps a exe-
cuo dessa funo, conter o valor da posio atual do apontador do arquivo. Observe que
fpos_t um novo tipo de dado, definido no arquivo-cabealho stdio.h , adequado para
armazenamento de uma posio qualquer de um arquivo. Se a obteno dessa posio for re-
alizada com sucesso, a funo devolve 0 (zero). Caso contrrio, a funo devolve um valor
diferente de 0 (zero).
A funo fsetpos posiciona o apontador de um arquivo em alguma posio escolhida e
tem a seguinte interface:

int fsetpos(FILE *aptarq, fpos_t *pos)

onde aptarq o apontador para um arquivo e pos uma varivel que contm a posio para
onde o apontador do arquivo ser deslocada. Se a determinao dessa posio for realizada
com sucesso, a funo devolve 0 (zero). Caso contrrio, a funo devolve um valor diferente
de 0 (zero).
A funo ftell determina a posio atual de um apontador em um dado arquivo. Sua
interface apresentada a seguir:

long int ftell(FILE *aptarq)

A funo ftell devolve a posio atual no arquivo apontado por aptarq . Se o arquivo
binrio, ento o valor o nmero de bytes a partir do incio do arquivo. Se o arquivo de texto,
ento esse valor pode ser usado pela funo fseek , como veremos a seguir. Se h sucesso na
sua execuo, a funo devolve a posio atual no arquivo. Caso contrrio, a funo devolve o
valor -1L .
A funo fseek posiciona o apontador de um arquivo para uma posio determinada por
um deslocamento. A interface da funo dada a seguir:

int fseek(FILE *aptarq, long int desloca, int apartir)

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 390

O argumento aptarq o apontador para um arquivo. O argumento desloca o nmero


de bytes a serem saltados a partir do contedo do argumento apartir . Esse contedo pode
ser um dos seguintes valores pr-definidos no arquivo-cabealho stdio.h :

SEEK_SET A partir do incio do arquivo


SEEK_CUR A partir da posio atual
SEEK_END A partir do fim do arquivo

Em um arquivo de texto, o contudo de apartir dever ser SEEK_SET e o contedo de


desloca deve ser 0 (zero) ou um valor devolvido pela funo ftell . Se a funo executada
com sucesso, o valor 0 (zero) devolvido. Caso contrrio, um valor diferente de 0 (zero)
devolvido.
A funo rewind faz com que o apontador de um arquivo seja posicionado para o incio
desse arquivo. A interface dessa funo a seguinte:

void rewind(FILE *aptarq)

56.3.4 Funes sobre arquivos

H funes na linguagem C que permitem que um programador remova um arquivo do


disco ou troque o nome de um arquivo. A funo remove tem a seguinte interface:

int remove(char *nome)

A funo remove elimina um arquivo, com nome armazenado na cadeia de caracteres


nome , do sistema de arquivos do sistema computacional. O arquivo no deve estar aberto no
programa. Se a remoo realizada, a funo devolve o valor 0 (zero). Caso contrrio, um
valor diferente de 0 (zero) devolvido.
A funo rename tem a seguinte interface:

int rename(char *antigo, char *novo)

A funo rename faz com que o arquivo com nome armazenado na cadeia de caracteres
antigo tenha seu nome trocado pelo nome armazenado na cadeia de caracteres novo . Se a
funo realiza a tarefa de troca de nome, ento rename devolve o valor 0 (zero). Caso contr-
rio, um valor diferente de 0 (zero) devolvido e o arquivo ainda pode ser identificado por seu
nome antigo.

FACOM UFMS
56.3 F UNES DE ENTRADA E SADA DA LINGUAGEM C 391

56.3.5 Arquivos do sistema

Sempre que um programa na linguagem C executado, trs arquivos ou seqncias de


caracteres so automaticamente abertos pelo sistema, identificados pelos apontadores stdin ,
stdout e stderr , definidos no arquivo-cabealho stdio.h da biblioteca padro de entrada
e sada. Em geral, stdin est associado ao teclado e stdout e stderr esto associados ao
monitor. Esses apontadores so todos do tipo FILE .
O apontador stdin identifica a entrada padro do programa e normalmente associado
ao teclado. Todas as funes de entrada definidas na linguagem C que executam entrada de
dados e no tm um apontador do tipo FILE como um argumento tomam a entrada a partir
do arquivo apontado por stdin . Assim, ambas as chamadas a seguir:

scanf("%d", &numero);

fscanf(stdin, "%d", &numero);

so equivalentes e lem um nmero do tipo inteiro da entrada padro, que normalmente o


terminal.
Do mesmo modo, o apontador stdout se refere sada padro, que tambm associada
ao terminal. Assim, as chamadas a seguir:

printf("Programar bacana!\n");

fprintf(stdout, "Programa bacana!\n");

so equivalentes e imprimem a mensagem acima entre as aspas duplas na sada padro, que
normalmente o terminal.
O apontador stderr se refere ao arquivo padro de erro, onde muitas das mensagens de
erro produzidas pelo sistema so armazenadas e tambm normalmente associado ao termi-
nal. Uma justificativa para existncia de tal arquivo , por exemplo, quando as sadas todas
do programa so direcionadas para um arquivo. Assim, as sadas do programa so escritas em
um arquivo e as mensagens de erro so escritas na sada padro, isto , no terminal. Ainda
h a possibilidade de escrever nossas prprias mensagens de erro no arquivo apontado por
stderr .

FACOM UFMS
56.4 E XEMPLOS 392

56.4 Exemplos

Nessa seo apresentaremos dois exemplos que usam algumas das funes de entrada e
sada em arquivos que aprendemos nesta aula.
O primeiro exemplo, apresentando no programa 56.2, bem simples e realiza a cpia do
contedo de um arquivo em outro arquivo.
O segundo exemplo, apresentado no programa 56.3, mescla o contedo de dois arquivos
texto em um arquivo resultante. Neste exemplo, cada par de palavras do arquivo resultante
composto por uma palavra de um arquivo e uma palavra do outro arquivo. Se um dos
arquivos contiver menos palavras que o outro arquivo, esse outro arquivo descarregado no
arquivo resultante.

FACOM UFMS
56.4 E XEMPLOS 393

Programa 56.2: Um exemplo de cpia de um arquivo.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 char nomebase[MAX+1], nomecopia[MAX+1];
6 int c;
7 FILE *aptbase, *aptcopia;
8 printf("Informe o nome do arquivo a ser copiado: ");
9 scanf("%s", nomebase);
10 printf("Informe o nome do arquivo resultante: ");
11 scanf("%s", nomecopia);
12 aptbase = fopen(nomebase, "r");
13 if (aptbase != NULL) {
14 aptcopia = fopen(nomecopia, "w");
15 if (aptcopia != NULL) {
16 c = fgetc(aptbase);
17 while (c != EOF) {
18 fputc(c, aptcopia);
19 c = fgetc(aptbase);
20 }
21 fclose(aptbase);
22 fclose(aptcopia);
23 printf("Arquivo copiado.\n");
24 }
25 else
26 printf("No possvel abrir o arquivo %s para escrita.\n", nomecopia);
27 }
28 else
29 printf("No possvel abrir o arquivo %s para leitura.\n", nomebase);
30 return 0;
31 }

FACOM UFMS
56.4 E XEMPLOS 394

Programa 56.3: Um segundo exemplo de uso de arquivos.

1 #include <stdio.h>
2 #define MAX 100
3 int main(void)
4 {
5 char nomeum[MAX+1], nomedois[MAX+1], palavra1[MAX+1], palavra2[MAX+1];
6 int i1, i2;
7 FILE *aptum, *aptdois, *aptresult;
8 printf("Informe o nome do primeiro arquivo: ");
9 scanf("%s", nomeum);
10 printf("Informe o nome do segundo arquivo: ");
11 scanf("%s", nomedois);
12 aptum = fopen(nomeum, "r");
13 aptdois = fopen(nomedois, "r");
14 if (aptum != NULL && aptdois != NULL) {
15 aptresult = fopen("mesclado", "w");
16 if (aptresult != NULL) {
17 i1 = fscanf(aptum, "%s", palavra1);
18 i2 = fscanf(aptdois, "%s", palavra2);
19 while (i1 != EOF && i2 != EOF) {
20 fprintf(aptresult, "%s %s ", palavra1, palavra2);
21 i1 = fscanf(aptum, "%s", palavra1);
22 i2 = fscanf(aptdois, "%s", palavra2);
23 }
24 while (i1 != EOF) {
25 fprintf(aptresult, "%s ", palavra1);
26 i1 = fscanf(aptum, "%s", palavra1);
27 }
28 while (i2 != EOF) {
29 fprintf(aptresult, "%s ", palavra2);
30 i2 = fscanf(aptdois, "%s", palavra2);
31 }
32 fprintf(aptresult, "\n");
33 fclose(aptum);
34 fclose(aptdois);
35 fclose(aptresult);
36 printf("Arquivo mesclado.\n");
37 }
38 else
39 printf("No possvel abrir um arquivo para escrita.\n");
40 }
41 else
42 printf("No possvel abrir os arquivos para leitura.\n");
43 return 0;
44 }

FACOM UFMS
56.4 E XEMPLOS 395

Exerccios

56.1 Escreva um programa que leia o contedo de um arquivo cujo nome fornecido pelo
usurio e copie seu contedo em um outro arquivo, trocando todas as letras minsculas
por letras maisculas.

56.2 Suponha que temos dois arquivos cujas linhas so ordenadas lexicograficamente. Por
exemplo, esses arquivos podem conter nomes de pessoas, linha a linha, em ordem al-
fabtica. Escreva um programa que leia o contedo desses dois arquivos, cujos nomes
so fornecidos pelo usurio, e crie um novo arquivo resultante contendo todas as linhas
dos dois arquivos ordenadas lexicograficamente. Por exemplo, se os arquivos contm as
linhas

Arquivo 1 Arquivo 2

Antnio Carlos
Berenice Clia
Diana Fbio
Solange Henrique
Snia
Zuleica

o arquivo resultante deve ser

Antnio
Berenice
Carlos
Clia
Diana
Fbio
Henrique
Solange
Snia
Zuleica

FACOM UFMS
A ULA 57

L ISTAS LINEARES

Listas lineares so as primeiras estruturas de dados que aprendemos. Essas estruturas so


muito usadas em diversas aplicaes importantes para organizao de informaes na me-
mria tais como representaes alternativas para expresses aritmticas, compartilhamento de
espao de memria, entre outras. Nesta aula, aprenderemos a definio dessa estrutura, sua
implementao em alocao esttica e suas operaes bsicas.

57.1 Definio

Informalmente, uma lista linear uma estrutura de dados que armazena um conjunto de
informaes que so relacionadas entre si. Essa relao se expressa apenas pela ordem relativa
entre os elementos. Por exemplo, nomes e telefones de uma agenda telefnica, as informaes
bancrias dos funcionrios de uma empresa, etc, so informaes que podem ser armazenadas
em uma lista linear. Cada informao contida na lista na verdade um registro contendo os
dados relacionados. Em geral, usamos um desses dados como uma chave para realizar diversas
operaes sobre essa lista, tais como busca de um elemento, insero, remoo, etc. J que os
dados acoplados chave, tambm chamados de dados satlites, so irrelevantes e participam
apenas das movimentaes dos registros, podemos imaginar que uma lista linear composta
apenas pelas chaves desses registros e que essas chaves so representadas por nmeros inteiros.
Agora formalmente, uma lista linear L um conjunto de n > 0 registros l1 , l2 , . . . , ln deter-
minada pela ordem relativa desses elementos:

(i) se n > 0 ento l1 o primeiro registro;

(ii) o registro li precedido pelo registro li1 para todo i, 1 < i 6 n.

As operaes bsicas sobre uma lista linear so as seguintes:

busca;

incluso; e

remoo.

De acordo com a poltica de armazenamento das informaes nos registros da lista, temos
listas lineares especiais tais como pilhas e filas, como veremos adiante.

396
57.1 D EFINIO 397

Dependendo da aplicao, muitas outras operaes tambm podem ser realizadas sobre
essa estrutura como, por exemplo, alterao de um elemento, combinao de duas listas, orde-
nao da lista de acordo com as chaves, determinao do elemento de menor ou maior chave,
determinao do tamanho ou nmero de elementos da lista, etc.
As listas lineares podem ser armazenadas na memria de duas maneiras distintas:

Alocao esttica ou seqencial: os elementos so armazenados em posies consecutivas de


memria, com uso da vetores;

Alocao dinmica ou encadeada: os elementos podem ser armazenados em posies no


consecutivas de memria, com uso de apontadores.

A aplicao, ou problema que queremos resolver, que define o tipo de armazenamento


a ser usado, dependendo das operaes sobre a lista, do nmero de listas envolvidas e das
caractersticas particulares das listas. Nas aulas 41 e 32 vimos especialmente a operao de
busca em uma lista linear em alocao seqencial.
Os registros de uma lista linear em alocao encadeada, tambm chamados de clulas,
encontram-se dispostos em posies aleatrias da memria e so ligados por apontadores que
indicam a posio do prximo elemento da lista. Assim, um campo acrescentado a cada re-
gistro da lista indicando o endereo do prximo elemento da lista. Veja a figura 57.1 para um
exemplo de um registro de uma lista.

conteudo prox

dados acoplados

Figura 57.1: Representao de um registro de uma lista linear em alocao encadeada.

Os dados acoplados, ou dados satlites, so irrelevantes nas nossas aplicaes e por isso
no so considerados. A definio de um registro de nossa lista linear ento descrita como a
seguir:

struct cel {
int conteudo;
struct cel *prox;
};

conveniente tratar as clulas de uma lista linear em alocao encadeada como um tipo
novo de dados, que chamaremos de celula :

typedef struct cel celula;

FACOM UFMS
57.1 D EFINIO 398

Uma clula c e um apontador p para uma clula podem ser declarados da seguinte forma:

celula c;
celula *p;

Se c uma clula ento c.conteudo o contedo da clula e c.prox o endereo da


clula seguinte. Se p o endereo de uma clula ento p->conteudo o contedo da clula
apontada por p e p->prox o endereo da clula seguinte. Se p o endereo da ltima
clula da lista ento p->prox vale NULL .
Uma ilustrao de uma lista linear em alocao encadeada mostrada na figura 57.2.

l5 l6

l3

l4
l2
l1

Figura 57.2: Representao de uma lista linear em alocao encadeada na memria.

Dizemos que o endereo de uma lista encadeada o endereo de sua primeira clula. Se p
o endereo de uma lista, podemos dizer simplesmente p uma lista e considere a lista
p . Reciprocamente, a expresso p uma lista deve ser interpretada como p o endereo
da primeira clula de uma lista.
Uma lista linear pode ser vista de duas maneiras diferentes, dependendo do papel que sua
primeira clula representa. Em uma lista linear com cabea, a primeira clula serve apenas
para marcar o incio da lista e portanto o seu contedo irrelevante. A primeira clula a
cabea da lista. Em uma lista linear sem cabea o contedo da primeira clula to relevante
quanto o das demais. Nas nossas aulas, trataremos das listas lineares sem cabea, embora as
listas lineares com cabea sejam ligeiramente mais simples de manipular. Assim, sempre que
nos referirmos a uma lista linear, na verdade estaremos nos referindo implicitamente a uma
lista linear sem cabea. Deixaremos as listas lineares com cabeas para os exerccios.
Uma lista linear est vazia se no tem clula alguma. Para criar uma lista vazia lista
basta escrever as seguintes sentenas:

celula *lista;
lista = NULL;

FACOM UFMS
57.2 B USCA 399

Para imprimir o contedo de todas as clulas de uma lista linear lista podemos usar a
seguinte funo:

void imprime_lista(celula *lista)


{
celula *p;
for (p = lista; p != NULL; p = p->prox)
printf("%d\n", p->conteudo);
}

A seguir vamos discutir e implementar as operaes bsicas de busca, insero e remoo


sobre listas lineares em alocao encadeada.

57.2 Busca

O processo de busca de um objeto x em uma lista linear em alocao encadeada, isto , de


verificar se x igual ao contedo de alguma clula da lista, pode ser descrito como apresenta-
mos abaixo. A funo buscaEnc recebe uma lista linear apontada por lista e um nmero
inteiro x e devolve o endereo de um registro contendo x , caso x ocorra em lista . Caso
contrrio, a funo devolve NULL .

1 celula *buscaEnc(celula *lista, int x)


2 {
3 celula *p;
4 p = lista;
5 while (p != NULL && p->conteudo != x)
6 p = p->prox;
7 return p;
8 }

interessante comparar a eficincia dessa funo com a da funo busca da aula 32, que
realiza processamento equivalente, porm em uma lista linear em alocao seqencial, isto ,
em um vetor.
Uma chamada funo buscaEnc pode ser realizada como mostramos a seguir, supondo
apontadores p e lista do tipo celula e x um nmero inteiro:

p = buscaEnc(lista, x);

FACOM UFMS
57.3 I NSERO 400

57.3 Insero

Observe que na alocao encadeada, os ns de uma lista linear se encontram dispostos


em posies de memria no necessariamente contnuas. Com a realizao de inseres e
remoes, h necessidade de encontrar novas posies de memria para armazenamento de
informaes e liberar outras posies que possam ser reutilizadas posteriormente. Podemos
gerenciar a memria na linguagem C usando funes que gerenciam seu espao disponvel.
As funes malloc e free , da biblioteca stdlib , executam a tarefa de ocupao e libera-
o, respectivamente, de compartimentos da lista de espaos disponveis da memria, como
vimos na aula 55.
Uma funo que insere um elemento x na lista linear L apresentada a seguir. Se x j
consta da lista, ento nada fazemos. Caso contrrio, inserimos x no incio da lista.

1 void insereEnc(celula **L, int x)


2 {
3 celula *novo;
4 if (buscaEnc(*L, x) == NULL) {
5 novo = (celula *) malloc(sizeof(celula));
6 if (novo != NULL) {
7 novo->conteudo = x;
8 novo->prox = *L;
9 *L = novo;
10 }
11 else {
12 printf("No h memria disponvel!\n");
13 exit(EXIT_FAILURE);
14 }
15 }
16 else
17 printf("Valor j se encontra na lista!\n");
18 }

Uma chamada funo insereEnc pode ser feita como abaixo, para uma lista e um
valor x :

insereEnc(&lista, x);

Observe ainda que as inseres so sempre realizadas no incio da lista linear. Podemos mo-
dificar essa comportamento, por exemplo, mantendo um outro apontador para o final da lista
linear e fazendo as inseres neste outro extremo. Deixamos essa idia para ser implementada
em um exerccio.

FACOM UFMS
57.4 R EMOO 401

57.4 Remoo

Para realizar a remoo de um registro da lista necessrio busc-lo. A funo buscaEnc


descrita nesta aula devolve um apontador para o registro cujo contedo procurado se encontra,
ou NULL caso esse valor no ocorra na lista. Um exemplo desta situao apresentado na
figura 57.3.
p

1 7 6 5 3 4

Figura 57.3: Busca em uma lista linear encadeada. O apontador devolvido como resultado.

Depois de ter encontrado o registro que contm o elemento que queremos remover, o que
devemos fazer? Infelizmente, esta funo buscaEnc no devolve uma informao fundamen-
tal para que a remoo possa ser realizada imediatamente com sucesso: um apontador para
o registro anterior ao registro que contm o valor que queremos remover. No exemplo da fi-
gura 57.3, precisamos de um apontador para o registro que contm o valor 6. Como s temos
um apontador para o registro seguinte, no conseguimos realizar a remoo, a menos que fa-
amos algum truque. Mesmo assim, ainda possvel realizar a remoo, mas vamos deixar
idias mais complicadas para um exerccio. A idia a ser apresentada aqui modificar um
pouco a funo buscaEnc de modo que possa devolver dois apontadores: um para o registro
que contm o valor buscado e outro para o registro imediatamente anterior a esse. A funo
buscaEnc2 a seguir resultado dessa idia.

1 void buscaEnc2(celula *L, celula **apt, celula **ant, int x)


2 {
3 *ant = NULL;
4 *apt = L;
5 while (*apt != NULL && (*apt)->conteudo != x) {
6 *ant = *apt;
7 *apt = (*apt)->prox;
8 }
9 }

Observe que a funo buscaEnc2 devolve um apontador para o elemento procurado em


*apt e um apontador para o elemento anterior ao procurado em *ant , se o elemento de fato
se encontra na lista. Caso contrrio, *apt conter um apontador NULL , como antes.
Agora, com os apontadores *apt e *ant , podemos descrever a funo removeEnc .

FACOM UFMS
57.4 R EMOO 402

1 void removeEnc(celula **L, int x)


2 {
3 celula *apt, *ant;
4 buscaEnc2(*L, &apt, &ant, x);
5 if (apt != NULL) {
6 if (ant != NULL)
7 ant->prox = apt->prox;
8 else
9 *L = apt->prox;
10 free(p);
11 }
12 else
13 printf("Valor no encontrado\n");
14 }

Veja a figura 57.4 para um exemplo de remoo.

ant p

1 7 6 5 3 4

(i)

ant p

1 7 6 5 3 4

(ii)

ant p

1 7 6 3 4

(iii)

Figura 57.4: Remoo do valor 5 em uma lista linear encadeada. (i) Aps a busca do valor. (ii)
Modificao do apontador ant . (iii) Liberao da memria.

FACOM UFMS
57.4 R EMOO 403

Uma chamada funo removeEnc ilustrada abaixo, para uma lista e um valor x :

removeEnc(&lista, x);

Observe que os apontadores apt e ant so variveis locais funo removeEnc , que
auxiliam tanto na busca como na remoo propriamente. Alm disso, se ant == NULL ento
o registro que contm x , que queremos remover, encontra-se na primeira posio da lista.
Dessa forma, h necessidade de alterar o contedo do apontador *L para o registro seguinte
desta lista.

Exerccios

57.1 Escreva uma funo insereFim que receba uma lista linear *L , um apontador f para
o fim da lista e um valor x e realize a insero desse valor no final da lista.

57.2 Se conhecemos apenas o apontador apt para um n de uma lista linear em alocao
encadeada, como na figura 57.5, e nada mais conhecido, como podemos modificar a lista
linear de modo que passe a conter apenas os valores 20, 4, 19, 47, isto , sem o contedo
do n apontado por apt ?

apt

20 4 5 19 47

Figura 57.5: Uma lista linear encadeada com um apontador apt .

57.3 O esquema apresentado na figura 57.6 permite percorrer uma lista linear encadeada nos
dois sentidos, usando apenas o campo prox que contm o endereo do prximo ele-
mento da lista. Usamos dois apontadores esq e dir , que apontam para dois elementos
vizinhos da lista. A idia desse esquema que medida que os apontadores esq e dir
caminham na lista, os campos prox so invertidos de maneira a permitir o trfego nos
dois sentidos.
Escreva funes para:

(a) mover esq e dir para a direita de uma posio.


(b) mover esq e dir para a esquerda de uma posio.

FACOM UFMS
57.4 R EMOO 404

esq dir

(i)

esq dir

(ii)

Figura 57.6: A figura (ii) foi obtida de (i) atravs da execuo da funo dada no exerccio 57.4(a)
por trs vezes.

57.4 Uma lista linear encadeada com cabea tal que a primeira clula serve apenas para
marcar o incio da lista e portanto o seu contedo irrelevante. A primeira clula a
cabea da lista. Se L o endereo da cabea de lista ento L->prox vale NULL se
e somente se a lista est vazia. Para criar uma lista vazia deste tipo, devemos fazer o
seguinte:

celula *L;
L = malloc(sizeof(celula));
L->prox = NULL;

Escreva funes de busca, insero e remoo em uma lista linear com cabea.

57.5 (a) Escreva uma funo que copie um vetor para uma lista linear encadeada.
(b) Escreva uma funo que copie uma lista linear encadeada em um vetor.

57.6 Escreva uma funo que decida se duas listas dadas tm o mesmo contedo.

57.7 Escreva uma funo que conte o nmero de clulas de uma lista linear encadeada.

57.8 Seja lista uma lista linear com seus contedos dispostos em ordem crescente. Escreva
as funes buscaEncOrd , insereEncOrd e removeEncOrd para realizao da busca,
insero e remoo, respectivamente, em uma lista linear com essas caracterstica. As
operaes de insero e remoo devem manter a lista em ordem crescente.

57.9 Sejam duas listas lineares L1 e L2 , com seus contedos dispostos em ordem crescente.
Escreva uma funo concatena que receba L1 e L2 e construa uma lista R resul-

FACOM UFMS
57.4 R EMOO 405

tante da intercalao dessas duas listas, de tal forma que a lista construda tambm esteja
ordenada. A funo concatena deve destruir as listas L1 e L2 e deve devolver R .

57.10 Seja L uma lista linear composta por registros contendo os valores l1 , l2 , . . . ln , nessa or-
dem.

(a) Escreva uma funo roda1 que receba *L e modifique e devolva essa lista de tal
forma que a lista resultante contenha as chaves l2 , l3 , . . . , ln , l1 , nessa ordem.
(b) Escreva uma funo inverte que receba *L e modifique e devolva essa lista de
tal forma que a lista resultante contenha as chaves ln , ln1 , . . . , l2 , l1 , nessa ordem.
(c) Escreva uma funo soma que receba *L e modifique e devolva essa lista de tal
forma que a lista resultante contenha as chaves l1 + ln , l2 + ln1 , . . . , ln/2 + ln/2+1 ,
nessa ordem. Considere n par.

57.11 Sejam S1 e S2 dois conjuntos disjuntos de nmeros inteiros. Suponha que S1 e S2 esto im-
plementados em duas listas lineares em alocao encadeada L1 e L2 , respectivamente.
Escreva uma funo uniao que receba as listas L1 e L2 representando os conjuntos S1
e S2 e devolva uma lista resultante R que representa a unio dos conjuntos, isto , uma
lista linear encadeada que representa o conjunto S = S1 S2 .

57.12 Seja um polinmio p(x) = a0 xn + a1 xn1 + . . . + an , com coeficientes de ponto flutuante.


Represente p(x) adequadamente por uma lista linear encadeada e escreva as seguintes
funes.

(a) Escreva uma funo pponto que receba uma lista p e um nmero de ponto flutu-
ante x0 e calcule e devolva p(x0 ).
(b) Escreva uma funo psoma que receba as listas lineares p e q , que representam
dois polinmios, e calcule e devolva o polinmio resultante da operao p(x) + q(x).
(c) Escreva uma funo pprod que receba as listas lineares p e q , que representam
dois polinmios, e calcule e devolva o polinmio resultante da operao p(x) q(x).

FACOM UFMS
A ULA 58

P ILHAS

O armazenamento seqencial de uma lista usado, em geral, quando essas estruturas so-
frem poucas modificaes ao longo do tempo, isto , poucas inseres e remoes so realizadas
durante sua existncia. Isso implica em poucas movimentaes de registros. Por outro lado,
a alocao encadeada mais usada na situao inversa, ou seja, quando modificaes na lista
linear so mais freqentes.
Caso os elementos a serem inseridos ou removidos de uma lista linear se localizem em
posies especiais nessas estruturas, como por exemplo a primeira ou a ltima posio, ento
temos uma situao favorvel para o uso de listas lineares em alocao seqencial. Este o
caso da estrutura de dados denominada pilha. O que torna essa lista linear especial a adoo
de uma poltica bem definida de inseres e remoes, sempre em um dos extremos da lista.
A implementao de uma pilha em alocao encadeada tem muitas aplicaes importantes e
tambm ser discutida nesta aula.

58.1 Definio

Uma pilha uma lista linear tal que as operaes de insero e remoo so realizadas em
um nico extremo dessa estrutura de dados.
O funcionamento dessa lista linear pode ser comparado a qualquer pilha de objetos que
usamos com freqncia como, por exemplo, uma pilha de pratos de um restaurante. Em geral,
os clientes do restaurante retiram pratos do topo da pilha e os funcionrios colocam pratos
limpos tambm no topo.
Observe que uma pilha tem sempre um indicador do extremo em que uma insero ou
remoo deve ser realizada. O indicador desse extremo, nesta estrutura, chamado de topo da
pilha.
Duas operaes bsicas so realizadas sobre uma pilha: insero e remoo. Essas duas
operaes so tambm chamadas de empilhamento e desempilhamento de elementos. Observe
que a operao de busca no foi mencionada e no faz parte do conjunto de operaes bsicas
de uma pilha.

58.2 Operaes bsicas em alocao seqencial

Suponha que uma pilha esteja armazenada em um vetor P[0..MAX-1] . A parte do vetor
efetivamente ocupada pela pilha P[0..t] onde t o ndice que define o topo da pilha.

406
58.2 O PERAES BSICAS EM ALOCAO SEQENCIAL 407

Uma ilustrao de uma pilha em alocao seqencial dada na figura 58.1.

0 1 2 3 MAX-1
P 3 1 8

Figura 58.1: Representao de uma pilha P em alocao seqencial.

Convencionamos que uma pilha est vazia se seu topo t vale 1 e cheia se seu topo t
vale MAX-1 .
Suponha que queremos inserir um elemento de valor 7 na pilha P da figura 58.1. Como
resultado desta operao, esperamos que a pilha tenha a configurao mostrada na figura 58.2
aps sua execuo.

0 1 2 3 MAX-1

P 3 1 8 7

Figura 58.2: Insero da chave 7 na pilha P da figura 58.1.

A operao de inserir um objeto em uma pilha, ou empilhar, descrita a seguir.

1 void empilhaSeq(int P[MAX], int *t, int x)


2 {
3 if (*t != MAX-1) {
4 (*t)++;
5 P[*t] = x;
6 }
7 else
8 printf("Impossvel inserir um novo elemento na pilha\n");
9 }

Suponha agora que queremos remover um elemento de uma pilha. Observe que, assim
como na insero, a remoo tambm deve ser feita em um dos extremos da pilha, isto , no
topo da pilha. Assim, a remoo de um elemento da pilha P que tem sua configurao ilus-
trada na figura 58.1 tem como resultado a pilha com a configurao mostrada na figura 58.3
aps a sua execuo.
A operao de remover, ou desempilhar, um objeto de uma pilha descrita na funo
desempilhaSeq a seguir.

FACOM UFMS
58.2 O PERAES BSICAS EM ALOCAO SEQENCIAL 408

0 1 2 3 MAX-1
P 3 1

Figura 58.3: Remoo de um elemento da pilha P da figura 58.1.

1 int desempilhaSeq(int P[MAX], int *t)


2 {
3 int removido;
4 if (*t != -1) {
5 removido = P[*t];
6 (*t)--;
7 return removido;
8 }
9 else {
10 printf("Impossvel remover um elemento da pilha\n");
11 return INT_MIN;
12 }
13 }

Agora que temos implementadas as operaes bsicas de insero e remoo, ou de empi-


lhar e desempilhar, em uma pilha, podemos usar essas funes em aplicaes que necessitam
dessa estrutura de dados. Diversas so as aplicaes que necessitam de uma ou mais pilhas,
dentre as quais podemos citar a converso de notao de expresses aritmticas (notao infixa
para notao posfixa), a pilha de execuo de um programa no sistema operacional, a anlise
lxica de um programa realizada pelo compilador, etc.
Uma aplicao simples e interessante apresentada a seguir. Considere, por exemplo, o
problema de decidir se uma dada seqncia de parnteses e chaves bem-formada. Por exem-
plo, a seqncia abaixo:

( ( ) { ( ) } )

bem-formada, enquanto que a a seqncia

( { ) }

malformada.
Suponha que a seqncia de parnteses e chaves est armazenada em uma cadeia de carac-
teres s . A funo bemformada a seguir recebe a cadeia de caracteres s e devolve 1 se s
contm uma seqncia bem-formada de parnteses e chaves e devolve 0 se a seqncia est
malformada.

FACOM UFMS
58.3 O PERAES BSICAS EM ALOCAO ENCADEADA 409

1 int bemformada(char s[])


2 {
3 char *p;
4 int t;
5 int n, i;
6 n = strlen(s);
7 p = (char *) malloc(n * sizeof(char));
8 t = -1;
9 for (i = 0; s[i] != \0; i++) {
10 switch (s[i]) {
11 case ):
12 if (t != -1 && p[t] == ()
13 t--;
14 else
15 return 0;
16 break;
17 case }:
18 if (t != -1 && p[t] == {)
19 t--;
20 else
21 return 0;
22 break;
23 default:
24 t++;
25 p[t] = s[i];
26 }
27 }
28 free(p);
29 return t == -1;
30 }

58.3 Operaes bsicas em alocao encadeada

As pilhas tambm podem ser implementadas em alocao encadeada. Existem muitas apli-
caes prticas onde as pilhas implementadas em alocao encadeada so importantes e bas-
tante usadas.
Consideramos que as clulas da pilha so do tipo declarado abaixo:

typedef struct cel {


int conteudo;
struct cel *prox;
} celula;

FACOM UFMS
58.3 O PERAES BSICAS EM ALOCAO ENCADEADA 410

Uma pilha vazia pode ser criada da seguinte forma:

celula *topo;
topo = NULL;

A operao de empilhar uma nova chave x em uma pilha t descrita na funo a seguir.

1 void empilhaEnc(celula **t, int x)


2 {
3 celula *nova;
4 nova = (celula *) malloc(sizeof(celula));
5 if (nova != NULL) {
6 nova->conteudo = x;
7 nova->prox = *t;
8 *t = nova;
9 }
10 else {
11 printf("Impossvel inserir um novo elemento na pilha\n");
12 exit(EXIT_FAILURE);
13 }
14 }

Suponha agora que queremos remover um elemento do topo de uma pilha t . A operao
de desempilhar um registro descrita na funo a seguir.

1 int desempilhaEnc(celula **t)


2 {
3 int x;
4 celula *aux;
5 if (*t != NULL) {
6 aux = *t;
7 x = (*t)->conteudo;
8 *t = (*t)->prox;
9 free(aux);
10 return x;
11 }
12 else {
13 printf("Impossvel remover um elemento da pilha\n");
14 return INT_MIN;
15 }
16 }

FACOM UFMS
58.3 O PERAES BSICAS EM ALOCAO ENCADEADA 411

Exerccios

58.1 Reescreva a funo bemformada armazenando a pilha em uma lista linear em alocao
encadeada.

58.2 Uma palavra um palndromo se a seqncia de caracteres que a constitui a mesma


quer seja lida da esquerda para a direita ou da direita para a esquerda. Por exemplo, as
palavras RADAR e MIRIM so palndromos. Escreva um programa eficiente para reconhe-
cer se uma dada palavra palndromo.

58.3 Suponha que exista um nico vetor M de registros de um tipo pilha pr-definido, com um
total de MAX posies. Este vetor far o papel da memria do computador. Este vetor M
ser compartilhado por duas pilhas em alocao seqencial. Implemente eficientemente
as operaes de empilhamento e desempilhamento para as duas pilhas de modo que
nenhuma das pilhas estoure sua capacidade de armazenamento, a menos que o total de
elementos em ambas as pilhas seja MAX.

58.4 Um estacionamento possui um nico corredor que permite dispor 10 carros. Existe so-
mente uma nica entrada/sada do estacionamento em um dos extremos do corredor. Se
um cliente quer retirar um carro que no est prximo sada, todos os carros impedindo
sua passagem so retirados, o cliente retira seu carro e os outros carros so recolocados
na mesma ordem que estavam originalmente. Escreva um algoritmo que processa o fluxo
de chegada/sada deste estacionamento. Cada entrada para o algoritmo contm uma le-
tra E para entrada ou S para sada, e o nmero da placa do carro. Considere que os
carros chegam e saem pela ordem especificada na entrada. O algoritmo deve imprimir
uma mensagem sempre que um carro chega ou sai. Quando um carro chega, a mensagem
deve especificar se existe ou no vaga para o carro no estacionamento. Se no existe vaga,
o carro no entra no estacionamento e vai embora. Quando um carro sai do estaciona-
mento, a mensagem deve incluir o nmero de vezes que o carro foi movimentado para
fora da garagem, para permitir que outros carros pudessem sair.

FACOM UFMS
A ULA 59

F ILAS

Uma fila , assim como uma pilha, uma lista linear especial, onde h uma poltica de inser-
es e remoes bem definida. Em uma fila, uma insero realizada em um dos extremos da
fila e a remoo no outro extremo. Em uma pilha, como vimos na aula passada, a insero e
a remoo so realizadas em um mesmo extremo. Nesta aula veremos como implementar as
operaes bsicas sobre uma fila, considerando sua implementao em alocao seqencial e
encadeada.

59.1 Definio

Uma fila uma lista linear tal que as operaes de insero so realizadas em um dos
extremos da lista e a remoo realizada no outro extremo. Uma ilustrao de uma fila
mostrada na figura 59.1. O funcionamento dessa estrutura pode ser comparado a qualquer fila
de objetos que usamos com freqncia como, por exemplo, uma fila de um banco. Em geral,
as pessoas entram, ou so inseridas, no final da fila e as pessoas saem, ou so removidas, do
incio da fila.
0 1 2 3 MAX-1
F 3 1 8

i f

Figura 59.1: Representao de uma fila F em alocao seqencial.

Note que uma fila tem sempre um indicador de onde comea e de onde termina. Quando
queremos inserir um elemento na fila, esta operao realizada no final da estrutura. Quando
queremos remover um elemento da fila, essa operao realizada no incio da estrutura. Esses
extremos so referenciados como inicio e fim de uma fila F . Se a fila F implementada
em alocao seqencial, isto , sobre um vetor, ento esses extremos so ndices desse vetor.
Caso contrrio, se for implementada em alocao encadeada, ento esses extremos so aponta-
dores para o primeiro e o ltimo registros da fila.
Assim como nas pilhas, duas operaes bsicas so realizadas sobre uma fila: insero e
remoo. Essas duas operaes so tambm chamadas de enfileiramento e desenfileiramento
de elementos. Observe que a operao de busca no foi mencionada e no faz parte do conjunto
de operaes bsicas de uma fila.

412
59.2 O PERAES BSICAS EM ALOCAO SEQENCIAL 413

59.2 Operaes bsicas em alocao seqencial

Uma fila est armazenada em um segmento F[i..f] de um vetor F[0..MAX-1] . Assim,


devemos ter 0 6 i 6 f 6 MAX 1. O primeiro elemento da fila est na posio i e o ltimo
na posio f . A fila est vazia se i = f = 1 e cheia se f + 1 mod MAX = i .
Suponha que queremos inserir um elemento de chave 7 na fila F da figura 59.1. Como
resultado desta operao, esperamos que a fila tenha a configurao mostrada na figura 59.2
aps sua execuo.

0 1 2 3 MAX-1
F 3 1 8 7

i f

Figura 59.2: Insero da chave 7 na fila F da figura 59.1.

Observe que o ndice i move-se somente quando da remoo de um registro da fila e


o ndice f move-se somente quando da insero de um registro na fila. Esses incrementos
fazem com que a fila movimente-se da esquerda para direita, o que pode ocasionar a falsa
impresso de transbordamento de memria. Consideramos ento os registros alocados seqen-
cialmente como se estivessem em um crculo, onde o compartimento F[MAX-1] seguido pelo
compartimento F[0] .
A operao de enfileirar um novo nmero inteiro x em uma fila F descrita a seguir.

1 void enfileiraSeq(int F[], int *i, int *f, int x)


2 {
3 int aux;
4 aux = (*f + 1) % MAX;
5 if (aux != *i) {
6 *f = aux;
7 F[*f] = x;
8 if (*i == -1)
9 *i = 0;
10 }
11 else
12 printf("Impossvel inserir um novo elemento na fila\n");
13 }

A varivel aux um ndice temporrio para o final da fila. Se o ndice armazenado em


aux igual ao ndice *i , ento no h mais espao disponvel na fila. Caso contrrio, o
ndice *f atualizado com esse valor e o novo elemento colocado no final da fila.
Suponha agora que queremos remover um elemento da fila F que aparece na figura 59.1.
Como resultado desta operao, esperamos que a fila tenha a configurao mostrada na fi-
gura 59.3 aps sua execuo.

FACOM UFMS
59.2 O PERAES BSICAS EM ALOCAO SEQENCIAL 414

0 1 2 3 MAX-1

F 1 8

i f

Figura 59.3: Remoo de um elemento da fila F da figura 59.1.

A operao de desenfileirar um nmero inteiro em uma fila F descrita a seguir.

1 int desenfileiraSeq(int F[], int *i, int *f)


2 {
3 int removido;
4 removido = INT_MIN;
5 if (*i != -1) {
6 removido = F[*i];
7 if (*i != *f)
8 *i = (*i + 1) % MAX;
9 else {
10 *i = -1;
11 *f = -1;
12 }
13 }
14 else
15 printf("Impossvel remover um elemento da fila\n");
16 return removido;
17 }

Como um exemplo de aplicao, suponha que temos n cidades numeradas de 0 a n 1 e


interligadas por estradas de mo nica. As ligaes entre as cidades so representadas por uma
matriz A definida da seguinte forma: A[x][y] vale 1 se existe estrada da cidade x para a
cidade y e vale 0 em caso contrrio. A figura 59.4 ilustra um exemplo. A distncia de uma
cidade o a uma cidade x o menor nmero de estradas que preciso percorrer para ir de o
a x . Estabelecemos ento o seguinte problema: determinar a distncia de uma dada cidade o
a cada uma das outras cidades da rede. As distncias so armazenadas em um vetor d de tal
modo que d[x] seja a distncia de o a x . Se for impossvel chegar de o a x , podemos dizer
que d[x] vale . Usaremos ainda 1 para representar uma vez que nenhuma distncia
real pode ter valor 1.
O programa 59.1 usa uma fila para solucionar o problema das distncias em uma rede. O
programa classifica as cidades durante sua execuo, da seguinte forma: uma cidade consi-
derada ativa se j foi visitada mas as estradas que nela comeam ainda no forma exploradas.
As cidades ativas so mantidas numa fila pelo programa. Em cada iterao, o programa re-
move da fila uma cidade x e insere na fila todas as cidades vizinhas a x que ainda no foram
visitadas.

FACOM UFMS
59.3 O PERAES BSICAS EM ALOCAO ENCADEADA 415

0 1 2 3 4 5
0 0 1 0 0 0 0 5 0 4
1 0 0 1 0 0 0 0 1 2 3 4 5
2 0 0 0 0 1 0 d 2 3 1 0 1 6
3 0 0 1 0 1 0
4 1 0 0 0 0 0
5 0 1 0 0 0 0 1 2 3

Figura 59.4: A matriz representa cidades 0, . . . , 5 interligadas por estradas de mo nica. O


vetor d d as distncias da cidade 3 a cada uma das demais.

Programa 59.1: Exemplo de uma funo que usa uma fila.

1 int *distancias(int A[MAX][MAX], int n, int o)


2 {
3 int *d, x, y;
4 int *F, i, f;
5 d = (int *) malloc(n * sizeof(int));
6 for (x = 0; x < n; x++)
7 d[x] = -1;
8 d[o] = 0;
9 F = (int *) malloc(n * sizeof(int));
10 i = 0;
11 f = 0;
12 F[i] = o;
13 while (i <= f) {
14 x = F[i];
15 i++;
16 for (y = 0; y < n; y++)
17 if (A[x][y] == 1 && d[y] == -1) {
18 d[y] = d[x] + 1;
19 f++;
20 F[f] = y;
21 }
22 }
23 free(F);
24 return d;
25 }

59.3 Operaes bsicas em alocao encadeada

A ttulo de recordao, lembramos que uma fila uma lista linear tal que suas operaes de
insero so realizadas em um dos extremos da lista e as operaes de remoo so realizadas
no extremo oposto. Observe ento que uma fila tem sempre dois indicadores/marcadores da
posio onde comea e da posio onde termina. A poltica de armazenamento de elementos

FACOM UFMS
59.3 O PERAES BSICAS EM ALOCAO ENCADEADA 416

nas clulas de uma fila estabelece que quando queremos inserir um elemento em uma fila,
essa operao sempre realizada no extremo identificado como o final dessa estrutura. Por
outro lado, quando queremos remover um elemento da fila, essa operao sempre realizada
no extremo identificado como incio da estrutura. Em alocao encadeada, esses extremos so
referenciados atravs de apontadores para clulas da fila, que contm o endereo das suas
primeira e ltima clulas.
Assim como nas pilhas, apenas duas operaes bsicas so realizadas sobre as filas: insero
e remoo. Essas duas operaes so tambm chamadas de enfileiramento e desefileiramento
de elementos. Observe que a operao bsica de busca, que especificamos nas listas lineares,
conforme a aula 57, no foi mencionada e tambm no faz parte do conjunto de operaes
bsicas de uma fila.
Uma fila em alocao encadeada dita vazia se e somente se ambos seus apontadores de
incio e fim contm o valor nulo ( NULL ). Dessa forma, podemos criar facilmente uma fila vazia
como especificado abaixo:

celula *ini, *fim;


ini = NULL;
fim = NULL;

A operao de enfileirar um novo elemento x em uma fila dada atravs da funo des-
crita abaixo.

1 void enfileiraEnc(celula **comeco, celula **final, int x)


2 {
3 celula *nova;
4 nova = (celula *) malloc(sizeof(celula));
5 if (nova != NULL) {
6 nova->conteudo = x;
7 nova->prox = NULL;
8 if (*final != NULL)
9 (*final)->prox = nova;
10 else
11 *comeco = nova;
12 *final = nova;
13 }
14 else
15 printf("Impossvel inserir um novo elemento na fila\n");
16 }

Suponha agora que queremos desenfileirar um elemento de uma fila identificada pelos
apontadores *comeco e *final . A operao de desenfileirar uma clula dessa fila des-
crita a seguir.

FACOM UFMS
59.3 O PERAES BSICAS EM ALOCAO ENCADEADA 417

1 int desenfileiraEnc(celula **comeco, celula **final)


2 {
3 int x;
4 celula *p;
5 if (*comeco != NULL) {
6 p = *comeco;
7 x = p->conteudo;
8 *comeco = p->prox;
9 free(p);
10 if (*comeco == NULL)
11 *final = NULL;
12 return x;
13 }
14 else {
15 printf("Impossvel remover um elemento da fila\n");
16 return INT_MIN;
17 }
18 }

Exerccios

59.1 Solucione o problema das distncias em uma rede usando uma fila em alocao encade-
ada.

59.2 Implemente uma fila usando duas pilhas.

59.3 Implemente uma pilha usando duas filas.

59.4 Um estacionamento possui um nico corredor que permite dispor 10 carros. Os carros
chegam pelo sul do estacionamento e saem pelo norte. Se um cliente quer retirar um
carro que no est prximo do extremo norte, todos os carros impedindo sua passagem
so retirados, o cliente retira seu carro e os outros carros so recolocados na mesma or-
dem que estavam originalmente. Sempre que um carro sai, todos os carros do sul so
movidos para frente, de modo que as vagas fiquem disponveis sempre no extremo sul
do estacionamento. Escreva um algoritmo que processa o fluxo de chegada/sada deste
estacionamento. Cada entrada para o algoritmo contm uma letra E para entrada ou S
para sada, e o nmero da placa do carro. Considere que os carros chegam e saem pela
ordem especificada na entrada. O algoritmo deve imprimir uma mensagem sempre que
um carro chega ou sai. Quando um carro chega, a mensagem deve especificar se existe
ou no vaga para o carro no estacionamento. Se no existe vaga, o carro deve esperar
at que exista uma vaga, ou at que uma instruo fornecida pelo usurio indique que
o carro deve partir sem que entre no estacionamento. Quando uma vaga torna-se dis-
ponvel, outra mensagem deve ser impressa. Quando um carro sai do estacionamento, a
mensagem deve incluir o nmero de vezes que o carro foi movimentado dentro da gara-
gem, incluindo a sada mas no a chegada. Este nmero 0 se o carro partiu da linha de
espera, isto , se o carro esperava uma vaga, mas partiu sem entrar no estacionamento.

FACOM UFMS
59.3 O PERAES BSICAS EM ALOCAO ENCADEADA 418

59.5 Imagine um tabuleiro quadrado 10por10. As casas livres so marcadas com 0 e as


casas bloqueadas so marcadas com 1. As casas (1, 1) e (10, 10) esto livres. Ajude
uma formiga que est na casa (1, 1) a chegar casa (10, 10). Em cada passo, a formiga s
pode se deslocar para uma casa livre que esteja direita, esquerda, acima ou abaixo da
casa em que est.

59.6 Um deque uma lista linear que permite a insero e a remoo de elementos em ambos
os seus extremos. Escreva quatro funes para manipular um deque: uma que realiza
a insero de um novo elemento no incio do deque, uma que realiza a insero de um
novo elemento no fim do deque, uma que realiza a remoo de um elemento no incio do
deque e uma que realiza a remoo de um elemento no fim do deque.

FACOM UFMS
A ULA 60

L ISTAS LINEARES CIRCULARES

Algumas operaes bsicas em listas lineares, em especial aquelas implementadas em alo-


cao encadeada, no so muito eficientes. Um exemplo emblemtico desse tipo de operao
a busca, como pudemos perceber em aulas anteriores. As listas lineares circulares realizam
algumas operaes mais eficientemente, j que possuem uma informao a mais que as listas
lineares ordinrias.
Uma lista linear circular , em geral, implementada em alocao encadeada e difere de uma
lista linear por no conter um apontador para nulo em qualquer de seus registros, o que no
permite que se identifique um fim, ou mesmo um comeo, da lista. Caso um registro especial,
chamado cabea da lista, faa parte da sua estrutura, ento possvel identificar facilmente um
incio e um fim da lista linear. Nesta aula veremos a implementao das operaes bsicas de
busca, insero e remoo em listas lineares circulares em alocao encadeada.

60.1 Alocao encadeada

A adio de uma informao na estrutura de uma lista linear em alocao encadeada per-
mite que operaes sejam realizadas de forma mais eficiente. Quando o ltimo registro da lista
linear encadeada aponta para o primeiro registro, temos uma lista linear circular. Dessa forma,
no podemos identificar um fim da lista linear. No entanto, o que aparenta uma dificuldade ,
na verdade, uma virtude desse tipo de estrutura, como veremos nas funes que implementam
suas operaes bsicas de busca, insero e remoo. Veja a figura 60.1 para um exemplo de
uma lista linear circular em alocao encadeada.
p

1 7 6 5 3 4

Figura 60.1: Representao de uma lista linear circular em alocao encadeada.

A definio de um tipo clula da lista linear circular permanece a mesma, como mostramos
abaixo:

419
60.2 B USCA 420

typedef struct cel {


int conteudo;
struct cel *prox;
} celula;

A seguir vamos discutir e implementar as operaes bsicas de busca, insero e remoo


sobre listas lineares circulares em alocao encadeada. importante observar que a implemen-
tao dessas listas tornam-se mais fceis se usamos o recurso de inserir uma clula especial no
incio da lista, chamada de cabea.

60.2 Busca

No processo de busca, dada uma lista linear circular e um valor, queremos saber se o valor
est presente na lista. A funo buscaCirc recebe um apontador circ para a lista linear
circular e um valor x e devolve uma clula contendo o valor procurado x , caso x ocorra em
na lista. Caso contrrio, a funo devolve NULL .

1 celula *buscaCirc(celula *circ, int x)


2 {
3 celula *p;
4 if (circ != NULL) {
5 p = circ->prox;
6 while (p != circ) {
7 if (p->conteudo == x)
8 return p;
9 p = p->prox;
10 }
11 if (p->conteudo == x)
12 return p;
13 }
14 return NULL;
15 }

60.3 Insero

A insero em uma lista linear circular encadeada simples e semelhante insero em uma
lista linear encadeada. Temos apenas de prestar ateno para que a insero sempre mantenha
a propriedade da lista linear ser circular, o que se d de modo natural se inserimos o novo
elemento logo em seguida clula referenciada pelo apontador da lista.

FACOM UFMS
60.4 R EMOO 421

1 void insereCirc(celula **circ, int x)


2 {
3 celula *nova;
4 if (buscaCirc(*circ, x) == NULL) {
5 nova = (celula *) malloc(sizeof(celula));
6 nova->conteudo = x;
7 if (*circ != NULL) {
8 nova->prox = (*circ)->prox;
9 (*circ)->prox = nova;
10 }
11 else {
12 nova->prox = nova;
13 *circ = nova;
14 }
15 }
16 else
17 printf("Valor j se encontra na lista!\n");
18 }

60.4 Remoo

A funo de remoo de um elemento em uma lista linear circular necessita de uma mo-
dificao na funo de busca, de modo a devolver implicitamente dois apontadores, um para
a clula contendo o elemento procurado e outro para a clula imediatamente anterior. A fun-
o buscaCirc2 uma modificao das funes buscaCirc e buscaSeq2 e deixada como
exerccio.

1 void removeCirc(celula **circ, int x)


2 {
3 celula *p, *ant;
4 buscaCirc2(*circ, &p, &ant, x);
5 if (p != NULL) {
6 if (p != ant) {
7 ant->prox = p->prox;
8 if (p == *circ)
9 *circ = p->prox;
10 }
11 else
12 *circ = NULL;
13 free(p);
14 }
15 else
16 printf("Elemento no encontrado!\n");
17 }

FACOM UFMS
60.4 R EMOO 422

Exerccios

60.1 Escreva a funo buscaCirc2 , que receba uma lista linear circular e um valor x e de-
volva dois apontadores ant e p que apontam para a clula anterior a clula que contm
x , respectivamente.

60.2 Suponha que queremos implementar uma lista linear circular em alocao encadeada de
tal forma que os contedos sempre permaneam em ordem crescente. Escreva as funes
para as operaes bsicas de busca, insero e remoo para listas lineares circulares em
alocao encadeada com os contedos ordenados.

60.3 Tambm para listas lineares circulares vale que as funes que implementam suas opera-
es bsicas so mais simples e eficientes se mantemos uma clula que indica seu incio,
chamada de cabea. Veja a figura 60.2.
Escreva as funes para as operaes bsicas de busca, insero e remoo para listas
lineares circulares em alocao encadeada com cabea.

(a)

4 3 5

(b)

Figura 60.2: Lista linear circular em alocao encadeada com cabea. (a) Lista vazia. (b) Lista
com trs clulas.

60.4 O problema de Josephus foi assim descrito atravs do relato de Flavius Josephus, um
historiador judeu que viveu no primeiro sculo, sobre o cerco de Yodfat. Ele e mais 40
soldados aliados estavam encurralados em uma caverna rodeada por soldados romanos
e, no havendo sada, optaram ento pelo suicdio antes da captura. Decidiram que for-
mariam um crculo e, a cada contagem de 3, um soldado seria morto. Josephus foi o
ltimo a se salvar.

FACOM UFMS
60.4 R EMOO 423

Podemos agora, descrever um problema mais geral como segue. Imagine n pessoas dis-
postas em crculo. Suponha que as pessoas so numeradas de 1 a n no sentido horrio.
Comeando com a pessoa de nmero 1, percorra o crculo no sentido horrio e elimine
cada m-sima pessoa enquanto o crculo tiver duas ou mais pessoas. Escreva e teste uma
funo que resolva o problema, imprimindo na sada o nmero do sobrevivente.

FACOM UFMS
A ULA 61

L ISTAS LINEARES DUPLAMENTE


ENCADEADAS

Quando trabalhamos com listas lineares (simplesmente) encadeadas, muitas vezes precisa-
mos de manter um apontador para um registro e tambm para o registro anterior para poder
realizar algumas operaes evitando percursos adicionais desnecessrios. No entanto, algumas
vezes isso no suficiente, j que podemos precisar percorrer a lista linear nos dois sentidos.
Neste caso, alm do campo apontador para o prximo registro no registro base da lista, adi-
cionamos um novo campo apontador para o registro anterior. O gasto de memria imposto
pela adio deste novo campo se justifica pela economia em no ter de reprocessar a lista linear
inteira.

61.1 Definio

Os registros de uma lista linear duplamente encadeada so ligados por apontadores que
indicam a posio do registro anterior e do prximo registro da lista. Assim, um outro campo
acrescentado a cada registro da lista indicando, alm do endereo do prximo registro, o
endereo do registro anterior da lista. Veja a figura 61.1.
conteudo

ant prox

Figura 61.1: Representao de uma clula de uma lista linear duplamente encadeada.

A definio de uma clula de uma lista linear duplamente encadeada ento descrita como
um tipo, como mostramos a seguir:

typedef struct cel {


int conteudo;
struct cel *ant;
struct cel *prox;
} celula;

424
61.2 B USCA 425

Uma representao grfica de uma lista linear em alocao encadeada mostrada na fi-
gura 61.2.

lista_d

l1 l2 ln

Figura 61.2: Representao de uma lista linear duplamente encadeada apontada por
lista_dup. A primeira clula tem seu campo ant apontando para NULL e a ltima tem seu
campo prox tambm apontando para NULL.

Uma lista linear duplamente encadeada pode ser criada e inicializada de forma idntica a
de uma lista linear, como mostramos a seguir:

celula *lista_d;
lista_d = NULL;

A seguir vamos discutir e implementar as operaes bsicas sobre listas lineares dupla-
mente encadeadas.

61.2 Busca

No processo de busca de um valor em uma lista linear duplamente encadeada, dada uma
lista linear duplamente encadeada dup e um valor x e queremos saber se x est presente em
dup . Uma funo que realiza esse processo pode ser descrita como a seguir.

A funo buscaDup recebe um apontador dup para uma lista duplamente encadeada e
um valor x e devolve um registro contendo o valor procurado x , caso x ocorra em dup .
Caso contrrio, a funo devolve NULL .

1 celula *buscaDup(celula *dup, int x)


2 {
3 celula *p;
4 p = dup;
5 while (p != NULL && p->conteudo != x)
6 p = p->prox;
7 return p;
8 }

Observe que a funo buscaDup praticamente uma cpia da funo buscaEnc .

FACOM UFMS
61.3 I NSERO 426

61.3 Insero

A funo que insere um elemento x em uma lista linear duplamente encadeada *dup
apresentada a seguir. A insero continua sendo feita no incio da lista.

1 void insereDup(celula **dup, int x)


2 {
3 celula *nova;
4 if (buscaDup(*dup, x) == NULL) {
5 nova = (celula *) malloc(sizeof(celula));
6 if (nova != NULL) {
7 nova->conteudo = x;
8 nova->ant = NULL;
9 nova->prox = *dup;
10 if (*dup != NULL)
11 (*dup)->ant = nova;
12 *dup = nova;
13 }
14 else {
15 printf("No h memria disponvel!\n");
16 exit(EXIT_FAILURE);
17 }
18 }
19 else
20 printf("Elemento j se encontra na lista!\n");
21 }

Observe novamente que as inseres so sempre realizadas no incio da lista linear dupla-
mente encadeada. Podemos modificar essa comportamento, por exemplo, mantendo um outro
apontador para o final da lista e fazendo as inseres neste outro extremo. Deixamos essa idia
para ser implementada em um exerccio.

61.4 Remoo

Como sabemos, para realizar a remoo de um elemento da lista necessrio busc-lo. A


funo buscaDup descrita anteriormente devolve um apontador para o elemento onde o valor
procurado se encontra ou NULL caso o valor no ocorra na lista. Se a busca tem sucesso, ento
o apontador devolvido aponta para a clula que se quer remover. Alm disso, essa clula
tem acesso clula anterior e posterior da lista duplamente encadeada e isso tudo o que
precisamos saber para realizar a remoo satisfatoriamente. A funo removeDup recebe como
parmetros um apontador *dup para o incio da lista duplamente encadeada e um valor x ,
busca uma clula na lista que contm esse valor e, caso esse valor ocorra na lista, realiza sua
remoo.

FACOM UFMS
61.4 R EMOO 427

1 void removeDup(celula **dup, int x)


2 {
3 celula *anterior, *posterior, *removido;
4 removido = buscaDup(*dup, x);
5 if (removido != NULL) {
6 anterior = removido->ant;
7 posterior = removido->prox;
8 if (anterior != NULL)
9 anterior->prox = posterior;
10 else
11 *dup = posterior;
12 if (posterior != NULL)
13 posterior->ant = anterior;
14 free(removido);
15 }
16 else
17 printf("Valor no se encontra na lista!\n");
18 }

Observe que os apontadores anterior , posterior e removido so variveis locais


funo removeDup que auxiliam tanto na busca como na remoo propriamente. Alm disso,
se a clula a ser removida a primeira da lista, h necessidade de alterar o contedo do apon-
tador para o incio da lista dup para a clula seguinte desta lista.
Observe ainda que o procedimento removeDup pode ser usado para remoo de uma c-
lula em uma lista linear encadeada ordenada ou no ordenada.

Exerccios

61.1 Considere uma lista linear duplamente encadeada contendo os elementos l1 , l2 , . . . , ln ,


como na figura 61.3.

lista_d

l1 l2 ln

Figura 61.3: Uma lista linear duplamente encadeada.

Escreva uma funo que altere os apontadores ant e prox da lista, sem mover suas
informaes, tal que a lista fique invertida como na figura 61.4.

FACOM UFMS
61.4 R EMOO 428

lista_d

ln ln1 l1

Figura 61.4: Inverso da lista linear duplamente encadeada da figura 61.3.

61.2 Escreva uma funo que receba um apontador para o incio de uma lista linear em aloca-
o duplamente encadeada, um apontador para o fim dessa lista e um valor, e realize a
insero desse valor no final da lista.

61.3 Suponha que voc queira manter uma lista linear duplamente encadeada com os con-
tedos em ordem crescente. Escreva as funes de busca, insero e remoo para essa
lista.

FACOM UFMS
R EFERNCIAS B IBLIOGRFICAS

[1] P. Breton. Histria da Informtica. Editora da UNESP, 1991. Traduo de Elcio Fernandes
do original Histoire de Linformatique, 1987, Editions La Decouvert. 1
[2] Computer history from B.C. to today.
http://www.computerhope.com/history/.
ltimo acesso em 28 de janeiro de 2009. 1
[3] R. X. Cringely. A history of the computer.
http://www.pbs.org/nerds/timeline/index.html.
ltimo acesso em 28 de janeiro de 2009. 1
[4] P. Feofiloff. Algoritmos em linguagem C. Editora Campus/Elsevier, 2009. 4.5, 4.5, 7.3, 17, 18,
20.2
[5] B. W. Kernighan and R. Pike. The Practice of Programming. Addison-Wesley Professional,
1999.
[6] B. W. Kernighan and D. M. Ritchie. C Programming Language. Prentice Hall, 2nd edition,
1988.
[7] K. N. King. C Programming A Modern Approach. W. W. Norton & Company, Inc., 2nd
edition, 2008. 17, 18
[8] S. G. Kochan. Unix Shell Programming. Sams Publishing, 3rd edition, 2003.
[9] J. Kopplin. An illustrated history of computers.
http://www.computersciencelab.com/ComputerHistory/History.htm.
ltimo acesso em 28 de janeiro de 2009. 1
[10] T. Kowaltowski. John von Neumann: suas contribuies computao. Revista de Estudos
Avanados, 10(26), Jan/Abr 1996. Instituto de Estudos Avanados da Universidade de So
Paulo. 2
[11] H. Lukoff. From Dits to Bits... a Personal History of the Electronic Computer. Hawley Books,
1979. 2.1
[12] Departamento de Cincia da Computao IME/USP, listas de exerccios Introduo
Computao. http://www.ime.usp.br/~macmulti/.
ltimo acesso em 28 de janeiro de 2009. 2
[13] A. Sapounov, E. Rosen, and J. Shaw. Computer history museum.
http://www.computerhistory.org/.
ltimo acesso em 28 de janeiro de 2009. 1

429
REFERNCIAS BIBLIOGRFICAS 430

[14] R. L. Shackelford. Introduction to Computing and Algorithms. Addison Wesley, 1997. 1

[15] S. S. Skiena and M. Revilla. Programming Challenges. Springer, 2003.

[16] A. M. Turing. On computable numbers, with an application to the Entscheidungsproblem.


Proceedings of the London Mathematical Society, 42(2):230265, 1936. 2.1, 2.4

[17] J. von Neumann. First draft of a report on the EDVAC. Technical report, Moore School of
Electrical Engineering University of Pennsylvania, 1945. 2.1, 2.3

[18] Wikipedia the free encyclopedia.


http://en.wikipedia.org/wiki/Main_Page.
ltimo acesso em 28 de janeiro de 2009. 1

[19] K. Zuse. Verfahren zur selbstttigen durchfhrung von rechnungen mit hilfe von rechen-
maschinen. Patentanmeldung Z 23 139 GMD Nr. 005/021, 1936. 2.1

FACOM UFMS

Vous aimerez peut-être aussi