Sei sulla pagina 1di 104

Apostila da Disciplina:

Linguagem C para Engenharia (ENG 03049) [UFRGS 2011]

Linguagem C para Engenharia


(ENG 03049)

Bardo E.J. Bodmann

Vers ao 4.3 (24 de outubro de 2011) Escola de Engenharia EE Universidade Federal do Rio Grande do Sul UFRGS

CONTEUDO

Conte udo
1 Introdu c ao 1.1 ANSI C e ISO C . . . . . . . . . 1.1.1 C99 . . . . . . . . . . . . 1.1.2 C1X . . . . . . . . . . . . 1.1.3 Comment ario geral . . . . 1.1.4 Rela co es com C++ . . . . 1.2 Gloss ario . . . . . . . . . . . . . 1.2.1 Programa ca o estruturada 1.2.2 Programa ca o imperativa . 1.2.3 Programa ca o procedural . 1.2.4 Programa ca o gen erica . . 1.3 Programa da disciplina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 2 2 4 4 4 4 4 5 6 8 8 8 8 8 9 10 10 10 10 10 10 11 11 12 12 13 14 14 14 15 17 17 17 18 18 19 19 21 21 21 21 23 23 23 23 23 24 25 25 26 26 26

2 Programa c ao com C 2.1 Cria ca o, comila ca o e execu ca o . . . . . . . . . . . . 2.1.1 Fase 1: Cria ca o do programa . . . . . . . . 2.1.2 Fase 2: Compila ca o . . . . . . . . . . . . . 2.1.3 Fase 3: Execu ca o do programa . . . . . . . 2.2 O modelo de compila ca o da linguagem C . . . . . . 2.2.1 O pr e-processador . . . . . . . . . . . . . . 2.2.2 O compilador . . . . . . . . . . . . . . . . . 2.2.3 O assembler . . . . . . . . . . . . . . . . . . 2.2.4 O linker . . . . . . . . . . . . . . . . . . . . 2.2.5 A utiliza ca o de bibliotecas . . . . . . . . . . 2.2.6 Como conseguir uma c opia do compilador? 2.3 Diferencia ca o de letras minusculas e maiusculas em 2.4 A estrutura basica de um programa . . . . . . . . 2.5 Introdu ca o ` as fun co es . . . . . . . . . . . . . . . . 2.5.1 Argumentos de fun co es . . . . . . . . . . . 2.5.2 Retorno de valores de uma fun ca o . . . . . 2.5.3 Syntaxe geral de uma fun ca o . . . . . . . . 2.6 A utiliza ca o simples de entradas e sa das . . . . . . 2.6.1 O tipo de vari aveis: Caracteres . . . . . . . 2.6.2 O tipo de dados: Strings . . . . . . . . . . . 2.6.3 A fun ca o de sa da padr ao: printf . . . . . . 2.6.4 A fun ca o de entrada padr ao: scanf . . . . . 2.7 Introdu ca o a controle de uxo: condi co es e la cos . 2.7.1 A condi ca o if . . . . . . . . . . . . . . . . . 2.7.2 O la co indexado com for . . . . . . . . . . . 2.8 Utiliza ca o de Coment arios . . . . . . . . . . . . . . 2.9 As Palavras da linguagem ANSI C . . . . . . . . . 3 Vari aveis, constantes, operadores e express oes 3.1 Nomes de vari veis . . . . . . . . . . . . . . . . . 3.2 Os Tipos de var aveis de C . . . . . . . . . . . . . 3.3 Declara ca o e inicializa ca o de Vari aveis . . . . . . 3.4 Constantes . . . . . . . . . . . . . . . . . . . . . 3.4.1 Constantes dos tipos b sicos . . . . . . . . 3.4.2 Constantes hexadecimais e octais . . . . . 3.4.3 Constantes strings . . . . . . . . . . . . . 3.4.4 Constantes especiais (de barra invertida) 3.5 Operadores aritm eticos e de atribui ca o . . . . . . 3.6 Operadores relacionais e l ogicos . . . . . . . . . . 3.6.1 Operadores relacionais . . . . . . . . . . . 3.6.2 Operadores l ogicos bit a bit . . . . . . . . 3.7 Express oes . . . . . . . . . . . . . . . . . . . . . . 3.7.1 Convers ao de tipos em express oes . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTEUDO

II

3.8

3.7.2 Nota ca o compacta para express oes . . . . . 3.7.3 Encadeamento de express oes: o Operador , 3.7.4 As preced encias do C . . . . . . . . . . . . Modeladores: Casts . . . . . . . . . . . . . . . . . .

. . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

26 27 27 27 29 29 29 30 31 31 32 32 33 34 35 35 36 37 37 37 40 40 40 41 42 42 42 43 43 43 44 45 45 45 46 46 46 48 48 49 50 50 51 51 51 52 53 53 53 54 55 56 57 57 57 58

4 Estruturas de controle de uxo 4.1 O Comando if . . . . . . . . . . . . . . . . . . . . . . 4.1.1 O adicional else . . . . . . . . . . . . . . . . . 4.1.2 A constru ca o if-else-if . . . . . . . . . . . . . 4.1.3 A express ao condicional . . . . . . . . . . . . 4.1.4 A constru ca o de ifs aninhados . . . . . . . . . 4.1.5 A forma compacta condicional: o operador ? 4.2 A implementa ca o de op co es: o comando switch . . . 4.3 Possibilidades de construir la cos com for . . . . . . . 4.3.1 O la co innito . . . . . . . . . . . . . . . . . 4.3.2 O la co sem conte udo . . . . . . . . . . . . . . 4.4 O la co n ao indexado utilizando while . . . . . . . . . 4.5 O la co com do . . . . . . . . . . . . . . . . . . . . . 4.6 O Comando break . . . . . . . . . . . . . . . . . . . 4.7 O Comando continue . . . . . . . . . . . . . . . . . . 4.8 R otulos e o comando goto . . . . . . . . . . . . . . . 5 Tipos de dados indexadas: matrizes e strings 5.1 Vetores . . . . . . . . . . . . . . . . . . . . . . . 5.2 Vetores de carateres: Strings . . . . . . . . . . . 5.2.1 Fun ca o de entrada gets . . . . . . . . . . 5.2.2 A fun ca o strcpy . . . . . . . . . . . . . . . 5.2.3 A fun ca o strcat . . . . . . . . . . . . . . . 5.2.4 A fun ca o strlen . . . . . . . . . . . . . . . 5.2.5 A fun ca o strcmp . . . . . . . . . . . . . . 5.3 Matrizes . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Matrizes bidimensionais . . . . . . . . . . 5.3.2 Matrizes de strings . . . . . . . . . . . . . 5.3.3 Matrizes multidimensionais . . . . . . . . 5.3.4 Inicializa ca o de vari aveis indexadas . . . . 5.3.5 Inicializa ca o sem especica co de tamanho 6 Ponteiros 6.1 A utilidade de ponteiros . . . . . . . . . . 6.2 A declara ca o e a utiliza ca o de ponteiros . 6.3 O uso de ponteiros com vetores . . . . . . 6.3.1 Vetores como ponteiros . . . . . . 6.3.2 Ponteiros como vetores . . . . . . . 6.3.3 Strings . . . . . . . . . . . . . . . . 6.3.4 Endere cos de elementos de vetores 6.3.5 Vetores de ponteiros . . . . . . . . 6.4 Inicializa ca o de ponteiros . . . . . . . . . 6.5 Encadeiar ponteiros para ponteiros . . . . 6.6 Erros comuns na utiliza ca o de ponteiros . 7 Fun co es 7.1 A estrutura de uma fun ca o . . . 7.2 Utilizando a fun ca o para retornar 7.3 Prot otipos de Fun co es . . . . . . 7.4 O tipo void . . . . . . . . . . . . 7.5 Arquivos-Cabe calhos . . . . . . . 7.6 Escopo de Vari aveis . . . . . . . 7.6.1 Vari aveis locais . . . . . . 7.6.2 Par ametros formais . . . . 7.6.3 Vari aveis globais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . velores: o comando return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTEUDO

III

7.7 7.8 7.9 7.10 7.11

Passagem de argumentos por valor e por Passando matrizes para fun co es . . . . . Os Argumentos argc e argv . . . . . . . Programa ca o recursiva . . . . . . . . . . Comentarios sobre o uso de fun co es . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

refer encia em . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

fun c oes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

58 59 60 60 61 62 62 62 62 64 64 64 64 65 66 66 66 66 66 66 66 67 67 67 68 68 69 69 70 71 71 71 72 72 73 73 73 73 74 74 75 75 76 76 77 77 77 79 79 79 79 79 80 80 80 81

8 Diretivas de compila c ao 8.1 As diretivas de compila ca o . 8.2 A Diretiva include . . . . . 8.3 As Diretivas dene e undef 8.4 As Diretivas ifdef e endif . . 8.5 A Diretiva ifndef . . . . . . 8.6 A Diretiva if . . . . . . . . . 8.7 A Diretiva else . . . . . . . 8.8 A Diretiva elif . . . . . . . .

9 Entradas e sa das padronizadas 9.1 Introdu ca o . . . . . . . . . . . . . . . . . 9.2 Ler e escrever caracteres . . . . . . . . . 9.2.1 A fun ca o getche e getch . . . . . 9.2.2 A fun ca o putchar . . . . . . . . . 9.3 Ler e escrever strings . . . . . . . . . . . 9.3.1 A fun ca o gets . . . . . . . . . . . 9.3.2 A fun ca o puts . . . . . . . . . . . 9.4 Entrada e sa da formatada . . . . . . . . 9.4.1 A fun ca o printf . . . . . . . . . . 9.4.2 A fun ca o scanf . . . . . . . . . . 9.4.3 As funcoes sprintf e sscanf . . . . 9.5 Abrir e fechar arquivos . . . . . . . . . . 9.5.1 A fun ca o fopen . . . . . . . . . . 9.5.2 A fun ca o exit . . . . . . . . . . . 9.5.3 A fun ca o fclose . . . . . . . . . . 9.6 Leitura sequencial de arquivos . . . . . . 9.6.1 A fun ca o putc . . . . . . . . . . 9.6.2 A fun ca o getc . . . . . . . . . . . 9.6.3 A fun ca o feof . . . . . . . . . . . 9.7 Fun co es elaborados de acesso a arquivos 9.7.1 Arquivos pr e-denidos . . . . . . 9.7.2 A fun ca o fgets . . . . . . . . . . 9.7.3 A fun ca o fputs . . . . . . . . . . 9.7.4 A fun ca o ferror e perror . . . . . 9.7.5 A fun ca o fread . . . . . . . . . . 9.7.6 A fun ca o fwrite . . . . . . . . . . 9.7.7 A fun ca o fseek . . . . . . . . . . 9.7.8 A fun ca o rewind . . . . . . . . . 9.7.9 A fun ca o remove . . . . . . . . . 9.8 Fluxos padr ao . . . . . . . . . . . . . . . 9.8.1 A fun ca o fprintf . . . . . . . . . 9.8.2 A fun ca o fscanf . . . . . . . . . .

10 Tipos de dados avan cados 10.1 Modicadores de Acesso . . . . . . . . . . . 10.1.1 O modicador const . . . . . . . . . 10.1.2 O modicador volatile . . . . . . . . 10.2 Especicadores de classe de armazenamento 10.2.1 O especifdicador auto . . . . . . . . 10.2.2 O especifdicador extern . . . . . . . 10.2.3 O especifdicador static . . . . . . . . 10.2.4 O especifdicador register . . . . . . .

CONTEUDO

IV

10.3 Convers ao de tipos . . . . . . . . . . . . . . . . . . . . 10.4 Modicadores de fun co es . . . . . . . . . . . . . . . . . 10.4.1 O modicador pascal . . . . . . . . . . . . . . . 10.4.2 O modicador cdecl . . . . . . . . . . . . . . . 10.4.3 O modicador interrupt . . . . . . . . . . . . . 10.5 Ponteiros para Fun co es . . . . . . . . . . . . . . . . . . 10.6 Aloca ca o din amica de mem oria . . . . . . . . . . . . . 10.6.1 A fun ca o malloc . . . . . . . . . . . . . . . . . 10.6.2 A fun ca o calloc . . . . . . . . . . . . . . . . . . 10.6.3 A fun ca o realloc . . . . . . . . . . . . . . . . . 10.6.4 A fun ca o free . . . . . . . . . . . . . . . . . . . 10.7 Aloca ca o din amica de mem oria para vetores e matrizes 10.7.1 Aloca ca o din amica de mem oria para vetores . . 10.7.2 Aloca ca o din amica de mem oria para matrizes . 11 Constru c ao de novos tipos de dados al em 11.1 Estruturas . . . . . . . . . . . . . . . . . . 11.1.1 Criando estruturas . . . . . . . . . 11.1.2 Usando estruturas . . . . . . . . . 11.1.3 Matrizes de estruturas . . . . . . . 11.1.4 Atribuindo estruturas . . . . . . . 11.1.5 Passando estruturas para fun co es . 11.1.6 Estruturas ponteiros . . . . . . . . 11.2 Uni oes . . . . . . . . . . . . . . . . . . . . 11.3 Enumera co es . . . . . . . . . . . . . . . . 11.4 O Comando sizeof . . . . . . . . . . . . . 11.5 O Comando typedef . . . . . . . . . . . . 12 Uma aplica c aoutilizando estruturas

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

81 81 82 82 82 82 83 83 84 85 85 86 86 87 89 89 89 90 91 91 92 92 93 95 95 96 97

dos padr ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1 INTRODUC AO

Introdu c ao

C e uma linguagem de programa ca o compilada de prop osito geral, estruturada, imperativa, procedural, de alto n vel, e padronizada, criada em 1972, por Dennis Ritchie, no AT&T Bell Labs, para desenvolver o sistema operacional UNIX (que foi originalmente escrito em Assembly). A linguagem C e classicada de alto n vel pela pr opria deni ca o desse tipo de linguagem. A programa ca o em linguagens de alto n vel tem como caracter stica n ao ser necess ario conhecer o processador, ao contr ario das linguagens de baixo n vel. As linguagens de baixo n vel est ao fortemente ligadas ao processador. A linguagem C permite acesso de baixo n vel com a utiliza ca o de c odigo Assembly no meio do c odigo fonte. Assim, o baixo n vel e realizado por Assembly e n ao C. Desde ent ao, espalhou-se por muitos outros sistemas, e tornou-se uma das linguagens de programa ca o mais usadas, e inuenciou muitas outras linguagens, especialmente C++, que foi originalmente desenvolvida como uma extens ao para C. O desenvolvimento inicial de C, ocorreu no AT&T Bell Labs, entre 1969 e 1973. Deu-se o nome C` a linguagem, porque muitas de suas caracter sticas derivaram da linguagem B. C foi originalmente desenvolvido, para a implementa ca o do sistema UNIX (originalmente escrito em PDP-7 Assembly, por Dennis Ritchie e Ken Thompson). Em 1973, com a adi ca o do tipo struct, C tornou-se poderoso o bastante para a maioria das partes do Kernel do UNIX, serem reescritas em C. Este foi um dos primeiros sistemas que foram implementados em uma linguagem, que n ao o Assembly, sendo exemplos anteriores, os sistemas: Multics (escrito em PL/I) e TRIPOS (escrito em BCPL). Em 1978, Brian Kernighan e Dennis Ritchie publicaram a primeira edi ca o do livro The C Programming Language. Esse livro, conhecido pelos programadores de C, como K&R, serviu durante muitos anos como uma especica ca o informal da linguagem. A vers ao da linguagem C que ele descreve e usualmente referida como K&R C. A segunda edi ca o do livro, cobriu o padr ao posterior, o ANSI C. K&R C introduziu as seguintes caracter sticas na linguagem: Biblioteca padr ao de E/S (entrada, sa da); Tipos de dado struct; Tipos de dado long int; Tipos de dado unsigned int; O operador = + foi alterado para + =, e = para = (o analisador l exico do compilador fazia confus ao entre i = +10 e i = +10. O mesmo acontecia com = ). K&R C e freqentemente considerado a parte mais b asica da linguagem, cujo suporte deve ser assegurado por um compilador C. Durante muitos anos, mesmo ap os a introdu ca o do padr ao ANSI C, K&R C foi considerado o menor denominador comum, em que programadores de C se apoiavam quando uma portabilidade m axima era desejada, j a que nem todos os compiladores eram actualizados o bastante para suportar o padr ao ANSI C. Nos anos que se seguiram ` a publica ca o do K&R C, algumas caracter sticas n ao-ociaisforam adicionadas ` a linguagem, suportadas por compiladores da AT&T e de outros vendedores. Estas inclu am: Fun co es void e tipos de dados void *; Fun co es que retornam tipos struct ou union; Campos de nome struct num espa co de nome separado para cada tipo struct; Atribui ca o a tipos de dados struct; Qualicadores const para criar um objecto s o de leitura; Biblioteca padr ao, que incorpora grande parte da funcionalidade implementada por v arios vendedores; Enumera co es; C alculos de ponto-utuante em precis ao simples (no K&R C, os c alculos intermedi arios eram feitos sempre em double, porque era mais eciente na m aquina onde a primeira implementa c ao do C foi feita).

1.1

ANSI C e ISO C

Durante os nais da d ecada de 1970, a linguagem C come cou a substituir a linguagem BASIC como a linguagem de programa ca o de microcomputadores mais usada. Durante a d ecada de 1980, foi adaptada para uso no PC IBM, e a sua popularidade come cou a aumentar signicativamente. Ao mesmo tempo, Bjarne Stroustrup, juntamente com outros nos laborat orios Bell, come cou a trabalhar num projecto onde se adicionavam constru co es de linguagens de programa ca o orientada por objectos ` a linguagem C. A linguagem que eles produziram, chamada C++, e nos dias de

1 INTRODUC AO

hoje a linguagem de programa ca o de aplica co es mais comum no sistema operativo Windows da companhia Microsoft; C permanece mais popular no mundo UNIX. Em 1983, o instituto norte-americano de padr oes (ANSI) formou um comit e, X3J11, para estabelecer uma especica ca o do padr ao da linguagem C. Ap os um processo longo e arduo, o padr ao foi completo em 1989 e raticado como ANSI X3.159-1989 Programming Language C. Esta vers ao da linguagem e freqentemente referida como ANSI C. Em 1990, o padr ao ANSI C, ap os sofrer umas modica co es menores, foi adotado pela Organiza ca o Internacional de Padr oes (ISO) como ISO/IEC 9899:1990, tamb em conhecido como C89 ou C90. Um dos objetivos do processo de padroniza ca o ANSI C foi o de produzir um sobreconjunto do K&R C, incorporando muitas das caracter sticas n ao-ociais subseq uentemente introduzidas. Entretanto, muitos programas tinham sido escritos e que n ao compilavam em certas plataformas, ou com um certo compilador, devido ao uso de bibliotecas de fun co es n ao-padr ao e ao fato de alguns compiladores n ao aderirem ao ANSI C. 1.1.1 C99

Ap os o processo da padroniza ca o ANSI, as especica co es da linguagem C permaneceram relativamente est aticas por algum tempo, enquanto que a linguagem C++ continuou a evoluir. (em 1995, a Normative Amendment 1 criou uma vers ao nova da linguagem C mas esta vers ao raramente e tida em conta.) Contudo, o padr ao foi submetido a uma revis ao nos nais da d ecada de 1990, levando ` a publica ca o da norma ISO 9899:1999 em 1999. Este padr ao e geralmente referido como C99. O padr ao foi adoptado como um padr ao ANSI em Mar co de 2000. As novas caracter sticas do C99 incluem: Fun co es em linha Remo ca o de restri co es sobre a localiza ca o da declara ca o de vari aveis (como em C++) Adi ca o de v arios tipos de dados novos, incluindo o long long int (para minimizar problemas na transi ca o de 32-bits para 64-bits), um tipo de dados boolean explicito (chamado Bool) e um tipo complex que representa n umeros complexos Vetores de dados de comprimento vari avel (o vetor pode ter um tamanho diferente a cada execu ca o de uma fun ca o, mas n ao cresce depois de criado) Suporte ocial para coment arios de uma linha iniciados por //, emprestados da linguagem C++ V arias fun co es de biblioteca novas, tais como snprintf() V arios arquivos-cabe calho novos, tais como stdint.h O interesse em suportar as caracter sticas novas de C99 parece depender muito das entidades. Apesar do GCC e v arios outros compiladores suportarem grande parte das novas caracter sticas do C99, os compiladores mantidos pela Microsoft e pela Borland suportam pouqu ssimos recursos do C99, e estas duas companhias n ao parecem estar muito interessadas em adicionar tais funcionalidades, ignorando por completo as normas internacionais. A Microsoft parece preferir dar mais enfase ao C++. 1.1.2 C1X

Em 2007, se iniciou o trabalho em antecipa ca o de outra revis ao do padr ao de C, informalmente chamada C1X. O comit e dos padr oes de C adotaram regras para limitar a ado ca o dos novos recursos que n ao haviam sido testadas pelas implementa co es existentes. 1.1.3 Comment ario geral

C e uma linguagem imperativa e procedural, para implementa ca o de sistemas. Seus pontos de design foram para ele ser compilado, fornecendo acesso de baixo n vel ` a mem oria e baixos requerimentos do hardware. Tamb em foi desenvolvido para ser uma linguagem de alto n vel, para maior reaproveitamento do c odigo. C foi u til para muitas aplica co es que foram codicadas originalmente em Assembly. Essa propriedade n ao foi acidental; a linguagem C foi criada com o objectivo principal em mente: facilitar a cria ca o de programas extensos com menos erros, recorrendo ao paradigma da programa ca o algor tmica ou procedimental, mas sobrecarregando menos o autor do compilador, cujo trabalho complica-se ao ter de realizar as caracter sticas complexas da linguagem. Para este m, a linguagem C possui as seguintes caracter sticas: Uma linguagem nuclear extremamente simples, com funcionalidades n ao-essenciais, tais como fun co es matem aticas ou manuseamento de arquivos, fornecida por um conjunto de bibliotecas de rotinas padronizada;

1 INTRODUC AO

A focaliza ca o no paradigma de programa ca o procedimental; Um sistema de tipos simples que evita v arias opera co es que n ao fazem sentido Uso de uma linguagem de pr e-processamento, o pr e-processador de C, para tarefas tais como a deni ca o de macros e a inclus ao de m ultiplos arquivos de c odigo fonte; Ponteiros d ao maior exibilidade ` a linguagem; Acesso de baixo-n vel, atrav es de inclus oes de c odigo Assembly no meio do programa C; poss Par ametros que s ao sempre passados por valor para as fun c oes e nunca por refer encia (E vel simular a passagem por refer encia com o uso de ponteiros); Deni ca o do alcance lexical de vari aveis; Estruturas de vari aveis, (structs), que permitem que dados relacionados sejam combinados e manipulados como um todo. Algumas caracter sticas u teis, que faltam em C, podem ser encontradas em outras linguagens, que incluem: Seguran ca de tipo; Coletor de lixo (mais comum em linguagens interpretadas); Vetores que crescem automaticamete; Classes ou objectos com comportamento (ver orienta ca o a objetos); Closures; Fun co es aninhadas; Programa ca o gen erica; Sobrecarga de operadores; Meta-programa ca o; Apoio nativo de multithreading e comunica ca o por rede. Apesar da lista de caracter sticas u teis que C possui n ao ser longa, isso n ao tem sido um impedimento ` a sua aceita ca o, pois isso permite que novos compiladores de C sejam escritos rapidamente para novas plataformas, e tamb em permite que o programador permane ca sempre em controle do que o programa est a a fazer. Isto e o que por v arias vezes permite o c odigo de C correr de uma forma mais eciente que muitas outras linguagens. Tipicamente, s o c odigo de assembly anado ` a m ao e que corre mais rapidamente, pois possui um controle completo da m aquina, mas avan cos na area de compiladores juntamente com uma nova complexidade nos processadores modernos permitiram que a diferen ca tenha sido rapidamente eliminada. Uma consequ encia da aceita ca o geral da linguagem C e que freq uentemente os compiladores, bibliotecas e at e int erpretes de outras linguagens de n vel maior sejam eles pr oprios implementados em C. C tem como ponto forte, a sua eci encia, e e a linguagem de programa ca o preferida para o desenvolvimento de tamb sistemas e softwares de base, apesar de tamb em ser usada para desenvolver programas de computador. E em muito usada no ensino de ci encia da computa ca o, mesmo n ao tendo sido projetada para estudantes e apresentando algumas diculdades no seu uso. Outra caracter stica importante de C, e sua proximidade do c odigo de m aquina, que permite que um projetista seja capaz de fazer algumas previs oes de como o software ir a se comportar, ao ser executado. C tem como ponto fraco, a falta de prote ca o que d a ao programador. Praticamente tudo que se expressa em um programa em C, pode ser executado, como por exemplo, pedir o vig esimo membro de um vetor com apenas dez membros. Os resultados s ao muitas vezes totalmente inesperados, e os erros, dif ceis de encontrar.

1 INTRODUC AO

1.1.4

Rela co es com C++

A linguagem de programa ca o C++ foi originalmente derivada do C para suportar programa ca o orientada a objetos. ` medida que as linguagens C e C++ foram evoluindo independentemente, a divis A ao entre as duas veio a aumentar. O padr ao C99 criou um n umero de caracter sticas que entram em conito. Hoje, as principais diferen cas entre as duas linguagens s ao: inline - em C++, fun co es em linha encontram-se no espa co global enquanto que em C encontram-se no espa co local. Por outras palavras, isso signica que, em C++, qualquer deni ca o de qualquer fun ca o em linha (sem ser a respeito da sobrecarga de fun co es de C++) tem de estar em conformidade com a regra de uma deni ca oda linguagem C++. Mas em C, a mesma fun ca o em linha pode ser denida de maneira diferente em diferentes arquivos. A palavra-chave bool, igual ` a usada em C++, em C99 necessita que se inclua o arquivo-cabe calho <stdbool.h> a sempre dispon vel). Padr oes anteriores de C n ao deniam um tipo booleano e v arios (e incompat veis) ( Bool est m etodos foram usados para simular um tipo booleano. Algumas caracter sticas originalmente desenvolvidas em C++ tamb em apareceram em C. Entre elas encontram-se: Prot otipos de fun ca o (com declara ca o de tipos de par ametros) e remo ca o do intimpl cito; coment arios de linha, indicados por //; coment arios de linha terminam com um car acter de nova-linha; a palavra-chave inline; tipagem mais forte.

1.2
1.2.1

Gloss ario
Programa c ao estruturada

Programa ca o estruturada e uma forma de programa ca o de computadores que preconiza que todos os programas poss veis podem ser reduzidos a apenas tr es estruturas: sequ encia, decis ao e itera ca o. Tendo, na pr atica, sido transformada na Programa ca o modular, a Programa ca o estruturada orienta os programadores para a cria ca o de estruturas simples em seus programas, usando as subrotinas e as fun co es. Foi a forma dominante na cria ca o de software entre a programa ca o linear e a programa ca o orientada por objetos. Apesar de ter sido sucedida pela programa ca o orientada por objetos, pode-se dizer que a programa ca o estruturada ainda e marcantemente inuente, uma vez que grande parte das pessoas ainda aprendem programa ca o atrav es dela. Al em disso, por exigir formas de pensar relativamente complexas, a programa ca o orientada a objetos at e hoje ainda n ao e bem compreendida ou usada pela maioria. 1.2.2 Programa c ao imperativa

Programa ca o imperativa (ou programa ca o procedural) e um paradigma de programa ca o que descreve a computa ca o como a co es (comandos) que mudam o estado (vari aveis) de um programa. Muito parecido com o comportamento imperativo das linguagens naturais que expressam ordens, programas imperativos s ao uma sequ encia de comandos para o computador executar. As linguagens de programa ca o imperativa contrastam com outros tipos de linguagem, tais como linguagens de programa ca o declarativa, funcional e l ogica. Linguagens de programa ca o funcional, como por exemplo Haskell, n ao s ao uma sequ encia de arma co es e n ao possuem, por conseq u encia, um estado global como as linguagens imperativas. Linguagens de programa ca o l ogica, como exemplo Prolog, freqentemente denem o que e para ser computado, mais do que comocomputar, como seria normal em uma linguagem imperativa. 1.2.3 Programa c ao procedural

O termo Programa ca o procedural (ou programa ca o procedimental) e as vezes utilizado como sin onimo de Programa ca o imperativa (Paradigma de programa ca o que especica os passos que um programa deve seguir para alcan car um estado desejado), mas o termo pode se referir (como neste texto) ` a um paradigma de programa ca o baseado no conceito de chamadas a procedimento. Procedimentos, tamb em conhecidos como rotinas, subrotinas, m etodos, ou fun co es (que n ao devem ser confundidas com fun co es matem aticas, mas s ao similares ` aquelas usadas na programa ca o funcional) simplesmente cont em um conjunto de passos computacionais a serem executados. Um dado procedimento pode ser chamado a qualquer hora durante a execu ca o de um programa, inclusive por outros procedimentos ou por si mesmo.

1 INTRODUC AO

