Você está na página 1de 235

Contedo

Pginas
Capa Programar em C Por que aprender a linguagem C Histria da linguagem C Pr-requisitos Utilizando um compilador Noes de compilao Um programa em C Conceitos bsicos Variveis Entrada e sada simples Operaes matemticas (Bsico) Operaes matemticas (Avanado) Controle de fluxo Funes Pr-processador Bibliotecas Entrada e sada em arquivos Vetores Strings Ponteiros Passagem de Parametros Tipos de dados definidos pelo usurio Enumerao Unio Estruturas Mais sobre variveis Gerenciamento de memria Sockets Makefiles Lista de palavras reservadas Seqncias de escape Lista de funes Lista de bibliotecas 1 1 3 4 7 8 10 11 12 14 19 28 30 34 44 56 61 64 67 69 73 87 88 94 97 99 110 116 119 120 128 128 129 130

Dicas de programao em C Listas encadeadas Pilha rvores binrias Algoritmos de ordenao Algoritmo de alocao Estudo Constantes Condicionais

131 134 138 140 143 145 146 217 219

Referncias
Fontes e Editores da Pgina Fontes, Licenas e Editores da Imagem 230 232

Licenas das pginas


Licena 233

Capa

Capa

ndice>> Ir para o ndice >>

Programar em C
pr-requisito para um bom aprendizado de qualquer linguagem de programao conceitos sobre lgica de programao.

Sumrio
Introduo
Por que aprender a linguagem C? Histria da Linguagem C Pr-requisitos Utilizando um compilador Noes de compilao

PARTE I - Bsico
Um programa em C Conceitos bsicos Variveis Entrada e sada simples Operaes matemticas (Bsico) Operaes matemticas (Avanado) Controle de fluxo Funes O pr-processador Bibliotecas Entrada e sada em arquivos Exerccios

Programar em C

PARTE II - Conceitos intermedirios e avanados


Vetores (arrays) Strings Ponteiros Passagem de Parametros Tipos de dados definidos pelo usurio Enumerao (enum) Unio (union) Estruturas (structs) Mais sobre variveis Gerenciamento de memria Operadores bit-a-bit Sockets X/Open Transport Interface(XTI) Makefiles

Apndices
Palavras Reservadas ou keywords Seqncias de escape Lista de funes Lista de bibliotecas Dicas de programao em C

Algoritmos e Estruturas de dados


Listas encadeadas Pilha Fila ou Queue rvores binrias Algoritmos de ordenao Algoritmo de alocao

Mdulo em processo de fuso


Estudo do C Tpicos movidos do livro de C++ 1. 2. 3. 4. Variveis Constantes Condicionais Vetores

Programar em C

Colaborao
Lista de autores

Por que aprender a linguagem C


Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

C uma das linguagens de programao mais populares para se escrever sistemas operacionais, como o Microsoft Windows, o Mac OS X e o GNU/Linux. Sistemas operacionais comunicam-se diretamente com o hardware; no h nenhuma camada mais baixa para mediar seus pedidos. Originalmente, os sistemas operacionais eram escritos na linguagem Assembly, o que resultava em um cdigo muito rpido e eficiente. Entretanto, escrever um sistema operacional em Assembly um processo tedioso (lento), e produz um cdigo que funcionar somente em uma arquitetura de CPU, tal como o Intel x86 ou AMD64. Escrever um sistema operacional em uma linguagem de alto nvel, tal como C, possibilita que os programadores readaptem o sistema operacional a vrias arquiteturas sem precisar reescrever todo o cdigo. O ncleo (kernel) Linux um exemplo de sistema operacional escrito em C, com apenas algumas sees do cdigo escritas em Assembly, para poder executar instrues que s existem em uma ou outra arquitetura e para algumas otimizaes. Mas porque C e no Java ou Basic, ou ainda Perl? Principalmente devido ao gerenciamento de memria. Ao contrrio de muitas linguagens de programao, o C permite ao programador enderear a memria de maneira muito parecida como seria feito em Assembly. Linguagens como o Java ou o Perl fornecem mecanismos que permitem que o programador faa o seu trabalho sem se ter de preocupar com a atribuio de memria ou com apontadores. Geralmente isso bom, uma vez que bastante tedioso lidar com a alocao de memria quando escrevemos aplicaes com algoritmos de alto nvel. No entanto, quando lidamos com tarefas de baixo-nvel como aquelas que um ncleo (kernel) tem obrigao de desempenhar, como a de copiar um conjunto de bytes para uma placa de rede, torna-se altamente necessrio um acesso directo memria algo que no possvel fazer apenas com Java. C pode ser diretamente compilado em cdigo de maquina, que rapido e eficiente. Ser uma surpresa que C seja uma linguagem to popular? Como num efeito domin, a prxima gerao de programas segue a tendncia dos seus ancestrais. Sistemas operacionais desenvolvidos em C sempre tm bibliotecas de sistema desenvolvidas em C. Essas bibliotecas so usadas para criar bibliotecas de programa (como OpenGL ou GTK), e seus desenvolvedores geralmente decidem usar a mesma linguagem das bibliotecas de sistema. Desenvolvedores de aplicao usam bibliotecas de programa para desenvolver processadores de texto, jogos, tocadores de mdia, etc. Muitos vo decidir trabalhar com a mesma linguagem que a biblioteca foi escrita. E assim o processo continua...

Histria da linguagem C

Histria da linguagem C
Este mdulo encontra-se em processo de traduo. A sua ajuda bem vinda.

Histria
Desenvolvimentos iniciais
O desenvolvimento inicial da linguagem C ocorreu nos laboratrios Bell da AT&T entre 1969 e 1973. Segundo Ritchie, o periodo mais criativo ocorreu em 1972. Deu-se o nome "C" linguagem porque muitas das suas caractersticas derivaram de uma linguagem de programao anterior chamada "B". H vrios relatos que se referem origem do nome "B": Ken Thompson d crdito linguagem de programao BCPL, mas ele tambm criou uma outra linguagem de programao chamada 'Bon, em honra da sua mulher Bonnie.

Kenneth Thompson ( esquerda) e Dennis Ritchie ( direita), os criadores da linguagem C

Por volta de 1973, a linguagem C tinha se tornado suficientemente poderosa para que grande parte do ncleo de UNIX, originalmente escrito na linguagem de programao PDP-11/20 Assembly, fosse reescrito em C. Este foi um dos primeiros ncleos de sistema operativo que foi implementado numa linguagem sem ser o Assembly, sendo exemplos anteriores o sistema Multics (escrito em PL/I) e TRIPOS (escrito em BCPL).

C de K&R
Em 1978, Ritchie e Kernighan publicaram a primeira edio do livro The C Programming Language. Esse livro, conhecido pelos programadores de C como "K&R", serviu durante muitos anos como uma especificao informal da linguagem. A verso da linguagem C que ele descreve usualmente referida como "C de K&R". (A segunda edio do livro cobre o posterior padro ANSI C, descrito abaixo.) K&R introduziram as seguintes caractersticas na linguagem: Tipos de dados struct Tipos de dados long int Tipos de dados unsigned int O operador =+ foi alterado para +=, e assim sucessivamente (a anlise lxica do compilador confundia o operador =+. Por exemplo, i =+ 10 e i = +10).

C de K&R frequentemente considerado a parte mais bsica da linguagem cujo suporte deve ser assegurado por um compilador C. Durante muitos anos, mesmo aps a introduo do padro C ANSI, ele era considerado o "menor denominador comum" em que programadores de C se apoiavam quando uma portabilidade mxima era desejada, j que nem todos os compiladores eram actualizados para suportar na ntegra o padro C ANSI, e o cdigo C de K&R razoavelmente bem escrito tambm vlido em relao ao C ANSI.

Histria da linguagem C Nos anos que se seguiram publicao do C K&R, algumas caractersticas "no-oficiais" foram adicionadas linguagem, suportadas por compiladores da AT&T e de outros vendedores. Estas incluam: Funes void e tipos de dados void * Funes que retornam tipos struct ou union Campos de nome struct num espao de nome separado para cada tipo struct Atribuio a tipos de dados struct Qualificadores const para criar um objecto s de leitura Uma biblioteca-padro que incorpora grande parte da funcionalidade implementada por vrios vendedores Enumeraes O tipo de ponto-flutuante de preciso simples

C ANSI e C ISO
Durante os finais da dcada de 1970, a linguagem C comeou a substituir a linguagem BASIC como a linguagem de programao de microcomputadores mais usada. Durante a dcada de 1980, foi adotada para uso no PC IBM, e a sua popularidade comeou a aumentar significativamente. Ao mesmo tempo, Bjarne Stroustrup, juntamente com outros nos laboratrios Bell, comeou a trabalhar num projeto onde se adicionavam construes de linguagens de programao orientada por objetos linguagem C. A linguagem que eles produziram, chamada C++, nos dias de hoje a linguagem de programao de aplicaes mais comum no sistema operativo Windows da companhia Microsoft; C permanece mais popular no mundo UNIX. Em 1983, o Instituto Norte-Americano de Padres (ANSI) formou um comit, X3j11, para estabelecer uma especificao do padro da linguagem C. Aps um processo longo e rduo, o padro foi completo em 1989 e ratificado como ANSI X3.159-1989 "Programming Language C". Esta verso da linguagem frequentemente referida como C ANSI. Em 1990, o padro C ANSI, aps sofrer umas modificaes menores, foi adotado pela Organizao Internacional de Padres (ISO) como ISO/IEC 9899:1990. Um dos objetivos do processo de padronizao C ANSI foi o de produzir um sobreconjunto do C K&R, incorporando muitas das caractersticas no-oficiais subsequentemente introduzidas. Entretanto, muitos programas tinham sido escritos e que no compilavam em certas plataformas, ou com um certo compilador, devido ao uso de bibliotecas de funes no-padro e ao fato de alguns compiladores no aderirem ao C ANSI.

C99
Aps o processo ANSI de padronizao, as especificaes da linguagem C permaneceram relativamente estticas por algum tempo, enquanto que a linguagem C++ continuou a evoluir. (Em 1995, a Normative Ammendment 1 criou uma verso nova da linguagem C mas esta verso raramente tida em conta.) Contudo, o padro foi submetido a uma reviso nos finais da dcada de 1990, levando publicao da norma ISO 9899:1999 em 1999. Este padro geralmente referido como "C99". O padro foi adoptado como um padro ANSI em Maro de 2000. As novas caractersticas do C99 incluem: Funes em linha Levantamento de restries sobre a localizao da declarao de variveis (como em C++) Adio de vrios tipos de dados novos, incluindo o long long int (para minimizar a dor da transio de 32-bits para 64-bits), um tipo de dados boolean explicito e um tipo complex que representa nmeros complexos Disposies de dados de comprimento varivel Suporte oficial para comentrios de uma linha iniciados por //, emprestados da linguagem C++ Vrias funes de biblioteca novas, tais como snprintf() Vrios ficheiros-cabealho novos, tais como stdint.h

Histria da linguagem C O interesse em suportar as caractersticas novas de C99 parece depender muito das entidades. Apesar do GCC e vrios outros compiladores suportarem grande parte das novas caractersticas do C99, os compiladores mantidos pela Microsoft e pela Borland no, e estas duas companhias no parecem estar muito interessadas adicionar tais funcionalidades, ignorando por completo as normas internacionais.

Resumo em ingls
Em 1947, trs cientistas do Laboratrio Telefonia Bell, William Shockley, Walter Brattain, e John Bardeen criaram o transistor.A computao moderna teve incio. Em 1956 no MIT o primeiro computador completamente baseado em transistores foi concluido, the TX-0. Em 1958 na Texas Instruments, Jack Kilby construiu o primeiro circuito integrado. Mas mesmo antes do primeiro circuito integrado existir, a primeira linguagem de alto nvel j tinha sido escrita. Em 1954 Fortran, a Formula Translator, foi escrito. Comeou como Fortran I em 1956. Fortran veio a ser Algol 58, o Algorithmic Language, em 1958. Algol 58 veio a ser Algol 60 em 1960. Algol 60 veio a ser CPL, o Combined Programming Language, em 1963. CPL veio a ser BCPL, Basic CPL, em 1967. BCPL veio a ser B em 1969. B veio a ser C em 1971. B was the first language in the C lineage directly, having been created at Bell Labs by Ken Thompson. B was an interpreted language, used in early, internal versions of the UNIX operating system. Thompson and Dennis Ritchie, also of Bell Labs, improved B, calling it NB; further extensions to NB created C, a compiled language. Most of UNIX was rewritten in NB and then C, which led to a more portable operating system. B was of course named after BCPL, and C was its logical successor. A portabilidade do UNIX foi a razo principal para a popularidade inicial de ambos, UNIX e C; rather than creating a new operating system for each new machine, system programmers could simply write the few system dependent parts required for the machine, and write a C compiler for the new system; and since most of the system utilities were written in C, it simply made sense to also write new utilities in the language.

Pr-requisitos

Pr-requisitos
pr-requisito para um bom aprendizado de qualquer linguagem de programao conceitos sobre lgica de programao. Alm disso, para programar em C, voc precisa de um editor de textos e um compilador, discutidos a seguir.

Editor
Para editar o cdigo de um programa, apenas necessrio um editor de textos, qualquer um, at mesmo o Bloco de Notas do Windows. No entanto, h diversos editores que apresentam recursos que facilitam a edio de programas, como: destaque/colorao de sintaxe, complementao de cdigo, formatao (indentao) automtica, ajuda integrada, comandos integrados para compilar etc. Entre todos eles podemos destacar o Vim e o Emacs, ambos com verses para Windows, Linux e Mac OS. Em sistemas GNU/Linux, a maioria dos editores de texto j possuem recursos para facilitar a edio de programas em C. Principalmente, devido ao fato da maioria deles (dos programas) e boa parte do sistema terem sido programados utilizando C ou C++. Entretanto, o editor apenas edita o cdigo. Para transforma-lo em linguagem de mquina e o executar, precisaremos de um compilador.

Compilador
O cdigo em linguagem C consiste em instrues que o computador dever seguir. O compilador realiza o trabalho de traduzir essas instrues para linguagem de mquina, de forma a poderem ser executadas pelo computador.

Obtendo um compilador
Existem diversos compiladores disponveis: Para Windows ou DOS MinGW (antigo mingw32): uma espcie de gcc para Windows. o compilador includo com o Dev-C++, da Bloodshed. O Dev-C++ um IDE (sigla em ingls para Ambiente Integrado de Desenvolvimento) que facilita a edio e compilao de programas. Tem traduo para Portugus do Brasil. Borland C++: a Borland disponibilizou um compilador gratuito que funciona em linha de comando, como alternativa ao IDE comercial. DJGPP: porte do gcc para DOS. Tambm funciona no Windows, mas se o objetivo for rodar no Windows, recomenda-se o uso do mingw, que pode usufruir de todos os recursos do Windows. Microsoft Visual C++: compilador comercial da Microsoft, que tambm tem um IDE. O Framework .NET, gratuito, tambm inclui o compilador (em linha de comando) do Visual C++. DEV-C++: ambiente de desenvolvimento integrado livre que utiliza os compiladores do projeto GNU para compilar programas para o sistema operacional Microsoft Windows.

Pr-requisitos Para Linux/Unix gcc: um conjunto de compiladores oficiais do projeto GNU, de cdigo aberto. Costumam vir instalados na maioria das distribuies GNU/Linux e est disponvel para diversas plataformas, principalmente para as baseadas em sistemas Unix. Compilando Digite no console: gcc MeuPrograma.c -o NomeDepoisDaCompilaao

Links externos
CodeBlocks [1]: pgina para download do CodeBlocks, uma IDE para C ao estilo do Dev-C++, porm, mais nova. Dev-C++ [2]: pgina para download do Dev-C++. DJGPP [3]: pgina oficial, com informaes e links para download. GCC [4]: pgina oficial do compilador para diversas plataformas.

Referncias
[1] [2] [3] [4] http:/ / www. codeblocks. org http:/ / www. bloodshed. net/ dev/ devcpp. html http:/ / www. delorie. com/ djgpp/ http:/ / gcc. gnu. org/

Utilizando um compilador
Compiladores: viso geral
Um compilador , geralmente, um programa de modo texto, que deve ser operado diretamente da linha de comando, sem nenhuma interface grfica. Essa uma das razes pelas quais muitas pessoas preferem usar IDEs. No entanto, saber um pouco sobre como usar o compilador pela linha de comando pode vir a ser til, por exemplo quando voc no tiver um IDE disposio. No nenhum bicho-de-sete-cabeas, e a sintaxe da maioria dos compiladores semelhante. Para executar o compilador, voc precisa abrir um terminal (ou "prompt de comando", como costuma ser chamado no Windows, ou ainda console). lgico que se voc estiver em um sistema sem ambiente grfico (como o DOS), voc no precisa fazer isso. O Windows s tem um terminal nativo, que o interpretador de comandos dele (cmd.exe ou command.com). Pacotes como o Cygwin e o MSys (do mesmo projeto que o MinGW) incluem terminais alternativos que funcionam basicamente maneira do Linux. No Linux, alm dos terminais de modo texto, h vrios emuladores de terminal, entre os quais esto o XTerm, o Konsole (KDE) e o Terminal do Gnome. O uso de todos eles idntico.

gcc
Com o gcc , compilador da GNU utilizado principalmente no sistema operacional linux ou de tipo unix , voc pode executar a compilao e a montagem separadamente ou com um nico comando. Se voc tem vrios arquivos-fonte, mais recomendvel executar as duas etapas separadamente: se voc atualizar apenas um arquivo, s precisar recompilar o que atualizou e depois remontar. No entanto, se voc est desenvolvendo um projeto grande, recomendvel usar ferramentas de automao do processo de compilao, como o make.

Utilizando um compilador Para compilar o arquivo "programa.c", gerando o cdigo-objeto "programa.o": gcc [OPES] -c programa.c Para gerar o executvel "programa binario" bin ou "programa.exe" no Windows/DOS a partir do cdigo-objeto: gcc [OPES] -o programa[.bin] programa.o Para gerar o executvel diretamente a partir do arquivo-fonte: gcc [OPES] -o programa[.bin] programa.c Aqui so listadas algumas das opes do gcc: -O2: ativa otimizao no nvel 2 -g: salva os smbolos de depurao (o que permite usar um depurador) -Wall: ativa todos os avisos do compilador -pedantic: ativa os avisos necessrios para que o cdigo esteja estritamente de acordo com os padres

Visual C++
Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

Em alguma verso no especificada do Visual C++, para compilar o arquivo "programa.c", gerando o cdigo-objeto "programa.obj": cl /c programa.c Para gerar o executvel "programa.exe" a partir do cdigo-objeto: link /out:programa.exe programa.obj Para gerar o executvel a partir do arquivo-fonte: cl programa.c

Noes de compilao

10

Noes de compilao
Compilao
Todo o cdigo em linguagem C que escrevermos deve ser salvo em um arquivo, em formato texto, com a extenso ".c". Esse cdigo no tem significado nenhum para a unidade de processamento; para que o processador possa executar nosso programa, este deve ser traduzido para a linguagem de mquina. Essa traduo se chama compilao e feita pelo programa denominado compilador. O compilador l todo o cdigo e cria um arquivo executvel, em linguagem de mquina, especfica para uma arquitetura de processadores e para um tipo de sistema operacional, o que significa que um programa compilado no Windows, por exemplo, no rodar nativamente no Linux se simplesmente copiarmos o executvel. Devemos, para isso, recompilar o cdigo-fonte do programa. No Windows, os arquivos executveis so aqueles com extenso ".exe". No Linux, os executveis so simplesmente arquivos com o atributo "executvel".

Etapas da compilao
O processo que chamamos corriqueiramente de compilao na verdade um conjunto de etapas: o preprocessamento, etapa em que o pr-processador (programa s vezes acoplado ao compilador) l o cdigo-fonte e faz algumas substituies para que o programa possa ser compilado. Em C, o preprocessador tem diversos usos: compilao condicional (por exemplo, usar trechos diferentes do cdigo para sistemas operacionais diferentes), macros, substituio de smbolos e incluso de arquivos externos que declaram funes e variveis. a verificao sinttica, que procura por eventuais erros nos cdigos dos programas: parnteses no fechados, falta de ponto-e-vrgula no final da instruo, etc. Todos esses problemas so alertados e causam a interrupo da compilao. a compilao propriamente dita, que transforma o cdigo preprocessado em um programa-objeto, que est em linguagem de mquina porm no pronto para ser executado. a linkedio (linking, em ingls) dos programas-objeto e bibliotecas necessrias em um nico executvel, feita pelo linkeditor (linker). Em C, pode-se distribuir um programa em vrios arquivos-fonte, o que ajuda na organizao e permite compilar apenas a parte do programa correspondente quando necessrio realizar alguma mudana. Na montagem, todas as partes constituintes do programa so deslocadas e/ou cortadas conforme necessrio para que tenhamos um programa executvel.

Um programa em C

11

Um programa em C
Um programa em C
comum que o primeiro programa escrito em uma linguagem de programao seja um programa que escreve "Hello world!" ("Ol mundo!"). Apresentamos o cdigo e, a seguir, analisaremos cada uma de suas linhas. No se preocupe se no entender ainda alguns aspectos, tudo ser abordado detalhadamente mais adiante. Note que o nmero das linhas dado apenas para facilitar a referncia; se for copiar o cdigo, lembre-se de tirar os nmeros de linha. 1. 2. 3. 4. 5. 6. 7. 8. /* o meu primeiro programa */ #include <stdio.h> int main(void) { printf("Ol, mundo!"); return 0; }

Salvando o cdigo acima em um arquivo com a extenso ".c" e seguindo as instrues de compilado do captulo de utilizao de compilador, voc dever ver como resultado um "Ol, mundo!" na tela. A seguir vamos a anlise do cdigo. A primeira linha um comentrio, que para o compilador no tem nenhum significado. Qualquer texto que esteja entre as marcaes /* e */, podendo inclusive ocupar vrias linhas, ser considerado como comentrio e ser completamente ignorado pelo compilador. muito til como documentao, explicando o que as prximas linhas de cdigo fazem. A linha 2 pede que seja inserido o contedo do arquivo stdio.h (que est num lugar j conhecido pelo compilador). Esse arquivo contm referncias a diversas funes de entrada e sada de dados (stdio abreviao de Standard Input/Output, ou Entrada e Sada Padronizadas), de modo que voc precisar dele em praticamente todos os programas ele o meio de quase toda comunicao com o teclado, com a tela e com arquivos.[1] Os programas em C so organizados em funes todo cdigo em C deve fazer parte de uma funo. Em particular, todo programa deve ter uma funo chamada main, pela qual ser iniciada a execuo do programa. A funo definida, no nosso exemplo, na linha 4, e delimitada pelas chaves { }. A palavra-chave int significa que a funo devolve um valor inteiro (voc pode pensar nesse valor exatamente como o valor de uma funo em matemtica). Na linha 6, executamos a funo printf, que imprime na tela os parmetros que lhe foram passados no nosso exemplo, passamos a seqncia de caracteres "Ol, mundo!" como parmetro. Essa uma das funes definidas no arquivo stdio.h. Note o ponto-e-vrgula no final da linha: todas as instrues em C devem terminar com um ponto-e-vrgula. (Essa uma causa muito comum de erros de compilao.) Na linha 7, dizemos que a funo main deve devolver (ou retornar) o valor 0 e terminar sua execuo. (Esse o valor inteiro que dissemos que amos retornar na linha 4.) O padro da linguagem C diz que a funo main deve devolver um valor inteiro, e esse valor diz se o programa foi executado com sucesso ou no. O valor zero indica que o programa foi finalizado sem nenhum erro, e valores diferentes de zero podem indicar diferentes erros. Voc no precisar se preocupar com isso no incio do seu estudo em C o valor devolvido por um programa geralmente usado em scripts, quando (por exemplo) um comando s

Um programa em C pode ser executado se o anterior tiver ocorrido com sucesso.


[1] Esse comando uma diretiva do pr-processador; voc aprender mais sobre esses comandos na seo Pr-processador.

12

Conceitos bsicos
Voc j viu um programa bsico em C. Antes de comear a se dedicar ao estudo do C, bom que voc compreenda alguns termos e alguns aspectos da linguagem, o que facilitar sua compreenso dos captulos seguintes. A seguir, formalizaremos alguns aspectos da estrutura bsica da linguagem.

Estrutura bsica
Um programa em C basicamente estruturado em blocos de cdigo. Blocos nada mais so que conjuntos de instrues, e devem ser delimitados com chaves ({ ... }). Um bloco tambm pode conter outros blocos. Uma instruo geralmente corresponde a uma ao executada, e deve sempre terminar com ponto-e-vrgula (;). O compilador ignora espaos, tabulaes e quebras de linha no meio do cdigo; esses caracteres so chamados genericamente de espao em branco (whitespace). Ou seja, os trs trechos a seguir so equivalentes: printf("Ol mundo");return 0; printf ("Ol mundo"); return 0; printf( "Ol mundo" ); return 0 ; No entanto, voc achar muito mais fcil de ler um estilo de cdigo mais parecido com o segundo exemplo. Costuma-se usar (mas no abusar de) espaos e tabulaes para organizar o cdigo. A linguagem sensvel utilizao de maisculas e minsculas. Por exemplo, se voc escrevesse Printf no lugar de printf, ocorreria um erro, pois o nome da funo totalmente em minsculas.

Escopo
Geralmente, em programao, no queremos que outras funes usem as variveis que estamos manipulando no momento. O conceito de escopo est justamente relacionado a isso. Escopo o nvel em que um dado pode ser acessado; em C h dois nveis: local e global. Uma varivel global pode ser acessada por qualquer parte do programa; variveis locais podem ser acessadas apenas dentro do bloco onde foram declaradas (ou nos seus sub-blocos), mas no fora dele (ou nos blocos que o contm). Isso possibilita que voc declare vrias variveis com o mesmo nome mas em blocos diferentes. Veja um exemplo: int a; { int a; int b;

Conceitos bsicos } { int b; } As duas variveis chamadas b so diferentes e s podem ser acessadas dentro do prprio bloco. A primeira varivel a global, mas s pode ser acessada no segundo bloco, pois a varivel local a no primeiro bloco oculta a varivel global de mesmo nome. Note que isso possvel em C, e tome cuidado para no cometer erros por causa disso.

13

Introduo s funes
Funes so muito usadas, no s em C, mas em linguagens de programao em geral. Uma funo basicamente um bloco de cdigo que realiza uma certa tarefa. Quando queremos realizar aquela tarefa, simplesmente fazemos uma chamada de funo para a funo correspondente. Uma funo pode precisar que o programador d certos dados para realizar a tarefa; esses dados so chamados argumentos. A funo tambm pode retornar um valor, que pode indicar se a tarefa foi realizada com sucesso, por exemplo; esse valor o valor de retorno. Podemos fazer uma analogia com as funes matemticas: as variveis independentes so os argumentos e o valor numrico da funo o valor de retorno. Em C, para chamar uma funo, devemos escrever o seu nome, seguido da lista de argumentos (separados por vrgula) entre parnteses, mesmo que no haja nenhum argumento. Lembre que a chamada de funo tambm uma instruo, portanto devemos escrever o ponto-e-vrgula no final. Alguns exemplos de chamadas de funes: funcao(arg1, arg2, arg3); funcao();

Se quisermos saber o valor de retorno de uma funo, podemos armazen-lo numa varivel. Variveis sero introduzidas logo adiante, mas a sintaxe muito fcil de aprender: valor_de_retorno = funcao(arg1, arg2); Em C, todo o cdigo (exceto as declaraes de variveis e funes) deve estar dentro de funes. Todo programa deve ter pelo menos uma funo, a funo main, que por onde comea a execuo do programa.

Expresses
Um conceito muito importante em programao o de expresso. Expresses so conjuntos de valores, variveis, operadores e chamadas de funes que so avaliados ou interpretados para resultar num certo valor, que chamado o valor da expresso. Por exemplo: 3 * 4 + 9 uma expresso de valor 21; a + 3 * b uma expresso equivalente expresso matemtica a + 3b; foo() uma expresso cujo valor o valor de retorno da funo foo.

Conceitos bsicos

14

Comentrios
Muitas vezes bastante til colocar comentrios no cdigo, por exemplo para esclarecer o que uma funo faz, ou qual a utilidade de um argumento, etc. A maioria das linguagens de programao permite comentrios; em C, eles podem aparecer de duas maneiras: /* Comentrios que podem ocupar vrias linhas. */ e // Comentrios de uma linha s, que englobam // tudo desde as duas barras at o final da linha. Tudo que estiver entre as marcas /* e */ ou entre // ser ignorado pelo compilador. Note que os comentrios de uma linha s (iniciados por //) foram incorporados ao padro da linguagem apenas em 1999, e portanto alguns compiladores podem no os suportar. As verses mais recentes do GCC no tero problema em suportar esse tipo de comentrio.

Variveis
Variveis
Em um programa, existe a necessidade de se guardar valores na memria, e isso feito atravs de variveis, que podem ser definidas simplificadamente como nomes que se referem a lugares na memria onde so guardados valores. Em C, para utilizar uma varivel, ela deve ser primeiramente declarada, ou seja, devemos requisitar o espao necessrio para essa varivel. Aps reservar um espao na memria, o computador ir associar a ele o nome da varivel. Se voc no declarar uma varivel e tentar utiliz-la, o compilador ir avis-lo disso e no continuar a compilao.

Declarando variveis
Genericamente, para declarar uma varivel, usamos a seguinte instruo: tipo_da_varivel nome_da_varivel; Por exemplo, para declarar uma varivel do tipo int com o nome a, podemos escrever int a; sempre necessrio indicar o tipo da varivel, pois cada tipo tem um tamanho diferente, ou seja, ocupa mais ou menos espao na memria do computador. Mais adiante introduziremos os tipos de varivel.

Variveis

15

Atribuindo valores
Se quisermos associar um valor a uma varivel, usamos o operador = (igual): a = 5; Nesse caso, estamos pedindo que o computador guarde o valor 5 no espao alocado varivel a. possvel tambm atribuir um valor a uma varivel ao mesmo tempo que ela declarada, o que chamado de inicializar a varivel. Por exemplo: int a = 5; possvel tambm declarar mais de uma varivel de um mesmo tipo em uma nica instruo, separando os nomes por vrgulas. Tambm possvel inicializar as variveis dessa maneira: int a, b, c, d; int e = 5, f = 6; int g, h = 2, i = 7, j;

Exemplo de erro
a = 25 Mesmo sabendo que um exemplo de erro, escreva o cdigo acima em um arquivo .c e tente compilar para se familiarizar com as mensagens de erro do compilador, assim voc saber o que fazer quando elas ocorrerem. No exemplo acima no foi declarada a varivel a, ao tentar compilar o compilador informa que o smbolo a no foi definido.

Nomes de variveis
Os nomes de variveis devem ser nicos: no podemos ter duas variveis com o mesmo nome! O C, assim como muitas outras linguagens de programao, sensvel utilizao de maisculas e minsculas. Portanto, o cdigo a seguir seria vlido e geraria trs variveis diferentes: int nome; int NOME; int Nome; Em nomes de variveis, podemos usar letras maisculas ou minsculas (de A a Z, sem acentos), nmeros e o caractere sublinhado (_), mas o primeiro caractere deve ser uma letra ou o sublinhado. Algumas palavras no podem ser usadas para nomes de variveis por serem palavras reservadas (palavras que tm significado especial na linguagem). O padro C atual especifica que nomes de at 31 caracteres devem ser aceitos. Alguns compiladores podem at aceitar nomes maiores que isso, mas no considere isso uma regra e no use nomes to longos.

Variveis

16

Tipos de variveis
At agora voc s viu as variveis do tipo int, que servem para guardar nmeros inteiros. A linguagem C tem 4 tipos fundamentais. So eles: int char float double

O primeiro, int, serve apenas para guardar nmeros inteiros. Por exemplo, 4, 2773 ou -15.J o segundo, char, serve para guardar um caractere. Alm disso, h uma diferena entre o intervalo de dados que cada um pode guardar. O mais comum que char guarde valores de -128 a 127 e int, em processadores de 32 bits, guarde valores de -2.147.483.648 a 2.147.483.647 (-231 a 231 1). Os outros dois, float e double, servem para guardar nmeros de ponto flutuante, ou seja, nmeros reais, como 3,1415 (pi), -2,3333, 0,00015, 6,02 1023. A diferena entre os dois , alm do intervalo de dados, a preciso. Geralmente, o tipo float guarda dados (com sinal positivo ou negativo) de 3,4E-38 a 3,4E+38 (alm do zero). J double costuma suportar nmeros to pequenos quanto 1,7E-308 e no mximo 1,7E+308. Na seo Anexos est a tabela completa com todos os tipos de variveis.

Modificadores de tipo
Podemos alterar a maneira como os dados so guardados com os modificadores de tipo. Voc pode modificar os tipos de duas maneiras. Tamanho: short e long Voc pode modificar o tamanho de uma varivel usando os modificadores de tipo, que so dois: short e long. Note que float e char no podem ser modificados em tamanho. short diminui o espao necessrio para guardar a varivel (diminuindo tambm a gama de valores que esta pode assumir). S pode ser usado com int. long aumenta o espao tomado pela varivel, e portanto aumenta seu valor mximo e/ou sua preciso. Pode ser usado com int e double. O padro C de 1999 adicionou um terceiro modificador, suportado pelos compiladores mais recentes, inclusive o gcc: long long, que aumentaria ainda mais a capacidade da varivel. Alguns deles suportam esse modificador apenas para o tipo int, e outros suportam tambm para double. Uma observao necessria: segundo o padro, no existe nenhuma garantia de que uma varivel short int menor que uma varivel int, nem que long int maior que int. Apenas garantido que int no maior que long nem menor que short. De fato, nos sistemas x86 de 32 bits (ou seja, a maioria dos computadores pessoais atualmente), o tamanho de int igual ao de long. Geralmente, int ser o tamanho nativo do processador ou seja, 32 bits num processador de 32 bits, 16 bits num processador de 16 bits etc.

Variveis

17

Converter um tipo de varivel


A converso de uma varivel consiste em converter o tipo de uma varivel em um outro. Imagine que voc esteja trabalhando com uma varivel do tipo float e por alguma razo queira eliminar os nmeros que esto depois da virgula. Esta operao pode ser realizada de duas maneiras. Converses do tipo implcita: Consiste em uma modificao do tipo de varivel que feita automaticamente pelo compilador. Ex: int x; x = 7.123; Converses do tipo explcita: Tambm chamada de operao cast, consiste em forar a modificaao do tipo de varivel usando o operador cast "( )". Ex: int y; y = (int)7.123; Veja um exemplo da converso de tipo inteiro em caracteres. Aqui convertemos um numero decimal em um caracte ASCII.

#include <stdio.h> int main(void) { int y = 65; char x; x = (char) y; printf("O numero inteiro: %d \n O caracter: %c \n\n", x, y); } Sinal: signed e unsigned Existe outro tipo de modificador, que define se o nmero vai ser guardado com sinal ou no. So os modificadores signed e unsigned, suportados pelos tipos inteiros apenas. signed diz que o nmero deve ser guardado com sinal, ou seja, sero permitidos valores positivos e negativos. Esse o padro, portanto esse modificador no muito usado. unsigned diz que o nmero deve ser guardado sem sinal. Com isso, o valor mximo da varivel aumenta, j que no teremos mais valores negativos. Por exemplo, com uma varivel char podemos guardar valores de -128 a 127, mas com uma varivel unsigned char pode guardar valores de 0 a 255. Para usar esses modificadores, devemos coloc-los antes do nome do tipo da varivel, sendo que o modificador de sinal deve vir antes do modificador de tamanho caso ambos sejam usados. Por exemplo: unsigned char c; short int valor; unsigned long int resultado; long double preco;

Variveis Nota: Voc pode abreviar short int e long int para simplesmente short e long, respectivamente.

18

Literais
Em programao, um literal uma notao que representa um valor constante. Exemplos de literais em C so 415, 19.52, 'C', "Joo". Esses exemplos representam os quatro tipos de literais em C: literais de inteiros, literais de reais, literais de caracteres e literais de strings. S com esses exemplos j possvel deduzir como se usam os literais; mas importante fazer algumas observaes: Literais de inteiros podem ser especificados nas bases decimal, octal ou hexadecimal. Se o literal for prefixado com "0x" ou "0X", ele ser interpretado como hexadecimal; se o prefixo for apenas "0", ser interpretado como octal; ou se no houver prefixo, ser interpretado como decimal. Literais de reais podem ser especificados na forma decimal (144.57) ou em notao cientfica (1.4457e+2). Lembre-se que o separador decimal o ponto e no a vrgula, como seria usual. Literais de caracteres devem vir entre aspas simples (') e conter a representao de apenas um caractere1. Usos vlidos seriam: 'c', '\n', '\x1b', '\033'. Se voc quiser usar a aspa simples como caractere, preceda-a com uma barra invertida: '\''. Literais de strings devem vir entre aspas duplas ("). Para usar aspas duplas dentro de strings, preceda-as com barra invertida: "Ele disse \"Ol\".". Note que um literal de string adiciona o caractere nulo (\0) ao final da string, pois ele , em C, a maneira de delimitar o final de uma string.

Notas de rodap
1. Na verdade, segundo o padro C, literais de caracteres podem conter a representao de mais um caractere, mas o uso deles seria para representar nmeros e no seqncias de caracteres; um aspecto pouco utilizado da linguagem C.

Entrada e sada simples

19

Entrada e sada simples


Entrada e sada simples
Se voc pensar bem, perceber que um computador praticamente intil se no tiver nenhuma maneira de interagir com o usurio. Por exemplo, se voc abrir um processador de texto, nada ir acontecer at que voc abra um arquivo ou digite algum texto no teclado. Mas, da mesma maneira, necessrio que o computador fornea informao tambm: como voc poderia saber se uma tarefa foi concluda? As trocas de informao entre o computador e o usurio so chamadas entrada e sada (input e output, em ingls). Entrada a informao fornecida a um programa; sada a informao fornecida pelo programa. comum referir-se aos dois termos simultaneamente: entrada/sada ou E/S (I/O, em ingls). Freqentemente so usados os termos "sada padro" (standard output, stdout) e "entrada padro" (standard input, stdin). Eles se referem, na maioria das vezes, ao monitor/ecr e ao teclado, que so os meios bsicos de interao com o usurio. No entanto, os sistemas operacionais permitem redirecionar a sada e a entrada de programas para outros dispositivos ou arquivos. As funes de entrada e sada na linguagem C trabalham com fluxos (streams, em ingls), que so uma forma de abstrao de dados de maneira seqencial. Assim, toda entrada e sada feita da mesma maneira, com as mesmas funes, no importando o dispositivo com o qual estamos nos comunicando (teclado, terminal, arquivo, etc.). As mesmas funes que descrevem o acesso aos arquivos podem ser utilizadas para se acessar um terminal de vdeo. Em C, as funes da biblioteca padro para entrada e sada esto declaradas no cabealho stdio.h. Uma delas j foi introduzida em sees anteriores: printf(). A seguir daremos mais detalhes sobre essa funo e introduziremos outras.

puts() e putchar()
puts significa "put string" (colocar string), utilizado para "colocar" uma string na sada de dados. putchar significa "put char" (colocar caractere), utilizado para "colocar" um caractere na sada de dados. So as funes mais simples do cabealho stdio.h. Ambas enviam (ou "imprimem") sada padro os caracteres fornecidos a elas; putchar() manda apenas um caractere, e puts() manda uma seqncia de caracteres (ou string). Exemplo: puts ("Esta uma demonstrao da funo puts."); putchar ('Z'); Note que junto com a funo puts devemos usar literais de string (com aspas duplas), e com putchar devemos usar literais de caractere (com aspas simples). Se voc tentasse compilar algo como putchar ("T"), o compilador daria uma mensagem de erro. Lembre-se que "T" diferente de 'T'. Podemos tambm colocar caracteres especiais, como a tabulao (\t) e a quebra de linha (\n): puts ("Primeira linha\nSegunda linha\te um grande espao"); putchar ('\n'); // apenas envia uma quebra de linha Este cdigo resultaria em algo parecido com: Primeira linha Segunda linha

e um grande espao

Os outros caracteres especiais so introduzidos adiante.

Entrada e sada simples Note que o argumento deve ser uma seqncia de caracteres. Se voc tentar, por exemplo, imprimir o nmero 42 desta maneira: puts(42); Na verdade o que o compilador tentar fazer imprimir a seqncia de caracteres que comea na posio 42 da memria (provavelmente ele ir alert-lo sobre isso se voc tentar compilar esse cdigo). Se voc tentar executar esse cdigo, provavelmente ocorrer uma falha de segmentao (erro que ocorre quando um programa tenta acessar memria que no lhe pertence). A maneira correta de imprimir o nmero 42 seria coloc-lo entre aspas duplas: puts("42");

20

printf()
printf vem de "print formatted" (imprimir formatado). primeira vista, a funo printf() pode parecer idntica puts(). No entanto, ela muito mais poderosa. Ela permite facilmente imprimir valores que no so seqncias de caracteres, alm de poder formatar os dados e juntar vrias seqncias de caracteres. Por isso, a funo printf() muito mais usada que a puts(). Ela pode ser usada exatamente como a funo puts(), se fornecermos a ela apenas uma seqncia de caracteres: printf("Este um programa em C"); Mas e se precisarmos imprimir o contedo de uma varivel? A funo printf tambm pode fazer isso! Voc deve, obviamente, especificar onde o valor da varivel deve ser impresso. Isso feito atravs da especificao de formato %d, caso a varivel seja do tipo int (seqncias para outros tipos sero dadas adiante). Voc tambm precisar, logicamente, especificar qual varivel imprimir. Isso feito dando-se mais um argumento funo printf(). O cdigo dever ficar assim: int teste; teste = 42; printf ("A varivel 'teste' contm o nmero %d.", teste); Tendo colocado isso no seu programa, voc dever ver na tela: A varivel 'teste' contm o nmero 42. Vamos supor que voc queira imprimir um nmero no-inteiro. Voc teria que trocar "%d" por %f. Exemplo: float pi; pi = 3.1415; printf ("O valor de pi %f.", pi); O cdigo acima ir retornar: O valor de pi 3.1415. Voc pode imprimir quantos valores quiser, bastando para isso colocar mais argumentos e mais especificaes de formato, lembrando de colocar na ordem certa. Alguns compiladores, como o gcc, mostram um aviso caso o nmero de argumentos seja diferente do nmero de especificaes de formato, o que provavelmente causaria resultados indesejados. A sintaxe geral da funo printf() : printf ("string de formatao", arg1, arg2, ...); Suponha que voc tem um programa que soma dois valores. Para mostrar o resultado da conta, voc poderia fazer isso:

Entrada e sada simples int a, b, c; ... // leitura dos dados c = a + b; // c o resultado da soma printf ("%d + %d = %d", a, b, c); O que resultaria em, para a = 5 e b = 9: 5 + 9 = 14 A seguir mostramos os especificadores de formato para vrios tipos de dados.

21

Especificaes de formato
A documentao mais tcnica os chama de "especificadores de converso", pois o que ocorre na maioria das vezes , de fato, a converso de um valor numrico em uma seqncia de caracteres que representa aquele valor. Mas o nome "formato" no deixa de estar correto, pois eles especificam em que formato (inteiro, real etc.) est o argumento correspondente.
Cdigo %d %u %o %x Converso/Formato do argumento Nmero decimal inteiro (int). Tambm pode ser usado %i como equivalente a %d. Nmero decimal natural (unsigned int), ou seja, sem sinal. Nmero inteiro representado na base octal. Exemplo: 41367 (corresponde ao decimal 17143). Nmero inteiro representado na base hexadecimal. Exemplo: 42f7 (corresponde ao decimal 17143). Se usarmos %X, as letras sero maisculas: 42F7. Nmero decimal de ponto flutuante. No caso da funo printf, devido s converses implcitas da linguagem C, serve tanto para float como para double. No caso da funo scanf, %f serve para float e %lf serve para double. Nmero em notao cientfica, por exemplo 5.97e-12. Podemos usar %E para exibir o E maisculo (5.97E-12). Escolhe automaticamente o mais apropriado entre %f e %e. Novamente, podemos usar %G para escolher entre %f e %E. Ponteiro: exibe o endereo de memria do ponteiro em notao hexadecimal. Caractere: imprime o caractere que tem o cdigo ASCII correspondente ao valor dado. Seqncia de caracteres (string, em ingls).

%f

%e %g %p %c %s

Observao Se voc quiser imprimir um sinal de porcentagem, use %%. Exemplo: printf("O lucro para o ltimo ms foi de 20%%."); Numa seqncia de controle, possvel tambm indicar a largura do campo, o nmero de casas decimais, o tamanho da varivel e algumas opes adicionais. O formato geral : %[opes][largura do campo][.preciso][tamanho da varivel]tipo de dado A nica parte obrigatria o tipo de dado. Todas as outras podem ser omitidas.

Entrada e sada simples Opes As opes so parmetros opcionais que alteram a formatao. Voc pode especificar zero ou mais delas, colocando-as logo aps o sinal de porcentagem: 0: o tamanho do campo deve ser preenchido com zeros esquerda quando necessrio, se o parmetro correspondente for numrico. - (hfen): o valor resultante deve ser alinhado esquerda dentro do campo (o padro alinhar direita). (espao): no caso de formatos que admitem sinal negativo e positivo, deixa um espao em branco esquerda de nmeros positivos. +: o sinal do nmero ser sempre mostrado, mesmo que seja positivo. ' (aspa simples/apstrofo): nmeros decimais devem ser exibidos com separador de milhares caso as configuraes regionais o especifiquem. Essa opo normalmente s funciona nos sistemas Unix. Largura do campo Como o prprio nome j diz, especifica qual a largura mnima do campo. Se o valor no ocupar toda a largura do campo, este ser preenchido com espaos ou zeros. Por exemplo, podemos imprimir um cdigo de at 5 dgitos preenchido com zeros, de maneira que os valores 1, 27, 409 e 55192 apaream como 00001, 00027, 00409 e 55192. A largura deve ser especificada logo aps as opes, se presentes, e pode ser um nmero que especifica a largura ou um asterisco, que diz que a largura ser especificada pelo prximo argumento (ou seja, o argumento anterior ao valor a ser impresso). Neste exemplo, o campo ter largura igual ao valor de num e o valor impresso ser 300: printf ("%*d", num, 300); O campo impresso de acordo com as seguintes regras: Se o valor for mais largo que o campo, este ser expandido para poder conter o valor. O valor nunca ser cortado. Se o valor for menor que o campo, a largura do campo ser preenchida com espaos ou zeros. Os zeros so especificados pela opo 0, que precede a largura. O alinhamento padro direita. Para se alinhar um nmero esquerda usa-se a opo - (hfen ou sinal de menos) antes da largura do campo. Por exemplo, compare as trs maneiras de exibir o nmero 15: printf ("%5d", 15); printf ("%05d", 15); printf ("%-5d", 15); E alguns outros exemplos: printf ("%-10s", "Jos"); printf ("%10s", "Jos"); printf ("%4s", "Jos"); Preciso A preciso pode ter quatro significados diferentes: Se a converso solicitada for inteira (d, i, o, u, x, X): o nmero mnimo de dgitos a exibir (ser preenchido com zeros se necessrio). Se a converso for real (a, A, e, E, f, F): o nmero de casas decimais a exibir. O valor ser arredondado se a preciso especificada no formato for menor que a do argumento. Se a converso for em notao cientfica (g, G): o nmero de algarismos significativos. O valor ser arredondado se o nmero de algarismos significativos pedido for maior que o do argumento. Se a converso for de uma seqncia de caracteres (s): o nmero mximo de caracteres a exibir. // exibe "Jos " // exibe " Jos" // exibe "Jos" // exibe " 15" // exibe "00015" // exibe "15 "

22

Entrada e sada simples Assim como a largura do campo, a preciso pode ser especificada diretamente por um nmero ou com um asterisco, mas deve ser precedida por um ponto. Alguns exemplos: printf printf printf printf ("%.5d", ("%.5f", ("%.5g", ("%.5s", 314); // exibe "00314" 2.4); // exibe "2.40000" 23456789012345); // exibe "2.3457e+13" "Bom dia"); // exibe "Bom d"

23

claro que podemos combinar a largura com a preciso. Por exemplo, %10.4f indica um campo de nmero real de comprimento total dez e com 4 casas decimais. Note que, na largura do campo, o valor inteiro levado em conta, inclusive o ponto decimal, e no apenas a parte inteira. Por exemplo, essa formatao aplicada ao nmero 3.45 ir resultar nisto: " 3.4500"

Tamanho da varivel importante ressaltar que quando so usados modificadores de tamanho de tipos, a maneira como os dados so armazenados pode tornar-se diferente. Assim, devemos informar funo printf() precisamente qual o tipo da varivel cujo valor desejamos exibir. A funo printf() admite cinco principais modificadores de tamanho de varivel: hh: indica que a converso inteira corresponde a uma varivel char. Por exemplo, poderamos usar o formato %hhd para exibir uma varivel do tipo char na base decimal. h: indica que a converso inteira corresponde a uma varivel short. l: indica que a converso inteira corresponde a uma varivel long. ll: indica que a converso inteira corresponde a uma varivel long long. L: indica que a converso de nmero real corresponde a uma varivel long double. Quando o tipo da varivel no tem modificadores de tamanho (long ou short), no se usa nenhum modificador de tamanho da varivel na funo printf().

Seqncias de escape
Seqncias de escape so combinaes de caracteres que tm significado especial, e so sempre iniciadas por uma barra invertida (\). Voc pode us-las em qualquer literal de caractere ou string. Por exemplo, a string "linha 1\nlinha 2" equivale a: linha 1 linha 2 pois a seqncia \n indica uma quebra de linha. A seguir apresentamos a tabela com as seqncias de escape suportadas pela linguagem C:

Entrada e sada simples

24

Seqncia \n \t \b \r \a \f \v \" \' \\ \0 \N \xN Quebra de linha (line feed ou LF) Tabulao horizontal

Significado

Retrocede o cursor em um caractere (backspace) Retorno de carro (carriage return ou CR): volta o cursor para o comeo da linha sem mudar de linha Emite um sinal sonoro Alimentao de formulrio (form feed ou FF) Tabulao vertical (em impressoras) Aspa dupla Aspa simples Barra invertida Caractere nulo (caractere de valor zero, usado como terminador de strings) O caractere cuja representao octal N (dgitos de 0 a 7) O caractere cuja representao hexadecimal N (dgitos de 0 a 9 e de A a F)

Algumas particularidades
Vamos dar uma olhada na seqncia " \n " aqui permitido mudar o caracter "n" pelo seu valor em octal "\12" ou hexadecimal " \x0A " . No pegadinha voc pode usar esses dois exemplos no seu cdigo. Vou mostrar alguns exemplos. Hexadecimal \x00 \x0A \x0D \x07 \x08 \x0B Octal \00 \12 \15 \07 \10 \13 Caracter \0 \n \r \a \b \v

scanf()
A funo scanf() l dados da entrada padro (teclado) e os guarda em variveis do programa. Assim como para printf(), usamos uma string de formatao para especificar como sero lidos os dados. A sintaxe de scanf() esta: scanf ("string de formatao", &arg1, &arg2, ...); Como voc pode ver, a sintaxe quase igual de printf(), com exceo do E comercial (&). Voc entender melhor o seu uso nas sees seguintes, mas adiantamos que ele um operador que retorna o endereo de uma varivel. Isso necessrio pois a funo scanf() deve modificar as variveis, e quando no usamos o operador de endereo, passamos apenas o valor de uma varivel para a funo. Isso ser explicado melhor no captulo sobre ponteiros. O fato de scanf receber endereos de variveis (em vez de seus valores) tambm explica por que ele precisa ser informado da diferena entre %f (float) e %lf (double) enquanto que o printf no precisa. Um exemplo bsico da utilizao de scanf() este: int a; scanf ("%d", &a);

Entrada e sada simples O que este exemplo faz declarar uma varivel e aguardar o usurio digitar algo. Os dados s sero processados quando o usurio apertar Enter. Depois disso, os caracteres digitados pelo usurio sero convertidos para um valor inteiro e esse inteiro ser guardado no endereo que corresponde varivel a. Se o valor digitado no puder ser convertido (porque o usurio no digitou nenhum algarismo vlido), a varivel no ser modificada. Assim como na funo printf(), podemos receber quantos valores quisermos, bastando usar vrios especificadores de converso: int a; char b; float c; scanf ("%d %c %f", &a,&b,&c); Dessa maneira, se o usurio digitar 120 z 17.63, teremos a igual a 120, b igual ao caractere 'z' e c igual ao nmero 17,63. Se o usurio tentar digitar mais de um espao entre os dados ou simplesmente nenhum espao, ainda assim o programa obter os dados certos. Por exemplo, 120z17.63 tambm dar o mesmo resultado. Agora uma questo um pouco mais difcil: vamos supor que especificamos um formato inteiro e o usurio digitou um nmero real, como por exemplo 12.5. O que dever acontecer? #include <stdio.h> int main () { int a; printf ("Digite um nmero: "); scanf ("%d", &a); printf ("\nO nmero digitado foi %d", a); return (0); } Se voc testar com o valor 12.5, vai ver que o programa retornar o nmero 12, pois a funo scanf() apenas interpreta os caracteres vlidos para aquele formato. Os especificadores de converso so praticamente os mesmos que os da funo printf(), com algumas mudanas. A maioria deles pula espaos em branco, exceto dois. %i no mais sinnimo de %d. O que %i faz interpretar o valor digitado como hexadecimal, se iniciar-se por 0x ou 0X; como octal, se iniciar-se por 0; ou como decimal, caso nenhuma dessas condies seja verificada. %a, %e/%E e %g so sinnimos de %f. %lf deve ser usado para variveis do tipo double. %s l uma seqncia de caracteres no-brancos (qualquer caractere exceto espao, tabulao, quebra de linha etc.), ou seja, uma palavra. %c l uma seqncia de caracteres, sem ignorar espaos. O padro ler um caractere, se no for especificada a largura do campo. %[...] l uma seqncia de caracteres, sem ignorar espaos, especificando entre colchetes quais caracteres devem ser aceitos, ou, se o primeiro caractere dentro dos colchetes for um acento circunflexo (^), quais no devem ser aceitos. Alm disso, se colocarmos um trao entre dois caracteres, todos os caracteres entre os dois sero includos no padro. Por exemplo, se quisermos incluir qualquer letra minscula, poderimos escrever %[a-z]; se quisssemos tambm incluir as maisculas, colocaramos %[a-zA-Z]. A leitura pra quando for

25

Entrada e sada simples encontrado um caractere que no coincide com o padro especificado. J os modificadores funcionam de maneira bastante diferente: O modificador * (asterisco) especifica que o valor atual deve ser lido da maneira especificada, mas no ser guardado em nenhuma varivel, e portanto no deve haver um ponteiro correspondente a esse valor. Por exemplo, poderimos ter um programa que espera ler uma palavra e depois um nmero, mas no importa qual palavra . Nesse caso usaramos o modificador *: scanf ("%*s %d", &numero). O programa leria a palavra e guardaria o nmero na varivel numero. Como na funo printf(), existe o especificador de largura do campo, que deve aparecer antes do especificador de converso, mas em scanf() ele especifica a largura mxima. Se a largura mxima foi definida como n, scanf() pular para o prximo campo se j tiver lido n caracteres. Por exemplo, scanf ("%4d", &num) ler um nmero de at quatro algarismos. Se o usurio digitar mais, o excedente ser no ser lido por essa chamada, mas poder ser lido por uma prxima chamada a scanf. Mais detalhes sobre os especificadores de converso e os modificadores podem ser encontrados na documentao da biblioteca padro.

26

Valor de retorno
A funco scanf() retorna o nmero de converses realizadas com sucesso. Isso til pois, se o valor contido numa varivel aps a chamada de scanf() for igual ao valor anterior, no possvel saber se o valor digitado foi o mesmo que j havia ou se no foi feita a converso. Para obter esse nmero de converses realizadas, voc deve guardar o resultado numa varivel do tipo int. Veja como proceder: int a, b; int num; num = scanf("%d%d", &a, &b); Este exemplo l dois nmeros inteiros e os guarda nas variveis a e b. O nmero de converses realizadas guardado na varivel num. Se aps o scanf, num for diferente de 2, sinal de que o usurio digitou algo incompatvel com o formato desejado. Note que aqui introduzimos um conceito novo: o valor de retorno de uma funo. Ele pode ser obtido simplesmente associando o valor de uma varivel chamada da funo. Ele ser detalhado na seo Funes, mas j possvel compreender um pouco sua utilizao.

gets() e getchar()
gets() e getchar(), assim como scanf(), lem da entrada padro. Assim como puts() e putchar(), no suportam formatao. Como o nome sugere, getchar() l apenas um caractere, e gets() l uma string at o final da linha ou at que no haja mais dados para ler, e adiciona o terminador de string "\0". A sintaxe das funes : gets(ponteiro_para_string); char c; c = getchar(); No entanto, existe um problema com a funo gets(). Veja o exemplo a seguir: #include <stdio.h> int main() {

Entrada e sada simples char buffer[10]; printf("Entre com o seu nome: "); gets(buffer); printf("O nome : %s", buffer); return 0; } A notao char buffer[10], que ainda no foi introduzida (e ser detalhada na seo Vetores (arrays)), pede que seja reservado um espao para 10 caracteres para a string buffer. Portanto, se usurio digitar mais de 9 caracteres (pois o terminador de string adicionado ao que o usurio digitou), os caracteres excedentes adicionais sero colocados na rea de memria subsequente ocupada pela varivel, escrevendo uma regio de memria que no est reservada string. Este efeito conhecido como "estouro de buffer" e pode causar problemas imprevisveis. Por isso, no se deve usar a funo gets(); mais tarde introduziremos a funo fgets(), que no apresenta esse problema e que deve ser usada no lugar de gets().

27

sprintf() e sscanf()
sprintf e sscanf so semelhantes a printf e scanf. Porm, ao invs de escreverem na sada padro ou lerem da entrada padro, escrevem ou lem em uma string. A nica mudana nos argumentos a necessidade de especificar a string que deve ser lida ou atribuda no incio. Veja os exemplos para entender melhor. #include <stdio.h> int main() { int i; char string1[30]; printf("Entre um valor inteiro: "); scanf("%d", &i); sprintf(string1, "Valor de i = %d", i); puts(string1); return 0; } Nesse exemplo, a mensagem que queramos exibir na tela foi primeiramente salva em uma string, e depois essa string foi enviada para a tela. Se voc olhar bem, se voc tivesse alocado um valor menor para string1, tambm ocorreria um estouro de buffer. Para evitar esse problema, existe a funo snprintf, que tem mais um argumento: o tamanho da string (deve ser colocado depois da string onde a mensagem ser gravada). #include <stdio.h> int main() { int i, j, k; char string1[] = "10 20 30"; scanf(string1, "%d %d %d", &i, &j, &k); printf("Valores lidos: %d, %d, %d", i, j, k); return 0; }

Entrada e sada simples Nesse exemplo, usamos a funo sscanf para interpretar os valores contidos na string e guard-los nas variveis numricas.

28

Operaes matemticas (Bsico)


Operaes matemticas
Em C, fazer operaes matemticas simples bastante fcil e intuitivo. Por exemplo, se quisermos que uma varivel contenha o resultado da conta 123 + 912, fazemos assim: var = 123 + 912; Os operadores aritmticos bsicos so 5: + (adio), - (subtrao), * (multiplicao), / (diviso) e % (resto de diviso inteira). Outro exemplo: int a = 15; int b = 72; int c = a * b;

/* c valer 1572 */

Podemos usar mais de um operador na mesma expresso. A precedncia igual usada na matemtica comum: a = 2 + 4 * 10; a = 2 + 40 / 2 + 5; /* retornar 42, o mesmo que (2 + (4 * 10)) */ /* retornar 27, o mesmo que (2 + (40 / 2) + 5) */

Voc pode usar parnteses, como em expresses matemticas normais: a = (2 + 4) * 10; a = (2 + 40) / (2 + 5); /* retornar 60 */ /* retornar 6 */

Note que uma operao entre nmeros inteiros sempre retornar um nmero inteiro. Isso evidente para a adio, subtrao e multiplicao. Mas em uma diviso de inteiros, por exemplo 3/2, a expresso retornar apenas a parte inteira do resultado, ou seja, 1. Se quisermos um resultado no-inteiro, um dos operandos deve ser no-inteiro. Nesse exemplo, poderamos usar 3.0/2 ou 3/2.0, ou mesmo 3./2 ou (1.0 * 3)/2, pois, em C, uma operao envolvendo um nmero no-inteiro sempre ter como resultado um nmero real. Note que em C o separador decimal o ponto e no a vrgula. O seguinte exemplo poderia surpreender, pois o programa dir que o valor de f continua sendo 3. #include <stdio.h> int main() { int i = 5; int j = 2; float f = 3.0; f = f + j / i; printf("O valor de f %f", f); return 0; }

Operaes matemticas (Bsico) Mas, segundo a precedncia dos operadores, j / i deveria ser calculado primeiro, e como ambos os valores so do tipo inteiro, o valor dessa expresso zero. importante que voc grave um arquivo .c com o cdigo acima e execute usando o compilador para ver o funcionamento com os prprios olhos.

29

Abreviaes
Alguns tipos de atribuies so bastante comuns, e por isso foram criadas abreviaes. Por exemplo, muito comum incrementar em uma unidade o valor de uma varivel (em loops, por exemplo). Em vez de escrever var = var + 1, podemos escrever simplesmente var++. Da mesma maneira, existe o operador de decremento, que decrementa em uma unidade o valor da varivel: var-- (equivalente a var = var - 1). Os operadores de decremento e incremento tambm podem ser utilizados antes do nome da varivel. Isso significa que estas duas instrues so equivalentes: var++; ++var; Agora vamos supor que voc use em seu programa uma varivel que aumenta de 10 em 10 unidades. claro que usar var++ dez vezes no abreviaria nada. Em vez disso, existe a abreviao var += 10. Genericamente, para qualquer dos cinco operadores aritmticos op, vale a abreviao: var = var op num; var op= num; Ou seja, os seguintes pares so equivalentes: x x x x *= /= -= %= 12; 10; 2; 11; x x x x = = = = x x x x * / % 12; 10; 2; 11;

Este exemplo clarifica o uso dos operadores de incremento: #include <stdio.h> int main() { int a, b; a = b = 5; printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", return 0; }

++a + 5); a); b++ + 5); b);

O resultado que voc deve obter ao executar o exemplo : 11 6 10 6

Operaes matemticas (Bsico) Esse resultado mostra que ++var e var++ no so a mesma coisa se usados como uma expresso. Quando usamos os operadores na forma prefixal (antes do nome da varivel), o valor retornado depois de ser incrementado; na forma sufixal, o valor retornado e depois incrementado. O mesmo vale para o operador de decremento. E o que aconteceria se voc escrevesse algo como o seguinte? printf("%d\n", a / ++a); A resposta : no sabemos. Segundo o padro C, o resultado disso indefinido (o que significa que pode variar de um compilador para outro). No existe uma regra sobre avaliar primeiro o numerador ou o denominador de uma frao. Ou seja, no use uma varivel mais de uma vez numa expresso se usar operadores que a modificam. Talvez voc tenha achado estranha a linha: a = b = 5; Isso possvel porque atribuies so feitas da direita para a esquerda e uma instruo de atribuio tambm uma expresso que retorna o valor atribudo. Ou seja, a expresso b = 5 retornou o valor 5, que foi usado pela atribuio a = (b = 5), equivalente a a = 5. sabendo ento que uma operao inicial por sinais : + - * / seguindo suas porcentagens na aritmtica % ex: 14*57=798 sua porcentagem ser? 115/84

30

Operaes matemticas (Avanado)


O cabealho <math.h> contm prottipos de algumas funes na rea de matemtica. Na verso de 1990 do padro ISO, somente a verso double das funes foram especficadas; na verso de 1999 foram adicionadas as verses float e long double. As funes podem ser agrupadas nas seguintes categorias: 1. 2. 3. 4. 5. Funes Trigonomtricas Funes Hiperblicas Funes Exponencial e Logaritmo Funes pow e sqrt Funes de Arredondamento para Nmeros Inteiros, Valores Absolutos e Resto da Diviso

Funes Trigonomtricas
As funes acos e asin
A funo acos retorna o arco-cosseno dos seus argumentos em radianos, e a funo asin retorna o arco-seno dos seus argumentos em radianos. Todas as funes esperam por argumentos que estejam no intervalo [-1,+1]. O arco-cosseno retorna valores no intervalo [0,]; o arco-seno retorna valores no intervalo [-/2,+/2]. #include <math.h> float asinf(float x); /* C99 */ float acosf(float x); /* C99 */ double asin(double x); double acos(double x); long double asinl(long double x); /* C99 */ long double acosl(long double x); /* C99 */

Operaes matemticas (Avanado)

31

As funes atan e atan2


As funes atan retornam o arco-tangente dos arguments em radianos, e a funo atan2 retorna o arco-tangente de y/x em radianos. As funes atan retornam o valor no intervalo [-/2,+/2] (a razo pelo que /2 est incluido no intervalo porque os valores decimais pode representar o infinito, e atan() = /2); as funes atan2 retornam o valor no intervalo [-,+]. Para a funo atan2, um "domain error" pode ocorrer se os dois argumentos forem zero. #include <math.h> float atanf(float x); /* C99 */ float atan2f(float y, float x); /* C99 */ double atan(double x); double atan2(double y, double x); long double atanl(long double x); /* C99 */ long double atan2l(long double y, long double x); /* C99 */

As funes cos, sin e tan


As funes cos, sin, e tan retornam o coseno, seno, e tangente do argumento, expresso em radianos. #include <math.h> float cosf(float x); /* C99 */ float sinf(float x); /* C99 */ float tanf(float x); /* C99 */ double cos(double x); double sin(double x); double tan(double x); long double cosl(long double x); /* C99 */ long double sinl(long double x); /* C99 */ long double tanl(long double x); /* C99 */

Funes Hiperblicas
As funes cosh, sinh and tanh computam o coseno hiperblico, o seno hiperblico e a tangente hiperblica respectivamente. Para as funes de seno e coseno hiperblico, um erro de ... #include <math.h> float coshf(float x); /* C99 */ float sinhf(float x); /* C99 */ float tanhf(float x); /* C99 */ double cosh(double x); double sinh(double x); double tanh(double x); long double coshl(long double x); /* C99 */ long double sinhl(long double x); /* C99 */ long double tanhl(long double x); /* C99 */

Operaes matemticas (Avanado)

32

Funes Exponencial e Logaritmo


A funo exp
As funes exp computam a funo exponencial de x (ex). Um "range error" ocorre se o valor de x muito grande. #include <math.h> float expf(float x); /* C99 */ double exp(double x); long double expl(long double x); /* C99 */

As funes frexp, ldexp e modf


As funes frexp dividem um nmero real numa frao normalizada e um nmero inteiro mltiplo de 2. As funes guardam o nmero inteiro no objeto apontado por ex. As funes frexp retornam o valor x de forma que x tem o valor [1/2, 1) ou zero, e value igual a x vezes 2 elevado a *ex. Se value for zero, as duas partes do resultado seram zero. As funes ldexp multiplicam um nmero real por um nmero inteiro mltiplo de 2 e retornam o resultado. Um "range error" pode ocorrer. As funes modf divide o argumento value entre um parte inteira e uma frao, cada uma tem o mesmo sinal do argumento. As funes guardam o parte inteira no objeto apontado por *iptr e retornam o frao. #include <math.h> float frexpf(float value, int *ex); /* C99 */ double frexp(double value, int *ex); long double frexpl(long double value, int *ex); /* C99 */ float ldexpf(float x, int ex); /* C99 */ double ldexp(double x, int ex); long double ldexpl(long double x, int ex); /* C99 */ float modff(float value, float *iptr); /* C99 */ double modf(double value, double *iptr); long double modfl(long double value, long double *iptr); /* C99 */

As funes log e log10


As funes log computam o logaritmo natural do argumento e retornam o resultado. Um "domain error" ocorre se o argumento for negativo. Um "range error" pode ocorrer se o argumento for zero. As funs log10 computam o logaritmo comum (base-10) do argumento e retornam o resultado. Um "domain error" ocorre se o argumento for negativo. Um "range error" ocorre se o argumento for zero. #include <math.h> float logf(float x); /* C99 */ double log(double x); long double logl(long double x); /* C99 */ float log10f(float x); /* C99 */ double log10(double x); long double log10l(long double x); /* C99 */

Operaes matemticas (Avanado)

33

Funes pow e sqrt


As funes pow
As funes pow computam x elevado a y e retornam o resultado. Um "domain error" ocorre se x for negativo e y no for um nmero inteiro. Um "domain error" ocorre se o resultado no puder ser representado quando x zero e y menor ou igual a zero. Um "range error" pode ocorrer. #include <math.h> float powf(float x, float y); /* C99 */ double pow(double x, double y); long double powl(long double x, long double y); /* C99 */

As funes sqrt
As funes sqrt computam a raiz positiva de x e retornam o resultado. Um "domain error" ocorre se o argumento for negativo. #include <math.h> float sqrtf(float x); /* C99 */ double sqrt(double x); long double sqrtl(long double x); /* C99 */

Funes de Arredondamento para Nmeros Inteiros, Valores Absolutos e Resto da Diviso


As funes ceil e floor
As funes ceil computam o menor nmero inteiro que no seja menor que x e retornam o resultado; as funes floor computam o maior nmero inteiro que no seja maior que x e retornam o resultado. #include <math.h> float ceilf(float x); /* C99 */ double ceil(double x); long double ceill(long double x); /* C99 */ float floorf(float x); /* C99 */ double floor(double x); long double floorl(long double x); /* C99 */

As funes fabs
As funes fabs computam o valor absoluto do nmero real x e retornam o resultado. #include <math.h> float fabsf(float x); /* C99 */ double fabs(double x); long double fabsl(long double x); /* C99 */

Operaes matemticas (Avanado)

34

As funes fmod
As funes fmod computam o resto de x/y e retornam o valor x - i * y, pra algum nmero inteiro i onde, se y for um nmero diferente de zero, o resultado tem o mesmo sinal de x e magnitude menor que a magnitude de y. Se y for zero, dependendo da implementao da funo, ocorrer um "domain error" ou a funo fmod retornar zero. #include <math.h> float fmodf(float x, float y); /* C99 */ double fmod(double x, double y); long double fmodl(long double x, long double y); /* C99 */

Ligaes externas
Biblioteca de referncia C++ (C++ Reference Library) - cmath (math.h) [1]

Referncias
[1] http:/ / www. cplusplus. com/ reference/ clibrary/ cmath/

Controle de fluxo
Controle de fluxo
Dificilmente um programa em C ir executar sempre as mesmas instrues, na mesma ordem, independentemente do que tenha acontecido anteriormente ou do valor que foi fornecido. muito comum que algum queira que um pedao de cdigo s seja executado se uma certa condio for verdadeira; tambm comum querer que um pedao de cdigo seja repetido vrias vezes, de tal maneira que simplesmente copiar o cdigo no resolveria o problema ou seria trabalhoso demais. Para casos como esses, existem as estruturas de controle de fluxo. Em C, existem vrias instrues relacionadas ao controle de fluxo: if, que executa um bloco apenas se uma condio for verdadeira; switch, que executa um bloco de acordo com o valor de uma expresso ou varivel; for, que executa um bloco repetidas vezes enquanto uma condio for verdadeira, executando uma instruo (geralmente de incremento ou decremento de uma varivel) aps cada execuo; while, que executa um bloco enquanto uma condio for verdadeira; do, semelhante ao while, mas a condio avaliada aps a execuo (e no antes); goto, que simplesmente pula para um lugar pr-definido. Porm, antes de entrar no estudo dessas estruturas, voc deve saber como escrever uma condio. o que explicamos a seguir.

Expresses de condio
Uma expresso de condio uma expresso normal em C que, quando avaliada, ser interpretada como verdadeira ou falsa. Em C, na verdade, esse valor um valor inteiro que sendo 0 (zero) significa falso, sendo qualquer outro nmero significa verdadeiro. Geralmente em expresses condicionais usamos os operadores relacionais, ou seja, que avaliam a relao entre seus dois operandos. Existem seis deles:

Controle de fluxo

35

Operador > >= < <= == !=

Significado maior que maior ou igual a menor que menor ou igual a igual a diferente de

Todos esses operadores so binrios, ou seja, trabalham com dois valores ou operandos. Esses operadores sempre comparam o valor da esquerda com o da direita, ou seja, a expresso a > b significa "a maior que b". Note que para saber se dois nmeros so iguais devemos usar dois sinais de igual. Um erro muito comum esquecer de um deles, transformando a comparao numa atribuio por exemplo: if (x = 1) ... O que acontece aqui que a varivel x recebe o valor 1, de modo que a expresso entre parnteses tambm ter o valor 1 tornando a condio sempre verdadeira. Similarmente, se usssemos o nmero zero, a expresso sempre seria falsa. Portanto, sempre tome cuidado com esse tipo de comparao. A maneira certa de comparar com um nmero : if (x == 1) ... Tambm comum que combinemos condies. Por exemplo, podemos querer que um nmero seja menor que 10 ou maior que 50. Como o operador "ou" "||", escreveramos: n < 10 || n > 50. A seguir voc v os operadores lgicos:
Operador Significado || && ! ou (OR) e (AND) no (NOT)

Algumas explicaes sobre os operadores lgicos: O operador "no" unrio, ou seja, uma operao que envolve apenas um valor. O que ele faz inverter o valor de seu operando: retorna falso se a expresso for verdadeira e vice-versa. Deve-se usar parnteses ao negar uma expresso: !(x > 6), por exemplo. O operador "ou" retorna "verdadeiro" se pelo menos um dos operandos for verdadeiro; retorna "falso" apenas se ambos forem falsos. O operador "e" retorna "verdadeiro" apenas se ambos os seus operandos forem verdadeiros. Observao Se voc quer saber se um nmero est entre outros dois, a sintaxe matemtica (10 < n < 50) no funcionar. Se voc usar esse cdigo, na verdade primeiramente ser avaliada a expresso 10 < n, que poder resultar em 0 ou 1. Portanto, a expresso equivale a (0 ou 1) < 50, o que sempre verdadeiro. A comparao correta envolveria o operador "e" (&&): 10 < n && n < 50. Pelo fato de todo valor diferente de zero ser avaliado como verdadeiro e zero como falso, existem as seguintes equivalncias (apenas quando estas expresses so usadas como condies):

Controle de fluxo (x == 0) equivale a (!x) (x != 0) equivale a (x)

36

Testes
Testes so estruturas de controle que executam certos blocos de cdigo apenas se uma certa condio for verdadeira. Existem trs estruturas desse tipo em C:

if
O teste if avalia uma condio e, se ela for verdadeira, executa um bloco de cdigo. A sintaxe correspondente a isso : if (condio) { ... /* bloco a ser executado se a condio for verdadeira */ } Mas tambm podemos especificar um bloco a ser executado caso a condio for falsa. Nesse caso, escrevemos: if (condio) { ... /* bloco a ser executado se a condio for verdadeira */ } else { ... /* bloco a ser executado se a condio for falsa */ } As chaves podem ser omitidas caso haja apenas uma instruo no bloco. Por exemplo: if (x == 5) printf ("x igual a 5.\n"); Perceba que, se esquecermos as chaves, o compilador no dever dar nenhum erro; no entanto, tudo que exceder a primeira instruo ser executado incondicionalmente, mesmo que esteja na mesma linha! No exemplo a seguir, a frase "x igual a 5" seria exibida mesmo que o nmero no fosse 5! if (x == 5) j++; printf ("x igual a 5.\n"); Podemos avaliar diversas condies com os testes if, bastando para isso colocar um novo teste no bloco else. Tambm possvel aninhar blocos if, ou seja, colocar um dentro de outro: if (x > 9) { printf ("x maior que 9.\n"); } else if (x >= 5) { printf ("x maior ou igual a 5, mas no maior que 9.\n"); } else { if (x == 0) { printf ("x igual a zero.\n"); } else { printf ("x no-nulo e menor que 5.\n");

Controle de fluxo } }

37

switch
O teste switch compara uma expresso com diversos valores que podem estar associados a blocos de cdigos diferentes, e executa o bloco de cdigo correspondente ao valor encontrado. Voc tambm pode especificar um bloco que deve ser executado caso nenhum dos outros valores seja encontrado: o bloco default ("padro" em ingls). switch (expresso) { case valor1: instrues; break; case valor2: instrues; break; ... default: instrues; } Note que no teste switch no precisamos usar chaves em volta dos blocos, a menos que declaremos variveis neles. Um exemplo da utilizao de switch seria a criao de um menu: int opcao; printf ("[1] Cadastrar cliente\n" "[2] Procurar cliente\n" "[3] Inserir pedido\n" "[0] Sair\n\n" "Digite sua escolha: "); scanf ("%d", &opcao); switch (opcao) { case 1: cadastra_cliente(); break; case 2: procura_cliente(); break; case 3: insere_pedido(); break; case 0: return 0; default: printf ("Opo invlida!\n"); } A instruo break indica que deve-se continuar a execuo aps o final do bloco switch (pulando o que estiver no meio). Se ela no fosse usada, para um certo valor encontrado, seriam executadas tambm as instrues de todos

Controle de fluxo os valores abaixo dele. Em alguns casos, podemos omitir intencionalmente a instruo break. Por exemplo, no exemplo acima, no colocamos uma instruo break para o valor zero, pois quando retornamos de uma funo (return 0) o bloco switch j abandonado. Tambm podemos querer que uma instruo seja executada para mais de um valor. Vamos supor que no nosso menu as duas primeiras opes fossem "Cadastrar pessoa fsica" e "Cadastrar pessoa jurdica", e tvessemos uma funo que faz o cadastro diferentemente dependendo do valor da varivel pessoa_fisica. Poderamos fazer um cdigo assim: switch (opcao) { case 1: /* pessoa fsica */ pessoa_fisica = 1; case 2: cadastra(); break; ... } Nesse caso, para qualquer uma das duas opes seria executada a funo cadastra, mas se selecionarmos "pessoa fsica" a varivel ser atribuda antes.

38

Operador ternrio "?:"


O operador ternrio ?: uma alternativa abreviada da estrutura if/else. Ele avalia uma expresso e retorna um certo valor se ela for verdadeira, ou outro valor se ela for falsa. Sua sintaxe : condio ? valorSeVerdadeira : valorSeFalsa Note que, ao contrrio de if, ao usarmos o operador condicional ?: precisamos sempre prover tanto o valor para o caso de a condio ser falsa quanto o valor para o caso de ela ser verdadeira. O operador condicional pode ser usado em situaes como essa: int horaAbertura = (diaSemana == DOMINGO) ? 11 : 9; printf ("Abrimos s %d horas", horaAbertura); Ou seja, se o dia da semana for domingo, a varivel horaAbertura ser definida para 11; caso contrrio, ser definida para 9. Outro exemplo: if (numMensagens > 0) { printf ("Voc tem %d mensage%s", numMensagens, (numMensagens > 1) ? "ns" : "m"); } Neste caso, o programa utilizaria "mensagens" caso houvesse mais de uma mensagem, e "mensagem" caso houvesse apenas uma mensagem.

Controle de fluxo

39

Loops
Loops so conjuntos de instrues que devem ser executadas repetidas vezes, enquanto uma condio for verdadeira. Em C h 3 tipos de loops: while, do ... while e for.

while
O loop while testa uma condio; se ela for verdadeira, o bloco correspondente executado e o teste repetido. Se for falsa, a execuo continua logo aps o bloco. A sintaxe de while : while (condio) { ... } Por exemplo: while (a < b) { printf ("%d menor que %d", a, b); a++; } Este cdigo seria executado at que a fosse igual a b; se a fosse igual ou maior que b, nada seria executado. Por exemplo, para b = 10 e a < 10, a ltima mensagem que o usurio veria "9 menor que 10". Repare que o loop while como fosse um if, ou seja, o bloco executado se a condio for verdadeira. A diferena que ao final da execuo, o while executado novamente, mas o if no. No loop while (assim como nos loops do e for) tambm podemos usar a sintaxe abreviada para apenas uma instruo: while (a < b) a++; Loops infinitos Voc pode fazer loops infinitos com while, usando uma condio que sempre verdadeira, como "1 == 1" ou simplesmente "1" (que, como qualquer valor no-nulo, considerado "verdadeiro"): while (1) { ... } Voc pode sair de um loop infinito ou no com a instruo break, que voc j viu no teste switch e ser explicada mais abaixo.

do ... while
O loop "do ... while" exactamente igual ao "while" exceto por um aspecto: a condio testada depois do bloco, o que significa que o bloco executado pelo menos uma vez. A estrutura do ... while executa o bloco, testa a condio e, se esta for verdadeira, volta para o bloco de cdigo. Sua sintaxe : do { ... } while (condio); Note que, ao contrrio das outras estruturas de controle, necessrio colocar um ponto-e-vrgula aps a condio.

Controle de fluxo do { printf ("%d\n", a); a++; } while (a < b); Um exemplo de utilizao de do ... while em um menu. Pediramos que o usurio escolhesse uma opo at que ele escolhesse uma opo vlida: #include <stdio.h> int main () { int i; do { printf ("Escolha a fruta pelo nmero:\n\n"); printf ("\t(1) Mamo\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 ("Voc escolheu mamo.\n"); break; case 2: printf ("Voc escolheu abacaxi.\n"); break; case 3: printf ("Voc escolheu laranja.\n"); break; } return 0; }

40

for
O loop for nada mais que uma abreviao do loop while, que permite que alguma inicializao seja feita antes do loop e que um incremento (ou alguma outra ao) seja feita aps cada execuo sem incluir o cdigo dentro do bloco. A sua forma geral for (inicializao; condio; incremento) { instrues; } E equivale a inicializao; while (condio) {

Controle de fluxo instrues; incremento; } Um exemplo do uso de for: for (a = 1; a < 10; a++) { if(a == 1) puts ("Numero de voltas previstas 9."); printf("Numero de loop ou volta : %i ", a ); printf("Valor de a : %i ", a ); } Nesse exemplo, primeiro definimos o valor de a como 1; depois, o cdigo (...) repetido enquanto a for menor que dez, incrementando em uma unidade o valor de a aps cada execuo do cdigo. Analisando essas condies, voc podera perceber que o cdigo ser executado nove vezes: na primeira execuo, temos a = 1; aps a nona execuo, a igual a 10, e portanto o bloco no ser mais repetido. Tambm podemos dar mais de uma instruo de inicializao ou de incremento (separadas por vrgula), alm de poder usar naturalmente condies compostas com o uso dos operadores lgicos: for (a = 1, b = 1; a < 10 && (b / a) < 20; a++, b *= 2) { ... } Nesse exemplo, "a" e "b" so inicializados com o valor 1. A cada loop, o valor de "a" incrementado em uma unidade e o de "b" dobrado. Isso ocorre enquanto "a" for menor que 10 e a razo entre "b" e "a" for menor que 20. Se voc construir uma tabela com os valores de cada varivel a cada loop (ou colocar algum contador dentro do loop), ver que ocorrem oito execues. Assim como while, o loop for testa a condio; se a condio for verdadeira ele executa o bloco, faz o incremento e volta a testar a condio. Ele repete essas operaes at que a condio seja falsa. Podemos omitir qualquer um dos elementos do for se desejarmos. Se omitirmos a inicializao e o incremento, o comportamento ser exatamente igual ao de while. Se omitirmos a condio, ficaremos com um loop infinito: for (inicializao; ; incremento) { ... } Podemos tambm omitir o bloco de cdigo, se nos interessar apenas fazer incrementos ou se quisermos esperar por alguma situao que estabelecida por uma funo externa; nesse caso, usamos o ponto-e-vrgula aps os parnteses de for. Isso tambm valido para o loop while: for (inicializao; condio; incremento) ; while (condio) ; Por exemplo, suponha que temos uma biblioteca grfica que tem uma funo chamada graphicsReady(), que indica se podemos executar operaes grficas. Este cdigo executaria a funo repetidas vezes at que ela retornasse "verdadeiro" e ento pudssemos continuar com o programa: while (!graphicsReady()) ;

41

Controle de fluxo

42

break e continue
Voc j viu break sendo usado para sair do teste switch; no entanto, ele funciona tambm nos loops while, do e for. Nos trs casos, ele sai do ltimo loop iniciado (mesmo que haja mais de um). Por exemplo: while (1) { if (a > b) break; a++; } break sempre faz com que a execuo do programa continue na primeira instruo seguinte ao loop ou bloco. A instruo continue parecida com break, porm ao execut-la saltamos para a prxima iterao loop ao invs de termin-lo. Usar continue equivale a chegar ao final do bloco; os incrementos so realizados (se estivermos em um loop for) e a condio reavaliada (qualquer que seja o loop atual). #include <stdio.h> int main() { int opcao = 0; while (opcao != 5) { printf("Escolha uma opo entre 1 e 5: "); scanf("%d", &opcao); /* se a opo for invlida, volta ao incio do loop */ if (opcao > 5 || opcao < 1) continue; switch (opcao) { case 1: printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break; } } return 0;

Controle de fluxo } Esse exemplo recebe uma opo do usurio. Se ele digitar uma opo invlida (ou seja, no for um nmero de 1 a 5), a instruo continue voltar ao comeo do loop e o programa pedir novamente a entrada do usurio. Se ele digitar uma opo vlida, o programa seguir normalmente.

43

Loops sem contedo


Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

Em alguns casos pode ser interessante o uso de loops sem contedo ou seja, sem instrues dentro de seu corpo. Verifique nessa mesma pgina casos de omisso para for e while;

Saltos incondicionais: goto


O goto uma instruo que salta incondicionalmente para um local especfico no programa. Esse local identificado por um rtulo. A sintaxe da instruo goto : goto nome_do_rtulo; Os nomes de rtulo so identificadores sufixados por dois-pontos (:), no comeo de uma linha (podendo ser precedidos por espaos). Por exemplo: nome_do_rtulo: ... goto nome_do_rtulo; Muitos programadores evitam usar o goto pois a maioria dos saltos pode ser feita de maneira mais clara com outras estruturas da linguagem C. Na maioria das aplicaes usuais, pode-se substituir o goto por testes, loops e chamadas de funes.

Funes

44

Funes
Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

Bsico
O que funo
Uma funo um pedao de cdigo que faz alguma tarefa especfica e pode ser chamado de qualquer parte do programa quantas vezes desejarmos. Utilizamos funes para obter: Clareza do cdigo: separando pedaos de cdigo da funo main(), podemos entender mais facilmente o que cada parte do cdigo faz. Alm disso, para procurarmos por uma certa ao feita pelo programa, basta buscar a funo correspondente. Isso torna muito mais fcil o ato de procurar por erros. Reutilizao: muitas vezes queremos executar uma certa tarefa vrias vezes ao longo do programa. Repetir todo o cdigo para essa operao muito trabalhoso, e torna mais difcil a manuteno do cdigo: se acharmos um erro nesse cdigo, teremos que corrig-lo em todas as repeties do cdigo. Chamar uma funo diversas vezes contorna esses dois problemas. Independncia: uma funo relativamente independente do cdigo que a chamou. Uma funo pode modificar variveis globais ou ponteiros, mas limitando-se aos dados fornecidos pela chamada de funo. A idia de uma funo est, naturalmente, permitir voc encapsular um idia ou operao, dando um nome a ela, ento chamar que operao de vrias partes do resto de seu programa simplesmente usando o seu nome. Estando corretamente projetado e estruturado o programa, em uma situao ideal, dever ser possvel modificar as funes sem efeito no resto do programa.

Definindo uma funo


Uma funo pode necessitar de alguns dados para que possa realizar alguma ao baseada neles. Esses dados so chamados parmetros da funo. Alm disso, a funo pode retornar um certo valor, que chamado valor de retorno. Os parmetros (e seus tipos) devem ser especificados explicitamente, assim como o tipo do valor de retorno. A forma geral da definio de uma funo : [tipo de retorno da funo] [nome da funo] (1 parmetro, 2 parmetro, ) { //cdigo } Para o nome da funo e dos parmetros valem as mesmas regras que foram dadas para os nomes de variveis. No podemos usar o mesmo nome para funes diferentes em um programa. Todas as funes devem ser definidas antes da funo main, ou deve ser feito o prottipo da funo, que veremos mais adiante. O cdigo deve estar obrigatoriamente dentro das chaves e funciona como qualquer outro bloco.

Funes

45

Valor de retorno
Freqentemente, uma funo faz algum tipo de processamento ou clculo e precisa retornar o resultado desse procedimento. Em C, isso se chama valor de retorno e pode ser feito com a instruo return. Para poder retornar um valor, precisamos especificar seu tipo (char, int, float, double e variaes). Para efetivamente retornar um valor, usamos a instruo return seguida do valor de retorno, que pode ou no vir entre parnteses. Um exemplo bem simples de funo que retorna um valor inteiro: int tres() { return 3; }

// poderia tambm ser return (3);

O tipo de retorno, alm dos tipos normais de variveis (char, int, float, double e suas variaes), pode ser o tipo especial void, que na verdade significa que no h valor de retorno. Nota Muitos livros dizem que a funo main tem tipo de retorno void, o que no est correto. Segundo o padro da linguagem C, a funo main deve ter retorno do tipo int. Compiladores como o gcc daro mensagens de erro caso a funo main() no seja definida corretamente.

Parmetros
Como j foi dito, um parmetro um valor que fornecido funo quando ela chamada. comum tambm chamar os parmetros de argumentos, embora argumento esteja associado ao valor de um parmetro. Os parmetros de uma funo podem ser acessados da mesma maneira que variveis locais. Eles na verdade funcionam exatamente como variveis locais, e modificar um argumento no modifica o valor original no contexto da chamada de funo, pois, ao dar um argumento numa chamada de funo, ele copiado como uma varivel local da funo. A nica maneira de modificar o valor de um parmetro usar ponteiros, que sero introduzidos mais adiante. Para declarar a presena de parmetros, usamos uma lista de parmetros entre parnteses, com os parmetros separados por vrgulas. Cada declarao de parmetro feita de maneira semelhante declarao de variveis: a forma geral tipo nome. Por exemplo: int funcao (int a, int b) float funcao (float preco, int quantidade) double funcao (double angulo) Para especificar que a funo no usa nenhum parmetro, a lista de parmetros deve conter apenas a palavra-chave void. No entanto, ela freqentemente omitida nesses casos. Portanto, voc poderia escrever qualquer uma destas duas linhas: void funcao (void) void funcao () Note que os nomes dos parmetros so usados apenas na prpria funo (para distinguir os argumentos); eles no tm nenhuma relao com as variveis usadas para chamar a funo.

Funes

46

Chamadas de funes
Para executar uma funo, fazemos uma chamada de funo, que uma instruo composta pelo nome da funo, seguido pela lista de argumentos entre parnteses: nome_da_funo (arg1, arg2, ...); Os argumentos podem ser qualquer tipo de expresso: podem ser variveis, valores constantes, expresses matemticas ou at mesmo outras chamadas de funo. Lembre que voc deve sempre dar o mesmo nmero de argumentos que a funo pede. Alm disso, embora algumas converses de tipo sejam feitas automaticamente pelo compilador, voc deve atender aos tipos de argumentos. Note que o valor dos argumentos copiado para a funo, de maneira que as variveis originais ficam inalteradas mesmo que na funo tentemos alter-las. A isso chamamos passagem de argumentos por valor (ao contrrio de por referncia). Veremos como modificar as variveis originais na seo Ponteiros. A prpria chamada de funo tambm uma expresso cujo valor o valor de retorno da funo, bastando coloc-la no lado direito de um sinal de igual para guardar o valor numa varivel. Por exemplo, se a funo "quadrado" retorna o quadrado de um nmero inteiro, podemos fazer assim para calcular o quadrado de 11 na varivel x: int x = quadrado (11);

Dois exemplos
#include <stdio.h> int quadrado (int x) { return (x * x); } void saudacao (void) { printf ("Ol!\n"); } void despedida (void) { printf ("Fim do programa.\n"); } int main () { int numero, resultado; saudacao (); printf ("Digite um nmero inteiro: "); scanf ("%d", &numero); resultado = quadrado (numero); printf ("O quadrado de %d %d.\n", numero, resultado);

Funes despedida (); return 0; } Voc veria na tela, ao executar o programa: Ol! Digite um nmero inteiro: 42 O quadrado de 42 1764. Fim do programa. Repare que, ao chegar na chamada de uma funo, o programa passa o controle para essa funo e, aps seu trmino, devolve o controle para a instruo seguinte na funo original. Mais um exemplo, com uma funo de 3 argumentos: #include <stdio.h> /* Multiplica 3 numeros */ void mult (float a, float b, float c) { printf ("%f",a*b*c); } int main () { float x, y; x = 23.5; y = 12.9; mult (x, y, 3.87); return 0; }

47

Prottipo ou Declarao de funo


Quando um programa C est sendo compilado e uma chamada de funo encontrada, o compilador precisa saber o tipo de retorno e os parmetros da funo, para que ele possa manipul-los corretamente. O compilador s tem como saber isso se a funo j tiver sido definida. Portanto, se tentarmos chamar uma funo que est definida abaixo da linha onde estamos fazendo a chamada, ou mesmo em outro arquivo, o compilador dar uma mensagem de erro, pois no conseguiu reconhecer a funo. //Exemplo de erro de chamada de funo int main() { int a = 1; int b = 2; soma(a,b); // erro: a funo est definida abaixo desta linha! } void soma(int a, int b) { printf("%d", a+b);

Funes } Nesses casos, podemos declarar uma funo antes de defini-la. Isso facilita o trabalho de usar diversas funes: voc no precisar se importar com a ordem em que elas aparecem nos arquivos. A declarao de funo (tambm chamada de prottipo de funo) nada mais que a definio da funo sem o bloco de cdigo. Como uma instruo, ela deve ser seguida de um ponto-e-vrgula. Portanto, para declarar a funo: int quadrado (int x) { return (x * x); } escreveramos: int quadrado (int x); Numa declarao, tambm podemos omitir os nomes dos parmetros, j que estes so ignorados por quem chama a funo: int quadrado (int); Poderamos, por exemplo, reorganizar o incio do programa-exemplo dado um pouco acima, o que permitiria colocar as funes em qualquer ordem mesmo que houvesse interdependncia entre elas: #include <stdio.h> int quadrado (int x); void saudacao (void); void despedida (void); // seguem as funes do programa Note que a definio da funo no deve contradizer a declarao da mesma funo. Se isso ocorrer, uma mensagem de erro ser dada pelo compilador.

48

Variveis locais versus globais


Quando declaramos as variveis, ns podemos faz-lo Dentro de uma funo Fora de todas as funes inclusive a main(). As primeiras so as designadas como locais: s tm validade dentro do bloco no qual so declaradas. As ltimas so as globais, elas esto vigentes em qualquer uma das funes. Quando uma funo tem uma varivel local com o mesmo nome de uma varivel global a funo dar preferncia varivel local. Daqui conclui-se e bem que, podemos ter variveis com o mesmo nome, o que contradiz o que ns dissemos no capitulo das variveis. Ento reformulamos: apenas na situao em que temos 2 variveis locais que colocada a restrio de termos nomes diferentes caso contrrio no conseguiramos distinguir uma da outra. Exemplo: Ento aonde estvamos?? Sim ,aqui "largo" e "alto" sao variveis internas fazem parte de "minhaFuncion()". /*espanhol para incultos :)*/ <== Comentrios da funo void minhaFuncion()

Funes { double largo = 5; double alto = 6; } As variveis largo e alto no esto definidas aqui abaixo, isto quer dizer que elas nao tem nem um valor. E no podemos usar os valores definido dentro da "minhaFuncion", pois no h nenhuma instruo que defina que valor usar! Lembre-se: O computador no vai adivinhar qual valor usar. Deve-se definir cada instruo. void calcular() /*No houve definio de valor entre parenteses*/ { long surface = largo * alto; /*Error bip bip valor nao definido*/ return(surface); } Nesse exemplo abaixo, poderemos usar o valor das variveis externas dentro de todas as funoes : Exemplo:
#include <stdio.h> /*Variavel externa*/ long largo = 10; long alto { /*variavel interna*/ long somma = largo + alto ; } long calcular() { long surface = largo * alto; } int main(void) { long reslt = calcular(); printf("A surface e:%ld\n",reslt); return 0 ; } /*Aqui ele pode enxegar a varivel "externa" (global)*/ = 20; void minhaFuncion()

49

Curiosidade
Em C++ existe a palavra reservada "auto", que serve para dizer que uma varivel local. Mas intil pois as variveis declaradas dentro de um bloco j so consideradas locais.

Funes

50

Lista de argumentos
Na linguagem C tambm possvel funes como "printf" aonde o numero de argumentos podem variar. As reticncias ( ... ) indicam um numero varivel de argumentos ou argumentos com tipos varivel. Ex: void f_erro(int n, char *fmt, ...); Essa declaraao indica que se deve fornecer pelo menos dois argumentos, um do tipo int e um do tipo char mais pode se fornecer argumentos suplementares. Ou seja, "no h limites para sua criatividade"! Ex: f_erro( 3, "Erro: misso impossvel "); f_erro( valor, "%s %d\n", mensagem, errno); E necessrio ter pelo menos um argumento antes dos pontos.Veja um exemplo incorreto. void erreur(...); O arquivo de cabealho stdarg.h declara um tipo va_list e define trs macros para manipular uma lista de argumentos cuja quantidade e tipos so desconhecidos pela funo. va_start, va_arg et va_end (va como variable argument)

#include <stdio.h> #include <stdarg.h> void meu_printf(char *fmt, ...) { va_list pa; int n; char *s, c; float f; va_start(pa, fmt); while (*fmt != '\0') { if ( *fmt == '%' ) { /* (*++fmt) equivale a (*fmt = *fmt + 1 )*/ switch (*++fmt) { case '%' : putchar('%'); break; case 'c' : /* char*/ c = va_arg(pa, int); putchar(c); break; case 'd' : /* int */ n = va_arg(pa, int); printf("%d", n); break; case 'f' : /* float */ f = va_arg(pa, double); /* !!!!! */ printf("%f", f); break; case 's' : /* string */ s = va_arg(pa, char *);

Funes for ( ; *s != '\0'; s++ ) putchar( *s ); break; } /* end switch */ } else putchar( *fmt ); /*incrementa o ponteiro*/ fmt++; } va_end(pa); } main() { meu_printf("float = %f\n", (float) 1.2345); meu_printf("int = %d char = %c String = %s\n", 123, 'A', "C is beautiful !" ); return 0; }

51

Passagem de parmetros por valor e por referncia


Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

O que ns temos feito quando chamamos uma funo a dita chamada por valor. Quer dizer, quando chamamos uma funo e passamos parmetros para a funo prottipo e depois para a funo definio, o valor dos argumentos passados so copiados para os parmetros da funo. Estes existem independentemente das variveis que foram passadas. Eles tomam apenas uma cpia do valor passado, e se esse valor for alterado o valor dos argumentos passados no so alterados. Ou seja, no so alterados os valores dos parmetros fora da funo. Este tipo de chamada de funo denominado chamada (ou passagem de parmetros) por valor. Dito de outra maneira. Passamos a varivel a, ela entra na definio da funo como copia de a e entra como varivel b. Se a varivel b for alterada no decorrer da funo, o valor de a no alterado. //Exemplo #include <iostream> using namespace std; float quadrado(float num); //prottipo da funo quadrado() int main () { float num, res; //declaro 2 variveis: num e res cout << "Entre com um numero: "; cin >> num; //associo o valor inserido varivel num res = quadrado(num); //chamo a funo quadrado e passo o parmetro num cout << "\n\nO numero original e: " << num; cout << " e seu quadrado vale:" << res; float a, b;

Funes cout << "Entre com um numero: "; cin >> a; a = a*a; b = a; cout << "\n\nO numero original e: " << a; cout << " e seu quadrado vale:" << b ; system ("pause"); return 0; } float quadrado (float num) //descrio da funo quadrado { return num*num; //retorna num ao quadrado } Quando a funo main() executada, ela chega a meio e v uma chamada para a funo quadrado() e onde passado o parmetro "num". Ela j estava a espera, pois "viu" o prottipo. Ela ento vai executar a funo que est depois da funo do main(). E o que acontece que o "num", vai ficar com o dobro do valor. Esse valor do main() vai entrar novamente no main(). E associado varivel "res". Depois temos a impresso da varivel "num" e "res". Ora o que acontece que o valor do "num" fica igual ao valor antes de entrar na funo. Fazemos a mesma coisa agora com a varivel "a" e "b", e vemos que agora a funo a alterada. Resumindo, o valor varivel quando entra numa outra funo no alterado (na passagem por valor). Quando o valor do parmetro alterado denominamos chamada (ou passagem) por referncia. O C no faz chamadas por referncia. Mas podemos simular isto com outra arma do C que so os ponteiros, que sero melhor explicados mais adiante.

52

Os argumentos argc e argv


A funo main(), como dissemos antes, uma funo especial. Introduzimo-la como uma funo sem parmetros; no entanto, ela tambm pode receber parmetros formais. No entanto, o programador no pode escolher quais sero. Eles devem ser os seguintes: int main (int argc, char *argv[]) argc (argument count) um inteiro e possui o nmero de argumentos com os quais o programa foi chamado na linha de comando. Ele no mnimo 1, pois o nome do programa contado como sendo o primeiro argumento. argv (argument values) um ponteiro para uma matriz de strings (conceitos que sero abordados mais frente). Cada string desta matriz um dos parmetros da linha de comando. argv[0] sempre aponta para o nome do programa (que, como j foi dito, considerado o primeiro argumento). para saber quantos elementos temos em argv que temos argc. Como pode se imaginar, os nomes dos parmetros "argc" e "argv" podem ser mudados, mas por questo de padronizao no se costuma modific-los. S no vamos ilustrar aqui o uso de argc e argv pois ainda no foram apresentadas as matrizes.

Funes

53

Avanado
VOID
Como dissemos, uma funo retorna um valor. E pode receber parmetros. O void utilizado da seguinte forma: void funo(void) { //codigo } No exemplo acima, a palavra void define que: no vai receber parmetros; e no vai retornar qualquer valor. Ou melhor, void uma explicitao do programador que aquela funo no vai receber ou retornar nenhum valor. O valor da funo ignorado, mas a funo realmente retorna um valor, por isso para que o resultado no seja interpretado como um erro e bom declarar void.
IMPORTANTE No se pode utilizar void na funo principal main, apesar de existirem exemplos com void em algumas bibliografias. Infelizmente, alguns compiladores aceitam void main(). O main() especial e tem de retornar um int. Uma execuo bem sucedida do programa costuma retornar 0 (zero) e, em caso de erro, retorna 1 (um).

Recursividade
Uma funo pode chamar a si prpria. Uma funo assim chamada funo recursiva. H vrias operaes matemticas recursivas, das quais exemplos bem conhecidos so a seqncia de Fibonacci e o fatorial. Daremos o exemplo do clculo do fatorial de um nmero, definido como o produto de todos os nmeros naturais (no nulos) menores ou iguais a ele por exemplo, 5! (l-se "cinco fatorial") igual a . Ateno conveno . Uma maneira de definir o algoritmo de fatorial :

E a implementao correspondente seria esta: #include <stdio.h> int fatorial(int n) { if (n >= 2) return n * fatorial(n - 1); else return 1; } int main() {

Funes int n; printf("Digite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d %d", n, fatorial(n)); return 0; }

54

Parmetros default (padro)


Aqui a funao "funDefa()" contida na funo "main()" contem um valor default "nfunDefa( k,30)". Isto indica ao compilador que na ausncia de argumento definido deve usar este valor. Parmetros default no fazem parte da linguagem "C" , mais sim da "linguagem C++" . Por exemplo: Na linha 16 aonde esta escrito a funo nfunDefa() , coloque nfunDefa(50,25). Concluso este e um exemplo intil na "linguagem C" .
#include <stdio.h> #include <stdlib.h> /*-----------------------------Cabealho--------------------------------*/ /*Definimos uma funao*/ void nfunDefa(int pr,int se) { printf("Meu Primeiro argumento:%d\n",pr ); printf("Meu Segundo argumento :%d\n",se ); } int main (void) { int k=20,n=30; /*Na linguagem "C" a chamada de uma funao *Deve conter a mesma quantidade de argumentos *Nao podemos colocar so k ou so n*/ nfunDefa( k,30); } /*chamada "normal"*/ que a funao que esta esperando

Na linguagem "C" os argumentos contido na funo que faz a chamada deve ser equivalente ao nmero de argumentos definidos em a funo que esta a esperar. Por exemplo:
funcion(int a,int b) ------> Dois parmetros funcion(30) funcion (25,58) ---------> incorreto ou correto so na linguagem C++ ---------> Dois parmetros Correto nas duas linguagens "C" e C++

Ento dentro da "funo main" na funo "funDefa()" no podemos definir s "n" ou s "k". A linguagem C++ por ser mais verstil nos permite.

Funes

55

Sobrecarga de funes
Em C++ duas funes podem ter o mesmo nome se: Tiverem um n diferente de parmetros e/ou Se os parmetros forem de tipos diferentes (ints floats,..) A funo no pode ser overloaded apenas com diferentes tipo de retorno de funo (ie, uma funo retornar ints e a outra retornar floats) ento os parmetros que interessam. #include <iostream> using namespace std; void ConvertFToC(double f, double &c); void ConvertFToC(float f, float &c); void ConvertFToC(int f, int &c); int main() { double df, dc; float ff, fc; int i_f,i_c; //if is a reserved word df = 75.0; ff = 75.0; i_f = 75; // The compiler resolves the correct // version of ConvertFToC based on // the arguments in each call cout << "Calling ""double"" version" << endl; ConvertFToC(df,dc); cout << df << " == " << dc << endl << endl; cout << "Calling ""float"" version" << endl; ConvertFToC(ff,fc); cout << ff << " == " << fc << endl << endl; cout << "Calling ""int"" version" << endl; ConvertFToC(i_f,i_c); cout << i_f << " == " << i_c << endl << endl; system ("pause"); } void ConvertFToC(double f, double &c) { cout << "In ""double"" version" << endl; c = (f - 32.0) * 5. / 9.; } void ConvertFToC(float f, float &c) { cout << "In ""float"" version" << endl; c = (f - 32.0) * 5. / 9.; } void ConvertFToC(int f, int &c) { cout << "In ""int"" version" << endl;

Funes c = (f - 32) * 5. / 9.; } O que que acontece se tivermos um n diferente de argumentos entre a chamada e a definio? A soluo aqui proposta quando no sabemos a quantidade de parmetros que a funo vai ser chamada ou mesmo a tipologia desses argumentos, o que se sugere fazer vrias definies para a funo e dar a todas elas o mesmos nome, que o compilador vai saber escolher a definio correcta atravs do n e tipologia de argumentos. Entretanto, por boa prtica, as funes no devem ser sobrecarregadas se fizerem operaes distintas.

56

inline
Uma funo inline, em vez de ser chamada, ser movida para o local de chamada no momento da compilao. Se fizermos um paralelismo com as directivas de compilao, como #define, ela vai substituir cada chamada da funo pela prpria funo, como fosse uma macro. Mas isto s tem vantagens para, primeiro para cdigos pequenos, e para quem necessite muito da velocidade no processamento. Alguns compiladores j fazem isto automaticamente. Para tornar uma funo inline basta preceder a declarao da funo com o nome inline. inline [tipo_de_retorno] [nome_da_funo] (argumentos) { //cdigo }

Pr-processador
O pr-processador
O pr-processador C um programa que examina o programa fonte escrito em C e executa certas modificaes nele, baseado nas diretivas de compilao (ou diretivas do pr-processador). As diretivas de compilao so comandos que no so compilados, sendo dirigidos ao pr-processador, executado pelo compilador antes da execuo do processo de compilao propriamente dito. Portanto, o pr-processador modifica o programa fonte, que ainda no estaria pronto para ser entregue ao compilador. Todas as directivas de compilao so iniciadas pelo caractere # (sharp). As diretivas podem ser colocadas em qualquer parte do programa, mas no podem ser colocadas na mesma linha que outra diretiva ou instruo. As principais diretivas de compilao so: #include #define #undef #ifdef #ifndef #if #else #elif

#endif

Pr-processador

57

Diretivas de compilao
#include
A diretiva #include diz ao pr-processador para incluir naquele ponto um arquivo especificado. Sua sintaxe : #include "nome_do_arquivo" ou #include <nome_do_arquivo> A diferena entre se usar "" e <> somente a ordem de procura nos directrios pelo arquivo especificado. Se voc quiser informar o nome do arquivo com o caminho completo, ou se o arquivo estiver no directrio de trabalho, use "arquivo". Se o arquivo estiver nos caminhos de procura pr-especificados do compilador, isto , se ele for um arquivo do prprio sistema (como o caso de arquivos como stdio.h, string.h, etc...), use <arquivo>.

#define
A diretiva #define tem duas utilidades. Uma delas apenas definir um smbolo que pode ser testado mais tarde. Outra definir uma constante ou ainda uma macro com parmetros. As trs maneiras de usar a diretiva so: #define nome_do_smbolo #define nome_da_constante valor_da_constante #define nome_da_macro(parmetros) expresso_de_substituio Toda vez que o pr-processador encontrar nome_da_constante no cdigo a ser compilado, ele deve substitu-lo por valor_da_constante. Toda vez que o pr-processador encontrar nome_da_macro(parmetros), ele deve substituir por expresso_de_substituio, tambm substituindo os parmetros encontrados na expresso de substituio; funciona mais ou menos como uma funo. Veja o exemplo para entender melhor. Exemplo 1: #include <stdio.h> #define PI #define VERSAO 3.1416 "2.02"

int main () { printf ("Programa verso %s\n", VERSAO); printf ("O numero pi vale: %f\n", PI); return 0; } Exemplo 2: #define max(A, B) ((A > B) ? (A) : (B)) #define min(A, B) ((A < B) ? (A) : (B)) ... x = max(i, j); y = min(t, r);

Pr-processador Aqui, a linha de cdigo: x = max(i, j); ser substituda pela linha: x = ((i) > (j) ? (i) : (j));. Ou seja, atribuiremos a x o maior valor entre i ou j. Quando voc utiliza a diretiva #define, nunca deve haver espaos em branco no identificador (o nome da macro). Por exemplo, a macro #define PRINT (i) printf(" %d \n", i) no funcionar correctamente porque existe um espao em branco entre PRINT e (i).

58

#undef
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 no conhecer mais esta macro.

#ifdef e #ifndef
O pr-processador tambm tem estruturas condicionais. No entanto, como as diretivas so processadas antes de tudo, s podemos usar como condies expresses que envolvam constantes e smbolos do pr-processador. A estrutura ifdef a mais simples delas: #ifdef nome_do_smbolo cdigo ... #endif O cdigo entre as duas diretivas s ser compilado se o smbolo (ou constante) nome_do_smbolo j tiver sido definido. H tambm a estrutura ifndef, que executa o cdigo se o smbolo no tiver sido definido. Lembre que o smbolo deve ter sido definido atravs da diretiva #define.

#if
A diretiva #if tem a seguinte forma geral: #if expresso cdigo ... #endif A sequncia de declaraes ser compilada apenas se a expresso fornecida for verdadeira. muito importande ressaltar que a expresso fornecida no pode conter nenhuma varivel, apenas valores constantes e smbolos do pr-processador.

#else
A diretiva #else funciona como na estrutura de bloco if (condio) {...} else {...}: #if expresso /* ou #ifndef expresso */ cdigo /* ser executado se a expresso for verdadeira */ #else cdigo /* ser executado se a expresso for falsa */ #endif Um exemplo:

Pr-processador #define WINDOWS ... /* cdigo */ ... #ifdef WINDOWS #define CABECALHO "windows_io.h" #else #define CABECALHO "unix_io.h" #endif #include CABECALHO

59

#elif
A diretiva #elif serve para implementar uma estrutura do tipo if (condio) {...} else if (condio) {...}. Sua forma geral : #if expresso_1 cdigo #elif expresso_2 cdigo #elif expresso_3 cdigo . . . #elif expresso_n cdigo #endif Podemos tambm misturar diretivas #elif com #else; obviamente, s devemos usar uma diretiva #else e ela deve ser a ltima (antes de #endif).

Usos comuns das diretivas


Um uso muito comum das diretivas de compilao em arquivos-cabealho, que s precisam/devem ser includos uma vez. Muitas vezes inclumos indiretamente um arquivo vrias vezes, pois muitos cabealhos dependem de outros cabealhos. Para evitar problemas, costuma-se envolver o arquivo inteiro com um bloco condicional que s ser compilado se o arquivo j no tiver includo. Para isso usamos um smbolo baseado no nome do arquivo. Por exemplo, se nosso arquivo se chama "cabecalho.h", comum usar um smbolo com o nome CABECALHO_H: #ifndef CABECALHO_H #define CABECALHO_H . . . #endif Se o arquivo ainda no tiver sido includo, ao chegar na primeira linha do arquivo, o pr-processador no encontrar o smbolo CABECALHO_H, e continuar a ler o arquivo, o que lhe far definir o smbolo. Se tentarmos incluir novamente o arquivo, o pr-processador pular todo o contedo pois o smbolo j foi definido.

Pr-processador

60

Concatenao
O preprocessador C oferece duas possibilidades para manipular uma cadeia de caracteres . A primeira usando o operador # que permite substituir a grafia de um parmetro .

#include<stdio.h> int main (void) {

/* mad equivale a "mad" */ #define String(mad) #mad printf ( String( Estou aqui ) "\n" ); } A segunda usando o operador ## que serve para concatenar vrios parmetros . Ex: ban##ana igual a banana .

#include<stdio.h> int main (void) { int teste = 1000 ; #define CONCAT(x, y) x##y /* igual a "tes" + "te" */ printf (" %i \n", CONCAT ( tes, te ) ); }

Bibliotecas

61

Bibliotecas
Bibliotecas
Bibliotecas so conjuntos de funes que foram feitas por algum e que podem ser usadas por outros programas sem que nos preocupemos com o cdigo dessas funes. Alm da vantagem de organizar o cdigo, bibliotecas tambm tm a vantagem de poderem ser utilizadas em vrios programas sem necessidade de copiar grandes trechos de cdigo; basta dizer ao compilador que queremos adicionar aquela biblioteca ao executvel. Por exemplo, vamos tentar criar a nossa prpria biblioteca, com duas funes: uma para gerar nmeros (pseudo-)aleatrios e uma para calcular o valor de pagamento de uma amortizao com juros compostos. Tambm incluiremos uma funo para gerar um nmero inicial a partir da hora atual, o que far com que as seqncias de nmeros no sejam sempre as mesmas. Chamaremos a biblioteca de teste1. #include <math.h> #include <time.h> int rand_seed = 10; /* Gerador de nmeros pseudo-aleatrios */ int rand () { rand_seed = rand_seed * 1103515245 + 12345; return (unsigned int) (rand_seed / 65536) % 32768; } void init_seed () { rand_seed = time (NULL); } /* Clculo do valor de cada pagamento de uma amortizao * Dados: vp = valor presente; * n = nmero de pagamentos; * i = taxa de juros (em formato decimal) */ double vf (double vp, int n, double i) { return (vp * i * pow (1 + i, n - 1) / (pow (1 + i, n) - 1)); } As linhas acima so o arquivo do cdigo da nossa biblioteca. Abaixo est o cdigo de um programa que testar essa biblioteca. Lembre-se de que os dois trechos devem estar em arquivos separados. #include <stdio.h>

Bibliotecas int main() { int r1, r2, n_pgtos; double a_vista, juros, v_pgto; r1 = rand (); r2 = rand (); printf ("Nmeros aleatrios: %d, %d\n\n", r1, r2); printf (" Valor vista: "); scanf ("%lf", &a_vista); printf ("Nmero de pagamentos: "); scanf ("%d", &n_pgtos); printf (" Taxa de juros: "); scanf ("%lf", &juros); juros /= 100; /* converte a porcentagem em nmero */ v_pgto = vf (a_vista, n_pgtos, juros); printf ("Valor de cada pagamento: %lf\n", v_pgto); return 0; } Algo que voc deve ter notado que nesse arquivo no demos nenhuma informao sobre as funes vf e rand nele usadas. Realmente, se voc tentar compilar o cdigo como est, o compilador dar um aviso; mas ao tentar criar o executvel, o montador no poder continuar pois no recebeu nenhuma informao sobre onde as funes esto. Para isso, precisamos realizar trs passos adicionais antes de compilar o programa teste: 1. Fazer um arquivo-cabealho com informaes sobre as funes. Esse arquivo ser incluido com a diretiva #include, da mesma maneira que cabealhos padro como "stdio.h" ou "math.h". 2. Compilar a biblioteca separadamente. 3. Instruir o compilador/montador a procurar pela biblioteca ao compilar o programa teste.

62

O arquivo-cabealho
Arquivos-cabealho so arquivos que contm informaes que servem para o compilador reconhecer funes "VER : convenes para chamadas a funes ou calling convention", macros, tipos de dados e variveis que no esto no arquivo sendo compilado. Esses arquivos costumam ter a extenso ".h" o caso, por exemplo, dos cabealhos padro stdio.h e math.h. A letra H usada pois a inicial de header (cabealho em ingls). Em uma biblioteca, os cabealhos contm, os prottipos das funes disponibilizadas pela biblioteca e, quando necessrio, sobre os tipos de estruturas usados. Bibliotecas mais complexas costumam dividir essas funes entre vrios arquivos. Para fazer nosso prprio cabealho, precisamos colocar as declaraes das funes disponveis na biblioteca: int rand (); void init_seed (); double vf (double, int, double);

Bibliotecas Se voc se lembra da ltima lio, poder sugerir que coloquemos algumas linhas a mais: #ifndef _TESTE1_H #define _TESTE1_H int rand (); void init_seed (); double vf (double, int, double); #endif Agora, sempre que precisarmos usar a biblioteca teste1, basta incluir o arquivo teste1.h no incio do nosso programa: #include "teste1.h"

63

Note que se o cabealho estiver instalado nos diretrios padro do compilador ou do sistema, voc deve trocar as aspas pelos sinais de menor/maior (< ... >).

Compilao da biblioteca
Tendo salvo o cdigo da biblioteca no arquivo teste1.c, voc deve compilar a biblioteca.

No GCC
Compile o arquivo-fonte normalmente, mas sem gerar o executvel: gcc -c teste1.c -o libteste1.o Crie o arquivo da biblioteca com o comando ar. Voc ainda no o conhece, mas a sintaxe simples: basta digitar ar rv, seguido do nome do arquivo da biblioteca e depois dos nomes dos arquivos-objeto a serem includos (separados por espaos). No GCC, as bibliotecas estticas costumam ter o nome "libnome.a". ar rv libteste1.a libteste1.o

No MS Visual C++
No Visual C++, o nome padro das bibliotecas "nome.lib", assim como em vrios outros compiladores para Windows. Nele, os comandos correspondentes aos dois passos acima so: cl /c teste1.c lib /out:teste1.lib teste1.obj

Compilao do programa
Aps criar o arquivo objeto libteste1.o com o comando ( gcc -c teste1.c -o libteste1.o ) e a biblioteca esttica com o comando "ar" , voc deve instruir o compilador com as opes de edio de links para poder inclu-la no seu programa: No GCC: gcc main.c -L. -l libteste1.a -o main.bin -lm

Note as opes que voc no conhecia: -L e -l . A primeira indica em que diretrio deve ser procurada a biblioteca; o ponto indica o diretrio atual. Se essa opo for omitida, o compilador procurar apenas nos diretrios padro. A segunda uma opo do editor de links indicando uma biblioteca a ser includa; o compilador procurar

Bibliotecas pelo arquivo adicionando o prefixo lib e a extenso .a, da a necessidade de dar o nome "libteste1.a" biblioteca. Mais bibliotecas podem ser includas como a -lm que neste caso serve para chamar a biblioteca math do math.h, sem este comando ele poder apresentar um erro na hora da compilao. No Visual C++: link /out:main.exe main.obj teste1.lib Note que nesse caso simplesmente especificamos os arquivos que devem ser montados. O diretrio de procura pode ser especificado pela opo /libpath:diretrio.

64

Entrada e sada em arquivos


Trabalhando com arquivos
J vimos como podemos receber e enviar dados para usurio atravs do teclado e da tela; agora veremos tambm como ler e gravar dados em arquivos, o que alis muito importante ou at essencial em muitas aplicaes. Assim como as funes de entrada/sada padro (teclado e tela), as funes de entrada/sada em arquivos esto declaradas no cabealho stdio.h que significa "STanDard Input-Output". Alis, as funes para manipulao de arquivos so muito semelhantes s usadas para entrada/sada padro. Como j dissemos na seo sobre a entrada e sada padres, a manipulao de arquivos tambm se d por meio de fluxos (streams). Na manipulao de um arquivo, h basicamente trs etapas que precisam ser realizadas: 1. abrir o arquivo; 2. ler e/ou gravar os dados desejados; 3. fechar o arquivo. Em C, todas as operaes realizadas com arquivos envolvem seu identificador de fluxo, que uma varivel do tipo FILE * (sobre o qual no cabe agora falar). Para declarar um identificador de fluxo, faa como se fosse uma varivel normal: FILE *fp; // no se esquea do asterisco!

Abrindo e fechando um arquivo


No surpreendentemente, a primeira coisa que se deve fazer para manipular um arquivo abri-lo. Para isso, usamos a funo fopen(). Sua sintaxe :

FILE *fopen (char *nome_do_arquivo, char *modo_de_acesso);


O nome do arquivo deve ser uma string ou com o caminho completo (por exemplo, /usr/share/appname/app.conf ou C:\Documentos\nomes.txt) ou o caminho em relao ao diretrio atual (nomes.txt, ../app.conf) do arquivo que se deseja abrir ou criar. O modo de acesso uma string que contm uma seqncia de caracteres que dizem se o arquivo ser aberto para gravao ou leitura. Depois de aberto o arquivo, voc s poder executar os tipos de ao previstos pelo modo de acesso: no poder ler de um arquivo que foi aberto somente para escrita, por exemplo. Os modos de acesso esto descritos na tabela a seguir.

Entrada e sada em arquivos

65

Modo r r+ w

Significado Abre o arquivo somente para leitura. O arquivo deve existir. (O r vem do ingls read, ler) Abre o arquivo para leitura e escrita. O arquivo deve existir. Abre o arquivo somente para escrita no incio do arquivo. Apagar o contedo do arquivo se ele j existir, criar um arquivo novo se no existir. (O w vem do ingls write, escrever) Abre o arquivo para escrita e leitura, apagando o contedo pr-existente. Abre o arquivo para escrita no final do arquivo. No apaga o contedo pr-existente. (O a vem do ingls append, adicionar, apender) Abre o arquivo para escrita no final do arquivo e leitura.

w+ a a+

Em ambientes DOS/Windows, ao ler arquivos binrios (por exemplo, programas executveis ou certos tipos de arquivos de dados), deve-se adicionar o caractere "b" ao final da string de modo (por exemplo, "wb" ou "r+b") para que o arquivo seja lido/gravado corretamente. Isso necessrio porque no modo texto (o padro quando no adicionado o b) ocorrem algumas tradues de caracteres (por exemplo, a terminao de linha "\r\n" substituda apenas por "\n" na leitura) que poderiam afetar a leitura/gravao dos arquivos binrios (indevidamente inserindo ou suprimindo caracteres). O valor de retorno da funo fopen() muito importante! Ele o identificador do fluxo que voc abriu e s com ele que voc conseguir ler e escrever no arquivo aberto. Se houver um erro na abertura/criao do arquivo, a funo retornar o valor NULL. O erro geralmente acontece por duas razes: O arquivo no existe, caso tenha sido requisitado para leitura. O usurio atual no tem permisso para abrir o arquivo com o modo de acesso pedido. Por exemplo, o arquivo somente-leitura, ou est bloqueado para gravao por outro programa, ou pertence a outro usurio e no tem permisso para ser lido por outros. Ao terminar de usar um arquivo, voc deve fech-lo. Isso feito pela funo fclose():

int fclose (FILE *fluxo);


O nico argumento o identificador do fluxo (retornado por fopen). O valor de retorno indica o sucesso da operao com o valor zero.

Exemplo
Um pequeno exemplo apenas para ilustrar a abertura e fechamento de arquivos: #include <stdio.h> int main() { FILE *fp; fp = fopen ("README", "w"); if (fp == NULL) { printf ("Houve um erro ao abrir o arquivo.\n"); return 1; } printf ("Arquivo README criado com sucesso.\n"); fclose (fp); return 0; }

Entrada e sada em arquivos

66

Arquivos pr-definidos
Na biblioteca padro do C, existem alguns fluxos pr-definidos que no precisam (nem devem) ser abertos nem fechados: stdin: dispositivo de entrada padro (geralmente o teclado) stdout: dispositivo de sada padro (geralmente o vdeo) stderr: dispositivo de sada de erro padro (geralmente o vdeo) stdaux: dispositivo de sada auxiliar (em muitos sistemas, associado porta serial) stdprn: dispositivo de impresso padro (em muitos sistemas, associado porta paralela)

Escrevendo em arquivos
Para escrever em arquivos, h quatro funes, das quais trs so anlogas s usadas para sada padro:
Sada padro Arquivos putchar puts printf N/A fputc fputs fprintf fwrite Explicao Imprime apenas um caractere. Imprime uma string diretamente, sem nenhuma formatao. Imprime uma string formatada. Grava dados binrios para um arquivo.

A seguir apresentamos os prottipos dessas funes:

void fputc (int caractere, FILE *fluxo); void fputs (char *string, FILE *fluxo); void fprintf (FILE *fluxo, char *formatao, ...); int fwrite (void *dados, int tamanho_do_elemento, int num_elementos, FILE *fluxo);
Sintaxe quase igual de printf(); s necessrio adicionar o identificador de fluxo no incio.

fwrite
Essa funo envolve os conceitos de ponteiro e vetor, que s sero abordados mais tarde.

Lendo de arquivos
Novamente, h quatro funes, das quais trs se assemelham s usadas para a sada padro:
Sada padro Arquivos getchar gets scanf N/A fgetc fgets fscanf fread Explicao Recebe apenas um caractere. L uma string (geralmente uma linha inteira). Recebe uma string formatada. L dados binrios de um arquivo.

int fgetc (FILE *fluxo); void fgets (char *string, int tamanho, FILE *fluxo); void fscanf (FILE *fluxo, char *formatao, ...); int fread (void *dados, int tamanho_do_elemento, int num_elementos, FILE *fluxo);
Este mdulo tem a seguinte tarefa pendente: criar exemplos de uso das funes

Entrada e sada em arquivos

67

fgets
Ao chamar a funo fgets(), voc deve fornecer o ponteiro para a string onde os dados lidos devem ser guardados, alm do tamanho mximo dos dados a serem lidos (para que a memria reservada string no seja ultrapassada).

fscanf
Sintaxe quase igual de scanf(); s necessrio adicionar o identificador de fluxo no incio.

fread
Essa funo envolve os conceitos de ponteiro e vetor, que s sero abordados mais tarde.

Outras funes
Funo feof ferror fseek rewind Explicao Detecta se o final do arquivo j foi atingido Verifica se a ltima operao de leitura ou escrita do fluxo foi executada com sucesso Movimenta o indicador de posio dentro do arquivo de um fluxo especificado Volta para o comeo do arquivo de um fluxo

remove Remove um arquivo especificado

Vetores
Vetores
Vetores, tambm chamados arrays (do ingls)ou arranjo ou ainda matrizes, so uma maneira de armazenar vrios dados num mesmo nome de varivel atravs do uso de ndices numricos. Em C, vetores devem sempre conter dados do mesmo tipo de varivel. Declaramos vetores de maneira muito semelhante declarao de variveis normais. A nica diferena que depois do nome da varivel deve ser informada a quantidade de elementos do vetor. Para declarar um vetor chamado vetor, com cinco elementos inteiros, escrevemos: int vetor[5]; Note que a quantidade de elementos de um vetor no pode ser alterada depois que o vetor for declarado. Para criar vetores de tamanho dinmico, podemos usar ponteiros, que sero abordados mais adiante. Da mesma maneira que podemos inicializar uma varivel junto com sua declarao, podemos usar as chaves ({}) para inicializar um array. int vetor[5] = {17, 42, 9, 33, 12}; Para fazer referncia a um valor de um elemento contido em um vetor, usamos a notao vetor[ndice], que serve tanto para obter quanto para definir o valor de um elemento especfico, dada sua posio. Note que os elementos so numerados a comear do zero, e, portanto, se o nmero de elementos , o ndice ou posio do ltimo elemento ser vetor[0] = 3; int x = vetor[2]; .

Vetores int y = vetor[5]; // ERRO! Repare em que a ltima linha contm um erro: ela referencia um elemento do vetor que no existe. No entanto, o compilador no se recusar a compilar esse cdigo; dar apenas um aviso. Se essa linha for executada, a varivel y receber um valor que no tem nada a ver com o vetor.

68

Abreviando as declaraes
Ao inicializar um vetor com vrios valores, pode ser trabalhoso contar todos os valores para colocar o tamanho do vetor na declarao. Por isso, em C podemos omitir o nmero de elementos quando os valores so inicializados; o tamanho do vetor ser o nmero de valores inicializados. Por exemplo, as duas notaes abaixo so equivalentes: int valores[5] = {1, 2, 3, 4, 5}; int valores[] = {1, 2, 3, 4, 5};

Exemplo de Aplicao de Vetores


O cdigo abaixo de um programa que recebe 5 nmeros inteiros e informa qual destes maior. #include <stdio.h> #include <stdlib.h> #include <conio.h> int main(void) { int vetor[5]; int x, y; printf ("digite 5 numeros\n"); for (y = 0; y < 5; y++) /*Este laco faz o scan de cada elemento do vetor*/ { scanf("%d", &vetor[y] ); } y = 1; x = vetor[0]; while (y < 5) /*Este laco compara cada elemento do vetor*/ { if (vetor[y] > x) { x = vetor[y]; } y++; } printf("\n O maior numero que voce digitou foi %d .\n",x); getch (); return 0; }

Vetores

69

Vetores multidimensionais (matrizes)


Em C, matrizes so vetores que armazenam vetores. Ou seja, vetor com vrias dimenses.

Argumentos na funo main


Na seo Funes, dissemos que a funo main() aceita dois argumentos, mas no falamos mais pois um deles envolve o conceito de vetores. Agora iremos falar mais sobre eles. Os dois argumentos de main() indicam os argumentos dados para o programa na linha de comando, na forma de um vetor de strings.

Strings
Strings
Strings (Ingls) so cadeias ou seqncias ordenadas de caracteres. Na verdade j trabalhamos com strings neste livro, mas preferimos deixar maiores explicaes para um momento em que j tivesse sido introduzido o conceito de vetor. A linguagem C, ao contrrio de outras linguagens de programao, no possui um tipo de dados correspondente s strings; no lugar, usam-se vetores (e ponteiros, como veremos mais adiante). Em C, strings so vetores de caracteres terminados pelo caractere nulo ('\0'). Por exemplo: char nome[] = {'P', 'e', 'd', 'r', 'o', '\0'}; No entanto, escrever strings dessa maneira muito trabalhoso; por isso, foi criada uma notao abreviada que equivale notao acima e elimina a necessidade de colocar o caractere terminador: char nome[] = "Pedro"; Assim como nos vetores, podemos acessar e modificar elementos individuais de uma string. Podemos tambm diminuir o tamanho de uma string: uma vez que a nica marcao do tamanho o terminador \0, colocar um terminador em outro local determinar o novo final da string. No entanto, aumentar o tamanho da string mais difcil; isso ficar para outra seo. Ateno ao usar-se acentos numa string. Como existem diferentes formas de codificar caracteres acentuados, o tratamento de uma string do tipo: char nome[] = "Joo"; pode ser diferente de uma mquina para outra. Neste captulo no sero tratados acentos, este assunto ser abordado mais adiante.

Strings

70

Funes da biblioteca padro


A biblioteca padro fornece vrias funes teis para manipular strings. A seguir mostraremos algumas delas. Para us-las, voc deve incluir o cabealho string.h no incio dos seus arquivos.

strlen
strlen retorna o tamanho, em caracteres, de uma string dada. Na verdade o strlen() procura o terminador de string e calcula a distncia dele ao incio da string. Por exemplo: char nome[] = "Maria da Silva"; int s = strlen (nome); // s conter o valor 14 /*TL'*/

strcpy
strcpy copia o contedo de uma string para outra e coloca um terminador de string. Sua sintaxe strcpy (destino, origem). char nome[] = "Clarice Lispector"; char nome2[] = "Oswald de Andrade"; strcpy (nome, nome2); // agora nome conter "Oswald de Andrade" Tome cuidado com strcpy(), pois se a string a ser copiada for maior que a string de destino, provavelmente voc gravar dados em lugares indesejados um problema conhecido como estouro de buffer. Para evitar esse problema, use a funo strncpy, que recebe um terceiro argumento que corresponde ao nmero mximo de caracteres a serem copiados: char msg[] = "Bom dia!"; char nome[] = "Maria da Silva"; strncpy (msg, nome, strlen(msg)); // agora msg conter "Maria da"

strcat
strcat concatena duas strings, adicionando o contedo da segunda ao final da primeira, alm do terminador (\0). Note que a primeira string deve ter espao suficiente para conter a segunda, para que no ocorra um "estouro de buffer". Por exemplo: char nome[50] = "Maria"; char sobrenome[] = " da Silva"; strcat (nome, sobrenome); // agora nome contm "Maria da Silva" Analogamente funo strncpy, existe tambm a funo strncat, onde o nmero mximo de caracteres a serem copiados o terceiro argumento.

Strings

71

strcmp
Se voc tentar criar duas strings com o mesmo contedo e compar-las como faria como nmeros, ver que elas "no so iguais". Isso ocorre porque, na verdade, o que est sendo comparado so os endereos de memria onde esto guardadas as strings. Para comparar o contedo de duas strings, voc deve usar a funo strcmp (ou suas variantes):

int strcmp (char *s1, char *s2);


O valor de retorno : menor que zero se s1 for menor que s2; igual a zero se s1 e s2 so iguais; maior que zero se s1 for maior que s2. Costuma parecer estranho dizer que uma string menor ou maior que outra; na verdade essa comparao entre a primeira letra que difere nas duas strings. Assim, se tivermos s1 = "abc" e s2 = "abd", diremos que s2 maior que s1, pois na primeira posio em que as duas strings diferem, a letra em s2 "maior". importante notar que a comparao feita por strcmp distingue maisculas de minsculas. Isto , as strings "ABC" e "abc" no so iguais para essa funo. As variantes mais usadas de strcmp so: strncmp - compara apenas os n primeiros caracteres das duas strings, sendo n um terceiro argumento. stricmp - compara duas strings sem distino entre maisculas e minsculas. A sintaxe igual de strcmp. Essa funo no faz parte da biblioteca padro, mas comumente encontrada como extenso particular de vrias delas.

strrchr
strrchr Retorna um ponteiro sobre a ultima ocorrncia de c de uma string apontada por s se no retorna NULL . Sua sintaxe strrchr(const char *s, int c);. Exemplo: char path[50] = "/teste/string"; char *p = strrchr(path, '/'); *p++; printf("Resultado: %s \n", p );

memcpy
Sintaxe:

#include <string.h> void *memcpy (void *dest, const void *srce, size_t n); Descrio: Copiar um bloco de n octetos de srce para dest. Ateno:Se as regies de srce e dest se sobreporem o comportamento da funo imprevisvel. Valor de retorno : memcpy retorna o valor de dest . Ex: #include <stdio.h> #include <string.h>

Strings int main() { int tab[2][5] = { { 1, 2, 3, 4, 5, {11, 12, 13, 14, 15} }; int temp[2][5]; memcpy(temp, tab, sizeof(tab)); puts("Resultado:\n"); printf("temp[1][4] = %d\n", temp[1][4]); return 0; }

72

memset
Sintaxe:

#include <string.h> void *memset (void *buffer, int c, size_t n); Descrio: memset inicializa n octetos do buffer com o inteiro c. Valor de retorno : O valor do buffer. Ex: #include <stdio.h> #include <string.h> int main() { char buf[] = "W.I.K.I."; printf("Buf antes 'memset': %s\n", buf); memset(buf, '*', strlen(buf)); printf("Buf depois 'memset': %s\n", buf); return 0; }

Ponteiros

73

Ponteiros
Poderamos escrever um livro inteiro sobre ponteiros, pois o contedo demasiadamente extenso. Por esse motivo este assunto foi dividido em bsico, intermedirio e avanado, assim o leitor poder fazer seus estudos conforme suas necessidades. recomendvel para quem est vendo programao pela primeira vez aqui que no se preocupe com o avanado sobre ponteiros por enquanto.

Bsico
O que um ponteiro?
Um ponteiro simplesmente uma varivel que armazena o endereo de outra varivel. Um exemplo : O que o ponteiro de um relgio? o que aponta para as horas, minutos ou segundos. Um ponteiro aponta para algo. Em programao, temos as variveis armazenadas na memria, e um ponteiro aponta para um endereo de memria. Imagine as variveis como documentos, a memria do computador como pastas para guardar os documentos, e o ponteiro como atalhos para as pastas. No se desespere caso no consiga entender num primeiro momento, o conceito fica mais claro com a prtica.

Declarando e acessando ponteiros


Um ponteiro, como qualquer varivel, deve ter um tipo, que o tipo da varivel para a qual ele aponta. Para declarar um ponteiro, especificamos o tipo da varivel para a qual ele aponta e seu nome precedido por asterisco: int ponteiro int *ponteiro ; ; /* declara uma varivel comum do tipo inteiro */ /* declara um ponteiro para um inteiro */

Tome cuidado ao declarar vrios ponteiros em uma linha, pois o asterisco deve vir antes de cada nome de varivel. Note os trs exemplos: int p, q, r; // estamos a declarar trs variveis comuns int *p, q, r; // cuidado! apenas p ser um ponteiro! int *p, *q, *r; // agora sim temos trs ponteiros Para acessar o endereo de uma varivel, utilizamos o operador & (E comercial), chamado "operador de referncia" ou "operador de endereo". Como o nome sugere, ele retorna o endereo na memria de seu operando. Ele unrio e deve ser escrito antes do seu operando. Por exemplo, se uma varivel nome foi guardada no endereo de memria 1000, a expresso &nome valer 1000.
Esquema de um ponteiro

Com isso, fica claro o esquema ao lado: a varivel a contm o valor 1234 e o ponteiro p contem o endereo de a (&a). Para atribuir um valor ao ponteiro, usamos apenas seu nome de varivel. Esse valor deve ser um endereo de memria, portanto obtido com o operador &:

Ponteiros int a; int *p; p = &a; Claro que tambm podemos inicializar um ponteiro: int *p = &a; Nos dois casos, o ponteiro p ir apontar para a varivel a. Mas, como o ponteiro contm um endereo, podemos tambm atribuir um valor varivel guardada nesse endereo, ou seja, varivel apontada pelo ponteiro. Para isso, usamos o operador * (asterisco), que basicamente significa "o valor apontado por". Ex: int a ; int *p = &a ; *p = 20 ; Para ver o resultado : printf (" a :%i\n", a); printf ("*p :%i\n", *p); Cuidado! Voc nunca deve usar um ponteiro sem antes inicializ-lo; esse um erro comum. Inicialmente, um ponteiro pode apontar para qualquer lugar da memria do computador. Ou seja, ao tentar ler ou gravar o valor apontado por ele, voc estar manipulando um lugar desconhecido na memria! int *p; *p = 9; Nesse exemplo, estamos a manipular um lugar desconhecido da memria! Se voc tentar compilar esse cdigo, o compilador dever dar uma mensagem de aviso; durante a execuo, provavelmente ocorrer uma falha de segmentao (erro que ocorre quando um programa tenta acessar a memria alheia). Um exemplo mais elaborado:

74

#include <stdio.h> int main() { int i = 10 ; int *p ; p = &i ; *p = 5 ; printf ("%d\t%d\t%p\n", i, *p, p); return 0; } Primeiramente declaramos a varivel i, com valor 10, e o ponteiro p, que apontar para o endereo de i. Depois, guardamos o valor 5 no endereo apontado por p. Se voc executar esse exemplo, ver algo parecido com: 5 5 0022FF74

Ponteiros claro que os valores de i e de *p sero iguais, j que p aponta para i. O terceiro valor o endereo de memria onde est i (e, conseqentemente, o prprio valor de p), e ser diferente em cada sistema.

75

Ponteiro e NULL
Uma falha de segmentao ou em ingls (segmentation fault) ocorre quando um programa tenta aceder a um endereo na memria que est reservado ou que no existe.Nos sistemas Unix quando acontece este tipo de erro o sinal SIGSEGV enviado ao programa indicando uma falha de segmentao. Aqui o ponteiro contem null, definido com o endereo (0x00000000) que causa uma falha de segmentao .

/*Endereo invalido*/ #define null ( (char*) 0 ) int main(void){ int a = 5; int *p = null; *p = a; } Esse programa termina anormalmente. Voc esta tentando colocar o valor 5 em um endereo invlido. Para que isso no acontea o ponteiro deve ser inicializado com um endereo valido. Exemplo :

#include <stdio.h> #include <errno.h> #include <stddef.h> int main(void){ int a = 5; int *p = NULL; p = &a; /* A operao no permitida */ if(p == NULL) return -EPERM ; else{ printf("Endereo a disposio:%p\n", p ); *p = a; /* Pode colocar 5 */ } } NULL est definido dentro do cabealho stddef.h . Aqui voc no espera que o programa acabe com algum tipo de mgica, se NULL igual ao valor do ponteiro isso significa que no foi encontrado nem um endereo acessvel, ento voc para. Caso contrario voc estar executando uma operao que no permitida. Ou colocar 5 em (0x00000000) .

Ponteiros

76

Mais operaes com ponteiros


Suponhamos dois ponteiros inicializados p1 e p2. Podemos fazer dois tipos de atribuio entre eles: p1 = p2; Esse primeiro exemplo far com que p1 aponte para o mesmo lugar que p2. Ou seja, usar p1 ser equivalente a usar p2 aps essa atribuio. *p1 = *p2; Nesse segundo caso, estamos a igualar os valores apontados pelos dois ponteiros: alteraremos o valor apontado por p1 para o valor apontado por p2. Agora vamos dar mais alguns exemplos com o ponteiro p: p++; Aqui estamos a incrementar o ponteiro. Quando incrementamos um ponteiro ele passa a apontar para o prximo valor do mesmo tipo em relao ao valor para o qual o ponteiro aponta. Isto , se temos um ponteiro para um inteiro e o incrementamos, ele passa a apontar para o prximo inteiro. Note que o incremento no ocorre byte-a-byte! (*p)++; Aqui, colocamos *p entre parnteses para especificar que queremos alterar o valor apontado por p. Ou seja, aqui iremos incrementar o contedo da varivel apontada pelo ponteiro p. *p++ Neste caso, o efeito no to claro quanto nos outros exemplos. A precedncia do operador ++ sobre o operador * faz com que a expresso seja equivalente a *(p++). O valor atual de p retornado ao operador *, e o valor de p incrementado. Ou seja, obtemos o valor atual do ponteiro e j o fazemos apontar para o prximo valor. x = *(p + 15); Esta linha atribui a uma varivel x o contedo do dcimo-quinto inteiro adiante daquele apontado por p. Por exemplo, suponhamos que tivssemos uma srie de variveis i0, i1, i2, i15 e que p apontasse para i0. Nossa varivel x receberia o valor de i15. Tente acompanhar este exemplo dos dois tipos de atribuio de ponteiros: int *a, *b, c = 4, d = 2; a = &c; // a apontar para c b = &d; // b apontar para d *b = 8; // altero o valor existente na variavel d *a = *b; // copio o valor de d (apontado por b) // para c (apontado por a) *a = 1; // altero o valor da varivel c b = a; // b aponta para o mesmo lugar que a, // ou seja, para c *b = 0; // altero o valor de c

Ponteiros

77

Intermedirio
Ponteiro de estrutura
Para comear e deixar mais claro definimos uma estrutura simples com dois campos. struct { int i; double f; } minha_estrutura; O passo seguinte definir um ponteiro para essa estrutura. struct minha_estrutura *p_minha_estrutura; A partir do ponteiro podemos ter acesso a um campo da estrutura usando um seletor "->" (uma flecha). p_minha_estrutura-> i = 1; p_minha_estrutura-> f = 1.2; O mesmo resultado pode ser optido da seguinte forma. (*p_minha_estrutura).i = 1; (*p_minha_estrutura).f = 1.2;

Ponteiros como parmetros de funes


Comecemos por uma situao-problema: eu tenho 2 variveis e quero trocar o valor delas. Vamos comear com um algoritmo simples, dentro da funo main():

#include <stdio.h> int main() { int a = 5, b = 10, temp; printf ("%d %d\n", a, b); temp = a; a = b; b = temp; printf ("%d %d\n", a, b); return 0; } Esse exemplo funcionar exatamente como esperado: primeiramente ele imprimir "5 10" e depois ele imprimir "10 5". Mas e se quisermos trocar vrias vezes o valor de duas variveis? muito mais conveniente criar uma funo que faa isso. Vamos fazer uma tentativa de implementao da funo swap (troca, em ingls):

#include <stdio.h> void swap(int i, int j)

Ponteiros { int temp; temp = i; i = j; j = temp; } int main() { int a, b; a = 5; b = 10; printf ("%d %d\n", a, b); swap (a, b); printf ("%d %d\n", a, b); return 0; } No entanto, o que queremos no ir acontecer. Voc ver que o programa imprime duas vezes "5 10". Por que isso acontece? Lembre-se do escopo das variveis: as variveis a e b so locais funo main(), e quando as passamos como argumentos para swap(), seus valores so copiados e passam a ser chamados de i e j; a troca ocorre entre i e j, de modo que quando voltamos funo main() nada mudou. Ento como poderamos fazer isso? Como so retornados dois valores, no podemos usar o valor de retorno de uma funo. Mas existe uma alternativa: os ponteiros!

78

#include <stdio.h> void swap (int *i, int *j) { int temp; temp = *i; *i = *j; *j = temp; } int main () { int a, b; a = 5; b = 10; printf ("\n\nEles valem %d, %d\n", a, b); swap (&a, &b); printf ("\n\nEles agora valem %d, %d\n", a, b); return 0; }

Ponteiros Neste exemplo, definimos a funo swap() como uma funo que toma como argumentos dois ponteiros para inteiros; a funo faz a troca entre os valores apontados pelos ponteiros. J na funo main(), passamos os endereos das variveis para a funo swap(), de modo que a funo swap() possa modificar variveis locais de outra funo. O nico possvel inconveniente que, quando usarmos a funo, teremos de lembrar de colocar um & na frente das variveis que estivermos passando para a funo. Se voc pensar bem, j vimos uma funo em que passamos os argumentos precedidos de &: a funo scanf()! Por que fazemos isso? simples: chamamos a funo scanf() para que ela ponha nas nossas variveis valores digitados pelo usurio. Ora, essas variveis so locais, e portanto s podem ser alteradas por outras funes atravs de ponteiros! Quando uma funo recebe como parmetros os endereos e no os valores das variveis, dizemos que estamos a fazer uma chamada por referncia; o caso desse ltimo exemplo. Quando passamos diretamente os valores das variveis para uma funo, dizemos que uma chamada por valor; foi o caso do segundo exemplo.

79

Ponteiros e vetores
Em C, os elementos de um vetor so sempre guardados seqencialmente, a uma distncia fixa um do outro. Com isso, possvel facilmente passar de um elemento a outro, percorrendo sempre uma mesma distncia para frente ou para trs na memria. Dessa maneira, podemos usar ponteiros e a aritmtica de ponteiros para percorrer vetores. Na verdade, vetores so ponteiros um uso particular dos ponteiros. Acompanhe o exemplo a seguir.

#include <stdio.h> int main () { int i; int vetorTeste[3] = {4, 7, 1}; int *ptr = vetorTeste; printf("%p\n", vetorTeste); printf("%p\n", ptr); printf("%p\n", &ptr); for (i = 0; i < 3; i++) { printf("O endereo do ndice %d do vetor %p\n", i, &ptr[i]); printf("O valor do ndice %d do vetor %d\n", i, ptr[i]); } return 0; } Comeamos declarando um vetor com trs elementos; depois, criamos um ponteiro para esse vetor. Mas repare que no colocamos o operador de endereo em vetorTeste; fazemos isso porque um vetor j representa um endereo, como voc pode verificar pelo resultado da primeira chamada a printf(). Como voc j viu anteriormente neste captulo, podemos usar a sintaxe *(ptr + 1) para acessar o inteiro seguinte ao apontado pelo ponteiro ptr. Mas, se o ponteiro aponta para o vetor, o prximo inteiro na memria ser o prximo elemento do vetor! De fato, em C as duas formas *(ptr + n) e ptr[n] so equivalentes.

Ponteiros No necessrio criar um ponteiro para usar essa sintaxe; como j vimos, o vetor em si j um ponteiro, de modo que qualquer operao com ptr ser feita igualmente com vetorTeste. Todas as formas abaixo de acessar o segundo elemento do vetor so equivalentes: vetorTeste[1]; *(vetorTeste + 1); ptr[1]; *(ptr + 1) Veja mais este exemplo:

80

#include <stdio.h> int main() { int numbers[5]; int *p; int n; p = numbers; *p = 10; p++; *p = 20; p = &numbers[2]; *p = 30; p = numbers + 3; *p = 40; p = numbers; *(p + 4) = 50; for (n = 0; n < 5; n++) cout << numbers[n] << ", "; return 0; } Ele resume as vrias formas de acessar elementos de um vetor usando ponteiros.

Indexao estranha de ponteiros


o C permite fazer um tipo indexao de um vetor quando uma varivel controla seu ndice. O seguinte cdigo vlido e funciona: Observe a indexao vetor[i]. #include <stdio.h> int main () { int i; int vetor[10]; for (i = 0; i < 10; i++) { printf ("Digite um valor para a posicao %d do vetor: ", i + 1);

Ponteiros scanf ("%d", &vetor[i]); //isso equivalente a fazer *(x + i) } for (i = 0; i < 10; i++) printf ("%d\n", vetor[i]); return (0); } Essa indexao, apesar de estranha, funciona corretamente e sem aviso na compilao. Ela prtica, mas, para os iniciantes, pode parecer complicada. s treinar para entender.

81

Comparando endereos
Como os endereos so nmeros, eles tambm podem ser comparados entre si. Veja o exemplo a seguir, com efeito equivalente ao primeiro exemplo da seo anterior:

#include <stdio.h> int main() { int vetorTeste[3] = {4, 7, 1}; int *ptr = vetorTeste; int i = 0; while (ptr <= &vetorTeste[2]) { printf("O endereo do ndice %d do vetor %p\n", i, ptr); printf("O valor do ndice %d do vetor %d\n", i, *ptr); ptr++; i++; } return 0; } Esse programa incrementa o ponteiro enquanto esse endereo for igual (ou menor) ao endereo do ltimo elemento do vetor (lembre-se que os ndices do vetor so 0, 1 e 2).

Ponteiros

82

Avanado
Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

Este mdulo tem a seguinte tarefa pendente: mover o que de C++ para o livro de C++

Ponteiros para ponteiros


Acompanhe este exemplo: char a; char *b; char **c; a = b = c = **c 'z'; &a; &b; = 'M';

#include <stdlib.h> #include<stdio.h> int int int int int vInt = 10; *pInt = &vInt; **pInt2 = &pInt; ***pInt3 = &pInt2; x=0;

int main(void){ printf("+-----------------+\n"); printf("| vInt |\n"); printf("+-----------------+\n"); printf("|%17d|\n",vInt); printf("|%17p|<-+\n",&vInt); printf("+-----------------+ |\n"); printf("| pInt | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt); printf("|%17p|<-+\n",&pInt); printf("|%17d| |\n",*pInt); printf("+-----------------+ |\n"); printf("| pInt2 | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt2); printf("|%17p|<-+\n",&pInt2); printf("|%17d| |\n",**pInt2); printf("+-----------------+ |\n");

Ponteiros printf("| pInt3 | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt3); printf("|%17p|\n",&pInt3); printf("|%17d|\n",***pInt3); printf("+-----------------+\n"); ***pInt3 = 76; printf("+-----------------+\n"); printf("| vInt |\n"); printf("+-----------------+\n"); printf("|%17d|\n",vInt); printf("|%17p|<-+\n",&vInt); printf("+-----------------+ |\n"); printf("| pInt | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt); printf("|%17p|<-+\n",&pInt); printf("|%17d| |\n",*pInt); printf("+-----------------+ |\n"); printf("| pInt2 | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt2); printf("|%17p|<-+\n",&pInt2); printf("|%17d| |\n",**pInt2); printf("+-----------------+ |\n"); printf("| pInt3 | |\n"); printf("+-----------------+ |\n"); printf("|%17p|--+\n",pInt3); printf("|%17p|\n",&pInt3); printf("|%17d|\n",***pInt3); printf("+-----------------+\n");

83

// importante ressaltar que o valor de vInt foi modificado pois a indireo mltipla a modoficou. printf("+----------------------------------------------------------------+"); printf("| PROGRAMA FEITO POR : DANIEL CAMPOS DINIZ dinizdc@gmail.com |\n"); printf("+----------------------------------------------------------------+"); return 0; }

Ponteiros Perceba que temos dois "nveis": c aponta para b, e b aponta para a. Assim, para acessar a usando o ponteiro c, necessrio usar duas vezes o operador *: uma para obter o valor de b (cujo endereo est guardado em c), e a outra para obter o valor de a, apontado por b. Uma aplicao de ponteiros para ponteiros est nas strings, j que strings so vetores, que por sua vez so ponteiros. Um vetor de strings seria justamente um ponteiro para um ponteiro.
Este mdulo tem a seguinte tarefa pendente: Criar cdigo exemplo sobre ponteiro para ponteiro

84

Passando vetores como argumentos de funes


Os ponteiros podem ser passados como argumentos de funes. Parmetro ponteiro passando um array.

#include <stdio.h> void atribuiValores(int[], int); void mostraValores(int[], int); int main() { int vetorTeste[3]; // crio um vetor sem atribuir valores atribuiValores(vetorTeste, 3); mostraValores(vetorTeste, 3); return 0; } void atribuiValores(int valores[], int num) { for (int i = 0; i < num; i++) { printf("Insira valor #%d: ", i + 1); scanf("%d", &valores[i]); } } void mostraValores(int valores[], int num) { for (int i = 0; i < num; i++) { printf("Valor #%d: %d\n", i + 1, valores[i]); } } Repare que passamos dois parmetros para as funes: 1. O "nome" do vetor, que representa o seu endereo na memria. (Temos 3 maneiras para passar o endereo do vetor: diretamente pelo seu "nome", via um ponteiro ou pelo endereo do primeiro elemento.)

Ponteiros 2. Uma constante, que representa o nmero de elementos do vetor. Isso importante pois o C no guarda informaes sobre o tamanho dos vetores; voc no deve tentar alterar ou acessar valores que no pertencem ao vetor. claro que devemos passar o endereo do vetor (por "referncia"), pois os seus valores so alterados pela funo atribuiValores. De nada adiantaria passar o vetor por valor, pois o valor s seria alterado localmente na funo (como j vimos no caso de troca do valor de duas variveis). Por causa dessa equivalncia entre vetores e ponteiros, podemos fazer uma pequena alterao no prottipo (tanto na declarao quanto na definio) das funes atribuiValores e mostraValores, sem precisar alterar o cdigo interno dessas funes ou a chamada a elas dentro da funo main ? trocando void atribuiValores(int[], int); void mostraValores(int[], int); por void atribuiValores(int*, int); void mostraValores(int*, int); Para o compilador, voc no fez mudana alguma, justamente por conta dessa equivalncia. Em ambos os casos, foi passado o endereo do vetor para as funes.

85

Ponteiros para funes


Os ponteiros para funes servem, geralmente, para passar uma funo como argumento de uma outra funo. Neste exemplo

#include <stdio.h> int soma(int a, int b) { return (a + b); } int operacao(int x, int y, int (*func)(int,int)) { int g; g = (*func)(x, y); return (g); } int main () { int m; m = operacao(7, 5, soma); printf("%d\n", m); return 0; } Veja que criamos uma funo que retorna a soma dos dois inteiros a ela fornecidos; no entanto, ela no chamada diretamente. Ela chamada pela funo operacao, atravs de um ponteiro. A funo main passa a funo soma como

Ponteiros argumento para operacao, e a funo operacao chama essa funo que lhe foi dada como argumento. Note bem o terceiro argumento da funo operacao: ele um ponteiro para uma funo. Nesse caso, ele foi declarado como um ponteiro para uma funo que toma dois inteiros como argumentos e retorna outro inteiro. O * indica que estamos declarando um ponteiro, e no uma funo. Os parnteses em torno de *func so essenciais, pois sem eles o compilador entenderia o argumento como uma funo que retorna um ponteiro para um inteiro. A forma geral para declarar um ponteiro para uma funo : tipo_retorno (*nome_do_ponteiro)(lista de argumentos) Para chamar a funo apontada pelo ponteiro, h duas sintaxes. A sintaxe original (*nome_do_ponteiro)(argumentos); Se ptr um ponteiro para uma funo, faz bastante sentido que a funo em si seja chamada por *ptr. No entanto, a sintaxe mais moderna permite que ponteiros para funes sejam chamados exatamente da mesma maneira que funes: nome_do_ponteiro(argumentos); Por fim, para inicializar um ponteiro para funo, no precisamos usar o operador de endereo (ele j est implcito). Por isso, quando chamamos a funo operacao, no precisamos escrever &soma. Veja mais um exemplo na verdade, uma extenso do exemplo anterior:

86

#include <stdio.h> int soma(int a, int b) { return (a+b); } int subtracao(int a, int b) { return (a-b); } int (*menos)(int, int) = subtracao; int operacao(int x, int y, int (*func)(int,int)) { int g; g = func(x, y); return (g); } int main() { int m, n; m = operacao(7, 5, soma); n = operacao(20, m, menos);

Ponteiros printf("%d\n", n); return 0; } Aqui, criamos mais uma funo, subtracao, alm de criar um outro ponteiro para ela (uma espcie de "atalho"), menos. Na funo main, referimo-nos funo de subtrao atravs desse atalho. Veja tambm que aqui usamos a sintaxe moderna para a chamada de ponteiros de funes, ao contrrio do exemplo anterior.

87

Passagem de Parametros
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Passagem de Parmetros
Esta explicao para quem compila com o GNU gcc. O que so parmetros? Com os programas em interface grfica usa-se botes ou cones. Quando utiliza-se os parmetros com o console ou prompt os parmetros so reconhecidos como opes. Para quem usa sistemas do tipo Unix como o Linux, onde o console no banalizado como em outros SO's, mais fcil de se entender. Imagine que exista um programa cujo nome "Calcular" e que ele serve para executar operaes aritmticas. Pense agora na sua execuo no shell. $./Calcular restar

"Calcular" o nome, a "chamada" ao seu programa, enquanto que "restar" um parmetro, uma opo. Esse programa pode comportar vrios parmetros como somar, subtrair e multiplicar, por exemplo. Exemplo: /*----------------------------Parmetros.c------------------------------*/ #include <stdio.h> /* igual a int main(int argc, char *argv[]) */ int main(int argument_count, char *argument_value[]) { int i; printf("Nome do Programa :%s\n", argument_value[0] ); for (i = 1 ; i < argument_count; i++) printf("Parmetros passados %d : %s\n", i, argument_value[i]); /* De um enter no fim*/ } Para compilar: user@SO:/meu_diretotio$ gcc Parametros.c -o Argumentos Como diramos s passar alguns argumentos para o compilador ;) Examinando o cdigo

Passagem de Parametros Vamos dar uma olhada na funo main(int argc, char *argv[]) vocs podem remarcar os nomes: argc "argument count" : Conta o numero de argumentos incluindo o nome do programa. E argv "arguments values" : Vamos dizer que cada argv[] um nome de parmetro. Ento temos um que nos da a quantidade de parmetros e outro que nos da os nomes de cada parmetro ou opo. So nomes tradicionais eles podem ser modificado para outros nomes desde que os tipos continuem sendo os mesmos. Exe: NOME opao1 $./Calcular somar Aqui argc igual a 4. argv[] na realidade uma tabela de ponteiros exe: argv[0] e igual a '.' '/' 'C' 'a' 'l' 'c' 'u' 'l ' 'a' 'r' '/0' Calcular argv[1] e igual a 's' 'o' 'm' 'a' 'r' '/0' somar opao2 depois opao3 restar : : argc = 4 argv[] vai de argv[0]

88

argv[3]

Tipos de dados definidos pelo usurio


Tipos de dados definidos pelo usurio
Muitas vezes necessrio manipular dados complexos que seriam difceis de representar usando apenas os tipos primitivos (char, int, double, float). Para isso, h, em C, trs tipos de dados que podem ser definidos pelo usurio: estruturas (struct); unies (union); enumeraes (enum). As estruturas e unies so compostas por vrias variveis (escolhidas pelo programador), por isso so ditos definidos pelo usurio. J as enumeraes so, resumidamente, tipos cujos valores devem pertencer a um conjunto definido pelo programador.

Estruturas
Foi proposta a fuso deste mdulo com: Programar_em_C/Estruturas (discuta).

Uma estrutura (ou struct) um tipo de dados resultante do agrupamento de vrias variveis nomeadas, no necessariamente similares, numa s; essas variveis so chamadas membros da estrutura. Para declarar uma estrutura, usamos a palavra-chave struct, seguida do nome que se deseja dar estrutura (ao tipo de dados) e de um bloco contendo as declaraes dos membros. Veja um exemplo: struct mystruct { int a, b, c; double d, e, f; char string[25]; }; Este exemplo cria um tipo de dados denominado mystruct, contendo sete membros (a, b, c, d, e, f, string). Note que o nome mystruct o nome do tipo de dados, no de uma varivel desse tipo.

Tipos de dados definidos pelo usurio Um exemplo simples de aplicao de estruturas seria uma ficha pessoal que tenha nome, telefone e endereo; a ficha seria uma estrutura. Ou, mais amplamente, uma estrutura seria uma representao de qualquer tipo de dado definido por mais de uma varivel. Por exemplo, o tipo FILE* na verdade um ponteiro para uma estrutura que contm alguns dados que o sistema usa para controlar o acesso ao fluxo/arquivo. No necessrio, para a maioria dos programadores, conhecer a estrutura do tipo FILE.

89

Definindo o tipo
A definio de um tipo de estrutura feita com a palavra-chave struct, seguida do nome a ser dado ao tipo e de um bloco contendo as declaraes dos elementos da estrutura: struct nome_do_tipo { tipo_elem a; tipo_elem b, c; ... }; muito importante incluir o ponto-e-vrgula ao final do bloco!

Declarando
Para declarar uma varivel de um tipo j definido, fornecemos o nome do tipo, incluindo a palavra-chave struct: struct nome_do_tipo variavel; Tambm possvel condensar a definio do tipo e a declarao em um passo, substituindo o nome do tipo pela definio, sem o ponto-e-vrgula: struct mystruct { int a, b, c; double d, e, f; char string[25]; } variavel; Tambm possvel inicializar uma estrutura usando as chaves {} para envolver os elementos da estrutura, separados por vrgulas. Os elementos devem estar na ordem em que foram declarados, mas no obrigatrio inicializar todos; no entanto, para inicializar um elemento, todos os anteriores devem ser inicializados tambm. Por exemplo, poderamos declarar valores iniciais para a varivel acima da seguinte maneira: struct mystruct variavel = {4, 6, 5, 3.14, 2.718, 0.95, "Teste"}; struct mystruct v2 = {9, 5, 7};

Tipos de dados definidos pelo usurio

90

Inicializador designado
Para quem usa o C99 com o compilador GNU. Durante a inicializao de um estrutura possvel especificar o nome do campo com '.nome_do_campo =' antes do valor. Exemplo: struct mystruct v2 = {.a=9,.b=5,.c=7};

Acessando
Para acessar e modificar os membros de uma estrutura, usamos o operador de seleo. (ponto). esquerda do ponto deve estar o nome da varivel (estrutura) e direita, o nome do membro. Podemos usar os membros como variveis normais, inclusive passando-os para funes como argumentos: variavel.a = 5; variavel.f = 6.17; strcpy (variavel.string, "Bom dia"); printf ("%d %f %s\n", variavel.a, variavel.f, variavel.string);

Vetores de estruturas
Sendo as estruturas como qualquer outro tipo de dados, podemos criar vetores de estruturas. Por exemplo, suponha algum programa que funcione como um servidor e permita at 10 usurios conectados simultaneamente. Poderamos guardar as informaes desses usurios num vetor de 10 estruturas: struct info_usuario { int id; char nome[20]; long endereco_ip; time_t hora_conexao; }; struct info_usuario usuarios[10]; E, por exemplo, para obter o horrio em que o 2 usurio usurio se conectou, poderamos escrever usuarios[1].hora_conexao.

Atribuio e cpia
Podemos facilmente copiar todos os campos de uma estrutura para outra, fazendo uma atribuio simples como a de inteiros: struct int int }; ... struct struct b = a; // agora o ponto b tambm tem coordenadas (2, 3) ponto { x; y;

ponto a = {2, 3}; ponto b = {5, 8};

Tipos de dados definidos pelo usurio No entanto, devemos ter cuidado se a estrutura contiver campos ponteiros, pois, nesses casos, o que ser copiado o endereo de memria (e no o contedo daquele endereo). Por exemplo, se tivermos uma estrutura que comporta um inteiro e uma string, uma cpia sua conter o mesmo inteiro e um ponteiro para a mesma string, o que significa que alteraes na string da cpia sero refletidas tambm no original!

91

Passando para funes


J vimos acima que podemos normalmente passar membros de uma estrutura como argumentos de funes. Tambm possvel passar estruturas inteiras como argumentos: #include <stdio.h> struct ponto { int x; int y; }; void imprime_ponto (struct ponto p) { printf ("(%d, %d)\n", p.x, p.y); } int main () { struct ponto a = {3, 7}; imprime_ponto (a); return 0; } No entanto, h dois possveis problemas nisso: Alteraes nos membros da estrutura s tero efeito dentro da funo chamada, mas no na funo que a chamou. Isso ocorre pois a estrutura passada por valor (e no por referncia). Quando a estrutura contiver muitos elementos, a passagem por valor tornar-se- um processo de cpia de muitos dados. Por isso, de costume passar estruturas por referncia (como ponteiros), mesmo que a estrutura em questo seja pequena.

Unies
Foi proposta a fuso deste mdulo com: Programar em C/Unio (discuta).

Unies so parecidas com estruturas, mas h uma diferena fundamental: nas unies, todos os elementos ocupam o mesmo espao de memria. Por isso, s possvel acessar um elemento por vez, j que uma mudana em um elemento causar mudana em todos os outros. A definio e a declarao de unies igual das estruturas, trocando a palavra struct por union. H principalmente dois usos para as unies: economia de espao, j que guardam-se vrias variveis no mesmo espao; representao de uma informao de mais de uma maneira. Um exemplo disso so os endereos IP, que na biblioteca de sockets podem ser representados como um grupo de 4 octetos (char) ou como um nico valor inteiro (int). Isso feito com uma unio parecida com esta:

Tipos de dados definidos pelo usurio union ip_address { int s_long; char s_byte[4]; }; Dessa maneira, o endereo pode ser facilmente representado de maneira humanamente legvel (com 4 octetos), sem dificultar o processamento interno (com o valor inteiro).

92

Enumeraes
Foi proposta a fuso deste mdulo com: Programar em C/Enumerao (discuta).

Enumerao (enum) ou tipo enumerado um tipo de dados que tem como conjunto de valores possveis um conjunto finito de identificadores (nomes) determinados pelo programador. Em C, cada identificador em uma enumerao corresponde a um inteiro. Enumeraes so definidas de maneira similar s estruturas e unies, com algumas diferenas. A palavra chave usada enum. enum nome_enumerao { IDENTIFICADOR_1, IDENTIFICADOR_2, ... IDENTIFICADOR_n }; Note as diferenas: no h ponto-e-vrgula no final ou no meio das declaraes (mas ainda h no final do bloco), e no h declarao de tipos. Com essa declarao, ao IDENTIFICADOR_1 ser atribuido o valor 0, ao IDENTIFICADOR_2 ser atribudo o valor 1, e assim por diante. Podemos tambm explicitar os valores que quisermos colocando um sinal de igual e o valor desejado aps o identificador. Caso no haja valor determinado para o primeiro identificador, ele ser zero. Para os demais identificadores, o padro seguir a ordem dos nmeros, a partir do valor do identificador anterior. Podemos misturar identificadores de valor determinado com identificadores de valor implcito, bastando seguir a regra acima. Por exemplo: enum cores { VERMELHO, AZUL = 5, VERDE, AMARELO, MARROM = 10 };

/* /* /* /* /*

0 */ 5 */ 6 */ 7 */ 10 */

Tipos de dados definidos pelo usurio

93

Uso
Da mesma maneira que criamos uma varivel de um tipo struct ou union, podemos criar variveis de um tipo enumerado (enum): enum cores cor_fundo; Para atribuir valores a uma varivel enumerada, podemos usar como valor tanto o identificador quanto o valor correspondente. Seriam equivalentes, portanto: cor_fundo = VERDE; cor_fundo = 6; Na verdade, variveis enumeradas agem de maneira quase igual aos inteiros; possvel, assim, atribuir valores que no correspondem a nenhum dos identificadores.

Campo de bits
Na linguagem c o campo de bits (bitfields) uma estrutura um pouco estranha , em vez de usar variveis com tipos diferentes os campos so formados com as partes de um inteiro. O tamanho de um campo de bits no pode ser maior que o tipo usado , aqui um short . typedef struct { unsigned campo_1: campo_2: campo_3: campo_4: campo_5: }BIT_FIELD_1;

short 6, /* Tamanho 6 bit */ 6, 1, 1, 2;

Essa estrutura esta formada por um tipo que tem o tamanho de um short esse mesmo tipo ser divido em pores menores. No exemplo acima os campos tem os tamanhos 6,6,1,1,2 igual a 16 bits que o tamanho de um unsigned short . Para acessar os campos usamos o mesmo mtodo que usamos com estruturas normais . BIT_FIELD_1 meu_campo; meu_campo.campo_1 = 16; meu_campo.campo_4 = 0;

Enumerao

94

Enumerao
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Enumerations (enum)
Aqui vamos retornar a um tpico antigo. Enumerations so um outro mtodo de definir constantes. Recordam-se? Tnhamos o: 1. #define 2. . const int a: 3. . enumerations.

Criando uma nova tipologia de dados


As enumerations definem uma nova tipologia de varivel e limita desde logo os valores. enum colors {black, blue, green, cyan, red, purple, yellow, white}; a maneira mais simples de interpretar uma enumeration imagina-la como uma matriz de apenas uma linha Temos o nome da linha de temos as vrias clulas na linha. Cada constante enumerada (muitas vezes chamado de enumerator) tem um valor inteiro (caso no seja especificado ele comea em zero) Exemplo:
black blue green cyan purple yellow white 0 1 2 3 4 5 6

Mas podemos definir o valor tipo enum forma {quadrado=5, rectangulo,triangulo=27, circulo, elipse} ficaramos com a nossa linha do tipo:
Quadrado Rectngulo Triangulo Circulo Elipse 5 6 27 28 29

reparem nos valores dos nmeros!!! A vantagem em termos enumeraes que se uma varivel declarada tipo enumerao, tem uma tipologia nica e os seus valores esto limitados e podero ser verificados durante a compilao. tal como as estruturas criar tipologias de variveis #include <stdio.h> /*Definindo o cabealho*/ enum cores { AZUL = 1, VERDE, BRANCO, }; /*Aqui um ponto virgula*/

Enumerao /*typedef transformamos 2 palavras em uma -> tipo_cores*/ typedef enum cores tipo_cores ;

95

/*A funo default da lib ou glibc*/ int main(void) { /*Agora usando o nosso novo tipo * Aqui sem typedef teramos que colocar enum cores */ tipo_cores cor = VERDE ; if(cor == 1) { printf("Cor azul \n"); } if(cor == 2) { printf("Cor verde \n"); } /* printf no ser executado */ if(cor == 3 ) { printf("Cor branco \n"); } return 0 ; /*De um enter depois de } para evitar warning */ } Aqui podemos ver um exemplo com uma funo "mostrarRes()" e um switch: Em este exemplo uma constante e definida e o valor das outra ser definido automaticamente. #include <stdio.h> #include <stdlib.h> mostrarRes(int quem); /*Aqui os valores Italia = 4 e Brasil = 5 so incrementados automaticamente*/ enum { ARGENTINA = 3, ITALIA, BRASIL }; int main(void) { /*Colocamos 5 se voc for Argentino coloque 3 */ int n = BRASIL ;

Enumerao mostrarRes(n); }

96

int mostrarRes(int quem) { switch(quem) { case BRASIL ); break; case ARGENTINA : printf("Argentina um dia quem sabe\n") ; break; case ITALIA : printf("Foi sorte\n") ; break; default : printf("Se estou vivo teve erro do sistema xx \n") ; } printf("The end , hasta la vista\n \n"); /*Final feliz*/ /*De um enter depois de } para evitar warning */ }

: printf( "Brasil invencvel como de costume\n"

Unio

97

Unio
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Unions
As unions so muito parecidas com as estruturas, estas guardam variveis de vrias tipologias. e portanto guardam cada varivel de acordo com a sua tipologia, ie, se tivermos uma varivel membro que um int e outro float, ela guarda exactamente de acordo com essa tipologia. O que se passa aqui que vai guardar as variveis todas com uma nica tipologia que aquela tipologia que ocupa mais espao dentro das tipoogias das variveis membro. ou seja se tivermos uma variavel membro int e outra float , a union vai guardar estas variveis como fossem as duas float.

Declarao
union mytypes_t { int i; float f; } mytypes;

Unions com estruturas


Neste exemplo temos unions e estruturas misturados!! union mix_t { long l; struct { short hi; short lo; } s; char c[4]; } mix;
mytypes_t long l Struct Char C[4] mix

Pergunta: no apanhei isto! repare que a estrutura no tem nome

Anonymous unions estruturas com unions


// estrutura usando "regular union" struct { char title[50]; char author[50]; union {

Unio float dollars; int yens; } price; } book; // estrutura usando "anonymous union" struct { char title[50]; char author[50]; union { float dollars; int yens; }; } book; Se declararmos uma unio sem nome, ela vai ficar annima e poderemos aceder aos seus membros directamente atravs dos nomes dos membros. // Uso regular book.price.dollars book.price.yens // Uso anonimo book.dollars book.yens

98

Estruturas

99

Estruturas
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Structures
As stuctures permitem com que possamos ter variveis de vrias tipologias aglomeradas sob o mesmo nome. E esse mesmo nome vai passar a ser uma nova tipologia de dados tal como o int ou float. Mas o uso disto que podemos ter valores que tenham alguma relao lgica, por exemplo guardar um int de idade e um string de nome. Isto pode ser atributos de uma pessoa. Ou seja podemos empacotar vrias variveis de vrias tipologias com o objectivo de representar o mundo real e dar um nome a essas variveis todas. Ao fazer isto crimos uma tipologia de dados da mesma forma como fazemos em relao ao int ou ao float.

Declarar uma estrutura


A sintaxe : struct <identificador> { <tipo> campo_um ; <tipo> campo_dois ; }; Aqui o tipo struct indica que vamos criar uma estrutura. O nome ou identificador pode ser alunos,familia,etc . (tm de ser vlidos identifiers) No esquecer o ponto e vrgula ; no fim da declarao. Campo_um e Campo_dois so variveis membro member variables ou campo da estrutura. Podemos ter tambm funes membro. Estamos a criar novas tipologias de dados. Primeiro Mtodo : struct minha_estrutura { int variavel_um; int campo_dois; char fruta[40]; } ; Aqui o identificador do tipo "struct" "minha_estrutura" dentro dessa estrutura temos trs campos o ultimo "fruta" Agora podemos usar esse tipo "struct" para definir variveis. struct minha_estrutura nova_estructura ; Para ter acesso aos membros definidos dentro da estrutura utilizamos um operador de seleao de membro "."(um ponto). nova_estrutura.fruta[0] ;

Nos da o primeiro caracter da palavra contida dentro do membro "fruta".

Estruturas

100

Matriz (uma maneira de ver a coisa)


Uma maneira fcil para entender as structures fazer a associao com uma matriz.
Person String Name int height p1 p2 p[20]

Ento temos as seguintes situaes: Criar colunas queremos criar variveis e funes membro Criar linhas queremos criar instancias. (dentro e fora da declarao da estrutura) Definir funes membro fora da declarao da estrutura Atribuir/ler valores s clulas Acrescentar colunas (aps primeira iniciao) Acrescentar linhas (aps primeira iniciao)

Declarar instncias (objectos) da estrutura


Podemos declarar os objectos de duas formas: Ao mesmo tempo que declaramos a estrutura struct product { int weight; float price; } apple, banana, melon; Ou como uma varivel normal struct product { .. } int main() { struct product apple, banana, melon; } E at podemos declarar um array delas Person p[20]; Pergunta: como que feito exactamente os objectos? Para cada objecto vo ser feito uma cpia dos elementos da estrutura. Agora isso significa que os objectos so distintos entre si em termos de reserva de memria? ie, medida que enumero os objectos vo ser reservado para cada objecto o tamanho x de bytes? ou somam-se todos os objectos e reserva-se para todos os objectos de uma forma seguida? Penso que deve ser a 1 opo. Se tivermos apenas um objecto (ou varivel da estrutura) no necessrio darmos o nome da estrutura struct { char item[40]; // name of item

Estruturas double cost; // cost double retail; // retail price int on_hand; // amount on hand int lead_time; // number of days before resupply } temp; Pergunta: neste caso como que chamamos o objecto fora da declarao da estrutura? Pergunta: como objectos podemos ter o qu? os objectos so como que constantes e no tanto como variveis, ie, apenas estamos a chamar um nome a valores de variveis. por isso faz sentido poderemos ter arrays, afinal eles so apenas uma forma de criar vrios nomes. e ponteiros? ter ponteiros apontar para onde? para outro objecto. sim pode fazer sentido. e funes? no isto no me parece que faa sentido. e estruturas?

101

Aceder as variveis membro das estruturas


Agora queremos dar valores a cada uma das pessoas, queremos dar o nome e a altura, para isso faramos; p1.name = "Tiago"; p1.altura =1.9; Se quisssemos imprimir o valor guardado cout << "The name of p1 is " << p1.name; Um erro usual fazer Person.name = "Tiago"; A forma genrica : structure-varname.member-name ou seja o [objecto_estrutura][member_estrutura] Exemplo #include <iostream> #include <string> using namespace std; const int MAX = 3; struct Person { string name; int height; }; int main () { Person p[MAX]; for (int x = 0; x < MAX; x++) { //no funciona

Estruturas cout << "Enter person's name: "; getline(cin, p[x].name); cout << "Enter height in meters: "; cin >> p[x].height; cin.ignore(); } cout << "Outputting person data\n"; cout << "======================\n"; for (int x = 0; x < MAX; x++){ cout << "Person #" << x + 1 << "'s name is " << p[x].name << " and height is " << p[x].height << endl; } system (pause); return 0; }

102

Iniciar uma estrutura


Podemos iniciar uma estrutura de 2 maneiras: Usando uma lista de iniciao esta seria algo: Person p1 = {"Jeff Kent", 72}; isto basicamente igual a arrays, apenas com a diferena de termos tipologias diferentes. Logo a ordem vai interessar, por exemplo se escrevssemos Person p1 = {72, "Jeff Kent"}; Usando construtores Os construtores so funes que so criadas automaticamente sempre que tentamos criar um objecto. A esses que so criados automaticamente so os chamados de defaut. Se escrevermos o cdigo
#include <iostream> #include <string> using namespace std; const int MAX = 3; struct Person { string name; int height; }; int main () { Person p1; cout << "The person's name is " << p1.name << " and height is " << p1.height << endl; system (pause);

//no iria funcionar- erro de compilao

Estruturas
return 0; }

103

O resultado The person's name is and height is -858993460 Aqui criado um defaut constructor no momento em que criamos a instancia p1 ie com a linha Person p1; Como as variveis membro no foram iniciadas, o valor de name est vazio e o na varivel height est um valor qualquer que lixo!

Constructor sem argumentos


Podemos ter um constructor sem argumentos que ao contrrio do defaut constructor designa valores defaut s variveis membro. struct Person { string name; int height; Person() //constructor sem argumentos { name = "No name assigned"; height = -1; } }; O nome do constructor sempre igual ao nome da estrutura, sem excepo. O constructor no retorna qualquer valor, sem excepo Refazendo o nosso exemplo
#include <iostream> #include <string> using namespace std; const int MAX = 3; struct Person { string name; int height; Person() { name = "No name assigned"; height = -1; } }; int main () { Person p1; cout << "The person's name is "<< p1.name << " and height is " << p1.height << endl; system (pause); return 0; }

Estruturas Repare que demos valores defaut s variveis. Agora no estamos no caso de ter p1.name=??? Por mais instncias que criemos eles vo ter sempre valores padro.

104

Constructor com argumentos


Termos um constructor sem argumentos um melhoramento face ao defaut constructor pois agora temos valores defaut para as variveis membro. Porm seria melhor se consegussemos inicializar as variveis membro com valores dados pelo utilizador enquanto o programa estivesse e a correr. E realmente podemos fazer se passarmos argumentos.
#include <iostream> #include <string> using namespace std; const int MAX = 3; struct Person { string name; int height; Person() { name = "No name assigned"; height = -1; } Person(string s, int h) { name = s; height = h; } }; int main () { int metro; string strName; cout << "Entre com o nome da pessoa: "; getline(cin, strName); cout << "Enter height in metro: "; cin >> metro; cin.ignore(); Person p1(strName,metro); cout << "The person's name is " << p1.name << " and height is " << p1.height << endl; system (pause); return 0; } //constructor com 2 argumentos //constructor sem argumentos

Repare que os argumentos do construtor tm de estar na ordem esperada

Estruturas

105

Separar o constructor prototype da implementao


#include <iostream> #include <string> using namespace std; const int MAX = 3; struct Person { string name; int height; Person(); Person(string, int); }; Person::Person() { name = "No name assigned"; height = -1; } Person::Person(string s, int h) { name = s; height = h; } int main () { int metro; string strName; cout << "Enter person's name: "; getline(cin, strName); cout << "Enter height in inches: "; cin >> metro; cin.ignore(); Person p1(strName, inches); cout << "The person's name is " << p1.name << " and height is " << p1.height << endl; system (pause); return 0; } //constructor sem argumento //constructor com dois parametros, apenas necessrio dizer a tipologia dos parmetros o nome no necessrio)

Vamos ver a funo main(): declarmos 2 variveis uma int e outra string. Pedimos para a pessoa escrever o nome e colocmos o valor na varivel string, depois pedimos a altura e colocmos na varivel int. Depois chammos o constructor com dois argumentos e passamos as variveis anteriores como argumentos. Por fim mandmos imprimir no ecr os valores das variveis membro da estrutura. Repare que para definirmos fora o construtor recorremos ao operador scope ::

Estruturas Person::Person() Person::Person(string s, int h) Repare que no prototype dos constructor apenas tivemos de dizer a tipologia dos parmetros

106

Estruturas aninhadas
A ideia ter uma estrutura dentro de outra estrutura. #include <iostream> #include <string> using namespace std; struct Date //estrutura chamada de date { int month; int day; int year; }; struct Person { string name; int height; Date bDay; //temos uma nova varivel, mas notem a tipologia }; void setValues(Person&); void getValues(const Person&); int main () { Person p1; setValues(p1); cout << "Outputting person data\n"; cout << "======================\n"; getValues(p1); system (pause); return 0; } void setValues(Person& pers) { cout << "Enter person's name: "; getline(cin, pers.name); cout << "Enter height in inches: "; cin >> pers.height; cin.ignore(); cout << "Enter month, day and year of birthday separated by spaces: " cin >> pers.bDay.month >> pers.bDay.day >> pers.bDay.year; cin.ignore();

Estruturas } void getValues(const Person& pers) { cout << "Person's name: " << pers.name << endl; cout << "Person's height in inches is: " << pers.height << endl; cout << "Person's birthday in mm/dd/yyyy format is: " << pers.bDay.month << "/" << pers.bDay.day << "/" << pers.bDay.year << endl; } Reparem que a estrutura Date tem de ser declarada antes da estrutura Person, pois caso contrrio o compilador no entendia a tipologia declarada na estrutura Person. Agora para termos os valores da estruturura cin >> pers.bDay.month >> pers.bDay.day >> pers.bDay.year;

107

Passando estruturas com argumento do funes


#include <iostream> #include <string> using namespace std; struct Person { string name; int height; }; void setValues(Person&); void getValues(const Person&); int main () { Person p1; setValues(p1); cout << "Outputting person data\n"; cout << "======================\n"; getValues(p1); system (pause); return 0; } void setValues(Person& pers) { cout << "Enter person's name: "; getline(cin, pers.name); cout << "Enter height in inches: "; cin >> pers.height;

Estruturas
cin.ignore(); } void getValues(const Person& pers) { cout << "Person's name is " << pers.name << " and height is " << pers.height << endl; }

108

A estrutura passada por referncia ou por address

Ponteiros para estruturas


struct movies_t { string title; int year; }; movies_t amovie; movies_t * pmovie; Ns crimos algo movies_t amovie * pmovie title year

Vejamos que temos um ponteiro como instncia. // pointers to structures #include <iostream> #include <string> #include <sstream> using namespace std; struct movies_t { string title; int year; }; int main () { string mystr; movies_t amovie; movies_t *pmovie; pmovie = &amovie; //atribumos valor ao ponteiro cout << "Enter title: "; getline (cin, pmovie->title); //operador ->

Estruturas cout << "Enter year: "; getline (cin, mystr); (stringstream) mystr >> pmovie->year; cout << "\nYou have entered:\n"; cout << pmovie->title; cout << " (" << pmovie->year << ")\n"; system (pause); return 0; } Como j devem ter deduzido o operador -> ser muito similar a pmovie->title equivalente a (*pmovie).title Mas olhem que diferente a: *pmovie.title Ah estas subtilezas!!! Pergunta: porque ser que s podemos acrescentar objectos e no tambm membros fora das structs. ou seja podemos fazer adies na matriz structs nas linhas mas no nas colunas. Pergunta: ser que em vez de termos apenas variveis nas estruturas podamos ter tambm funes, ou melhor instrues? Para declarar uma estrutura: 1. Apenas nos basta criar as colunas que so os members. 2. necessitamos da palavra struct que indica ao compilador que vamos criar uma estrutura 3. necessitamos de definir alguns members (variveis ou funes) Para definir os objectos 1. podemos fazer internamente, ao mesmo tempo que a declarao da estrutura 2. ou podemos fazer fora da declarao, fazendo como se fosse uma outra variavel normal. Para definir os members 1. podemos defini-los ao mesmo tempo que declaramos a estrutura 2. ou podemos defini-los externamente, recorrendo ao operador :: Para alocar valores nos objectos -member variveis 1. fazemos fora da declarao. uma vez que os objectos s so definidos dentro da estructura no final. Pergunta: faz sentido ter funes membro para cada objecto? no. para isso criamos novas estruturas. s iria complicar. Pergunta: faz sentido podemos declarar variveis member fora da declarao da estrutura? sim faz, mas traz o problema de termos de andar atrs dessas variveis depois no cdigo. que equivalente a *(pmovie.title) //operador -> //operador ->

109

//operador ->

Mais sobre variveis

110

Mais sobre variveis


Este mdulo precisa ser revisado por algum que conhea o assunto (discuta).

typedef
A instruo typedef serve para definir um novo nome para um certo tipo de dados intrnseco da linguagem ou definido pelo usurio. Por exemplo, se fizssemos a seguinte declarao: typedef unsigned int uint; poderamos declarar variveis inteiras sem sinal (unsigned int) da seguinte maneira: uint numero; // equivalente a "unsigned int numero;" Como se v, typedef cria uma espcie de "apelido" para um tipo de dados, permitindo que esse tipo seja referenciado atravs desse apelido em vez de seu identificador normal. Um dos usos mais comuns de typedef abreviar a declarao de structs ou estruturas. Veja este exemplo: struct pessoa { char nome[40]; int idade; }; struct pessoa joao; Observe que, para declarar a varivel joao, precisamos escrever a palavra struct. Podemos usar typedef para abreviar essa escrita: typedef struct _pessoa { char nome[40]; int idade; } Pessoa; Pessoa joao; Um "apelido" de tipo utilizado com bastante freqncia, embora no costumemos dar por isso: o tipo FILE, usado nas funes de entrada/sada de arquivos. typedef struct _iobuf { char* _ptr; int _cnt; char* _base; int _flag; int _file; int _charbuf; int _bufsiz; char* _tmpfname;

Mais sobre variveis } FILE; Ento, quando declaramos algo como FILE *fp; na verdade estamos a declarar um ponteiro para uma estrutura, que ser preenchida mais tarde pela funo fopen. Ateno! Voc no deve tentar manipular uma estrutura do tipo FILE; sua composio foi apresentada apenas como exemplo ou ilustrao.

111

sizeof
sizeof uma macro substituda em tempo de compilao por um valor representando o tamanho de um tipo. O erro mais comum com os iniciantes achar que sizeof retorna o tamanho de variveis em tempo de execuo, portanto importante ressaltar sizeof() s funciona em tempo de compilao Exemplo de uso: Correto: #include <string.h> #include <stdio.h> int main(void) { char *nome; nome = malloc(sizeof(char) * 9); sprintf(nome, "wikibooks"); printf("Site: http://pt.%s.org/", nome); /* Imprime: Site: http://pt.wikibooks.org/ */ } Incorreto: const char *FRASE; FRASE = "Wikibooks eh legal"; printf("Eu acho que o tamanho da string FRASE %d", sizeof(FRASE)); A sentena acima NO funciona, pois sizeof substituido pelo tamanho de um tipo em tempo de compilao. Um exemplo mais interessante do uso de sizeof: /* Criando um tipo estruturado de dados: */ typedef struct { const char *nome; const char *sobrenome; int idade; } Pessoa;

Mais sobre variveis

112

int main(void) { Pessoa *joaquim; joaquim = malloc(sizeof(Pessoa)); joaquim->nome = "Joaquim"; joaquim->sobrenome = "Silva"; joaquim->idade = 15; } O sizeof acima foi til pra saber a soma do tamanho de todos os tipos da estrutura Pessoa.

Converso de tipos
Casting: converso manual
Em C, cada tipo bsico ocupa uma determinada poro de bits na memria, por exemplo: * * * * * * char possui 8 bits, vai de -127 a 127 int possui 16 bits, vai de -32.767 a 32.767 short int possui 8 bits, vai de 0 a 255 float possui 32 bits e seis dgitos de preciso double possui 64 bits e dez dgitos de preciso long double possui 128 bits e dez dgitos de preciso

Logo, a converso entre tipos nem sempre algo nativo da linguagem, por assim dizer. H funes como atol e atof que convertem string em inteiro longo (long int) e string em double, respectivamente. Mas em muitos casos possvel usar o casting. O casting feito para "cortar" bits de tipos, podendo convert-los a tipos de tamanho menor. Vamos entender melhor tomando os seguintes tpicos: * float possui 32 bits * int possui 16 bits Mas qual a diferena de float pra int ? Um nmero float: 43.023 ao ser convertido para int dever ser "cortado", ficando inteiro: 43 O que ser feito ignorar os bits extras, usados nos campos decimais. int i; float f; f = 43.023; /* A converso com casting : */ i = (int) f; Em poucas palavras: Casting colocar um tipo entre parnteses antes da atribuio de uma varivel. variavel_destino = (tipo) variavel_origem; Isso tambm pode ser utilizado com ponteiros e estruturas de dados, mas lembre-se:

Mais sobre variveis O casting fala ao compilador pra utilizar o tipo entre parnteses como "forma" para cortar bits do dado antes de atribu-lo varivel.

113

Modificadores de acesso
Estes modificadores, como o prprio nome indica, mudam a maneira com a qual a varivel acessada e modificada.Alguns dos exemplos usam conceitos que s sero abordados nas sees seguintes, ento voc pode deixar esta seo para depois se assim o desejar.

const
O modificador const faz com que a varivel no possa ser modificada no programa. Como o nome j sugere til para se declarar constantes. Poderamos ter, por exemplo: const float PI = 3.1415; Podemos ver pelo exemplo que as variveis com o modificador const podem ser inicializadas. Mas PI no poderia ser alterado em qualquer outra parte do programa. Se o programador tentar modificar PI o compilador gerar um erro de compilao. Outro uso de const, alis muito comum que o outro, evitar que um parmetro de uma funo seja alterado pela funo. Isto muito til no caso de um ponteiro, pois o contedo de um ponteiro pode ser alterado por uma funo. Para proteger o ponteiro contra alteraes, basta declarar o parmetro como const. #include <stdio.h> int sqr (const int *num); int main(void) { int a = 10; int b; b = sqr(&a); } int sqr (const int *num) { return ((*num)*(*num)); } No exemplo, num est protegido contra alteraes. Isto quer dizer que, se tentssemos fazer *num = 10; dentro da funo sqr(), o compilador daria uma mensagem de erro.

Mais sobre variveis

114

volatile
O modificador volatile diz ao compilador que a varivel em questo pode ser alterada sem que este seja avisado. Isto evita "bugs" que poderiam ocorrer se o compilador tentasse fazer uma otimizao no cdigo que no segura quando a memria modificada externamente. Digamos que, por exemplo, tenhamos uma varivel que o BIOS do computador altera de minuto em minuto (um relgio, por exemplo). Seria importante que declarssemos esta varivel como volatile. Um uso importante de variveis volatile em aplicaes com vrias threads (linhas de execuo), onde a memria compartilhada por vrios pedaos de cdigo que so executados simultaneamente.

extern
O modificador extern diz ao compilador que a varivel indicada foi declarada em outro arquivo que no podemos incluir diretamente, por exemplo o cdigo de uma biblioteca padro. Isso importante pois, se no colocarmos o modificador extern, o compilador ir declarar uma nova varivel com o nome especificado, "ocultando" a varivel que realmente desejamos usar. E se simplesmente no declarssemos a varivel, j sabemos que o compilador no saberia o tamanho da varivel. Quando o compilador encontra o modificador extern, ele marca a varivel como no resolvida, e o montador se encarregar de substituir o endereo correto da varivel. extern float sum; extern int count; float returnSum (void) { count++; return sum; } Neste exemplo, o compilador ir saber que count e sum esto sendo usados no arquivo mas que foram declarados em outro. Uma varivel externa freqentemente usada a varivel errno (declarada no arquivo-cabealho errno.h), que indica o ltimo cdigo de erro encontrado na execuo de uma funo da biblioteca padro ou do sistema.

static
O funcionamento das variveis declaradas como static depende de se estas so globais ou locais. Variveis globais static funcionam como variveis globais dentro de um mdulo, ou seja, so variveis globais que no so (e nem podem ser) conhecidas em outros mdulos (arquivos). Isto util se quisermos isolar pedaos de um programa para evitar mudanas acidentais em variveis globais. Isso um tipo de encapsulamento que , simplificadamente, o ato de no permitir que uma varivel seja modificada diretamente, mas apenas por meio de uma funo. Variveis locais estticas so variveis cujo valor mantido de uma chamada da funo para a outra. Veja o exemplo: int count (void) { static int num = 0; num++; return num;

Mais sobre variveis } A funo count() retorna o nmero de vezes que ela j foi chamada. Veja que a varivel local int inicializada. Esta inicializao s vale para a primeira vez que a funo chamada pois num deve manter o seu valor de uma chamada para a outra. O que a funo faz incrementar num a cada chamada e retornar o seu valor. A melhor maneira de se entender esta varivel local static implementando. Veja por si mesmo, executando seu prprio programa que use este conceito.

115

register
O computador pode guardar dados na memria (RAM) e nos registradores internos do processador. As variveis (assim como o programa como um todo) costumam ser armazenadas na memria. O modificador register diz ao compilador que a varivel em questo deve ser, se possvel, guardada em um registrador da CPU. Vamos agora ressaltar vrios pontos importantes: Porque usar register? Variveis nos registradores da CPU vo ser acessadas em um tempo muito menor pois os registradores so muito mais rpidos que a memria. No entanto, a maioria dos compiladores otimizantes atuais usa registradores da CPU para variveis, ento o uso de register freqentemente desnecessrio. Em que tipo de varivel podemos usar o register? Antes da criao do padro ANSI C, register aplicava-se apenas aos tipos int e char, mas o padro atual permite o uso de register para qualquer um dos quatro tipos fundamentais. claro que seqncias de caracteres, arrays e estruturas tambm no podem ser guardadas nos registradores da CPU por serem grandes demais. register um pedido que o programador faz ao compilador. Este no precisa ser atendido necessariamente, e alguns compiladores at ignoram o modificador register, o que permitido pelo padro C. register no pode ser usado em variveis globais, pois isto implicaria em um registrador da CPU ficar o tempo todo ocupado por essa varivel. Um exemplo do uso do register dado a seguir: int main (void) { register int count; for (count = 0; count < 10; count++) { ... } return 0; } O loop acima, em compiladores que no guardam variveis em registradores por padro, deve ser executado mais rapidamente do que seria se no usssemos o register. Este o uso mais recomendvel para o register: uma varivel que ser usada muitas vezes em seguida.

Gerenciamento de memria

116

Gerenciamento de memria
Alocao dinmica
Todos os dados de um programa so armazenados na memria do computador; muito comum necessitar reservar um certo espao na memria para poder guardar dados mais tarde. Por exemplo, poderamos reservar um espao de 1000 bytes para guardar uma string que o usurio viesse a digitar, declarando um vetor de 1000 caracteres. E se quisssemos reservar um espao que s conhecido no tempo de execuo do programa? E se o espao fosse muito grande, de modo que declarar vetores de tal tamanho seria inconveniente (pois, entre outras coisas, aumenta sem necessidade o tamanho do executvel)? Para solucionar esse problema, existe a alocao dinmica de memria, que como o nome sugere, uma maneira de alocar memria medida que o programa vai sendo executado. As quatro funes relacionadas com a alocao dinmica sero descritas a seguir.

malloc e free
Essas duas funes so as mais bsicas para o gerenciamento de memria. malloc responsvel pela alocao de um pedao de memria, e free responsvel por liberar esse pedao de memria. Para alocar um espao na memria, precisamos fornecer funo malloc o nmero de bytes desejados. Para isso, precisaremos usar o operador sizeof, introduzido na seo anterior. Veja um exemplo. #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { /* ponteiro para memria que ser alocada */ int *p; int i; /* alocar 10 elementos inteiros, ou seja, ( sizeof (int) * 10 ) */ p = (int *) malloc ( sizeof (int) * 10); if ( p == NULL ) { printf ("Erro: No foi possivel alocar memria\n"); exit(1); } for(i = 0; i < 10; i++) { p[i] = i * 2; printf ("%d\n", p[i]); } /* libera a memria alocada por malloc */ free (p); return 0;

Gerenciamento de memria } importante notar que a funo malloc retorna um ponteiro NULL caso no consiga alocar memria.

117

calloc
Sintaxe:

#include <stdlib.h> void *calloc(size_t nelem, size_t elsize); Descrio: A funo calloc reserva um bloco com o tamanho (nelem x elsize) octetos consecutivos. O bloco reservado inicializado a 0. Valor de retorno : Se a operao foi um sucesso, calloc retorna um ponteiro sobre o inicio do bloco reservado. Se no houver espao suficiente ou se nelem ou elsize valem 0 ,calloc retorna NULL . Ex:

#include <stdio.h> #include <stdlib.h> int main() { int *str = NULL; str = (char *) calloc(10, sizeof(int)); printf("%d\n", str[9]); free(str); return 0; }

realloc
Sintaxe:

#include <stdlib.h> void *realloc(void *ptr, size_t size); Descrio: A funo realloc ajusta o tamanho de um bloco a size octetos consecutivos. Argumentos: ptr: Aponta para um bloco criado por malloc, calloc ou realloc.Se o ponteiro NULL realloc equivale a malloc. size: Tamanho do novo bloco em octetos. Valor de retorno : Se a funo terminar normalmente ela retorna o endereo do novo bloco.Pode acontecer que o endereo mude com relao ao endereo usado em argumento, nesse caso o conteudo do bloco transferido para o novo bloco e a regio antiga e liberada automaticamente. Se faltar lugar na memria ou size for igual a 0 realloc retorna NULL . Ex:

Gerenciamento de memria

118

#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *str1=NULL, *str2=NULL; str1 = (char *) malloc(11); strcpy(str1, "ABCDEFGHIJ"); str2 = (char *) realloc(str2, 20); printf("Endereo de str1 : %p\n", str1); printf("Endereo de str2 : %p\n", str2); str1 = (char *) realloc(str1, 100); printf("Novo endereo de str1 : %p\n", str1); printf("Conteudo de str1 : %s\n", str1); free(str1); free(str2); return 0; }

Sockets

119

Sockets
Abstraes
A verso Unix BSD 4.1c de 1982 para VAX foi a primeira a incluir TCP/IP no kernel do sistema operacional, oferecendo ao mesmo tempo uma interface de programao ou abstraco para esses protocolos. Os soquetes ou sockets so uma API (Application Program Interface) isso quer dizer uma interface entre os programas e a camada de transporte. Exemplo: TCP, UDP. Os soquetes podem usar outros protocolos como AppleTalk, Xrox XNS, etc. As duas principais API para Unix so os soquetes Berkeley e TLI(Transport Layer Interface) que surgiram com o Unix system V. Essas duas interfaces foram desenvolvidas em C. O Windows possui uma interface similar conhecida com o nome de Winsock.

Funes da biblioteca padro


int accept(int, struct sockaddr *restrict, socklen_t *restrict); int bind(int, const struct sockaddr *, socklen_t); int connect(int, const struct sockaddr *, socklen_t); int getpeername(int, struct sockaddr *restrict, socklen_t *restrict); int getsockname(int, struct sockaddr *restrict, socklen_t *restrict); int getsockopt(int, int, int, void *restrict, socklen_t *restrict); int listen(int, int); ssize_t recv(int, void *, size_t, int); ssize_t recvfrom(int, void *restrict, size_t, int, struct sockaddr *restrict, socklen_t *restrict); ssize_t recvmsg(int, struct msghdr *, int); ssize_t send(int, const void *, size_t, int); ssize_t sendmsg(int, const struct msghdr *, int); ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); int setsockopt(int, int, int, const void *, socklen_t); int shutdown(int, int); int socket(int, int, int); int sockatmark(int); int socketpair(int, int, int, int[2]);

Makefiles

120

Makefiles
Makefile
O makefile um arquivo utilizado por um programa chamado make ou em outros sistemas operativos ,gmake, nmake, tmake, etc. Ele pode variar de um sistema a outro pois no faz parte de nenhuma normalizao . O texto contido em um makefile usado para a compilao, montagem, "linkagem" de um projeto entre outros. Vantagens do uso. Evita a compilao de arquivos desnecessrios. Por exemplo, se seu programa utiliza 120 bibliotecas e voc altera apenas uma, o makefile descobre (comparando as datas de alterao dos arquivos fontes com as dos arquivos anteriormente compilados) qual arquivo foi alterado e compila apenas a biblioteca necessria. As explicaes a seguir so para o utilitrio GNU make. Ento vamos para a apresentao do Makefile atravs da compilao de um pequeno projeto em linguagem C. Criar uma pasta com esses 4 arquivos : teste.c ,teste.h , main.c, Makefile. De um nome para a pasta Projeto. /*================ teste.c ======================*/ #include <stdio.h> #include <stdlib.h> /*Uma funao makeTeste()*/ void makeTeste(void){ printf("O Makefile super Legal\n"); } Aqui escrevemos o header : /*======================= teste.h ===============*/ /*================== Cabealho ou header ========*/ #ifndef _H_TESTE #define _H_TESTE /* A nossa funo */ void makeTeste(void); /* De um enter depois de endif*/ /*Para evitar warning*/ #endif Agora a funo main : /*====================== main.c =================*/ #include <stdio.h> #include <stdlib.h> #include "teste.h" /* Aqui main ;( */ int main(void){ makeTeste();

Makefiles return (0); } Para compilar fazemos um arquivo Makefile minimal. #Para escrever comentrios ## ############################# Makefile ########################## all: teste teste: teste.o main.o gcc -o teste teste.o main.o #-----> Distancia com o botao TAB ### e nao com espaos teste.o: teste.c gcc -o teste.o -c teste.c -W -Wall -ansi -pedantic main.o: main.c teste.h gcc -o main.o -c main.c -W -Wall -ansi -pedantic clean: rm -rf *.o mrproper: clean rm -rf teste Para no ter erros os espaos devem ser feito com a tecla TAB. E compilar s ir dentro da pasta "Projeto" apertar F4 escrever make e apertar enter. Uma vez compilado podemos modificar teste.c . Se teste.c foi modificado ento make modifica teste.o e se no deixa teste.o como esta. all : o nome das regras a serem executadas. teste: teste.c .Pode ser interpretado com arquivo_de_destino: arquivo_de_origem. clean: Apaga os arquivos intermedirios.Se voc escrever no console make clean ele apaga os arquivos objeto da pasta. mrproper: Apaga tudo o que deve ser modificado.No console escreva make mrproper

121

Sintaxe de criao do arquivo


O makefile funciona de acordo com regras, a sintaxe de uma regra : regra: dependncias Apertar o boto TAB

comando comando ...

Regras complementarias all : o nome das regras a serem executadas. clean: Apaga os arquivos intermedirios. mrproper: Apaga tudo o que deve ser modificado.

Makefiles Definir Variveis As variveis servem para facilitar o trabalho. Em vez de mudar varias linhas mudamos s o valor da varivel. Deve ser por isso que se chama varivel, no? Definimos da forma seguinte. NOME=VALOR E para utilizar esta varivel colocamos entre $() . Ento ela vai ficar assim $(NOME) Vamos para o exemplo com o nosso Makefile. Colocamos em vez de : NOME SRC E em vez de VALOR main.c . E para poder usar $(SRC) Ser que na pratica funciona?. Vamos ver.. #Para escrever comentrios ## ############################# Makefile ########################## #Definimos a varivel SRC=main.c all: teste teste: teste.o main.o gcc -o teste teste.o main.o #-----> Distancia com o botao TAB ### e nao com espaos teste.o: teste.c gcc -o teste.o -c teste.c -W -Wall -ansi -pedantic # #Coloquei $(SRC) em todos os lugares aonde estava main.c main.o: $(SRC) teste.h gcc -o main.o -c $(SRC) -W -Wall -ansi -pedantic clean: rm -rf *.o mrproper: clean rm -rf teste Todos os lugares do cdigo que contem o CONTEDO da varivel so modificados colocando no lugar respectivo o NOME da varivel.

122

Makefiles Variveis Personalizadas CC=gcc .Definimos CC para nomes de compiladores de C ou C++ .Aqui o gcc. CFLAGS=-W -Wall -ansi -pedantic .Serve para definir opes passadas ao compilador. Para o c++ o NOME e CXXFLAGS . LDFLAGS e utilizado para editar as opes de links. EXEC=teste .EXEC define o NOME do futuro programa executvel. OBJ=teste.o main.o . Para cada arquivo.c um arquivo OBJETO e criado com a extenso ".o" arquivo.o . Ento e s olhar na sua pasta todos os arquivos com a extenso ".c" e colocar na varivel OBJ com a extenso".o" . Outra maneira e mesma coisa. OBJ agora e igual a main.o teste.o SRC = main.c teste.c OBJ= $(SRC:.c=.o) E super manero a tua idia camarada. Mais tenho 200 arquivos.c e no quero olhar o nome de todos um por um. Tem outra idia?? Poderamos utilizar *c mais no podemos utilizar este caracter joker na definio de uma varivel. Ento vamos utilizar o comando " wildcard " ele permite a utilizao de caracteres joker na definio de variveis. Fica assim. SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) Observao se quiser fazer aparecer uma mensagem durante a compilao escreva @echo "Minha mensagem" . E mais tem um monte de mensagens e fica muito feio Tem outra idia??.. O pessoal vamos parando ;) no sou uma maquina de idias. Para deixar as mensagens em modo silencioso coloque "@" no comeo do comando. Fica assim

123

@$(CC) -o $@ $^ Variveis internas $@ $< $^ $? $* Nome da regra. Nome da primeira dependncia Lista de dependncias Lista de dependncias mais recentes que a regra. Nome do arquivo sem sufixo

Makefiles As regras de interferncia No disse nada antes porque estvamos no estado principiantes "noob". So regras genricas chamadas por default. .c.o : .Ela significa fazer um arquivo.o a partir de um arquivo.c . %.o: %.c .A mesma coisa. A linha teste.o: teste.c pode ser modificada com essa regra. .PHONY: .Preste bem ateno. Esta regra permite de evitar conflitos. Por exemplo "clean:" e uma regra sem nem uma dependncia no temos nada na pasta que se chame clean. Agora vamos colocar na pasta um arquivo chamado clean. Se voc tentar apagar os "arquivos.o" escrevendo "make clean" no vai acontecer nada porque make diz que clean no foi modificado. Para evitar esse problema usamos a regra .PHONY : . Fica assim. .PHONY: clean mrproper .PHONY: diz que clean e mrproper devem ser executados mesmo se arquivos com esses nomes existem. Agora vamos modificar mais uma vez o nosso Makefile com tudo o que sabemos sobre variveis. #Para escrever comentrios ## ############################# Makefile ########################## #Definimos a varivel CC=gcc CFLAGS=-W -Wall -ansi -pedantic EXEC=teste OBJ=teste.o main.o all: $(EXEC) @echo "Vou comear a compilao" #No coloquei a varivel OBJ para que possam entender as variveis internas. #Se entenderam podem colocar $(OBJ) no lugar de teste.o main.o teste: teste.o main.o # $@ = teste: # $^ = teste.o main.o $(CC) -o $@ $^ # teste.o:teste.c %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) main.o: main.c teste.h $(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: rm -rf *.o @echo "Compilaao prontinha" mrproper: clean rm -rf $(EXEC) Po legal ;) parece at trabalho de gente grande.

124

Makefiles Sub Makefiles Ler tudo isso s para compilar um programa?? O sub-makefile e lanado por meio de um "Makefile principal" vamos simplificar para o Patro Makefile. Aonde estvamos??...Ah sim, para que serve?? O Makefile Principal executa os sub-makesfiles de outras pastas. Como ele faz?? Usamos uma varivel pre-definida $(MAKE). Bao ao trabalho.. Crie dentro da pasta "Projetos" outra pasta com o nome "sub-make".Dentro da pasta sub-make crie um arquivo Makefile e um arquivo submake.c Dentro da pasta sub-make coloque este Makefile. ##################Pasta:sub-make ## Makefile ################### CC=gcc CFLAGS=-W -Wall -ansi -pedantic EXEC=teste2 SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) @echo "compilando sub-makefile" @echo "sub-makefile compilado" teste2: $(OBJ) @$(CC) -o $@ $^ .PHONY: clean mrproper clean: @rm -rf *.o mrproper: clean @rm -rf $(EXEC) Agora vamos escrever o arquivo submake.c . #include <stdio.h> #include <stdlib.h> /* Informao * Nao utilizem este cdigo para fazer um kernel */ int main(void) { printf("Sou o binrio que est em sub-make"); printf("Finalmente em fim vivo graas ao Patro Makefiles ;)"); return (0); } Agora retorne na pasta "Projeto" vamos modificar o Makefile . Vamos colocar a seguinte linha: @cd sub-make && $(MAKE) Explicando: "@" silencioso "cd" para abrir a pasta sub-make "&&" e executar make "$(MAKE)"

125

Makefiles Vamos fazer a mesma coisa para "clean:" e "mrproper:" ento ao executar "make clean" no console ele vai executar o mesmo comando no sub-makefile. ########################## O Makefile principal ##########################" CC=gcc CFLAGS=-W -Wall -ansi -pedantic EXEC=teste SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) @echo "Compilando Projeto" @echo "O patro foi compilado" #A linha que vai compilar sub-make @cd sub-make && $(MAKE) teste: $(OBJ) @$(CC) -o $@ $^ %.o: %.c @$(CC) -o $@ -c $< $(CFLAGS) main.o: main.c teste.h @$(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: @rm -rf *.o *~ # E a mesma coisa que dar um F4 dentro da pasta sub-make # e escrever make clean @cd sub-make && $(MAKE) $@ mrproper: clean @rm -rf $(EXEC) #modificamos aqui tambm @cd sub-make && $(MAKE) $@ No esquea de dar TAB em todas as linhas que esto em baixo dos ":" dois pontinhos. OK agora s dar um F4 dentro da pasta projetos e voc tem trs comandos a disposio. make make clean make mrproper Make install Automatizando a instalao do programa com a regra install: . install: .Coloca o binrio ou executvel em uma determinada pasta, como por exemplo /bin ou /usr/bin no Linux. Pode ser em qualquer outra, utilizando o comando "mv" ou "cp" para mover ou copiar. Crie uma pasta bin dentro de "Projetos". Devem saber que no devem colocar nada intil que venha da internet na pasta raiz do linux. Vamos fazer duas variveis: prefix=/caminho/ate onde/esta/Projetos bindir=$(prefix)/bin .Igual a /caminho ate/Projetos/dentro de Projetos a pasta bin . E adicionarmos a regra install:all com seus comandos.

126

Makefiles Modificando o make principal. ########################## O Makefile principal ##########################" #Coloque o caminho at Projeto aqui prefix=/home/USER/Projeto bindir=$(prefix)/bin CC=gcc CFLAGS=-W -Wall -ansi -pedantic EXEC=teste SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) @echo "Compilando Projeto" @echo "O patrao foi compilado" #A linha que vai compilar sub-make @cd sub-make && $(MAKE) teste: $(OBJ) @$(CC) -o $@ $^ %.o: %.c @$(CC) -o $@ -c $< $(CFLAGS) main.o: main.c teste.h @$(CC) -o $@ -c $< $(CFLAGS) #Entao depois e so executar make e depois make install install:all @mv $(EXEC) $(bindir)/ .PHONY: clean mrproper clean: @rm -rf *.o *~ # E a mesma coisa que dar um F4 dentro da pasta sub-make # e escrever make clean @cd sub-make && $(MAKE) $@ mrproper: clean @cd bin && rm -rf $(EXEC) #modificamos aqui tambem @cd sub-make && $(MAKE) $@ Ento quando voc digitar no console "make" depois "make install" ele vai colocar o binario que esta em "Projetos" dentro de "bin". Se voc quiser colocar o binario que esta na pasta "sub-make" na pasta "bin" Copiar e colar no makefile da "sub-make" as variaveis "prefix" e "bindir"e a regra install:com seu comando. E no "Makefile principal" em baixo de "install:" coloque esta linha @cd sub-make && $(MAKE) $@ Aqui eu modifiquei o "mrproper" porque agora os binarios que devem ser apagados com "make mrproper" esto em "bin". Vou deixar voces modificarem o "mrproper" do "sub-makefile" como pessoas adultas e responsaveis ;) Valeu galera. Os comandos no console so: make

127

Makefiles make install make clean make mrproper .Para apagar os binarios.

128

Lista de palavras reservadas


A linguagem C possui um total de 32 palavras conforme definido pelo padro ANSI, que so elas:
auto break case char const default do double else enum float for goto if int long return short signed sizeof static struct switch typedef union unsigned void volatile while

register

extern

continue

importante lembrar que todas as palavras reservadas so escritas em minsculo e no podem ser utilizadas para outro propsito. Alguns compiladores incluem outras palavras reservadas como, asm, cdecl, far, fortran, huge, interrupt, near, pascal, typeof.

Seqncias de escape
O C tem vrias seqncias de escape. Elas servem geralmente para inserir um caractere especial numa String. Algumas dessas seqncias so: \a - Alarm, Alarme = Toca o alarme sonoro do sistema \b - Back space, Retrocesso = Apaga o caractere esquerda do cursor \n - NewLine, Nova linha = Pula uma linha \t - Tabulao horizontal = Equivale dar um TAB na string \r - Carriage Return, Retorno do Carro = Volta para o incio da linha. \t - Horz. Tab, Tabulao Harizontal = Salta frente conforme seus ajustes de tabulao \0 - Null, Nulo = Caractere nulo ou zero geralmente estabelecido como fim de string

Lista de funes

129

Lista de funes
Aqui esto as vrias funes presentes em C separadas por cabealho: stdio.h printf scanf vsnprintf sprintf vprintf fprintf fscanf feof fflush calloc malloc system

gets fgets puts fputs stdlib.h atoi atof atol itoa string.h strcmp stricmp strlen strstr strcat strcpy strncpy strncat strchr strrev signal.h iso10646.h time.h math.h tan sin cos atan asin acos

Lista de funes pow sqrt abs

130

Lista de bibliotecas
Cabealhos de bibliotecas padro ANSI C (C89)/ISO C (C90):
assert.h ctype.h errno.h float.h limits.h locale.h math.h setjmp.h signal.h stdarg.h stddef.h stdio.h stdlib.h string.h time.h

Cabealhos adicionados no ISO C (C94/C95):


iso646.h wchar.h wctype.h

Cabealhos adicionados no ISO C (C99) (suportados somente em compiladores mais novos):


complex.h fenv.h inttypes.h stdbool.h stdint.h tgmath.h

Ligaes externas
The Open Group Base Specifications Issue 7 [1] (english)

Referncias
[1] http:/ / www. opengroup. org/ onlinepubs/ 9699919799/ functions/ contents. html

Dicas de programao em C

131

Dicas de programao em C
A funo printf a melhor amiga de um programador
Um programador novato tende a ver apenas duas aplicaes para o printf: 1. Solicitar entrada para o usurio do programa. 2. Imprimir o resultado do programa. O fato que um programador pode aplicar o printf a fim de saber o que ocorre durante a execuo de programa. Isto permite, dentre outras coisas, detectar erros. Por exemplo, suponha um programa no qual vrias funes e rotinas so executadas. Algo como: int main(int argc, char *argv[]) { ... funcao1(...); funcao2(...); funcao3(...); funcao4(...); ... return 0; } Digamos que o programa tenha sido compilado com sucesso, mas ocorra algum erro durante sua execuo. Podemos usar o printf para detectar o erro da seguinte maneira: int main(int argc, char *argv[]) { ... printf("iniciando funcao1"); funcao1(...); printf("completa funo1, iniciando funcao2"); funcao2(...); printf("completa funo2, iniciando funcao3"); funcao3(...); printf("completa funo3, iniciando funcao4"); funcao4(...); printf("completa funo4"); ... return 0; } Isto permite o programador determinar at que ponto o programa roda antes de dar erro, facilitando muito a deteco deste. Outro exemplo de como o printf til na deteco de problemas. Suponha um programa cheio de laos aninhados. Tal como: for(...) { while(...)

Dicas de programao em C { ... for(...) { ... } } } Caso durante a execuo o programa entre em um loop infinito, uma forma de detectar em qual dos laos est o problema : for(...) { printf("Teste 1"); while(...) { printf("Teste 2"); ... for(...) { printf("Teste 3"); ... } } } A impresso que se repetir eternamente aquela dentro do lao problemtico. Um ltimo exemplo de deteco de problemas por meio do printf. Suponha que a resposta dada por um programa no a esperada, que a resposta consiste na impresso de uma varivel x, a qual recebe diversas atribuies ao longo do programa. Podemos identificar o erro dando um printf em x aps cada uma de suas atribuies: x=... printf("primeira atribuicao de x eh %tipo", x); ... x=... printf("segunda atribuicao de x eh %tipo", x); ... x=... printf("terceira atribuicao de x eh %tipo", x); ... printf("A resposta eh %tipo", x); Caso o valor de x dependa do valor de outras variveis que no so impressas, imprimi-las pode ajudar na deteco do problema. Para uso como debug, a linguagem C apresenta duas macros que quando utilizadas junto com o printf so timos recursos. __FILE__ = nome do arquivo. __LINE__ = numero da linha de execuo. O Compilador gcc ainda dispe de uma outra macro bastante util: __PRETTY_FUNCTION__ = nome da funo atual.

132

Dicas de programao em C ... printf("%d:%s:%s\n", __LINE__, __FILE__, __PRETTY_FUNCTION__); ... O trecho acima vai te dar uma saida para debug muito util com o seguinte conteudo: Exemplo: 3:hello.c:main

133

Tecle 1 para rodar


Existem duas formas de manter um programa rodando enquanto o usurio desejar: 1. Conter a maior parte do programa dentro de um lao. 2. Usar o comando goto(lembre-se que o comando goto no de uso aconselhado para a programao estruturada). Alguns exemplos: Com while: int main(int argc, char *argv[]) { int rodando=1; while(rodando==1)/*Este laco mantem o programa rodando enquanto o usuario desejar*/ { ... printf("\nDigite 1 para continuar rodando o programa."); printf("\nDigite qualquer outro numero para encerrar o programa. "); scanf("%d", &rodando); } return 0; } Com do...while int main(int argc, char *argv[]) { short int rodando; do /*Este laco mantem o programa rodando enquanto o usuario desejar*/ { ... printf("\nDigite 1 para manter o programa rodando. "); scanf("%d", &rodando); }while(rodando==1); return 0; }

Dicas de programao em C Com o goto int main(int argc, char *argv[]) { MARCA: ... FIM: int y; printf("Tecle 1 para continuar rodando o programa. Tecle 0 para encerrar o programa\n"); scanf("%d",&y); if(y==1) { goto MARCA; } if(y!=1 && y!=0) { goto FIM; } return 0; }

134

Listas encadeadas
Listas encadeadas so estruturas de dados lineares e dinmicas, a grande vantagem que elas possuem em relao ao uso de vetor o fato de terem tamanho mximo relativamente infinito (o tamanho mximo o da memria do computador), ao mesmo tempo que podem ter o tamanho mnimo de 1 elemento evitando o desperdcio de memria.

Primitivas
No existe nenhuma normalizao quanto as primitivas usadas para a manipulao de uma lista. Abaixo voc pode ver uma lista com algumas delas . Colocar o ndice sobre o primeiro elemento da lista. Colocar o ndice sobre o ltimo elemento da lista . Colocar o ndice sobre o elemento que segue o elemento atual . Colocar o ndice sobre o elemento que precede o elemento atual . Verificar se a lista est vazia : Se a lista estiver vazia retorna verdadeiro, se no, falso. Verificar se o primeiro elemento : Retorna verdadeiro se o elemento atual o primeiro, se no, falso. Verificar se o ltimo elemento : Retorna verdadeiro se o elemento atual o ltimo, se no, falso. Verificar o nmero de elementos da lista : Retorna o nmero de elementos da lista. Adicionar um elemento no incio : Adicionar um elemento antes do primeiro elemento da lista . Adicionar um elemento no fim : Adicionar um elemento depois do ltimo elemento da lista .

Insero : Inserir um elemento antes do elemento atual . Troca : Trocar o elemento atual . Remoo : Remover o elemento atual .

Listas encadeadas Listar todos os elementos da lista .

135

Lista encadeada linear


Cada n ou elemento de uma lista encadeada ir possuir guardar o valor do n e o endereo do prximo n. Em uma lista encadeada linear o ultimo elemento aponta para NULL . struct No{ char *p_dados; struct No *p_prox; };

Iniciar uma lista


A funo abaixo demonstra como iniciar uma lista criando o espao da raiz na memria. void criar_Lista(struct No **p_Raiz){ *p_Raiz = NULL; }

Insero
Existem 3 tipos de insero em uma lista, pode-se inserir no comeo, no final ou entre dois elementos da lista.

Insero no incio
int inserir_No_Inicio(struct No **p_Raiz, char *p_String){ struct No *p_Novo; /** Alocao dinmica da memoria */ if((p_Novo = (struct No *) malloc(sizeof(struct No))) == NULL ){ puts( "Falta Memoria\n"); return -1 ; } p_Novo->p_dados = p_String; if(*p_Raiz == NULL){ *p_Raiz = p_Novo; p_Novo->p_prox = NULL; }else{ p_Novo->p_prox = *p_Raiz; *p_Raiz = p_Novo; } }

Insero no fim
int inserir_No_Fim(struct No **p_Raiz, char *p_String){ struct No *p_Novo; if(( p_Novo = (struct No *) malloc(sizeof(struct No))) == NULL ){ puts( "Falta Memoria\n"); return -1 ; }

Listas encadeadas p_Novo->p_dados = p_String; p_Novo->p_prox = NULL; if(*p_Raiz == *p_Raiz = else{ struct No e_atual = NULL) p_Novo; *e_atual; *p_Raiz; /*@ Elemento atual*/ /*@ Primeiro elemento*/

136

while(e_atual->p_prox != NULL){ e_atual = e_atual->p_prox; } e_atual->p_prox = p_Novo; } }

Remoo
Assim como na insero tambm existem 3 tipos de remoo, no incio, no fim ou entre dois elementos da lista.

Remoo no incio
void remover_No_Inicio(struct No **p_Raiz){ if(*p_Raiz == NULL) printf("\nA lista ja esta vazia\n"); else{ struct No *p_atual; p_atual = *p_Raiz; *p_Raiz = (*p_Raiz)->p_prox; free(p_atual); } }

Remoo no fim
void remover_No_Fim(struct No **p_Raiz){ if(*p_Raiz == NULL) printf("\nA lista ja esta vazia"); else{ struct No *p_atual, *p_anterior ; p_atual = *p_Raiz; while(p_atual->p_prox != NULL){ p_anterior = p_atual ; p_atual = p_atual->p_prox; } p_anterior->p_prox = NULL; free(p_atual); } }

Listas encadeadas

137

Exibio
Do fim para a raiz
void mostrar_Do_Fim_Para_Raiz(struct No *p_Raiz){ if(p_Raiz == NULL) printf("\nLista vazia"); else{ struct No *p_Atual_Corredor, *p_Atual_Fim; p_Atual_Corredor = p_Raiz; p_Atual_Fim = p_Raiz; while(p_Atual_Fim->p_prox != NULL){ //ir para o ultimo elemento p_Atual_Fim = p_Atual_Fim->p_prox; } while(p_Atual_Corredor != p_Atual_Fim){ if(p_Atual_Corredor->p_prox == p_Atual_Fim){ printf(" <- %s", p_Atual_Fim->p_dados); p_Atual_Fim = p_Atual_Corredor; p_Atual_Corredor = p_Raiz; } else p_Atual_Corredor = p_Atual_Corredor->p_prox; } printf(" <- %s", p_Atual_Fim->p_dados); } }

Da raiz para o fim


void mostrar_Da_Raiz_Para_Fim(struct No *p_Raiz){ if(p_Raiz == NULL) printf("\nLista vazia"); else{ struct No *p_atual; p_atual = *p_Raiz; while(p_atual != NULL){ printf("%s", p_atual->p_dados); p_atual = p_atual->p_prox; } } }

Pilha

138

Pilha
Pilha
Pilha ou stack uma lista linear em que todas as inseres e remoes de elemento s podem ser feitos em uma extremidade chamada topo.As pilhas tambm so chamadas de estruturas LIFO (Last In First Out) ou seja o ltimo elemento inserido o primeiro removido.

Construo do prottipo de um elemento da lista.


typedef struct Elemento_da_lista{ char *dados; struct Elemento_da_lista *proximo; }Elemento;

typedef struct Localizar{ Elemento *inicio; int tamanho; } Pilha;

Inicializao
void iniciar (Pilha *monte){ monte->inicio = NULL; monte->tamanho = 0; }

Inserir um elemento na pilha(push)


Algoritmo: Declarao do elemento(s) a ser inserido. Alocao da memria para o novo elemento Inicializar o campo de dados. Preencher o ponteiro inicio com o primeiro elemento Colocar em dia o tamanho da pilha. int empilhar(Pilha * monte, char *dados){ Elemento *novo_elemento; if ((novo_elemento = (Elemento *) malloc (sizeof (Elemento))) == NULL) return -1; if ((novo_elemento->dados = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (novo_elemento->dados, dados); novo_elemento->proximo = monte->inicio; monte->inicio = novo_elemento; monte->tamanho++;

Pilha }

139

Retirar um elemento da pilha (pop)


int desempilhar (Pilha *monte){ Elemento *p_elemento; if (monte->tamanho == 0) return -1; p_elemento = monte->inicio; monte->inicio = monte->inicio->proximo; free (p_elemento->dados); free (p_elemento); monte->tamanho--; return 0; }

Imprimir os elementos da pilha


void mostrar(Pilha * monte){ Elemento *atual; int i; atual = monte->inicio; for(i=0;i<monte->tamanho;++i){ printf("\t\t%s\n", atual->dados); atual = atual->proximo; } }

rvores binrias

140

rvores binrias
Arvore binria
Uma arvore binria uma estrutura de dados que pode ser representada como uma hierarquia onde cada elemento chamado de n . O n inicial ou o primeiro elemento chamado de raiz. Em uma arvore binria um elemento pode ter um mximo de dois filhos no nvel inferior denominados como sub-rvore esquerda e sub-rvore direita.Um n sem filhos chamado de folha . A profundidade de um n a distncia deste n at a raiz e a distancia entre a folha mais distante e a raiz a altura da arvore.Um conjunto de ns com a mesma profundidade denominado, nvel da rvore.

Struct
typedef struct No{ int numero; struct No *pEsquerda; struct No *pDireita; }No;

Iniciar
void criarArvore(No **pRaiz){ *pRaiz = NULL; }

Insero
void inserir(No **pRaiz, int numero){ if(*pRaiz == NULL){ *pRaiz = (No *) malloc(sizeof(No)); (*pRaiz)->pEsquerda = NULL; (*pRaiz)->pDireita = NULL; (*pRaiz)->numero = numero; }else{ if(numero < (*pRaiz)->numero) inserir(&(*pRaiz)->pEsquerda, numero); if(numero > (*pRaiz)->numero) inserir(&(*pRaiz)->pDireita, numero); } }

Remoo
void remover(No **pRaiz, int numero){ No *pAux = NULL; if(numero < (*pRaiz)->numero) remover(&(*pRaiz)->pEsquerda, numero); else if (numero > (*pRaiz)->numero) remover(&(*pRaiz)->pDireita, numero);

rvores binrias else{ pAux = *pRaiz; if((*pRaiz)->pEsquerda == NULL) *pRaiz = (*pRaiz)->pDireita; else if((*pRaiz)->pDireita == NULL) *pRaiz = (*pRaiz)->pEsquerda; else{ noMaior(&(*pRaiz)->pEsquerda); (*pRaiz)->numero = pAux->numero; } } } OU Fazendo Recurso: int mydel(no **raiz){ if ((*raiz)->esq==NULL) { int n = (*raiz)->info; (*raiz)=(*raiz)->dir; return n; } return (mydel(&(*raiz)->esq)); } int remove(int n, no **raiz){ if((*raiz)==NULL) return 0; if ((*raiz)->info==n){ if((*raiz)->esq==NULL) (*raiz)=(*raiz)->dir; struct no *aux; aux=(*raiz); free(aux); }else{ (*raiz)->info=mydel(&(*raiz)->dir); return 1; } if ((*raiz)->info>n) return (remove (n,&(*raiz)-> esq)); return (remove (n,&(*raiz)-> dir));

141

rvores binrias

142

Exibio
Em ordem
void exibirEmOrdem(No *pRaiz){ if(pRaiz != NULL){ exibirEmOrdem(pRaiz->pEsquerda); printf("\n%i", pRaiz->numero); exibirEmOrdem(pRaiz->pDireita); } }

Pr-ordem
void exibirPreOrdem(No *pRaiz){ if(pRaiz != NULL){ printf("\n%i", pRaiz->numero); exibirPreOrdem(pRaiz->pEsquerda); exibirPreOrdem(pRaiz->pDireita); } }

Ps-ordem
void exibirPosOrdem(No *pRaiz){ if(pRaiz != NULL){ exibirPosOrdem(pRaiz->pEsquerda); exibirPosOrdem(pRaiz->pDireita); printf("\n%i", pRaiz->numero); } }

Contar ns
int contarNos(No *pRaiz){ if(pRaiz == NULL) return 0; else return 1 + contarNos(pRaiz->pEsquerda) + contarNos(pRaiz->pDireita); }

Contar folhas
int contarFolhas(No *pRaiz){ if(pRaiz == NULL) return 0; if(pRaiz->pEsquerda == NULL && pRaiz->pDireita == NULL) return 1; return 0 + contarFolhas(pRaiz->pEsquerda) +

rvores binrias contarFolhas(pRaiz->pDireita); }

143

Altura da rvore
int maior(int a, int b){ if(a > b) return a; else return b; }

int altura(No *pRaiz){ if((pRaiz == NULL) || (pRaiz->pEsquerda == NULL && pRaiz->pDireita == NULL)) return 0; else return 1 + maior(altura(pRaiz->pEsquerda), altura(pRaiz->pDireita)); }

Algoritmos de ordenao
Insertion sort
void insertion_sort(int tabela[], int largura) { int i, memoria, contador; bool marcador; for(i=1; i<largura; i++) { memoria = tabela[i]; contador = i-1; do { marcador = false; if(tabela[contador] > memoria) { tabela[contador+1] = tabela[contador]; contador--; marcador = true;

Algoritmos de ordenao } if(contador < 0) marcador = false; } while(marcador); } tabela[contador+1] = memoria;

144

Bubble sort
O bubble sort, ou ordenao por flutuao (literalmente "por bolha"), um algoritmo de ordenao dos mais simples. A ideia percorrer o vetor diversas vezes, a cada passagem fazendo flutuar para o topo o maior elemento da sequncia. Essa movimentao lembra a forma como as bolhas em um tanque de gua procuram seu prprio nvel, e disso vem o nome do algoritmo. No melhor caso, o algoritmo executa No pior caso, so feitas operaes relevantes, onde n representa o nmero de elementos do vetor. operaes. A complexidade desse algoritmo de Ordem quadrtica. Por isso, ele no

recomendado para programas que precisem de velocidade e operem com quantidade elevada de dados.

Cdigo da Funo
void BubbleSort(int vetor[], int tamanho) { int aux, i, j; for(j=tamanho-1; j>=1; j--) { for(i=0; i<j; i++) { if(vetor[i]>vetor[i+1]) { aux=vetor[i]; vetor[i]=vetor[i+1]; vetor[i+1]=aux; } } } }

Cdigo da Funo Melhorado


Termina a execuo quando nenhuma troca realizada aps uma passada pelo vetor. void BubbleSort(int vetor[], int tamanho) { int memoria, troca, i, j; troca=1; /*A varivel "troca" ser a verificao da troca em cada passada*/

Algoritmos de ordenao for(j=tamanho-1; (j>=1) && (troca==1); j--) { troca=0; /*Se o valor continuar 0 na prxima passada quer dizer que no houve troca e a funo encerrada.*/ for(i=0; i<j; i++) { if(vetor[i]>vetor[i+1]) { memoria=vetor[i]; vetor[i]=vetor[i+1]; vetor[i+1]=memoria; troca=1; /*Se houve troca, "troca" recebe 1 para continuar rodando.*/ } } } }

145

Algoritmo de alocao
worst fit
O algoritmo worst fit aloca o bloco de memria na regio que tem o maior espao livre. Est tcnica por procurar ocupar primeiro as parties maiores termina por deixar espaos livres que poderiam ser utilizados para que outros blocos de outros programas as utilizassem, diminuindo e/ou retardando a fragmentao.

Estudo

146

Estudo
PROGRAMAR EM C
Manual autodidacta,

Comear a programar em C
Vamos buscar o programa que escrevemos anteriormente. melhor memorizar estas linhas. /* o meu primeiro programa */ #include <stdio.h> #include <stdlib.h> int main() { printf("This is output from my first program!\n"); system ("pause"); return 0; } #include <stdio.h> O smbolo # uma chamada de ateno ao compilador quando ele fizer o seu trabalho ter um cuidado exclusivo com esta linha. Depois temos o include (que basicamente diz para incluirmos). Incluir o qu? O arquivo-cabealho stdio.h (std = standard, padro em ingls; io = Input/Output, entrada e sada ==> stdio = Entrada e sada padronizadas; entrada temos o keyboard e sada temos o ecr). A terminao .h vem de header e normalmente tratam-se de bibliotecas. Existem outros arquivos para alm do stdlib.h, temos a biblioteca de matemtica, a de tempo, etc . Podemos ver na parte dos anexos algumas que existem e com as funes de cada uma. Mas, ns prprios podemos criar uma biblioteca. E at podemos comprar bibliotecas que existem por a. Bibliotecas so cdigos que alguns programadores fizeram antes. As que citamos primeiramente so as standard, so aquelas que tm as funcionalidades bsicas. Veja voc que precisamos da biblioteca at para escrever no ecr (std + out) que nos permite utilizar a funo printf. Eu recorri biblioteca do stdlib.h para poder utilizar a funo system (pause). Caso contrrio, o programa era executado e corrido e ns no vamos nada, a janela abria e fechava-se e ns no perceberamos a sada "Hello World". int main() a funo principal. Em programao em C temos funes atrs de funes. Esta funo main() a tal com que vamos chamar as outras. Por isso mesmo ela obrigatria em qualquer programa. Mas ela a primeira funo a correr e quando o programa acabar de correr a funo main o programa acaba! a primeira e ultima. o int significa que a funo vai retornar um inteiro. So a variedade de tipos. Temos int que a abreviatura de inteiro char que a abreviatura de character float que a abreviatura de floating number, ou seja um nmero real temos mais mas por enquanto vamos continuar.(ver a seco resumo e quadros -anexos) Note que todas as funes tm ( ), s assim que o compilador reconhece que uma funo. A idia de ter funes permitir o encapsulamento de uma idia ou operao, dar um nome a isso e depois chamar essa operao de vrias partes do programa simplesmente usando o seu nome. Ns podemos chamar funes j existentes do C e tambm criar as nossas prprias funes

Estudo dando nomes a elas. Uma das grandes vantagens nisto a economia de cdigo que se pode ter durante a construo de um programa. {} O compilador ao ver as chavetas compreende aquilo como um bloco. O cdigo que estiver dentro das chaves ser executado sequencialmente. printf () Podemos utilizar esta funo apenas porque pusemos no header a biblioteca stdio . O que esta funo nos permite enviar o que temos entre aspas para o monitor (out). Mas termos a hiptese de poder formatar o texto enviado, por exemplo se acrescentssemos \n a meio tipo: printf("This is output \n from my first program! "), o resultado seria chegar ao fim do output e teriamos uma nova linha. experiementem! return 0 faz com que a funo retorne o valor zero que o fim da execuo do programa. repare-se que podamos ter posto return (0) que at seria mais correcto, mas como o valor zero, o compilador aceita. ; o ponto e virgula essencial para a programao em C, funciona como o ponto final: separa as frases e contextos. No C, o ponto e vrgula est no final da maioria das linhas e determina o final de cada "operao". Na sua ausncia teremos erro enquanto compilamos o programa. /*o meu primeiro programa*/ Como qualquer outra linguagem, o C nos permite inserir comentrios entrelaados no cdigo-fonte do programa, ou melhor permite escrever o que pretendermos entre /* e */ que na hora da compilao estes sero descartados: no tm utilidade em nada para o compilador, no entanto so bons para ns programadores, pois pode nos auxiliar a no ficarmos perdidos durante a programao de um programa de cdigo-fonte muito extenso.

147

2 Programa
#include <stdio.h> /*ao evocarmos esta biblioteca podemos utilizar a funo printf e scanf*/ #include <stdlib.h> /*ao evocarmos esta biblioteca podemos utilizar a funo system (pause)*/ int main() /*declaramos que a funo main vai retornar um inteiro*/ { int a, b, c; /*estamos a declarar 3 variveis que vo ter valores inteiros */ a = 5; /*aqui estamos a alocar o valor 5 varivel a*/ printf (digite um valor); /* escrito no ecr a frase digite um valor*/ scanf (%d, &b); /*a funo l algo que se espera que esteja no formato de inteiro e associa ao endereo da varivel b, ie, escreve o valor recebido na varivel b o endereo vem do & */ c = a + b; /*aqui vai pegar no valor da varivel a e b vai proceder operao soma e o resultado vai ser colocado na varivel */ printf("%d + %d = %d\n", a, b, c); /*aqui a funo printf vai imprimir o texto entre aspas, mas antes vai substituir o %d pelo valor das vrias variavies*/ system (pause); /* com esta funo podemos ver o

Estudo resultado podemos fazer uma se antes de encerrar */ return 0; /*a funo main vai retornar o valor zero e encerra o programa*/ }

148

Declarando variveis
Vai-nos ocorrer a necessidade do programa guardar certos valores. Assim vamos querer declarar uma varivel para depois guardar um valor e mais tarde poder ser utilizado a sua forma geral : tipo_da_varivel lista_de_variveis; Para poder declarar uma varivel podemos escrever int a; Neste caso apenas estamos a declarar uma varivel que chamamos a. O que que est a acontecer? Estamos a pedir ao computador para reservar um espao da sua memria. e que depois associe esse espao de memria ao nome a. Assim o computador tem de saber onde aloca as coisas. como tivssemos um quadro de duas colunas, uma para os nomes e outra para o valor. a=5; Neste caso estamos a fazer com que o computador guarde o valor 5 no espao alocado varivel a; Como primeiro pedimos para reservar memria e depois escrevemos o valor, por isso que temos que dizer o quanto espao de memria que deve ser reservado da o int antes de a; Agora o que aconteceria se pusssemos a=5,555.. no sei qual que seria o resultado. Mas ou no compilaria, ou se isso acontecesse estaramos a alocar mais memria do que pedimos e portanto poderia acontecer estarmos a escrever em memria que pertencia a outro programa, logo podemos estar a corromper os nossos dados, penso que no coisa que se queira. Podemos declarar uma varivel ao mesmo tempo que lhe damos um valor, por exemplo: int a=5; Podemos ainda declarar quantas variveis queiramos ao mesmo tempo; int a, b,c,d; tm que ter as vrgulas no meio e ponto e virgula no final Podemos declarar vrias variveis ao mesmo tempo e associar os respectivos valores? int a=5, b=6; sim tambm podemos fazer isso, no esquecer as virgulas. Lembre-se sempre que necessrio declarar a varivel antes de atribuirmos um valor, caso contrrio esse valor ir para um stio qualquer na memria e bem possvel alterar uma memria j atribuda. E declarar a varivel antes de utilizarmos. O nome das variveis tero de ser nicas, como lgico! H diferena entre termos maisculas e minsculas (pois elas tm um valor diferente no cdigo ASCII) logo ter nome e NOME ou mesmo Nome so todas variveis diferentes o C CASE SENSITIVE Por fim no podem ter o nome das palavras reservadas que so os nomes das funes e tipologia, (ver quadro) Utiliza-se as letras nmeros e o sublinhado (_) Variveis at 32 caracteres so aceites pratica comum usar letras minsculas para nomes de variveis e maisculas para nomes de constantes.

Estudo

149

Palavras Reservadas do C
for continue break switch else case return goto default do while if struct typedef sizeof union extern enum int double long char float short unsigned void signed const register volatile static auto

Tipologia de variveis
C tem 5 tipos bsicos: 1. 2. 3. 4. 5. char, int, float, void, double

Quando declaramos uma varivel teremos de indicar de que tipologia a varivel . A razo disto tem a ver que cada tipologia ocupa um nmero de bits diferentes. e o computador agrega cada tipologia em tabelas distintas, por exemplo, uma para ints outra para floats, etc. ou seja uma das razes realmente a optimizao da memria. mas tambm existe uma outra razo que tem a ver com as operaes. isto , se tivermos um dado int, e se passarmos para o seguinte ns vamos passar para o prximo int. ou seja asseguramos que o nmero seguinte um int. convm ver a tabela das tipologias. Tipo Num de bits Formato para leitura com scanf Intervalo Inicio Fim 8 %c -128 127 8 %c 0 255 8 %c -128 127 16 %i -32.768 32.767 16 %u 0 65.535 16 %i -32.768 32.767 16 %hi -32.768 32.767 16 %hu 0 65.535 16 %hi -32.768 32.767 32 %li -2.147.483.648 2.147.483.647 32 %li -2.147.483.648 2.147.483.647 32 %lu 0 4.294.967.295 32 %f 3,4E-38 3.4E+38 64 %lf 1,7E-308 1,7E+308 80 %Lf 3,4E-4932 3,4E+4932

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 float double long double

para os nmeros reais temos as seguintes variantes: float double long double para os inteiros: int short int long int unsigned signed

Estudo

150

printf ()
A funo printf () est dentro da biblioteca stdio.h. O que ela faz enviar para out, no nosso caso o ecr o que est dentro das aspas. printf(amor mio); resultado: amor mio printf (a varivel a = %d, a); Aqui vai novamente imprimir no ecr o texto que est entre aspas, mas vai fazer umas substituies antes de o fazer. V que existe o smbolo % e vai substituir pelo valor da varivel a que era 5. O d a seguir ao %, uma formatao do valor que vai ser substitudo. O d de inteiro. Por isso, independentemente da tipologia que seja o valor a (quer seja inteiro, float) ele vai ser substitudo pelo seu valor como inteiro. printf (%c\t%f\t%d\t, a, a ,a); Aqui o que acontece pedirmos para fazer a substituio do primeiro % pelo valor da primeira varivel, o segundo % pela segunda, etc e por a adiante. No exemplo tnhamos o valor 5 na varivel a, e aqui pedimos para escrever o 5 em c de carcter, em f de float e em d de inteiro.

Formataes do printf
Constantes de barra invertida Cdigo \b \f \n \t \" \' \0 \\ \v \a \N \xN Cdigo %c %d %i %e %E %f %g %G Significado Retrocesso ("back") Alimentao de formulrio ("form feed") Nova linha ("new line") Tabulao horizontal ("tab") Aspas Apstrofo Nulo (0 em decimal) Barra invertida Tabulao vertical Sinal sonoro ("beep") Constante octal (N o valor da constante) Constante hexadecimal (N o valor da constante) Formato Um caracter (char) Um nmero inteiro decimal (int) O mesmo que %d Nmero em notao cientfica com Nmero em notao cientfica com Ponto flutuante decimal Escolhe automaticamente o melhor Escolhe automaticamente o melhor

o "e"minsculo o "e"maisculo entre %f e %e entre %f e %E

Estudo %o %s %u %x %X %% %p Nmero octal String Decimal "unsigned" (sem sinal) Hexadecimal com letras minsculas Hexadecimal com letras maisculas Imprime um % Ponteiro

151

possvel tambm indicar o 1. . Tamanho do campo, 2. . Justificao e o 3. . Nmero de casas decimais %5d estamos indicando que o campo ter cinco caracteres de comprimento no mnimo Se o inteiro precisar de mais de cinco caracteres para ser exibido ento o campo ter o comprimento necessrio para exibi-lo. Se o comprimento do inteiro for menor que cinco ento o campo ter cinco de comprimento e ser preenchido com espaos em branco. Se se quiser um preenchimento com zeros pode-se colocar um zero antes do nmero O alinhamento padro direita. Para se alinhar um nmero esquerda usa-se um sinal antes do nmero de casas. Ento %-5d ser o nosso inteiro com o nmero mnimo de cinco casas, s que justificado a esquerda. %10.4f indica um ponto flutuante de comprimento total dez e com 4 casas decimais. Cdigo Imprime printf ("%-5.2f",456.671); printf ("%5.2f",2.671); printf ("%-10s","Ola");

| 456.67| | 2.67| |Ola |

Operaes
o C permite realmente fazer umas operaes entre valores. Ou seja com estas armas podemos fazer operaes entre valores, variveis (e os seus valores) e ainda os endereos das variveis. temos os operadores aritmticos, os relacionais e lgicos e os especficos para os bits. Operadores Aritmticos e de Atribuio Operador Ao + Soma (inteira e ponto flutuante) Subtrao ou Troca de sinal (inteira e ponto flutuante) * Multiplicao (inteira e ponto flutuante) / Diviso (inteira e ponto flutuante) % Resto de diviso (de inteiros) ++ Incremento (inteiro e ponto flutuante) -Decremento (inteiro e ponto flutuante) Operadores Relacionais e Lgicos Operador Ao > Maior do que

Estudo >= < <= == != Maior ou igual a Menor do que Menor ou igual a Igual a Diferente de

152

(repare no valor = = quando queremos comparar. no C o = associativo, ie, como dizer atribui o valor. por isso temos o igual igual para fazer a distino quando queremos comparar os valores. Ver o quadro dos operadores lgicos, especialmente os smbolos: && o || e o ! que so and, or, e not, respectivamente que so os mais estranhos.) Operador Ao && AND (E) || OR (OU) ! NOT (NO) Operadores Lgicos Bit a Bit Operador Ao & AND | OR ^ XOR (OR exclusivo) ~ NOT >> Deslocamento de bits direita << Deslocamento de bits esquerda valor>>nmero_de_deslocamentos Pergunta: mas como que impriminos um n na forma binria, uma vez que temos operadores binrios??? ou melhor: ainda no vi nada sobre os bits. para os operadores de aritmtica convm dizer que o C permite algumas abreviaturas, o que me parece ser uma estupidez permiti-las uma vez que elas no poupam assim muito, e claro, tm o inconveniente de obrigar a memorizar. Expresso Original Expresso Equivalente x=x+k; x+=k; x=x-k; x-=k; x=x*k; x*=k; x=x/k; x/=k; x=x>>k; x>>=k; x=x<<k; x<<=k; x=x&k; x&=k; etc...a seguir abreviatura de abreviatura X++ equivale a x+=1 #include <stdio.h> #include <stdlib.h> main() { int a,b;

Estudo a = b = 5; printf("%d\n", ++a+5); printf("%d\n", a); printf("%d\n", b++ +5); printf("%d\n", b); system (pause); return 0; } Este exemplo mostra que no bem igual ter ++a ou a++. este ultimo adiciona quando for passar para a linha seguinte. Convm ainda dizer que nesta linguagem toda do C h certos smbolos que tm maior precedncia. isto perfeitamente normal ter de esclarecer uma vez que admitimos operaes aritmticas. e a sequencia nem sempre deve ser seguida. melhor dar um exmplo: se eu tiver a=10+2*3. se eu fizesse isto sequencialmente daria a=12*3 e a=36 se eu der agora precedncia de operadores, e portanto a hierarquia dos operadores que vai dar o resultado. ficaria a=10+6=16. apanharam o ponto! espero que sim porque bom ter em mente sempre esta tabela. se uma pessoa tiver duvidas pode sempre recorrer ao topo e ao fim da hierarquia e fazer sempre eg. a=10+(2*3). em caso de igualdade de precedncia predomina a sequencia de enumerao, ou seja o que vem antes Maior precedncia () [] -> ! ~ ++ -- . -(unrio) (cast) *(unrio) &(unrio) sizeof * / % + << >> <<= >>= == != & ^ | && || ? = += -= *= /= , Menor precedncia #include <stdio.h> #include <stdlib.h> main() { int i,j;

153

Estudo float f; i = 5; j = 2; f = 3.0; f = f + j / i; printf("value of f is %f\n", f); system (pause); return 0; }

154

. Modeladores (Cast)
Ora bem j temos a tipologia de variveis e agora mesmo as operaes aritmticas, convm agora explicar o ponto comum entre as duas. Se eu tiver a=10/3, o resultado sei que 3,(3). Ou seja a diviso de dois nmeros inteiros deu um nmero real. acontece que se eu declarar int a; Estou a dizer que ele ser inteiro, o resultado seria 3. mesmo que eu diga float a; o resultado continua a ser 3 mas desta vez, 3,0000 ou seja tenho tenho de converter um dos inteiros (pelo menos) em float, (que o outro se converte). ento eu poderia fazer a=(float)10/3; forma geral : (tipo)expresso mas existem umas converses automticas:
int f(void) { float f_var; double d_var; long double l_d_var; f_var = 1; d_var = 1; l_d_var = 1; d_var = d_var + f_var; l_d_var = d_var + f_var; return l_d_var; } /*o float convertido em double*/ /*o float e o double convertidos em long double*/

repare-se que a converso feita para o maior. possvel fazer a converso ao contrrio de um tipo com mais bits para um com menos bits e isso truncar.

Estudo

155

. scanf ()
O scanf() uma funo que est tambm na biblioteca stdio.h o que faz ler o que foi inserido, por exemplo, via teclado e guardar o que foi escrito. scanf (%d, &a); Neste caso estamos a dizer para a varivel ler o que foi teclado, eg, que vai ser um inteiro, ou melhor em formato inteiro e depois colocar esse valor no endereo da varivel a Na verdade confesso que no percebo bem esta parte o smbolo & smbolo de endereo fsico. Ou seja ns criamos a varivel a antes. e agora estamos a dizer para alocar o valor que for teclado nesse espao. assim porque escrever & ,quando nos bastaria escrever a? Isto um pouco intrigante. mas vamos ter a resposta daqui a um pouco. scanf (%d%c%f, &a,&b,&c); Tal como na funo printf, podemos receber quantos valores quisermos, neste caso, estamos espera de um inteiro e associamos ao primeiro endereo, o 2 ao 2 endereo, e por a adiante, repare-se que tm de estar separados por virgulas Respondendo questo intrigante que coloquei aqui, penso que a resposta pode ser esta: utiliza-se um intermedirio. ou seja, ao escrevermos vai ser reservado um espao pelo sistema operativo e vai colocar esse valor l nesse espao reservado por ele, ns depois como queremos dar o nome de a, vamos fazer com que ele aponte para o valor de a. No percebo bem a vantagem disto masat faz sentido porque asim no temos de fazer o copy para a nova morada e eliminar a antiga. uma etapa extra sem qualquer necessidade Fao aqui uma questo: vamos imaginar que imprimamos 12.5 e estvamos espera de receber um int. o que que vai acontecer. vamos testar. #include <stdio.h> #include <stdlib.h> int main () { int a; printf ("digite um nmero); scanf (%d, &a); printf ("\no n impresso foi %d, a); system (pause); return 0; } ele s vai pegar no 12! vou retomar esta funo mais tarde por causa do & que convm perceber!!! Prottipo: int scanf (char *str,...); Devemos sempre nos lembrar que a funo scanf() deve receber ponteiros como parmetros. Isto significa que as variveis que no sejam por natureza ponteiros devem ser passadas

Estudo precedidas do operador &. Cdigo %c %d %i %hi %li %e %f %lf %h %o %s %x %p Formato Um nico caracter (char) Um nmero decimal (int) Um nmero inteiro Um short int Um long int Um ponto flutuante Um ponto flutuante Um double Inteiro curto Nmero octal String Nmero hexadecimal Ponteiro

156

Branching - IF
#include <stdio.h> /*ao evocarmos esta biblioteca podemos utilizar a funo printf e scanf*/ #include <stdlib.h> /*ao evocarmos esta biblioteca podemos utilizar a funo system (pause) */ int main() /*declaramos que a funo main vai retornar um inteiro*/ { int a; /*estamos a declarar 1 variveis que vai ter valores inteiros */ printf (digite uma valor); /* escrito na tela a frase*/ scanf (%d, &a); /*a funo l algo que se espera que esteja no formato de inteiro e associa ao endereo da varivel a, ie, escreve o valor recebido na varivel b*/ if (a<10) /*verifica a condio a<10, se for verdadeira l o cdigo do bloco*/ printf ("o valor %d menor que 10\n", a); /*aqui a funo printf vai imprimir o texto entre aspas,mas antes vai substituir o %d pelo valor das vrias variareis/ system (pause); /* com esta funo podemos ver o resultado podemos fazer uma pause antes de encerrar */ return 0; /*a funo main vai retornar o valor zero e encerra o programa*/ } A funo if muito parecida com a ideia que ns temos na linguagem comum, ou seja, se (isto)ento (aquilo). mas vamos ver algumas variantes, para dar mais flexibilidade. mas o sistema vai ser sempre igual. if (condio) declarao;

Estudo Se a condio for avaliada como zero, a declarao no ser executada if (condio) declarao_1; else declarao_2; Se a condio for avaliada como diferente de zero, a declarao_1 ser executada. caso contrrio ser a declarao_2. Repare que estamos a garantir que uma das declaraes ser sempre executada if (condio_1) declarao_1; else if (condio_2) declarao_2; . else if (condio_n) declarao_n; else declarao_default o programa comea a testar as condies comeando pela 1 e continua a testar at que ele ache uma expresso cujo resultado d diferente de zero. Neste caso ele executa a declarao correspondente. S uma declarao ser executada, ou seja, s ser executada a declarao equivalente primeira condio que der diferente de zero. A ltima declarao (default) a que ser executada no caso de todas as condies darem zero e opcional. Podemos ter ainda os if aninhados que basicamente um if dentro do outro. #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; } Mais abreviaturas!!! Continuo a dizer que uma estupidez isto das abreviaturas para no escrever uns poucos de caracteres!!

157

Estudo
if (num!=0) if (num==0) for (i=0 ; string[i] !=\0 ; i++) equivalente a ter equivalente a ter equivalente a ter if (num) if (!num)

158

for (i=0; string[i]; i++)

Comando "?"
Temos ainda o operador ? que mais outro tipo de abreviaturas if (a>0) b=-150; else b=150; b=a>0?-150:150; condio?expresso_1:expresso_2; esta porcaria abreviaturas que torna isto horrvel. obriga a um esforo elevado de memorizao sem ter um retorno. bem o retorno atrevo-me a dizer que at negativo!

O Comando switch
A forma geral switch (varivel) { case constante_1: declarao_1; break; case constante_2: declarao_2; break; . . . case constante_n: declarao_n; break; default declarao_default; } O comando break, faz com que o switch seja interrompido assim que uma das declaraes seja executada #include <stdio.h> int main () { int num; printf ("Digite um numero: "); scanf ("%d",&num); switch (num)

Estudo { 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; }

159

LOOP- WHILE
O que que um loop. Basicamente repetir um conjunto de linhas vrias vezes at ie, sujeito a uma condio. O while() testa uma condio. Se esta for verdadeira a declarao executada e faz-se o teste novamente Se eu tiver. while (a<b) { printf (%d menor que %d,a, b) a=a+1 } Em portugus seria enquanto a condio for verdadeira repete o cdigo do bloco. Repare-se que como fosse um if, ie, se a condio for verdadeira faz isto. S que o isto repetido enquanto a condio for verdadeira. Volta ao incio do bloco. enquanto o if no! segue sempre em frente.

LOOP DO WHILE
do { printf (%d\n, a); a=a+1; } while (a<b); A funo do while exactamente igual do while s que pe a condio depois do bloco, o que significa que o bloco executado pelo menos uma vez. do { declarao; } while (condio);

Estudo O ponto-e- vrgula final obrigatrio. A estrutura do-while executa a declarao, testa a condio e, se esta for verdadeira, volta para a declarao. Garante que a declarao ser executada pelo menos uma vez. Um dos usos da estrutura do-while em menus #include <stdio.h> #include <stdlib.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; } system (pause); return 0; }

160

LOOP FOR
Este para mim a melhor funo, muito compacta e extremamente poderosa a sua forma geral for (inicializao;condio;incremento) declarao; for (a=1; a<10; a=a+1) { cdigo do bloco } o que diz para a igual a 1, at que a seja menor que 10, executa o cdigo do bloco e depois executa o incremento de uma unidade a a. Volta a repetir o processo at que a fique maior ou igual a 10. note que quando fazemos a=1, estamos a atribuir o valor 1 a a.

Estudo a funo loop to verstil que podemos dar vrias condies de iniciao, vrias de finalizao e vrias de incremento. for (a=1, b=1; a<10 || b>20 ; a=a+1, b++) { cdigo do bloco } Neste caso temos duas condies de iniciao. Repare que esto separadas por virgulas. Temos depois duas de finalizao com o operador lgico ou a primeira das condies que se tornar verdadeira ir parar o ciclo. Se a condio for verdadeira ele executa a declarao, faz o incremento e volta a testar a condio. Ele fica repetindo estas operaes at que a condio seja falsa. Um ponto importante que podemos omitir qualquer um dos elementos do for, isto , se no quisermos uma inicializao poderemos omiti-la. repare que se eliminarmos a condio,[ for (inicializao; ;incremento) declarao;] temos um loop infinito

161

O Comando break
O break faz com que a execuo do programa continue na primeira linha seguinte ao loop ou bloco

O Comando continue
Quando o comando continue encontrado, o loop pula para a prxima iterao, sem o abandono do loop. melhor ver o exemplo:
#include <stdio.h> #include <stdlib.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; switch (opcao) { case 1: printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); /* se Opcao invalida: volta ao inicio do loop */

Estudo
break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break; } } system (pause); return 0; }

162

recebe uma opo do usurio. Se esta opo for invlida, o continue faz com que o fluxo seja desviado de volta ao incio do loop. Caso a opo escolhida seja vlida o programa segue normalmente.

O Comando goto
O goto realiza um salto para um local especificado. Este local determinado por um rtulo. Portanto pode ser em qualquer parte do programa. nome_do_rtulo: .... goto nome_do_rtulo; ....

ARRAYS VECTORES
Bem podemos dizer que ao entrar agora nesta parte vamos partir para uma segunda etapa, como tivssemos a criar mais dimenses. Aqui vamos! Aqui vamos declarar novamente variveis. S que desta vez vamos pensar como conseguiramos criar 1000 variveis. Como vimos declarar variveis era apenas int a; Agora podemos fazer int a[1000]; Funciona tipo um ndice, ie, cria o a[0], a[1], a[2].. a[999]. repare-se que comea no 0 e no 1. Maior parte dos bugs vm daqui pensarmos que o ndice vai at 1000!!!! Podemos at ter int a[i]; for (i=0; i<5; i++) a[i]=i; Neste exemplo iramos ter i at 5 e cada um com um valor j atribudo. Este exemplo realmente est engraado. Porque podamos brincar never mind

Estudo

163

Matrizes
se ns pensarmos melhor, podemos declarar ainda mais variveis tipo_da_varivel nome_da_varivel [altura][largura]; Ter em ateno que: ndice mais direita varia mais rapidamente que o ndice esquerda. No esquecer os ndices variam de zero ao valor declarado, menos um

multidimensionais
podemos ter ainda conjunto de variveis multidimensionais. tipo_da_varivel nome_da_varivel [tam1][tam2] ... [tamN]; onde a iniciao : tipo_da_varivel nome_da_varivel [tam1][tam2] ... [tamN] = {lista_de_valores}; 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" }; Podemos, em alguns casos, inicializar matrizes das quais no sabemos o tamanho a priori. O compilador C vai, neste caso verificar o tamanho do que voc declarou e considerar como sendo o tamanho da matriz. Isto ocorre na hora da compilao e no poder mais ser mudado durante o programa

FUNES
Como dissemos no incio, as funes no so mais do que pedaos de cdigo que estava na funo main() e que resolvemos tirar para fora do main e demos um nome. Isto tem duas vantagens: Uma primeira porque temos blocos mais pequenos de cdigo e permite uma melhor leitura e deteco de erros. Por outro lado permite utilizar vrias vezes ao longo do bloco do main, apenas chamando. S temos que cumprir com alguns requisitos: necessitamos de dizer que tipologia de retorno vai ter e que parmetros vo receber. Os parmetros so as variveis que colocamos dentro das (). Isto facilita porque podemos necessitar do resultado de uma funo para executar uma outra. Um aparte: Mas existe aqui um problema para mim. Que : e quando tivermos uma funo que ir retornar vrios valores de vrias tipologias? Ser que temos que criar funes separadas para cada tipologia. Bem at pode fazer sentido! A idia de uma funo est, naturalmente, permitir voc encapsular um idia ou operao, dando um nome a ela, ento chamar que operao de vrias partes do resto de seu programa simplesmente usando o seu nome. Estando corretamente projetado e estruturado os programas, dever ser possvel a mudana do caminho que uma funo faz seu trabalho (como espera-se que o trabalho em si prprio no

Estudo muda) sem efeito no resto do programa. Exemplo:imaginemos que tnhamos um cdigo do tipo #include <stdio.h> #include <stdlib.h> int main () { printf ("Ola!\n "); printf ("Eu estou vivo!\n"); system (pause); return 0; } aqui a nossa funo main () vai imprimir apenas duas frases! mas agora imaginemos que o nosso cdigo comeava a ter muitas e muitas linhas e necessitvamos de colocar vrias vezes o printf ("Ola!\n "); ento o que que fazamos? crivamos uma funo. tirvamos para fora do main e escrevamos da seguinte maneira: #include <stdio.h> #include <stdlib.h> int mensagem () { printf ("Ola!\n "); return 0; } int main () { mensagem(); printf ("Eu estou vivo!\n"); system (pause); return 0; }

164

/* Funo simples: s imprime Ol! */

Ou seja crimos uma funo sem argumentos. repare que so as chavetas () que dizem que aquilo uma funo vamos agora pegar num exemplo melhorzinho 2 Exemplo criao de funo com 1 argumento #include <stdio.h> #include <stdlib.h> int square (int x) /* Calcula o quadrado de x */ { printf ("O quadrado e %d\n\n ",(x*x)); return 0; } int main () { int num; printf ("Entre com um numero: "); scanf ("%d",&num);

Estudo square(num); system (pause); return 0; } repare no seguinte: quando chamamos a funo square() no main estamos a colocar a varivel do main! esse valor do num copiado e vai entrar como input da funo square. realizado as linhas de cdigo da funo square e quando estas terminarem voltamos para o main! 3 Exemplo criao de funo com 3 argumento #include <stdio.h> #include <stdlib.h> int mult (float a, float b,float c) { printf ("%f",a*b*c); return 0; } int main () { float x,y; x=23.5; y=12.9; mult (x,y,3.87); system (pause); return 0; }

165

/* Multiplica 3 numeros */

Neste exemplo temos inputs que podem ser constantes, por isso no necessrio serem variveis! mas convm dizer o seguinte: 1. . Em primeiro lugar temos de satisfazer aos requisitos da funo quanto ao tipo e quantidade de argumentos quando a chamamos. Apesar de existirem algumas converses de tipo, que o C faz automaticamente, importante ficar atento. 1. . Em segundo lugar, no importante o nome da varivel que se passa como argumento, ou seja, a varivel num, ao ser passada como argumento para square() copiada para a varivel x. Dentro de square() trabalha-se apenas com x. Se mudarmos o valor de x dentro de square() o valor de num na funo main() permanece inalterado 1. . Repare que, neste caso, os argumentos so separados por vrgula e que deve-se explicitar o tipo de cada um dos argumentos, um a um. Note, tambm, que os argumentos passados para a funo no necessitam ser todos variveis porque mesmo sendo constantes sero copiados para a varivel de entrada da funo. 1. . h ainda um outro requisito, que a funo main aquela que primeiro executada, e sequencial. Agora no sei bem porqu, mas as funes extras tm de ser enumeradas antes do main(). tal como os headers. Penso que porque o compilador tem de saber que tipo de retorno vai ter. Da surgiram os prottipos.

Estudo

166

FUNCTION PROTOTYPE
Bem, isto no mais do que aumentar um pouco a nossa flexibilidade. Ns nas funes temos de declar-las antes da funo main. Aqui as funes prottipo pegar nas funes e coloc-las depois da funo main, com a grande particularidade de termos de copiar a primeira linha da declarao da funo e coloc-la antes da funo main. Ou seja a Linguagem c necessita de saber que tem uma funo tal que vai retornar um inteiro, ou float e vai receber de parmetros x,y, e z que so todos inteiros. Isto tem ainda um outro benefcio que no me apercebi de imediato que a questo do C se utilizarmos prottipos, ir confirmar os tipos e a lista de parmetros. um aparte. isto poderia ser muito bem contornado se em vez de se declarar nome_da_funo() fazer tipo_da_funo nome_da_funo() dentro da funo main() ou seja temos a diferena entre declarao e definio de funes exemplo de um prototype. #include <stdio.h> float Square (float a); int main () { float num; printf ("Entre com um numero: "); scanf ("%f",&num); num=Square(num); printf ("\n\nO seu quadrado vale: %f\n",num); return 0; } float Square (float a) { return a*a; } A diferena entre parmetros e argumentos: parametros so o nome das variaveis inputs uma funo argumentos so os valores dos argumentos quando uma funo chamada.

VOID
Void significa vazia em ingles acho que posso agora inserir um ponto que a tipologia void. como dissemos uma funo retorna um valor. e pode receber parmetros. como uma boa execuo do programa costuma ser zero. o void utilizado da seguinte forma: void funo(void) { } o que aqui estamos a dizer que temos uma funo que no vai receber parmetros nenhuns. e no vai retornar qualquer valor. Ou melhor o void uma explicitao do programador que aquela funo no vai receber nem dar nenhum valor. no quero saber dos valores .. o valor da funo ignorado, mas a funo realmente retorna um valor. por isso para que o resultado

Estudo seja interpretado como um erro e bom declarar void. Mais uma nota sobre o void. No se pode utilziar void na funo principal a main(), apesar de j ter visto isso em alguma bibliografia. A funo main() especial, tem de retornar um int no mnimo.

167

Variveis locais globais e passagem de parmetros por valor e referncia


Quando declaramos as variveis, ns podemos faz-lo dentro de uma funo ou fora de todas as funes inclusive a main(). As primeiras so as designadas como locais: s tm validade dentro do bloco no qual so declaradas. as ultimas so as globais, elas esto vigentes em qualquer uma das funes. A palavra reservada do C auto serve para dizer que uma varivel local. Mas no precisaremos us-la pois as variveis declaradas dentro de um bloco j so consideradas locais Quando uma funo tem uma varivel local com o mesmo nome de uma varivel global a funo dar preferncia varivel local. quando chamamos por uma funo e damos um parmetro, no exemplo anterior, passmos a varivel float num. mas ao parmetros formais o float a. E estes existem independentemente das variveis que foram passadas. eles tomam apenas uma cpia do valor passado. ou seja no so alterados os valores dos parmetros fora da funo. Este tipo de chamada de funo denominado chamada por valor. Isto importante porqu? Acho alis que dos mais importantes. Crucial! percebendo isto apanhamos os ponteiros com as pernas s costas!
#include <stdio.h> #include <stdlib.h> float sqr (float num); int main () { float num,sq; printf ("Entre com um numero: "); scanf ("%f",&num); sq=sqr(num); printf ("O seu quadrado vale: %f\n",sq); float a, b; printf ("Entre com um numero: "); scanf ("%f",&a); a=a*a; b=a; printf ("\n\nO numero original e: %f\n",a); printf ("O seu quadrado vale: %f\n",b); system (pause); return 0; } float sqr (float num) { /*descrio da funo sqr*/ /*associo o valor inserido variavel num*/ /*chamo a funo sqr e passo o parametro num*/ /*declaro 2 varaveis: num e sq*/ /*prottipo da funo sqr()*/

printf ("\n\nO numero original e: %f\n",num);

Estudo
num=num*num; return num; } /*retorna o num*/

168

Este exemplo explica um detalhe que a meu ver crucial, na compreenso disto tudo! quando a funo main() executada, ela chega a meio e v uma chamada para a funo sqr() e onde passado o parmetro num. Ora ela j estava espera pois viu o prottipo. ela ento vai executar a funo que est depois da funo do main() e o que acontece que o num, vai ficar com o dobro do valor. Esse valor do main vai entrar novamente no main.e associado varivel sq. depois temos a impresso da varivel num e sq. Ora o que acontece que o valor do num fica igual ao valor antes de entrar na funo. eu fao a mesma coisa agora com a varivel a e b, e vemos que agora a funo a alterada. resumindo, o valor varivel quando entra numa outra funo no alterado.!! Quando o valor do parmetro alterado denominado por chamada por referncia. O C no faz chamadas por referncia. mas podemos simular isto com outra arma do C que so os ponteiros. retornamos a este ponto aps desta seco seguinte.

POINTERS
Os pointers supostamente so um bicho de 7 cabeas. supostamente a parte mais difcil. E se uma pessoa compreender isto, torna-se craque em c. Uma varivel normal tem uma localizao na memria que pode conter um valor. Por exemplo quando declaramos int i; quatro bytes de memria so reservados e a esses 4 bytes chamamos de name i. Um pointer (ou ponteiro, em portugus) uma varivel que quando declarada, tambm reservado um espao na memria, mas em vez de guardar um valor guarda o endereo de memria de uma outra varivel, guarda a direco de uma outra varivel. Ponteiros guardam endereos de memria. Um ponteiro tambm tem tipo Qual a vantagem dos pointers. Uma das vantagens a situao, imaginemos que queremos colocar uma quantidade enorme de dados para uma funo. muito mais fcil indicar a localizao dos dados do que copiar cada elemento dos dados. Para declarar usamos: tipo_do_ponteiro *nome_da_varivel; o asterisco (*) que faz o compilador saber que aquela varivel no vai guardar um valor mas sim um endereo para aquele tipo especificado int *pt; se tivermos vrios pointers, temos de colocar o asterisco em cada um; int *pt1, *pt2;

Estudo Ainda no foi inicializado. Isto significa que eles apontam para um lugar indefinido Para saber o endereo de uma varivel basta usar o operador &. int count=10; int *pt; pt=&count; *pt=12; Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A expresso &count nos d o endereo de count, o qual armazenamos em pt. A ltima linha modifica o valor a 12 da varivel onde o apontador est apontar, que o count
#include <stdio.h> #include <stdlib> int main() { int i=10; int *p; p = &i; *p=5; /* a pointer to an integer */ /*associamos o endereo de i ao ponteiro p*/ /*atribumos o valor 5 varivel que p aponta*/

169

printf("%d\t %d\t %p", i, *p, p); system (pause); return 0; }

/*imprimimos a varivel i e para onde aponta p e ainda o endereo para onde aponta*/

Aqui est o que acontece Os erros mais comuns so: A no iniciao dos pointers. dito de outra forma dizer que realmente temos um pointer e reservada memria, mas que depois no dizemos o que queremos guardar nesse espao. p=&i; isso faz com que o p aponte para qualquer stio na memria. E depois quando fazemos *p=12; estamos a pedir para guardar esse n 12 nesse local da memria que no sabemos onde . Se eu tiver dois ponteiros e mesmo depois de iniciados fizer p1=p2. Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que a varivel apontada por p1 tenha o mesmo contedo da varivel apontada por p2 devemos fazer *p1=*p2

Operaes com ponteiros


p++; Quando incrementamos um ponteiro ele passa a apontar para o prximo valor do mesmo tipo para o qual o ponteiro aponta. Isto , se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o prximo inteiro. (*p)++; Para incrementar o contedo da varivel apontada pelo ponteiro p,

Estudo *(p+15); o contedo do ponteiro 15 posies adiante

170

PONTEIROS COMO PARAMENTROS DE FUNES


Os ponteiros do jeito! Porqu? Vamos por um exemplo: eu tenho 2 variveis e quero trocar o valor. eu poderia fazer como fiz no 1 programa em baixo ou se quisesse fazer uma funo da operao de troca faria com est no 2 programa. 1 PROGRAMA #include <stdio.h> #include <stdlib.h> int main() { int a,b; a=5; b=10; printf("%d %d\n", a, b); int t; t=a; a=b; b=t; printf("%d %d\n", a, b); system (pause); return 0; } Agora o 2 PROGRAMA: #include <stdio.h> #include <stdlib.h> void swap(int i, int j) { int t; t=i; i=j; j=t; } int main() { int a,b; a=5; b=10; printf("%d %d\n", a, b); swap(a,b); printf("%d %d\n", a, b); system (pause);

/*ao valor de a atribumos variavel t*/ /*ao valor de b atribumos variavel a*/ /*ao valor de t atribumos variavel a*/

Estudo return 0; } ato, o que que est a acontecer??? Lembram-se da histria da chamada por valor e chamada por referncia. Essa a resposta. No primeiro caso, a operao de troca faz-se na mesma funo. No segundo, a operao faz-se numa funo extra, e o valor dos parmetros no alterado. Afinal como que ns podamos fazer isto. Uma maneira era fazer a troca na funo extra e fazer retornar o valor a duas novas variveis que depois voltaramos a associar a a e b. Mas s estas trocas todas fazem dissuadir de uma pessoa utilizar funes extras, ie, fora do main. Mas existe uma alternativa! Usando os ponteiros. Ou seja vamos passar ponteiros que mais fcil #include <stdio.h> #include <stdlib.h> void Swap (int *i,int *j) /*como vo ser passados os endereos, necessitamos de manipular os valores desses endereos da o uso de "*" - de ponteiros */ { int t; t=*i; *i=*j; *j=t; } int main () { int a,b; a=5; b=10; printf ("\n\nEles valem %d %d\n",a,b); Swap (&a,&b); /*estamos a passar o endereo em vez dos valores*/ printf ("\n\nEles agora valem %d %d\n",a,b); system (pause); return 0; } Os ponteiros so a "referncia" que precisamos para poder alterar a varivel fora da funo. O nico inconveniente que, quando usarmos a funo, teremos de lembrar de colocar um & na frente das variveis que estivermos passando para a funo. Que est acontecendo que passamos para a funo Swap o endereo das variveis a e b. Estes endereos so copiados nos ponteiros i e j. Atravs do operador * estamos acessando o contedo apontado pelos ponteiros e modificando-o. Mas, quem este contedo? Nada mais que os valores armazenados em num1 e num2, que, portanto, esto sendo modificados!

171

Estudo Espere um momento... ser que ns j no vimos esta histria de chamar uma funo com as variveis precedidas de &? J! assim que ns chamamos a funo scanf(). Mas porqu? Vamos pensar um pouco. A funo scanf() usa chamada por referncia porque ela precisa alterar as variveis que passamos para ela! No para isto mesmo que ela feita? Ela l variveis para ns e portanto precisa alterar seus valores. Por isto passamos para a funo o endereo da varivel a ser modificada!

172

Os Argumentos argc e argv


A funo main() como dissemos antes uma funo especial. Mas a funo main() tambm pode ter parmetros formais. no entanto o programador no pode escolher quais sero int main (int argc,char *argv[]); O argc (argument count) um inteiro e possui o nmero de argumentos com os quais a funo main() foi chamada na linha de comando. Ele , no mnimo 1, pois o nome do programa contado como sendo o primeiro argumento. O argv (argument values) um ponteiro para uma matriz de strings. Cada string desta matriz um dos parmetros da linha de comando. O argv[0] sempre aponta para o nome do programa (que, como j foi dito, considerado o primeiro argumento). para saber quantos elementos temos em argv que temos argc. Exemplo: Escreva um programa que faa uso dos parmetros argv e argc. O programa dever receber da linha de comando o dia, ms e ano correntes, e imprimir a data em formato apropriado. Veja o exemplo, supondo que o executvel se chame data: data 19 04 99 O programa dever imprimir: 19 de abril de 1999 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int mes; char *nome_mes [] = {"Janeiro", "Fevereiro", "Maro", "Abril", "Maio", "Junho","Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"}; if(argc == 4) /* Testa se o numero de parmetros fornecidos est correcto, o primeiro parmetro o nome do programa, o segundo o dia, o terceiro o mes e o quarto os dois ltimos 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");

Estudo else printf("\n%s de %s de 19%s", argv[1], nome_mes[mes-1], argv[3]); } else printf("Erro!\nUso: data dia mes ano, todos inteiros"); }

173

Recursividade
Uma funo pode chamar a si prpria. Uma funo assim chamada funo recursiva #include <stdio.h> #include <stdlib.h> int fat(int n) { if (n) return n*fat(n-1); 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)); system ("pause"); return 0; }

TYPEDEF
Tambm o typedef deve ser utilizado como header. Ele muito parecido com o #define a diferena que trabalha com a tipologia das variveis. (type+definition) que (tipologia de variveis +definio) typedef int boolean; Isto vai dizer ao compilador que quando encontrar no cdigo a palavra boolean a vai substituir por int. Assim podemos escrever o cdigo utilizando um nome que nos agrade mais. mas repare que apenas para tipologias de variveis!!

Estudo

174

DIRETIVAS DE COMPILAO
As Diretivas de Compilao O pr-processador C um programa que examina o programa fonte escrito em C e executa certas modificaes nele, baseado nas Diretivas de Compilao As directivas de compilao so comandos que no so compilados, sendo dirigidos ao pr-processador, que executado pelo compilador antes da execuo do processo de compilao propriamente dito. Portanto, o pr-processador modifica o programa fonte, entregando para o compilador um programa modificado. Todas as directivas de compilao so iniciadas pelo carcter #. As directivas podem ser colocadas em qualquer parte do programa. 1. #include 2. #define 3. #undef 4. #ifdef e #endif 5. #ifndef 6. #if 7. #else 8. #elif

1.A Diretiva #include


Ela diz ao compilador para incluir, na hora da compilao, um arquivo especificado #include "nome_do_arquivo" ou #include <nome_do_arquivo> A diferena entre se usar " " e < > somente a ordem de procura nos directrios pelo arquivo especificado. Se voc quiser informar o nome do arquivo com o caminho completo, ou se o arquivo estiver no directrio de trabalho, use " ". Se o arquivo estiver nos caminhos de procura pr-especificados do compilador, isto , se ele for um arquivo do prprio sistema (como o caso de arquivos como stdio.h, string.h, etc...) use < >.

2.A Diretiva #Define


#define nome_da_macro sequncia_de_caracteres Toda vez que ele encontrar o nome_da_macro no programa a ser compilado, ele deve substitu-lo pela sequncia_de_caracteres fornecida. #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; } Outro exemplo: #define max(A,B) ((A > B) ? (A):(B)) #define min(A,B) ((A < B) ? (A):(B)) x = max(i,j); y = min(t,r);

Estudo Assim, a linha de cdigo: x = max(i,j); Ser substituda pela linha: x = ((i)>(j) ? (i):(j)); Quando voc utiliza a diretiva #define nunca deve haver espaos em branco no identificador. Por exemplo, a macro: #define PRINT (i) printf(" %d \n", i) no funcionar correctamente porque existe um espao em branco entre PRINT e (i).

175

3. A Diretivas #undef
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 no conhecer mais esta macro.

4. As Diretivas #ifdef e #endif


Directivas de compilao condiciona, #ifdef nome_da_macro sequncia_de_declaraes #endif A sequncia de declaraes ser compilada apenas se o nome da macro estiver definido. A directiva de compilao #endif til para definir o fim de uma sequncia de declaraes para todas as directivas de compilao condicional.

5. A Diretiva #ifndef
A diretiva #ifndef funciona ao contrrio da diretiva #ifdef. Sua forma geral : #ifndef nome_da_macro sequncia_de_declaraes #endif A sequncia de declaraes ser compilada se o nome da macro no tiver sido definido

6. A Diretiva #if
A diretiva #if tem a seguinte forma geral: #if expresso_constante sequncia_de_declaraes #endif A sequncia de declaraes ser compilada se a expresso-constante for verdadeira. muito importande ressaltar que a expresso fornecida deve ser constante, ou seja, no deve ter nenhuma varivel.

Estudo

176

7. A Diretiva #else
A diretiva #else tem a seguinte forma geral: #if expresso_constante sequncia_de_declaraes #else sequncia_de_declaraes #endif Ela funciona como seu correspondente, o comando else. #define SISTEMA DOS ... /*linhas de codigo..*/ ... #if SISTEMA == DOS #define CABECALHO "dos_io.h" #else #define CABECALHO "unix_io.h" #endif #include CABECALHO

8. A Diretiva #elif
A diretiva #elif serve para implementar a estrutura if-else-if. Sua forma geral : #if expresso_constante_1 sequncia_de_declaraes_1 #elif expresso_constante_2 sequncia_de_declaraes_2 #elif expresso_constante_3 sequncia_de_declaraes_3 . . . #elif expresso_constante_n sequncia_de_declaraes_n #endif O funcionamento desta estrutura idntico ao funcionamento apresentado anteriormente.

Estudo

177

LIBRARIES BIBLIOTECAS
As bibliotecas so tipo funes, ou conjunto de funes, que algum j fez. Mas desta vez, essas funes esto fora do nosso programa. Elas para alm da vantagem, tal como nas funes, fora do main, permitem uma organizao do cdigo, e que possam ser chamadas vrias vezes ao longo do cdigo, tm uma vantagem extra que que podem ser chamadas por vrios cdigos que faamos. No necessitamos de fazer copy e paste, de um cdigo para outro cdigo que faamos mais tarde, basta-nos chamar. Por exemplo, vamos tentar criar a nossa prpria biblioteca. Vamos criar 2 funes para alem do main
#include <stdio.h> #include <stdlib.h> #define MAX 10 int a[MAX]; int rand_seed=10; int rand() { rand_seed = rand_seed * 1103515245 +12345; return (unsigned int)(rand_seed / 65536) % 32768; } void bubble_sort(int m) { int x,y,t; for (x=0; x < m-1; x++) for (y=0; y < m-x-1; y++) if (a[y] > a[y+1]) { t=a[y]; a[y]=a[y+1]; a[y+1]=t; int main() { int i,t,x,y; for (i=0; i < MAX; i++) { a[i]=rand(); printf("%d\n",a[i]); } bubble_sort(MAX); printf("--------------------\n"); for (i=0; i < MAX; i++) printf("%d\n",a[i]); } /* print sorted array */ /* fill array */ /* 2 funo repare que recebe um parmetro/ /*na hora da compilao vai substituir max por 10*/ /*definio de varivel global*/ /*definio de variavel global e afectao de valor*/ /* 1 funo*/

Ns podemos generalizar o buble sort mais

Estudo
#include <stdio.h> #include <stdlib.h> #define MAX 10 int a[MAX]; int rand_seed=10; int rand() { rand_seed = rand_seed * 1103515245 +12345; return (unsigned int)(rand_seed / 65536) % 32768; } void bubble_sort(int m, int a[]) { int x,y,t; for (x=0; x < m-1; x++) for (y=0; y < m-x-1; y++) if (a[y] > a[y+1]) { t=a[y]; a[y]=a[y+1]; a[y+1]=t; } } int main() { int i,t,x,y; for (i=0; i < MAX; i++) { a[i]=rand(); printf("%d\n",a[i]); } bubble_sort(MAX, a); /* necessito de alterar, para receber 2 parametros */ /* recebe agora 2 parmetros*/

178

printf("--------------------\n"); for (i=0; i < MAX; i++) printf("%d\n",a[i]); }

Todas as bibliotecas tm 2 partes: 1. . header file (que temo h sufixo contem informao sobre a biblioteca. de uma forma gera, contm constantes, types e prototipods de funes disponibilizadas pela biblioteca. 1. . e o file do cdigo efectivo Como eu quero criar uma biblioteca para a funo do bublesort, vou fazer o seguinte 1. . Vamos colocar o seguinte cdigo e gravar com o nome de util.h as duas linhas que colocmos so prottipos. a palavra extern representa uma funo que mais tarde ser linked
extern int rand(); /*cdigo das funes prototipo para o ficheiro header util.h */

extern void bubble_sort(int, int []);

Estudo 1. . agora vamos criar um outro ficheiro com o nome util.c /* cdigo efectivo para a criao do ficheiro util.c */ /* !!!repare que eu aqui j chamo o header criado, mas repare que agora tenho em vez de <>*/ #include "util.h" int rand_seed=10; int rand() /*esta a funo rand()*/ { rand_seed = rand_seed * 1103515245 +12345; return (unsigned int)(rand_seed / 65536) % 32768; } void bubble_sort(int m,int a[]) /*esta a funo buble_sort()*/ { int x,y,t; for (x=0; x < m-1; x++) for (y=0; y < m-x-1; y++) if (a[y] > a[y+1]) { t=a[y]; a[y]=a[y+1]; a[y+1]=t; } } 1. . Agora coloquemos o seguinte programa e gravemos com o nome main.c
#include <stdio.h> #include "util.h" /* REPARE que eu agora chamo o header file.

179

/ #define MAX 10 int a[MAX]; int main() { int i,t,x,y; for (i=0; i < MAX; i++) { a[i]=rand(); printf("%d\n",a[i]); } bubble_sort(MAX,a); printf("--------------------\n"); for (i=0; i < MAX; i++) printf("%d\n",a[i]); } Como vimos agora s chamar pela biblioteca que criamos, sempre e quando quisermos.

/* fill array */

/* print sorted array */

Estudo

180

Entradas e Sadas Padronizadas


O sistema de entrada e sada da linguagem C est estruturado na forma de uma biblioteca de funes Quando apresentarmos uma funo, vamos, em primeiro lugar, apresentar o seu prottipo. Outro aspecto importante, quando se discute a entrada e sada na linguagem C o conceito de fluxo Seja qual for o dispositivo de entrada e sada (discos, terminais, teclados, ...) que se estiver trabalhando, o C vai t-lo como um fluxo. Todos os fluxos so similares em seu funcionamento e independentes do dispositivo ao qual esto associados. Assim, as mesmas funes que descrevem o acesso aos discos podem ser utilizadas para se acessar um terminal de vdeo. Assim um ficheiro tal como outros componentes, apenas necessita de ser aberto e depois fechado por forma a que o fluxo- troca de informao se possa realizar.

TEXT FILES
Existem 6 comandos de I/O na biblioteca <stdio.h> printf puts putc scanf gets getc, - prints formatted output to stdout - prints a string to stdout - prints a character to stdout - reads formatted input from stdin - reads a string from stdin - reads a character from stdin

outros: getchar putchar getch, getche l um caractere do stdin podemos ver estas duas funes na biblioteca conio.h. A 2 funo imprime no ecr depois de ler. sprintf vamos fazer agora apenas umas notas a estas funes:

a funo gets
(get+string) tem como prototipo char *gets (char *s); a funo l a string do teclado, ou melhor, ai armazenar uma string s no ponteiro s: gets(nome_da_string) mas existe um problema que pode ser perigoso. #include <stdio.h> int main() { char buffer[10]; printf("Entre com o seu nome"); gets(buffer); printf("O nome : %s", buffer); return 0;

Estudo } se o usurio digitar mais do que 10 caracteres incluindo o "\0", os caracteres adicionais sero colocados na rea de memria subsequente ocupada por ela, escrevendo uma regio de memria que no est reservada string. Este efeito conhecido como "estouro de buffer" e pode causar problemas imprevisveis. Uma forma de se evitar este problema usar a funo fgets (vamos utiliz-la mais tarde)

181

Funo sprintf e sscanf


sprintf e sscanf so semelhantes a printf e scanf. Porm, ao invs de escreverem na sada padro ou lerem da entrada padro, escrevem ou leem em uma string.
#include <stdio.h> #include <stdlib.h> int main() { int i; char string1[20]; printf( " Entre um valor inteiro: "); scanf("%d", &i); sprintf(string1,"Valor de i = %d", i); puts(string1); system ("pause"); return 0; } /*coloca na string1. a frase...*/

a varivel i "impressa" em string1. Alm da representao de i como uma string, string1 tambm conter "Valor de i=" . #include <stdio.h> #include <stdlib.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); system ("pause"); return 0; }

/*l para o string1*/ /*imprime no ecr*/

foi utilizada a funo sscanf para converter a informao armazenada em string1 em seu valor numrico

Estudo

182

Funo putc
A funo putc a primeira funo de escrita de arquivo que veremos. Seu prottipo : int putc (int ch,FILE *fp); Escreve um caractere no arquivo.O programa a seguir l uma string do teclado e escreve-a, caractere por caractere em um arquivo em disco (o arquivo arquivo.txt, que ser aberto no diretrio corrente).
#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char string[100]; int i; fp = fopen("arquivo.txt","w"); 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); system ("pause"); return 0; } /* Arquivo ASCII, para escrita */

Depois de executar este programa, verifique o contedo do arquivo arquivo.txt (voc pode usar qualquer editor de textos). Voc ver que a string que voc digitou est armazenada nele.

getc
Retorna um caractere lido do arquivo. Prottipo: int getc (FILE *fp);

strcpy
strcpy (string_destino,string_origem);

strcat
strcat (string_destino,string_origem); A string de origem permanecer inalterada e ser anexada ao fim da string de destino.

Estudo

183

strlen
strlen (string); retorna o comprimento da string fornecida. O terminador nulo no contado.

strcmp
strcmp (string1,string2); compara a string 1 com a string 2. Se as duas forem idnticas a funo retorna zero. Se elas forem diferentes a funo retorna no-zero.

Abrindo e Fechando um Arquivo


arquivos pr-definidos: stdin: dispositivo de entrada padro (geralmente o teclado) stdout: dispositivo de sada padro (geralmente o vdeo) stderr: dispositivo de sada de erro padro (geralmente o vdeo) stdaux: dispositivo de sada auxiliar (em muitos sistemas, associado porta serial) stdprn : dispositivo de impresso padro (em muitos sistemas, associado porta

paralela) O sistema de entrada e sada do ANSI C composto por uma srie de funes, cujos prottipos esto reunidos em stdio.h . Todas estas funes trabalham com o conceito de "ponteiro de arquivo". Este no um tipo propriamente dito, mas uma definio usando o comando typedef. Esta definio tambm est no arquivo stdio.h. Podemos declarar um ponteiro de arquivo da seguinte maneira: FILE *p; p ser ento um ponteiro para um arquivo. usando este tipo de ponteiro que vamos poder manipular arquivos no C. pelo o que eu estou a perceber o nome FILE tipo um int ou um float ou ainda um typedef 1. . fopen - opens a text file 2. . fclose - closes a text file 3. . feof - detects end-of-file marker in a file 4. . fgets - reads a string from a file 5. . fputs - prints a string to a file 6. . ferror e perror 7. . fread 8. . fwrite 9. . fseek 10. . rewind 11. . remove 12. . fprintf - prints formatted output to a file 13. . fscanf - reads formatted input from a file 14. . fputc - prints a character to a file 15. . fgetc - reads a character from a file'

Estudo

184

1. fopen
Esta a funo de abertura de arquivos. Seu prottipo : FILE *fopen (char *nome_do_arquivo,char *modo); O nome_do_arquivo determina qual arquivo dever ser aberto. Este nome deve ser vlido no sistema operacional que estiver sendo utilizado. O modo de abertura diz funo fopen() que tipo de uso voc vai fazer do arquivo. A tabela abaixo mostra os valores de modo vlidos:
Modo "r"- read Significado Abre um arquivo texto para leitura. O arquivo deve existir antes de ser

aberto.
"w"-write Abrir um arquivo texto para gravao. Se o arquivo no existir, ele ser

criado. Se j existir, o contedo anterior ser destrudo.


"a"-append Abrir um arquivo texto para gravao. Os dados sero adicionados no fim do

arquivo ("append"), se ele j existir, ou um novo arquivo ser criado, no caso de arquivo no existente anteriormente.
"r+" Abre um arquivo texto para leitura e gravao. O arquivo deve existir e pode ser

modificado.
"w+" Cria um arquivo texto para leitura e gravao. Se o arquivo existir, o contedo

anterior ser destrudo. Se no existir, ser criado.


"a+" Abre um arquivo texto para gravao e leitura. Os dados sero adicionados no fim do

arquivo se ele j existir, ou um novo arquivo ser criado, no caso de arquivo no existente anteriormente.
"rb" Abre um arquivo binrio para leitura. Igual ao modo "r" anterior, s que o arquivo

binrio.
"wb" Cria um arquivo binrio para escrita, como no modo "w" anterior, s que o arquivo

binrio.
"ab" Acrescenta dados binrios no fim do arquivo, como no modo "a" anterior, s que o

arquivo binrio.
"r+b" Abre um arquivo binrio para leitura e escrita. O mesmo que "r+" acima, s que o

arquivo binrio.
"w+b" Cria um arquivo binrio para leitura e escrita. O mesmo que "w+" acima, s que o

arquivo binrio.
"a+b" Acrescenta dados ou cria uma arquivo binrio para leitura e escrita. O mesmo que

"a+" acima, s que o arquivo binrio Poderamos ento, para abrir um arquivo binrio para escrita, escrever:

Estudo
FILE *fp; fp=fopen ("exemplo.bin","wb"); /* Declarao da estrutura*/ /* o arquivo se chama exemplo.bin e est localizado no

185

diretrio corrente */ if (!fp) printf ("Erro na abertura do arquivo."); A condio !fp testa se o arquivo foi aberto com sucesso porque no caso de um erro a funo fopen() retorna um ponteiro nullo (NULL). Uma vez aberto um arquivo, vamos poder ler ou escrever nele utilizando as funes que sero apresentadas nas prximas pginas. Exemplo: #include <stdio.h> #define MAX 10 int main() { FILE *f; int x; f=fopen("out","w"); if (!f) return 1; for(x=1; x<=MAX; x++) fprintf(f,"%d\n",x); fclose(f); return 0; }

/* est criado um ponteiro para o arquivo */ /*a funo abre o file out no modo w*/

/*a a funo printf para file da fprintf */

Vai abrir um file chamado de out e escrever os nmeros de 1 a 10. Depois fecha o file .Repare que abrir um ficheiro no modo w destrutivo, ou seja, se o ficheiro no existe ele vai ser criado, mas se existe um outro ficheiro o novo ficheiro fica no seu lugar. este comando fopen retorna um pointer ao ficheiro que guardado na varivel f. se no possvel abrir o ficheiro o f fica com o valor null. Exemplo 2:
#include <stdio.h> #include <stdlib.h> int main() { FILE *f; char s[1000]; f=fopen("infile","r"); if (!f) return 1; while (fgets(s,1000,f)!=NULL) printf("%s",s); fclose(f); system (pause); /* enquando for diferente de zero,ie, at o enter*/ /*fgets em vez do fscanf*/ /*abre o file chamado infile em modo r*/ /* no caso de abrir mal*/

Estudo
return 0; }

186

para ler o file infile mas agora no modo r- de read. Repare que utilizamos o fgets em vez do fscanf porque este requere que o texto esteja perfeitamente formatado. o fgets ainda tem a vantagem de acrescentar um 1n em cada linha que l. ele vai ler at encontar o eof-end of file marker.no exemplo ele vai ler 1000 caracteres vamos ver os comandos melhor.

exit
Prottipo : void exit (int codigo_de_retorno); Para utiliz-la deve-se colocar um include para o arquivo de cabealho stdlib.h. Esta funo aborta a execuo do programa. Pode ser chamada de qualquer ponto no programa e faz com que o programa termine e retorne, para o sistema operacional, o cdigo_de_retorno. A conveno mais usada que um programa retorne zero no caso de um trmino normal e retorne um nmero no nulo no caso de ter ocorrido um problema. #include <stdio.h> #include <stdlib.h> /* Para a funo exit() */ main (void) { FILE *fp; ... fp=fopen ("exemplo.bin","wb"); if (!fp) { printf ("Erro na abertura do arquivo. Fim de programa."); exit (1); } ... return 0; }

2. fclose
Acabmos de usar um arquivo que abrimos, devemos fech-lo. Para tanto usa-se a funo fclose(): int fclose (FILE *fp); O ponteiro fp passado funo fclose() determina o arquivo a ser fechado. A funo retorna zero no caso de sucesso. Fechar um arquivo faz com que qualquer caracter que tenha permanecido no "buffer" associado ao fluxo de sada seja gravado. Mas, o que este "buffer"? Quando voc envia caracteres para serem gravados em um arquivo, estes caracteres so armazenados temporariamente em uma rea de memria (o "buffer") em vez de serem escritos em disco imediatamente. Quando o "buffer" estiver cheio, seu contedo escrito no disco de uma vez. A razo para se fazer

Estudo isto tem a ver com a eficincia nas leituras e gravaes de arquivos. Se, para cada caracter que fossemos gravar, tivssemos que posicionar a cabea de gravao em um ponto especfico do disco, apenas para gravar aquele caracter, as gravaes seriam muito lentas. Assim estas gravaes s sero efetuadas quando houver um volume razovel de informaes a serem gravadas ou quando o arquivo for fechado. A funo exit () fecha todos os arquivos que um programa tiver aberto.

187

3. feof
EOF ("End of file") indica o fim de um arquivo. s vezes, necessrio verificar se um arquivo chegou ao fim. Para isto podemos usar a funo feof(). Ela retorna no-zero se o arquivo chegou ao EOF, caso contrrio retorna zero. Seu prottipo : int feof (FILE *fp); Outra forma de se verificar se o final do arquivo foi atingido comparar o caractere lido por getc com EOF. O programa a seguir abre um arquivo j existente e o l, caracter por caracter, at que o final do arquivo seja atingido. Os caracteres lidos so apresentados na tela:
#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char c; fp = fopen("arquivo.txt","r"); if(!fp) { printf( "Erro na abertura do arquivo"); exit(0); } while((c = getc(fp) ) != EOF) printf("%c", c); fclose(fp); return 0; } /* Enquanto no chegar ao final do arquivo */ /* imprime o caracter lido */ /* Arquivo ASCII, para leitura */

Verifique o exemplo.
#include <stdio.h> #include <stdlib.h> #include <string.h> int 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"); gets(str); if (!(p = fopen(str,"w"))) { /* Le um nome para o arquivo a ser aberto: */ /* Caso ocorra algum erro na abertura do arquivo..*/

Estudo
printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); } strcat(frase, str); for (i=0; frase[i]; i++) putc(frase[i],p); fclose(p); p = fopen(str,"r"); c = getc(p); while (!feof(p)) { printf("%c",c); c = getc(p); } fclose(p); } /* Fecha o arquivo */ /* Imprime o caracter na tela */ /* Se nao houve erro,imprime no arquivo e o fecha ...*/ /* Abre novamente para leitura */ /* o programa aborta automaticamente */

188

/* Le o primeiro caracter */ /* Enquanto no se chegar no final do arquivo */

/* Le um novo caracter no arquivo */

4. fgets
Para se ler uma string num arquivo podemos usar fgets() cujo prottipo : char *fgets (char *str, int tamanho,FILE *fp); A funo recebe 3 argumentos: a string a ser lida, o limite mximo de caracteres a serem lidos e o ponteiro para FILE, que est associado ao arquivo de onde a string ser lida. A funo l a string at 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 parte da string, o que no acontecia com gets. A funo fgets semelhante funo gets(), porm, alm dela poder fazer a leitura a partir de um arquivo de dados e incluir o caracter de nova linha na string, ela ainda especifica o tamanho mximo da string de entrada. Como vimos, a funo gets no tinha este controle, o que poderia acarretar erros de "estouro de buffer". Portanto, levando em conta que o ponteiro fp pode ser substitudo por stdin, como vimos acima, uma alternativa ao uso de gets usar a seguinte construo: fgets (str, tamanho, stdin);

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

6. ferror e perror
Prottipo de ferror: int ferror (FILE *fp); A funo retorna zero, se nenhum erro ocorreu e um nmero diferente de zero se algum erro ocorreu durante o acesso ao arquivo. se torna muito til quando queremos verificar se cada

Estudo 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. Porm, existem situaes em que isto no ocorre. Por exemplo, pode acabar o espao em disco enquanto gravamos, ou o disco pode estar com problemas e no conseguimos ler, etc. Uma funo que pode ser usada em conjunto com ferror() a funo perror() (print error), cujo argumento uma string que normalmente indica em que parte do programa o problema ocorreu. #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); }

189

7. fread
Podemos escrever e ler blocos de dados. Para tanto, temos as funes fread() e fwrite(). O prottipo de fread() : unsigned fread (void *buffer, int numero_de_bytes, int count, FILE *fp); O buffer a regio de memria na qual sero armazenados os dados lidos. O nmero de bytes o tamanho da unidade a ser lida. count indica quantas unidades devem ser lidas. Isto significa que o nmero total de bytes lidos : numero_de_bytes*count A funo retorna o nmero de unidades efetivamente lidas. Este nmero pode ser menor que count quando o fim do arquivo for encontrado ou ocorrer algum erro. Quando o arquivo for aberto para dados binrios, fread pode ler qualquer tipo de dados.

Estudo

190

8. fwrite
A funo fwrite() funciona como a sua companheira fread(), porm escrevendo no arquivo. Seu prottipo : unsigned fwrite(void *buffer,int numero_de_bytes,int count,FILE *fp); A funo retorna o nmero de itens escritos. Este valor ser 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 varivel float em um arquivo binrio.
#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 binrio para escrita */ { printf("Erro na abertura do arquivo"); exit(1); } if(fwrite(&pi, sizeof(float), 1,pf) != 1) /* Escreve a varivel 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) printf("Erro na leitura do arquivo"); printf("\nO valor de PI, lido do arquivo e': %f", pilido); fclose(pf); return 0; } /* Le em pilido o valor da varivel armazenada anteriormente */

Note-se o uso do operador sizeof, que retorna o tamanho em bytes da varivel ou do tipo de dados.

Estudo

191

9. fseek
Para se fazer procuras e acessos randmicos em arquivos usa-se a funo fseek(). Esta move a posio corrente de leitura ou escrita no arquivo de um valor especificado, a partir de um ponto especificado. Seu prottipo : int fseek (FILE *fp,long numbytes,int origem); O parmetro origem determina a partir de onde os numbytes de movimentao sero contados. Os valores possveis so definidos por macros em stdio.h e so: Nome SEEK_SET SEEK_CUR SEEK_END Valor Significado Incio do arquivo Ponto corrente no arquivo Fim do arquivo

0 1 2

Tendo-se definido a partir de onde ir se contar, numbytes determina quantos bytes de deslocamento sero dados na posio atual.

10. rewind
A funo rewind() de prottipo void rewind (FILE *fp); retorna a posio corrente do arquivo para o incio.

11. remove
Prottipo: int remove (char *nome_do_arquivo); Apaga um arquivo especificado. O exerccio da pgina anterior poderia ser reescrito usando-se, por exemplo, fgets() e fputs(), ou fwrite() e fread(). A seguir apresentamos uma segunda verso que se usa das funes fgets() e fputs(), e que acrescenta algumas inovaes.
#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); for(i=0; str[i]; i++) if(str[i]=='\n') str[i]=0; if (!(p = fopen(str,"w"))) { printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); } fputs(frase, p); /* Se nao houve erro, imprime no arquivo, e o fecha ...*/ /* Le um nome para o arquivo a ser aberto: */

/* Usa fgets como se fosse gets */ /* Elimina o \n da string lida */ /* Caso ocorra algum erro na abertura do arquivo..*/ /* o programa aborta automaticamente */

Estudo
fputs(str,p); fclose(p); p = fopen(str,"r"); fgets(resposta, 79, p); printf("\n\n%s\n", resposta); fclose(p); remove(str); return 0; /* Fecha o arquivo */ /* Apaga o arquivo */ /* abre novamente e le */

192

fprintf
A funo fprintf() funciona como a funo printf(). A diferena que a sada de fprintf() um arquivo e no a tela do computador. Prottipo: int fprintf (FILE *fp,char *str,...); Como j poderamos esperar, a nica diferena do prottipo de fprintf() para o de printf() a especificao do arquivo destino atravs do ponteiro de arquivo.

fscanf
A funo fscanf() funciona como a funo scanf(). A diferena que fscanf() l de um arquivo e no do teclado do computador. Prottipo: int fscanf (FILE *fp,char *str,...); Como j poderamos esperar, a nica diferena do prottipo de fscanf() para o de scanf() a especificao do arquivo destino atravs do ponteiro de arquivo. Talvez a forma mais simples de escrever o programa da pgina 97 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); if (!(p = fopen(str,"w"))) { printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); } fprintf(p,"Este e um arquivo chamado:\n%s\n", str); fclose(p); p = fopen(str,"r"); while (!feof(p)) { fscanf(p,"%c",&c); printf("%c",c); /* Se nao houve erro, imprime no arquivo, fecha ...*/ /* abre novamente para a leitura */ /* Caso ocorra algum erro na abertura do arquivo..*/ /* o programa aborta automaticamente */ /* Le um nome para o arquivo a ser aberto: */

Estudo
} fclose(p); return 0; }

193

Tipos de Dados Avanados


J vimos que uma varivel declarada como tipo_da_varivel lista_de_variveis; Vimos tambm que existem modificadores de tipos. Estes modificam o tipo da varivel declarada. Destes, j vimos os modificadores signed, unsigned, long, e short. Estes modificadores so includos na declarao da varivel da seguinte maneira: modificador_de_tipo tipo_da_varivel lista_de_variveis; Vamos discutir agora outros modificadores de tipo. Modificadores de Acesso Estes modificadores, como o prprio nome indica, mudam a maneira com a qual a varivel acessada e modificada.

const
O modificador const faz com que a varivel no possa ser modificada no programa. Como o nome j sugere til para se declarar constantes. Poderamos ter, por exemplo: const float PI=3.141; Podemos ver pelo exemplo que as variveis com o modificador const podem ser inicializadas. Mas PI no poderia ser alterado em qualquer outra parte do programa. Se o programador tentar modificar PI o compilador gerar um erro de compilao. O uso mais importante de const no declarar variveis constantes no programa. Seu uso mais comum evitar que um parmetro de uma funo seja alterado pela funo. Isto muito til no caso de um ponteiro, pois o contedo de um ponteiro pode ser alterado por uma funo. Para tanto, basta declarar o parmetro 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 protegido contra alteraes. Isto quer dizer que, se tentssemos fazer *num=10; Dentro da funo sqr() o compilador daria uma mensagem de erro.

Estudo

194

volatile
O modificador volatile diz ao compilador que a varivel em questo pode ser alterada sem que este seja avisado. Isto evita "bugs" serssimos. Digamos que, por exemplo, tenhamos uma varivel que o BIOS do computador altera de minuto em minuto (um relgio por exemplo). Seria muito bom que declarssemos esta varivel como sendo volatile. extern float sum; int RetornaCount (void) { return count; } Assim, o compilador ir saber que count e sum esto sendo usados no bloco mas que foram declarados em outro.

static
O funcionamento das variveis declaradas como static depende se estas so globais ou locais. Variveis globais static funcionam como variveis globais dentro de um mdulo, ou seja, so variveis globais que no so (e nem podem ser) conhecidas em outros modulos. Isto util se quisermos isolar pedaos de um programa para evitar mudanas acidentais em variveis globais. Variveis locais static so variveis cujo valor mantido de uma chamada da funo para a outra. Veja o exemplo: int count (void) { static int num=0; num++; return num; } A funo count() retorna o nmero de vezes que ela j foi chamada. Veja que a varivel local int inicializada. Esta inicializao s vale para a primeira vez que a funo chamada pois num deve manter o seu valor de uma chamada para a outra. O que a funo faz incrementar num a cada chamada e retornar o seu valor. A melhor maneira de se entender esta varivel local static implementando. Veja por si mesmo, executando seu prprio programa que use este conceito.

register
O computador tem a memria principal e os registradores da CPU. As variveis (assim como o programa como um todo) so armazenados na memria. O modificador register diz ao compilador que a varivel em questo deve ser, se possvel, usada em um registrador da CPU. Vamos agora ressaltar vrios pontos importantes. Em primeiro lugar, porque usar o register? Variveis nos registradores da CPU vo ser acessadas em um tempo muito menor pois os registradores so muito mais rpidos que a memria. Em segundo lugar, em que tipo de varivel usar o register? O register no pode ser usado em variveis globais. Isto implicaria que um registrador da CPU ficaria o tempo todo ocupado por conta de uma varivel. Os tipos de dados onde mais aconselhado o uso do register so os tipos char e int, mas pode-se us-lo em qualquer tipo de dado. Em terceiro lugar, o register um pedido que o programador faz ao compilador. Este no precisa ser atendido necessariamente. Um exemplo do uso do register dado:

Estudo main (void) { register int count; for (count=0;count<10;count++) { ... } return 0; } O loop for acima ser executado mais rapidamente do que seria se no usssemos o register. Este o uso mais recomendvel para o register: uma varivel que ser usada muitas vezes em seguida.

195

Converso de Tipos
Em atribuies no C temos o seguinte formato: destino=orgem; Se o destino e a orgem so de tipos diferentes o compilador faz uma converso entre os tipos. Nem todas as converses so possveis. O primeiro ponto a ser ressaltado que o valor de origem convertido para o valor de destino antes de ser atribudo e no o contrrio. importante lembrar que quando convertemos um tipo numrico para outro ns nunca ganhamos preciso. Ns podemos perder preciso ou no mximo manter a preciso anterior. Isto pode ser entendido de uma outra forma. Quando convertemos um nmero no estamos introduzindo no sistema nenhuma informao adicional. Isto implica que nunca vamos ganhar preciso. Abaixo vemos uma tabela de converses numricas com perda de preciso, para um compilador com palavra de 16 bits: De Para Informao Perdida unsigned char char Valores maiores que 127 so alterados short int char Os 8 bits de mais alta ordem int char Os 8 bits de mais alta ordem long int char Os 24 bits de mais alta ordem long int short int Os 16 bits de mais alta ordem long int int Os 16 bits de mais alta ordem float int Preciso resultado arredondado double float Preciso - resultado arredondado long double double Preciso - resultado arredondado

Modificadores de Funes
A forma geral de uma funo , como j foi visto, tipo_de_retorno nome_da_funo (declarao_de_parmetros) { corpo_da_funo } Uma funo pode aceitar um modificador de tipo. Este vai modificar o modo como a funo opera na passagem de parmetros. A forma geral da funo ficaria ento: modificador_de_tipo tipo_de_retorno nome_da_funo (declarao_de_parmetros) { corpo_da_funo } lembram-se do casting que tnhamos que fazer para a tipologia das variaveis, (quando

Estudo tinhamos um int a dividir por um int que dava um nmero real e s nos aparecia o resultado da diviso como um int, em vez de ser um float. ), pois bem aqui parecido mas com as funes. ns no vamos avanar mais. Apenas para ficarem com o conhecimento.

196

Ponteiros para Funes


O C permite que acessemos variveis e funes atravs de ponteiros! Podemos ento fazer coisas como, por exemplo, passar uma funo como argumento para outra funo. Um ponteiro para uma funo tem a seguinte declarao: tipo_de_retorno (*nome_do_ponteiro)(); ou tipo_de_retorno (*nome_do_ponteiro)(declarao_de_parmetros); Repare nos parnteses que devem ser colocados obrigatoriamente. Se declaramos: tipo_de_retorno * nome(declarao_de_parmetros); Estaramos, na realidade, declarando uma funo que retornaria um ponteiro para o tipo especificado. Porm, no obrigatrio se declarar os parmetros da funo. Veja um exemplo do uso de ponteiros para funes:
#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 *); p=puts; /* Declaracao do ponteiro para funo Funcao apontada e' inteira e recebe como parametro uma string constante */

/* O ponteiro p passa a apontar para a funo puts que tem o seguinte prototipo: int puts(const char *) */ /* O ponteiro passado como parametro para PrintString */

PrintString (String, p); return 0; }

void PrintString (char *str, int (*func)(const char *)) { (*func)(str); func(str); } /* chamada a funo atravs do ponteiro para funo */ /* maneira tambm vlida de se fazer a chamada a funo puts atravs do ponteiro para funo func */

Veja que fizemos a atribuio de puts a p simplesmente usando: p = puts; Disto, conclumos que o nome de uma funo (sem os parnteses) , na realidade, o endereo daquela funo! Note, tambm, as duas formas alternativas de se chamar uma funo atravs de um ponteiro. No programa acima, fizemos esta chamada por: (*func)(str); e func(str); Estas formas so equivalentes entre si. Alm disto, no programa, a funo PrintString() usa uma funo qualquer func para imprimir a string na tela. O programador pode ento fornecer no s a string mas tambm a funo que ser usada para imprim-la. No main() vemos como podemos atribuir, ao ponteiro para funes p, o endereo da funo puts() do C. Em sntese, ao declarar um ponteiro para funo, podemos atribuir a este ponteiro o endereo de uma funo e podemos tambm chamar a funo apontada atravs dele. No podemos fazer algumas coisas que fazamos com ponteiros "normais", como, por exemplo, incrementar ou

Estudo decrementar um ponteiro para funo.

197

Alocao Dinmica
A alocao dinmica permite ao programador alocar memria para variveis quando o programa est sendo executado. Assim, poderemos definir, por exemplo, um vetor ou uma matriz cujo tamanho descobriremos em tempo de execuo. O padro C ANSI define apenas 4 funes para o sistema de alocao dinmica, disponveis na biblioteca stdlib.h: No entanto, existem diversas outras funes que so amplamente utilizadas, mas dependentes do ambiente e compilador. Neste curso sero abordadas somente estas funes padronizadas. pelo que eu percebi de dados dinmicos utilizar uma memria que dinmica, ie, quando utilizamos um programa utilizada essa memria e depois quando chamamos outro programa a mesma memria que utilizou o programa anterior agora utilizada para o novo programa. mas ressalve que os dados importantes do resultado do programa anterior so gravados. isto basicamente a historia de memria ram e rom.

malloc
A funo malloc() serve para alocar memria e tem o seguinte prottipo: void *malloc (unsigned int num); A funo toma o nmero de bytes que queremos alocar (num), aloca na memria e retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribudo a qualquer tipo de ponteiro. Se no houver memria suficiente para alocar a memria requisitada a funo malloc() retorna um ponteiro nulo. Veja um exemplo de alocao dinmica com malloc():
#include <stdio.h> #include <stdlib.h> main (void) { int *p; int a; int i; ... p=malloc(a*sizeof(int)); /* Determina o valor de a em algum lugar */ /* Aloca a nmeros inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } for (i=0; i<a ; i++) p[i] = i*i; ... return 0; /* p pode ser tratado como um vetor com a posicoes */ /* Para usar malloc() */

Estudo
}

198

No exemplo acima, alocada memria suficiente para se armazenar a nmeros inteiros. O operador sizeof() retorna o nmero de bytes de um inteiro. Ele util para se saber o tamanho de tipos. O ponteiro void* que malloc() retorna convertido para um int* pelo cast e atribudo a p. A declarao seguinte testa se a operao foi bem sucedida. Se no tiver sido, p ter um valor nulo, o que far com que !p retorne verdadeiro. Se a operao tiver sido bem sucedida, podemos usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)]. Os programas utilizam muito esta funo que reserva um bloco de memria que podemos utilizar vontade para o nosso programa e quando o bloco de cdigo executado ele reciclado pelo sistema operativo. int main() { int *p; p = malloc(sizeof(int)); if (p == 0) { printf("ERROR: Out of memory\n"); return 1; } *p = 5; printf("&d\n", *p); free(p); return 0; } Vamos comear por tentar explicar esta linha de cdigo. p = malloc(sizeof(int)); a funo malloc pegunta ao Heap (pretence ao sistema operativo) existe memria disponvel para um bloco de memria deste tamanho? e que tamnaho esse? esse valor depreendido pela funo sizeof(int). Como um int est a pedir 4 bytes. Assim a funo malloc retorna 0 se no consegue obter o tal espao de memria e 1 se consegue. Se consegue ento aloca um pointer varivel p A seguinte linha de cdigo mostra o valor a ns pelo ecr qual o valor retornado pela funo malloc, se consegui arranjar o espao de memoria ou no. vamos ver agora um exemplo; int main() { int *p, *q; p = malloc(sizeof(int)); q = p; *p = 10; printf("%d\n", *q); *q = 20;

Estudo printf("%d\n", *q); } Outro exemplo


int main() { int *p, *q; p = malloc(sizeof(int)); q = malloc(sizeof(int)); *p = 10; *q = 20; *p = *q; printf("%d\n", *p); } /*podemos simplificar por p = malloc(4) */

199

o compilador aceita *p=*q porque so ambos int. o compilador aceita tambm p=q porque so ambos pointes e apontam para a mesma tipologia Podemos simplificar p = malloc(sizeof(int)); por p = malloc(4); mas como temos sistemas operativos de 16,32, 64 bits a primeira declarao torna as coisas mais portveis. Repare que utilizamos o typecasting (int *) que fora a converso do pointer retornado do malloc que seja um pointer para um int.

calloc
A funo calloc() tambm serve para alocar memria, mas possui um prottipo um pouco diferente: void *calloc (unsigned int num, unsigned int size); A funao aloca uma quantidade de memria igual a num * size, isto , aloca memria suficiente para um vetor de num objetos de tamanho size. Retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribudo a qualquer tipo de ponteiro. Se no houver memria suficiente para alocar a memria requisitada a funo calloc() retorna um ponteiro nulo. Veja um exemplo de alocao dinmica com calloc():
#include <stdio.h> #include <stdlib.h> main (void) { int *p; int a; int i; ... p=calloc(a,sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } for (i=0; i<a ; i++) /* p pode ser tratado como um vetor com a posicoes */ /* Determina o valor de a em algum lugar */ /* Aloca a nmeros inteiros p pode agora ser tratado como um vetor com a posicoes */ /* Para usar calloc() */

Estudo
p[i] = i*i; ... return 0; }

200

No exemplo acima, alocada memria suficiente para se colocar a nmeros inteiros. O operador sizeof() retorna o nmero de bytes de um inteiro. Ele util para se saber o tamanho de tipos. O ponteiro void * que calloc() retorna convertido para um int * pelo cast e atribudo a p. A declarao seguinte testa se a operao foi bem sucedida. Se no tiver sido, p ter um valor nulo, o que far com que !p retorne verdadeiro. Se a operao tiver sido bem sucedida, podemos usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)].

realloc
A funo realloc() serve para realocar memria e tem o seguinte prottipo: void *realloc (void *ptr, unsigned int num); A funao modifica o tamanho da memria previamente alocada apontada por *ptr para aquele especificado por num. O valor de num pode ser maior ou menor que o original. Um ponteiro para o bloco devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se isso ocorrer, o contedo do bloco antigo copiado no novo bloco, e nenhuma informao perdida. Se ptr for nulo, aloca size bytes e devolve um ponteiro; se size zero, a memria apontada por ptr liberada. Se no houver memria suficiente para a alocao, um ponteiro nulo devolvido e o bloco original deixado inalterado.
#include <stdio.h> #include <stdlib.h> main (void) { int *p; int a; int i; ... a = 30; p=malloc(a*sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } for (i=0; i<a ; i++) p[i] = i*i; a = 100; p = realloc (p, a*sizeof(int)); for (i=0; i<a ; i++) p[i] = a*i*(i-6); ... /* p pode ser tratado como um vetor com a posicoes */ /* p pode ser tratado como um vetor com a posicoes */ /* O tamanho de p deve ser modificado, por algum motivo ... */ /* Aloca a nmeros inteiros p pode agora ser tratado como um vetor com a posicoes */ /* Determina o valor de a em algum lugar */ /* Para usar malloc() e realloc*/

Estudo
return 0; }

201

free
Quando alocamos memria dinamicamente necessrio que ns a liberemos quando ela no for mais necessria. Para isto existe a funo free() cujo prottipo : void free (void *p); Basta ento passar para free() o ponteiro que aponta para o incio da memria alocada. Mas voc pode se perguntar: como que o programa vai saber quantos bytes devem ser liberados? Ele sabe pois quando voc alocou a memria, ele guardou o nmero de bytes alocados numa "tabela de alocao" interna. Vamos reescrever o exemplo usado para a funo malloc() usando o free() tambm agora: #include <stdio.h> #include <stdlib.h> /* Para usar malloc e free */ main (void) { int *p; int a; ... p=malloc(a*sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } ... free(p); ... return 0; }

Alocao Dinmica de Vetores e Matrizes


Alocao Dinmica de Vetores A alocao dinmica de vetores utiliza os conceitos aprendidos na aula sobre ponteiros e as funes de alocao dinmica apresentados. Um exemplo de implementao para vetor real fornecido a seguir:
#include <stdio.h> #include <stdlib.h> float *Alocar_vetor_real (int n) { float *v; if (n < 1) { printf ("** Erro: Parametro invalido **\n"); return (NULL); /* verifica parametros recebidos */ /* ponteiro para o vetor */

Estudo
} v = calloc (n, sizeof(float)); if (v == NULL) { printf ("** Erro: Memoria Insuficiente **"); return (NULL); } return (v); } float *Liberar_vetor_real (float *v) { if (v == NULL) return (NULL); free(v); return (NULL); } int main (void) { float *p; int a; ... /* outros comandos, inclusive a inicializacao de a /* libera o vetor */ /* retorna o ponteiro */ /* retorna o ponteiro para o vetor */ /* aloca o vetor */

202

/
p = Alocar_vetor_real (a); ... p = Liberar_vetor_real (p); } /* outros comandos, utilizando p[] normalmente */

Alocao Dinmica de Matrizes


A alocao dinmica de memria para matrizes realizada da mesma forma que para vetores, com a diferena que teremos um ponteiro apontando para outro ponteiro que aponta para o valor final, ou seja um ponteiro para ponteiro, o que denominado indireo mltipla. A indireo mltipla pode ser levada a qualquer dimenso desejada, mas raramente necessrio mais de um ponteiro para um ponteiro. Um exemplo de implementao para matriz real bidimensional fornecido a seguir. A estrutura de dados utilizada neste exemplo composta por um vetor de ponteiros (correspondendo ao primeiro ndice da matriz), sendo que cada ponteiro aponta para o incio de uma linha da matriz. Em cada linha existe um vetor alocado dinamicamente, como descrito anteriormente (compondo o segundo ndice da matriz).
#include <stdio.h> #include <stdlib.h> float **Alocar_matriz_real (int m, int n) { float **v; int i; /* ponteiro para a matriz */ /* variavel auxiliar */

if (m < 1 || n < 1) { /* verifica parametros recebidos */

Estudo
printf ("** Erro: Parametro invalido **\n"); return (NULL); } v = calloc (m, sizeof(float *)); if (v == NULL) { printf ("** Erro: Memoria Insuficiente **"); return (NULL); } for ( i = 0; i < m; i++ ) { v[i] = calloc (n, sizeof(float)); if (v[i] == NULL) { printf ("** Erro: Memoria Insuficiente **"); return (NULL); } } return (v); } float **Liberar_matriz_real (int m, int n, float **v) { int i; /* variavel auxiliar */ /* retorna o ponteiro para a matriz */ /* m vetores de n floats */ /* aloca as colunas da matriz */ /* aloca as linhas da matriz */ /*Um vetor de m ponteiros para float */

203

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]); free (v); return (NULL); } int main (void) { float **mat; int l, c; /* matriz a ser alocada */ /* numero de linhas e colunas da matriz */ /* libera as linhas da matriz */ /* libera a matriz (vetor de ponteiros) */ /* retorna um ponteiro nulo */

int i, j; ... 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 */ /* outros comandos, inclusive inicializacao para l e c */

mat = Liberar_matriz_real (l, c, mat); ... }

Estudo

204

Estruturas - Primeira parte


Uma estrutura agrupa vrias variveis numa s. Funciona como uma ficha pessoal que tenha nome, telefone e endereo. A ficha seria uma estrutura. A estrutura, ento, serve para agrupar um conjunto de dados no similares, formando um novo tipo de dados. struct rec { int a,b,c; float d,e,f; }; struct rec r; o c permite que ns agreguemos vrias variveis, chamando essas variveis todas por um nome apenas. no nosso exemplo chammos de rec Podemos compactar da seguinte forma. so equivalentes. struct rec { int a,b,c; float d,e,f; } r; podemos atribuir o valor varivel a do grupo fazendo: r.a=5; Confesso que no entendi bem isto. no vejo grande vantagem. mas admito que pode ser til quando temos um cdigo muito grande e temos muitas variveis, e convm agrega-las numa forma lgica.

Criando
Para se criar uma estrutura usa-se o comando struct. Sua forma geral : struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variveis_estrutura; O nome_do_tipo_da_estrutura o nome para a estrutura. As variveis_estrutura so opcionais e seriam nomes de variveis que o usurio j 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 uma estrutura com dois campos, i e f. Foram tambm declaradas duas

Estudo variveis, a e b que so do tipo da estrutura, isto , a possui os campos i e f, o mesmo acontecendo com b. Vamos criar uma estrutura de endereo: 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 ficha_pessoal com os dados pessoais de uma pessoa: struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; Vemos, pelos exemplos acima, que uma estrutura pode fazer parte de outra ( a struct tipo_endereco usada pela struct ficha_pessoal).

205

Usando
Vamos agora utilizar as estruturas declaradas na seo anterior para escrever um programa que preencha uma ficha. #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; }; int main (void) { struct ficha_pessoal ficha;

Estudo 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 varivel ficha do tipo ficha_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 ficha, escrevemos: ficha.telefone = 4921234; Como a struct ficha pessoal possui um campo, endereco, que tambm 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 ficha e, dentro deste campo, estamos acessando o campo numero e o campo CEP.

206

Matrizes de estruturas
Uma estrutura como qualquer outro tipo de dado no C. Podemos, portanto, criar matrizes de estruturas. Vamos ver como ficaria a declarao de um vetor de 100 fichas pessoais: struct ficha_pessoal fichas [100]; Poderamos ento acessar a segunda letra da sigla de estado da dcima terceira ficha fazendo: fichas[12].endereco.sigla_estado[1]; Analise atentamente como isto est sendo feito ...

Atribuindo
Podemos atribuir duas estruturas que sejam do mesmo tipo. O C ir, neste caso, copiar uma estrutura, campo por campo, na outra. Veja o programa abaixo:
struct est1 { int i; float f; }; int main() { struct est1 primeira, segunda; primeira.i = 10; primeira.f = 3.1415; segunda = primeira; /* A segunda struct e' agora igual a primeira */ %d e %f ", segunda.i , segunda.f); /* Declara primeira e segunda como structs do tipo est1 */

printf(" Os valores armazenasdos na segunda struct sao :

Estudo
}

207

So 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 so copiados em segunda apenas com a expresso de atribuio: segunda = primeira; Todos os campos de primeira sero copiados na segunda. Note que isto diferente do que acontecia em vetores, onde, para fazer a cpia dos elementos de um vetor em outro, tnhamos que copiar elemento por elemento do vetor. Nas structs muito mais fcil! Porm, devemos tomar cuidado na atribuio de structs que contenham campos ponteiros. Veja abaixo: #include <stdio.h> #include <string.h> #include <stdlib.h> struct tipo_end { char *rua; /* A struct possui um campo que um ponteiro */ int numero; }; int 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 = malloc((strlen(buffer)+1)*sizeof(char)); /* Aloca a quantidade de memoria suficiente para armazenar a string */ strcpy(end1.rua, buffer); /* Copia a string */ printf("\nEntre o numero:"); scanf("%d", &end1.numero); end2 = end1; /* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */ 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 do que e' apontado 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 um erro grave, pois ao se fazer a atribuio end2 = end1, o campo

Estudo rua de end2 estar apontando para a mesma posio de memria que o campo rua de end1. Assim, ao se modificar o contedo apontado por end2.rua estaremos tambm modificando o contedo apontado por end1.rua !!!

208

Passando para funes


No exemplo apresentado no tem usando, vimos o seguinte comando: strcpy (ficha.nome,"Luiz Osvaldo Silva"); Neste comando um elemento de uma estrutura passado para uma funo. Este tipo de operao pode ser feita sem maiores consideraes. Podemos tambm passar para uma funo uma estrutura inteira. Veja a seguinte funo: void PreencheFicha (struct ficha_pessoal ficha) { ... } Como vemos acima fcil passar a estrutura como um todo para a funo. Devemos observar que, como em qualquer outra funo no C, a passagem da estrutura feita por valor. A estrutura que est sendo passada, vai ser copiada, campo por campo, em uma varivel local da funo PreencheFicha. Isto significa que alteraes na estrutura dentro da funo no tero efeito na varivel fora da funo. Mais uma vez podemos contornar este pormenor usando ponteiros e passando para a funo um ponteiro para a estrutura.

Ponteiros
Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia ser declarado um ponteiro para as estruturas de ficha que estamos usando nestas sees: struct ficha_pessoal *p; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. Para us-lo, haveria duas possibilidades. A primeira apont-lo para uma varivel struct j existente, da seguinte maneira: struct ficha_pessoal ficha; struct ficha_pessoal *p; p = &ficha; A segunda alocando memria para ficha_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 = malloc (a * sizeof(struct ficha_pessoal)); p[0].telefone = 3443768; free(p); } /* Exemplo de acesso ao campo telefone da primeira ficha apontada por p */

Estudo H mais um detalhe a ser considerado. Se apontarmos o ponteiro p para uma estrutura qualquer (como fizemos em p = &ficha; ) e quisermos acessar um elemento da estrutura poderamos fazer: (*p).nome Os parnteses so necessrios, porque o operador . tem precedncia maior que o operador * . Porm, este formato no muito usado. O que comum de se fazer acessar o elemento nome atravs do operador seta, que formado por um sinal de "menos" (-) seguido por um sinal de "maior que" (>), isto : -> . Assim faremos: p->nome A declarao acima muito mais fcil e concisa. Para acessarmos o elemento CEP dentro de endereco faramos: p->endereco.CEP Fcil, no?

209

Declarao Union
Uma declarao union determina uma nica localizao de memria onde podem estar armazenadas vrias variveis diferentes. A declarao de uma unio semelhante declarao de uma estrutura: union nome_do_tipo_da_union { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variveis_union; Como exemplo, vamos considerar a seguinte unio: union angulo { float graus; float radianos; }; Nela, temos duas variveis (graus e radianos) que, apesar de terem nomes diferentes, ocupam o mesmo local da memria. Isto quer dizer que s gastamos o espao equivalente a um nico float. Unies podem ser feitas tambm com variveis de diferentes tipos. Neste caso, a memria alocada corresponde ao tamanho da maior varivel no union. Veja o exemplo: #include <stdio.h> #define GRAUS 'G' #define RAD 'R' union angulo

Estudo { int graus; float radianos; }; int 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 poderamos 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' necessariamente 123 ...*/

210

O programa acima muito perigoso pois voc est lendo uma regio da memria, que foi "gravada" como um inteiro, como se fosse um ponto flutuante. Tome cuidado! O resultado pode no fazer sentido.

Estudo

211

Enumeraes
Numa enumerao podemos dizer ao compilador quais os valores que uma determinada varivel pode assumir. Sua forma geral : 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 programador diz ao compilador que qualquer varivel do tipo dias_da_semana s pode ter os valores enumerados. Isto quer dizer que poderamos 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 ("So dias diferentes."); } return 0; } Voc deve estar se perguntando como que a enumerao funciona. Simples. O compilador pega a lista que voc fez de valores e associa, a cada um, um nmero inteiro. Ento, ao primeiro da lista, associado o nmero zero, o segundo ao nmero 1 e assim por diante. As variveis declaradas so ento variveis int.

O Comando sizeof
O operador sizeof usado para se saber o tamanho de variveis ou de tipos. Ele retorna o tamanho do tipo ou varivel em bytes. Devemos us-lo para garantir portabilidade. Por exemplo, o tamanho de um inteiro pode depender do sistema para o qual se est compilando. O sizeof um operador porque ele substitudo pelo tamanho do tipo ou varivel no momento da compilao. Ele no uma funo. O sizeof admite duas formas: sizeof nome_da_varivel sizeof (nome_do_tipo) Se quisermos ento saber o tamanho de um float fazemos sizeof(float). Se declararmos a varivel f como float e quisermos saber o seu tamanho faremos sizeof f. O operador sizeof tambm funciona com estruturas, unies e enumeraes. Outra aplicao importante do operador sizeof para se saber o tamanho de tipos definidos pelo usurio. Seria, por exemplo, uma tarefa um tanto complicada a de alocar a memria para um ponteiro para a estrutura ficha_pessoal, criada na primeira pgina desta aula, se no

Estudo 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; }; int main(void) { struct ficha_pessoal *ex = malloc(sizeof(struct ficha_pessoal)); ... free(ex); return 0; }

212

O Comando typedef
O comando typedef permite ao programador definir um novo nome para um determinado tipo. Sua forma geral : 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 tambm pode ser utilizado para dar nome a tipos complexos, como as estruturas. As estruturas criadas no exemplo da pgina anterior poderiam ser definidas como tipos atravs do comando typedef. O exemplo ficaria: #include <stdio.h> typedef struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; } TEndereco; typedef struct ficha_pessoal {

Estudo char nome [50]; long int telefone; TEndereco endereco; }TFicha; int main(void) { TFicha *ex; ... } Veja que no mais necessrio usar a palavra chave struct para declarar variveis do tipo ficha pessoal. Basta agora usar o novo tipo definido TFicha.

213

Uma aplicao de structs: as listas simplesmente encadeadas


Vrias estruturas de dados complexas podem ser criadas utilizando simultaneamente structs e ponteiros. Uma destas estruturas a lista encadeada. Uma lista encadeada uma seqncia de structs, que so os ns da lista, ligados entre si atravs de ponteiros. Esta seqncia pode ser acessada atravs de um ponteiro para o primeiro n, que a cabea da lista. Cada n contm um ponteiro que aponta para a struct que a sua sucessora na lista. O ponteiro da ltima struct da lista aponta para NULL, indicando que se chegou ao final da lista. Esta estrutura de dados criada dinamicamente na memria (utiliza-se malloc() e free()), de modo que se torna simples introduzir ns nela, retirar ns, ordenar os ns, etc. No vamos entrar em detalhes sobre todos os algoritmos que poderamos criar em uma lista encadeada, pois isto geralmente feito em cursos de algoritmos e estruturas de dados, no se incluindo no escopo deste curso. Aqui, veremos somente formas de se criar uma lista encadeada em C e tambm maneiras simples de percorrer esta lista. Supondo que queiramos criar uma lista encadeada para armazenar os produtos disponveis em uma loja. Poderamos criar um n desta lista usando a seguinte struct:
struct Produto { int codigo; double preco; struct Produto *proximo; }; /* Codigo do produto */ /* Preco do produto */ /* Proximo elemento da lista encadeada de Produtos */

Note que esta struct possui, alm dos campos de dados codigo e preco, um campo adicional que um ponteiro para uma struct do tipo Produto. este campo que ser utilizado para apontar para o prximo n da lista encadeada. O programa a seguir faz uso desta struct, atravs 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 usada para criar os ns da lista */

Estudo int codigo; */ double preco; */ struct tipo_produto *proximo; /* Proximo elemento da lista encadeada de Produtos */ } TProduto; void inserir(TProduto **cabeca); /* Prototipos das funcoes para inserir e listar produtos */ void listar (TProduto *cabeca); int main() { TProduto *cabeca = NULL; /* Ponteiro para a cabeca da lista */ TProduto *noatual; /* Ponteiro a ser usado para percorrer a lista no momento de desalocar seus elementos*/ char q; /* Caractere para receber a opcao do usuario */ 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') ); noatual = cabeca; /* Desaloca a memoria alocada para os elementos da lista */ while (noatual != NULL) { cabeca = noatual->proximo; free(noatual); noatual = cabeca; } } void listar (TProduto *noatual) elementos presentes na lista encadeada */ /* Lista todos os /* Preco do produto /* Codigo do produto

214

Estudo { 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); 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 = 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 */ }

215

Estudo } interessante notar que, no programa anterior no existe limite para o nmero de produtos que se vai armazenar na lista. Toda vez que for necessrio criar um novo produto, memria para ele ser alocada e ele ser criado no final da lista. Note que a funo inserir recebe o endereo do ponteiro cabea da lista. Qual a razo disto? A razo que o endereo para o qual a cabea da lista aponta poder ser modificado caso se esteja inserindo o primeiro elemento na lista. Tente entender todos os passos deste programa, pois ele possui vrias das caractersticas presentes em programas que manipulam listas encadeadas. Tambm importante notar que vrias outras estruturas de dados complexas podem ser criadas com structs contendo ponteiros que apontam para outras structs. Ir para o Indice | Exerccios
[1]

216

Referncias
[1] O que Ginstica Laboral? O programa de Ginstica Laboral Reposturar tem como objetivo prevenir doenas ocasionadas pelo trabalho repetitivo, agindo de forma a interromper a monotonia ocupacional. O programa consiste na aplicao de exerccios como pilates, R.P.G., medicina chinesa, alongamentos, fortalecmentos muscular e esqueltico, eutonia e relaxamento muscular, no ambiente de trabalho por um perodo de 10 a 15 minutos. Benefcios para a equipe: Promover o bem estar geral; Melhorar a qualidade de vida; Combater o sedentarismo; Diminuir o stress ocupacional; Melhorar a coordenao motora e a flexibilidade msculo - articular; Promover a integrao social dos funcionrios; Promover momentos de descontrao, relaxamento e lazer Benefcios para a empresa: Reduzir o absentesmo e a rotatividade; Melhorar a qualidade total; Prevenir e reabilitar as doenas ocupacionais (L.E.R., DORT); Mostrar a preocupao da empresa com seu funcionrio; Diminuir acidentes de trabalho; Aumentar a produtividade.

Constantes

217

Constantes
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Constantes
Em um captulo anterior abordamos as variveis e agora vamos abordar constantes! A razo que as coisas esto mais maduras e uma pessoa sabe muito bem o que uma constante. Que simplesmente um valor que no se altera durante a execuo do programa. A questo de no se alterar durante a escrita do programa realmente a razo deste captulo. Devemos separar as guas entre uma constante e um literal. O literal o prprio valor. Existem 3 maneiras para criar constantes: 1. #define 2. [const] [tipologia da variavel][nome da variavel] 3. enumerations Esta ltima vamos deixar para mais tarde, pois so uma boa introduo para as classes.

DEFINED CONSTANTS (#DEFINE)


#define PI 3.14159265 #define NEWLINE '\n'

Se colocarmos estas linhas no header, o que vai acontecer o seguinte: O pr-processador ir verificar o nosso cdigo fonte e sempre que encontrar a directiva #define ir, literalmente, substituir cada ocorrncia do identificador no cdigo fonte pelo valor definido. A vantagem disto que: Podemos ter um identificador ao nosso gosto, e sempre que necessitarmos do valor escrevemos o identificador, em vez do valor, at porque se o valor fosse complicado poderamos enganar-nos a escrever. Claro que nos poderamos enganar tambm a escrever o identificador, da a escolhermos um nome familiar. E se necessitarmos de alterar o valor, alteramos apenas 1 vez, em vez de todas as vezes onde apareceria o valor. O formato geral : #define identificador valor Repare que a directiva de preprocessador no tem o ;- ponto e virgula no fim! O que normal para directivas de Prprocessador. O que que acontece se tivermos o ; no fim? Ser que encontrei um bug? se eu colocar o ; no #define NEWLINE '\n'; no acontece nada. Vale lembrar que cada #define preprocessador, ou seja, no pode ser alterado dentro do programa durante sua execuo. // defined constants: calculate circumference #include <iostream> using namespace std; #define PI 3.14159 #define NEWLINE '\n' int main () { double r=5.0; // radius double circle;

Constantes circle = 2 * PI * r; cout << circle; cout << NEWLINE; system (pause); return 0; } // utilizmos o Pi e no 3,.

218

Declared constants (const)


Ns podemos transformar uma varivel numa constante do gnero: const int tamanho = 100; const char tabul = '\t'; const cdigo = 12440; Com o prefixo const, dizemos que a varivel no poder alterar o seu valor. Repare que se fosse uma varivel eu poderia ter: int a=5; e logo a seguir dizer a=6; e o valor do a ficava com o valor de 6; Agora com o prefixo const eu no poderei alterar o valor, porque constante constante, no pode mudar o valor. Esta maneira bem melhor do que a anterior para declarar constantes, primeiro porque aqui temos a informao da tipologia de varivel, em segundo porque podemos fazer este const int a; como uma funo local e no global.

Condicionais

219

Condicionais
Esta pgina precisa ser reciclada (discuta). Ao melhor-la, voc estar ajudando o Wikilivros.

Control Structures
Aqui vamos criar funes que nos permitam alterar a leitura de execuo do cdigo. Ou seja, j dissemos que a leitura de execuo do cdigo de cima para baixo (l a primeira linha de cdigo executa, l a 2 linha --executa,..e por a adiante) O que agora vamos fazer e criar mecanismos para alterar esse leitura sequencial e permitir: Execuo de cdigo sujeito a condio Repetio de execuo de cdigo sujeito a condio Saltar linhas de cdigo enfim isto o que nos vai permitir realmente comear a ser imaginativos, vejamos

Branching - Funo IF
A funo if s executada se a expresso relacional for true=1. A sintax : if (Boolean value) statement; Reparar que s temos o ponto e virgula no final do statement. Exemplificando:
#include <iostream> using namespace std; int main(void) { int num; cout << "entre com um nmero inteiro: "; cin >> num; if ( num % 2 == 0 ) cout << "o nmero par" << endl; /* O comando system utilizar um argumento difetente conforme o compilador. Retire o comentrio daquela que parecer funcionar no compilador corrente */ // system (pause); // system ("PAUSE"); // cin.get() // Outra opo utiliza cin.get() ao invs de system ("PAUSE") return 0; }

No exemplo se colocarmos um valor impar o programa acaba, se colocarmos um valor par ele apresenta a frase o nmero par. Repare no operador de igualdade ==. Como o operador aritmtico tem uma precedncia superior em relao aos operadores relacionais primeiro feita a conta num%2. Erro: Um erro bastante comum colocar o ponto e virgula antes do statement como j avisei. Mas isto no vai dar um erro de compilao mas sim um erro lgico. Isto aceite porque permitido ter nenhum statement. Erro: Outro erro tambm bastante comum trocar o = por ==, ou seja querem fazer a comparao em vez de atribuio!

Condicionais Depois ainda temos o ponto se tivermos 2 statements depois do if, s o primeiro que afectado pela avaliao do valor boolean. Ou seja o 2 statement como estivesse fora do if. Para colocarmos vrios (+de 2) afectados pela avaliao do if temos de os colocar em grupo com {}. Pergunta: no tem realmente sentido a sintax do c++ permitir a questo do ponto e vrgula, para funes de controlo de execuo de cdigo. Deveria dar logo erro de compilao. Mas penso que isto se deve por termos mais operadores para alm do if. IF-ELSE No exemplo anterior no acontecia nada se inserssemos um valor impar. Podemos remendar isso se utilizarmos o if-else if (relational expression) conditional statement; else conditional statement; Remendando o exemplo anterior ficamos com
#include <iostream> using namespace std; int main(void) { int num; cout << "Enter a whole number: "; cin >> num; if ( num % 2 == 0 ) cout << "The number is even" << endl; else cout << "The number is odd" << endl; system (pause); return 0; }

220

No utilizar o ; a seguir ao else Aqui tambm costuma acontecer um erro bem comum que
if ( num % 2 == 0 ) cout << "The number is even" << endl; else ( num % 2 == 1 ) cout << "The number is odd" << endl;

O problema est no else que no deve ter a expresso relacional If - ELSE IF - Else

Condicionais

221
#include <iostream> using namespace std; int main(void) { int testScore; cout << "Entre com um valor (0 a 100): "; cin >> testScore; if (testScore >= 90 ) cout << "o valor A" << endl; else if (testScore >= 80 ) cout << " o valor B" << endl; else if (testScore >= 70 ) cout << " o valor C" << endl; else if (testScore >= 60 ) cout << " o valor D" << endl; else cout << " o valor F" << endl; system (pause); return 0; }

Notar que o ultimo apenas else e no else if Mas h ainda aqui 1 pormenor importante. Se inserirmos 87, deveria dar B e ainda C e D, pois para ambos cumprida a relao de comparao. o que se passa que quando uma das hipteses realizada, os outros elses so ignorados. Portanto se alterssemos o else-if do C para antes do B teramos o resultado C. portanto aqui temos a questo da ordenao do cdigo o programa comea a testar as condies comeando pela primeira at achar uma condio que seja verdadeira, se no encontrar nenhuma temos o else final (que o defaut) mas ela opcional! IF-ELSE dentro de IF-ELSE
if (age >= 18) if(citizen == true) cout << "You are eligible to vote"; else cout << "You are not eligible to vote"; else cout << "You are not eligible to vote";

Chama-se a IF dentro de IF ou NESTED IF ou ainda em portugus IF ANINHADO

Condicionais

222

Branching - Switch
O switch muito parecido com o if-else. Apenas a sintaxe e construo diferente
#include <iostream> using namespace std; int main(void) { char grade; cout << "Enter your grade (A cin >> grade; switch (grade) { case 'A': cout << "Your break; case 'B': cout << "Your break; case 'C': cout << "Your break; case 'D': cout << "Your break; default: cout << "Your } system (pause); return 0; }

to F): ";

average must be between 90 - 100"<< endl;

average must be between 80 - 89"<< endl;

average must be between 70 - 79"<< endl;

average must be between 60 - 69"<< endl;

average must be below 60" << endl;

Cada um dos casos tem de ser uma constante (no pode alterar durante a vida do programa), neste exemplo A, B, O defaut serve para a condio de todas as avaliaes dos casos anteriores der falsa. ( tipo o else) O break serve para terminar o switch, caso contrrio se num dado case fosse verdadeiro, iria executar todos os statementes mesmo de outros cases at terminar o switch. Aqui para cada caso no necessitamos de {} se tivermos mais do que 2 statements. o if-else mais forte do que o switch por que permite fazer coisas como: if (apples == oranges) do this; else if (sales >= 5000) do that; Para alm do ponto j dito de os casos serem obrigatoriamente constants, no switch Tambm posso utilizar operadores lgicos no switch
switch (age >= 18 && citizen == true) { case true: cout << "You are eligible to vote"; break; case false: cout << "You are not eligible to vote"; }

Condicionais

223

LOOP - WHILE
#include <iostream> using namespace std; int main(void) { int num = 1; while (num <= 10) { cout << num << " "; num++; } system (pause); return 0; }

Notar que colocamos o incremento dentro do bloco caso contrrio tnhamos um loop infinito. Notar que se quisermos podemos ter o incremento e a condio ao mesmo tempo while (num++ < 10) Mas neste caso quem que tem maior precedncia o ++ postfix ou o <.? (Vem primeiro a comparao.) a condio do loop pode ser da forma(a0) no esquecer a aula dos operadores

LOOP DO WHILE
Aqui a diferena que o teste da condio feita depois dos statements, o que significa que os statements so executados pelo menos uma vez do { statement(s); } while (condition);

A funo do while exactamente igual while s que pe a condio depois do bloco, o que significa que o bloco executado pelo menos uma vez. ou seja o seu uso garante que os statements ou declarao executada pelo menos uma vez! por isso que ela to usada nos menus.

LOOP - FOR
Esta para mim a arma mais poderosa
#include <iostream> using namespace std; int main(void) { for (int num = 1; num <= 10; num++) cout << num << " "; system (pause); return 0; }

A sintaxe for ([iniciao]; [condio]; [incremento]) statement;

Condicionais

224

A condio tem de ser true (=1) para ser executado o statement. Depois dos statements serem executados vem a instruo de incremento e Novamente avaliada a condio. Se esta ainda for true volta-se a repetir o ciclo. Notar mais uma vez onde est o ponto e vrgula. Notar que a condio pode ser a<10 || b>20, a iniciao e o incremento tambm podem ser desta forma. Pergunta: se no tivermos um dos parmetros o que que acontece? Com esta possibilidade podemos alcanar loops infinitos, tipo Caso 1: ausncia do 2 parmetro for (int num =1; ;num++) { cout << num << " "; } Isto vai colocar um valor a mais no num indefinidamente. Caso 2: ausncia do 3 parmetro for (int num=1; num <= 10; { cout << num << " "; } )

Isto vai repetir sempre o num=1, apesar de termos condio, mas a condio num chega a ser alcanada. Felizmente o sistema operativo consegue detectar muitas vezes o loop infinito, mas caso isso no acontea utilizar o CRTL-BREAK. Podemos ter tambm loops dentro de loops Podemos tambm quebrar o loop atravs do break

Condicionais

225

Comando - Break
O que o break faz quebrar a execuo para fora do bloco de cdigo onde ela est presente
#include <iostream> using namespace std; int main(void) { int num; char choice; bool quit = false; while (true) { cout << "Enter a positive number: "; cin >> num; if (num > 0) break; else { cout << "Number must be positive; try again (Y/N): "; cin >> choice; if (choice != 'Y') { quit = true; break; } } } if (quit == false) cout << "The number you entered is " << num << " "; else cout << "You did not enter a positive number"; system (pause); return 0; }

O break faz com que a execuo do programa continue na primeira linha seguinte ao loop ou bloco

Condicionais

226

Comando Continue
Esta instruo bem parecida com o break, mas algo diferente. Pois em vez de mandar a execuo para fora do bloco manda-a para a avaliao do loop. Ou seja faz saltar uma determinada iterao do loop, enquanto o break faz acabar o loop
#include <iostream> using namespace std; int main(void) { int num, counter = 0, total = 0; cout << "How many items do you want to buy: "; cin >> num; while (counter++ < num) { if (counter % 13 == 0) continue; total += 3; } cout << "Total for " << num << " items is $" << total; return 0; }

Neste exemplo quando o conter for mltiplo de 13, a instruo seguinte saltada total+=3

Comando goto
O goto realiza um salto para um local especificado. Este local determinado por um rtulo. Portanto pode ser em qualquer parte do programa. nome_do_rtulo: .... goto nome_do_rtulo; ....

// goto loop example #include <iostream> using namespace std; int main () { int n=10; loop: cout << n << ", "; n--; if (n>0) goto loop; cout << "FIRE!"; system (pause); return 0; }

repare no rotulo

Condicionais

227

JUMP O Comando exit


Esta funo definida com a biblioteca cstdlib (c+std+lib) O propsito da funo terminar com o programa com um especfico cdigo de sada O prottipo : int exit (int exitcode); Esta funo usada por alguns sistemas operativos e podem ser usadas para chamar programas. Por conveno o cdigo 0 se sada significa que o programa terminou normalmente, como previsto, se vier com outro nmero significa que houve um erro e algo de inesperado sucedeu. Pergunta: qual a diferena entre o exit e o return?

COMMA OPERATOR ( , )
O operador vrgula (,) usado para separar 2 ou mais expresses onde se espera apenas uma expresso. Quando tivermos vrias expresses estiverem a ser avaliadas para um valor, a expresso mais direita a que considerada. a = (b=3, b+2); Neste exemplo iria colocar o valor 3 em b e depois adicionaria 2 ao valor de b ficando a com 5 e b com 3

Operador condicional ?
A sintaxe [Relational expression] ? [statement if true] : [statement if false] Ou seja, este operador faz um teste expresso relacional e se o resultado for verdadeiro executa logo a 1 afirmao caso contrrio executa a segunda. ou seja isto no mais do que um if-else. H quem goste de usar este operador porque poupa escrita, mas acho que no vale a pena!
#include <iostream> using namespace std; int main(void) { int num; cout << "Enter a whole number: "; cin >> num; cout << "The number is " << (num % 2 == 0 ? "even" : "odd") << endl; system (pause); return 0; }

Notar que o operador condicional exige 3 operandos. vamos fazer uns exerccios: 7==5 ? 4 : 3 // returns 3, since 7 is not equal to 5. 7==5+2 ? 4 : 3 // returns 4, since 7 is equal to 5+2. 5>3 ? a : b // returns the value of a, since 5 is greater than 3. a>b ? a : b // returns whichever is greater, a or b.

Condicionais

228

Increment/decremente Operator
Aqui vamos voltar a um tpico anterior que foi abordado nos operadores Temos a=a+1 equivalente a ter a+=1 e ainda a ter a++ (este novo) Mas isto tudo s no caso do incremento ser 1. Podemos ter ++a ou ainda a++. Eles so parecidos mas diferentes, a questo do prefixo e ps-fixo. A diferena que O prefixo, faz o incremento ainda durante a instruo O ps-fixo faz o incremento quando se passa para a instruo seguinte.
#include <iostream> using namespace std; int main(void) { int num = 2; cout << num << \n; cout << ++num << \n; cout << num++ <<\n; cout << num << \n; system (pause); return 0; }

Portanto int num = 5; cout << (++num == 5);

Exerccios
Crie um programa que d o factorial de um nmero
#include <iostream> using namespace std; int main(void) { int num, counter, total = 1; cout << "Enter a number: "; cin >> num; cout << "The factorial of " << num << " is "; for (int counter = 1; counter <= num; counter++) total *= counter; cout << total; system (pause); return 0; }

Crie um programa para o utilizador adivinhar um nmero de 0 a 3. D 3 hipteses para adivinhar. No caso de acertar antes de chegar ao fim das 3 hipteses termine.

Condicionais

229

#include <iostream> using namespace std; int main(void) { int num, counter, secret = 3; cout << "Guess a number between 1 and 10\n"; cout << "You have 3 tries\n"; for (int counter = 1; counter <= 3; counter++) { cout << "Enter the number now: "; cin >> num; if (num == secret) { cout << "You guessed the secret number!"; break; } } cout << "Program over"; system (pause); return 0; } Criao de menu.

#include <iostream> (i>3)); using namespace std; switch (i) int main () { { case 1: int i; cout << ("\t\tVoce escolheu Mamao.\n"); do break; { case 2: cout << "\n\nEscolha a fruta pelo numero:\n\n"; cout <<"\t\tVoce escolheu Abacaxi.\n"; cout << "\t(1)...Mamao\n"; break; cout << "\t(2)...Abacaxi\n"; case 3: cout << "\t(3)...Laranja\n\n"; cout << ("\t\tVoce escolheu Laranja.\n"); cin >> i; break; } } while ((i<1) system (pause); return(0); }

Fontes e Editores da Pgina

230

Fontes e Editores da Pgina


Capa Fonte: https://pt.wikibooks.org/w/index.php?oldid=186016 Contribuidores: David Stress, Delemon, Edudobay, Elvire, Joaodaveiro, Jorge Morais, Lightningspirit, Master, SallesNeto BR, Voz da Verdade, Wbrito, 159 edies annimas Programar em C Fonte: https://pt.wikibooks.org/w/index.php?oldid=234258 Contribuidores: Dante Cardoso Pinto de Almeida, Devarde, Edudobay, EvertonS, Helder.wiki, Jorge Morais, Joo Jernimo, Lightningspirit, Master, Maxtremus, Noturno99, Raylton P. Sousa, RibeiroGustavoTI, Thiagol, Voz da Verdade, Wbrito, 16 edies annimas Por que aprender a linguagem C Fonte: https://pt.wikibooks.org/w/index.php?oldid=212766 Contribuidores: Edudobay, Jorge Morais, Joo Jernimo, Lightningspirit, SallesNeto BR, Sourf, Thiagol, Wbrito, 2 edies annimas Histria da linguagem C Fonte: https://pt.wikibooks.org/w/index.php?oldid=228044 Contribuidores: Edudobay, Helder.wiki, Jorge Morais, Lightningspirit, Scorpion, 11 edies annimas Pr-requisitos Fonte: https://pt.wikibooks.org/w/index.php?oldid=233387 Contribuidores: Albmont, Edudobay, Helder.wiki, Jorge Morais, Lightningspirit, Marcos Antnio Nunes de Moura, Wbrito, 11 edies annimas Utilizando um compilador Fonte: https://pt.wikibooks.org/w/index.php?oldid=236622 Contribuidores: Albmont, Edudobay, EvertonS, Master, Thiagol, 3 edies annimas Noes de compilao Fonte: https://pt.wikibooks.org/w/index.php?oldid=212773 Contribuidores: Aprendiz de feiticeiro, Edudobay, SallesNeto BR, Thiagol, Wbrito Um programa em C Fonte: https://pt.wikibooks.org/w/index.php?oldid=236343 Contribuidores: Awillian, Edudobay, EvertonS, Jorge Morais, Marcos Antnio Nunes de Moura, Thiagol, Wbrito, 7 edies annimas Conceitos bsicos Fonte: https://pt.wikibooks.org/w/index.php?oldid=212774 Contribuidores: Algum, Edudobay, Helder.wiki, Thiagol, Wbrito, 2 edies annimas Variveis Fonte: https://pt.wikibooks.org/w/index.php?oldid=235689 Contribuidores: Defender, Edudobay, EvertonS, Jorge Morais, Marcos Antnio Nunes de Moura, Mr.Yahoo!, Thiagol, Wbrito, 5 edies annimas Entrada e sada simples Fonte: https://pt.wikibooks.org/w/index.php?oldid=234174 Contribuidores: Albmont, Edudobay, EvertonS, Thiagol, Wbrito, Yuu eo, 8 edies annimas Operaes matemticas (Bsico) Fonte: https://pt.wikibooks.org/w/index.php?oldid=236335 Contribuidores: Edudobay, Wbrito, 4 edies annimas Operaes matemticas (Avanado) Fonte: https://pt.wikibooks.org/w/index.php?oldid=225811 Contribuidores: Rogerboff, SallesNeto BR, Thiagol, Wbrito, 8 edies annimas Controle de fluxo Fonte: https://pt.wikibooks.org/w/index.php?oldid=235549 Contribuidores: Albmont, Edudobay, Helder.wiki, Hycesar, Rogerboff, Thiagol, Wbrito, 7 edies annimas Funes Fonte: https://pt.wikibooks.org/w/index.php?oldid=235555 Contribuidores: Albmont, Awillian, Edudobay, EvertonS, Hycesar, Rogerboff, Wbrito, 24 edies annimas Pr-processador Fonte: https://pt.wikibooks.org/w/index.php?oldid=235568 Contribuidores: Edudobay, EvertonS, Hycesar, Lgrave, Rogerboff, SallesNeto BR, 2 edies annimas Bibliotecas Fonte: https://pt.wikibooks.org/w/index.php?oldid=212778 Contribuidores: Edudobay, EvertonS, Helder.wiki, Rogerboff, Wbrito, 5 edies annimas Entrada e sada em arquivos Fonte: https://pt.wikibooks.org/w/index.php?oldid=212654 Contribuidores: Edudobay, EvertonS, Wbrito, 2 edies annimas Vetores Fonte: https://pt.wikibooks.org/w/index.php?oldid=236547 Contribuidores: Ajraddatz, Dante Cardoso Pinto de Almeida, Edudobay, EvertonS, Helder.wiki, Hycesar, Jonas AGX, Wbrito, 17 edies annimas Strings Fonte: https://pt.wikibooks.org/w/index.php?oldid=234186 Contribuidores: Albmont, Defender, Edudobay, Jorge Morais, Wbrito, 8 edies annimas Ponteiros Fonte: https://pt.wikibooks.org/w/index.php?oldid=235609 Contribuidores: Albmont, Edudobay, EvertonS, Hycesar, Jesielt, Jorge Morais, Marcos Antnio Nunes de Moura, Noturno99, Wbrito, 25 edies annimas Passagem de Parametros Fonte: https://pt.wikibooks.org/w/index.php?oldid=232598 Contribuidores: David Stress, EvertonS, Wbrito, 4 edies annimas Tipos de dados definidos pelo usurio Fonte: https://pt.wikibooks.org/w/index.php?oldid=234520 Contribuidores: Edudobay, Wbrito, 11 edies annimas Enumerao Fonte: https://pt.wikibooks.org/w/index.php?oldid=234195 Contribuidores: Daveiro, EvertonS, Helder.wiki, Jorge Morais, Marcelo-Silva, Marcos Antnio Nunes de Moura, Master, Wbrito, 16 edies annimas Unio Fonte: https://pt.wikibooks.org/w/index.php?oldid=221495 Contribuidores: Daveiro, Jorge Morais, Marcelo-Silva, Marcos Antnio Nunes de Moura, Master, Wbrito, 5 edies annimas Estruturas Fonte: https://pt.wikibooks.org/w/index.php?oldid=236001 Contribuidores: Albmont, Daveiro, EvertonS, Jorge Morais, Marcelo-Silva, Marcos Antnio Nunes de Moura, Master, Wbrito, 17 edies annimas Mais sobre variveis Fonte: https://pt.wikibooks.org/w/index.php?oldid=236108 Contribuidores: Edudobay, GabrielFalcao, Helder.wiki, Wbrito, 2 edies annimas Gerenciamento de memria Fonte: https://pt.wikibooks.org/w/index.php?oldid=234187 Contribuidores: Edudobay, Marcos Antnio Nunes de Moura, 20 edies annimas Sockets Fonte: https://pt.wikibooks.org/w/index.php?oldid=233658 Contribuidores: Albmont, EvertonS, Jorge Morais, 5 edies annimas Makefiles Fonte: https://pt.wikibooks.org/w/index.php?oldid=235039 Contribuidores: David Stress, EvertonS, Helder.wiki, Jorge Morais, 47 edies annimas Lista de palavras reservadas Fonte: https://pt.wikibooks.org/w/index.php?oldid=186025 Contribuidores: Jorge Morais, 2 edies annimas Seqncias de escape Fonte: https://pt.wikibooks.org/w/index.php?oldid=186035 Contribuidores: Devarde, Master, SallesNeto BR, 3 edies annimas Lista de funes Fonte: https://pt.wikibooks.org/w/index.php?oldid=186024 Contribuidores: Devarde, Master, SallesNeto BR Lista de bibliotecas Fonte: https://pt.wikibooks.org/w/index.php?oldid=186023 Contribuidores: EvertonS, Jorge Morais, 2 edies annimas Dicas de programao em C Fonte: https://pt.wikibooks.org/w/index.php?oldid=190134 Contribuidores: Dante Cardoso Pinto de Almeida, Helder.wiki, 4 edies annimas Listas encadeadas Fonte: https://pt.wikibooks.org/w/index.php?oldid=234952 Contribuidores: EvertonS, Jorge Morais, Maxtremus, 8 edies annimas Pilha Fonte: https://pt.wikibooks.org/w/index.php?oldid=232348 Contribuidores: 5 edies annimas rvores binrias Fonte: https://pt.wikibooks.org/w/index.php?oldid=232389 Contribuidores: EvertonS, Maxtremus, Ruy Pugliesi, 24 edies annimas Algoritmos de ordenao Fonte: https://pt.wikibooks.org/w/index.php?oldid=224248 Contribuidores: EvertonS, 11 edies annimas Algoritmo de alocao Fonte: https://pt.wikibooks.org/w/index.php?oldid=223824 Contribuidores: EvertonS, Jorge Morais, 2 edies annimas Estudo Fonte: https://pt.wikibooks.org/w/index.php?oldid=231229 Contribuidores: Algum, Edudobay, EvertonS, Jorge Morais, Lightningspirit, Marcos Antnio Nunes de Moura, Savh, 22 edies annimas Constantes Fonte: https://pt.wikibooks.org/w/index.php?oldid=117867 Contribuidores: Daveiro, Helder.wiki, Jorge Morais, Marcelo-Silva, Marcos Antnio Nunes de Moura, Master, SallesNeto BR, Wbrito, 8 edies annimas

Fontes e Editores da Pgina


Condicionais Fonte: https://pt.wikibooks.org/w/index.php?oldid=207774 Contribuidores: Daveiro, Jorge Morais, Marcelo-Silva, Marcos Antnio Nunes de Moura, Master, Wbrito, 10 edies annimas

231

Fontes, Licenas e Editores da Imagem

232

Fontes, Licenas e Editores da Imagem


Imagem:Programar_c_cover.png Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Programar_c_cover.png Licena: GNU Free Documentation License Contribuidores: Dante Cardoso Pinto de Almeida, Lightningspirit Imagem:50%.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:50%.svg Licena: Public Domain Contribuidores: Siebrand Imagem:100%.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:100%.svg Licena: Public Domain Contribuidores: Siebrand Imagem:25%.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:25%.svg Licena: Public Domain Contribuidores: Karl Wick Imagem:75%.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:75%.svg Licena: Public Domain Contribuidores: Siebrand Imagem:Searchtool.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Searchtool.svg Licena: GNU Lesser General Public License Contribuidores: David Vignoni, Ysangkok Imagem:Translation_arrow.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Translation_arrow.svg Licena: GNU Free Documentation License Contribuidores: Jesse Burgheimer Image:Ken n dennis.jpg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Ken_n_dennis.jpg Licena: Public Domain Contribuidores: 32X, Bastique, Ebcdic, Lzur, Mormegil, Quibik, Reisio, Sven, 1 edies annimas Image:Recycle001.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Recycle001.svg Licena: desconhecido Contribuidores: Users Cbuckley, Jpowell on en.wikipedia Imagem:Crystal_Clear_app_kaddressbook.png Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Crystal_Clear_app_kaddressbook.png Licena: GNU Free Documentation License Contribuidores: CyberSkull, It Is Me Here, Rocket000 Imagem:EsquemaPonteiro.png Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:EsquemaPonteiro.png Licena: desconhecido Contribuidores: Edudobay Image:Merge-arrows.svg Fonte: https://pt.wikibooks.org/w/index.php?title=Ficheiro:Merge-arrows.svg Licena: Public Domain Contribuidores: User:Erin Silversmith, User:Lifeisunfair, User:Rei-artur

Licena

233

Licena
Creative Commons Attribution-Share Alike 3.0 Unported //creativecommons.org/licenses/by-sa/3.0/

Você também pode gostar