A programa ca o procedural e geralmente uma escolha melhor que programa ca o sequencial e n ao estruturada em muitas situa co es que envolvem uma complexidade m edia e requerem facilidade de manuten ca o. Poss veis benef cios s ao: A habilidade de reutilizar o mesmo c odigo em diferentes lugares no programa sem copi a-lo. Uma forma mais f acil de organizar o uxo do programa que uma cole ca o de comandos gotoou jump(que podem transformar um programa grande e complicado no assim chamado C odigo espaguete). A habilidade de ser fortemente modular e estruturado. A Modularidade e uma caracter stica geralmente desej avel, especialmente em programas grandes e complicados. Ela pode ser alcan cada com a utiliza ca o de procedimentos com canais de entrada e sa da estritamente denidos, usualmente acompanhados de regras claras sobre quais tipos de entrada e sa da s ao permitidos ou esperados. As entradas costumam ser especicadas sintaticamente na forma de argumentos, e as sa das entregues na forma de valores de retorno. O gerenciamento de escopo e outra t ecnica que ajuda a manter procedimentos fortemente modulares. Ela impede que o procedimento acesse vari aveis de outros procedimentos (e vice-versa), incluindo inst ancias anteriores de si mesmo, sem autoriza ca o expl cita. Isto ajuda a impedir confus oes entre vari aveis com o mesmo nome sendo utilizadas em locais diferentes, e tamb em que os procedimentos atrapalhem a execu ca o um do outro. Procedimentos menos modulares, freq uentemente utilizados em programas pequenos ou escritos rapidamente, tendem a interagir com um grande n umero de vari aveis no ambiente de execu ca o, que tamb em podem ser modicadas por outros procedimentos. O fato de que muitas vari aveis agem como pontos de contato entre as v arias partes do programa e o que o torna menos modular. Por causa da habilidade de especicar uma interface simples, de serem auto-contidos, e de serem reutilizados, procedimentos facilitam a cria ca o de programas ou bibliotecas de programa ca o por v arias pessoas ou grupos diferentes. Todas (ou pelo menos a maioria) das linguagens procedurais tamb em s ao linguagens imperativas, pois fazem refer encias expl citas ao estado do ambiente de execu ca o. Isto pode signicar desde vari aveis (que podem corresponder aos registradores do processador) a algo como a posi ca o da tartarugana linguagem de programa ca o Logo (que por sua vez pode ser desde um cursor na tela a um dispositivo f sico que se move no ch ao de uma sala). Algumas formas de programa ca o imperativa, como a programa ca o orientada a objetos n ao s ao necessariamente procedurais. 1.2.4 Programa c ao gen erica

Programa ca o gen erica e um paradigma de programa ca o no qual os algoritmos s ao escritos em uma gram atica estendida de forma a adaptar-se atrav es da especica ca o das partes vari aveis que s ao denidas na inst ancia do algoritmo. Especicamente, a gram atica estendida eleva um elemento n ao vari avel ou uma constru ca o impl cita na gram atica base para uma vari avel ou constante, permitindo a utiliza ca o do c odigo gen erico. diferente da forma normal de programa E ca o na medida que invoca de certa forma as facilidade de metaprograma ca o da linguagem. Como isso ocorre em uma extens ao da linguagem, novas sem anticas s ao introduzidas e a linguagem relacionada com a metaprograma e enriquecida no processo. E ca o, mas n ao envolve a gera ca o de c odigo fonte, pelo diferente tamb menos visivelmente ao programador. E em da programa ca o por macros, j a que esta refere-se somente a busca e substitui ca o de termos, n ao fazendo parte da gram atica da linguagem, implementada somente na fase de pr e-processamento do c odigo. Para efeitos pr aticos, o paradigma permite que um par ametro assuma diferentes tipos de dados desde que certas regras sejam mantidas, como sub-tipos e assinaturas. Por exemplo, para criar uma lista usando programa ca o gen erica, uma poss vel declara ca o seria List<T>, no qual T e o tipo de dado. Para instanciar, poderia-se usar List<Inteiro> ou List<Animal>, j a que o conceito de lista independe do tipo utilizado. Entre linguagens orientadas a objeto, C++, Linguagem D, BETA, Eiel e vers oes de Java (1.5 e acima) fornecem o paradigma gen erico. Visual Basic .NET, C# e Delphi.Net come caram a fornecer o paradigma a partir do .NET 2.0. Muito antes de todas as linguagens mencionadas, programa c ao gen erica j a havia sido implementada na d ecada de 1970 em linguagens como CLU e Ada. Mas somente o conceito de templates do C++ que popularizou o conceito. A t ecnica permite que algoritmos sejam escritos independente dos tipos de dados utilizados. Os autores do conceituado livro de 1995 Design Patterns referem-se ` a programa ca o gen erica como tipos parametrizados, o que permite que um tipo possa ser denido sem especicar todos os outros tipos que ele utiliza. Os autores ainda descrevem que tal t ecnica e muito poderosa, especialmente quando combinada com o padr ao Delegar.

1 INTRODUC AO

1.3

Programa da disciplina
(a) Diferencia ca o de letras minusculas e maiusculas em C (b) A estrutura basica de um programa (c) Introdu ca o ` as fun co es (d) A utiliza ca o simples de entradas e Sa das (e) Introdu ca o a controle de uxo: condi co es e la cos (f) Utiliza ca o de Coment arios (g) As Palavras da linguagem ANSI C

1. Programa ca o com C

2. Vari aveis, constantes, operadores e express oes (a) Nomes de vari veis (b) Os Tipos de var aveis de C (c) Declara ca o e inicializa ca o de Vari aveis (d) Constantes (e) Operadores aritm eticos e de atribui ca o (f) Operadores relacionais e l ogicos (g) Express oes (h) Modeladores: Casts 3. Estruturas de controle de uxo (a) O Comando if (b) A implementa ca o de op co es: o comando switch (c) Possibilidades de construir la cos com for (d) O la co n ao indexado utilizando while (e) O la co com do (f) O Comando break (g) O Comando continue (h) R otulos e o comando goto 4. Tipos de dados indexadas: matrizes e strings (a) Vetores (b) Vetores de carateres: Strings (c) Matrizes 5. Ponteiros (a) A utilidade de ponteiros (b) A declara ca o e a utiliza ca o de ponteiros (c) O uso de ponteiros com vetores (d) Inicializa ca o de ponteiros (e) Encadeiar ponteiros para ponteiros (f) Erros comuns na utiliza ca o de ponteiros 6. Fun co es (a) A estrutura de uma fun ca o (b) Utilizando a fun ca o para retornar velores: o comando return (c) Prot otipos de Fun co es

1 INTRODUC AO

(d) O tipo void (e) Arquivos-Cabe calhos (f) Escopo de Vari aveis (g) Passagem de argumentos por valor e por refer encia em fun co es (h) Passando matrizes para fun co es (i) Os Argumentos argc e argv (j) Programa ca o recursiva (k) Comentarios sobre o uso de fun co es 7. Diretivas de compila ca o (a) As diretivas de compila ca o (b) A Diretiva include (c) As Diretivas dene e undef (d) As Diretivas ifdef e endif (e) A Diretiva ifndef (f) A Diretiva if (g) A Diretiva else (h) A Diretiva elif 8. Entradas e sa das padronizadas (a) Introdu ca o (b) Ler e escrever caracteres (c) Ler e escrever strings (d) Entrada e sa da formatada (e) Abrir e fechar arquivos (f) Leitura sequencial de arquivos (g) Fun co es elaborados de acesso a arquivos (h) Fluxos padr ao 9. Tipos de dados avan cados (a) Modicadores de Acesso (b) Especicadores de classe de armazenamento (c) Convers ao de tipos (d) Modicadores de fun co es (e) Ponteiros para Fun co es (f) Aloca ca o din amica de mem oria (g) Aloca ca o din amica de mem oria para vetores e matrizes 10. Constru ca o de novos tipos de dados al em dos padr ao (a) Estruturas (b) Uni oes (c) Enumera co es (d) O Comando sizeof (e) O Comando typedef

COM C 2 PROGRAMAC AO

Programa c ao com C

OC e uma linguagem de programa ca o gen erica que e utilizada para a cria ca o de programas diversos como processadores de texto, planilhas eletr onicas, sistemas operacionais, programas de comunica ca o, programas para a automa ca o industrial, gerenciadores de bancos de dados, programas de projeto assistido por computador, programas para a solu ca o bem prov de problemas da Engenharia, F sica, Qu mica e outras Ci encias, etc ... . E avel que o Navegador que voc e usa tenha sido escrito em C ou C++. Estudaremos a estrutura do ANSI C, o C padronizado pela ANSI. A utiliza ca o do compilador e essencial para aprender a lidar com mensagens de aviso, mensagens de erro, bugs, etc. O conhecimento de uma linguagem de programa ca o exige muito mais que conhecer estruturas e fun co es; exige, al em do dom nio da linguagem em si, o completo dom nio do compilador e experi encia em achar os chamados bugs (erros l ogicos) nos programas. Por isto e muito importante, que voc e digite, compile, ache e corrija os erros apresentados, execute e conra os programas.

2.1

Cria c ao, comila c ao e execu c ao

Os estados de desenvolvimento de um programa em C s ao: Cria ca o do programa fonte (texto). Compila ca o desse programa, para a sua tradu ca o para c odigo execut avel. Execu ca o do c odigo produzido. No caso da detec ca o de qualquer erro em qualquer dos estados, todos eles dever ao ser repetidos desde o in cio. 2.1.1 Fase 1: Cria c ao do programa

A cria ca o dos programas fonte em linguagem C faz-se com o aux lio de um editor de texto gen erico, ou espec co de um ambiente de desenvolvimento. Em geral, os arquivos de texto produzidos dever ao ter a extens ao .c, para poderem ser reconhecidos automaticamente pelo compilador como sendo arquivos contendo c odigo fonte em C. Obviamente o conte udo dos arquivos dever a vericar rigorosamente a sintaxe da linguagem C. 2.1.2 Fase 2: Compila c ao

A compila ca o dos programas em C faz-se atrav es da invoca ca o de um compilador (p. ex. no UNIX, o comando cc). O comando de compila ca o dever a ser seguido pelo nome do arquivo que cont em o c odigo fonte (geralmente com a tamb extens ao .c). E em comum colocar como par ametros de chamada do compilador, v arias op co es de compila ca o (p. ex. no UNIX, a indica ca o do nome do arquivo execut avel com o resultado da compila ca o). Assim uma compila ca o b asica poderia ser executada no UNIX atrav es do comando: cc program.c onde program.c e o nome do arquivo contendo o c odigo fonte. Se existirem erros de sintaxe no c odigo fonte, o compilador detecta-los- a e indicar a a sua localiza ca o junto com uma breve descri ca o do erro. Erros na l ogica do programa apenas poder ao ser detectados durante a execu c ao do mesmo. Se o programa n ao contiver erros de sintaxe o compilador produzir a c odigo execut avel. Tratando-se de um programa completo o c odigo execut avel e colocado, por default, num arquivo chamado a.out (isto no UNIX). Se se pretender colocar o resultado da compila ca o noutro arquivo dever a utilizar-se a op ca o -o: cc -o program program.c Neste caso o c odigo execut avel e colocado no arquivo program que e j a criado com os necess arios direitos de execu ca o (no UNIX). 2.1.3 Fase 3: Execu c ao do programa

Se a opera ca o anterior tiver sucesso, a execu ca o do programa compilado produzido faz-se simplesmente invocando-o como se fosse um comando do sistema operativo. Durante a execu ca o podem tornar-se evidentes mais alguns erros: erros de execu ca o (p. ex. divis ao por zero), ou erros que levem a que o programa n ao se comporte como esperado. Neste caso e necess ario voltar ` a edi ca o do programa fonte para corrigir a sua l ogica, e depois efectuar tamb em uma nova compila ca o para produzir a nova vers ao do c odigo execut avel.

COM C 2 PROGRAMAC AO

2.2

O modelo de compila c ao da linguagem C

Salientaremos aqui apenas os pontos principais do modelo de compila ca o da linguagem C. Esse modelo pode ser ilustrado atrav es da gura 1.

C odigo fonte Pr e-processador

Compilador

Por vezes um s o componente Assembler

C odigo assembly

Bibliotecas externas

Linker

C odigo objeto

C odigo execut avel Figura 1: Modelo de compila ca o da linguagem C.

COM C 2 PROGRAMAC AO

10

2.2.1

O pr e-processador

O pr e-processador atua apenas ao n vel do c odigo fonte, modicando-o. Trabalha apenas com texto. Algumas das suas fun co es s ao: Remover os coment arios de um programa; interpretar directivas especiais a si dirigidas, que come cam pelo car acter #. Por exemplo: #include - insere o conte udo de um arquivo de texto no arquivo corrente. Esses arquivos s ao usualmente designados por cabe calhos (header les) e t em a extens ao .h: #include <math.h> - Insere o conte udo do arquivo math.h com a declara ca o das fun co es matem aticas da biblioteca standard. #include <stdio.h> - Idem para as fun co es standard de entrada/sa da. #dene - dene um nome simb olico cujas ocorr encias no arquivo ser ao substitu das por outro nome ou constante: encias de MAX ARRAY SIZE por 100. #dene MAX ARRAY SIZE 100 - substitui todas as ocorr 2.2.2 O compilador

Alguns compiladores traduzem o c odigo fonte (texto) recebido do pr e-processador para linguagem assembly (tamb em texto). No entanto s ao tamb em comuns os compiladores capazes de gerarem diretamente c odigo objeto (instru co es do processador j a em c odigo bin ario). 2.2.3 O assembler

O assembler traduz c odigo em linguagem assembly (texto) para c odigo objeto. Pode estar integrado no compilador. O c odigo objeto e geralmente armazenado em arquivos com a extens ao .o (unix) ou .obj (ms-dos). 2.2.4 O linker

Se o programa referencia fun co es da biblioteca standard ou outras fun co es contidas em arquivos com c odigo fonte diferentes do principal (que cont em a fun ca o main() ), o linker combina todos os objectos com o resultado compilado dessas fun co es num u nico arquivo com c odigo execut avel. As refer encias a vari aveis globais externas tamb em s ao resolvidas pelo linker. 2.2.5 A utiliza c ao de bibliotecas

A linguagem C e muito compacta. Muitas das fun co es que fazem parte de outras linguagens n ao est ao diretamente inclu das na linguagem C. Temos como exemplo as opera co es de entrada/sa da, a manipula ca o de strings e certas opera co es matem aticas. A funcionalidade correspondente a estas e outras opera co es n ao faz parte integrante da linguagem, mas est a inclu da numa biblioteca externa, bastante rica e standard. Todas essas opera co es s ao executadas por via da invoca ca o de fun co es externas denidas nessa biblioteca standard. Qualquer programador poder a desenvolver a sua pr opria biblioteca de fun co es, podendo at e substituir algumas das fun co es standard, e tamb em utilizar outras bibliotecas comerciais j a existentes (p. ex. NAG, PHIGS, etc). 2.2.6 Como conseguir uma c opia do compilador?

Para fazer o curso, voc e precisar a de um compilador C, padrao ANSI, instalado em seu computador. No caso de m aquinas Unix/Linux, este compilador normalmente j a vem com o sistema operacional e se chama cc (C Compiler) ou gcc (GNU C Compiler). Estes compiladores geralmente seguem o padr ao ANSI. M aquinas rodando Windows, necessitam da instala ca o um compilador C. Existem v arias op co es: 1. No ambiente Windows voc e tem uma boa op ca o de compilador gratuito. O LCC-Win32, que e um compilador C que pode ser baixado a partir do endere co http://www.cs.virginia.edu/lcc-win32/index.html. O compilador e padr ao ANSI C (ele n ao compila programas em C++, apenas em C. 2. Existem v arios compiladores comerciais (pagos) dispon veis e os mais comuns s ao os da Borland (Borland C/C++ e Borland C++ Builder) e da Microsoft. 3. Se voc e n ao quiser usar estes compiladores, pode tentar o cat alogo de compiladores gr atis na Internet, utilizando uma maquina de busca e a palavra de busca C compiler.

COM C 2 PROGRAMAC AO

11

2.3

Diferencia c ao de letras minusculas e maiusculas em C

OC e Case Sensitive, isto e, mai usculas e min usculas fazem diferen ca. Se se declarar uma vari avel com o nome soma ela ser a diferente de Soma, SOMA, SoMa ou sOmA. Da mesma maneira, os comandos do C if e for, por exemplo, s o podem ser escritos em min usculas pois sen ao o compilador n ao ir a interpret a-los como sendo comandos, mas sim como vari aveis.

2.4

A estrutura basica de um programa

Vejamos nosso primeiro programa em C: #include <stdio.h> void main() /* Meu Primeiro Programa */ { printf("O primeiro programa em C!\n"); } Compilando e executando este programa voc e ver a que ele coloca a mensagem O primeiro programa em C! na tela. Vamos analisar o programa por partes. A linha #include <stdio.h> diz ao compilador que ele deve incluir o arquivo-cabe calho stdio.h. Neste arquivo existem declara co es de fun co es u teis para entrada e sa da de dados (std = standard, padr ao em ingl es; io = Input/Output, entrada e sa da stdio = Entrada e sa da padronizadas). Toda vez que voc e quiser usar uma destas fun co es deve-se incluir este comando. O C possui diversos arquivos-cabe calhos. Quando fazemos um programa, uma boa id eia e usar coment arios que ajudem a elucidar o funcionamento do mesmo. No caso acima temos um coment ario: /* Meu Primeiro Programa */. O compilador C desconsidera qualquer coisa que esteja come cando com /* e terminando com */. Um coment ario pode, inclusive, ter mais de uma linha. A linha void main() dene uma fun ca o de nome main. Todos os programas em C t em que ter uma fun ca o main, pois e esta fun ca o que ser a chamada quando o programa for executado. O conte udo da fun ca o e delimitado por chaves { }. O c odigo que estiver dentro das chaves ser a executado sequencialmente quando a fun ca o for chamada. A palavra void indica que esta fun ca o n ao retorna nada, isto e seu retorno e vazio. Posteriormente, veremos que as fun co es em C podem retornar valores. A u nica coisa que o programa realmente faz e chamar a fun ca o printf(), passando a string (uma string e uma seq encia de caracteres, como veremos brevemente) O primeiro programa em C !\ncomo argumento. por causa do uso da fun E ca o printf() pelo programa que devemos incluir o arquivo-cabe calho stdio.h. A fun ca o printf() neste caso ir a apenas colocar a string na tela do computador. O \n e uma constante chamada de constante barra invertida. No caso, o \n e a constante barra invertida de new linee ele e interpretado como um comando de mudan ca de linha, isto e, ap os imprimir O primeiro programa em C! o cursor passar a para a pr oxima linha. E importante observar tamb em que os comandos do C terminam com ;. Podemos agora tentar um programa mais complicado: #include <stdio.h> void main() { int Dias; float Anos;

/* Declaracao de Variaveis */

printf("Entre com o n\umero de dias: "); /* Entrada de Dados */ scanf("%d", &Dias); Anos=Dias / 365.25; /* Conversao Dias->Anos */ printf("\n\n%d dias equivalem a %f anos.\n", Dias, Anos); } Vamos entender como o programa acima funciona. S ao declaradas duas vari aveis chamadas Dias e Anos. A primeira e um int (inteiro) e a segunda um oat (ponto utuante). e feita ent ao uma chamada ` a fun ca o printf(), que coloca uma mensagem na tela. Queremos agora ler um dado que ser a fornecido pelo usu ario e coloc a-lo na vari avel Dias. Para tanto usamos a fun ca o scanf(). A string %ddiz a ` fun ca o que iremos ler um inteiro. O segundo par ametro passado ` a

COM C 2 PROGRAMAC AO

12

importante ressaltar a necessidade de se colocar fun ca o diz que o dado lido dever a ser armazenado na vari avel Dias. E um & antes do nome da vari avel a ser lida quando se usa a fun ca o scanf(). O motivo disto s o car a claro mais tarde. Observe que, no C, quando temos mais de um par ametro para uma fun ca o, eles ser ao separados por v rgula. Temos ent ao uma express ao matem atica simples que atribui a Anos o valor de Dias dividido por 365,25. Como Anos e uma vari avel oat o compilador far a uma convers ao autom atica entre os tipos das vari aveis (veremos isto com detalhes mais tarde). A segunda chamada ` a fun ca o printf() tem tr es argumentos. A string \n\n%d dias equivalem a %f anos \ndiz ` a fun ca o para dar dois retornos de carro (passar para a pr oxima linha e depois para a subseq uente), colocar um inteiro na tela, colocar a mensagem dias equivalem a, colocar um valor oat na tela, colocar a mensagem anos.e dar mais uma linha nova com posi ca o do cursor no in cio da linha. Os outros par ametros s ao as vari aveis das quais devem ser lidos os valores do inteiro e do oat, respectivamente.

2.5

Introdu c ao ` as fun c oes

Uma fun ca o e um bloco de c odigo de programa que pode ser usado diversas vezes em sua execu ca o. O uso de fun co es permite que o programa que mais leg vel, mais bem estruturado. Um programa em C consiste, no fundo, de v arias fun co es colocadas juntas. Abaixo o tipo mais simples de fun ca o: #include <stdio.h> void mensagem() { printf("Ola! "); } /* Funcao simples: so imprime Ola! */

void main() { mensagem(); printf("Eu estou aprendendo C!\n"); } Este programa ter a o mesmo resultado que o primeiro exemplo da se ca o anterior. O que ele faz e denir uma fun ca o mensagem() que coloca uma string na tela e n ao retorna nada (void). Depois esta fun ca o e chamada a partir de main() (que tamb em e uma fun ca o). 2.5.1 Argumentos de fun co es

atrav Argumentos s ao as entradas que a fun ca o recebe. E es dos argumentos que passamos par ametros para a fun ca o. J a vimos fun co es com argumentos. As fun co es printf() e scanf() s ao fun co es que recebem argumentos. Vamos ver um outro exemplo simples de fun ca o com argumentos: #include <stdio.h> void square(int x) /* Calcula o quadrado de x */ { printf("O quadrado e %d", (x * x)); } void main() { int num; printf("Entre com um numero: "); scanf("%d", &num); printf("\n\n"); square(num); }

COM C 2 PROGRAMAC AO

13

Na deni ca o de square() dizemos que a fun ca o receber a um argumento inteiro x. Quando fazemos a chamada ` fun a ca o, o inteiro num e passado como argumento. H a alguns pontos a observar. Em primeiro lugar temos de satisfazer aos requesitos da fun ca o quanto ao tipo e ` a quantidade de argumentos quando a chamamos. Apesar de existirem algumas convers oes de tipo, que o C faz automaticamente, e importante car atento. Em segundo lugar, n ao e importante o nome da vari avel que se passa como argumento, ou seja, a vari avel num, ao ser passada como argumento para square() e copiada para a vari avel x. Dentro de square() trabalha-se apenas com x. Se mudarmos o valor de x dentro de square() o valor de num na fun ca o main() permanece inalterado. Vamos dar um exemplo de fun ca o de mais de uma vari avel. Repare que, neste caso, os argumentos s ao separados por v rgula e que deve-se explicitar o tipo de cada um dos argumentos, um a um. Note tamb em que os argumentos passados para a fun ca o n ao necessitam ser todos vari aveis porque mesmo sendo constantes ser ao copiados para a vari avel de entrada da fun ca o. #include <stdio.h> void mult(float a, float b, float c) /* Multiplica 3 numeros */ { printf("%f", a * b * c); } void main() { float x, y; x = 23.5; y = 12.9; mult(x, y, 3.87); }

2.5.2

Retorno de valores de uma fun c ao

Muitas vezes e necess ario fazer com que uma fun ca o retorne um valor. As fun co es que vimos at e aqui n ao retornam nada, pois especicamos um retorno void. Podemos especicar um tipo de retorno indicando-o antes do nome da fun ca o. Mas para dizer ao C o que vamos retornar precisamos da palavra reservada return. Sabendo disto ca f acil fazer uma fun ca o para multiplicar dois inteiros e que retorna o resultado da multiplica ca o. Veja: #include <stdio.h> int prod(int x, int y) { return (x * y); } void main() { int saida; char str1[100], str2[100]; saida = prod (12, 7); printf("A saida e: %d\n", saida); } Veja que como prod retorna o valor de 12 multiplicado por 7, este valor pode ser usado em uma express ao qualquer. No programa zemos a atribui ca o deste resultado ` a vari avel saida, que posteriormente foi impressa usando o printf. Uma observa ca o adicional: se n ao especicarmos o tipo de retorno de uma fun ca o, o compilador C automaticamente supor a que este tipo e inteiro. Por em, n ao e uma boa pr atica n ao se especicar o valor de retorno e, nesta apostila, este valor ser a sempre especicado.

COM C 2 PROGRAMAC AO

14

Mais um exemplo de fun ca o, que agora recebe dois oats e tamb em retorna um oat. Repare que neste exemplo especicamos um valor de retorno para a fun ca o int main() e retornamos zero. Normalmente e isto que fazemos com a fun ca o main, que retorna zero quando ela e executada sem qualquer tipo de erro: #include <stdio.h> float prod (float x, float y) { return (x * y); } int main() { float saida; saida=prod (45.2, 0.0067); printf("A saida e: %f\n", saida); return(0); }

2.5.3

Syntaxe geral de uma fun c ao

Apresentamos aqui a forma geral de uma fun ca o: ca o (lista de argumentos) tipo de retorno nome da fun c odigo da fun ca o

2.6
2.6.1

A utiliza c ao simples de entradas e sa das


O tipo de vari aveis: Caracteres

Os caracteres s ao um tipo de dado: o char. O C trata os caracteres como sendo vari aveis de um byte (8 bits). Um bit e a menor unidade de armazenamento de informa co es em um computador. Os inteiros (ints) t em um n umero maior de bytes. Dependendo da implementa ca o do compilador, eles podem ter 2 bytes (16 bits) ou 4 bytes (32 bits). Isto ser a melhor explicado mais adiante. Na linguagem C, tamb em podemos usar um char para armazenar valores num ericos inteiros, al em de us a-lo para armazenar caracteres de texto. Para indicar um caractere de texto usamos ap ostrofes. Veja um exemplo de programa que usa caracteres: #include <stdio.h> int main() { char Ch; Ch = D; printf("%c", Ch); return(0); } No programa acima, %c indica que printf() deve colocar um caractere na tela. Como vimos anteriormente, um char tamb em e usado para armazenar um n umero inteiro. Este n umero e conhecido como o c odigo ASCII correspondente ao caractere. Veja o programa abaixo:

COM C 2 PROGRAMAC AO

15

#include <stdio.h> int main() { char Ch; Ch = D; printf("%d", Ch); /* Imprime o caracter como inteiro */ return(0); } Este programa vai imprimir o n umero 68 na tela, que e o c odigo ASCII correspondente ao caractere D (d mai usculo). Muitas vezes queremos ler um caractere fornecido pelo usu ario. Para isto as fun co es mais usadas, quando se est a trabalhando em ambiente DOS ou Windows, s ao getch() e getche(). Ambas retornam o caractere pressionado. getche() imprime o caractere na tela antes de retorn a-lo e getch() apenas retorna o caractere pressionado sem imprim lo na tela. Ambas as fun co es podem ser encontradas no arquivo de cabe calho conio.h. Geralmente estas fun co es n ao est ao dispon veis em ambiente Unix (compiladores cc e gcc) e podem ser substitu das pela fun ca o scanf(), por em sem as mesmas funcionalidades. Eis um exemplo que usa a fun ca o getch(), e seu correspondente em ambiente Unix: #include <stdio.h> #include <conio.h> int main() { char Ch; Ch = getch(); printf("Voce pressionou a tecla %c", Ch); return(0); } Equivalente para o ambiente Unix do programa acima, sem usar getch(): #include <stdio.h> int main() { char Ch; scanf("%c", &Ch); printf("Voce pressionou a tecla %c", Ch); return(0); } A principal diferen ca da vers ao que utiliza getch() para a vers ao que n ao utiliza getch() e que no primeiro caso o usu ario simplesmente aperta a tecla e o sistema l e diretamente a tecla pressionada. No segundo caso, e necess ario apertar tamb em a tecla <ENTER>. 2.6.2 O tipo de dados: Strings

No C uma string e um vetor de caracteres terminado com um caractere nulo. O caracter nulo e um caractere com valor inteiro igual a zero (c odigo ASCII igual a 0). O terminador nulo tamb em pode ser escrito usando a conven ca o

COM C 2 PROGRAMAC AO

16

de barra invertida do C como sendo \0. Embora o assunto vetores seja discutido posteriormente, veremos aqui os fundamentos necess arios para que possamos utilizar as strings. Para declarar uma string podemos usar o seguinte formato geral: char nome da string[tamanho]; Isto declara um vetor de caracteres (uma string) com n umero de posi co es igual a tamanho. Note que, como temos que reservar um caractere para ser o terminador nulo, temos que declarar o comprimento da string como sendo, no m nimo, um caractere maior que a maior string que pretendemos armazenar. Vamos supor que declaremos uma string de 7 posi co es e coloquemos a palavra Maria nela. Teremos: M a r i a \0 ... . No caso acima, as duas c elulas n ao usadas t em valores indeterminados. Isto acontece porque o C n ao inicializa vari aveis, cabendo ao programador esta tarefa. Se quisermos ler uma string fornecida pelo usu ario podemos usar a fun ca o gets(). Um exemplo do uso desta fun ca o e apresentado abaixo. A fun ca o gets() coloca o terminador nulo na string, quando voc e aperta a tecla <ENTER>. #include <stdio.h> int main() { char string[100]; printf("Digite uma string: "); gets(string); printf("\n\nVoce digitou %s", string); return(0); } Neste programa, o tamanho m aximo da string que voc e pode entrar e uma string de 99 caracteres. Se voc e entrar com uma string de comprimento maior, o programa ir a aceitar, mas os resultados podem ser desastrosos. Veremos porque posteriormente. Como as strings s ao vetores de caracteres, para se acessar um determinado caracter de uma string, basta indexarmos, ou seja, usarmos um ndice para acessarmos o caracter desejado dentro da string. Suponha uma string chamada str. Podemos acessar a segunda letra de str da seguinte forma: str[1] = a; Por qu e se est a acessando a segunda letra e n ao a primeira? Na linguagem C, o ndice come ca em zero. Assim, a primeira letra da string sempre estar a na posi ca o 0. A segunda letra sempre estar a na posi ca o 1 e assim sucessivamente. Segue um exemplo que imprimir a a segunda letra da string Maria, apresentada acima. Em seguida, ele mudar a esta letra e apresentar a a string no nal. #include <stdio.h> int main() { char str[10] = "Maria"; printf("\n\nString: %s", str); printf("\nSegunda letra: %c", str[1]); str[1] = U; printf("\nAgora a segunda letra eh: %c", str[1]); printf("\n\nString resultante: %s", str); return(0); }

COM C 2 PROGRAMAC AO

17

Nesta string, o terminador nulo est a na posi ca o 4. Das posi co es 0 a 5, sabemos que temos caracteres v alidos, e portanto podemos escrev e-los. Note a forma como inicializamos a string str com os caracteres M a r i a e \0 simplesmente declarando char str[10] = "Maria"; Veremos, posteriormente que Maria(uma cadeia de caracteres entre aspas) e o que chamamos de string constante, isto e, uma cadeia de caracteres que est a pr e-carregada com valores que n ao podem ser modicados. J a a string str e uma string vari avel, pois podemos modicar o que nela est a armazenado, como de fato zemos. No programa acima, %s indica que printf() deve colocar uma string na tela. Vamos agora fazer uma abordagem inicial ` as duas fun co es que j a temos usado para fazer a entrada e sa da. 2.6.3 A fun c ao de sa da padr ao: printf

A fun ca o printf() tem a seguinte forma geral: printf(string de controle, lista de argumentos); Teremos, na string de controle, uma descri ca o de tudo que a fun ca o vai colocar na tela. A string de controle mostra n ao apenas os caracteres que devem ser colocados na tela, mas tamb em quais as vari aveis e suas respectivas posi co es. Isto e feito usando-se os c odigos de controle, que usam a nota ca o %. Na string de controle indicamos quais, de qual tipo e em que posi ca o est ao as vari aveis a serem apresentadas. e muito importante que, para cada c odigo de controle, tenhamos um argumento na lista de argumentos. Apresentamos agora alguns dos c odigos %: C odigo %d %f %c %s %% Signicado Inteiro Float Caractere String Coloca na tela um %

Vamos ver alguns exemplos de printf() e o que eles exibem: printf(Teste %% %%); printf(%f, 40.345); printf(Um caractere %c e um inteiro %d, D, 120); printf(%s e um exemplo, Este); printf(%s%d%%,Juros de , 10); Teste % % 40.345 Um caractere D e um inteiro 120 Este e um exemplo Juros de 10%

Maiores detalhes sobre a fun ca o printf() (incluindo outros c odigos de controle) ser ao vistos posteriormente, mas podem ser consultados de antem ao pelos interessados. 2.6.4 A fun c ao de entrada padr ao: scanf

O formato geral da fun ca o scanf() e: scanf(string de controle, lista de argumentos); Usando a fun ca o scanf() podemos pedir dados ao usu ario. Um exemplo de uso, pode ser visto acima. Mais uma vez, devemos car atentos a m de colocar o mesmo n umero de argumentos que o de c odigos de controle na string de imposs controle. Outra coisa importante e lembrarmos de colocar o & antes das vari aveis da lista de argumentos. E vel justicar isto agora, mas veremos depois a raz ao para este procedimento. Maiores detalhes sobre a fun ca o scanf() ser ao vistos posteriormente, mas podem ser consultados agora interessados.

2.7

Introdu c ao a controle de uxo: condi c oes e la cos

Os comandos de controle de uxo s ao aqueles que permitem ao programador alterar a sequ encia de execu ca o do programa. Vamos dar uma breve introdu ca o a dois comandos de controle de uxo. Outros comandos ser ao estudados posteriormente.

COM C 2 PROGRAMAC AO

18

2.7.1

A condi c ao if

fa O comando if representa uma tomada de decis ao do tipo SE acontecer isto ENTAO ca aquilo. A sua forma geral e: if (condi ca o) declara ca o; A condi ca o do comando if e uma express ao que ser a avaliada. Se o resultado for zero a declara ca o n ao ser a executada. Se o resultado for qualquer coisa diferente de zero a declara ca o ser a executada. A declara ca o pode ser um bloco de c odigo ou apenas um comando. e interessante notar que, no caso da declara ca o ser um bloco de c odigo, n ao e necess ario (e nem permitido) o uso do ; no nal do bloco. Isto e uma regra geral para blocos de c odigo. Abaixo apresentamos um exemplo: #include <stdio.h> int main() { int num; printf("Digite um numero: "); scanf("%d", &num); if (num > 10) printf("\n\nO numero e maior que 10"); if (num == 10) { printf("\n\nVoce acertou!\n"); printf("O numero e igual a 10."); } if (num<10) printf("\n\nO numero e menor que 10"); return (0); } No programa acima a express ao num 10 e avaliada e retorna um valor diferente de zero, se verdadeira, e zero, se falsa. Repare que quando queremos testar igualdades usamos o operador == e n ao =. Isto e porque o operador = representa apenas uma atribui ca o. Isto pode parecer estranho ` a primeira vista, mas se escrev essemos if (num = 10) ... /* Isto esta errado */

o compilador iria atribuir o valor 10 ` a vari avel num e a express ao num = 10 iria retornar 10, fazendo com que o nosso valor de num fosse adulterado e fazendo com que a declara ca o fosse executada sempre. Este problema gera erros frequentes entre iniciantes e, portanto, muita aten ca o deve ser tomada. Os operadores de compara ca o s ao: == (igual), ! = (diferente de), > (maior que), < (menor que), >= (maior ou igual), <= (menor ou igual). 2.7.2 O la co indexado com for

O loop (la co) for e usado para repetir um comando, ou bloco de comandos, diversas vezes, de maneira que se possa ter um bom controle sobre o loop. Sua forma geral e: for (inicializa ca o; condi ca o; incremento) declara ca o; A declara ca o no comando for tamb em pode ser um bloco ({}) e neste caso o ; e omitido. O melhor modo de se entender o loop for e ver de que maneira ele funciona por dentro. O loop for e equivalente a se fazer o seguinte: inicializa ca o; if (condi ca o) { declara ca o; incremento;

COM C 2 PROGRAMAC AO

19

Volte para o comando if } Podemos ver ent ao que o for executa a inicializa ca o incondicionalmente e testa a condi ca o. Se a condi ca o for falsa ele n ao faz mais nada. Se a condi ca o for verdadeira ele executa a declara ca o, o incremento e volta a testar a condi ca o. Ele ca repetindo estas opera co es at e que a condi ca o seja falsa. Abaixo vemos um programa que coloca os primeiros 100 n umeros na tela: #include <stdio.h> int main() { int count; for (count = 1; count <= 100; count = count + 1) printf("%d ", count); return(0); } Outro exemplo interessante e mostrado a seguir: o programa l e uma string e conta quantos dos caracteres desta string s ao iguais ` a letra c #include <stdio.h> int main() { char string[100]; int i, cont;

/* String, ate 99 caracteres */

printf("\n\nDigite uma frase: "); gets(string); /* Le a string */ printf("\n\nFrase digitada:\n%s", string); cont = 0; for (i = 0; string[i] != \0; i = i + 1) { if ( string[i] == c ) /* Se for a letra c */ cont = cont + 1; /* Incrementa o contador de caracteres */ } printf("\nNumero de caracteres c = %d", cont); } Note o teste que est a sendo feito no for: o caractere armazenado em string[i] e comparado com \0 (caractere nal da string). Caso o caractere seja diferente de \0, a condi ca o e verdadeira e o bloco do for e executado. Dentro do bloco existe um if que testa se o caractere e igual a c. Caso seja, o contador de caracteres c e incrementado.

2.8

Utiliza c ao de Coment arios

Como j a foi dito, o uso de coment arios torna o c odigo do programa mais f acil de se entender. Os coment arios do C devem come car com /* e terminar com */. O C padr ao n ao permite coment arios aninhados (um dentro do outro), mas alguns compiladores aceitam.

2.9

As Palavras da linguagem ANSI C

Todas as linguagens de programa ca o t em palavras reservadas. As palavras reservadas n ao podem ser usadas a n ao ser nos seus prop ositos originais, isto e, n ao podemos declarar fun co es ou vari aveis com os mesmos nomes. Como o C e case sensitivepodemos declarar uma vari avel For, apesar de haver uma palavra reservada for, mas isto n ao e uma

COM C 2 PROGRAMAC AO

20

coisa recomend avel de se fazer pois pode gerar confus ao. Apresentamos a seguir as palavras reservadas do ANSI C. Veremos o signicado destas palavras chave ` a medida em que o curso for progredindo: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

21

3
3.1

Vari aveis, constantes, operadores e express oes


Nomes de vari veis

As vari aveis no C podem ter qualquer nome se duas condi co es forem satisfeitas: o nome deve come car com uma letra umeros ou sublinhado ( ). H a apenas mais duas ou sublinhado ( ) e os caracteres subsequentes devem ser letras, n restri co es: o nome de uma vari avel n ao pode ser igual a uma palavra reservada, nem igual ao nome de uma fun ca o declarada pelo programador, ou pelas bibliotecas do C. Vari aveis de at e 32 caracteres s ao aceitas. Mais uma coisa: e bom sempre lembrar que o C e case sensitivee portanto deve-se prestar aten ca o ` as mai usculas e min usculas.

3.2

Os Tipos de var aveis de C

O C tem 5 tipos b asicos: char, int, oat, void, double. Destes n ao vimos ainda o u ltimo: O double e o ponto utuante duplo e pode ser visto como um ponto utuante com muito mais precis ao. Para cada um dos tipos de vari aveis existem os modicadores de tipo. Os modicadores de tipo do C s ao quatro: signed, unsigned, long e short. Ao oat n ao se pode aplicar nenhum e ao double pode-se aplicar apenas o long. Os quatro podem ser aplicados a inteiros. A inten ca o e que short e long devam prover tamanhos diferentes de inteiros onde isto for pr atico. int normalmente ter a o tamanho natural para uma determinada m aquina. Assim, numa m aquina de 16 bits, int provavelmente ter a 16 bits. Numa m aquina de 32, int dever a ter 32 bits. Na verdade, cada compilador e livre para escolher tamanhos adequados para o seu pr oprio hardware, com a u nica restri ca o de que shorts e ints devem ocupar pelo menos 16 bits, longs pelo menos 32 bits, e short n ao pode ser maior que int, que n ao pode ser maior que long. A seguir est ao listados os tipos de dados permitidos e seu valores m aximos e m nimos em um compilador t pico para um hardware de 16 bits: Tipo char unsigned char signed char int unsigned int signed int short int unsigned short int signed short int long int signed long int unsigned long int oat double long double Num de bits 8 8 8 16 16 16 16 16 16 32 32 32 32 64 80 Intervalo Valor Inicial Valor Final -128 127 0 255 -128 127 -32768 32767 0 65535 -32768 32767 -32768 32767 0 65535 -32768 32767 -2147483648 2147483647 -2147483648 2147483647 0 4294967295 3,4E-38 3.4E+38 1,7E-308 1,7E+308 3,4E-4932 3,4E+4932

Aqui E signica que o valor deve ser multiplicado por 10 na pot encia indicada. Por exemplo 3,4E-308 representa 3, 4 10308 . importante observar que os intervalos de O tipo long double e o tipo de ponto utuante com maior precis ao. E ponto utuante, na tabela acima, est ao indicados em faixa de expoente, mas os n umeros podem assumir valores tanto positivos quanto negativos.

3.3

Declara c ao e inicializa c ao de Vari aveis

As vari aveis no C devem ser declaradas antes de serem usadas. A forma geral da declara ca o de vari aveis e: tipo da vari avel lista de vari aveis; As vari aveis da lista de vari aveis ter ao todas o mesmo tipo e dever ao ser separadas por v rgula. Como o tipo default do C e o int, quando vamos declarar vari aveis int com algum dos modicadores de tipo, basta colocar o nome do modicador de tipo. Assim um long basta para declarar um long int. Por exemplo, as declara co es char ch, letra; long count;

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

22

float pi; declaram duas vari aveis do tipo char (ch e letra), uma variavel long int (count) e um oat pi. H a tr es lugares nos quais podemos declarar vari aveis. O primeiro e fora de todas as fun co es do programa. Estas vari aveis s ao chamadas vari aveis globais e podem ser usadas a partir de qualquer lugar no programa. Pode-se dizer que, como elas est ao fora de todas as fun co es, todas as fun co es as v eem. O segundo lugar no qual se pode declarar vari aveis e no in cio de um bloco de c odigo. Estas vari aveis s ao chamadas locais e s o t em validade dentro do bloco no qual s ao declaradas, isto e, s o a fun ca o ` a qual ela pertence sabe da exist encia desta vari avel, dentro do bloco no qual foram declaradas. O terceiro lugar onde se pode declarar vari aveis e na lista de par ametros de uma fun ca o. Mais uma vez, apesar de estas vari aveis receberem valores externos, estas vari aveis s ao conhecidas apenas pela fun ca o onde s ao declaradas. Veja o programa abaixo: #include <stdio.h> int contador; int func1(int j) { ... } int main() { char condicao; int i; for (i = 0; ...) { /* Bloco do for */ float f2; ... func1(i); } ... return(0); } A vari avel contador e uma vari avel global, e e acess vel de qualquer parte do programa. As vari aveis condi ca o e i, s o existem dentro de main(), isto e s ao vari aveis locais de main. A vari avel oat f2 e um exemplo de uma vari avel de bloco, isto e, ela somente e conhecida dentro do bloco do for, pertencente ` a fun ca o main. A vari avel inteira j e um exemplo de declara ca o na lista de par ametros de uma fun ca o (a fun ca o func1). As regras que regem onde uma vari avel e v alida chamam-se regras de escopo da vari avel. H a mais dois detalhes que devem ser ressaltados. Duas vari aveis globais n ao podem ter o mesmo nome. O mesmo vale para duas vari aveis locais de uma mesma fun ca o. J a duas vari aveis locais, de fun c oes diferentes, podem ter o mesmo nome sem perigo algum de conito. Podemos inicializar vari aveis no momento de sua declara ca o. Para fazer isto podemos usar a forma geral avel nome da vari avel = constante; tipo da vari Isto e importante pois quando o C cria uma vari avel ele n ao a inicializa. Isto signica que at e que um primeiro valor seja atribu do ` a nova vari avel ela tem um valor indenido e que n ao pode ser utilizado para nada. Nunca presuma que uma vari avel declarada vale zero ou qualquer outro valor. Exemplos de inicializa ca o s ao dados abaixo: char ch = D; int count = 0; float pi = 3.141;

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

23

3.4

Constantes

Constantes s ao valores que s ao mantidos xos pelo compilador. J a usamos constantes neste curso. S ao consideradas constantes, por exemplo, os n umeros e caracteres como 45.65 ou n, etc.. 3.4.1 Constantes dos tipos b sicos

Constantes relativas aos tipos b asicos do C: Tipo de Dado char int long int short int unsigned int oat double 3.4.2 Constantes hexadecimais e octais Exemplos de Constantes b, \n, \0 2, 32000, -130 100000, -467 100, -30 50000, 35678 0.0, 23.7, -12.3e-10 12546354334.0, -0.0000034236556

Muitas vezes precisamos inserir constantes hexadecimais (base dezesseis) ou octais (base oito) no nosso programa. O C permite que se fa ca isto. As constantes hexadecimais come cam com 0x. As constantes octais come cam em 0. Alguns exemplos: Constante 0xEF 0x12A4 03212 034215432 Tipo Char Hexadecimal (8 bits) Int Hexadecimal (16 bits) Char Octal (8 bits) Int Octal (16 bits)

Nunca escreva portanto 013 achando que o C vai compilar isto como se fosse 13. Na linguagem C 013 e diferente de 13! 3.4.3 Constantes strings

J a mostramos como o C trata strings. Vamos agora alertar para o fato de que uma string Joao e na realidade uma constante string. Isto implica, por exemplo, no fato de que t e diferente de t, pois t e um char enquanto que t e uma constante string com dois chars onde o primeiro e t e o segundo e \0. 3.4.4 Constantes especiais (de barra invertida)

Constantes de barra invertida O C utiliza, para nos facilitar a tarefa de programar, v arios c odigos chamados c odigos de barra invertida. Estes s ao caracteres que podem ser usados como qualquer outro. A lista completa dos c odigos de barra invertida e dada a seguir: C odigo \b \f \n \r \t \ \ \0 \\ \v \a \N \xN Signicado Retrocesso (back) Alimenta ca o de formul ario (form feed) Nova linha (new line) Retorno de carro (carriage return) Tabula ca o horizontal (tab) Aspas Ap ostrofo Nulo (0 em decimal) Barra invertida Tabula ca o vertical Sinal sonoro (beep) Constante octal (N e o valor da constante) Constante hexadecimal(N valor da constante)

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

24

3.5

Operadores aritm eticos e de atribui c ao

Os operadores aritm eticos s ao usados para desenvolver opera co es matem aticas. Os operadores da linguagem C s ao: Operador + / % ++ A ca o Soma (inteira e ponto utuante) Subtra ca o ou Troca de sinal (inteira e ponto utuante) Multiplica ca o (inteira e ponto utuante) Divis ao (inteira e ponto utuante) Resto de divis ao (de inteiros) Incremento (inteiro e ponto utuante) Decremento (inteiro e ponto utuante)

O C possui operadores un arios e bin arios. Os un arios agem sobre uma vari avel apenas, modicando ou n ao o seu valor, e retornam o valor nal da vari avel. Os bin arios usam duas vari aveis e retornam um terceiro valor, sem alterar as vari aveis originais. A soma e um operador bin ario pois pega duas vari aveis, soma seus valores, sem alterar as vari aveis, e retorna esta soma. Outros operadores bin arios s ao os operadores (subtra ca o), , / e %. O operador como troca de sinal e um operador un ario que n ao altera a vari avel sobre a qual e aplicado, pois ele retorna o valor da vari avel multiplicado por 1. O operador / (divis ao) quando aplicado a vari aveis inteiras, nos fornece o resultado da divis ao inteira; quando aplicado a vari aveis em ponto utuante nos fornece o resultado da divis ao real. Assim seja o seguinte trecho de c odigo: int a = 17, b = 3; int x, y; float z = 17. , z1, z2; x = a / b; y = a % b; z1 = z / b; z2 = a / b; Ao nal da execu ca o destas linhas, os valores calculados seriam x = 5, y = 2, z 1 = 5.666666 e z 2 = 5.0. Note que na linha correspondente a z 2, primeiramente e feita uma divis ao inteira (pois os dois operandos s ao inteiros). Somente ap os efetuada a divis ao e que o resultado e atribu do a uma vari avel oat. Os operadores de incremento e decremento s ao un arios que alteram a vari avel sobre a qual est ao aplicados. O que eles fazem e incrementar ou decrementar, a vari avel sobre a qual est ao aplicados, de 1. Ent ao x++; x--; s ao equivalentes a x = x + 1; x = x - 1; Estes operadores podem ser pr e-xados ou p os- xados. A diferen ca e que quando s ao pr e-xados eles incrementam e retornam o valor da vari avel j a incrementada. Quando s ao p os-xados eles retornam o valor da vari avel sem o incremento e depois incrementam a vari avel. Ent ao, em x = 23; y = x++; teremos, no nal, y = 23 e x = 24. Em

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

25

x = 23; y = ++x; teremos, no nal, y = 24 e x = 24. Uma curiosidade: a linguagem de programa ca o C++ tem este nome pois ela seria um incrementoda linguagem C padr ao. A linguagem C++ e igual a linguagem C s o que com extens oes que permitem a programa ca o orientada a objeto, o que e um recurso extra. O operador de atribui ca o do C e o =. O que ele faz e pegar o valor ` a direita e atribuir ` a vari avel da esquerda. Al em disto ele retorna o valor que ele atribuiu. Isto faz com que as seguintes express oes sejam v alidas: x = y = z = 1.5; if (k = w) ... /* Expressao 1 */ /* Expressao 2 */

A express ao 1 e v alida, pois quando fazemos z=1.5 ela retorna 1.5, que e passado adiante. A express ao dois ser a verdadeira se w for diferente de zero, pois este ser a o valor retornado por k = w. Pense bem antes de usar a express ao dois, pois ela pode gerar erros de interpreta ca o. Voc e n ao est a comparando k e w. Voc e est a atribuindo o valor de w a k e usando este valor para tomar a decis ao.

3.6
3.6.1

Operadores relacionais e l ogicos


Operadores relacionais

Os operadores relacionais do C realizam compara co es entre vari aveis. S ao eles: Operador > >= < <= == != A ca o Maior do que Maior ou igual a Menor do que Menor ou igual a Igual a Diferente de

Os operadores relacionais retornam verdadeiro (1) ou falso (0). Para fazer opera co es com valores l ogicos (verdadeiro e falso) temos os operadores l ogicos: Operador && || ! A ca o AND (E) OR (OU) NOT (N ao)

Usando os operadores relacionais e l ogicos podemos realizar uma grande gama de testes. A tabela-verdade destes operadores e dada a seguir: p falso falso verdadeiro verdadeiro q falso verdadeiro falso verdadeiro p AND q falso falso falso verdadeiro p OR q falso verdadeiro verdadeiro verdadeiro

Exemplo: No trecho de programa abaixo o if ser a executado, pois o resultado da express ao l ogica e verdadeiro: int i = 5, j =7; if ( (i > 3) && ( j <= 7) && ( i != j) ) j++; V AND V AND V = V

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

26

3.6.2

Operadores l ogicos bit a bit

O C permite que se fa ca opera co es l ogicas bit-a-bitem n umeros. Ou seja, neste caso, o n umero e representado por sua forma bin aria e as opera co es s ao feitas em cada bit dele. Imagine um n umero inteiro de 16 bits, a vari avel i, armazenando o valor 2. A representa ca o bin aria de i, ser a: 0000000000000010 (quinze zeros e um u nico 1 na segunda posi ca o da direita para a esquerda). Poderemos fazer opera co es em cada um dos bits deste n umero. Por exemplo, se zermos a nega ca o do n umero (opera ca o bin aria NOT, ou operador bin ario em C), isto e, i, o n umero se transformar a em 1111111111111101. As opera co es bin arias ajudam programadores que queiram trabalhar com o computador em baixo n vel. As opera co es l ogicas bit a bit s o podem ser usadas nos tipos char, int e long int. Os operadores s ao: Operador & | >> << A ca o AND OR XOR NOT Deslocamento de bits a direita Deslocamento de bits a esquerda

Os operadores &, |,e s ao as opera co es l ogicas bit a bit. A forma geral dos operadores de deslocamento e: valor >> n umero de deslocamentos valor << n umero de deslocamentos O n umero de deslocamentos indica o quanto cada bit ir a ser deslocado. Por exemplo, para a vari avel i anterior, armazenando o n umero 2: i << 3; far a com que i agora tenha a representa ca o bin aria: 0000000000010000, isto e, o valor armazenado em i passa a ser igual a 16.

3.7

Express oes

Express oes s ao combina co es de vari aveis, constantes e operadores. Quando montamos express oes temos que levar em considera ca o a ordem com que os operadores s ao executados, conforme a tabela de preced encias da linguagem C. Exemplos de express oes: Anos = Dias / 365.25; i = i + 3; c = a * b + d / e; c = a * (b + d) / e;

3.7.1

Convers ao de tipos em express oes

Quando o C avalia express oes onde temos vari aveis de tipos diferentes o compilador verica se as convers oes s ao poss veis. Se n ao s ao, ele n ao compilar a o programa, dando uma mensagem de erro. Se as convers oes forem poss veis ele as faz, seguindo as regras abaixo: 1. Todos os chars e short ints s ao convertidos para ints. Todos os oats s ao convertidos para doubles. 2. Para pares de operandos de tipos diferentes: se um deles e long double o outro e convertido para long double; se um deles e double o outro e convertido para double; se um e long o outro e convertido para long; se um e unsigned o outro e convertido para unsigned. 3.7.2 Nota c ao compacta para express oes

O C admite as seguintes equival encias, que podem ser usadas para simplicar express oes ou para facilitar o entendimento de um programa:

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

27

Express ao Original x = x + k; x = x k; x = x k; x = x/k ; x = x >> k ; x = x << k ; x = x&k ; etc... 3.7.3 Encadeamento de express oes: o Operador ,

Express ao Equivalente x+ = k ; x = k ; x = k ; x/ = k ; x >>= k ; x <<= k ; x& = k ;

O operador , determina uma lista de express oes que devem ser executadas sequencialmente. O valor retornado por uma express ao com o operador , e sempre dado pela express ao mais ` a direita. No exemplo abaixo: x = (y = 2, y +3); o valor 2 vai ser atribu do a y , se somar a 3 a y e o retorno (5) ser a atribu do ` a vari avel x. Pode-se encadear quantos operadores , forem necess arios. 3.7.4 As preced encias do C

Esta e a tabela de preced encia dos operadores em C. Alguns (poucos) operadores ainda n ao foram estudados, e ser ao apresentados em aulas posteriores. Maior preced encia () [] ! ++ . (un ario) (cast) (un ario) &(un ario) sizeof / % + << >> <<= >>= == ! = & | && || ? = += = = /= ,

Menor preced encia

u Uma dica aos iniciantes: Voc e n ao precisa saber toda a tabela de preced encias de cor. E til que voc e conhe ca as principais rela co es, mas e aconselh avel que ao escrever o seu c odigo, voc e tente isolar as express oes com par enteses, para tornar o seu programa mais leg vel.

3.8

Modeladores: Casts

Um modelador e aplicado a uma express ao. Ele for ca a mesma a ser de um tipo especicado. Sua forma geral e: (tipo)express ao Um exemplo: #include <stdio.h> int main() { int num; float f; num = 10; f = (float)num / 7;

3 VARIAVEIS, CONSTANTES, OPERADORES E EXPRESSOES

28

printf("%f", f); return(0); } Se n ao tiv essemos usado o modelador no exemplo acima o C faria uma divis ao inteira entre 10 e 7. O resultado seria 1 (um) e este seria depois convertido para oat mas continuaria a ser 1.0. Com o modelador temos o resultado correto.

4 ESTRUTURAS DE CONTROLE DE FLUXO

29

Estruturas de controle de uxo

As estruturas de controle de uxo s ao fundamentais para qualquer linguagem de programa ca o. Sem elas s o haveria uma maneira do programa ser executado: de cima para baixo comando por comando. N ao haveria condi co es, repeti co es poss ou saltos. A linguagem C possui diversos comandos de controle de uxo. E vel resolver todos os problemas sem utilizar todas elas, mas devemos nos lembrar que a eleg ancia e facilidade de entendimento de um programa dependem do uso correto das estruturas no local certo.

4.1

O Comando if

J a estudamos antes o comando if. Sua forma geral e: if (condi ca o) declara ca o; A express ao, na condi ca o, ser a avaliada. Se ela for zero, a declara ca o n ao ser a executada. Se a condi ca o for diferente de zero a declara ca o ser a executada. Aqui reapresentamos o exemplo de um uso do comando if: #include <stdio.h> int main() { int num; printf("Digite um nmero: "); scanf("%d", &num); if (num > 10) printf("\n\nO nmero e maior que 10"); if (num == 10) { printf("\n\nVoce acertou!\n"); printf("O nmero e igual a 10."); } if (num < 10) printf("\n\nO nmero e menor que 10"); return(0); }

4.1.1

O adicional else

Podemos pensar no comando else como sendo um complemento do comando if. O comando if completo tem a seguinte forma geral: if (condi ca o) declara ca o 1; else declara ca o 2; A express ao da condi ca o ser a avaliada. Se ela for diferente de zero a declara ca o 1 ser a executada. Se for zero a importante nunca esquecer que, quando usamos a estrutura if-else, estamos garantindo declara ca o 2 ser a executada. E que uma das duas declara co es ser a executada. Nunca ser ao executadas as duas ou nenhuma delas. Abaixo est a um exemplo do uso do if-else que deve funcionar como o programa da se ca o anterior. #include <stdio.h> int main() { int num; printf("Digite um numero: "); scanf("%d", &num);

4 ESTRUTURAS DE CONTROLE DE FLUXO

30

if (num == 10) { printf("\n\nVoce printf("O numero } else { printf("\n\nVoce printf("O numero } return(0); }

acertou!\n"); e igual a 10.\n");

errou!\n"); e diferente de 10.\n");

4.1.2

A constru c ao if-else-if

A estrutura if-else-if e apenas uma extens ao da estrutura if-else. Sua forma geral pode ser escrita como sendo: ca o 1; if (condi ca o 1) declara else if (condi ca o 2) declara ca o 2; else if (condi ca o 3) declara ca o 3; . . . else if (condi ca o n) declara ca o n; else declara ca o default; A estrutura acima funciona da seguinte maneira: o programa come ca a testar as condi co es come cando pela 1 e continua a testar at e que ele ache uma express ao cujo resultado d e diferente de zero. Neste caso ele executa a declara ca o correspondente. S o uma declara ca o ser a executada, ou seja, s o ser a executada a declara ca o equivalente ` a primeira condi ca o que der diferente de zero. A u ltima declara c ao (default) e a que ser a executada no caso de todas as condi co es darem zero e e opcional. Um exemplo da estrutura acima: #include <stdio.h> int main() { int num; printf("Digite um numero: "); scanf("%d", &num); if (num>10) printf("\n\nO numero e maior que 10"); else if (num == 10) { printf("\n\nVoce acertou!\n"); printf("O numero e igual a 10."); } else if (num < 10) printf("\n\nO numero e menor que 10"); return(0); }

4 ESTRUTURAS DE CONTROLE DE FLUXO

31

4.1.3

A express ao condicional

Quando o compilador avalia uma condi ca o, ele quer um valor de retorno para poder tomar a decis ao. Mas esta express ao n ao necessita ser uma express ao no sentido convencional. Uma vari avel sozinha pode ser uma express aoe esta retorna o seu pr oprio valor. Isto quer dizer que teremos as seguintes express oes: int num; if (num != 0) .... if (num == 0) .... for (i = 0; string[i] == \0; i++) ... equivalem a int num; if (num) .... if (!num) .... for (i = 0; string[i]; i++) ... Isto quer dizer que podemos simplicar algumas express oes simples. 4.1.4 A constru c ao de ifs aninhados

O if aninhado e simplesmente um if dentro da declara ca o de um outro if externo. O u nico cuidado que devemos ter e o de saber exatamente a qual if um determinado else est a ligado. Vejamos um exemplo: #include <stdio.h> int main() { int num; printf("Digite um numero: "); scanf("%d", &num); if (num == 10) { printf("\n\nVoce acertou!\n"); printf("O numero e igual a 10.\n"); } else { if (num > 10) { printf("O numero e maior que 10."); } else { printf("O numero e menor que 10."); } } return(0); }

4 ESTRUTURAS DE CONTROLE DE FLUXO

32

4.1.5

A forma compacta condicional: o operador ?

Uma express ao como: if (a > 0) b = -150; else b = 150; pode ser simplicada usando-se o operador ? da seguinte maneira: b = a > 0 ? -150 : 150; De uma maneira geral express oes do tipo: if (condicao) expressao_1; else expressao_2; podem ser substitu das por: condicao ? expressao_1 : expressao_2; O operador ? e limitado (n ao atende a uma gama muito grande de casos) mas pode ser usado para simplicar express oes complicadas. Uma aplica ca o interessante e a do contador circular. Veja o exemplo: #include <stdio.h> int main() { int index = 0, contador; char letras[5] = "Joao"; for (contador = 0; contador < 1000; contador++) { printf("\n%c", letras[index]); index = (index == 4) ? index=0 : ++index; } } O nome Joao e escrito na tela verticalmente at e a vari avel contador determinar o t ermino do programa. Enquanto isto a vari avel index assume os valores 0, 1, 2, 3, 4, 0, 1, ... progressivamente.

4.2

A implementa c ao de op c oes: o comando switch

O comando if-else e o comando switch s ao os dois comandos de tomada de decis ao. Sem d uvida alguma o mais importante dos dois e o if, mas o comando switch tem aplica co es valiosas. Mais uma vez vale lembrar que devemos usar o comando certo no local certo. Isto assegura um c odigo limpo e de f acil entendimento. O comando switch e pr oprio para se testar uma vari avel em rela ca o a diversos valores pr e-estabelecidos. Sua forma geral e: switch (vari\avel) { case constante_1: declaracao_1; break; case constante_2: declaracao_2; break;

4 ESTRUTURAS DE CONTROLE DE FLUXO

33

. . . case constante_n: declaracao_n; break; default declaracao_default; } Podemos fazer uma analogia entre o switch e a estrutura if-else-if apresentada anteriormente. A diferen ca fundamental e que a estrutura switch n ao aceita express oes. Aceita apenas constantes. O switch testa a vari avel e executa a declara ca o cujo case corresponda ao valor atual da vari avel. A declara ca o default e opcional e ser a executada apenas se a vari avel, que est a sendo testada, n ao for igual a nenhuma das constantes. O comando break, faz com que o switch seja interrompido assim que uma das declara co es seja executada. Mas ele n ao e essencial ao comando switch. Se ap os a execu ca o da declara ca o n ao houver um break, o programa continuar a executando. Isto pode ser u til em algumas situa co es, mas eu recomendo cuidado. Veremos agora um exemplo do comando switch: #include <stdio.h> int main() { int num; printf("Digite um numero: "); scanf("%d", &num); switch(num) { case 9: printf("\n\nO numero e igual a 9.\n"); break; case 10: printf("\n\nO numero e igual a 10.\n"); break; case 11: printf("\n\nO numero e igual a 11.\n"); break; default: printf("\n\nO numero nao e nem 9 nem 10 nem 11.\n"); } return(0); }

4.3

Possibilidades de construir la cos com for

for e a primeira de uma s erie de tr es estruturas para se trabalhar com loops de repeti ca o. As outras s ao while e do. As tr es comp oem a segunda fam lia de comandos de controle de uxo. Podemos pensar nesta fam lia como sendo a das estruturas de repeti ca o controlada. Como j a foi dito, o loop for e usado para repetir um comando, ou bloco de comandos, diversas vezes, de maneira que se possa ter um bom controle sobre o loop. Sua forma geral e: for (inicializacao; condicao; incremento) declaracao; O melhor modo de se entender o loop for e ver como ele funciona por dentro. O loop for e equivalente a se fazer o seguinte: inicializacao; if (condicao) { declaracao; incremento; "Volte para o comando if"

4 ESTRUTURAS DE CONTROLE DE FLUXO

34

} Podemos ver, ent ao, que o for executa a inicializa ca o incondicionalmente e testa a condi ca o. Se a condi ca o for falsa ele n ao faz mais nada. Se a condi ca o for verdadeira ele executa a declara ca o, faz o incremento e volta a testar a condi ca o. Ele ca repetindo estas opera co es at e que a condi ca o seja falsa. Um ponto importante e que podemos omitir qualquer um dos elementos do for, isto e, se n ao quisermos uma inicializa ca o poderemos omiti-la. Abaixo vemos um programa que coloca os primeiros 100 n umeros inteiros na tela: #include <stdio.h> int main() { int count; for (count = 1; count <= 100; count++) printf("%d ",count); return(0); } Note que, no exemplo acima, h a uma diferen ca em rela ca o ao exemplo anterior. O incremento da vari avel count e feito usando o operador de incremento que n os agora j a conhecemos. Esta e a forma usual de se fazer o incremento (ou decremento) em um loop for. O for na linguagem C e bastante ex vel. Temos acesso ` a inicializa ca o, ` a condi ca o e ao incremento. Isto nos permite fazer o que quisermos com o comando, conforme veremos a seguir. 4.3.1 O la co innito

O loop innito tem a forma for (inicializa ca o; ; incremento) declara ca o; Este loop chama-se loop innito porque ser a executado para sempre (n ao existindo a condi ca o, ela ser a sempre considerada verdadeira), a n ao ser que ele seja interrompido. Para interromper um loop como este usamos o comando break. O comando break vai quebrar o loop innito e o programa continuar a sua execu ca o normalmente. Como exemplo vamos ver um programa que faz a leitura de uma tecla e sua impress ao na tela, at e que o usuario aperte uma tecla sinalizadora de nal (um FLAG). O nosso FLAG ser a a letra X. #include <stdio.h> #include <conio.h> int main() { int Count; char ch; for (Count = 1; ; Count++) { ch = getch(); if (ch == X) break; printf("\nLetra: %c", ch); } return(0); }

4 ESTRUTURAS DE CONTROLE DE FLUXO

35

4.3.2

O la co sem conte udo

O loop sem conte udo Loop sem conte udo e aquele no qual se omite a declara ca o. Sua forma geral e (aten ca o ao ponto e v rgula!): for (inicializa ca o; condi ca o; incremento); Uma das aplica co es desta estrutura e gerar tempos de espera. O programa #include <stdio.h> int main() { long int i; printf("\a"); /* Imprime o caracter de alerta (um beep) */

for (i = 0; i < 10000000; i++); /* Espera 10.000.000 de iteracoes */ printf("\a"); return(0); } faz isto. /* Imprime outro caracter de alerta */

4.4

O la co n ao indexado utilizando while

O comando while tem a seguinte forma geral: while (condi ca o) declara ca o; Assim como zemos para o comando for, vamos tentar mostrar como o while funciona fazendo uma analogia. Ent ao o while seria equivalente a: if (condi ca o) { declara ca o; Volte para o comando if } Podemos ver que a estrutura while testa uma condi ca o. Se esta for verdadeira a declara ca o e executada e faz-se o teste novamente, e assim por diante. Assim como no caso do for, podemos fazer um loop innito. Para tanto basta colocar uma express ao eternamente verdadeira na condi ca o. Pode-se tamb em omitir a declara ca o e fazer um loop sem conte udo. Vamos ver um exemplo do uso do while. O programa abaixo espera o usu ario digitar a tecla q e s o depois naliza: #include <stdio.h> int main() { char Ch; Ch=\0; while (Ch!=q) { Ch = getch();

4 ESTRUTURAS DE CONTROLE DE FLUXO

36

} return(0); }

4.5

O la co com do

A terceira estrutura de repeti ca o que veremos e o do-while de forma geral: do { declara ca o; } while(condi ca o); Mesmo que a declara ca o seja apenas um comando e uma boa pr atica deixar as chaves. O ponto-e-v rgula nal e obrigat orio. Vamos, como anteriormente, ver o funcionamento da estrutura do-while por dentro: declara ca o; if (condi ca o) Volta para a declara ca o Vemos pela an alise do bloco acima que a estrutura do-while executa a declara ca o, testa a condi ca o e, se esta for verdadeira, volta para a declara ca o. A grande novidade no comando do-while e que ele, ao contr ario do for e do while, garante que a declara ca o ser a executada pelo menos uma vez. Um dos usos da extrutura do-while e em menus, nos quais voc e quer garantir que o valor digitado pelo usu ario seja v alido, conforme apresentado abaixo: #include <stdio.h> int main() { int i; do { printf("\n\nEscolha a fruta pelo numero:\n\n"); printf("\t(1)...Mamao\n"); printf("\t(2)...Abacaxi\n"); printf("\t(3)...Laranja\n\n"); scanf("%d", &i); } while ((i<1)||(i>3)); switch (i) { case 1: printf("\t\tVoce escolheu Mamao.\n"); break; case 2: printf("\t\tVoce escolheu Abacaxi.\n"); break; case 3: printf("\t\tVoce escolheu Laranja.\n"); break; } return(0); }

4 ESTRUTURAS DE CONTROLE DE FLUXO

37

4.6

O Comando break

N os j a vimos dois usos para o comando break: interrompendo os comandos switch e for. Na verdade, estes s ao os dois usos do comando break: ele pode quebrar a execu ca o de um comando (como no caso do switch) ou interromper a execu ca o de qualquer loop (como no caso do for, do while ou do do while). O break faz com que a execu ca o do programa continue na primeira linha seguinte ao loop ou bloco que est a sendo interrompido.

4.7

O Comando continue

O comando continue pode ser visto como sendo o oposto do break. Ele s o funciona dentro de um loop. Quando o comando continue e encontrado, o loop pula para a pr oxima itera ca o, sem o abandono do loop, ao contr ario do que acontecia no comando break. O programa abaixo exemplica o uso do continue: #include <stdio.h> int main() { int opcao; while (opcao != 5) { printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5) || (opcao < 1)) continue; /* Opcao invalida: volta ao inicio do loop */

switch (opcao) { case 1: break; case 2: break; case 3: break; case 4: break; case 5: break; } } return(0); }

printf("\n --> Primeira opcao.."); printf("\n --> Segunda opcao.."); printf("\n --> Terceira opcao.."); printf("\n --> Quarta opcao.."); printf("\n --> Abandonando..");

O programa acima ilustra uma aplica ca o simples para o continue. Ele recebe uma op ca o do usuario. Se esta op ca o for inv alida, o continue faz com que o uxo seja desviado de volta ao in cio do loop. Caso a op ca o escolhida seja v alida o programa segue normalmente.

4.8

R otulos e o comando goto

O goto e o u ltimo comando de controle de uxo. Ele pertence a uma classe ` a parte: a dos comandos de salto incondicional. O goto realiza um salto para um local especicado. Este local e determinado por um r otulo. Um r otulo, na linguagem C, e uma marca no programa. Voc e d a o nome que quiser a esta marca. Podemos tentar escrever uma forma geral: nome do rotulo: ...

4 ESTRUTURAS DE CONTROLE DE FLUXO

38

goto nome do rotulo; ... Devemos declarar o nome do r otulo na posi ca o para a qual vamos dar o salto seguido de :. O goto pode saltar para um r otulo que esteja mais ` a frente ou para tr as no programa. Uma observa ca o importante e que o r otulo e o goto devem estar dentro da mesma fun ca o. Como exemplo do uso do goto vamos reescrever o equivalente ao comando for apresentado na se ca o equivalente ao mesmo: inicializa ca o; inicio do loop: if (condi ca o) { declara ca o; incremento; goto inicio do loop; } O comando goto deve ser utilizado com parcim onia, pois o abuso no seu uso tende a tornar o c odigo confuso. O goto n ao e um comando necess ario, podendo sempre ser substitu do por outras estruturas de controle. Puristas da programa ca o estruturada recomendam que o goto nunca seja usado. Por em, em algumas situa co es muito espec cas o comando goto pode tornar um c odigo mais f acil de se entender se ele for bem empregado. Um caso em que ele pode ser u til e quando temos v arios loops e ifs aninhados e se queira, por algum motivo, sair destes loops e ifs todos de uma vez. Neste caso um goto resolve o problema mais elegantemente que v arios breaks, sem contar que os breaks exigiriam muito mais testes. Ou seja, neste caso o goto e mais elegante e mais r apido. O exemplo da p agina anterior pode ser reescrito usando-se o goto: #include <stdio.h> int main() { int opcao; while (opcao != 5) { REFAZ: printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5) || (opcao < 1)) goto REFAZ; switch (opcao) { case 1: break; case 2: break; case 3: break; case 4: break; case 5: break; } } /* Opcao invalida: volta ao rotulo REFAZ */

printf("\n --> Primeira opcao.."); printf("\n --> Segunda opcao.."); printf("\n --> Terceira opcao.."); printf("\n --> Quarta opcao.."); printf("\n --> Abandonando..");

4 ESTRUTURAS DE CONTROLE DE FLUXO

39

return(0); }

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

40

5
5.1

Tipos de dados indexadas: matrizes e strings


Vetores

imporVetores nada mais s ao que matrizes unidimensionais. Vetores s ao uma estrutura de dados muito utilizada. E tante notar que vetores, matrizes bidimensionais e matrizes de qualquer dimens ao s ao caracterizadas por terem todos os elementos pertencentes ao mesmo tipo de dado. Para se declarar um vetor podemos utilizar a seguinte forma geral: avel nome da vari avel [tamanho]; tipo da vari Quando o C v e uma declara ca o como esta ele reserva um espa co na mem oria sucientemente grande para armazenar o n umero de c elulas especicadas em tamanho. Por exemplo, se declararmos: oat exemplo [20]; o C ir a reservar 4x20=80 bytes. Estes bytes s ao reservados de maneira cont gua. Na linguagem C a numera ca o come ca sempre em zero. Isto signica que, no exemplo acima, os dados ser ao indexados de 0 a 19. Para acess a-los vamos escrever: exemplo[0] exemplo[1] ... exemplo[19]. Mas ningu em o impede de escrever: exemplo[30] exemplo[103]. Por qu e? Porque o C n ao verica se o ndice que voc e usou est a dentro dos limites v alidos. Este e um cuidado que voc e deve tomar. Se o programador n ao tiver aten ca o com os limites de validade para os ndices ele corre o risco de ter vari aveis sobreescritas ou de ver o computador travar. Bugs terr veis podem surgir. Vamos ver agora um exemplo de utiliza ca o de vetores: #include <stdio.h> int main() { int num[100]; /* Declara um vetor de inteiros de 100 posicoes */ int count = 0; int totalnums; do { printf("\nEntre com um numero (-999 p/ terminar): "); scanf("%d", &num[count]); count++; } while (num[count-1] != -999); totalnums = count - 1; printf("\n\n\n\t Os n\umeros que voc\^e digitou foram:\n\n"); for (count = 0; count < totalnums; count++) printf(" %d",num[count]); return(0); } No exemplo acima, o inteiro count e inicializado em 0. O programa pede pela entrada de n umeros at e que o usu ario entre com o Flag -999. Os n umeros s ao armazenados no vetor num. A cada n umero armazenado, o contador do vetor e incrementado para na pr oxima itera ca o escrever na pr oxima posi ca o do vetor. Quando o usu ario digita o ag, o programa abandona o primeiro loop e armazena o total de n umeros gravados. Por m, todos os n umeros s ao bom lembrar aqui que nenhuma restri impressos. E ca o e feita quanto a quantidade de n umeros digitados. Se o usu ario digitar mais de 100 n umeros, o programa tentar a ler normalmente, mas o programa os escrever a em uma parte n ao alocada de mem oria, pois o espa co alocado foi para somente 100 inteiros. Isto pode resultar nos mais variados erros no instante da execu ca o do programa.

5.2

Vetores de carateres: Strings

Strings s ao vetores de chars. As strings s ao o uso mais comum para os vetores. Devemos apenas car atentos para o fato de que as strings t em o seu u ltimo elemento como um \0. A declara ca o geral para uma string e: char nome da string[tamanho];

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

41

Devemos lembrar que o tamanho da string deve incluir o \0 nal. A biblioteca padr ao do C possui diversas fun co es que manipulam strings. Estas fun co es s ao u teis pois n ao se pode, por exemplo, igualar duas strings: string1 = string2; /* Errado! */

Fazer isto e um desastre. Quando voc e terminar de ler a se ca o que trata de ponteiros voc e entender a porqu e. As strings devem ser igualadas elemento a elemento. Quando vamos fazer programas que tratam de string muitas vezes podemos fazer bom proveito do fato de que uma string termina com \0 (isto e, o n umero inteiro 0). Veja, por exemplo, o programa abaixo que serve para igualar duas strings (isto e, copia os caracteres de uma string para o vetor da outra): #include <stdio.h> int main() { int count; char str1[100], str2[100]; .... /* Aqui o programa le str1 que sera copiada para str2 */ for (count = 0; str1[count]; count++) str2[count] = str1[count]; str2[count] = \0; .... /* Aqui o programa continua */ } A condi ca o no loop for acima e baseada no fato de que a string que est a sendo copiada termina em \0. Quando o elemento encontrado em str1[count] e o \0, o valor retornado para o teste condicional e falso (nulo). Desta forma a express ao que vinha sendo verdadeira (n ao zero) continuamente, torna-se falsa. Vamos ver agora algumas fun co es b asicas para manipula ca o de strings. 5.2.1 Fun c ao de entrada gets

A fun ca o gets() l e uma string do teclado. Sua forma geral e: gets(nome da string); O programa abaixo demonstra o funcionamento da fun ca o gets(): #include <stdio.h> int main() { char string[100]; printf("Digite o seu nome: "); gets(string); printf("\n\n Ola %s",string); return(0); } Repare que e v alido passar para a fun ca o printf() o nome da string. Voc e ver a mais adiante porque isto e v alido. Como o primeiro argumento da fun ca o printf() e uma string tamb em e v alido fazer: printf(string); isto simplesmente imprimir a a string.

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

42

5.2.2

A fun c ao strcpy

Sua forma geral e: strcpy(string destino, string origem); A fun ca o strcpy() copia a string-origem para a string- destino. Seu funcionamento e semelhante ao da rotina apresentada na se ca o anterior. As fun co es apresentadas nestas se co es est ao no arquivo cabe calho string.h. A seguir apresentamos um exemplo de uso da fun ca o strcpy(): #include <stdio.h> #include <string.h> int main() { char str1[100], str2[100], str3[100]; printf("Entre com uma string: "); gets(str1); strcpy(str2,str1); strcpy(str3, "Voce digitou a string "); printf("\n\n%s%s", str3, str2); return(0); } /* Copia str1 em str2 */ /* Copia "Voce digitou a string" em str3 */

5.2.3

A fun c ao strcat

A fun ca o strcat() tem a seguinte forma geral: strcat (string destino, string origem); A string de origem permanecer a inalterada e ser a anexada ao m da string de destino. Um exemplo: #include <stdio.h> #include <string.h> int main() { char str1[100], str2[100]; printf("Entre com uma string: "); gets(str1); strcpy(str2, "Voce digitou a string "); strcat (str2, str1); /* str2 armazenara Voce digitou a string + o conteudo de str1 */ printf("\n\n%s", str2); return(0); }

5.2.4

A fun c ao strlen

Sua forma geral e: strlen (string);

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

43

A fun ca o strlen() retorna o comprimento da string fornecida. O terminador nulo n ao e contado. Isto quer dizer que, de fato, o comprimento do vetor da string deve ser um a mais que o inteiro retornado por strlen(). Um exemplo do seu uso: #include <stdio.h> #include <string.h> int main() { int size; char str[100]; printf("Entre com uma string: "); gets(str); size=strlen(str); printf("\n\nA string que voce digitou tem tamanho %d", size); return(0); }

5.2.5

A fun c ao strcmp

Sua forma geral e: strcmp (string1, string2); A fun ca o strcmp() compara a string 1 com a string 2. Se as duas forem id enticas a fun ca o retorna zero. Se elas forem diferentes a fun ca o retorna n ao-zero. Um exemplo da sua utiliza ca o: #include <stdio.h> #include <string.h> int main() { char str1[100], str2[100]; printf("Entre com uma string: "); gets(str1); printf("\n\nEntre com outra string: "); gets(str2); if (strcmp(str1, str2)) printf("\n\nAs duas strings s\~ao diferentes."); else printf("\n\nAs duas strings s\~ao iguais."); return(0); }

5.3
5.3.1

Matrizes
Matrizes bidimensionais

Matrizes bidimensionais J a vimos como declarar matrizes unidimensionais (vetores). Vamos tratar agora de matrizes bidimensionais. A forma geral da declara ca o de uma matriz bidimensional e muito parecida com a declara ca o de um vetor: avel nome da vari avel[altura][largura]; tipo da vari

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

44

muito importante ressaltar que, nesta estrutura, o E ndice da esquerda indexa as linhas e o da direita indexa as colunas. Quando vamos preencher ou ler uma matriz no C o ndice mais ` a direita varia mais rapidamente que o ndice a esquerda. Mais uma vez ` e bom lembrar que, na linguagem C, os ndices variam de zero ao valor declarado, menos um; mas o C n ao vai vericar isto para o usu ario. Manter os ndices na faixa permitida e tarefa do programador. Abaixo damos um exemplo do uso de uma matriz: #include <stdio.h> int main() { int mtrx[20][10]; int i, j, count; count = 1; for (i = 0; i < 20; i++) for (j = 0;j < 10; j++) { mtrx[i][j] = count; count++; } return(0); } No exemplo acima, a matriz mtrx e preenchida, sequencialmente por linhas, com os n umeros de 1 a 200. Voc e deve entender o funcionamento do programa acima antes de prosseguir. 5.3.2 Matrizes de strings

Matrizes de strings s ao matrizes bidimensionais. Imagine uma string. Ela e um vetor. Se zermos um vetor de strings estaremos fazendo uma lista de vetores. Esta estrutura e uma matriz bidimensional de chars. Podemos ver a forma geral de uma matriz de strings como sendo: avel[num de strings][compr das strings]; char nome da vari s A surge a pergunta: como acessar uma string individual? F acil. E o usar apenas o primeiro ndice. Ent ao, para acessar uma determinada string fa ca: nome da vari avel[ ndice] Aqui est a um exemplo de um programa que l e 5 strings e as exibe na tela: #include <stdio.h> int main() { char strings[5][100]; int count; for (count = 0; count < 5; count++) { printf("\n\nDigite uma string: "); gets(strings[count]); } printf("\n\n\nAs strings que voce digitou foram:\n\n"); for (count = 0; count < 5; count++) printf("%s\n", strings[count]);

5 TIPOS DE DADOS INDEXADAS: MATRIZES E STRINGS

45

return(0); }

5.3.3

Matrizes multidimensionais

O uso de matrizes multidimensionais na linguagem C e simples. Sua forma geral e: tipo da vari avel nome da vari avel[tam1][tam2] ... [tamN]; Uma matriz N-dimensional funciona basicamente como outros tipos de matrizes. Basta lembrar que o ndice que varia mais rapidamente e o ndice mais ` a direita. 5.3.4 Inicializa c ao de vari aveis indexadas

Podemos inicializar matrizes, assim como podemos inicializar vari aveis. A forma geral de uma matriz como inicializa ca o e: tipo da vari avel nome da vari avel[tam1][tam2] ... [tamN] = lista de valores; A lista de valores e composta por valores (do mesmo tipo da vari avel) separados por v rgula. Os valores devem ser dados na ordem em que ser ao colocados na matriz. Abaixo vemos alguns exemplos de inicializa co es de matrizes: float vect[6] = { 1.3, 4.5, 2.7, 4.1, 0.0, 100.1 }; int matrx[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; char str[10] = { J, o, a, o, \0 }; char str[10] = "Joao"; char str_vect[3][10] = { "Joao", "Maria", "Jose" }; O primeiro demonstra inicializa ca o de vetores. O segundo exemplo demonstra a inicializa ca o de matrizes multidimensionais, onde matrx est a sendo inicializada com 1, 2, 3 e 4 em sua primeira linha, 5, 6, 7 e 8 na segunda linha e 9, 10, 11 e 12 na u ltima linha. No terceiro exemplo vemos como inicializar uma string e, no quarto exemplo, um modo mais compacto de inicializar uma string. O quinto exemplo combina as duas t ecnicas para inicializar um vetor de strings. Repare que devemos incluir o ; no nal da inicializa ca o. 5.3.5 Inicializa c ao sem especica co de tamanho

Podemos, em alguns casos, inicializar matrizes das quais n ao sabemos o tamanho a priori. O compilador C vai, neste caso vericar o tamanho do que voc e declarou e considerar como sendo o tamanho da matriz. Isto ocorre na hora da compila ca o e n ao poder a mais ser mudado durante o programa, sendo muito u til, por exemplo, quando vamos inicializar uma string e n ao queremos contar quantos caracteres ser ao necess arios. Alguns exemplos: char mess [] = "Linguagem C: flexibilidade e poder."; int matrx [][2] = { 1, 2, 2, 4, 3, 6, 4, 8, 5, 10 }; No primeiro exemplo, a string mess ter a tamanho 36. Repare que o artif cio para realizar a inicializa ca o sem especica ca o de tamanho e n ao especicar o tamanho! No segundo exemplo o valor n ao especicado ser a 5.

6 PONTEIROS

46

Ponteiros

OC e altamente dependente dos ponteiros. Para ser um bom programador em C e fundamental que se tenha um bom dom nio deles. Por isto, recomendo ao leitor um carinho especial com esta parte do curso que trata deles. Ponteiros s ao t ao importantes na linguagem C que voc e j a os viu e nem percebeu, pois mesmo para se fazer um introdu ca o b asica ` a linguagem C precisa-se deles. O uso descuidado de ponteiros pode levar a s erios bugs .

6.1

A utilidade de ponteiros

Os ints guardam inteiros. Os oats guardam n umeros de ponto utuante. Os chars guardam caracteres. Ponteiros guardam endere cos de mem oria. Quando voc e anota o endere co de um colega voc e est a criando um ponteiro. O ponteiro e este seu peda co de papel. Ele tem anotado um endere co. Qual e o sentido disto? Simples. Quando voc e anota o endere co de um colega, depois voc e vai usar este endere co para ach a-lo. O C funciona assim. Voc e anota o endere co de algo numa vari avel ponteiro para depois usar. Da mesma maneira, uma agenda, onde s ao guardados endere cos de v arios amigos, poderia ser vista como sendo uma matriz de ponteiros no C. Um ponteiro tamb em tem tipo. Veja: quando voc e anota um endere co de um amigo voc e o trata diferente de quando voc e anota o endere co de uma rma. Apesar de o endere co dos dois locais ter o mesmo formato (rua, n umero, bairro, cidade, etc.) eles indicam locais cujos conte udos s ao diferentes. Ent ao os dois endere cos s ao ponteiros de tipos diferentes. No C quando declaramos ponteiros n os informamos ao compilador para que tipo de vari avel vamos apont a-lo. Um ponteiro int aponta para um inteiro, isto e, guarda o endere co de um inteiro.

6.2

A declara c ao e a utiliza c ao de ponteiros

Para declarar um ponteiro temos a seguinte forma geral: avel; tipo do ponteiro *nome da vari o asterisco (*) que faz o compilador saber que aquela vari E avel n ao vai guardar um valor mas sim um endere co para aquele tipo especicado. Vamos ver exemplos de declara co es: int *pt; char *temp,*pt2; O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para caracteres. Eles ainda n ao foram inicializados (como toda vari avel do C que e apenas declarada). Isto signica que eles apontam para um lugar indenido. Este lugar pode estar, por exemplo, na por ca o da mem oria reservada ao sistema operacional do computador. Usar o ponteiro nestas circunst anicias pode levar a um travamento do micro, ou a algo pior. O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado! Isto e muito importante. Para atribuir um valor a um ponteiro rec em-criado poder amos igual a-lo a um valor de mem oria. Mas, como saber a posi ca o na mem oria de uma vari avel do nosso programa? Seria muito dif cil saber o endere co de cada vari avel que usamos, mesmo porque estes endere cos s ao determinados pelo compilador na hora da compila ca o e realocados na execu ca o. Podemos ent ao deixar que o compilador fa ca este trabalho por n os. Para saber o endere co de uma vari avel basta usar o operador &. Veja o exemplo: int count=10; int *pt; pt=&count; Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A express ao &count nos d a o endere co de count, o qual armazenamos em pt. Repare que n ao alteramos o valor de count, que continua valendo 10. Como n os colocamos um endere co em pt, ele est a agora liberadopara ser usado. Podemos, por exemplo, alterar o valor o operador *. No exemplo acima, de count usando pt. Para tanto vamos usar o operador inversodo operador &. E uma vez que zemos pt = &count a express ao *pt e equivalente ao pr oprio count. Isto signica que, se quisermos mudar o valor de count para 12, basta fazer *pt = 12. Vamos fazer uma pausa e voltar ` a nossa analogia para ver o que est a acontecendo. Digamos que exista uma rma. Ela e como uma vari avel que j a foi declarada. Voc e tem um papel em branco onde vai anotar o endere co da rma. O papel e um ponteiro do tipo rma. Voc e ent ao liga para a rma e pede o seu endere co, o qual voc e vai anotar no papel. Isto e equivalente, no C, a associar o papel ` a rma com o operador &. Ou seja, o operador & aplicado ` a rma e equivalente a voc e ligar para a mesma e pedir o endere co. Uma vez de posse do endere co no papel voc e poderia, por exemplo, fazer uma visita ` a rma. No C voc e faz uma visita ` a rma aplicando o operador * ao papel. Uma vez dentro da rma voc e pode copiar seu conte udo ou modic a-lo. Uma observa ca o importante: apesar do s mbolo ser o mesmo, o operador * (multiplica ca o) n ao e o mesmo operador que o * (refer encia de ponteiros). Para come car o primeiro e bin ario, e o segundo e un ario pr e-xado. Aqui v ao dois exemplos de usos simples de ponteiros:

6 PONTEIROS

47

#include <stdio.h> int main() { int num,valor; int *p; num=55; p=&num; /* Pega o endereco de num */ valor=*p; /* Valor e igualado a num de uma maneira indireta */ printf("\n\n%d\n",valor); printf("Endereco para onde o ponteiro aponta: %p\n",p); printf("Valor da variavel apontada: %d\n",*p); return(0); } #include <stdio.h> int main() { int num,*p; num=55; p=&num; /* Pega o endereco de num */ printf("\nValor inicial: %d\n",num); *p=100; /* Muda o valor de num de uma maneira indireta */ printf("\nValor final: %d\n",num); return(0); } Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o c odigo %p usado na fun ca o printf() indica ` a fun ca o que ela deve imprimir um endere co. Podemos fazer algumas opera co es aritm eticas com ponteiros. A primeira, e mais simples, e igualar dois ponteiros. Se temos dois ponteiros p1 e p2 podemos igual a-los fazendo p1 = p2. Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que a vari avel apontada por p1 tenha o mesmo conte udo da vari avel apontada por p2 devemos fazer *p1 = *p2. Basicamente, depois que se aprende a usar os dois operadores (& e *) ca f acil entender opera co es com ponteiros. As pr oximas opera co es, tamb em muito usadas, s ao o incremento e o decremento. Quando incrementamos um ponteiro ele passa a apontar para o pr oximo valor do mesmo tipo para o qual o ponteiro aponta. Isto e, se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o pr oximo inteiro. Esta e mais uma raz ao pela qual o compilador precisa saber o tipo de um ponteiro: se voc e incrementa um ponteiro char* ele anda 1 byte na mem oria e se voc e incrementa um ponteiro double* ele anda 8 bytes na mem oria. O decremento funciona semelhantemente. Supondo que p e um ponteiro, as opera co es s ao escritas como: p++; p--; Mais uma vez insisto. Estamos falando de opera co es com ponteiros e n ao de opera co es com o conte udo das vari aveis para as quais eles apontam. Por exemplo, para incrementar o conte udo da vari avel apontada pelo ponteiro p, fazse: (*p)++; Outras opera co es aritm eticas u teis s ao a soma e subtra ca o de inteiros com ponteiros. Vamos supor que voc e queira incrementar um ponteiro de 15. Basta fazer: p = p + 15; *(p + 15); A subtra ca o funciona da mesma maneira.

6 PONTEIROS

48

Uma outra opera ca o, ` as vezes u til, e a compara ca o entre dois ponteiros. Mas que informa ca o recebemos quando comparamos dois ponteiros? Bem, em primeiro lugar, podemos saber se dois ponteiros s ao iguais ou diferentes (== e ! =). No caso de opera co es do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posi ca o mais alta na mem oria. Ent ao uma compara ca o entre ponteiros pode nos dizer qual dos dois est a mais adiantena mem oria. A compara ca o entre dois ponteiros se escreve como a compara ca o entre outras duas vari aveis quaisquer: ou p += 15; E se voc e quiser usar o conte udo do ponteiro 15 posi co es adiante: p1 > p2 H a entretanto opera co es que voc e n ao pode efetuar num ponteiro. Voc e n ao pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair oats ou doubles de ponteiros.

6.3

O uso de ponteiros com vetores

Veremos nestas se co es que ponteiros e vetores t em uma liga ca o muito forte. 6.3.1 Vetores como ponteiros

Vamos dar agora uma id eia de como o C trata vetores. Quando voc e declara uma matriz da seguinte forma: avel nome da vari avel[tam1][tam2] ... [tamN]; tipo da vari O compilador C calcula o tamanho, em bytes, necess ario para armazenar esta matriz. Este tamanho e: tam1 tam2 tam3 ... tamN tamanho do tipo. O compilador ent ao aloca este n umero de bytes em um espa co livre de mem oria. O nome da vari avel que voc e declarou e na verdade um ponteiro para o tipo da vari avel da matriz. Este conceito e fundamental. Eis porque: Tendo alocado na mem oria o espa co para a matriz, ele toma o nome da vari avel (que e um ponteiro) e aponta para o primeiro elemento da matriz. Mas a surge a pergunta: ent ao como e que podemos usar a seguinte nota ca o? avel[ ndice] nome da vari Isto pode ser facilmente explicado desde que voc e entenda que a nota ca o acima e absolutamente equivalente a se fazer: *(nome_da_variavel + indice) Agora podemos entender como e que funciona um vetor! Vamos ver o que podemos tirar de informa ca o deste fato. Fica porque, ao pegarmos o valor do primeiro claro, por exemplo, porque e que, no C, a indexa ca o come ca com zero. E elemento de um vetor, queremos, de fato, *nome da vari avel e ent ao devemos ter um ndice igual a zero. Ent ao sabemos que: *nome da vari avel e equivalente a nome da vari avel[0]. Outra coisa: apesar de, na maioria dos casos, n ao fazer muito sentido, poder amos ter ndices negativos. Estar amos pegando posi co es de mem oria antes do vetor. Isto explica tamb em porque o C n ao verica a validade dos ndices. Ele n ao sabe o tamanho do vetor. Ele apenas aloca a mem oria, ajusta o ponteiro do nome do vetor para o in cio do mesmo e, quando voc e usa os ndices, encontra os elementos requisitados. Vamos ver agora um dos usos mais importantes dos ponteiros: a varredura sequencial de uma matriz. Quando temos que varrer todos os elementos de uma matriz de uma forma sequencial, podemos usar um ponteiro, o qual vamos incrementando. Qual a vantagem? Considere o seguinte programa para zerar uma matriz: int main() { float matrx [50][50]; int i,j; for (i = 0; i < 50; i++) for (j = 0; j < 50; j++) matrx[i][j] = 0.0; return(0); }

6 PONTEIROS

49

Podemos reescrev e-lo usando ponteiros: int main() { float matrx [50][50]; float *p; int count; p=matrx[0]; for (count = 0; count < 2500; count++) { *p=0.0; p++; } return(0); } No primeiro programa, cada vez que se faz matrx[i][j] o programa tem que calcular o deslocamento para dar ao ponteiro. Ou seja, o programa tem que calcular 2500 deslocamentos. No segundo programa o u nico c alculo que deve ser feito e o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro e muito mais r apido que calcular 2500 deslocamentos completos. H a uma diferen ca entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro e uma vari avel, mas o nome de um vetor n ao e uma vari avel. Isto signica, que n ao se consegue alterar o endere co que e apontado pelo nome do vetor. Seja: int vetor[10]; int *ponteiro, i; ponteiro = &i; /* as operacoes a seguir sao invalidas */ vetor = vetor + 2; vetor++; vetor = ponteiro; /* ERRADO: vetor nao e variavel */ /* ERRADO: vetor nao e variavel */ /* ERRADO: vetor nao e variavel */

Teste as opera co es acima no seu compilador. Ele dar a uma mensagem de erro. Alguns compiladores dir ao que vetor n ao e um Lvalue. Lvalue, signica Left value, um s mbolo que pode ser colocado do lado esquerdo de uma express ao de atribui ca o, isto e, uma vari avel. Outros compiladores dir ao que tem-se incompatible types in assignment, tipos incompat veis em uma atribui ca o. /* as operacoes abaixo sao validas */ /* CERTO: ponteiro e variavel */ /* CERTO: ponteiro e variavel */

ponteiro = vetor; ponteiro = vetor+2;

6.3.2

Ponteiros como vetores

Sabemos agora que, na verdade, o nome de um vetor e um ponteiro constante. Sabemos tamb em que podemos indexar o nome de um vetor. Como consequ encia podemos tamb em indexar um ponteiro qualquer. O programa mostrado a seguir funciona perfeitamente: #include <stdio.h> int main()

6 PONTEIROS

50

{ int matrx [10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *p; p=matrx; printf("O terceiro elemento do vetor e: %d", p[2]); return(0); } Podemos ver que p[2] equivale a *(p+2). 6.3.3 Strings

Strings Seguindo o racioc nio acima, nomes de strings, s ao do tipo char*. Isto nos permite escrever a nossa fun ca o StrCpy(), que funcionar a de forma semelhante ` a fun ca o strcpy() da biblioteca: #include <stdio.h> void StrCpy(char *destino,char *origem) { while (*origem) { *destino = *origem; origem++; destino++; } *destino = \0; } int main() { char str1[100],str2[100],str3[100]; printf("Entre com uma string: "); gets(str1); StrCpy(str2,str1); StrCpy(str3,"Voce digitou a string "); printf("\n\n%s%s",str3,str2); return(0); } H a v arios pontos a destacar no programa acima. Observe que podemos passar ponteiros como argumentos de fun co es. Na verdade e assim que fun co es como gets() e strcpy() funcionam. Passando o ponteiro voc e possibilita ` a fun ca o alterar o conte udo das strings. Voc e j a estava passando os ponteiros e n ao sabia. No comando while (*origem) estamos usando o fato de que a string termina com \0 como crit erio de parada. Quando fazemos origem++ e destino++ o leitor poderia argumentar que estamos alterando o valor do ponteiro-base da string, contradizendo o que recomendei que se deveria fazer, no nal de uma se ca o anterior. O que o leitor talvez n ao saiba ainda (e que ser a estudado em detalhe mais adiante) e que, no C, s ao passados para as fun co es c opias dos argumentos. Desta maneira, quando alteramos o ponteiro origem na fun ca o StrCpy() o ponteiro str2 permanece inalterado na fun ca o main(). 6.3.4 Endere cos de elementos de vetores

avel[ ndice] e v alida e retorna o endere co do ponto Nesta se ca o vamos apenas ressaltar que a nota ca o &nome da vari interessante notar que, como do vetor indexado por ndice. Isto seria equivalente a nome da vari avel + indice. E consequ encia, o ponteiro nome da vari avel tem o endere co &nome da vari avel[0], que indica onde na mem oria est a guardado o valor do primeiro elemento do vetor.

6 PONTEIROS

51

6.3.5

Vetores de ponteiros

Podemos construir vetores de ponteiros como declaramos vetores de qualquer outro tipo. Uma declara ca o de um vetor de ponteiros inteiros poderia ser: int *pmatrx[10]; No caso acima, pmatrx e um vetor que armazena 10 ponteiros para inteiros.

6.4

Inicializa c ao de ponteiros

Podemos inicializar ponteiros. Vamos ver um caso interessante dessa inicializa ca o de ponteiros com strings. Precisamos, para isto, entender como o C trata as strings constantes. Toda string que o programador insere no programa e colocada num banco de strings que o compilador cria. No local onde est a uma string no programa, o compilador coloca o endere co por isto que podemos usar strcpy() do seguinte modo: do in cio daquela string (que est a no banco de strings). E strcpy(string, String constante.); strcpy() pede dois par ametros do tipo char*. Como o compilador substitui a string String constante.pelo seu endere co no banco de strings, tudo est a bem para a fun ca o strcpy(). O que isto tem a ver com a inicializa ca o de ponteiros? E que, para uma string que vamos usar v arias vezes, podemos fazer: char *str1 = "String constante."; A poder amos, em todo lugar que precisarmos da string, usar a vari avel str1. Devemos apenas tomar cuidado ao usar este ponteiro. Se o alterarmos vamos perder a string. Se o usarmos para alterar a string podemos facilmente corromper o banco de strings que o compilador criou. Mais uma vez ca o aviso: ponteiros s ao poderosos mas, se usados com descuido, podem ser uma otima fonte de dores de cabe ca.

6.5

Encadeiar ponteiros para ponteiros

Um ponteiro para um ponteiro e como se voc e anotasse o endere co de um papel que tem o endere co da casa do seu amigo. Podemos declarar um ponteiro para um ponteiro com a seguinte nota ca o: tipo da vari avel **nome da vari avel; avel e o conte udo nal da vari avel apontada; *nome da vari avel e o conte udo Algumas considera co es: **nome da vari do ponteiro intermedi ario. No C podemos declarar ponteiros para ponteiros para ponteiros, ou ent ao, ponteiros para ponteiros para ponteiros para ponteiros e assim por diante. Para fazer isto basta aumentar o n umero de asteriscos na declarac ao. A l ogica e a mesma. Para acessar o valor desejado apontado por um ponteiro para ponteiro, o operador asterisco deve ser aplicado duas vezes, como mostrado no exemplo abaixo: #include <stdio.h> int main() { float fpi = 3.1415, *pf, **ppf; pf = &fpi; /* ppf = &pf; /* printf("%f", **ppf); /* printf("%f", *pf); /* return(0); } pf armazena o endereco de fpi */ ppf armazena o endereco de pf */ Imprime o valor de fpi */ Tambem imprime o valor de fpi */

6 PONTEIROS

52

6.6

Erros comuns na utiliza c ao de ponteiros

O principal cuidado ao se usar um ponteiro deve ser: saiba sempre para onde o ponteiro est a apontando. Isto inclui: nunca use um ponteiro que n ao foi inicializado. Um pequeno programa que demonstra como n ao usar um ponteiro: int main() /* Nao Execute este programa, ele esta ERRADO!!! */ { int x, *p; x = 13; *p=x; return(0); } Este programa compilar a e rodar a. O que acontecer a? Ningu em sabe. O ponteiro p pode estar apontando para qualquer lugar. Voc e estar a gravando o n umero 13 em um lugar desconhecido. Com um n umero apenas, voc e provavelmente n ao vai ver nenhum defeito. Agora, se voc e come car a gravar n umeros em posi co es aleat orias no seu computador, n ao vai demorar muito para travar o micro (se n ao acontecer coisa pior).

7 FUNC OES

53

Fun c oes

Fun co es s ao as estruturas que permitem ao usu ario separar seus programas em blocos. Se as fun co es n ao existissem, os programas teriam que ser curtos e de pequena complexidade. Para fazermos programas grandes e complexos temos de constru -los bloco a bloco.

7.1

A estrutura de uma fun c ao

Uma fun ca o no C tem a seguinte forma geral: tipo de retorno nome da fun ca o(declara ca o de par ametros) corpo da fun ca o O tipo-de-retorno e o tipo de vari avel que a fun ca o vai retornar. O default e o tipo int, ou seja, uma fun ca o para qual n ao declaramos o tipo de retorno e considerada como retornando um inteiro. A declara ca o de par ametros e uma lista com a seguinte forma geral: tipo nome1, tipo nome2, ... , tipo nomeN. Repare que o tipo deve ser especicado para na declara cada uma das N vari aveis de entrada. E ca o de par ametros que informamos ao compilador quais ser ao as nele que entradas da fun ca o (assim como informamos a sa da no tipo-de-retorno). O corpo da fun ca o e a sua alma. E as entradas s ao processadas, sa das s ao geradas ou outras coisas s ao feitas.

7.2

Utilizando a fun c ao para retornar velores: o comando return

O comando return tem a seguinte forma geral: return valor de retorno; ou return; Digamos que uma fun ca o est a sendo executada. Quando se chega a uma declara ca o return a fun ca o e encerrada importante lembrar que o valor de imediatamente e, se o valor de retorno e informado, a fun ca o retorna este valor. E retorno fornecido tem que ser compat vel com o tipo de retorno declarado para a fun ca o. Uma fun ca o pode ter mais de uma declara ca o return. Isto se torna claro quando pensamos que a fun ca o e terminada quando o programa chega a primeira declara ` ca o return. Abaixo est ao dois exemplos de uso do return: #include <stdio.h> int Square (int a) { return(a*a); } int main() { int num; printf("Entre com um numero: "); scanf("%d",&num); num = Square(num); printf("\n\nO seu quadrado vale: %d\n",num); return 0; }

#include <stdio.h> int EPar(int a) { if (a%2)

/* Verifica se a e divisivel por dois */

7 FUNC OES

54

return 0; /* Retorna 0 se nao for divisivel */ else return 1; /* Retorna 1 se for divisivel */ } int main() { int num; printf("Entre com numero: "); scanf("%d",&num); if (EPar(num)) printf("\n\nO numero e par.\n"); else printf("\n\nO numero e impar.\n"); return 0; } importante notar que, como as fun E co es retornam valores, podemos aproveit a-los para fazer atribui co es, ou mesmo para que estes valores participem de express oes. Mas n ao podemos fazer: func(a, b) = x; /* Errado! */

No segundo exemplo vemos o uso de mais de um return em uma fun ca o. Fato importante: se uma fun ca o retorna um valor voc e n ao precisa aproveitar este valor. Se voc e n ao zer nada com o valor de retorno de uma fun ca o ele ser a descartado. Por exemplo, a fun ca o printf() retorna um inteiro que n os nunca usamos para nada. Ele e descartado. Veja como voc e est a. Escreva a fun ca o EDivisivel(int a, int b) (tome como base EPar(int a)). A fun ca o dever a retornar 1 se o resto da divis ao de a por b for zero. Caso contr ario, a fun ca o dever a retornar zero.

7.3

Prot otipos de Fun c oes

At e agora, nos exemplos apresentados, escrevemos as fun c oes antes de escrevermos a fun ca o main(). Isto e, as fun c oes est ao sicamente antes da fun ca o main(). Isto foi feito por uma raz ao. Imagine-se na pele do compilador. Se voc e fosse compilar a fun ca o main(), onde s ao chamadas as fun co es, voc e teria que saber com anteced encia quais s ao os tipos de retorno e quais s ao os par ametros das fun co es para que voc e pudesse gerar o c odigo corretamente. Foi por isto as fun co es foram colocadas antes da fun ca o main(): quando o compilador chegasse ` a fun ca o main() ele j a teria compilado as fun co es e j a saberia seus formatos. Mas, muitas vezes, n ao poderemos nos dar ao luxo de escrever nesta ordem. Muitas vezes teremos o nosso programa espalhado por v arios arquivos. Ou seja, estaremos chamando fun co es em um arquivo que ser ao compiladas em outro arquivo. Como manter a coer encia? A solu ca o s ao os prot otipos de fun co es. Prot otipos s ao nada mais, nada menos, que declara co es de fun co es. Isto e, voc e declara uma fun ca o que ir a usar. O compilador toma ent ao conhecimento do formato daquela fun ca o antes de compil a-la. O c odigo correto ser a ent ao gerado. Um prot otipo tem o seguinte formato: ca o (declara ca o de par ametros); tipo de retorno nome da fun onde o tipo de retorno, o nome da fun ca o e a declara ca o de par ametros s ao os mesmos que voc e pretende usar quando realmente escrever a fun ca o. Repare que os prot otipos t em uma n tida semelhan ca com as declara co es de vari aveis. Vamos implementar agora um dos exemplos da se ca o anterior com algumas altera co es e com prot otipos: #include <stdio.h> float Square(float a); int main() { float num; printf("Entre com um numero: ");

7 FUNC OES

55

scanf("%f", &num); num = Square(num); printf("\n\nO seu quadrado vale: %f\n",num); return 0; } float Square(float a) { return (a * a); } Observe que a fun ca o Square() est a colocada depois de main(), mas o seu prot otipo est a antes. Sem isto este programa n ao funcionaria corretamente. Usando prot otipos voc e pode construir fun co es que retornam quaisquer tipos de bom ressaltar que fun vari aveis. E co es podem tamb em retornar ponteiros sem qualquer problema. Os prot otipos n ao s o ajudam o compilador. Eles ajudam a voc e tamb em: usando prot otipos, o compilador evita erros, n ao deixando que o programador use fun co es com os par ametros errados e com o tipo de retorno errado, o que e uma grande ajuda!

7.4

O tipo void

Agora vamos ver o u nico tipo da linguagem C que n ao detalhamos ainda: o void. Em ingl es, void quer dizer vazio e e isto mesmo que o void e. Ele nos permite fazer fun co es que n ao retornam nada e fun co es que n ao t em par ametros! Podemos agora escrever o prot otipo de uma fun ca o que n ao retorna nada: ca o(declara ca o de par ametros); void nome da fun Numa fun ca o, como a acima, n ao temos valor de retorno na declara ca o return. Ali as, neste caso, o comando return n ao e necess ario na fun ca o. Podemos, tamb em, fazer fun co es que n ao t em par ametros: tipo de retorno nome da fun ca o (void); ou, ainda, que n ao tem par ametros e n ao retornam nada: ca o(void); void nome da fun Um exemplo de fun co es que usam o tipo void: #include <stdio.h> void Mensagem (void); int main() { Mensagem(); printf("\tDiga de novo:\n"); Mensagem(); return 0; } void Mensagem (void) { printf("Ola! Eu estou vivo.\n"); } Se quisermos que a fun ca o retorne algo, devemos usar a declara ca o return. Se n ao quisermos, basta declarar a fun ca o como tendo tipo-de-retorno void. Devemos lembrar agora que a fun ca o main() e uma fun ca o e como tal devemos trat a-la. O compilador acha que a fun ca o main() deve retornar um inteiro. Isto pode ser interessante se quisermos que o sistema operacional receba um valor de retorno da fun ca o main(). Se assim o quisermos, devemos nos lembrar da seguinte conven ca o: se o programa retornar zero, signica que ele terminou normalmente, e, se o programa retornar um valor diferente de zero, signica que o programa teve um t ermino anormal. Se n ao estivermos interessados neste tipo de coisa, basta declarar a fun ca o main como retornando void. As duas fun co es main() abaixo s ao v alidas:

7 FUNC OES

56

main(void) { .... return 0; }

void main(void) { .... }

7.5

Arquivos-Cabe calhos

Arquivos-cabe calhos s ao aqueles que temos mandado o compilador incluir no in cio de nossos exemplos e que sempre terminam em .h. A extens ao .h vem de header (cabe calho em ingl es). J a vimos exemplos como stdio.h, conio.h, string.h. Estes arquivos, na verdade, n ao possuem os c odigos completos das fun co es. Eles s o cont em prot otipos de o que basta. O compilador l fun co es. E e estes prot otipos e, baseado nas informa co es l a contidas, gera o c odigo correto. O corpo das fun co es cujos prot otipos est ao no arquivo-cabe calho, no caso das fun co es do pr oprio C, j a est ao compiladas e normalmente s ao inclu das no programa no instante da linkagem. Este e o instante em que todas as refer encias a fun co es cujos c odigos n ao est ao nos nossos arquivos fontes s ao resolvidas, buscando este c odigo nos arquivos de bibliotecas. Se voc e criar algumas fun co es que queira aproveitar em v arios programas futuros, ou m odulos de programas, voc e pode escrever arquivos-cabe calhos e inclu -los tamb em. Suponha que a fun ca o int EPar(int a), seja importante em v arios programas, e desejemos declar a-la num m odulo separado. No arquivo de cabe calho chamado por exemplo de funcao.h teremos a seguinte declara ca o: int EPar(int a);. O c odigo da fun ca o ser a escrito num arquivo a parte. Vamos cham a-lo de funcao.c. Neste arquivo teremos a deni ca o da fun ca o: int EPar (int a) /* Verifica se a e divisivel por dois */ { if (a % 2) return 0; else return 1; } Por m, no arquivo do programa principal teremos o programa principal. Vamos chamar este arquivo aqui de princip.c. #include <stdio.h> #include "funcao.h" void main() { int num; printf("Entre com numero: "); scanf("%d",&num); if (EPar(num)) printf("\n\nO numero e par.\n"); else printf("\n\nO numero e impar.\n"); } Este programa poderia ser compilado usando a seguinte linha de comando para o gcc: gcc princip.c funcao.c -o saida onde saida seria o arquivo execut avel gerado.

7 FUNC OES

57

7.6

Escopo de Vari aveis

J a foi dada uma introdu ca o ao escopo de vari aveis. O escopo e o conjunto de regras que determinam o uso e a validade de vari aveis nas diversas partes do programa. 7.6.1 Vari aveis locais

O primeiro tipo de vari aveis que veremos s ao as vari aveis locais. Estas s ao aquelas que s o t em validade dentro do bloco no qual s ao declaradas. Podemos declarar vari aveis dentro de qualquer bloco. S o para lembrar: um bloco come ca quando abrimos uma chave e termina quando fechamos a chave. At e agora s o t nhamos visto vari aveis locais para fun co es completas. Mas um comando for pode ter vari aveis locais e que n ao ser ao conhecidas fora dali. A declara ca o de vari aveis locais e a primeira coisa que devemos colocar num bloco. A caracter stica que torna as vari aveis locais t ao importantes e justamente a de serem exclusivas do bloco. Podemos ter quantos blocos quisermos com uma vari avel local chamada x, por exemplo, e elas n ao apresentar ao conito entre elas. A palavra reservada do C auto serve para dizer que uma vari avel e local. Mas n ao precisaremos us a-la pois as vari aveis declaradas dentro de um bloco j a s ao consideradas locais. Abaixo vemos um exemplo de vari aveis locais: func1(...) { int abc, x; ... } func(...) { int abc; ... } void main() { int a, x, y; for(...) { float a, b, c; ... } ... } No programa acima temos tr es fun co es. As vari aveis locais de cada uma delas n ao ir ao interferir com as vari aveis locais de outras fun co es. Assim, a vari avel abc de func1() n ao tem nada a ver (e pode ser tratada independentemente) com a vari avel abc de func2(). A vari avel x de func1() e tamb em completamente independente da vari avel x de main(). As vari aveis a, b e c s ao locais ao bloco for. Isto quer dizer que s o s ao conhecidas dentro deste bloco for e s ao desconhecidas no resto da fun ca o main(). Quando usarmos a vari avel a dentro do bloco for estaremos usando a vari avel a local ao for e n ao a vari avel a da fun ca o main(). 7.6.2 Par ametros formais

O segundo tipo de vari avel que veremos s ao os par ametros formais. Estes s ao declarados como sendo as entradas de f uma fun ca o. N ao h a motivo para se preocupar com o escopo deles. E acil: o par ametro formal e uma vari avel local da fun ca o. Voc e pode tamb em alterar o valor de um par ametro formal, pois esta altera ca o n ao ter a efeito na vari avel que foi passada ` a fun ca o. Isto tem sentido, pois quando o C passa par ametros para uma fun ca o, s ao passadas apenas c opias das vari aveis. Isto e, os par ametros formais existem independentemente das vari aveis que foram passadas para a fun ca o. Eles tomam apenas uma c opia dos valores passados para a fun ca o.

7 FUNC OES

58

7.6.3

Vari aveis globais

Vari aveis globais Vari aveis globais s ao declaradas, como j a sabemos, fora de todas as fun co es do programa. Elas s ao conhecidas e podem ser alteradas por todas as fun co es do programa. Quando uma fun ca o tem uma vari avel local com o mesmo nome de uma vari avel global a fun ca o dar a prefer encia ` a vari avel local. Vamos ver um exemplo: int z, k; func1(...) { int x, y; ... } func2(...) { int x, y, z; ... z = 10; ... } main() { int count; ... } No exemplo acima as vari aveis z e k s ao globais. Veja que func2() tem uma vari avel local chamada z. Quando temos ent ao, em func2(), o comando z = 10 quem recebe o valor de 10 e a vari avel local, n ao afetando o valor da vari avel global z. Evite ao m aximo o uso de vari aveis globais. Elas ocupam mem oria o tempo todo (as locais s o ocupam mem oria enquanto est ao sendo usadas) e tornam o programa mais dif cil de ser entendido e menos geral.

7.7

Passagem de argumentos por valor e por refer encia em fun c oes

J a vimos que, na linguagem C, quando chamamos uma fun ca o os par ametros formais da fun ca o copiam os valores dos par ametros que s ao passados para a fun ca o. Isto quer dizer que n ao s ao alterados os valores que os par ametros t em fora da fun ca o. Este tipo de chamada de fun ca o e denominado chamada por valor. Isto ocorre porque s ao passados para a fun ca o apenas os valores dos par ametros e n ao os pr oprios par ametros. Veja o exemplo abaixo: #include <stdio.h> float sqr(float num); void main() { float num, sq; printf("Entre com um numero: "); scanf("%f", &num); sq = sqr(num); printf("\n\nO numero original e: %f\n", num); printf("O seu quadrado vale: %f\n", sq); } float sqr(float num) {

7 FUNC OES

59

num = num * num; return num; } No exemplo acima o par ametro formal num da fun ca o sqr() sofre altera co es dentro da fun ca o, mas a vari avel num da fun ca o main() permanece inalterada: e uma chamada por valor. Outro tipo de passagem de par ametros para uma fun ca o ocorre quando altera co es nos par ametros formais, dentro da fun ca o, alteram os valores dos par ametros que foram passados para a fun ca o. Este tipo de chamada de fun c ao tem o nome de chamada por refer encia. Este nome vem do fato de que, neste tipo de chamada, n ao se passa para a fun ca o os valores das vari aveis, mas sim suas refer encias (a fun ca o usa as refer encias para alterar os valores das vari aveis fora da fun ca o). O C s o faz chamadas por valor. Isto e bom quando queremos usar os par ametros formais ` a vontade dentro da fun ca o, sem termos que nos preocupar em estar alterando os valores dos par ametros que foram passados para a fun ca o. Mas isto tamb em pode ser ruim ` as vezes, porque podemos querer mudar os valores dos par ametros fora da fun ca o tamb em. O C++ tem um recurso que permite ao programador fazer chamadas por refer encia. H a entretanto, no C, um recurso de programa ca o que podemos usar para simular uma chamada por refer encia. Quando queremos alterar as vari aveis que s ao passadas para uma fun ca o, n os podemos declarar seus par ametros formais como sendo ponteiros. Os ponteiros s ao a refer enciaque precisamos para poder alterar a vari avel fora da fun ca o. O u nico inconveniente e que, quando usarmos a fun ca o, teremos de lembrar de colocar um & na frente das vari aveis que estivermos passando para a fun ca o. Veja um exemplo: #include <stdio.h> void Swap(int *a, int *b); void main(void) { int num1, num2; num1 = 100; num2 = 200; Swap(&num1, &num2); printf("\n\nEles agora valem %d %d\n", num1, num2); } void Swap(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; } O que est a acontecendo e que passamos para a fun ca o Swap o endere co das vari aveis num1 e num2. Estes endere cos s ao copiados nos ponteiros a e b. Atrav es do operador * estamos acessando o conte udo apontado pelos ponteiros e modicando-o. Mas, quem e este conte udo? Nada mais que os valores armazenados em num1 e num2, que, portanto, assim que n est ao sendo modicados! E os chamamos a fun ca o scanf(). A fun ca o scanf() usa chamada por refer encia porque ela precisa alterar as vari aveis que passamos para ela! N ao e para isto mesmo que ela e feita? Ela l e vari aveis para n os e portanto precisa alterar seus valores. Por isto passamos para a fun ca o o endere co da vari avel a ser modicada!

7.8

Passando matrizes para fun c oes

Quando vamos passar um vetor como argumento de uma fun ca o, podemos declarar a fun ca o de tr es maneiras equivalentes. Seja o vetor: int matrx [50]; e que queiramos pass a-la como argumento de uma fun ca o func(). Podemos declarar func() das tr es maneiras seguintes: void func (int matrx[50]); void func (int matrx[]); void func (int *matrx); Nos tr es casos, teremos dentro de func() um int* chamado matrx. Ao passarmos um vetor para uma fun ca o, na

7 FUNC OES

60

realidade estamos passando um ponteiro. Neste ponteiro e armazenado o endere co do primeiro elemento do vetor. Isto signica que n ao e feita uma c opia, elemento a elemento do vetor. Isto faz com que possamos alterar o valor dos elementos do vetor dentro da fun ca o. Um exemplo disto j a foi visto quando implementamos a fun ca o StrCpy().

7.9

Os Argumentos argc e argv

A fun ca o main() pode ter par ametros formais. Mas o programador n ao pode escolher quais ser ao eles. A declara ca o mais completa que se pode ter para a fun ca o main() e: int main(int argc,char *argv[]); Os par ametros argc e argv d ao ao programador acesso ` a linha de comando com a qual o programa foi chamado. O argc (argument count) e um inteiro e possui o n umero de argumentos com os quais a fun ca o main() foi chamada na linha de comando. Ele e, no m nimo 1, pois o nome do programa e contado como sendo o primeiro argumento. O argv (argument values) e um ponteiro para uma matriz de strings. Cada string desta matriz e um dos par ametros da linha de comando. O argv[0] para saber sempre aponta para o nome do programa (que, como j a foi dito, e considerado o primeiro argumento). E quantos elementos temos em argv que temos argc. Exemplo: Escreva um programa que fa ca uso dos par amentros argv e argc. O programa dever a receber da linha de comando o dia, m es e ano correntes, e imprimir a data em formato apropriado. Veja o exemplo, supondo que o execut avel se chame data: data 19 04 99 O programa dever a imprimir: 19 de abril de 1999. #include <stdio.h> #include <stdlib.h> void main(int argc, char *argv[]) { int mes; char *nomemes [] = {"Janeiro", "Fevereiro", "Mar\c{c}o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"}; if(argc == 4) /* Testa se o numero de parametros fornecidos esta correto o primeiro parametro e o nome do programa, o segundo o dia o terceiro o mes e o quarto os dois ultimos algarismos do ano */ { mes = atoi(argv[2]); /* argv contem strings. A string referente ao mes deve ser transformada em um numero inteiro. A funcao atoi esta sendo usada para isto: recebe a string e transforma no inteiro equivalente */ if (mes < 1 || mes > 12) /* Testa se o mes e valido */ printf("Erro!\nUso: data dia mes ano, todos inteiros"); else printf("\n%s de %s de 19%s", argv[1], nomemes[mes-1], argv[3]); } else printf("Erro!\nUso: data dia mes ano, todos inteiros"); }

7.10

Programa c ao recursiva

Na linguagem C, assim como em muitas outras linguagens de programa ca o, uma fun ca o pode chamar a si pr opria. Uma fun ca o assim e chamada fun ca o recursiva. Todo cuidado e pouco ao se fazer fun co es recursivas. A primeira coisa a se providenciar e um crit erio de parada. Este vai determinar quando a fun ca o dever a parar de chamar a si mesma. Isto impede que a fun ca o se chame innitas vezes. Uma fun ca o que calcule o fatorial de um n umero inteiro n e um bom exemplo de uma fun ca o recursiva: #include <stdio.h> int fat(int n) { if (n) return n*fat(n-1);

7 FUNC OES

61

else return 1; } int main() { int n; printf("\n\nDigite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d e %d", n, fat(n)); return 0; } Note que, enquanto n n ao for igual a 0, a fun ca o fat chama a si mesma, cada vez com um valor menor. n = 0 e crit erio de parada para esta fun ca o. H a certos algoritmos que s ao mais ecientes quando feitos de maneira recursiva, mas a recursividade e algo a ser evitado sempre que poss vel, pois, se usada incorretamente, tende a consumir muita mem oria e ser lenta. Lembre-se que mem oria e consumida cada vez que o computador faz uma chamada a uma fun ca o. Com fun co es recursivas a mem oria do computador pode se esgotar rapidamente.

7.11

Comentarios sobre o uso de fun c oes

Uma fun ca o, como foi dito anteriormente, e um bloco de constru ca o muito u til. No C as fun co es s ao ex veis. A exibilidade d a poder, mas exige cuidado. Fun co es devem ser implementadas, quando poss vel, da maneira mais geral poss vel. Isto as torna mais f aceis de serem reutilizadas e entendidas. Evite, sempre que poss vel, fun co es que usem vari aveis globais. Se houver uma rotina que deve ser o mais veloz poss vel, seria bom implement a-la sem nenhuma (ou com o m nimo de) chamadas a fun co es, porque uma chamada a uma fun ca o consome tempo e mem oria. Um outro ponto importante e que, como j a sabemos um bocado a respeito de fun co es, quando formos ensinar uma das fun co es das bibliotecas do C vamos mostrar, em primeiro lugar, o seu prot otipo. Quem entendeu tudo que foi ensinado nesta parte sobre fun co es pode retirar in umeras informa c oes de um prot otipo (tipo de retorno, nome da fun ca o, tipo dos argumentos, passagem por valor ou passagem por refer encia). Sugiro que neste ponto, o leitor leia um arquivo um bom treino. Estes arquivo podem ser encontrados no cabe calho como, por exemplo o conio.h ou o string.h. E diret orio apropriado do compilador que voc e estiver utilizando (geralmente o subdiret orio include do diret orio onde voc e instalou o compilador).

8 DIRETIVAS DE COMPILAC AO

62

8
8.1

Diretivas de compila c ao
As diretivas de compila c ao

O pr e-processador C e um programa que examina o programa fonte escrito em C e executa certas modica co es nele, baseado nas Diretivas de Compila ca o. As diretivas de compila ca o s ao comandos que n ao s ao compilados, sendo dirigidos ao pr e-processador, que e executado pelo compilador antes da execu ca o do processo de compila ca o propriamente dito. Portanto, o pr e-processador modica o programa fonte, entregando para o compilador um programa modicado. Todas as diretivas de compila ca o s ao iniciadas pelo caracter #. As diretivas podem ser colocadas em qualquer parte do programa. J a vimos, e usamos muito, a diretiva #include. Sabemos que ela n ao gera c odigo mas diz ao compilador que ele deve incluir um arquivo externo na hora da compila ca o. As diretivas do C s ao identicadas por come carem por #. As diretivas que estudaremos s ao denidas pelo padr ao ANSI: #if #else #include #ifdef #elif #define #ifndef #endif #undef

8.2

A Diretiva include

A diretiva #include j a foi usada durante o nosso curso diversas vezes. Ela diz ao compilador para incluir, na hora da compila ca o, um arquivo especicado. Sua forma geral e: #include nome do arquivo ou #include nome do arquivo A diferen ca entre se usar e e somente a ordem de procura nos diret orios pelo arquivo especicado. Se voc e quiser informar o nome do arquivo com o caminho completo, ou se o arquivo estiver no diret orio de trabalho, use . Se o arquivo estiver nos caminhos de procura pr e-especicados do compilador, isto e, se ele for um arquivo do pr oprio sistema (como e o caso de arquivos como stdio.h, string.h, etc...) use . Observe que n ao h a ponto e v rgula ap os a diretiva de compila ca o. Esta e uma caracter stica importante de todas as diretivas de compila ca o e n ao somente da diretiva #include

8.3

As Diretivas dene e undef

A diretiva #dene tem a seguinte forma geral: encia de caracteres #dene nome da macro sequ Quando voc e usa esta diretiva, voc e est a dizendo ao compilador para que, toda vez que ele encontrar o nome da macro e muito u til para deixar no programa a ser compilado, ele deve substitu lo pela sequ encia de caracteres fornecida. Isto o programa mais geral. Veja um exemplo: #include <stdio.h> #define PI 3.1416 #define VERSAO "2.02" int main() { printf("Programa versao %s",VERSAO); printf("O numero pi vale: %f",PI); return 0; } Se quisermos mudar o nosso valor de PI, ou da VERSAO, no programa acima, basta mexer no in cio do programa. Isto torna o programa mais ex vel. H a quem diga que, em um programa, nunca se deve usar constantes como 10, 3.1416, etc., pois estes s ao n umeros que ningu em sabe o que signicam (muitas pessoas os chamam de n umeros m agicos). uma conven Ao inv es disto, deve-se usar apenas #denes. E ca o de programa ca o (que deve ser seguida, pois torna o programa mais leg vel) na linguagem C que as macros declaradas em #denes devem ser todas em mai usculas. Um outro uso da diretiva #dene e o de simplesmente denir uma macro. Neste caso usa-se a seguinte forma geral:

8 DIRETIVAS DE COMPILAC AO

63

#dene nome da macro Neste caso o objetivo n ao e usar a macro no programa (pois ela seria substitu da por nada), mas, sim, denir uma macro para ser usada como uma esp ecie de ag. Isto quer dizer que estamos denindo um valor como sendo verdadeiropara depois podermos test a-lo. Tamb em e poss vel denir macros com argumentos. Veja o exemplo a seguir: #define max(A,B) ((A>B) ? (A):(B)) #define min(A,B) ((A<B) ? (A):(B)) ... x = max(i,j); y = min(t,r); Embora pare ca uma chamada de fun ca o, o uso de max (ou min) simplesmente substitui, em tempo de compila ca o, o c odigo especicado. Cada ocorr encia de um par ametro formal (A ou B, na deni ca o) ser a substitu do pelo argumento real correspondente. Assim, a linha de c odigo: x = max(i,j); ser a substitu da pela linha: x = ((i)>(j) ? (i):(j)); A linha de c odigo: x = max(p+q,r+s); ser a substitu da pela linha: x = ((p+q)>(r+s) ? (p+q):(r+s)); Isto pode ser muito u til. Verique que as macros max e min n ao possuem especica ca o de tipo. Logo, elas trabalham corretamente para qualquer tipo de dado, enquanto os argumentos passados forem coerentes. Mas isto pode trazer tamb em algumas armadilhas. Veja que a linha x = max(p++,r++); ser a substitu da pelo c odigo x = ((p++)>(r++) ? (p++):(r++)); e em consequ encia, incrementar a o maior valor duas vezes. Outra armadilha em macros est a relacionada com o uso de par enteses. Seja a macro: #dene SQR(X) X*X. Imagine que voc e utilize esta macro na express ao abaixo: y = SQR(A+B); Ao fazer isto, a substitui ca o que ser a efetuada n ao estar a correta. A express ao gerada ser a: y = A + B * A + B; que obviamente e diferente de (A+B)*(A+B)! A solu ca o para este problema e incluir par enteses na deni c ao da macro: #define SQR(X)(X)*(X)

8 DIRETIVAS DE COMPILAC AO

64

Quando voc e utiliza a diretiva #dene nunca deve haver espa cos em branco no identicador. Por exemplo, a macro: #dene PRINT (i) printf(%d n, i) n ao funcionar a corretamente porque existe um espa co em branco entre PRINT e (i). Ao se tirar o espa co, a macro funcionar a corretamente e poder a ser utilizada para imprimir o n umero inteiro i, saltando em seguida para a pr oxima linha. A diretiva #undef tem a seguinte forma geral: #undef nome da macro. Ela faz com que a macro que a segue seja apagada da tabela interna que guarda as macros.O compilador passa a partir deste ponto a n ao conhecer mais esta macro.

8.4

As Diretivas ifdef e endif

Nesta se ca o, e at e mais a frente, veremos as diretivas de compila ca o condicional. Elas s ao muito parecidas com os comandos de execu ca o condicional do C. As duas primeiras diretivas que veremos s ao as #ifdef e #endif. Suas formas encia de declara co es #endif. A sequ encia de declara co es ser a compilada apenas gerais s ao: #ifdef nome da macro sequ se o nome da macro estiver denido. A diretiva de compila ca o #endif e util para denir o m de uma sequ encia de declara co es para todas as diretivas de compila ca o condicional. As linhas #define PORT_0 0x378 ... /* Linhas de codigo qualquer... */ ... #ifdef PORT_0 #define PORTA PORT_0 #include "../sys/port.h" #endif demonstram como estas diretivas podem ser utilizadas. Caso PORT 0 tenha sido previamente denido, a macro PORTA e denida e o header le port.h e inclu do.

8.5

A Diretiva ifndef

A diretiva #ifndef funciona ao contr ario da diretiva #ifdef. Sua forma geral e: encia de declara co es #endif #ifndef nome da macro sequ A sequ encia de declara co es ser a compilada se o nome da macro n ao tiver sido denido.

8.6

A Diretiva if

A diretiva #if tem a seguinte forma geral: #if express ao constante sequ encia de declara co es #endif muito importande ressaltar que A sequ encia de declara co es ser a compilada se a express ao-constante for verdadeira. E a express ao fornecida deve ser constante, ou seja, n ao deve ter nenhuma vari avel.

8.7

A Diretiva else

A diretiva #else tem a seguinte forma geral: encia de declara co es #if express ao constante sequ #else sequ encia de declara co es #endif Ela funciona como seu correspondente, o comando else. Imagine que voc e esteja trabalhando em um sistema, e deseje que todo o c odigo possa ser compilado em duas diferentes plataformas (i.e. Unix e Dos). Para obter isto, voc e encapsulatoda a parte de entrada e sa da em arquivos separados, que ser ao carregados de acordo com o header le carregado. Isto pode ser facilmente implementado da seguinte forma:

8 DIRETIVAS DE COMPILAC AO

65

#define SISTEMA DOS ... /*linhas de codigo..*/ ... #if SISTEMA == DOS #define CABECALHO "dos_io.h" #else #define CABECALHO "unix_io.h" #endif #include CABECALHO

8.8

A Diretiva elif

A diretiva #elif serve para implementar a estrutura if-else-if. Sua forma geral e: encia de declara co es 1 #if express ao constante 1 sequ #elif express ao constante 2 sequ encia de declara co es 2 #elif express ao constante 3 sequ encia de declara co es 3 ... encia de declara co es n #elif express ao constante n sequ #endif O funcionamento desta estrutura e id entico ao funcionamento apresentado anteriormente.

9 ENTRADAS E SA IDAS PADRONIZADAS

66

9
9.1

Entradas e sa das padronizadas


Introdu c ao

O sistema de entrada e sa da da linguagem C est a estruturado na forma de uma biblioteca de fun co es. J a vimos algumas destas fun co es, e agora elas ser ao reestudadas. Novas fun co es tamb em ser ao apresentadas. N ao e objetivo deste curso explicar, em detalhes, todas as poss veis fun co es da biblioteca de entrada e sa da do C. A sintaxe completa destas fun co es pode ser encontrada no manual do seu compilador. Alguns sistemas trazem uma descri ca o das fun co es na ajuda do compilador, que pode ser acessada on line. Um ponto importante e que agora, quando apresentarmos uma fun ca o, vamos, em primeiro lugar, apresentar o seu prot otipo. Voc e j a deve ser capaz de interpretar as informa co es que um prot otipo nos passa. Se n ao, deve voltar a estudar a aula sobre fun co es. Outro aspecto importante, quando se discute a entrada e sa da na linguagem C e o conceito de uxo. Seja qual for o dispositivo de entrada e sa da (discos, terminais, teclados, acionadores de tas) que se estiver trabalhando, o C vai enxerg a-lo como um uxo, que nada mais e que um dispositivo l ogico de entrada ou sa da. Todos os uxos s ao similares em seu funcionamento e independentes do dispositivo ao qual est ao associados. Assim, as mesmas fun co es que descrevem o acesso aos discos podem ser utilizadas para se acessar um terminal de v deo. Todas as opera co es de entrada e sa da s ao realizadas por meio de uxos. Na linguagem C, um arquivo e entendido como um conceito que pode ser aplicado a arquivos em disco, terminais, modens, etc. Um uxo e associado a um arquivo atrav es da realiza ca o de uma opera ca o de abertura. Uma vez aberto, informa co es podem ser trocadas entre o arquivo e o programa. Um arquivo e dissociado de um uxo atrav es de uma opera ca o de fechamento de arquivo.

9.2

Ler e escrever caracteres

Uma das fun co es mais b asicas de um sistema e a entrada e sa da de informa co es em dispositivos. Estes podem ser um monitor, uma impressora ou um arquivo em disco. Vamos ver os principais comandos que o C nos fornece para isto. 9.2.1 A fun c ao getche e getch

As fun co es getch() e getche() n ao s ao denidas pelo padr ao ANSI. Por em, elas geralmente s ao inclu das em compiladores baseados no DOS, e se encontram no header le conio.h. Vale a pena repetir: s ao fun co es comuns apenas para compiladores baseados em DOS e, se voc e estiver no UNIX normalmente n ao ter a estas fun co es dispon veis. Prot otipos: int getch (void); int getche (void); getch() espera que o usu ario digite uma tecla e retorna este caractere. Voc e pode estar estranhando o fato de getch() retornar um inteiro, mas n ao h a problema pois este inteiro e tal que quando igualado a um char a convers ao e feita corretamente. A fun ca o getche() funciona exatamente como getch(). A diferen ca e que getche() gera um echona tela antes de retornar a tecla. Se a tecla pressionada for um caractere especial estas fun co es retornam zero. Neste caso voc e deve usar as fun co es novamente para pegar o c odigo da tecla extendida pressionada. A fun ca o equivalente a getche() no mundo ANSI e o getchar(). O problema com getchar e que o caracter lido e colocado em uma area intermedi aria at e que o usu ario digite um <ENTER>, o que pode ser extremamente inconveniente em ambientes interativos. 9.2.2 A fun c ao putchar

Prot otipo: int putchar (int c); putchar() coloca o caractere c na tela. Este caractere e colocado na posi ca o atual do cursor. Mais uma vez os tipos s ao inteiros, mas voc e n ao precisa se preocupar com este fato. O header le e stdio.h.

9.3
9.3.1

Ler e escrever strings


A fun c ao gets

Prot otipo: char *gets(char *s); Pede ao usu ario que entre uma string, que ser a armazenada na string s. O ponteiro que a fun ca o retorna e o pr oprio s. gets n ao e uma fun ca o segura. Por qu e? Simplesmente porque com gets pode ocorrer um estouro da quantidade de posi co es que foi especicada na string. Veja o exemplo abaixo: #include <stdio.h> int main() { char buffer[10]; printf("Entre com o seu nome");

9 ENTRADAS E SA IDAS PADRONIZADAS

67

gets(buffer); printf("O nome \e: %s", buffer); return 0; } Se o usu ario digitar como entrada: Daniel Duesentrieb ou seja, digitar um total de 18 caracteres: 19 posi co es (incluindo o 0 ) ser ao utilizadas para armazenar a string. Como a string buer[] s o tem 10 caracteres, os 9 caracteres adicionais ser ao colocados na area de mem oria subsequente a ` ocupada por ela, escrevendo uma regi ao de mem oria que n ao est a reservada ` a string. Este efeito e conhecido como estouro de buere pode causar problemas imprevis veis. Uma forma de se evitar este problema e usar a fun ca o fgets. 9.3.2 A fun c ao puts

Prot otipo: int puts (char *s); puts() coloca a string s na tela.

9.4

Entrada e sa da formatada

As fun co es que resumem todas as fun co es de entrada e sa da formatada no C s ao as fun co es printf() e scanf(). Um dom nio destas fun co es e fundamental ao programador. 9.4.1 A fun c ao printf

Prot otipo: int printf(char *str,...); As retic encias no prot otipo da fun ca o indicam que esta fun ca o tem um n umero de argumentos vari avel. Este n umero est a diretamente relacionado com a string de controle str, que deve ser fornecida como primeiro argumento. A string de controle tem dois componentes. O primeiro s ao caracteres a serem impressos na tela. O segundo s ao os comandos de formato. Como j a vimos, os u ltimos determinam uma exibi ca o de vari aveis na sa da. Os comandos de formato s ao precedidos de %. A cada comando de formato deve corresponder um argumento na fun ca o printf(). Se isto n ao ocorrer podem acontecer erros imprevis veis no programa. Abaixo apresentamos a tabela de c odigos de formato: C odigo Formato %c Um caracter (char) %d Um n umero inteiro decimal (int) %i O mesmo que %d %e N umero em nota ca o cient ca com o emin usculo %E N umero em nota ca o cient ca com o emai usculo %f Ponto utuante decimal %g Escolhe automaticamente o melhor entre %f e %e %G Escolhe automaticamente o melhor entre %f e %E %o N umero octal %s String %u Decimal unsigned(sem sinal) %x Hexadecimal com letras min usculas %X Hexadecimal com letras mai usculas %% Imprime um % %p Ponteiro Vamos ver alguns exemplos: C odigo Imprime printf(Um %%%c %s,c,char); Um %c char printf(%X %f %e,107,49.67,49.67); 6B 49.67 4.967e1 printf(%d %o,10,10); 10 12 E poss vel tamb em indicar o tamanho do campo, justica ca o e o n umero de casas decimais. Para isto usa-se c odigos colocados entre o % e a letra que indica o tipo de formato. Um inteiro indica o tamanho m nimo, em caracteres, que deve ser reservado para a sa da. Se colocarmos ent ao %5d estamos indicando que o campo ter a cinco caracteres de comprimento no m nimo. Se o inteiro precisar de mais de cinco caracteres para ser exibido ent ao o campo ter a o comprimento necess ario para exibi-lo. Se o comprimento do inteiro for menor que cinco ent ao o campo ter a cinco de comprimento e ser a preenchido com espa cos em branco. Se se quiser um preenchimento com zeros pode-se colocar um zero antes do n umero. Temos ent ao que %05d reservar a cinco casas para o n umero e se este for menor ent ao se far ao

9 ENTRADAS E SA IDAS PADRONIZADAS

68

preenchimento com zeros. O alinhamento padr ao e` a direita. Para se alinhar um n umero ` a esquerda usa-se um sinal - antes do n umero de casas. Ent ao %-5d ser a o nosso inteiro com o n umero m nimo de cinco casas, s o que justicado a esquerda. Pode-se indicar o n umero de casas decimais de um n umero de ponto utuante. Por exemplo, a nota ca o %10.4f indica um ponto utuante de comprimento total dez e com 4 casas decimais. Entretanto, esta mesma nota ca o, quando aplicada a tipos como inteiros e strings indica o n umero m nimo e m aximo de casas. Ent ao %5.8d e um inteiro com comprimento m nimo de cinco e m aximo de oito. Vamos ver alguns exemplos: C odigo Imprime printf(%-5.2f,456.671); 456.67 printf(%5.2f,2.671); 2.67 printf(%-10s,Ola); Ola Nos exemplos o pipe( ) indica o in cio e o m do campo mas n ao s ao escritos na tela. 9.4.2 A fun c ao scanf

Prot otipo: int scanf(char *str,...); A string de controle str determina, assim como com a fun ca o printf(), quantos par ametros a fun ca o vai necessitar. Devemos sempre nos lembrar que a fun ca o scanf() deve receber ponteiros como par ametros. Isto signica que as vari aveis que n ao sejam por natureza ponteiros devem ser passadas precedidas do operador &. Os especicadores de formato de entrada s ao muito parecidos com os de printf(). Os caracteres de convers ao d, i, u e x podem ser precedidos por h para indicarem que um apontador para short ao inv es de int aparece na lista de argumento, ou pela letra l (letra ele) para indicar que que um apontador para long aparece na lista de argumento. Semelhantemente, os caracteres de convers ao e, f e g podem ser precedidos por l para indicarem que um apontador para double ao inv es de oat est a na lista de argumento. Exemplos: C odigo Formato %c Um u nico caracter (char) %d Um n umero decimal (int) %i Um n umero inteiro %hi Um short int %li Um long int %e Um ponto utuante %f Um ponto utuante %lf Um double %h Inteiro curto %o N umero octal %s String %x N umero hexadecimal %p Ponteiro 9.4.3 As funcoes sprintf e sscanf

sprintf e sscanf s ao semelhantes a printf e scanf. Por em, ao inv es de escreverem na sa da padr ao ou lerem da entrada padr ao, escrevem ou leem em uma string. Os prot otipos s ao: int sprintf(char *destino, char *controle, ...); int sscanf(char *destino, char *controle, ...); Estas fun co es s ao muito utilizadas para fazer a convers ao entre dados na forma num erica e sua representa ca o na forma de strings. No programa abaixo, por exemplo, a vari avel i e impressaem string1. Al em da representa ca o de i como uma string, string1 tamb em conter a Valor de i=. #include <stdio.h> int main() { int i;

9 ENTRADAS E SA IDAS PADRONIZADAS

69

char string1[20]; printf( " Entre um valor inteiro: "); scanf("%d", &i); sprintf(string1,"Valor de i = %d", i); puts(string1); return 0; } J a no programa abaixo, foi utilizada a fun ca o sscanf para converter a informa ca o armazenada em string1 em seu valor num erico: #include <stdio.h> int main() { int i, j, k; char string1[]= "10 20 30"; sscanf(string1, "%d %d %d", &i, &j, &k); printf("Valores lidos: %d, %d, %d", i, j, k); return 0; }

9.5

Abrir e fechar arquivos

O sistema de entrada e sa da do ANSI C e composto por uma s erie de fun co es, cujos prot otipos est ao reunidos em stdio.h . Todas estas fun co es trabalham com o conceito de ponteiro de arquivo. Este n ao e um tipo propriamente dito, mas uma deni ca o usando o comando typedef. Esta deni ca o tamb em est a no arquivo stdio.h. Podemos declarar usando este tipo um ponteiro de arquivo da seguinte maneira: FILE *p; p ser a ent ao um ponteiro para um arquivo. E de ponteiro que vamos poder manipular arquivos no C. 9.5.1 A fun c ao fopen

Esta e a fun ca o de abertura de arquivos. Seu prot otipo e: FILE *fopen (char *nome do arquivo, char *modo); O nome do arquivo determina qual arquivo dever a ser aberto. Este nome deve ser v alido no sistema operacional que estiver sendo utilizado. O modo de abertura diz ` a fun ca o fopen() que tipo de uso voc e vai fazer do arquivo. A tabela abaixo mostra os valores de modo v alidos:

9 ENTRADAS E SA IDAS PADRONIZADAS Modo r w a rb wb ab r+ w+ a+ r+b w+b a+b

70

Signicado Abre um arquivo texto para leitura. O arquivo deve existir antes de ser aberto. Abrir um arquivo texto para grava ca o. Se o arquivo n ao existir, ele ser a criado. Se j a existir, o conte udo anterior ser a destru do. Abrir um arquivo texto para grava ca o. Os dados ser ao adicionados no m do arquivo (append), se ele j a existir, ou um novo arquivo ser a criado, no caso de arquivo n ao existente anteriormente. Abre um arquivo bin ario para leitura. Igual ao modo ranterior, s o que o arquivo e bin ario. Cria um arquivo bin ario para escrita, como no modo wanterior, s o que o arquivo e bin ario. Acrescenta dados bin arios no m do arquivo, como no modo aanterior, s o que o arquivo e bin ario. Abre um arquivo texto para leitura e grava ca o. O arquivo deve existir e pode ser modicado. Cria um arquivo texto para leitura e grava ca o. Se o arquivo existir, o conte udo anterior ser a destru do. Se n ao existir, ser a criado. Abre um arquivo texto para grava ca o e leitura. Os dados ser ao adicionados no m do arquivo se ele j a existir, ou um novo arquivo ser a criado, no caso de arquivo n ao existente anteriormente. Abre um arquivo bin ario para leitura e escrita. O mesmo que r+acima, s o que o arquivo e bin ario. Cria um arquivo bin ario para leitura e escrita. O mesmo que w+acima, s o que o arquivo e bin ario. Acrescenta dados ou cria uma arquivo bin ario para leitura e escrita. O mesmo que a+acima, s o que o arquivo e bin ario.

Poder amos ent ao, para abrir um arquivo bin ario para escrita, escrever: FILE *fp; /* Declara\c{c}\~ao da estrutura */

fp=fopen ("exemplo.bin","wb");

/* o arquivo se chama exemplo.bin e est\a localizado no diret\orio corrente */ if (!fp) printf("Erro na abertura do arquivo.");

A condi ca o !fp testa se o arquivo foi aberto com sucesso porque no caso de um erro a fun ca o fopen() retorna um ponteiro nullo (NULL). Uma vez aberto um arquivo, vamos poder ler ou escrever nele utilizando as fun co es que ser ao apresentadas nas pr oximas p aginas. Toda vez que estamos trabalhando com arquivos, h a uma esp ecie de posi ca o atual no arquivo. Esta e a posi ca o de onde ser a lido ou escrito o pr oximo caractere. Normalmente, num acesso sequencial a um arquivo, n ao temos que mexer nesta posi ca o pois quando lemos um caractere a posi ca o no arquivo e automaticamente atualizada. Num acesso rand omico teremos que mexer nesta posi ca o (ver fseek()). 9.5.2 A fun c ao exit

Aqui abrimos um par enteses para explicar a fun ca o exit() cujo prot otipo e: void exit (int codigo de retorno); Para utiliz a-la deve-se colocar um include para o arquivo de cabe calho stdlib.h. Esta fun ca o aborta a execu ca o do programa. Pode ser chamada de qualquer ponto no programa e faz com que o programa termine e retorne, para ca o mais usada e que um programa retorne zero no caso de o sistema operacional, o c odigo de retorno. A conven um t ermino normal e retorne um n umero n ao nulo no caso de ter ocorrido um problema. A fun ca o exit() se torna importante em casos como aloca ca o din amica e abertura de arquivos pois nestes casos, se o programa n ao conseguir a mem oria necess aria ou abrir o arquivo, a melhor sa da pode ser terminar a execu ca o do programa. Poder amos reescrever o exemplo da se ca o anterior usando agora o exit() para garantir que o programa n ao deixar a de abrir o arquivo: #include <stdio.h> #include <stdlib.h> /* Para a fun\c{c}\~ao exit() */ main(void) { FILE *fp; ... fp=fopen ("exemplo.bin","wb");

9 ENTRADAS E SA IDAS PADRONIZADAS

71

if (!fp) { printf("Erro na abertura do arquivo. Fim de programa."); exit (1); } ... return 0; }

9.5.3

A fun c ao fclose

Quando acabamos de usar um arquivo que abrimos, devemos fech a-lo. Para tanto usa-se a fun ca o fclose(): int fclose (FILE *fp); O ponteiro fp passado ` a fun ca o fclose() determina o arquivo a ser fechado. A fun ca o retorna zero no caso de sucesso. Fechar um arquivo faz com que qualquer caracter que tenha permanecido no buerassociado ao uxo de sa da seja gravado. Mas, o que e este buer? Quando voc e envia caracteres para serem gravados em um arquivo, estes caracteres s ao armazenados temporariamente em uma area de mem oria (o buer) em vez de serem escritos em disco imediatamente. Quando o buerestiver cheio, seu conte udo e escrito no disco de uma vez. A raz ao para se fazer isto tem a ver com a eci encia nas leituras e grava co es de arquivos. Se, para cada caracter que fossemos gravar, tiv essemos que posicionar a cabe ca de grava ca o em um ponto espec co do disco, apenas para gravar aquele caracter, as grava co es seriam muito lentas. Assim estas grava co es s o ser ao efetuadas quando houver um volume razo avel de informa co es a serem gravadas ou quando o arquivo for fechado. A fun ca o exit() fecha todos os arquivos que um programa tiver aberto.

9.6
9.6.1

Leitura sequencial de arquivos


A fun c ao putc

A fun ca o putc e a primeira fun ca o de escrita de arquivo que veremos. Seu prot otipo e: int putc (int ch,FILE *fp); Escreve um caractere no arquivo. O programa a seguir l e uma string do teclado e escreve-a, caractere por caractere em um arquivo em disco (o arquivo arquivo.txt, que ser a aberto no diret orio corrente). #include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char string[100]; int i; fp = fopen("arquivo.txt","w"); /* Arquivo ASCII, para escrita */ if(!fp) { printf( "Erro na abertura do arquivo"); exit(0); } printf("Entre com a string a ser gravada no arquivo:"); gets(string); for(i=0; string[i]; i++) putc(string[i], fp); /* Grava a string, caractere a caractere */ fclose(fp); return 0; } Depois de executar este programa, verique o conte udo do arquivo arquivo.txt (voc e pode usar qualquer editor de textos). Voc e ver a que a string que voc e digitou est a armazenada nele.

9 ENTRADAS E SA IDAS PADRONIZADAS

72

9.6.2

A fun c ao getc

O getc retorna um caractere lido do arquivo. Prot otipo: int getc (FILE *fp);

9.6.3

A fun c ao feof

EOF (End of le) indica o m de um arquivo. As vezes, e necess ario vericar se um arquivo chegou ao m. Para isto podemos usar a fun ca o feof(). Ela retorna n ao-zero se o arquivo chegou ao EOF, caso contr ario retorna zero. Seu prot otipo e: int feof (FILE *fp); Outra forma de se vericar se o nal do arquivo foi atingido e comparar o caractere lido por getc com EOF. O programa a seguir abre um arquivo j a existente e o l e, caracter por caracter, at e que o nal do arquivo seja atingido. Os caracteres lidos s ao apresentados na tela: #include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char c; fp = fopen("arquivo.txt","r"); /* Arquivo ASCII, para leitura */ if(!fp) { printf( "Erro na abertura do arquivo"); exit(0); } while((c = getc(fp) ) != EOF) /* Enquanto n\~ao chegar ao final do arquivo */ printf("%c", c); /* imprime o caracter lido */ fclose(fp); return 0; } A seguir e apresentado um programa onde v arias opera co es com arquivos s ao realizadas, usando as fun co es vistas nesta p agina. Primeiro o arquivo e aberto para a escrita, e imprime-se algo nele. Em seguida, o arquivo e fechado e novamente aberto para a leitura. Verique o exemplo. #include <stdio.h> #include <stdlib.h> #include <string.h> void main() { FILE *p; char c, str[30], frase[80] = "Este e um arquivo chamado: "; int i; printf("\n\n Entre com um nome para o arquivo:\n");

9 ENTRADAS E SA IDAS PADRONIZADAS

73

gets(str); /* Le um nome para o arquivo a ser aberto: */ if (!(p = fopen(str,"w"))) /* Caso ocorra algum erro na abertura do arquivo..*/ { printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); /* o programa aborta automaticamente */ } strcat(frase, str); for (i = 0; frase[i]; i++) putc(frase[i],p); /* Se nao houve erro, imprime no arquivo e o fecha ...*/ fclose(p); p = fopen(str,"r"); /* Abre novamente para leitura */ c = getc(p); /* Le o primeiro caracter */ while (!feof(p)) /* Enquanto n\~ao se chegar no final do arquivo */ { printf("%c",c); /* Imprime o caracter na tela */ c = getc(p); /* Le um novo caracter no arquivo */ } fclose(p); /* Fecha o arquivo */ }

9.7
9.7.1

Fun c oes elaborados de acesso a arquivos


Arquivos pr e-denidos

Quando se come ca a execu ca o de um programa, o sistema automaticamente abre alguns arquivos pr e-denidos: stdin: dispositivo de entrada padr ao (geralmente o teclado); stdout: dispositivo de sa da padr ao (geralmente o v deo); stderr: dispositivo de sa da de erro padr ao (geralmente o v deo); stdaux: dispositivo de sa da auxiliar (em muitos sistemas, associado ` a porta serial) e stdprn : dispositivo de impress ao padr ao (em muitos sistemas, associado ` a porta paralela). Cada uma destas constantes pode ser utilizada como um ponteiro para FILE, para acessar os perif ericos associados a eles. Desta maneira, pode-se, por exemplo, usar: ch = getc(stdin); para efetuar a leitura de um caracter a partir do teclado, ou : putc(ch, stdout); para imprim -lo na tela. 9.7.2 A fun c ao fgets

Para se ler uma string num arquivo podemos usar fgets() cujo prot otipo e: char *fgets(char *str, int tamanho,FILE *fp); A fun ca o recebe 3 argumentos: a string a ser lida, o limite m aximo de caracteres a serem lidos e o ponteiro para FILE, que est a associado ao arquivo de onde a string ser a lida. A fun ca o l e a string at e que um caracter de nova linha seja lido ou tamanho-1 caracteres tenham sido lidos. Se o caracter de nova linha (\n) for lido, ele far a parte da string, o que n ao acontecia com gets. A string resultante sempre terminar a com \0 (por isto somente tamanho-1 caracteres, no m aximo, ser ao lidos). A fun ca o fgets e semelhante ` a fun ca o gets(), por em, al em dela poder fazer a leitura a partir de um arquivo de dados e incluir o caracter de nova linha na string, ela ainda especica o tamanho m aximo da string de entrada. Como vimos, a fun ca o gets n ao tinha este controle, o que poderia acarretar erros de estouro de buer. Portanto, levando em conta que o ponteiro fp pode ser substitu do por stdin, como vimos acima, uma alternativa ao uso de gets e usar a seguinte constru ca o: fgets(str, tamanho, stdin); onde str e a string que se est a lendo e tamanho deve ser igual ao tamanho alocado para a string subtra do de 1, por causa do \0. 9.7.3 A fun c ao fputs

fputs Prot otipo: char *fputs (char *str,FILE *fp); Escreve uma string num arquivo.

9 ENTRADAS E SA IDAS PADRONIZADAS

74

9.7.4

A fun c ao ferror e perror

Prot otipo de ferror: int ferror (FILE *fp); A fun ca o retorna zero, se nenhum erro ocorreu e um n umero diferente de zero se algum erro ocorreu durante o acesso ao arquivo. ferror() se torna muito u til quando queremos vericar se cada acesso a um arquivo teve sucesso, de modo que consigamos garantir a integridade dos nossos dados. Na maioria dos casos, se um arquivo pode ser aberto, ele pode ser lido ou gravado. Por em, existem situa co es em que isto n ao ocorre. Por exemplo, pode acabar o espa co em disco enquanto gravamos, ou o disco pode estar com problemas e n ao conseguimos ler, etc. Uma fun ca o que pode ser usada em conjunto com ferror() e a fun ca o perror() (print error), cujo argumento e uma string que normalmente indica em que parte do programa o problema ocorreu. No exemplo a seguir, fazemos uso de ferror, perror e fputs #include <stdio.h> #include <stdlib.h> int main() { FILE *pf; char string[100]; if((pf = fopen("arquivo.txt","w")) ==NULL) { printf("\nNao consigo abrir o arquivo ! "); exit(1); } do { printf("\nDigite uma nova string. Para terminar, digite <enter>: "); gets(string); fputs(string, pf); putc(\n, pf); if(ferror(pf)) { perror("Erro na gravacao"); fclose(pf); exit(1); } } while (strlen(string) > 0); fclose(pf); }

9.7.5

A fun c ao fread

Podemos escrever e ler blocos de dados. Para tanto, temos as fun co es fread() e fwrite(). O prot otipo de fread() e: unsigned fread (void *buffer, int numero_de_bytes, int count, FILE *fp); buer e a regi ao de mem oria na qual ser ao armazenados os dados lidos. O n umero de bytes e o tamanho da unidade a ser lida. count indica quantas unidades devem ser lidas. Isto signica que o n umero total de bytes lidos e: ca o retorna o n umero de unidades efetivamente lidas. Este n umero pode ser menor numero de bytes*count. A fun que count quando o m do arquivo for encontrado ou ocorrer algum erro. Quando o arquivo for aberto para dados bin arios, fread pode ler qualquer tipo de dados.

9 ENTRADAS E SA IDAS PADRONIZADAS

75

9.7.6

A fun c ao fwrite

A fun ca o fwrite() funciona como a sua companheira fread(), por em escrevendo no arquivo. Seu prot otipo e: unsigned fwrite(void *buffer,int numero_de_bytes,int count,FILE *fp); A fun ca o retorna o n umero de itens escritos. Este valor ser a igual a count a menos que ocorra algum erro. O exemplo abaixo ilustra o uso de fwrite e fread para gravar e posteriormente ler uma vari avel oat em um arquivo bin ario. #include <stdio.h> #include <stdlib.h> int main() { FILE *pf; float pi = 3.1415; float pilido; if((pf = fopen("arquivo.bin", "wb")) == NULL) /* Abre arquivo bin\ario para escrita */ { printf("Erro na abertura do arquivo"); exit(1); } if(fwrite(&pi, sizeof(float), 1,pf) != 1) /* Escreve a vari\avel pi */ printf("Erro na escrita do arquivo"); fclose(pf); /* Fecha o arquivo */ if((pf = fopen("arquivo.bin", "rb")) == NULL) /* Abre o arquivo novamente para leitura */ { printf("Erro na abertura do arquivo"); exit(1); } if(fread(&pilido, sizeof(float), 1,pf) != 1) /* Le em pilido o valor da vari\avel armazenada anteriormente */ printf("Erro na leitura do arquivo"); printf("\nO valor de PI, lido do arquivo e: \%f", pilido); fclose(pf); return(0); } Note-se o uso do operador sizeof, que retorna o tamanho em bytes da vari avel ou do tipo de dados. 9.7.7 A fun c ao fseek

Para se fazer procuras e acessos rand omicos em arquivos usa-se a fun ca o fseek(). Esta move a posi ca o corrente de leitura ou escrita no arquivo de um valor especicado, a partir de um ponto especicado. Seu prot otipo e: int fseek (FILE *fp,long numbytes,int origem);

9 ENTRADAS E SA IDAS PADRONIZADAS

76

O par ametro origem determina a partir de onde os numbytes de movimenta ca o ser ao contados. Os valores poss veis s ao denidos por macros em stdio.h e s ao: Nome Valor Signicado SEEK SET 0 In cio do arquivo SEEK CUR 1 Ponto corrente no arquivo SEEK END 2 Fim do arquivo Tendo-se denido a partir de onde ir a se contar, numbytes determina quantos bytes de deslocamento ser ao dados na posi ca o atual. 9.7.8 A fun c ao rewind

A fun ca o rewind() de prot otipo void rewind (FILE *fp); retorna a posi ca o corrente do arquivo para o in cio. 9.7.9 A fun c ao remove

Prot otipo: int remove (char *nome_do_arquivo); Apaga um arquivo especicado. O exerc cio da p agina anterior poderia ser reescrito usando-se, por exemplo, fgets() e fputs(), ou fwrite() e fread(). A seguir apresentamos uma segunda vers ao que se usa das fun co es fgets() e fputs(), e que acrescenta algumas inova co es. #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { FILE *p; char str[30], frase[] = "Este e um arquivo chamado: ", resposta[80]; int i; printf("\n\n Entre com um nome para o arquivo:\n"); fgets(str,29,stdin); /* Le um nome para o arquivo a ser aberto: */ /* Usa fgets como se fosse gets */ for(i=0; str[i]; i++) if(str[i]==\n) str[i]=0; /* Elimina o \n da string lida */ if (!(p = fopen(str,"w"))) /* Caso ocorra algum erro na abertura do arquivo..*/ { printf("Erro! Impossivel abrir o arquivo!\n"); /* o programa aborta automaticamente */ exit(1); } fputs(frase, p); fputs(str,p); fclose(p);

/* Se nao houve erro, imprime no arquivo, e o fecha ...*/

p = fopen(str,"r"); /* abre novamente e le */

9 ENTRADAS E SA IDAS PADRONIZADAS

77

fgets(resposta, 79, p); printf("\n\n\%s\n", resposta); fclose(p); /* Fecha o arquivo */ remove(str); /* Apaga o arquivo */ return(0); }

9.8

Fluxos padr ao

Os uxos padr ao em arquivos permitem ao programador ler e escrever em arquivos da maneira padr ao com a qual o j a l amos e escrev amos na tela. 9.8.1 A fun c ao fprintf

A fun ca o fprintf() funciona como a fun ca o printf(). A diferen ca e que a sa da de fprintf() e um arquivo e n ao a tela do computador. Prot otipo: int fprintf(FILE *fp,char *str,...); Como j a poder amos esperar, a u nica diferen ca do prot otipo de fprintf() para o de printf() e a especica ca o do arquivo destino atrav es do ponteiro de arquivo. 9.8.2 A fun c ao fscanf

A fun ca o fscanf() funciona como a fun ca o scanf(). A diferen ca e que fscanf() l e de um arquivo e n ao do teclado do computador. Prot otipo: int fscanf(FILE *fp,char *str,...); Como j a poder amos esperar, a u nica diferen ca do prot otipo de fscanf() para o de scanf() e a especica ca o do arquivo destino atrav es do ponteiro de arquivo. Talvez a forma mais simples de escrever o programa anterior seja usando fprintf() e fscanf(). Fica assim: #include <stdio.h> #include <stdlib.h> int main() { FILE *p; char str[80],c; printf("\n\n Entre com um nome para o arquivo:\n"); gets(str); /* Le um nome para o arquivo a ser aberto: */ if (!(p = fopen(str,"w"))) /* Caso ocorra algum erro na abertura do arquivo..*/ { printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); /* o programa aborta automaticamente */ } fprintf(p,"Este e um arquivo chamado:\n\%s\n", str); /* Se nao houve erro, imprime no arquivo, fecha ...*/ fclose(p); } p = fopen(str,"r"); /* abre novamente para a leitura */

9 ENTRADAS E SA IDAS PADRONIZADAS

78

while (!feof(p)) { fscanf(p,"\%c",&c); printf("\%c",c); } fclose(p); return(0);

10 TIPOS DE DADOS AVANC ADOS

79

10
10.1

Tipos de dados avan cados


Modicadores de Acesso

avel lista de vari aveis; Vimos tamb em que existem modiJ a vimos que uma vari avel e declarada como tipo da vari cadores de tipos. Estes modicam o tipo da vari avel declarada. Destes, j a vimos os modicadores signed, unsigned, long, e short. Estes modicadores s ao inclu dos na declara ca o da vari avel da seguinte maneira: modicador de tipo tipo da vari avel lista de vari aveis; como j a vimos na se ca o 3.3. Vamos discutir agora outros modicadores de tipo. Estes modicadores, como o pr oprio nome indica, mudam a maneira com a qual a vari avel e acessada e modicada. 10.1.1 O modicador const

O modicador const faz com que a vari avel n ao possa ser modicada no programa. Como o nome j a sugere eu til para se declarar constantes. Poder amos ter, por exemplo: const float PI=3.141; Podemos ver pelo exemplo que as vari aveis com o modicador const podem ser inicializadas. Mas PI n ao poderia ser alterado em qualquer outra parte do programa. Se o programador tentar modicar PI o compilador gerar a um erro de compila ca o. O uso mais importante de const n ao e declarar vari aveis constantes no programa. Seu uso mais comum e evitar que um par ametro de uma fun ca o seja alterado pela fun ca o. Isto e muito u til no caso de um ponteiro, pois o conte udo de um ponteiro pode ser alterado por uma fun ca o. Para tanto, basta declarar o par ametro como const. Veja o exemplo: #include <stdio.h> int sqr (const int *num); main(void) { int a=10; int b; b=sqr (&a); }

int sqr (const int *num) { return ((*num)*(*num)); } No exemplo, num est a protegido contra altera co es. Isto quer dizer que, se tent assemos fazer *num=10; dentro da fun ca o sqr() o compilador daria uma mensagem de erro. 10.1.2 O modicador volatile

O modicador volatile diz ao compilador que a vari avel em quest ao pode ser alterada sem que este seja avisado. Isto evita bugsser ssimos. Digamos que, por exemplo, tenhamos uma vari avel que o BIOS do computador altera de minuto em minuto (um rel ogio por exemplo). Seria muito bom que declar assemos esta vari avel como sendo volatile.

10.2

Especicadores de classe de armazenamento

Estes modicadores de tipo atuam sobre a maneira com a qual o compilador vai armazenar a vari avel.

10 TIPOS DE DADOS AVANC ADOS

80

10.2.1

O especifdicador auto

O especicador de classe de armazenamento auto dene vari aveis autom aticas, isto e, vari aveis locais. Raramente usado pois todas as vari aveis locais do C s ao auto por deni ca o. 10.2.2 O especifdicador extern

O extern dene vari aveis que ser ao usadas em um arquivo apesar de terem sido declaradas em outro. Ao contr ario dos programas at e aqui vistos, podemos ter programas de v arios milhares de linhas. Estes podem ser divididos em v arios arquivos (m odulos) que ser ao compilados separadamente. Digamos que para um programa grande tenhamos duas vari aveis globais: um inteiro count e um oat sum. Estas vari aveis s ao declaradas normalmente em um dos m odulos do programa. Por exemplo: int count; float sum; main(void) { ... return 0; } Num outro m odulo do programa temos uma rotina que deve usar as vari aveis globais acima. Digamos que a rotina que queremos se chama RetornaCount() e retorna o valor atual de count. O problema e que este m odulo ser a compilado em separado e n ao tomar a conhecimento dos outros m odulos. O que fazer? Ser a que funcionaria se zermos assim: int count; /* errado */ float sum; int RetornaCount (void) { return count; } O m odulo compilaria sem problema, mas, na hora que zermos a linkagem (uni ao dos m odulos j a compilados para gerar o execut avel) vamos nos deparar com uma mensagem de erro dizendo que as vari aveis globais count e sum foram declaradas mais de uma vez. A maneira correta de se escrever o m odulo com a fun ca o RetornaCount() e: extern int count; /* certo */ extern float sum; int RetornaCount (void) { return count; } Assim, o compilador ir a saber que count e sum est ao sendo usados no bloco mas que foram declarados em outro. 10.2.3 O especifdicador static

O funcionamento das vari aveis declaradas como static depende se estas s ao globais ou locais. Vari aveis globais static funcionam como vari aveis globais dentro de um m odulo, ou seja, s ao vari aveis globais que n ao s ao (e nem podem ser) conhecidas em outros modulos. Isto e util se quisermos isolar peda cos de um programa para evitar mudan cas acidentais em vari aveis globais. Vari aveis locais static s ao vari aveis cujo valor e mantido de uma chamada da fun ca o para a outra. Veja o exemplo:

10 TIPOS DE DADOS AVANC ADOS

81

int count (void) { static int num=0; num++; return num; } A fun ca o count() retorna o n umero de vezes que ela j a foi chamada. Veja que a vari avel local int e inicializada. Esta inicializa ca o s o vale para a primeira vez que a fun ca o e chamada pois num deve manter o seu valor de uma chamada para a outra. O que a fun ca o faz e incrementar num a cada chamada e retornar o seu valor. A melhor maneira de se entender esta vari avel local static e implementando. Veja por si mesmo, executando seu pr oprio programa que use este conceito. 10.2.4 O especifdicador register

O computador tem a mem oria principal e os registradores da CPU. As vari aveis (assim como o programa como um todo) s ao armazenados na mem oria. O modicador register diz ao compilador que a vari avel em quest ao deve ser, se poss vel, usada em um registrador da CPU. Vamos agora ressaltar v arios pontos importantes. Em primeiro lugar, porque usar o register? Vari aveis nos registradores da CPU v ao ser acessadas em um tempo muito menor pois os registradores s ao muito mais r apidos que a mem oria. Em segundo lugar, em que tipo de vari avel usar o register? O register n ao pode ser usado em vari aveis globais. Isto implicaria que um registrador da CPU caria o tempo todo ocupado por conta de uma vari avel. Os tipos de dados onde e mais aconselhado o uso do register s ao os tipos char e int, mas pode-se us a-lo em qualquer tipo de dado. Em terceiro lugar, o register e um pedido que o programador faz ao compilador. Este n ao precisa ser atendido necessariamente. Um exemplo do uso do register e dado: main(void) { register int count; for (count = 0; count < 10; count++) { ... } return 0; } O loop for acima ser a executado mais rapidamente do que seria se n ao us assemos o register. Este e o uso mais recomend avel para o register: uma vari avel que ser a usada muitas vezes em seguida.

10.3 10.4

Convers ao de tipos Modicadores de fun c oes

Em atribui co es no C temos o seguinte formato: destino = or gem; Se o destino e a or gem s ao de tipos diferentes o compilador faz uma convers ao entre os tipos. Nem todas as convers oes s ao poss veis. O primeiro ponto a ser ressaltado e que o valor de origem e convertido para o valor de destino antes de importante lembrar que quando convertemos um tipo num ser atribu do e n ao o contr ario. E erico para outro n os nunca ganhamos precis ao. N os podemos perder precis ao ou no m aximo manter a precis ao anterior. Isto pode ser entendido de uma outra forma. Quando convertemos um n umero n ao estamos introduzindo no sistema nenhuma informa ca o adicional. Isto implica que nunca vamos ganhar precis ao. Abaixo vemos uma tabela de convers oes num ericas com perda de precis ao, para um compilador com palavra de 16 bits: De unsigned char short int int long int long int long int oat double long double Para char char char char short int int int oat double Informa ca o Perdida Valores maiores que 127 s ao alterados Os 8 bits de mais alta ordem Os 8 bits de mais alta ordem Os 24 bits de mais alta ordem

10 TIPOS DE DADOS AVANC ADOS

82

Os 16 bits de mais alta ordem Os 16 bits de mais alta ordem Precis ao - resultado arredondado Precis ao - resultado arredondado Precis ao - resultado arredondado. A forma geral de uma fun ca o e, como j a foi visto, ca o (declara ca o de par ametros) corpo da fun ca o tipo de retorno nome da fun Uma fun ca o pode aceitar um modicador de tipo. Este vai modicar o modo como a fun ca o opera na passagem de par ametros. A forma geral da fun ca o caria ent ao: ca o (declara ca o de par ametros) corpo da fun ca o modicador de tipo tipo de retorno nome da fun O nosso curso n ao aborda detalhes do funcionamento interno de fun co es. Para saber mais, consulte o manual do seu compilador ou algum livro especializado. 10.4.1 O modicador pascal

Faz com que a fun ca o use a conven ca o de fun co es da linguagem de programa ca o Pascal. Isto faz com que as fun co es sejam compat veis com programas em Pascal. 10.4.2 O modicador cdecl

O modicador de tipo cdecl faz com que a fun ca o use a conven c ao para fun co es do C. Raramente e usado pois eo default. Pode-se pensar no cdecl como sendo o inversodo pascal. 10.4.3 O modicador interrupt

Diz ao compilador que a fun ca o em quest ao ser a usada como um manipulador de interrup co es. Isto faz com que o compilador preserve os registradores da CPU antes e depois da chamada ` a fun ca o. Mais uma vez este t opico est a fora do escopo do curso.

10.5

Ponteiros para Fun c oes

O C permite que acessemos vari aveis e fun co es atrav es de ponteiros! Podemos ent ao fazer coisas como, por exemplo, passar uma fun ca o como argumento para outra fun ca o. Um ponteiro para uma fun ca o tem a seguinte declara ca o: tipo de retorno (*nome do ponteiro)(); ou tipo de retorno (*nome do ponteiro)(declara ca o de par ametros); Repare nos par enteses que devem ser colocados obrigatoriamente. Se declaramos: tipo de retorno * nome(declara ca o de par ametros); Estar amos, na realidade, declarando uma fun ca o que retornaria um ponteiro para o tipo especicado. Por em, n ao e obrigat orio se declarar os par ametros da fun ca o. Veja um exemplo do uso de ponteiros para fun co es: #include <stdio.h> #include <string.h> void PrintString (char *str, int (*func)(const char *)); main(void) { char String [20]="Curso de C."; int (*p)(const char *); /* Declaracao do ponteiro para fun\c{c}\~ao Funcao apontada e inteira e recebe como parametro uma string constante */ p=puts; /* O ponteiro p passa a apontar para a fun\c{c}\~ao puts que tem o seguinte prototipo: int puts(const char *) */ PrintString (String, p); /* O ponteiro \e passado como parametro para PrintString */ return 0;

10 TIPOS DE DADOS AVANC ADOS

83

void PrintString (char *str, int (*func)(const char *)) { (*func)(str); /* chamada a fun\c{c}\~ao atrav\es do ponteiro para fun\c{c}\~ao */ func(str); /* maneira tamb\em v\alida de se fazer a chamada a fun\c{c}\~ao puts atrav\es do ponteiro para fun\c{c}\~ao func */ } Veja que zemos a atribui ca o de puts a p simplesmente usando: p = puts; Disto, conclu mos que o nome de uma fun ca o (sem os par enteses) e, na realidade, o endere co daquela fun ca o! Note, tamb em, as duas formas alternativas de se chamar uma fun ca o atrav es de um ponteiro. No programa acima, zemos esta chamada por: (*func)(str); e func(str); Estas formas s ao equivalentes entre si. Al em disto, no programa, a fun ca o PrintString() usa uma fun ca o qualquer func para imprimir a string na tela. O programador pode ent ao fornecer n ao s o a string mas tamb em a fun ca o que ser a usada para imprim -la. No main() vemos como podemos atribuir, ao ponteiro para fun co es p, o endere co da fun ca o puts() do C. Em s ntese, ao declarar um ponteiro para fun ca o, podemos atribuir a este ponteiro o endere co de uma fun ca o e podemos tamb em chamar a fun ca o apontada atrav es dele. N ao podemos fazer algumas coisas que faz amos com ponteiros normais, como, por exemplo, incrementar ou decrementar um ponteiro para fun ca o.

10.6

Aloca c ao din amica de mem oria

A aloca ca o din amica permite ao programador alocar mem oria para vari aveis quando o programa est a sendo executado. Assim, poderemos denir, por exemplo, um vetor ou uma matriz cujo tamanho descobriremos em tempo de execu ca o. O padr ao C ANSI dene apenas 4 fun co es para o sistema de aloca ca o din amica, dispon veis na biblioteca stdlib.h: malloc, calloc, realloc, free. No entanto, existem diversas outras fun co es que s ao amplamente utilizadas, mas dependentes do ambiente e compilador. Neste curso ser ao abordadas somente estas fun co es padronizadas. 10.6.1 A fun c ao malloc

A fun ca o malloc() serve para alocar mem oria e tem o seguinte prot otipo: void *malloc (unsigned int num); A fun cao toma o n umero de bytes que queremos alocar (num), aloca na mem oria e retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribu do a qualquer tipo de ponteiro. Se n ao houver mem oria suciente para alocar a mem oria requisitada a fun ca o malloc() retorna um ponteiro nulo. Veja um exemplo de aloca ca o din amica com malloc(): #include <stdio.h> #include <stdlib.h> main(void) { int *p; int a; int i; ... /* Determina o valor de a em algum lugar */ p=(int *)malloc(a*sizeof(int)); /* Aloca a n\umeros inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) /* Para usar malloc() */ { printf("** Erro: Memoria Insuficiente **"); exit;

10 TIPOS DE DADOS AVANC ADOS

84

for (i=0; i<a ; i++) p[i] = i*i; ... return 0; } No exemplo acima, e alocada mem oria suciente para se armazenar a n umeros inteiros. O operador sizeof() retorna o n umero de bytes de um inteiro. Ele e util para se saber o tamanho de tipos. O ponteiro void* que malloc() retorna e convertido para um int* pelo cast e e atribu do a p. A declara ca o seguinte testa se a opera ca o foi bem sucedida. Se n ao tiver sido, p ter a um valor nulo, o que far a com que !p retorne verdadeiro. Se a opera ca o tiver sido bem sucedida, podemos usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)]. 10.6.2 A fun c ao calloc

A fun ca o calloc() tamb em serve para alocar mem oria, mas possui um prot otipo um pouco diferente: void *calloc (unsigned int num, unsigned int size); A fun cao aloca uma quantidade de mem oria igual a num * size, isto e, aloca mem oria suciente para um vetor de num objetos de tamanho size. Retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribu do a qualquer tipo de ponteiro. Se n ao houver mem oria suciente para alocar a mem oria requisitada a fun ca o calloc() retorna um ponteiro nulo. Veja um exemplo de aloca ca o din amica com calloc(): #include <stdio.h> #include <stdlib.h> main(void) { int *p; int a; int i; ... /* Determina o valor de a em algum lugar */ p=(int *)calloc(a,sizeof(int)); /* Aloca a n\umeros inteiros p pode agora ser tratado como um vetor com a posicoes */

/* Para usar calloc() */

if (!p) { printf("** Erro: Memoria Insuficiente **"); exit; } for (i=0; i<a ; i++) p[i] = i*i; ... return 0; }

o exemplo acima, e alocada mem oria suciente para se colocar a n umeros inteiros. O operador sizeof() retorna o n umero de bytes de um inteiro. Ele e util para se saber o tamanho de tipos. O ponteiro void * que calloc() retorna e

10 TIPOS DE DADOS AVANC ADOS

85

convertido para um int * pelo cast e e atribu do a p. A declara ca o seguinte testa se a opera ca o foi bem sucedida. Se n ao tiver sido, p ter a um valor nulo, o que far a com que !p retorne verdadeiro. Se a opera ca o tiver sido bem sucedida, podemos usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)]. 10.6.3 A fun c ao realloc

A fun ca o realloc() serve para realocar mem oria e tem o seguinte prot otipo: void *realloc (void *ptr, unsigned int num); A fun cao modica o tamanho da mem oria previamente alocada apontada por *ptr para aquele especicado por num. O valor de num pode ser maior ou menor que o original. Um ponteiro para o bloco e devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se isso ocorrer, o conte udo do bloco antigo e copiado no novo bloco, e nenhuma informa ca o e perdida. Se ptr for nulo, aloca size bytes e devolve um ponteiro; se size e zero, a mem oria apontada por ptr e liberada. Se n ao houver mem oria suciente para a aloca ca o, um ponteiro nulo e devolvido e o bloco original e deixado inalterado. #include <stdio.h> #include <stdlib.h> main(void) { int *p; int a; int i; ... a = 30;

/* Para usar malloc() e realloc*/

/* Determina o valor de a em algum lugar */

p=(int *)malloc(a*sizeof(int)); /* Aloca a n\umeros inteiros p pode agora ser tratado a posicoes como um vetor com */ if (!p) { printf("** Erro: Memoria Insuficiente **"); exit; } for (i=0; i<a ; i++) p[i] = i*i; a = 100; /* O tamanho de p deve ser modificado, por algum motivo ... */ p = realloc (p, a*sizeof(int)); for (i=0; i<a ; i++) /* p pode ser tratado como um vetor com a posicoes */ p[i] = a*i*(i-6); ... }

10.6.4

A fun c ao free

Quando alocamos mem oria dinamicamente e necess ario que n os a liberemos quando ela n ao for mais necess aria. Para isto existe a fun ca o free() cujo prot otipo e: void free (void *p);

10 TIPOS DE DADOS AVANC ADOS

86

Basta ent ao passar para free() o ponteiro que aponta para o in cio da mem oria alocada. Mas voc e pode se perguntar: como e que o programa vai saber quantos bytes devem ser liberados? Ele sabe pois quando voc e alocou a mem oria, ele guardou o n umero de bytes alocados numa tabela de aloca ca ointerna. Vamos reescrever o exemplo usado para a fun ca o malloc() usando o free() tamb em agora: #include <stdio.h> #include <stdlib.h> main(void) { int *p; int a; ... p=(int *)malloc(a*sizeof(int)); if (!p) { printf("** Erro: Memoria Insuficiente **"); exit; } ... free(p); ... return 0; }

/* Para usar malloc e free */

10.7
10.7.1

Aloca c ao din amica de mem oria para vetores e matrizes


Aloca c ao din amica de mem oria para vetores

A aloca ca o din amica de vetores utiliza os conceitos aprendidos na aula sobre ponteiros e as fun co es de aloca ca o din amica apresentados. Um exemplo de implementa ca o para vetor real e fornecido a seguir: #include <stdio.h> #include <stdlib.h>

float *Alocar_vetor_real (int n) { float *v; /* ponteiro para o vetor */ if (n < 1) /* verifica parametros recebidos */ { printf("** Erro: Parametro invalido **\n"); return (NULL); } v = (float *) calloc (n, sizeof(float)); /* aloca o vetor */ if (v == NULL) { printf("** Erro: Memoria Insuficiente **"); return (NULL);

10 TIPOS DE DADOS AVANC ADOS

87

} return (v); /* retorna o ponteiro para o vetor */ }

float *Liberar_vetor_real (float *v) { if (v == NULL) return (NULL); free(v); /* libera o vetor */ return (NULL); /* retorna o ponteiro */ }

void main(void) { float *p; int a; ... /* outros comandos, inclusive a inicializacao de a */ p = Alocar_vetor_real (a); ... /* outros comandos, utilizando p[] normalmente */ p = Liberar_vetor_real (p); }

10.7.2

Aloca c ao din amica de mem oria para matrizes

A aloca ca o din amica de mem oria para matrizes e realizada da mesma forma que para vetores, com a diferen ca que teremos um ponteiro apontando para outro ponteiro que aponta para o valor nal, ou seja e um ponteiro para ponteiro, o que e denominado indire ca o m ultipla. A indire ca o m ultipla pode ser levada a qualquer dimens ao desejada, mas raramente e necess ario mais de um ponteiro para um ponteiro. Um exemplo de implementa ca o para matriz real bidimensional e fornecido a seguir. A estrutura de dados utilizada neste exemplo e composta por um vetor de ponteiros (correspondendo ao primeiro ndice da matriz), sendo que cada ponteiro aponta para o in cio de uma linha da matriz. #include <stdio.h> #include <stdlib.h> float **Alocar_matriz_real (int m, int n) { float **v; /* ponteiro para a matriz */ int i; /* variavel auxiliar */ if (m < 1 || n < 1) /* verifica parametros recebidos */ { printf("** Erro: Parametro invalido **\n"); return (NULL); } v = (float **) calloc (m, sizeof(float *)); /* Um vetor de m ponteiros para float */ /* aloca as linhas da matriz */ if (v == NULL) { printf("** Erro: Memoria Insuficiente **"); return (NULL);

10 TIPOS DE DADOS AVANC ADOS

88

} for ( i = 0; i < m; i++ ) { v[i] = (float*) calloc (n, sizeof(float)); /* aloca as colunas da matriz */ /* m vetores de n floats */ if (v[i] == NULL) { printf("** Erro: Memoria Insuficiente **"); return (NULL); } } return (v); /* retorna o ponteiro para a matriz */ }

float **Liberar_matriz_real (int m, int n, float **v) { int i; /* variavel auxiliar */ if (v == NULL) return (NULL); if (m < 1 || n < 1) /* verifica parametros recebidos */ { printf("** Erro: Parametro invalido **\n"); return (v); } for (i=0; i<m; i++) free (v[i]); /* libera as linhas da matriz */ free (v); /* libera a matriz (vetor de ponteiros) */ return (NULL); /* retorna um ponteiro nulo */ } void main(void) { float **mat; /* matriz a ser alocada */ int l, c; /* numero de linhas e colunas da matriz */ int i, j; ... /* outros comandos, inclusive inicializacao para l e c */ mat = Alocar_matriz_real (l, c); for (i = 0; i < l; i++) for ( j = 0; j < c; j++) mat[i][j] = i+j; { ... /* outros comandos utilizando mat[][] normalmente */ mat = Liberar_matriz_real (l, c, mat); ... }

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

89

11
11.1

Constru c ao de novos tipos de dados al em dos padr ao


Estruturas

Uma estrutura agrupa v arias vari aveis numa s o. Funciona como uma cha pessoal que tenha nome, telefone e endere co. A cha seria uma estrutura. A estrutura, ent ao, serve para agrupar um conjunto de dados n ao similares, formando um novo tipo de dados. 11.1.1 Criando estruturas

Para se criar uma estrutura usa-se o comando struct. Sua forma geral e: struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } vari\aveis_estrutura; e o nome para a estrutura. As vari aveis estrutura s ao opcionais e seriam nomes de O nome do tipo da estrutura vari aveis que o usu ario j a estaria declarando e que seriam do tipo nome do tipo da estrutura. Um primeiro exemplo: struct est { int i; float f; } a, b; Neste caso, est e uma estrutura com dois campos, i e f . Foram tamb em declaradas duas vari aveis, a e b que s ao do tipo da estrutura, isto e, a possui os campos i e f , o mesmo acontecendo com b. Vamos criar uma estrutura de endere co: struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; Vamos agora criar uma estrutura chamada cha pessoal com os dados pessoais de uma pessoa: struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; e usada pela struct Vemos, pelos exemplos acima, que uma estrutura pode fazer parte de outra ( a struct tipo endereco cha pessoal).

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

90

11.1.2

Usando estruturas

Vamos agora utilizar as estruturas declaradas na se ca o anterior para escrever um programa que preencha uma cha. #include <stdio.h> #include <string.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; main(void) { struct ficha_pessoal ficha; strcpy(ficha.nome, "Luiz Osvaldo Silva"); ficha.telefone = 4921234; strcpy(ficha.endereco.rua, "Rua das Flores"); ficha.endereco.numero = 10; strcpy(ficha.endereco.bairro, "Cidade Velha"); strcpy(ficha.endereco.cidade, "Belo Horizonte"); strcpy(ficha.endereco.sigla_estado, "MG"); ficha.endereco.CEP = 31340230; return 0; } O programa declara uma vari avel cha do tipo cha pessoal e preenche os seus dados. O exemplo mostra como podemos acessar um elemento de uma estrutura: basta usar o ponto (.). Assim, para acessar o campo telefone de cha, escrevemos: ficha.telefone = 4921234; Como a struct cha pessoal possui um campo, endereco, que tamb em e uma struct, podemos fazer acesso aos campos desta struct interna da seguinte maneira: ficha.endereco.numero = 10; ficha.endereco.CEP = 31340230; Desta forma, estamos acessando, primeiramente, o campo endereco da struct cha e, dentro deste campo, estamos acessando o campo numero e o campo CEP.

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

91

11.1.3

Matrizes de estruturas

Um estrutura e como qualquer outro tipo de dado no C. Podemos, portanto, criar matrizes de estruturas. Vamos ver como caria a declara ca o de um vetor de 100 chas pessoais: struct ficha_pessoal fichas [100]; Poder amos ent ao acessar a segunda letra da sigla de estado da d ecima terceira cha fazendo: fichas[12].endereco.sigla_estado[1];

11.1.4

Atribuindo estruturas

Podemos atribuir duas estruturas que sejam do mesmo tipo. O C ir a, neste caso, copiar uma estrutura, campo por campo, na outra. Veja o programa abaixo: struct est1 { int i; float f; }; void main() { struct est1 primeira, segunda;

/* Declara primeira e segunda como structs do tipo est1 */

primeira.i = 10; primeira.f = 3.1415; segunda = primeira;

/* A segunda struct e agora igual a primeira */ segunda.i , segunda.f);

printf(" Os valores armazenasdos na segunda struct sao : %d e %f ", }

S ao declaradas duas estruturas do tipo est1, uma chamada primeira e outra chamada segunda. Atribuem-se valores aos dois campos da struct primeira. Os valores de primeira s ao copiados em segunda apenas com a express ao de atribui ca o: segunda = primeira; Todos os campos de primeira ser ao copiados na segunda. Note que isto e diferente do que acontecia em vetores, onde, para fazer a c opia dos elementos de um vetor em outro, t nhamos que copiar elemento por elemento do vetor. Nas structs e muito mais f acil! Por em, devemos tomar cuidado na atribui ca o de structs que contenham campos ponteiros. Veja abaixo: #include <stdio.h> #include <string.h> #include <stdlib.h> struct tipo_end { char *rua; int numero; }; void main() { struct tipo_end end1, end2; char buffer[50]; printf("\nEntre o nome da rua:"); gets(buffer); /* Le o nome da rua em uma string de buffer */ end1.rua = (char *) malloc((strlen(buffer)+1)*sizeof(char)); /* A struct possui um campo que \e um ponteiro */

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

92

/* Aloca a quantidade de memoria suficiente para armazenar a string */ strcpy(end1.rua, buffer); printf("\nEntre o numero:"); scanf("%d", &end1.numero); end2 = end1; /* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */ /* Copia a string */

printf("Depois da atribuicao:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua, end1.numero, end2.rua, end2.numero); strcpy(end2.rua, "Rua Mesquita"); /* Uma modificacao na memoria apontada por end2.rua causara a modificacao ontado por end1.rua, o que, esta errado !!! */ end2.numero = 1100; /* Nesta atribuicao nao ha problemas */ printf(" \n\nApos modificar o endereco em end2:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua, end1.numero, end2.rua, end2.numero); } Neste programa h a um erro grave, pois ao se fazer a atribui ca o end2 = end1, o campo rua de end2 estar a apontando para a mesma posi ca o de mem oria que o campo rua de end1. Assim, ao se modicar o conte udo apontado por end2.rua estaremos tamb em modicando o conte udo apontado por end1.rua! 11.1.5 Passando estruturas para fun co es

No exemplo apresentado no tem usando estruturas, vimos o seguinte comando: strcpy(ficha.nome, "Luiz Osvaldo Silva"); Neste comando um elemento de uma estrutura e passado para uma fun ca o. Este tipo de opera ca o pode ser feita sem maiores considera co es. Podemos tamb em passar para uma fun ca o uma estrutura inteira. Veja a seguinte fun ca o: void PreencheFicha (struct ficha_pessoal ficha) { ... } Como vemos acima e f acil passar a estrutura como um todo para a fun ca o. Devemos observar que, como em qualquer outra fun ca o no C, a passagem da estrutura e feita por valor. A estrutura que est a sendo passada, vai ser copiada, campo por campo, em uma vari avel local da fun ca o PreencheFicha. Isto signica que altera co es na estrutura dentro da fun ca o n ao ter ao efeito na vari avel fora da fun ca o. Mais uma vez podemos contornar este pormenor usando ponteiros e passando para a fun ca o um ponteiro para a estrutura. 11.1.6 Estruturas ponteiros

Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia ser declarado um ponteiro para as estruturas de cha que estamos usando nestas se co es: struct ficha_pessoal *p; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. Para us a-lo, haveria duas possibilidades. A primeira e apont a-lo para uma vari avel struct j a existente, da seguinte maneira:

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

93

struct ficha_pessoal ficha; struct ficha_pessoal *p; p = &ficha; A segunda e alocando mem oria para cha pessoal usando, por exemplo, malloc(): #include <stdlib.h> main() { struct ficha_pessoal *p; int a = 10; /* Faremos a alocacao dinamica de 10 fichas pessoais */ p = (struct ficha_pessoal *) malloc (a * sizeof(struct ficha_pessoal)); p[0].telefone = 3443768; /* Exemplo de acesso ao campo telefone da primeira ficha apontada por p */ free(p); } H a mais um detalhe a ser considerado. Se apontarmos o ponteiro p para uma estrutura qualquer (como zemos em p = &cha; ) e quisermos acessar um elemento da estrutura poder amos fazer: (*p).nome. Os par enteses s ao necess arios, porque o operador . tem preced encia maior que o operador *. Por em, este formato n ao e muito usado. O que e comum de se fazer e acessar o elemento nome atrav es do operador seta, que e formado por um sinal de menos(-) seguido por um sinal de maior que(), isto e: - . Assim faremos: p-nome. A declara ca o acima e muito mais f acil e concisa. Para acessarmos o elemento CEP dentro de endereco far amos: p -> endereco.CEP

11.2

Uni oes

Uma declara ca o union determina uma u nica localiza ca o de mem oria onde podem estar armazenadas v arias vari aveis diferentes. A declara ca o de uma uni ao e semelhante ` a declara ca o de uma estrutura: union nome_do_tipo_da_union { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variaveis_union; Como exemplo, vamos considerar a seguinte uni ao: union angulo { float graus; float radianos; };

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

94

Nela, temos duas vari aveis (graus e radianos) que, apesar de terem nomes diferentes, ocupam o mesmo local da mem oria. Isto quer dizer que s o gastamos o espa co equivalente a um u nico oat. Uni oes podem ser feitas tamb em com vari aveis de diferentes tipos. Neste caso, a mem oria alocada corresponde ao tamanho da maior vari avel no union. Veja o exemplo: #include <stdio.h> #define GRAUS G #define RAD R union angulo { int graus; float radianos; }; void main() { union angulo ang; char op; printf("\nNumeros em graus ou radianos? (G/R):"); scanf("%c", &op); if (op == GRAUS) { ang.graus = 180; printf("\nAngulo: %d\n", ang.graus); } else if (op == RAD) { ang.radianos = 3.1415; printf("\nAngulo: %f\n", ang.radianos); } else printf("\nEntrada invalida!!\n"); } Temos que tomar o maior cuidado pois poder amos fazer: #include <stdio.h> union numero { char Ch; int I; float F; }; main(void) { union numero N; N.I = 123; printf("%f", N.F); return 0; }

/* Vai imprimir algo que nao e necessariamente123 ...*/

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

95

O programa acima e muito perigoso pois voc e est a lendo uma regi ao da mem oria, que foi gravadacomo um inteiro, como se fosse um ponto utuante. Tome cuidado! O resultado pode n ao fazer sentido.

11.3

Enumera c oes

Numa enumera ca o podemos dizer ao compilador quais os valores que uma determinada vari avel pode assumir. Sua forma geral e: enum nome_do_tipo_da_enumerao { lista_de_valores } lista_de_variveis; Vamos considerar o seguinte exemplo: enum dias_da_semana { segunda, terca, quarta, quinta, sexta, sabado, domingo }; o pode ter os valores enumerados. O programador diz ao compilador que qualquer vari avel do tipo dias da semana s Isto quer dizer que poder amos fazer o seguinte programa: #include <stdio.h> enum dias_da_semana {segunda, terca, quarta, quinta, sexta, sabado, domingo}; main(void) { enum dias_da_semana d1, d2; d1 = segunda; d2 = sexta; if (d1 == d2) { printf("O dia e o mesmo."); } else { printf("S\~ao dias diferentes."); } return 0; } Voc e deve estar se perguntando como e que a enumera ca o funciona. Simples. O compilador pega a lista que voc e fez de valores e associa, a cada um, um n umero inteiro. Ent ao, ao primeiro da lista, e associado o n umero zero, o segundo ao n umero 1 e assim por diante. As vari aveis declaradas s ao ent ao vari aveis int.

11.4

O Comando sizeof

O operador sizeof e usado para se saber o tamanho de vari aveis ou de tipos. Ele retorna o tamanho do tipo ou vari avel em bytes. Devemos us a-lo para garantir portabilidade. Por exemplo, o tamanho de um inteiro pode depender do sistema para o qual se est a compilando. O sizeof e um operador porque ele e substitu do pelo tamanho do tipo ou vari avel no momento da compila ca o. Ele n ao e uma fun ca o. O sizeof admite duas formas:

DE NOVOS TIPOS DE DADOS ALEM DOS PADRAO 11 CONSTRUC AO

96

sizeof nome_da_variavel sizeof (nome_do_tipo) Se quisermos ent ao saber o tamanho de um oat fazemos sizeof(oat). Se declararmos a vari avel f como oat e quisermos saber o seu tamanho faremos sizeof f. O operador sizeof tamb em funciona com estruturas, uni oes e enumera co es. Outra aplica ca o importante do operador sizeof e para se saber o tamanho de tipos denidos pelo usu ario. Seria, por exemplo, uma tarefa um tanto complicada a de alocar a mem oria para um ponteiro para a estrutura cha pessoal, criada anteriormente se n ao fosse o uso de sizeof. Veja o exemplo: #include <stdio.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; void main(void) { struct ficha_pessoal *ex; ex = (struct ficha_pessoal *) malloc(sizeof(struct ficha_pessoal)); ... free(ex); }

11.5

O Comando typedef

O comando typedef permite ao programador denir um novo nome para um determinado tipo. Sua forma geral e: typedef antigo_nome novo_nome; Como exemplo vamos dar o nome de inteiro para o tipo int: typedef int inteiro; Agora podemos declarar o tipo inteiro. O comando typedef tamb em pode ser utilizado para dar nome a tipos complexos, como as estruturas. As estruturas criadas no exemplo da p agina anterior poderiam ser denidas como tipos atrav es do comando typedef. O exemplo caria: #include <stdio.h> typedef struct tipo_endereco {

12 UMA APLICAC AOUTILIZANDO ESTRUTURAS

97

char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; } TEndereco; typedef struct ficha_pessoal { char nome [50]; long int telefone; TEndereco endereco; }TFicha; void main(void) { TFicha *ex; ... } Veja que n ao e mais necess ario usar a palavra chave struct para declarar vari aveis do tipo cha pessoal. Basta agora usar o novo tipo denido TFicha.

12

Uma aplica c aoutilizando estruturas

V arias estruturas de dados complexas podem ser criadas utilizando simultaneamente structs e ponteiros. Uma destas estruturas e a lista encadeada. Uma lista encadeada e uma seq encia de structs, que s ao os n os da lista, ligados entre si atrav es de ponteiros. Esta seq encia pode ser acessada atrav es de um ponteiro para o primeiro n o, que e a cabe ca da lista. Cada n o cont em um ponteiro que aponta para a struct que e a sua sucessora na lista. O ponteiro da u ltima struct da lista aponta para NULL, indicando que se chegou ao nal da lista. Esta estrutura de dados e criada dinamicamente na mem oria (utiliza-se malloc() e free()), de modo que se torna simples introduzir n os nela, retirar n os, ordenar os n os, etc. N ao vamos entrar em detalhes sobre todos os algoritmos que poder amos criar em uma lista encadeada, pois isto geralmente e feito em cursos de algoritmos e estruturas de dados, n ao se incluindo no escopo deste curso. Aqui, veremos somente formas de se criar uma lista encadeada em C e tamb em maneiras simples de percorrer esta lista. Supondo que queiramos criar uma lista encadeada para armazenar os produtos dispon veis em uma loja. Poder amos criar um n o desta lista usando a seguinte struct: struct Produto { int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ struct Produto *proximo; /* Proximo elemento da lista encadeada de Produtos */ }; Note que esta struct possui, al em dos campos de dados codigo e preco, um campo adicional que e um ponteiro para este campo que ser uma struct do tipo Produto. E a utilizado para apontar para o pr oximo n o da lista encadeada. O programa a seguir faz uso desta struct, atrav es de um novo tipo criado por um typedef, para criar uma lista de produtos de uma loja: #include <stdio.h> #include <stdlib.h> typedef struct tipo_produto /* Estrutura que ser\a usada para criar os n\os da lista */ {

12 UMA APLICAC AOUTILIZANDO ESTRUTURAS

98

int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ struct tipo_produto *proximo; /* Proximo elemento da lista encadeada de Produtos */ } TProduto; /* Prototipos das funcoes para inserir e listar produtos */ void inserir(TProduto **cabeca); void listar (TProduto *cabeca); int main() { TProduto *cabeca = NULL; TProduto *noatual; char q; do { printf("\n\nOpcoes: \nI -> para inserir novo produto; \nL -> para listar os produtos; \nS -> para sair \n:"); scanf("%c", &q); /* Le a opcao do usuario */ switch(q) { case i: case I: inserir(&cabeca); break; case l: case L: listar(cabeca); break; case s: case S: break; default: printf("\n\n Opcao nao valida"); } fflush(stdin); /* Limpa o buffer de entrada */ } while ((q != s) && (q != S) ); /* Desaloca a memoria alocada para os elementos da lista */ noatual = cabeca; while (noatual != NULL) { cabeca = noatual->proximo; free(noatual); noatual = cabeca; } } void listar (TProduto *noatual) { int i=0; while( noatual != NULL) /* Enquanto nao chega no fim da lista */ { i++; printf("\n\nProduto numero %d\nCodigo: %d \nPreco:R$%.2lf", i, noatual->codigo, noatual->preco); /* Lista todos os elementos presentes na lista encadeada */

/* Ponteiro para a cabeca da lista */ /* Ponteiro a ser usado para percorrer a lista no momento de desalocar seus elementos*/ /* Caractere para receber a opcao do usuario */

12 UMA APLICAC AOUTILIZANDO ESTRUTURAS

99

noatual = noatual->proximo; /* Faz noatual apontar para o proximo no */ } } void inserir (TProduto **cabeca) /* Funcao para inserir um novo no, ao final da lista */ { TProduto *noatual, *novono; int cod; double preco; printf("\n Codigo do novo produto: "); scanf("%d", &cod); printf("\n Preco do produto:R$"); scanf("%lf", &preco); if (*cabeca == NULL) /* Se ainda nao existe nenhum produto na lista */ { *cabeca = (TProduto *) malloc(sizeof(TProduto)); /* cria o no cabeca */ (*cabeca)->codigo = cod; (*cabeca)->preco = preco; (*cabeca)->proximo = NULL; } else { noatual = *cabeca; /* Se ja existem elementos na lista, deve percorre-la ate o seu final e inserir o novo elemento */ while(noatual->proximo != NULL) noatual = noatual->proximo; /* Ao final do while, noatual aponta para o ultimo no */ novono = (TProduto *) malloc(sizeof(TProduto)); /* Aloca memoria para o novo no */ novono->codigo = cod; novono->preco = preco; novono->proximo = NULL; noatual->proximo = novono; /* Faz o ultimo no apontar para o novo no */ } } interessante notar que, no programa anterior n E ao existe limite para o n umero de produtos que se vai armazenar na lista. Toda vez que for necess ario criar um novo produto, mem oria para ele ser a alocada e ele ser a criado no nal da lista. Note que a fun ca o inserir recebe o endere co do ponteiro cabe ca da lista. Qual a raz ao disto? A raz ao e que o endere co para o qual a cabe ca da lista aponta poder a ser modicado caso se esteja inserindo o primeiro elemento na lista. Tente entender todos os passos deste programa, pois ele possui v arias das caracter sticas presentes em programas que manipulam listas encadeadas. Tamb em e importante notar que v arias outras estruturas de dados complexas podem ser criadas com structs contendo ponteiros que apontam para outras structs.

Potrebbero piacerti anche