Vous êtes sur la page 1sur 96

Versoantiga Embreve,versorevista

LINGUAGEM C

Otton Teixeira da Silveira Filho

Registro N 70.630 Biblioteca Nacional - Escritrio de Direitos Autorais

1997

Nmeros de pgina
SUMRIO 1)Introduo 2)C, A linguagem 2.1) Identificadores 2.1) Palavras-chave 2.2) Tipos de variveis 2.3) Modificadores 3)Operadores de atribuio vrgula aritmticos de incremento e decremento de endereo de bit lgicos relacionais 1 3 4 4 4 6 6 6 7 8 8 8 9

Precedncia de operadores Abreviao de operaes Converso de tipos 4) Constantes 5)Controle de fluxo if-else ?: switch-case-default goto 6)Laos while do-while for Palavras auxiliares break continue 15

9 10 11 12 13 13 13 13 14

15 15 15 16 16 16

7) Fatos e mitos - Velocidade de processamento17 8)Programas em C 7.1) Denteao 9)Funes Forma clssica 24 25 18

Variveis Globais, Locais e Automticas28 Um Comando do Preprocessador, cabealhos e arquivos


Departamento de Cincia da Computao-UFF

Nmeros de pgina
de incluso Algumas funes teis 10) Ponteiros Ponteiros e funes Ponteiros e vetores Aritmtica de Ponteiros Inicializando vetores Ponteiros para funes Mais Sobre Funes 11) O Preprocessador O comando #define (do preprocessador)51 Mais comandos do preprocessador 54 12) Vetores Multidimensionais 56 13) Vetores de Ponteiros e Ponteiros de Vetores 58 Ponteiros de Ponteiros & Ponteiros de Ponteiros de Ponteiros e etc...59 10) Funes Em notao moderna Funes em Forma Moderna Mais Algumas Funes 11) main() com parmetros 12) Modificadores Variveis estticas Variveis externas Variveis registrador 13) Reservando memria para uso temporrio Fragmentao 14) Estruturas e unies Estruturas Estruturas e Manipulao de Bits Estruturas e funes Unies Inicializando estruturas e unimos 71 71 73 74 76 81 69 70 60 29 31 36 36 39 42 43 44 46

60 61 63 65 65 67 67

15) Determinando o tamanho de Estruturas, Unies e etc82 15) Enumerao 16) Definio de Tipos 16) Trabalhando com arquivos em disco Funes de Alto Nvel Funes de Baixo Nvel 86 91 85 83

Departamento de Cincia da Computao-UFF

Nmeros de pgina
17)Arquivos padres e como usar a impressora 97 18) Respostas de problemas 19) Apndices Tabelas dos operadores lgicos101 99

Departamento de Cincia da Computao-UFF

Nmeros de pgina
INTRODUO C uma linguagem de programao que tem como caractersticas principais grande flexibilidade, escrita compacta, padronizao bem feita e alta velocidade de processamento. Por estas caractersticas, ela chamada, muitas vezes, de uma Linguagem de Programador pois envolve aspectos, como os j citados, que agradam a estes profissionais. Foi desenvolvida no incio da dcada de 70 por Dennis Ritchie influenciada pela linguagem B, esta desenvolvida por Ken Thompson. A linguagem B teve como fonte inspiradora uma outra linguagem, o BCPL. B uma linguagem que s tem um tipo de dado que corresponde ao tamanho da palavra do computador no qual trabalha. A linguagem C, por sua vez, tem uma gama relativamente grande de tipos de variveis mas no to fortemente tipada como Pascal. Permite ainda a criao de estruturas de dados no homogneas. Apesar destas diferenas, BCPL, B e C so filosoficamente semelhantes. A ideia ter uma linguagem de escrita compacta, com recursos de manipulao de dados a baixo nvel mas com caractersticas de linguagem de alto nvel. Criar um compilador de C uma tarefa consideravelmente mais fcil que criar um compilador Fortran, por exemplo, alm de poder ser bem pequeno existindo verses que completas e funcionais que ocupam menos de 100KB de espao em disco, exigem memria da ordem de 256KB, restrices que nos dias de hoje (1997) nos parecem paleolticas. As primeiras verses para micro (Apple II, por exemplo) funcionavam bem melhor que compiladores de outras linguagens. As idias por trs de C so to simplificadoras e "naturais" que ela serve de base para outras linguagens. Dando apenas um exemplo, existe uma linguagem que se baseia simultaneamente em C e no conceito de objetos[]. Esta linguagem, o C++ (pronuncia-se c mais mais), bem definida existindo o padro ANSI. Atualmente existem compiladores dos mais diversos fabricantes e ainda verses de domnio pblico largamente usados em ensino e pesquisa no mundo inteiro. O caso mais notrio o GNU C (embora o Projeto GNU no se limite este compilador) e existe verses do mesmo para os mais variados ambientes (UNIX/Linux, McIntosh, MS-DOS/Windows). C originalmente foi uma linguagem construda para desenvolver programas de sistemas, ou seja : Sistemas operacionais Assemblers Editores Interpretadores Compiladores Gerenciadores de rede, etc. Por exemplo, o sistema operacional UNIX escrito em C, estando esta linguagem e este sistema operacional intimamente ligados inclusive sob o ponto de vista de filosofia de trabalho e construo de programas. O que faz esta linguagem aplicvel a estas funes que C permite um nvel de manipulao de dados, s conseguido anteriormente pelo uso de programas em Assembler. Estes ltimos so de escrita difcil e tediosos de escrever alm de ter depurao geralmente problemtica. Um fator que pode levar a adoo do Assembler a velocidade mas, nem sempre este o fator principal e mesmo neste ponto C no faz feio. C, permite o uso de recursos de "baixo nvel" numa linguagem de "alto nvel" e boa velocidade de execuo. Por isso, muitas vezes C chamada de linguagem de "mdio nvel". Como se pode perceber, no h nenhuma conotao ruim nos termos baixo ou mdio. Para dar exemplos, Linguagens deBaixo Nvel so os Assembly de cada mquina. Linguagens de Alto Nvel so FORTRAN, ALGOL, BASIC, PASCAL, etc. As Linguagens de Mdio Nvel mais conhecidas so C e FORTH, esta ltima tendo uma estrutura mais flexvel e poderosa do que C mas geralmente de execuo mais lenta, por ser (nas suas verses mais comuns) parcialmente interpretada, alm de ser de programao confusa. claro que no demorou muito tempo para que uma linguagem com este poder fosse se difundindo fora da rea de programas de sistemas. Hoje, boa parte das aplicaes de alto desempenho so cada vez mais escritas em C, sejam elas comerciais ou cientficas. Voc poder escrever em C qualquer programa que execute qualquer tarefa escrita originalmente em linguagens como FORTRAN, Pascal, COBOL ou BASIC e coisas que estas no fazem ou que, para serem feitas, exigem conhecimento de truques ou macetes confusos ou especficos do produto com o qual voc estiver trabalhando. Mas nem tudo se resume a maravilhas. Em troca, C exige do programador muita responsabilidade e ateno. Outras linguagens criam barreiras que protegem certas estruturas na memria do computador, mas C no. Portanto se acostume com o lema de C : "O programador inteiramente responsvel pelo que faz." A filosofia de que o programador no precisa ser tutelado.
Departamento de Cincia da Computao-UFF

Nmeros de pgina
C no menospreza a sua inteligncia, mas tambm no se responsabiliza por descuidos. No se esquea que um algoritmo mal feito vai funcionar mal em qualquer linguagem. Mas no se assuste. H uma brincadeira entre os no usurios de C e mesmo os que iniciaram o seu estudo de maneira descuidada. Estes dizem: "Voc gosta de viver perigosamente ? Programe em C!" Se fosse to "perigosa" assim, C no seria to usada. Iniciaremos a discuso da linguagem C seguindo como referncia o que chamado "C padro", ou seja, a definio mnima contida no livro "A Linguagem de Programao C" escrito por Brian Kernigham e Dennis Ritchie em sua primeira edio. Haver ainda informaes sobre uma complementao definida pelo American National Standards Institute (ANSI). A esta chamaremos de "Padro ANSI". Sobre o pargrafo anterior bom chamar a ateno que cada fabricante de software que produz para uma mquina especfica, tende a incluir extenes da linguagem fora dos padres acima. Nem sempre possvel ignorar estas modificaes do padro. o caso dos famigerados modos de memria em mquinas microsoft/Intel. No entanto, por uma questo de produtividade, flexibilidade e transportabilidade devemos nos ater da melhor maneira possvel aos padres no dependentes de fabricante. Esta preocupao em seguir padres facilita a construo de produtos muito mais do que limita a produo. Seguir uma padronizao considerada uma atitude louvvel sendo que no caso do Unix todo produto que carrega os dizeres POSIX COMPLIANT considerado de maneira mais respeitosa. Como ponto ainda, temos verses Integradas e no-integradas, ou seja, verses onde temos um sistema que integra os passos de edio, compilao e depurao e sistemas em que cada uma destas fases so executadas separadamente. Os sistemas integrados so mais confortveis mais o sistema de integrao ocupa espao em memria e ainda um elemento estranho ao processamento do programa (podemos ter reaes diferentes do programa se ele roda dentro ou fora do integrador). Muitas vezes (principalmente em programas crticos) mais seguro botar a preguia de lado de sofrer o imenso desconforto de apertar uns botes a mais. O texto, no essencial, no se prende a um compilador ou a uma mquina especfica. Quanto a referncias a C++, damos o livro de definio da linguagem de Stroutrop e ainda chamamos a ateno que as verses mais recentes da verso de C do Projeto GNU j so efetivamente compiladores de C++. Produtores tradicionais de compiladores C tambm esto criando produtos j admitindo C++. Quero lembrar que este texto, pressupe um conhecimento prvio de tcnicas de programao em uma linguagem como FORTRAN, Pascal, Assemblers, etc. No recomendvel a adoo de C como primeira linguagem de programao. Os programadores que tiverem alguma experincia com assemblers, provavelmente, absolvero com mais facilidade alguns conceitos incomuns contidos em C e no em outras linguagens. Como este curso presupe to variadas origens, alguns pontos podem aparentar ser redundantes, bvios ou (para alguns) um tanto quanto obscuros. Antes de mais nada cuidado com as aparncias. O bvio pode no ser to bvio e o obscuro pode ter origem no sotaque que voc carrega da linguagem de programao que voc est mais acostumado. Quanto da forma do texto, fazemos inicialmente uma descrio da linguagem de uma maneira geral e apresentamos exemplos simples que (ao se apresentar novas estruturas) vamos modificando sistematicamente fazendo uma apresentao informal da linguagem. A medida que evoluimos, introduzimos mais conceitos novos e sofisticamos os exemplos at englobar a linguagem como um todo. De certa forma, esta estratgia tem por inspirao a origem de C. UNIX foi criado com a filosofia de construo que devemos ter ferramentas de software simples, dedicadas a determinada operao e confivel, algo como: Menos mais . Devemos ento adotar esta como filosofia de programao.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
C, A LINGUAGEM IDENTIFICADORES Um ponto importante em C so as especificaes dos identificadores e algumas convenes na criao dos mesmos. Os identificadores em C, podem comear por qualquer letra maiscula ou minscula ou o sinal de sublinhar (underscore _). No meio de um nome, poderam haver letras, nmeros ou o sinal _ (sublinhado ou underscore) e nada mais. O nmero de caracteres que difere um identificador de outro, pode variar de compilador para compilador sendo que na definio de C padro K&R so levadas em considerao 8 caracteres e em C ANSI 32 caracteres. Exemplos corretos e distintos (nem por isto todos so recomendveis!) : A a exemplo_de_identificador OUTRO_exemplo_DE_identificador MAIS_1_ExEmPlO _e_outro Exemplos incorretos : 1_exemplo 1a um-exemplo-errado +um_exemplo_errado esta_errado_tambem! (comea por nmero) (idem) (hfem entre palavras) (carater invlido "+") (carter invlido "!")

Chamamos a ateno para convenes adotadas para os identificadores : "Variveis tem seus identificadores em letras minsculas" "Constantes tem seus identificadores em maisculas" "Tipos criados pelo usurio devem ter os seus identificadores comeando por uma maiscula". Recomenda-se, por questes de padronizao, que voc adote esta conveno, alm do que ela facilita a visualizao de "quem quem" dentro do programa. Votaremos a falar dentro em pouco de constantes e mais tarde de declaraes de tipo. Variaes podem ser feitas sempre que facilite o entendimento. Mas lembre-se: estas so apenas recomendaes. Caso viol-las facilite a compreenso de um programa, um caso a se pensar. Lembre-se novamente que C distingue entre letras maisculas e minsculas, portanto, os identificadores abaixo so diferentes entre si. SOMA soma Soma Fazemos ainda a recomendao que voc ao definir os seus identificadores o faa da maneira mais bvia possvel. Se uma varivel acumula a soma de um oramento, denomine-a soma_do_orcamento e no s, xpto ou algo que o valha. Fica muito mais fcil de entender e uma maior garantia de que, meses depois voc (ou outra pessoa) se localize com mais facilidade (o prprio nome da varivel passa a fazer parte da documentao). Como veremos, C uma linguagem de escrita compacta, o que facilita a sua digitao mas pode levar a escrevermos programas incompreensveis se no formos bvios sempre que pudermos. Hoje o que mais vale no o programador cheio de truques e malandragens de programao mas aquele que prefere construir programas claros e de fcil visualizao. Isto facilita a manuteno do programa e permite uma produo mais constante, ou seja, se voc programa por hobby ou em desenvolvimento de projetos cientficos, a atitude de ser simples e claro lhe trar menos trabalho nos seus projetos. Caso voc seja um profissional de produo de software, isto significar menor dispndio de tempo e, consequentemente, maior eficincia.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Palavras-chave C ser discutido em duas partes: as palavras-chave e as funes. As primeiras so palavras reservadas, ou seja, identificadores que no podem ser usados pelo usurio. So identificadores que so ou constituem partes de comandos ou declaraes da linguagem. Quanto s funes, nada podemos fazer de prtico em C sem elas. Apesar disto, inicialmente s falaremos das palavras-chave. As palavras-chave so bem poucas, se compararmos com o nmero de comandos de linguagens como PASCAL e abaixo temos a relao de todas elas. PALAVRAS-CHAVE auto break case char const@ continue default do @ extenses ANSI As palavras marcadas com @ so conhecidas como extenses ANSI e as demais so as palavras padro K&R. Variando de compilador para compilador, podemos ter algumas palavras extras. Ateno: O fato de usar palavras-chave fora do padro, poder criar problemas caso voc queira que um determinado programa construido por voc possa ser levado de um compilador para outro ou mesmo para outra mquina. A esta caracterstica de podermos migrar um programa com um mnimo de modificaes chamada de Portabilidade. Se voc pretende fazer com que seu programa seja o mais portvel possvel, evite o uso de palavras-chave diferentes das acima. Alm disto, alguns compiladores mais antigos no aceitam as extenses ANSI. Um bom conhecimento da documentao de seu compilador vital. Toda palavra-chave em C se escreve em minsculas, como se pode notar, mas o importante chamar novamente a ateno que C distingue duas palavras iguais mas escritas uma em maisculas e outra em minsculas, o que a maioria das linguagens no faz. Isto ser fonte de erros no incio dos seus trabalhos com C. Outra fonte de erros surgir principalmente para os de imaginao mais frtil e que gostam de fazer paralelos entre linguagens, confiando em aparentes semelhanas. A maior parte das vezes, as dificuldades de entender C tem origem exatamente devido a suposies do tipo: "Este comando "igual" a daquela outra linguagem e, portanto, deve funcionar de maneira parecida." Toda vez que voc pensar coisas deste tipo, REPRIMA! Muitas semelhanas so apenas aparentes. Portanto, tenha em mente o lema: C se parece apenas com C. double else enum@ extern float for goto if int long register return short signed@ sizeof static struct switch typedef union unsigned void@ volatile@ while

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Tipos de Variveis Passemos a mais um ponto fundamental: os tipos de variveis. Os tipos disponveis ao programador com a gama de valores correspondentes esto relacionadas abaixo : Varivel char int short int unsigned int long int Varivel float Double Tamanho(Bytes) 1 2 1 2 4 Tamanho(Bytes) 4 8 Gama [0,255] [-32768,32767] [-128,127] [0,65535] [-2147483648,2147483647] Gama 10 Algarismos sig. 6 12

OBS: Os valores dados para os tipos float e double, podero tomar valores negativos nos respectivos domnios. comum que se dispense a palavra int depois de short, unsigned e long. Observe que aqui temos vrias palavras-chave. Temos alm destas algumas que atuam sobre o tipo da varivel e elas so listadas abaixo e que chamaremos aqui de modificadores : Modificadores Aqui chamamos de modificadores palavras reservadas que modificam certas caractersticas de tipos de varivel definidas anteriormente auto extern register @ extenses ANSI Apesar de termos vrios tipos de variveis, C permite muitas liberdades no trabalho destas. Se a operao tem significado ou no, isto deixado a voc, o programador. Chamamos a ateno que o tipo inteiro bsico em C geralmente o de dois bytes, embora isto possa variar (No se esquea sempre verifique a documentao de seu compilador). Ainda temos a dizer que na definio padro de C, todas as operaes de ponto flutuante so executadas em double. bom frizar isto, j que em programas que usem pesadamente deste tipo de operao, o processamento do mesmo ser mais lento do que se fosse feito em linguagens que trabalhem com variveis de ponto flutuante de quatro bytes, por razes bvias. No entanto, muitos compiladores permitem que voc use menor preciso como uma opo o que acelerar a execuo embora com detrimento da preciso. signed@ static volatile@

Departamento de Cincia da Computao-UFF

Nmeros de pgina
OPERADORES Temos, alm das palavras-chave, a definio de operadores de C. E eles so abundantes: Operador de atribuio '=' Ex.: a = b varivel a atribuido o valor de b. No caso de atribuio de caracteres, o caracter dever estar entre aspas simples. Ex.: a = 'b'. Operador vrgula , Este operador serve para separar vrias operaes a serem executadas dentro de parnteses. Se a sequncia de operaes atribuida a uma varivel, o valor atribuido a varivel ser a da ltima expresso entre parnteses. Ex : Seja a = 5 e b = 7, ento a expresso c = (a-b, b * 2) far que c tome o valor 14. comum que encontremos este operador colocado separando operaes dentro da declarao for, que veremos abaixo. Operadores aritmticos Suporemos aqui que b e c so inteiros e respectivamente iguais a 7 e 5. * multiplicao / diviso % modulo + adio - subtrao Ex. : a = b * c (a = 35) Ex. : a = b / c (a = 1) Ex. : a = b % c (a = 2) Ex. : a = b + c (a = 12) Ex. : a = b - c (a = 2)

Como a maioria das linguagens de programao, C tem vrios operadores sobrecarregados, ou seja, um mesmo operador atua sobre vrios tipos. Os operadores de multiplicao, diviso, soma e subtrao atuam indiscriminadamente sobre double, float, int e suas modificaes. Note que o operador de mdulo tambm sobrecarregado pois vale para qualquer inteiro. Observe ainda que no existe em C um operador de potenciao. Isto no um fato exclusivo desta linguagem. Por exemplo, Pascal tem esta mesma ausncia. Tambm bom observar (embora seja bvio) que o operador % s tem sentido para operandos inteiros. Operadores de incremento e de decremento ++ incremento -- decremento Ex. : a = b++ Ex. : a = b-ou ou a = ++b (resultados diferentes!) a = --b (resultados diferentes!)

Para clarear o que ocorre, trabalhemos inicialmente somente com o operador de incremento. Ao colocarmos o operador antes do identificador da varivel, primeiro a varivel ser incrementada e ento este valor ser usado em outras operaes na linha onde se encontra. Caso o operador estiver depois do identificador, ento este valor ser usado e s depois haver o incremento. Vamos exemplificar o funcionamento destes operadores com o caso abaixo onde temos uma varivel com um determinado valor e esta, com o uso dos operadores acima, atribuida a outra varivel. O valor da varivel dado ao lado de cada caso. Para b = 5 a = b++ a = ++b a = b-a = --b (a = 5) (a = 6) (a = 5) (a = 4)

Obs: Chamemos a ateno sobre uma questo de nomenclatura. Vamos tomar como exemplo o operador de incremento. Se o operador colocado antes da varivel (por exemplo, ++b) dizemos estar fazendo um princremento. Se o operador est depois da varivel (b++) dizemos ento que fazemos um ps-incremento. Vale o anlogo para o operador de decremento.
Departamento de Cincia da Computao-UFF

Nmeros de pgina
Operadores de endereo Estes aqui sero meramente apresentados. A maneira de utilizar cada um ser vista em detalhe no decorrer do texto. & "o endereo de" . Ex. : a = &b. a recebe o endereo da varivel b. Vide ponteiros. * "no endereo de". Ex. : c = *d. c recebe o valor da varivel de endereo dado por d. Vide ponteiros. [ ] "no endereo de ... mais um ndice" . Ex. : c = a[1]. c recebe o elemento de a de ndice 1. Vide vetores. ."elemento da estrutura" (operador ponto). Vide estruturas. -> "elemento da estrutura apontada por" (operador seta). Vide estruturas. Operadores de Bit Estes operadores trabalham a nvel de bits. Tais operadores so necessrios a uma linguagem que pretende substituir o assembler em projetos de software. Para ilustrar, suporemos aqui que a e b so inteiros e respectivamente iguais a 5 e 6, ou seja, 00000101 e 00000110 em base binria). << desloca para a esquerda. Ex. : c = a<<2 ( 20) (00010100) >> desloca para a direita. Ex. : c = a>>1 ( 2) (00000010) & E lgico Ex. : c = a & b ( 4) (00000100) | OU lgico Ex. : c = a | b (7) (00000111) ^ OU Exclusivo Ex. : c = a ^ b (c = 3) (00000011) ~ NAO Ex.: c = ~b (c = -32761) (11111001) Os operadores de deslocamento direita e a esquerda, deslocam os bits dentro das variveis nestas direes. Estas operaes, no caso de atuar sobre inteiros, equivalente a multiplicar (deslocamento esquerda) ou a dividir (deslocamento direita) por uma potncia de dois. OBS.: Observe que o operador & aparentemente faz dois servios: um como operador de endereos e outro como E lgico. A diferena est que no caso do operador de endereo, ele atua somente sobre um elemento (operador unrio), enquanto no caso de ser o operador E lgico, ele precisa de dois elementos para obter um resultado (operador binrio). Operadores lgicos Em C no temos variveis do tipo lgico e a definio do verdadeiro lgico dada por qualquer valor diferente de zero. Obviamente o falso lgico o valor zero. Portanto estes operadores trabalharo sobre quaisquer variveis dando uma resposta correspondente a condio examinada. && E lgico || OU lgico ! NAO lgico Ex. : condio1 && condio2. Ex. : condio1 || condio2. Ex. : !condio1.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Operadores relacionais Os operadores abaixo trabalham fazendo comparaes entre dois valores devolvendo como resultado um falso ou um verdadeiro. > "maior que". < "menor que" == "igual a". >= "maior ou igual a". <= "menor ou igual a". != "no igual a". OBS.: I) No confunda o operador == (igual a) com o operador = (de atribuio). II) Lembre-se novamente que o verdadeiro lgico qualquer valor diferente de zero e o falso o zero. No confunda os operadores acima com os operadores de bit. Ex. : a > b Ex. : a < b Ex. : a == b Ex. : a >= b Ex. : a <= b Ex. : a != b

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ABREVIAES Outro ponto interessante de C, que podemos abreviar algumas expresses. Por exemplo, se temos algo do formato <varivel1> = <varivel1> <operador> <expresso> podemos sempre escrever <varivel1> < operador> = <expresso> Exemplos: x += u equivalente a x = x + u x += delta * b equivalente a x = x + delta * b a >>= b equivalente a a = a >> b a &= b equivalente a a = a & b bvio que estas abreviaes podem confundir quem se inicia na linguagem ou tem pouco conhecimento da mesma. Tome os devidos cuidados, ento. PRECEDNCIA DOS OPERADORES Abaixo temos a precedncia de cada operador dentro de cada grupo e no caso geral. OPERADORES ARITMTICOS 1) ++, -2) - (UNRIO) 3) * , /, % 4) +, OPERADORES RELACIONAIS E LGICOS 1) ! 2) > , >=, <, <= 3) ==, != 4) && 5) || OPERADORES DE ENDEREO 1) ., -> 2) *, &

Departamento de Cincia da Computao-UFF

Nmeros de pgina
PRECEDNCIA GERAL 1)( ) Chamada de funo [] Elemento de matriz -> Ponteiro para membro de estrutura . Membro de estrutura 2) ! ~ ++ -Unrio (tipo) Moldagem * "no endereo de" & "o endereo de" sizeof 3) * Multiplicao / % 4) + 5) << >> 6) < <= >= > 7) == != 8) & E de bit 9) ^ OU Exclusivo de bit 10) | OU de bit 11) && 12) || 13) ?: 14) = *= /= %= += -= <<= >>= &= ^= |= 15) , Mudana de Precedncia A precedncia poder ser mudada pelo uso de expresses entre parnteses, tendo os parnteses mais internos maior prioridade que os demais. OBS. : Lembre-se: prefervel colocar parnteses sobrando para clarear o que ocorre do que permitir interpretaes dbias.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
CONVERSES DE TIPO E CONVERSES FORADAS (cast, ou moldagem) Agora que j falamos de tipos e operaes, devemos nos preocupar com o uso de operaes com tipos mistos, por exemplo, calcular o produto de uma varivel inteira com uma de ponto flutuante e este resultado ser colocado numa varivel de preciso dupla. H algumas converses que so feitas automaticamente. Em operaes mistas odos os char e short int so convertidos em int. Todos os float so convertidos em double. Sempre a converso entre pares de operandos feita para o tipo do maior operando, ou seja, operando-se um double e um int, temos como resultado um double e se operamos um long e um int, temos um long como resultado. O que queremos dizer como maior o tipo que puder representar o maior nmero. Portanto, operando um inteiro longo e um nmero de ponto flutuante, ambos de 4 bytes de comprimento, teremos a converso do resultado em um nmero de ponto flutuante. No entanto, nem sempre o resultado que temos o esperado. Algumas vezes pode ser interessante forar uma converso de tipo. Podemos fazer isto escrevendo, entre parnteses, o tipo ao qual queremos converter a varivel a esquerda. Este construtor denominadoCast ou ainda Moldagem. Vamos a um exemplo. Suponha que i e j sejam duas variveis inteiras de dois bytes (tipo int) e x uma varivel de ponto flutuante (tipo float). Poderamos escrever o que se segue i = (int)x /j Teramos a diviso de x por j, mas no o ponto flutuante x mas o valor convertido para inteiro. A moldagem pode ser tambm parte da documentao. Mesmo que saibamos que determinadas converses sero feitas automaticamente, sempre bom indicar explicitamente atravs da moldagem as nossas intenes. Isto muitas vezes facilita a compreeno do programa. Ainda devemos tomar cuidado ao forar certas converses. O resultado pode no s ser intil como gerador de dores de cabea, caso seja feita de forma pouco cuidadosa.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
CONSTANTES C tem uma notao especial para constantes de forma a ficar claro o seu tipo. Caso seja atribudo a uma varivel um nmero de ponto flutuante, este ser convertido primeiramente para double, seja a varivel tipo float ou no. Outras vezes mais prtico, por motivo de documentao, escrever uma constante em base hexadecimal do que em base decimal. Para evitar converses automticas desnecessrias (algumas vezes prejudiciais) alm de facilitar a documentao, os criadores desta linguagem adotaram o padro de colocar aps o ltimo algarismo uma letra indicando o tipo de constante. Usa-se F para indicar que a constante tipo float, L para indicar que do tipo long e U para unsigned. No caso dos nmeros hexadecimais, usa-se 0x como prefixo. Pode-se usar tanto letras maisculas quanto minsculas. Podemos ainda fazer combinaes com estes modificadores. Portanto, teramos que 100F 120000L 65000U 13500UL 0X130 o nmero 100 de ponto flutuante o nmero 120000 inteiro longo um nmero inteiro no assinalado um nmero inteiro longo no assinalado o nmero 130 na base hexadecimal

No custa relembrar que se no houver nenhuma declarao que o negue, as constantes so do tipoint. Alm destas h uma srie de constantes pr-definidas (usualmente chamadas Sequncias Escape ou de Fuga) que so caracteres de controle de dispositivos como impressoras, monitores, etc. Abaixo temos estas constantes. \a \b \f \t \v \\ \? \' \o \x Alerta(bell) Retrocesso Alimentao de formulrio Tabulao horizontal Tabulao vertical Barra invertida Interrogao Apstrofo Nmero octal Nmero Hexadecimal

Departamento de Cincia da Computao-UFF

Nmeros de pgina
COMANDOS Um comando uma expresso vlida seguida de um ponto-e-vrgula. Uma expresso vlida por sua vez um conjunto de variveis e operadores colocados de forma coerente. Para dar sentido mais claro a estas palavras vamos a alguns exemplos de comandos vlidos : a = b; ( atribuido varivel a o valor contido em b) a = a + c * d; (a recebe o produto de c por d e mais o prprio valor corrente) ; (Comando nulo) Para comandos no vlidos teremos : a=c a = a ^& b; a = 1z3 * b; (falta o ponto-e-vrgula) (^& no um operador) (varivel invlida)

BLOCOS Um bloco definido como um conjunto de comandos logicamente conectados. Ou seja, eles so trabalhados como se fossem um nico comando. A indicao de um bloco determinada por um abre chave no seu incio e um fecha chave ao seu final, ou seja, { COMANDO 1 COMANDO 2 COMANDO 3 . . . COMANDO n } Pela definio, um bloco pode conter blocos.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
CONTROLE DE FLUXO Temos aqui as formas de controle de fluxo em C. a) if (CONDIO) COMANDO1 else COMANDO2 Se CONDIO for verdadeira (diferente de zero), COMANDO1 ser executada. Caso seja falsa (ou seja, igual a zero), ento COMANDO2 ser executada. Ateno: O else opcional. b) ? : uma simplificao da declarao if. Exemplifiquemos seu uso : var = expr1 ? expr2 : expr3 Se a expresso 1 for verdadeira, ento avaliada a expresso 2, seno avaliada a expresso 3 e o resultado, num caso ou no outro, ser atribudo varivel var. c) switch , default e case Estas declaraes trabalham juntas, sendo que default opcional. switch (EXPRESSO) { case constante1 : COMANDO case constante2 : COMANDO . . . case constante n : COMANDO default : COMANDO } Quando uma opo tomada, a declarao correspondente executada e TODAS as instrues abaixo tambm o sero. Para que tal fato no ocorra, necessrio terminar cada bloco com um break, palavra que veremos mais abaixo. Caso o valor que EXPRESSO tomar no seja igual a nenhuma das constantes aps a palavra case, ento a declarao que estiver associada a palavra default ser executada. Caso ela no for usada, haver interrupo do lao. Ateno: Observe o fato que os blocos de instrues na declarao switch, depois do case, dispensarem o abre e fecha colchetes. Pela prpria natureza da execuo, no h a necessidade desta explicitao. Muitos programadores, por questo de padronizao, colocam abaixo de cada case um par de chaves definindo um bloco mesmo este no sendo necessrio. Como sempre, faa o que voc achar mais claro.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
d) goto uma declarao comum em outras linguagens, mas que deve ser evitada sempre que possvel, pois geralmente dificulta o entendimento do programa. Dificilmente voc encontrar um bom programador usando-a a no ser em ltimo caso. H situaes nas quais esta declarao, ao contrrio do habitual, facilita a compreenso do programa, embora sejam muito raras. Para a sua utilizao, necessrio a indicao de um rtulo que um identificador seguido de : (dois pontos). . . goto ok; . . ok : ....; . Aqui no veremos exemplos com goto. Possivelmente voc ver seu emprego muito raramente se que algum dia voc ver.

OBS.: Embora, a estrutura bsica do if e do switch-case se assemelhe a de outras linguagens, observe bem as diferenas.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
LAOS Como toda linguagem, C tem uma srie de instrues que executam laos controlados. Novamente, tome cuidado com semelhanas.

a) while (EXPRESSO) COMANDO Enquanto a expresso entre parnteses for verdadeira (diferente de zero) COMANDO executado.

b) do COMANDO while (EXPRESSO); Funciona de maneira semelhante ao tem "a". A diferena que aqui a deciso de parada aps a execuo da EXPRESSO.

c) for (EXPRESSO; CONDIO; EXPRESSO) COMANDO Esta declarao provavelmente conhecida por todos, por aparecer em vrias linguagens. comum que a EXPRESSO1 seja uma inicializao de uma varivel, CONDIO a condio de parada e EXPRESSO2 o modificador do valor da varivel mas poderemos ter coisas mais complicadas e interessantes. Este lao equivalente a um lao while com a seguinte estrutura : expressao1; while(condio) { comando expresso2; } Alguns programadores mais radicais recomendam o "esquecimento" para o for. No chegamos a tanto. Uma ressalva importante que CONDIO avaliada no incio do lao, como podemos observar da equivalncia mostrada acima. O esquecimento deste fato pode levar a aes inesperadas. Para os que acham que todo for igual, daremos um exemplo das particularidades deste lao. A expresso abaixo vlida em C for (; ; ) COMANDO e define um lao sem fim. Palavras Auxiliares a) break; um comando auxiliar no controle de laos. Se voc fizer uma construo com vrios laos embutidos, ao encontrar esta palavra chave o programa interrompe o lao no qual se encontra e salta para o imediatamente externo.

b) continue; Ao encontrar este comando, o programa saltar diretamente para a condio do lao, desconsiderando os comandos posteriores a ela. No caso de usado num for, ao encontrar um continue, o controle passar para a declarao 2 dentro dos parnteses do for. S pode ser usado dentro de laos.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Fatos e mitos - Velocidade de processamento Como j foi dito acima, C uma linguagem que gera programas de alta veloci ade de processamento. d Algumas pessoas j usaram C e acham que isto uma balela j que "Na linguagem xxx um programa meu foi executado mais rapidamente que em C." Um fato que devemos desde j chamar a ateno com nfase que C faz todas as suas operaes de ponto flutuante com o tipo double se voc no obrigar que estas sejam feitas com outra preciso. Obviamente isto gera duas consequncias: Maior preciso no clculo e maior tempo de execuo. C uma linguagem que gera programas rpidos, continuo insistindo, mas a nica maneira de se fazer um programa o mais rpido possvel o conhecimento no s da linguagem na qual programamos como tambm da implementao particular do compilador que usamos e o computador no qual estamos trabalhando. Haver situaes em que um programa em C ser mais lento que o mesmo programa em alguma outra linguagem. Isto obviamente no nenhum demrito. Se C ou outra linguagem qualquer solucionasse todos os problemas de programao, claro que no haveria a profuso de linguagens que existe, cada uma mais adaptada a determinadas tarefas. Devemos ter em mente as vantagens e limitaes de cada linguagem e a adequao dela ao problema que queremos resolver. Para evitar problemas e tomar uma atitude mais realista e madura quanto programar lembre-se da Lei de Murphy[ ] aplicada computao:

Se voc quer fazer uma besteira fcil, mas se quiser fazer uma grande besteira, voc vai precisar de um computador.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ESTRUTURAS DE PROGRAMAS EM C: A FUNO main() Um programa em C uma chamada de funo, no caso uma funo especial de nome main(), a qual chamaremos de funo principal. Ela a nica funo obrigatria de existir num programa em C e inicialmente trabalharemos apenas com ela. O mais simples programa em C, contendo apenas a funo main() (mas que no funcionar!), tem a forma que se segue main() { DECLARAO DAS VARIVEIS COMANDO1 COMANDO2 . . COMANDOn } Todas as variveis contidas no programa devero ser declaradas. Como toda linguagem de programao, em C voc poder tambm inserir comentrios. Isto feito colocando em qualquer coluna uma barra seguida de um asterisco depois escrevendo seu comentrio e ento, ao acaba-lo, teclando outro asterisco seguido de uma barra, ou seja, /* comentario */ Obs: No permitido o aninhamento de comentrios no ANSI C mas outras verses podero permitir. Em alguns momentos o aninhamento de comentrios cmoda mas muitas vezes (dependendo da forma de implementao) pode ser fonte de erros bobos. Escrevamos o nosso primeiro programa que: a)Some dois nmeros inteiros e coloque o valor da soma num outro nmero inteiro. b)Se a soma for maior que 9, faa a varivel que contm a soma, ser igual a 9. main() { /* Primeiro programa */ int a, b, c; a = 5; b = 3; c = a + b; if ( c > 9 ) { c = 9; } } Temos acima a declarao das variveis a, b e c como inteiras seguida da atribuio de valores e ento sua soma sendo atribuida a varivel c. Logo aps temos o teste do valor da soma. Como a = 5 e b = 3, temos que o resultado final ser c = 8. Portanto, depois do teste, c continuar com o valor 8. Para marcar bem a diferena entre o operador de atribuio = em C e em outras linguagens, vamos reescrever este programa.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main() { /* Primeiro programa, segunda verso */ int a = 5, b = 3, c; if ( (c = a + b) > 9 ) c = 9; } O operador de atribuio usado para inicializar as variveis no momento de sua definio. Dentro dos parnteses do if, h a atribuio do valor da soma de a com b varivel c. S aps feito isto que haver a comparao e avaliao da condio. Esta maneira de usar o operador de atribuio pode parecer estranha inicialmente mas, indiscutivelmente, tem o seu charme. Observe ainda que como s temos um comando para o if , no foi criado um bloco. Uma nova verso pode ser feita, agora com o uso do operador ? : main() { /* primeiro programa, terceira verso */ int a = 5, b = 3, c; c = (c = a + b) > 9 ? 9 : c; } O resultado idntico. Aqui usamos o operador ? : no lugar do if. Este operador trabalha, no programa acima, da seguinte maneira: Inicialmente as variveis a e b so somadas e depois o resultado atribuido varivel c. Verifica-se se o resultado maior que 9. Se for, a c atribuido o valor 9. Se no, a c atribuido o prprio c, que j contm a soma de a e b. Observe os parnteses obrigando que a atribuio da soma seja feita antes do teste. Faamos mais um programa. Queremos somar um nmero de ponto flutuante de preciso simples a ele mesmo, 10 vezes enquanto ele for menor que 2000. Ao final devemos ter uma varivel inteira que contenha quantas vezes o processo de soma foi feito. main() { /* Segundo programa */ int n; float x; x = 235.; for (n = 1; n <= 10; n++) { x = x + x; if ( x >= 2000.0) break; } } Observe o uso do operador de incremento na sua verso de ps-incremento dentro do for. Lembre-se: queremos dizer com esta expresso que o valor da varivel ser usada e s ento haver o incremento. Note ainda o uso do break para interromper o lao do for. Como j foi descrito, no momento que a palavra-chave break for encontrada, o lao, dentro do qual est o break, ser interrompido. Vamos reescrever o programa.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main() { /* Segundo programa, segunda verso */ int n; float x; x = 235.; n = 0; do { x += x; n++; } while ( (x < 2000.) && (n < 10)); } Aqui temos o mesmo programa s que agora a saida feita atravs de uma condio composta. So testadas as condies de parada de tal forma que se pelo menos uma das duas for falsa haver a interrupo da execuo. Lembre-se que o while continua sendo executado enquanto a expresso dentro dos parnteses for verdadeira. Temos ainda o uso do operador de ps-incremento usado isoladamente e tambm a utilizao da notao abreviada para o clculo de x. Observe bem as diferenas entre as duas verses no esquecendo que so totalmente equivalentes, embora o for ter uma equivalncia com o while e no com o do-while. Vamos a mais um programa. Temos uma varivel do tipo caracter (char) e vamos provocar as seguintes reaes : a) Se for um 'a' ou um 'b', uma varivel inteira tomar o valor 1; b) Se for um 'c', valor 2; c) Se for um 'd', 3; d) Se for um 'e', o valor 4. main() { /* Terceiro programa */ int numero; char letra; if ((letra == 'a') || (letra == 'b')) { numero = 1; } else { if (letra == 'c') { numero = 2; } else { if (letra == 'd') { numero = 3; } else { if (letra == 'e') { numero = 4; } } } } }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Observe mais uma condio composta no primeiro if. Neste caso basta uma das duas expresses serem verdadeiras para que a varivel numero tome o valor 1. Embora interessante como exemplo de uso de if, o programa no um bom exemplo de programao. Reescrevendo este programa (como est se tornando hbito) temos: main() { int numero; char letra; switch (letra) { case 'a' : numero = 1; break; case 'b' : numero = 1; break; case 'c' : numero = 2; break; case 'd' : numero = 3; break; case 'e' : numero = 4; } } Aqui substituimos uma sequncia de if's por uma declarao do tipo switch-case. A funo do break, chamamos novamente a ateno, interromper a execuo do bloco do switch. Nada nos impede de reescrevermos o programa como o que se segue: main() { /* Terceiro programa, terceira vercao */ int numero; char letra; switch (letra) { case 'a' : case 'b' : numero = 1; break; case 'c' : numero = 2; break; case 'd' : numero = 3; break; case 'e' : numero = 4; } } Como o valor tomado pela varivel numero igual para os casos do caracter ser a ou b, deixamos sem nenhuma declarao aps os :. No fluxo do programa, caso o caracter seja um a, automaticamente teremos como execuo a atribuio do valor 1 a varivel numero seguida da execuo do break, que interromper o switch-case. A maneira prolixa da primeira verso (com if) foi para dramatizar as vantagens do uso do swith-case.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Vamos a mais um exemplo. Agora descreveremos um problema a partir de sequncia de instrues, ou seja, de forma algortmica. No caso ser o algoritmo de Euclides para calcular o MDC, ou seja, o Mximo Divisor Comum (quem diria, voltamos escolinha!). O algoritmo dado a seguir : i) Sejam dois nmeros inteiros e positivos m e n dos quais queremos achar o mdc. Seja ainda uma varivel r. ii) Faa r tomar o valor do resto da diviso de m por n, fazendo ento m tomar o valor de n e este ltimo o valor de r. iii) Se r no for nulo, volte a ii). Caso r for nulo, m ter o valor do mdc. O programa que faz tais operaes afim de calcularmos o MDC dado abaixo : main() { /* programa que calcula o mdc de dois numeros m e n */ int m = 120, n = 9, r; do { r = m % n; m = n; n = r; } while(r != 0); } Usamos aqui mais um operador aritmtico, o %, que d o resto da diviso de dois nmeros. No while poderiamos usar no lugar da expresso r != 0 algo como !r. Funcionaria do mesmo jeito com uma aparente vantagem de ser mais compacto sendo, no entanto, mais enigmtico. Aqui se apostou em ser claro e, sempre que possvel, esta deve ser uma ttica de programao.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Indentao Repare que em todos os programas j exibidos, cada palavra reservada, cada varivel, no escrita a esmo mas obedece uma regra de posicionamento em cada linha. Esta tcnica geralmente chamada de indentao e usada em todas linguagens ditas estruturadas. A idia por trs de tal comportamento no s fazer programas bonitinhos mas sim, fazer programas mais legveis. C j uma linguagem muito concisa e se comecamos a teclar cada linha de qualquer jeito, teremos problemas de legibilidade que podero dificultar a depurao e manuteno de programas e sistemas. Notem ainda que so colocados espaos entre variveis e operadores e espao entre as linhas em muitas situaes. O estilo usado aqui simplesmente um estilo, existindo maneiras diferentes de organizar as linhas de um programa. A linguagem C permite esta liberdade no estilo da escrita de tal forma que podemos modificar ligeiramente a forma de escrever um programa da maneira que mais nos agradar. Os programas esto escritos neste texto de forma relativamente comum no sendo, obviamente, A Maneira Correta mas apenas (repito) uma maneira de se escrever programas em C. comum se usar de 2 5 espaos para marcar a indentao mas isto pode variar de acordo com seu prprio estilo. Por exemplo, podemos escrever uma declarao if como if (a > b) { c = a + b; d = a * c; { else { c = a + b; d = a * c; } if (a > b) { c = a + b; d = a * c;

if ( a > b) { c = a + b; d = a * c; .} else { c = a - b; d = a + c * b; }

}
else { c = a + b; d = a * c; }

ou outra maneira qualquer. No entanto, seja l qual estilo voc adotar, tenha em mente melhorar a legibilidade do programa. Para perceber o que queremos dizer, vamos a um caso extremo: o programa do MDC poderia ser escrito como abaixo main(){int m=120,n=9,r;do{r=m%n;m=n;n=r;}while(r!=0);} As vantagens de escrever assim so ainda desconhecidas mas alguns insistem.....

Departamento de Cincia da Computao-UFF

Nmeros de pgina
FUNES Forma Clssica At o momento, sobre funes s foi dito que existiam e que main() era uma funo. Agora falaremos de funes de forma mais genrica. Em C as funes podem ser escritas da seguinte forma, (chamada forma clssica) TIPO nome_da_funo(parmetro 1,parmetro 2,...,parmetro n) DECLARAO DE PARMETROS { DECLARAO DE VARIVEIS COMANDO1 COMANDO2 . . COMANDOn return(parmetro de saida); } onde TIPO diz respeito ao tipo de varivel que ser devolvida pela funo. Observe mais uma palavra chave, return. Podemos encontrar esta palavra chave sendo usada de duas maneiras: com o parmetro de saida entre parnteses e com o mesmo parmetro logo aps o return. Esta palavra optativa, no havendo necessidade de aparecer em nenhum ponto do bloco principal da funo. Tambm podemos ter vrios return dentro de uma funo embora isto no seja recomendvel. A semelhana da definio de uma funo com um programa em C por uma razo bvia: o programa principal de C uma funo, como j foi dito. Dependendo da forma de definio de funo, voc pode colocar a declarao de funo antes ou depois da funo main(). Aqui colocaremos as funes antes, de forma que um programa em C pode ter a seguinte estrutura (mas ainda no funcionar!): TIPO nome_da_funo(parmetro 1,parmetro 2,...,parmetro n) DECLARAO DE PARMETROS { DECLARAO DE VARIVEIS COMANDOS } main() { DECLARAO DAS VARIVEIS COMANDOS } bom chamar a ateno para que as variveis declaradas dentro das funes so "invisveis" para o programa principal ou para as outras funes (daqui a pouco falaremos mais sobre esta "invisibilidade"). As funes sempre devolvem algum valor, mesmo que no haja um return. Se este valor tem significado ou no, quem decide o programador. Alm disto, no h obrigatoriedade de usarmos o valor retornado. Quanto aos argumentos, eles so passados por valor e no por referncia. Isto significa que os valores dados atravs de variveis do programa principal no sero modificados no programa principal mesmo que voc as manipule dentro da funo. Se tiver como parmetros, por exemplo, x e y que valem respectivamente 2 e 3, se houver uma multiplicao entre um e outro atribuindo a x o resultado dentro da funo, veremos que o valor de x continuar igual a 2 no programa principal. Isto aparentemente limita o uso mas, como veremos, este fato no se trata de uma limitao mas apenas uma maneira de termos, explicitamente, conhecimento do alcance das operaes que sero feitas atravs das funes.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Como exemplo faremos do primeiro programa uma funo a qual chamaremos tetof(). int tetof(a,b,teto) int a,b,teto; { int c; if ( (c = a + b) > teto) c = teto; return(c); } main () { int a = 5, b = 3, c, tetof(); /* Primeiro programa, quarta vercao */ c = tetof(a, b, 9); } Novamente o funcionamento igual. Outro detalhe a saber que se nada for indicado, ficar pressuposto que a funo devolver um inteiro. A razo disto j sabemos: C trabalha preferencialmente com variveis do tipo inteiro. Portanto se nada for especificado quanto ao tipo de valor que uma funo devolver, se considerar que o resultado ser um inteiro. Se a funo devolver um valor no compatvel com inteiro, voc precisa avisar a funo que a chama, o tipo de resultado que ela ir devolver. Isto feito junto ao definir a funo e com a declara o de variveis. Vamos dizer que uma funo do tipo char e seu nome funcao1(). Ento, na funo que a chama deve haver junto, a declarao de variveis, o que se segue: char funcao1(); Observe que no necessrio colocar os parmetros da funo mas necessrio colocar os parnteses para que o compilador saiba que uma definio do tipo de valor devolvido pela funo e no a declarao de uma varivel. Como mais um exemplo, daremos uma funo que calcula o fatorial de um nmero. Lembre-se que o fatorial de um nmero inteiro positivo no nulo o produto dele por todos os nmeros positivos no nulos menores que ele.

int fatorial(n) int n; { int i, fat; for (i = 1; i <= n; i++) { fat = fat * i; } return(fat); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main() { int fatorial(), n = 5, f; f = fatorial(n); } Repare que na lista de declarao de variveis encontramos fatorial() colocado como outra varivel qualquer. Mas lembre-se, o que queremos dizer que o valor retornado inteiro. Chamemos a ateno para alguns aspectos sobre funes em C. Ao contrrio de outras linguagens (Pascal, por exemplo) C no permite aninhamento de funes, ou seja, no admite que tenhamos funes declaradas dentro de funes. Ainda temos que todas as funes em C so visveis umas pelas outras. Tal propriedade, no jargo habitual, se traduz como: As funes em C so globais.

EXERCCIO I) O programa acima s calcula corretamente fatoriais at o de 7 (sete). Porque ?

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VARIVEIS GLOBAIS, LOCAIS E AUTOMTICAS J foi dito que as variveis declaradas dentro de uma funo so "invisveis" para outras funes. Algumas vezes queremos que uma determinada varivel "exista" no s para uma funo ou para o programa principal, mas para todas as funes constituintes do programa, ou seja, seja de conhecimento GLOBAL. Partindo da nossa definio imperfeita de programa em C (que ainda no funcionar...), apresentamos como definir tais variveis: DECLARAO DE VARIVEIS GLOBAIS TIPO nome_da_funo(PARMETRO 1,PARMETRO 2,...,PARMETRO n) DECLARAO DE PARMETROS { DECLARAO DE VARIVEIS DECLARAES } main() { DECLARAO DAS VARIVEIS DECLARAES } Aqui devemos dar uma parada para dar nomes aos bois. As variveis declaradas fora das funes (incluindo a main()) so chamadas GLOBAIS e as declaradas dentro das chaves so chamadas de LOCAIS, DINMICAS ou AUTOMTICAS. Usaremos aqui o termo LOCAIS. No h uma palavra reservada para declarar variveis como globais mas existe uma para declarar como automtica: ela auto. Como todas as variveis em C so criadas como automticas, raramente (para no dizer nunca) voc ver esta palavra ( uto) a em uso. Outra coisa a ser dita que as variveis globais ocupam espao na memria o tempo todo enquanto as variveis locais s ocupam espao enquanto a execuo do programa estiver dentro dos blocos nos quais estas variveis esto declaradas. Quando a execuo sai destes blocos, as variveis locais "evaporam", poupando memria. Alm disto, sempre boa poltica usar o mnimo de variveis globais, j que estas so ativas para toda funo dentro de um programa podendo, assim, gerar reaes inesperadas por ter um trecho de programa alterando uma varivel global que em outro trecho era esperada com outro valor. Uso de variveis globais de forma indiscriminada leva dificuldades no reaproveitamento de cdigo e dificuldades no acompanhamento do processamento de um programa j construido. Os usurios de FORTRAN so particularmente tentados usar variveis globais a todo isntante. Se for o seu caso lembre-se que aqui voc est programando em C, pensando em C e que FORTRAN outro departamento.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
PREPROCESSADOR: CABEALHOS E ARQUIVOS DE INCLUSO Imagine que voc criasse um conjunto de funes que trabalhasse com a tela do computador, outro conjunto de funes que contivesse todas as funes de entrada e sada de dados e outro conjunto e outro e mais outro. Se voc um programador organizado, talvez achasse interessante que pudesse separar estas funes em colees de funes e invocar apenas as colees que fossem necessrias. Em C podemos fazer isto de maneira simples: Basta criar arquivos contendo as funes, constantes e definies relativas s mesmas. Assim, um programa em C poder ser constitudo de uma coleo de vrios arquivos. No entanto, tais arquivos podem depender de definies em comum. Como ento fazer com que todos os arquivos compartilhem estas definies? O que temos um tipo especial de arquivo denominado Arquivo de Cabealho e justamente nele que guardamos tais definies. Quanto ao nome deste arquivo, existe a conveno de usar a extenso .h para marcar o tipo deste arquivo. Se voc tem uma vasta biblioteca de funes para os mais variados fins (teclado, grficos, gerenciar arquivos em disco, etc.) poder invocar apenas as que necessitar. Como que eu invoco tais colees uma coisa ainda no dita. Para fazer esta invocao, usaremos uma declarao do chamado preprocessador, que mais uma parte integrante da linguagem C. Toda declarao do preprocessador indicada por comear pelo caracter # (cardinal ou velha). A que veremos aqui a declarao #include que se responsabilizar por carregar o cabealho invocado, tambm chamado arquivo de incluso. A forma geral deste comando o que se segue #include "nome_do_cabecalho.h" mas poder ser encontrada tambm nesta forma #include <nome_do_cabecalho.h> A diferena entre um e outro que o primeiro (entre aspas duplas) o arquivo de incluso se encontra no diretrio de trabalho corrente, enquanto o segundo (entre < e >) se encontra no diretrio padro onde se localizam os arquivos de incluso. Obs.: I) Um aspecto que devemos chamar a ateno que dentro de um arquivo de cabealho podemos ter outros #include. O nmero de aninhamentos destas declaraes dependente de cada compilador. II) Alguns compiladores automaticamente incluem alguns cabealhos automaticamente. Antes de ser uma comodidade isto um problema. Corremos o risco de ter um programa compilando e funcionando numa mquina e no funcionando em outra. Para evitar vcios e (com isto) problemas futuros, seja explcito sempre e configure seu compilador para no aceitar bibliotecas default. Com a incluso do preprocessador, um programa em C pode ter a seguinte forma (que, finalmente, funcionar!):

Departamento de Cincia da Computao-UFF

Nmeros de pgina
COMANDOS DO PREPROCESSADOR DECLARAO DAS VARIVEIS GLOBAIS FUNO1(PARMETROS) DECLARAO DE PARMETROS { DECLARAO DAS VARIVEIS LOCAIS CORPO DA FUNO } FUNO2(PARMETROS) . . main() { DECLARAO DE VARIVEIS LOCAIS COMANDOS } A estrutura acima muito comum de se encontrar, embora possamos colocar comandos do preprocessador no meio de um programa. Falaremos detalhadamente sobre o preprocessador mais adiante. OBS.: Uma pergunta que deve estar surgindo na cabea do leitor mais atento : Se toda funo retorna um valor a funo main() devolve o que? E para quem? Um pouco mais a frente teremos estas respostas.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ALGUMAS FUNES TEIS (Da stdio.h) A linguagem C no tem comandos para impresso. C deixa isto a cargo de funes e aqui vamos aprender como funcionam algumas para podermos trabalhar um pouco mais satisfeitos. Abaixo temos algumas que se encontram em stdio.h, ou seja, entrada e sada padro (stardard in/out). Primeiro uma que escreve caracteres na tela : printf("CONTROLE", PARMETRO1,PARMETRO2,....,PARMETROn); onde CONTROLE, contm declaraes de formato e caracteres. O formato tem as opes: %c %d %f %e %o %s %u %x caracter simples decimal inteiro ponto flutuante ponto flutuante em notao cientfica) octal cadeia de caracteres decimal inteiro sem sinal hexadecimal

H ainda caracteres especiais (alguns j apresentados quando falamos de constantes), alguns dos quais esto listados abaixo: \b \f \n \r \t \0 \\ retrocesso saltar pgina (ou limpar tela) saltar para prxima linha retorno de carro tabulao horizontal nulo Barra reversa Um exemplo. printf(" i = %d \n x = %f -- y = %f", i,x,y); Se i for igual a 1, x igual a 2.345679 e y igual a 3.141582, a sada ser da forma : i= 1 x = 2.345679 -- y = 3.141582 Observe: o que no for especificao de formato ou caracter especial escrito como foi colocado. Os valores impressos so ajustados para a esquerda. Alm disto, se passamos um nmero de ponto flutuante para esta funo, ele sair com todos os algarismos que o seu tipo permitir. Podemos formatar a sada limitando o nmero de casas decimais num ponto flutuante ou posicionando as variveis de maneira mais conveniente. O formato para ponto flutuante o que se segue [-]m.d onde o sinal - indicaria ajuste a esquerda, m o espao total e d o nmero de casa decimais que devem parecer. O simples fato de querermos formatar a sada, faz com que o ajuste se faa pela direita, da a necessidade do sinal negativo para indicarmos que queremos que o ajuste continue pela esquerda. Isto feito colocando antes do smbolo de porcentagem um nmero que corresponder ao espao que ser reservado para a varivel em questo. No caso de um nmero de ponto flutuante, podemos dar no s o espao para o nmero a ser impresso como tambm o nmero de casas decimais depois do ponto decimal adicionando aps o nmero de espaos reservados para o nmero a ser impresso um ponto seguido do nmero de casas decimais. Abaixo temos vrias opes de formato escritas a partir do exemplo acima no formatado e com suas correspondentes sadas.

a) printf("\n i = %10d x = %10f -- y = %10f", i,x,y); i= 1 x = 2.345679 -- y = 3.141582

Departamento de Cincia da Computao-UFF

Nmeros de pgina
b) printf("\n i = %-10d x = %-10f -- y = %-10f", i,x,y); i=1 x = 2.345679 -- y = 3.141582

c) printf("\n i = %-10.4d x = %-10.4f -- y = %-10.4f", i,x,y); i = 0001 x = 2.3456 -- y = 3.1415 Outras funes teis so especificadas abaixo: a) ch = getch() - Devolve em ch um inteiro sem sinal correspondente ao caracter lido no teclado. (Esta funo se encontra definida em conio.h (complementar in/out)) b) putchar(ch) - Manda um caracter para a tela. c) exit(i) - Interrompe a execuo do programa. Se i = 0, indica uma finalizao normal. Faamos mais um programa. Este dever, inicialmente, escrever na tela um menu com o seguinte contedo : <1> : <2> : <3> : <4> : <Outras> : imprime em formato ASCII imprime em octal imprime em decimal imprime em hexadecimal sai do programa

e aps escrever uma mensagem para entrar com um caracter. Feito isto, leia o caracter, imprima no formato pedido e volte ao menu.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> #include <conio.h> main() { do { int ch, opcao; printf(" <1> : imprime em formato ASCII \n"); printf(" <2> : imprime em octal \n"); printf(" <3> : imprime em decimal \n"); printf(" <4> : imprime em hexadecimal \n"); printf("<Outras> : sai \n"); printf(" Entre com uma opo "); opcao = getch(); printf("\n"); printf(" Entre com um caracter "); ch = getch(); switch (opcao) { case '1' : printf("\n ASCII - %c\n",ch); break; case '2' : printf("\n octal - %o\n",ch); break; case '3' : printf("\n decimal - %d\n",ch); break; case '4' : printf("\n hexadecimal - %x\n",ch); break; default : exit(0); } } while(1); } Observe o uso do while com a expresso de controle permanentemente verdadeira (diferente de zero) e, portanto, criando um lao sem fim. Assim, a nica forma de sada do programa atravs do apertar de qualquer tecla, menos as vlidas, pois isto forcaria a execuo da funo exit() que, como j vimos, interrompe o programa. Faa alguns testes e voc ver que se voc entrar com, por exemplo, uma letra teremos um nmero como resposta, se pedirmos uma opo diferente da de nmero 1. Isto no deve causar estranheza pois os formatos de sada do printf(), nas outras opes, so do tipo numrico. Lembramos que ao apertarmos a tecla correspondente a um caracter ou outra qualquer, no estamos na realidade "mandando" uma letra para o computador mas simplesmente um cdigo. Como os caracteres tem nos computadores uma representao interna numrica dada por um determinado padro, fica fcil de entender os nmeros que vo surgindo durante o uso deste programa. Voc encontrar tabelas de algumas destas representaes num dos apndices deste texto. Se examinarmos o programa acima, encontraremos um pequeno defeito. Se voc quiser sair do programa o mesmo pedir para voc entrar com um caracter, o que totalmente desnecessrio. Vamos criar uma verso deste programa no qual evitaremos este problema. /* Impressao do cabecalho */

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> #include <conio.h> int le_char() { printf(" Entre com um caracter "); return (getch()); } main() { do { char ch, opcao, le_char(); /* Impressao do cabecalho */ printf(" <1> : imprime em formato ASCII \n"); printf(" <2> : imprime em octal \n"); printf(" <3> : imprime em decimal \n"); printf(" <4> : imprime em hexadecimal \n"); printf("<Outras> : sai \n"); /* --------------------- */ printf(" Entre com uma opcao "); opcao = getch(); printf("\n"); switch (opo) { case '1' : ch = le_char(); printf(" ASCII - %c\n",ch); break; case '2' : ch = le_char(); printf(" octal - %o\n",ch); break; case '3' : ch = le_char(); printf(" decimal - %d\n",ch); break; case '4' : ch = le_char(); printf("hexadecimal - %x\n",ch); break; default : exit(0); } } while(1); } Criamos uma funo com o objetivo de mandar uma mensagem para a tela e ler o caracter. Daqui a pouco faremos uma outra verso um pouco mais interessante. OBS.: A forma que usamos o while (colocando uma constante dentro dos parnteses) NO DEVE SER IMITADA. Observe que 1 no tem significado nenhum em si. Devemos sempre usar uma varivel ou uma definio (isto veremos abaixo) no lugar de colocar constantes mgicas. Comentrios ao lado podem at esclarecer o funcionamento mas o ideal que deixemos a varivel "falar por si", ou seja, que ela tenha um "nome" que j seja o suficiente para mostrar a sua funo.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Mais um exemplo ser til para marcar as diferenas entre o for de C e laos semelhantes de outras linguagens. Aqui teremos duas variveis sendo contadas pelo mesmo for mas em ritmos diferentes. main() { int impr, i, nimpr, nt; nt = 100; nimpr = 10; for (impr = 1, i = 1; impr < nt; impr += nimpr, i++) { printf("\n impr = %d ### i = %d \n", impr, i); } } Neste caso, sero impressos os valores de impr que iro de 1 at 91 de 10 em 10, ou seja, sero gerados os nmeros 1, 11, 21,...,91 enquanto i ir de 1 at 10 estando o lao sob controle da condio sobre impr.

EXERCCIOS I)Experimente no usar a palavra reservada break no programa trs, terceira verso. II) Use a funo printf() para observar as sadas de todos os programas dados anteriormente como exerccio. (No se esquea de colocar #include <stdio.h>). III) Desenvolva uma funo que imprima um inteiro ou um caracter em forma binria. Feito isto, utilize-a no programa acima, criando outra opo no menu.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
PONTEIROS E FUNES Vamos agora falar de um tipo de varivel extremamente poderosa: o ponteiro, tambm chamado apontador. Recordemos que existem dois operadores chamados operadores de endereo e estes so representados por: & "o endereo de" . Ex. : a = &b. a recebe o endereo da varivel "b". * "no endereo de". Ex. : c = *d. c recebe o valor da varivel "apontada" por d. Observemos que num computador, quando escrevemos algo como : a = b; O computador faz as seguintes operaes: pega o contedo da posio da memria correspondente a varivelb, coloca este valor na posio da memria correspondente a varivel a. Ou seja, uma transferncia do contedo de endereo para contedo de endereo. Os operadores de endereo nos permite obtermos endereos de variveis ou tendo endereo de uma varivel, saber seu contedo. As variveis que contm os endereos de outras variveis denominamos PONTEIROS ou APONTADORES. claro que aqui surge uma suspeita: Como as variveis tem estruturas diferentes, provavelmente teremos tipos diferentes de ponteiros para cada tipo de varivel. Para usarmos o ponteiro certo com a varivel certa, teremos que definir os ponteiros tal como fazemos com as outras variveis. A notao a que se segue TIPO *nome_d_ponteiro_1, *nome_do_ponteiro_2, ...; Podemos fazer, se quisermos, a definio dos ponteiros junto com as das variveis. Lembre-se sempre que um ponteiro no tem "nenhuma" informao, o que ele tem o endereo de uma informao. Mas, neste ponto, algum pode perguntar: Porque o ttulo desta parte PONTEIROS E FUNES? justamente aqui que veremos uma dos usos dos operadores de endereo. Para demonstrar esta utilidade, daremos um exemplo clssico de uma funo que NO FUNCIONA. Abaixo temos escrita uma funo que faria a permuta entre duas variveis. #include <stdio.h> permutar(a,b) int a,b; { int auxiliar; auxiliar = a; a = b; b = auxiliar; } main() { int a = 5, b = 3, c = 7; permutar(a,b); permutar(a,c); printf(" %d, %d, %d .",a,b,c); } O que aconteceria se executssemos este programa? As variveis a e b no teriam os seus valores trocados. Lembrem-se que as variveis, como esto declaradas so locais e que funes em C s trabalham passando valores. O que fizemos dentro da funo permutar() no afetar o contedo das variveis a, b e c do programa principal.
Departamento de Cincia da Computao-UFF

Nmeros de pgina
A sada desta situao no esta em declarar a, b e c como variveis globais, j que assim voc s poderia intercambiar estas variveis. A funo ficaria restrita ao programa que voc esta fazendo e a certas variveis de entrada. A sada esta no fato de que a diferena entre as variveis locais de cada funo (apermutar() e a main ()) que elas ocupam posies diferentes na memria. Se passarmos o endereo da varivel em vez do valor da varivel, temos uma maneira de afetar este valor. Vejamos uma verso modificada do programa anterior: #include <stdio.h> permutar(a, b) int *a,*b; { int auxiliar; auxiliar = *a; *a = *b; *b = auxiliar; } main() { int a = 5, b = 3, c = 7; permutar(&a, &b); permutar(&a, &c); printf(" %d, %d, %d .", a, b, c); } Atravs do operador & ("o endereo de"), passamos para a funo permutar() os endereos de a e b do programa principal. Ao entrar na funo, observemos que os parmetros foram declarados com o uso do operador * ("no endereo de"). Portanto *a e *b so os contedos das variveis a e b do programa principal. No momento que eu estou fazendo as trocas dentro da funo, estamos na verdade trocando os contedos das variveis a e b do programa principal! Uma pergunta a ser feita : J que toda funo em C retorna algo, como fica permutar() que no precisa devolver nada? A inteno em no colocar nada na declarao do tipo de sada tentar passar a idia de que qualquer coisa retornada pela funo no tem significado. No entanto, esta no uma boa maneira. O ANSI C apresenta uma soluo e a veremos mais a frente. Repare ainda o formato de sada das variveis.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
PONTEIROS E VETORES Vetores so encontradas em muitas linguagens e C no escapa disto. Se declara um vetor em C da seguinte maneira: TIPO nome_do_vetor [TAMANHO] com uma ressalva: vetores em C comeam do ndice 0 (zero) e acabam em TAMANHO menos um. Por exemplo int vetor[10]; float evento[4]; Acima estamos declarando um vetor inteiro de dez posies e um vetor de ponto flutuante de quatro posies. O elemento vetor[0] o primeiro e vetor[9] o ltimo. Analogamente para o vetor evento, o primeiro elemento ser evento[0] e o ltimo evento[3]. No caso de cadeia de caracteres temos uma situao especial. Se declararmos o vetor char titulo[10] teremos uma cadeia de caracteres de no mximo 9 (NOVE) caracteres e no dez. Uma das caractersticas de C que toda cadeia de caracteres tem seu fim marcado por um caracter nulo. (Como atribuir um vetor uma cadeia de caracteres? Um momentinho s! J veremos isto.) Ento lembre-se sempre : "Para trabalhar com cadeias de caracteres, devemos deixar espao para o caracter terminador da cadeia (o nulo). " Outra particularidade : "No h verificao dos limites de um vetor. " O esquecimento deste fato uma grande fonte de erros. Mas o que os vetores tem com os ponteiros? Para explicar isto, de novo vamos falar de como o computador trabalha s que agora com vetores. Um vetor geralmente armazenado na memria do computador em posies contguas. Tendo a primeira posio de memria (que chamaremos de posio zero), acrescentando o ndice do vetor a esta posio multiplicado pelo tamanho do tipo de varivel, teremos a posio do elemento do vetor correspondente ao do ndice. Em C um vetor, na verdade, um ponteiro para o incio da seqncia de elementos do vetor. Podemos, ento, referenciarmos elementos do vetor tanto pelos ndices como atravs de ponteiros. Devemos aqui chamar a ateno que, o trabalho com ponteiros mais rpido que o trabalho com os ndices. Esta opo dupla existe j que certos algoritmos (como os de ordenao) so mais simples de serem implementados se a referncia feita por ndice enquanto outros ficam mais simples com o uso de ponteiros. Como exemplo, criaremos uma funo que escreve na tela uma cadeia de caracteres usando a funo putchar(), j descrita. #include <stdio.h> putstr(s,n) /* Setimo programa */ char *s; int n; { int i; for (i = 0; i < n; ++i) { putchar(s[i]); } } main()
Departamento de Cincia da Computao-UFF

Nmeros de pgina
{ char s[10]; . . putstr(s,10); } Voc ter que passar como parmetros no s o vetor como tambm o seu tamanho. Na verdade, no h a necessidade de passar o nmero de elementos. Poderamos usar uma palavra chave que no informa o tamanho de uma varivel, mas no iremos nesta direo. Mas, e se lembramos que uma cadeia de caracteres termina por nulo? Vejamos uma verso do programa acima que se utiliza disto. #include <stdio.h> putstr(s) char *s; { int i; for (i = 0; s[i]; ++i) { putchar(s[i]); } } main() { char s[10]; . . putstr(s); } Observe a condio de parada do for feita usando o fato que em C, uma cadeia de caracteres termina num nulo. Ficou bem elegante, embora um tanto quanto desagradvel. Ainda podemos melhora-la. #include <stdio.h> putstr(s) char *s; { while (*s) putchar(*s++); } main() { char s[10]; . . putstr(s); } Observe que, com ponteiros, a funo fica mais simples e ainda mais elegante. Esta funo, ou melhor, sua equivalente, est na biblioteca padro de C com o nome deputs().

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ARITMTICA DE PONTEIROS O nmero de operaes aritmticas que podem ser feitas com os ponteiros bem restrita, pela prpria natureza deles. Lembre-se que o ponteiro em si no tem nenhum significado. Ele apenas nos diz onde se encontra a informao que queremos acessar. bom sempre ter na cabea que : " Ponteiros no so inteiros." Como a estrutura de endereamento pode variar de computador para computador, no necessariamente o endereo pode ser representado por um nmero inteiro. Alm disto, mesmo que pudssemos representar os ponteiros como inteiros, obviamente os conceitos envolvidos so totalmente diferentes e, lembro mais uma vez, no devem ser misturados. Abaixo tabelamos os operadores que so vlidos em operaes com ponteiros. Suponhamos dois ponteiros p e q e um inteiro i: ++p --p p++ p-*p p+i p-i p-q pr-incrementa o valor do ponteiro pr-decrementa o valor do ponteiro ps-incrementa o valor do ponteiro ps-decrementa o valor do ponteiro acessa o contedo do endereo apontado por p Soma de um ponteiro com um inteiro Subtrao de um inteiro sobre o ponteiro Subtrao entre ponteiros desde que apontem para o mesmo vetor

Os operadores de incremento e de decremento com ponteiros, trabalham de tal forma que levam em considerao o tipo de ponteiro que est sendo usado. Portanto se estamos usando um destes operadores com um ponteiro de inteiro, automaticamente o incremento (ou decremento) ser de dois bytes. Se temos um ponteiro para ponto flutuante do tipo float, o incremento (ou decremento) ser de quatro bytes. Algumas vezes interessante compararmos ponteiros. Imagine que tenhamos dois ponteiros p1 e p2 (p1 > p2), que referenciam um determinado vetor. Logo p1 - p2 nos dar o nmero de elementos entre estes ponteiros. Observe, no entanto, que no h sentido em compararmos ponteiros que no estejam referenciando a mesma estrutura de dados.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
INICIALIZANDO VETORES Podemos inicializar os valores dos vetores no momento da declarao desde que eles tenham sido declarados globais. A forma geral a seguinte TIPO nome[TAMANHO] = {ELEMENTO0, ELEMENTO1,..,ELEMENTO(n-1)); e para o caso especial de vetores do tipo char char nome[TAMANHO + 1] = "cadeia de caracteres"; ou char nome[] = "cadeia de caracteres"; com o fato interessante de no determinarmos o tamanho da cadeia. Mais uma gentileza do compilador que dimensiona por voc. Se a entrada da cadeia de caracteres for pelo formato geral, voc no deve esquecer de incluir o caracter nulo, procedimento dispensvel no formato especial para caracteres. Exemplos: float a[3] = {0, 3.1415926, 2.718281828}; char titulo1[7] = {'T','i','t','u','l','o','\0'}; char titulo2[7] = "Titulo"; Para exemplificarmos o uso de vetores e a sua inicializao, abaixo temos um pequeno programa que dado um vetor contendo nmeros de ponto flutuante, temos como resultado final o maior e o menor elementos. #include <stdio.h> float a[10] = {0.0, 3.1415926, 2.718281828, 2.2360679, 6.0, 0.33333333, -1.4142135, 0.57721566, 0.6931471, 17.0}; main() { float max = a[0], min = a[0]; int i; for (i = 0; i < 10; i++) { if (max < a[i]) { max = a[i]; continue; } if (min > a[i]) min = a[i]; } } Temos aqui o uso do continue para saltar o outro if, j que se um nmero for maior que o mximo, no tem sentido em testa-lo. Um exemplo um pouco mais complexo o do programa abaixo que ordena um vetor de inteiros usando ordenao por seleo. A idia fazer comparaes entre os valores do vetor de tal forma que inicialmente coloquemos o menor valor do vetor na primeira posio do mesmo. Feito isto, examinamos os elementos restantes e colocamos o menor deles na segunda posio. Continuando o processo, teremos o vetor ordenado. #include <stdio.h> int x[10] = {2, 4, 1, 8, 6, 7, 3, 9, 2, 5}; permutar(a,b) int *a,*b; { int auxiliar;
Departamento de Cincia da Computao-UFF

Nmeros de pgina
auxiliar = *a; *a = *b; *b = auxiliar; } main() { int i, j, aux, m, nm1, n = 10; nm1 = n - 1; for (i = 0; i < nm1; i++) { m = i; for (j = i + 1; j < n; j++) if (x[j] < x[m]) m = j; permutar(&x[i], &x[m]); } for (i = 0; i < n; i++) printf(" x[%d] = %d", i, x[i]); } Aqui usamos a funo permutar() j definida anteriormente.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VETORES MULTI-DIMENSIONAIS Mas no existem s vetores unidimensionais em C. Assim como em outras linguagens, aqui tambm temos vetores de mais de uma dimenso. A forma de declarao destes vetores a que se segue : TIPO nome_do_vetor [TAMANHO1] [TAMANHO2] ...[TAMANHOn] Diferente da maioria das linguagens, como podemos ver. Aqui cada dimenso varia de 0 (zero) a TAMANHO menos um, como no caso unidimensional. Um exemplo de declarao de um vetor multidimensional dado abaixo int matriz [10][10]; declarando uma matriz de inteiros com dez linhas por dez colunas. Quando se deseja passar vetores multidimensionais como parmetros de uma funo, voc no precisa declarar a primeira dimenso, ou seja, se declarada a matriz de caracteres char a[80][24]; ao passa-la como parmetro, podemos fazer tela(a) char *a[][24]; . . . O processo de inicializao de vetores multidimensionais anlogo a inicializao de vetores unidimesionais e tem a mesma restrio, ou seja, s podem ser inicializados os vetores declarados como variveis globais. O formato de inicializao, para o caso bidimensional, o que se segue: TIPO nome_do_vetor [TAMANHO1] [TAMANHOn] = { {elemento(0,0),...,elemento(0,TAMANHO1-1)}, {elemento(1,0),...,elemento(1,TAMANHO2-1)}, . . . {elemento(TAMANHOn-1,0),..,elemento(TAMANHOn-1,TAMANHOn-1)}}; Podemos interpretar que o que temos a inicializao de um vetor de dimenso um onde cada elemento foi definido tambm como um vetor de dimenso um. Para o caso de mais dimenses, basta proceder de maneira anloga. Escreveremos um programa exemplo que contm uma funo que faz o produto de uma matriz por um vetor.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> int a[4][4] = { {1, 2, 3, 4}, {4, 1, 2, 1}, {1, 2, 1, 4}, {4, 3, 2, 1} }; int b[4] = {1, 1, 1, 1}; matvet(a, b, c, n) int a[][4], b[4], c[4], n; { int i, j, aux; for (i = 0; i < n; i++) { aux = 0; for ( j = 0; j < n; j++) { aux = aux + a[i][j] * b[j]; } c[i] = aux; } } main() { int c[4], i; matvet(a, b, c, 4); for (i = 0; i < 4; i++) { printf(" c[%d] = %d \n", i, c[i]); } } A funo matvet() tem como entrada a matriz a ser multiplicada, o vetor b e um vetor de sada c alm da dimenso n da matriz e do vetor no devolvendo nenhum valor. As aspas colocadas tem uma razo j conhecida: Toda funo de C devolve um valor, este valor tenha significado ou no ou se ns mandarmos devolver um valor ou no. Para deixar claro o relacionamento entre ponteiros, vetores e matrizes e como so suas as relaes, abaixo temos mais um programa que gera uma matriz e imprime a mesma de vrias formas.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> main() { int a[3][3], i, j, k = 0, N = 3, *p; for (i = 0; i < N; i++) for (j = 0; j < N; j++) a[i][j] = k++; puts("Imprimindo o conteudo da matriz\n"); for (i = 0; i < N; i++) for (j = 0; j < N; j++) printf("\n a[%d][%d] = %d", i, j, a[i][j]); puts("\n\n\ Agora os N elementos apontados\n"); p = a[1]; for (i = 0; i < N; i++) printf("\n p[%d] = %d", i, p[i]); puts("\n\n Finalmente pegando o ponteiro do inicio da matriz\n"); p = a[0]; for (i = 0; i < N * N; i++) printf("\n p[%d] = %d", i, p[i]); } Como est descrito no prprio programa, estamos imprimindo a matriz usando os ndices como convencional, depois usando um ponteiro dado pelo uso do identificador da matriz como se este tivesse s um ndice e variando este ndice e finalmente imprimindo o apontado por um ponteiro que referencia o endereo do primeiro elemento da matriz. Ao execut-lo voc ver que a segunda impresso mostrar a segunda linha da matriz e na terceira impresso teremos todos os elementos. Deste ltimo resultado temos que uma matriz tem suas linhas colocadas contiguamente. Vemos ento que podemos manipular uma matriz de diversas formas: Como matriz tradicional, como ponteiros para vetores ou como ponteiro para ponteiro. A forma que deve ser usada justamente a que for mais conveniente para sua tarefa e no a que for mais conveniente para sua preguia. Se bem que algumas vezes uma acompanha a outra.... EXERCCIO I) Faa um programa que compare os elementos de dois vetores inteiros de mesmo tamanho, imprimindo as diferenas entre eles.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VETORES DE PONTEIROS E PONTEIROS DE VETORES Vimos que podemos acessar um elemento de um vetor via ndice ou via referncia por ponteiro, usando as duas formas de acordo com nossas necessidades. O que falaremos aqui so os casos que realmente misturamos os conceitos. Veremos inicialmente os vetores de ponteiros. A declarao deste objeto feita da seguinte forma Tipo *nome[Tamanho] Se quisermos definir um vetor de ponteiros do tipo int de nome vetor e de 15 componentes, escreveramos int *vetor[15] Qual seria o significado desta definio? O que est definido acima um vetor de 15 posies cada uma contendo um ponteiro para inteiro. Quanto ao ponteiro de vetores, sua declarao seria Tipo (*nome)[Tamanho] Aqui temos um ponteiro que aponta para um determinado vetor. Observe que neste caso teremos uma situao curiosa se lembrarmos que a definio de um vetor em si a definio de um ponteiro para um ponto na memria. Ou seja, temos uma varivel na qual poderemos guardar o endereo de qualquer vetor do tipo definido. Mais abaixo falaremos sobre isto.. Estes dois objetos tem a sua utilidade, obviamente. Mas um deles, o vetor de ponteiros, veremos em breve e provavelmente ser um velho amigo em pouco tempo.

PONTEIROS PARA PONTEIROS & PONTEIROS PARA PONTEIROS PARA PONTEIROS & ETC. Falaremos de alguns tipos de acesso a informao que podem gerar bastante confuso. Como j foi dito, um vetor pode ser trabalhado atravs de um ponteiro. Isto sugere que os vetores de ponteiros ou os ponteiros de vetores, podem ser usados (e pensados) como ponteiros para ponteiros, ou seja, um endereo que contm outro endereo que por sua vez aponta para uma varivel. Os mais criativos j devem estar pensando que podemos descrever ponteiros para ponteiros para ponteiros ou coisa ainda mais complicada. A notao de tais entidades exemplificada abaixo : int **a int ***a so respectivamente um ponteiro de ponteiro para um inteiro e um ponteiro de ponteiro de ponteiro para inteiro. Raras vezes so necessrias indirees maiores que as dadas acima. A maioria dos casos nos quais o programador tentado a fazer muitas indirees com a idia de melhorar a performance do programa que est desenvolvendo, j que as operaes com ponteiros so geralmente mais rpidas que as operaes com ndice. A maioria das vezes o que se ganha em velocidade ganha-se vrias vezes o equivalente em dores de cabea. Sempre que possvel evite tais procedimentos.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
PONTEIROS PARA FUNES Quando invocamos uma funo, o que ocorre que o fluxo do programa se interrompe momentaneamente e continua numa outra posio de memria (ou seja, num outro endereo). Isto nos permite referenciar as funes atravs de ponteiros. Estes sero Ponteiros para Funes. Como os vetores, a simples referncia ao nome da funo nos d o endereo da mesma. Observe que o que disse acima de seu uso e conhecimento seja l que linguagem de programao voc tenha estudado anteriormente. Da mesma forma que j dissemos que no existe passagem por referncia para variveis (o que passamos so os endereos destas variveis) tambm no existe passagem por referncia de funes. Novamente o que a linguagem C faz dizer para que programa o que est acontecendo de fato. Pelo menos para o nvel de programao. Isto d uma riqueza de possibilidades muito maior que o mascaramento protetor comum em outras linguagens. Abordaremos alguns aspectos e possibilidades mais tpicas destes ponteiros, sem explorar todas as possibilidades chamando a ateno que o que j falamos sobre ponteiros (e falaremos) poder ser aplicado em muitas circunstncias este tipo de ponteiros. Uma aplicao tpica quando fazemos uma funo de tratamento geral para funes arbitrrias. Neste caso, a funo entra como parmetro de outra funo. Ento teremos que passar o ponteiro para a funo geral para que possamos executar nosso programa de maneira adequada. A descrio de um ponteiro para funo tem a seguinte forma tipo da funo (* Nome da funo)(Lista de parmetros) Como primeiro exemplo, vamos usar uma funo da biblioteca padro que faz ordenao usando o algoritmo Quick Sort desenvolvido por Hoare. Esta funo tem o seguinte formato qsort(v, N, tamanho, compar); onde v um ponteiro para a estrutura de dados a ser ordenada, N o tamanho desta estrutura, tamanho o tamanho em bytes de cada elemento da estrutura e compar() um ponteiro para a funo que far a comparao entre dois elementos e qsort() devolve algo sem significado. compar() dever devolver -1, 0 e 1 caso os elementos sejam respectivamente menor, igual ou maior que o outro. Abaixo temos um programa no qual dado inicialmente um vetor de inteiros que deve ser ordenado em ordem crescente. #include <stdio.h> #include <stdlib.h> int v[8] = {6, 0, 5, 4, 1, 2, 3, 1}; int compar(a, b) int *a, *b; { if (*a < *b) return -1; else { if(*a == *b) return 0; else return 1; } }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main() { int i, tamanho = 2, /* Tamanho de um inteiro em bytes */ n = 8, /* Numero de elementos */ compar(); qsort(v, n, tamanho, compar); for (i = 0; i < n; i++) printf(" v[%d] = %d", i, v[i]); } Observe no cabealho mais uma biblioteca: stdlib.h. Ela contm a definio de qsort() alm de outras funes. Como um exemplo um pouco mais complexo, apresentaremos um programa que contm duas funes. Uma funo arbitrria e outra calcula, pelo mtodo da bisseo, uma raiz da primeira funo. A idia bsica deste mtodo de achar razes simples e fcil de entender observando a figura abaixo :

X A R B

Temos acima uma funo que se anula no ponto indicado R e que est entre A e B. Mesmo sem o desenho podemos saber isto j que a funo tem valor negativo no ponto A e positivo no B. Se dividimos o intervalo em dois e calculamos o valor da funo no ponto mdio X podemos atravs do seu sinal saber se R se encontra entre A e X ou entre X e B. Se subdividimos novamente o intervalo onde se encontra a raiz, teremos uma melhor aproximao de R. Continuamos subdividir at atingirmos a preciso que acharmos conveniente ou impomos um limite para o nmero de passos. Algoritmo: Seja a funo f(x) que tem uma raiz num intervalo [a, b]. Escolha o nmero mximo n_max de passos e a tolerncia. Ento faa : i) Ache o valor de f no ponto a; ii) Ache o ponto mdio do intervalo (x = (a + b)/2); iii) Ache o valor de f no ponto x; iv) Se f(a) * f(x) for negativo, a raiz se encontra entre a e x, caso no, esta entre x e b. No primeiro caso faa b = f(x ) e no segundo a= f(x); v) Se a distncia entre a e b for menor que a tolerncia ou o nmero de passos for maior que n_max, pare, seno volte a i;

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> #include <math.h> int ACHOU = 1; double f(x) double x; { return(exp(x) - 3 * cos(x)); } int bissecao(f, a, b, tol, imax, *raiz) double (*f)(), *raiz; double a, b, tol; int imax; { int i; double x, fa, fx; for (i = 0; i < imax; i++) { fa = f(a); x = (a + b)/2; fx = f(x); if (fa * fx < 0) b = x; else a = x; if (fabs(a - b) < tol) { *raiz = a; return ACHOU; } } return (! ACHOU); } main() { int n_max = 10; double a = 0.0, b = 1.0, tol = 0.001, raiz, f(), bissecao(); if (bissecao(f, a, b, tol, n_max, &raiz) != ACHOU) else } printf("Nao atingiu a precisao") printf(" Raiz = %f", raiz);

Na definio da funo bissecao() observe como colocamos a declarao do ponteiro de funo. Os parnteses so necessrios para que o compilador no interprete o nosso ponteiro para funo como um ponteiro para varivel. Repare ainda a utilizao da varivel ACHOU. A maneira como ela usada colabora para facilitar o entendimento do programa. No entanto, esta no a melhor maneira de defini-la. Mais a frente veremos algo mais elegante. Como sempre, visamos a clareza e no a eficincia. Por exemplo, no existe a necessidade de calcular a funo nos pontos a e x em cada passo do algoritmo. e no h necessidade de termos dois critrios de parada, ou seja, por tolerncia e por nmero de passos j que h uma relao entre estes parmetros neste mtodo. Mais detalhes veja um livro de Clculo Numrico[].

Departamento de Cincia da Computao-UFF

Nmeros de pgina
MAIS SOBRE FUNES a) Recursividade Outro ponto sobre funes em C, que elas podem ser usadas de maneira recursiva, ou seja, podem invocar a elas mesmas. Talvez isto possa parecer estranho para alguns mas em muitos casos extremamente til. Como exemplo, daremos duas funes que calculam o fatorial de um nmero, uma iterativamente e outra recursivamente. #include <stdio.h> fatorial(n) int n; { int i, fat; for (i = 1; i <= n; i++) fat = fat * i; return(fat); } main() { int fatorial(), n = 5; printf(" o fatorial de %d e igual a %d ",n,fatorial(n)); } Verso recursiva #include <stdio.h> fatorial(n) int n; { int i = n; if (n == 1) return(i); else return( i * fatorial(--n)); } main() { int fatorial(), n = 5; printf(" fatorial de %d e igual a %d ",n,fatorial(n)); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
O prximo programa ser uma implementao do algortmo de Euclides para o clculo do MDC. Faremos a mesma estratgia apresentando as verses no recursiva e recursiva. #include <stdio.h> int mdc(m, n) int m, n; { int r; do { r = m % n; m = n; n = r; } while(r != 0); return(m); } main() { int m = 120, n = 9, mdc(); printf("\n o MDC de %d e %d e ", m, m, mdc(m, n)); } Verso recursiva #include <stdio.h> int mdc(a, b) int a, b; { if (b == 0) return(a); else return(mdc(b, a % b)); } main() { int m = 120, n = 9, mdc(); printf(", m = %d, n = %d, mdc = %d", m, n, mdc(m, n)); } Observe que a maneira como est escrita se assemelha mais com o algoritmo original que a verso no recursiva. Mais um exemplo de problema resolvido de maneira recursiva o de determinar a seqncia de Fibonacci. Esta definida como abaixo :

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Seja F0 = 0 e F1 = 1 os dois primeiros termos desta seqncia. Ento qualquer termo pode ser determinado a partir dos dois anteriores, ou seja, Fi+2 = Fi+1 + Fi Assim ficaramos com 0 1 1 2 3 5 8 13 e etc. Abaixo temos um programa que exibe o n-simo elemento desta seqncia por meio de um processo recursivo. #include <stdio.h> int fibo(int n) { if (n <2) return (n); else return(fibo(n-1) + fibo(n-2)); } main() { int fibo(); int n = 8; printf(" %d", fibo(n)); } Esta no a maneira mais eficiente de calcular esta seqncia mas indiscutivelmente fcil de reconhecer nesta implementao a definio do problema. Uma das origens da ineficincia desta implementao est no fato de fibo(n-2) ser chamada 2 vezes, fibo(n-3) ser chamada 3 vezes e assim por diante. A esta altura deve ter gente perguntando o porque de uso recursivo de funes, j que estas aparentemente no so mais que curiosidades. Obviamente isto no verdade. Existem alguns algoritmos que so de definio simples de maneira recursiva e extremamente complicados de outra forma. Muitos algoritmos extremamente eficientes usam o paradigma chamado Dividir para Conquistar e so definidos de maneira recursiva. No caso de ordenao, por exemplo, temos os algoritmos Quick Sort e Merge Sort com implementaes recursivas de fcil compreenso. O mesmo no se pode dizer de verses no recursivas de algoritmos conceitualmente recursivos. Vamos dar mais um exemplo de utilizao de tcnicas recursivas, construindo um programa que d a soluo para o problema da Torre de Hanoi. Para os que no conhecem, contemos uma historinha: Dizem que em algum ponto perto de Hani existe um mosteiro onde os monges se entregam a um trabalho de manipular uma pilha de discos perfurados entre trs postes. Os discos so todos de tamanhos diferentes e colocados num poste (digamos, o da esquerda) de tal maneira que os menores esto postos sobre os maiores. Ao lado desta pilha, temos dois outros postes que chamaremos de poste do meio e da direita. A manipulao dos monges consiste em levar todos os discos do poste da esquerda para o poste da direita sem nunca colocar um disco maior sobre um menor usando o poste central para auxiliar. Dizem que quando acabarem de fazer a transferncia de todos os discos, o mundo chegar ao seu fim. Apesar do clima apocalptico, este problema fica bem simples de ser implementado se usarmos de recurso. Uma recomendao porm: no tente colocar um valor muito grande de discos. O processamento poder ser muito longo. No porque o programa recursivo mas porque demorado mesmo! Pode ser demonstrado que o nmero de operaes (manipulaes de discos) igual a 2n -1, onde n o nmero de discos. No caso dos monges, podem ficar descansados quanto ao fim do mundo. O nmero de discos no primeiro poste era de 64 e levaria vrias vezes a idade estimada do universo para fazer a transferncia total, se cada troca levasse apenas alguns segundos. Antes de tentar executar este programa, leia os comentrios aps o mesmo.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> char posicao[][11] = {"a esquerda","o meio","a direita"}; int ESQUERDA = 0, MEIO = 1, DIREITA = 2; mova(n, daonde, auxiliar, praonde) int n, daonde, auxiliar, praonde; { if (n != 1) mova(n - 1, daonde, praonde, auxiliar); printf("Mova o disco d%-s para %-s", posicao[daonde], posicao[praonde]); if (n != 1) mova(n - 1, auxiliar, daonde, praonde); } main() { char *titulo = "\nTorre de Hanoi.\nDigite o nro. de discos : "; char *cadeia[20]; int discos; while((puts(titulo), discos=atoi(gets(cadeia))) != 0) { puts("Coloque os discos a esquerda e faca os movimentos:"); mova(discos, ESQUERDA, MEIO, DIREITA); } puts("Ok! Pilha da esquerda totalmente transferida para direita"); } Observe alguns aspectos interessantes desta implementao: I) Veja a utilizao do operador vrgula dentro da expresso a ser avaliada pelo while. ii) Digno de nota a inicializao do vetor posicao[] de uma maneira "estranha". Aqui temos uma liberdade que C nos d. Especificamos o tamanho que deve ser reservado para cada cadeia de caracteres e deixamos quantas cadeias teremos em aberto. iii) Observem ainda uma monstruosidade. Foi atribudo ao ponteiro titulo uma cadeia de caracteres. Qual a garantia de que temos espao para colocar a cadeia de caracteres a partir do endereo dado pelo ponteiro? No temos garantia nenhuma! Este programa, como est escrito, pode funcionar ou no! Devido isto poderemos ter esta cadeia de caracteres sendo escrita "por cima" de outras variveis. No bom arriscar. Soluo? Esperem a prxima verso... iv) No colocamos o tipo devolvido pela funo mova(). Sabemos que qualquer funo em C devolve algo. Aqui suprimimos o tipo como uma maneira de indicar que de fato esta funo nada devolve. No uma maneira maravilhosa de dizer isto, j sabemos, e o C ANSI tem maneiras mais bvias e precisas.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
OBSERVE AINDA QUE : i)Aqui estamos manipulando vetores de maneiras extremamente variada. A pergunta que pode surgir o porque desta variedade de opes. Certamente no para usarmos de qualquer maneira de acordo com o nosso humor no dia. A linguagem C permite que uma mesma informao seja acessada de uma gama de maneiras de forma que exista uma que faa que fique mais fcil (ou mais claro) um determinado procedimento a ser implementado. Lembre-se que clareza extremamente importante se o nosso objetivo a produtividade. ii) Um bom desafio (dos grandes) fazer uma verso no recursiva que resolva este problema. EXERCCIOS I) Faa um programa no recursivo que dado um inteiro positivo n, imprima todos os n termos da seqncia de Fibonacci.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
FUNES EM NOTAO MODERNA Aqui daremos mais algumas caractersticas das funes e descreveremos a notao moderna para a definio dos seus parmetros. Toda funo em C devolve algum valor. Mesmo que a funo no tenha nenhum return, ela devolver, de uma maneira geral, zero. Como em certas ocasies no h a necessidade de um valor retornado (ou necessariamente no tem um tipo especfico), foi criada uma palavra reservada, pertencente as extenses ANSI, que indica exatamente isto. Esta palavra void e veremos uma aplicao dela logo abaixo. Na notao moderna os parmetros das funes e o valor devolvido, tero os seus tipos indicados pelas definies de tipos. Teramos para a funo fatorial, definida num dos exemplos deste texto, a seguinte notao int fatorial(int n) indicando que voc passa para ela um inteiro e tem como resultado tambm um inteiro. No caso da funo permutar(), tambm definida num exerccio, teramos a definio void permutar(int *a,int *b). Aqui surge a palavra nova, void, indicando aqui que o valor devolvido (lembre-se: funes em C sempre devolvem algum valor) no tem significado. Temos ainda a indicao que passaremos dois ponteiros para inteiro como parmetros, ou seja, voc ao chamar a funo dever dar o endereo das variveis e no seus valores. Este jeito de definir uma funo denominado de Forma Moderna. As vantagens esto no s na questo de documentao como tambm provoca uma verificao de tipos gerando um alerta caso o tipo de dado de entrada numa funo no for compatvel com a definio dada desta forma. Ateno : Alguns compiladores antigos no admitem a escrita em notao moderna. MAIS ALGUMAS FUNES Daremos, abaixo, uma relao de algumas funes que podem ser teis em seus trabalhos e exerccios, j escritas em notao moderna. stdio.h char *gets(void) - entrada de uma cadeia de caracteres pelo teclado; puts(char *s) - escreve a cadeia de caracteres s na tela, saltando automaticamente para a outra linha; scanf(*char, *var1,...,*varn) - entrada formatada. O seu funcionamento anlogo ao printf(), o mesmo valendo para os caracteres de controle. Observe que o que temos como parmetros so endereos. ctype.h char tolower(char ch) - D como sada o caracter ch em minscula. char toupper(char ch) - D como sada o caracter ch em maiscula. stdlib.h int atoi(char *s) - Converte a cadeia de caracteres s num nmero inteiro. long int atol(char *s) - Converte a cadeia de caracteres s num nmero inteiro longo.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
SOBRE ESTAS FUNES Faamos alguns comentrios sobre algumas destas funes. As funes puts() e printf() com as seguintes estruturas so "equivalentes" puts(str) printf("%c\n", str)

O que as diferencia que a o arquivo executvel de um programa que utilize a funoputs() ser menor que o que usar um printf() equivalente. Algumas vezes este fato poder ser de importncia. Observe ainda que a sentena de controle do printf() uma cadeia de caracteres. Portanto, podemos criar programas nos quais o formato de sada seja dado em tempo de execuo. EXERCCIO I)Faa um programa em C que inverta as palavras de uma frase. A frase entrar via teclado. Exemplo : Entrada : Um exemplo simples. Sada : mU olpmexe selpmis. II) Na idade mdia e na renascena era muito comum os pesquisadores, nas mais variadas reas, codificarem suas descobertas. Geralmente estas descobertas eram condensadas em pequenas frases. A frase a ser codificada sofria uma modificao tendo todas as suas letras colocadas em ordem alfabtica e sem espao. Exemplo: Entrada : um exemplo simples. Sada : eeeillmmmoppssux. Crie um programa que faa este procedimento com uma frase que seja dada via teclado. Dica: consulte a tabela ASCII num dos apndices. O texto de sada s dever conter letras maisculas ou s minsculas.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
FUNO main() COM PARMETROS At o momento, trabalhamos com a funo principal main() como se ela no tivesse parmetros. Na verdade, os parmetros desta funo so "opcionais". Eles so usados para passar dados para a execuo do programa no momento de sua invocao. Expliquemos melhor com um exemplo : prog par1 par2 ... parn onde prog o nome de seu programa e par1, par2,..., parn so parmetros de entrada. (Uma pergunta surge: porque as aspas no opcionais acima? O motivo que main() pode ser interpretada como uma funo com nmero varivel de parmetros.) Muito provavelmente, voc j est acostumado a usar programas desta maneira. Agora voc poder fazer programas em C que aceitem dados assim. Um formato o que se segue: main(int argi, char *argv[]) onde argi um inteiro que d o nmero de parmetros mais um e argv um vetor de ponteiros para caracter de dimenso indefinida. Quero observar que argv um ponteiro para cadeias de caracteres onde o primeiro elemento contm o ponteiro para a cadeia de caracteres correspondente ao nome do programa executvel, o segundo contm outro ponteiro para uma cadeia de caracteres seguinte ao nome do programa, e assim por diante. Chamemos a ateno que a primeira cadeia de caracteres contem o nome do programa invocado. Vamos dar um exemplo de utilizao destes parmetros, reescrevendo o sexto programa : #include <stdio.h> #define N_PARM 3 void permutar(int *a, int *b) { int auxiliar; auxiliar = *a; *a = *b; *b = auxiliar; } main(int argc,char *argv[]) { int a,b,c; if (argc < N_PARM) { puts("Entre com dois numeros inteiros separados por espaco "); scanf("%d %d",&a,&b); } else { a = atoi(argv[1]); b = atoi(argv[2]); } permutar(&a,&b); printf(" %d %d",a,b); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Este programa esperar que os dados entrem logo aps a declarao do nome do programa executvel. Se o nmero de parmetros (lembre-se, esses so cadeias de caracteres) for menor que 3 (nmero de parmetros "teis" mais um), o programa solicitar os dados de entrada. J vimos que quando referenciamos um vetor, na verdade estamos falando de ponteiros. Logo o argumento *argv da funo main() pode ser trabalhado tambm como um ponteiro para ponteiro, ou seja algo que guarda um endereo que com tem outro endereo onde (finalmente) se encontra uma informao. Ou seja, podamos definir main() como main(argc, **argv) Algumas vezes trabalhar assim mais interessante. Outro ponto a chamar a ateno que main() apesar de ser uma funo especial ela uma funo com todas as atribuies e "deveres". Portanto, ela devolve um valor. A pergunta a quem?

PARA QUEM main() RETORNA UM VALOR No difcil de saber: a quem disparou a execuo do programa, mais comumente o sistema operacional mas no sempre. Voc pode escrever programas que disparam (ou so disparados por) outros programas uma necessidade bvia para uma linguagem que pretende ser a base de um sistema operacional. Para indicar que o parmetro de retorno de main() no tem significado relevante para as finalidades do programa podemos escrever ento void main(argc, **argv) ou a outra forma equivalente.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
O COMANDO #define (do preprocessador) Falando de inicializao, vamos apresentar outro comando do preprocessador que, muitas vezes, usado na inicializao de constantes. Mas veremos que ele mais que isto. Atravs dele, poderemos criar definies que sero substitudas em tempo de compilao, ou seja, fazer macro substituies. O seu formato #define nome definicao Por exemplo, #define TAMANHO 15 #define frase "Esta e uma frase" No programa que contiver tais definies, o compilador far substituies nos pontos em que surgirem cada nome acima. Vamos fazer mais um programa no qual acharemos o maior elemento, no negativo, num vetor dado. #include <stdio.h> #define PI 3.1415926 #define E 2.7182818 float vetor[5] = {1.4142135, PI, E, 1.7320508, 0.5772156}; main() { float maior = vetor[0]; int contador; for (contador = 4; contador; contador--) { if (maior < vetor[contador]) maior = vetor[contador]; } printf(" O maior elemento do vetor dado e %f\n",maior); } Observe a maneira pouco ortodoxa do uso do critrio de parada do for, levando em considerao que o zero significa uma condio falsa e estando o for com contagem regressiva. Note ainda que foi adotada a conveno de notar o valor de constantes em letras maisculas. Faamos uma variao do programa de determinar os elementos da seqncia de Fibonacci, j conhecida nossa. Aqui usaremos mais uma forma de achar esta seqncia. Podemos achar o n-zimo termo da srie de Fibonacci usando a frmula

an + bn 1+ 5 1 5 Fn +1 = ;a= ;b = 5 2 2
Temos abaixo a nova implementao.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> #include <math.h> #define raiz_quadrada_de_cinco sqrt(5) main() { double alfa = (1 + raiz_quadrada_de_cinco)/2, beta = (1 - raiz_quadrada_de_cinco)/2, f; int n = 15; f = (pow(alfa, n) + pow(beta, n))/raiz_quadrada_de_cinco; printf(" o elemento %d da serie de Fibonacci e %f", n, f); } Observe aqui mais uma biblioteca <math.h>, que contm as funes matemticas mais usadas (veja relao no apndice). As funes aqui usadas so pow() que acha a potncia de um nmero por outro e sqrt() que calcula a raiz quadrada de um nmero. Observe ainda mais uma maneira de usarmos o #define. No estamos atribuindo raiz_quadrada_de_cinco o valor de sqrt(5). O que o #define faz substituir, em tempo de compilao, os identificadores que encontrar pelo que se segue na sua definio. Podemos ento fazer coisas bem mais complexas com este comando: MACROS. Uma macro um conjunto de caracteres que pode ser entendido como uma pequena funo. Por exemplo, a funo getchar() geralmente definida como uma macro a partir da funo getc(). Como exemplo de utilizao, rescrevamos um programa. #include <stdio.h> #define TETO 9 #define tetof(a,b,teto) a + b > TETO ? TETO : a + b main() { int a = 5, b = 3, c; c = tetof(a, b, TETO); } Observe que devemos tomar cuidado com as macros. Se usamos parmetros que so manipulados em pontos distintos da mesma, podemos ter surpresas desagradveis. Ao usarmos teto() da forma teto(i++, j++, teto) teremos uma reao estranha pois teremos a equivalncia desta expresso abaixo i++ + j++ > TETO ? TETO : i++ + j++ Assim os valores de i e j tero seus valores incrementados duas vezes e o resultado estar incorreto.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Para que fique claro o alcance das macros, abaixo temos mais um programa onde definiremos a partir da funo printf() uma macro de impresso. #include <stdio.h> #define imprime(x) printf(" %d \n", x) main() { int a = 1, b = 2, c = 3; imprime(a); imprime(b); imprime(c); } Apesar de termos de tomar cuidado extremamente til o uso das macros. Para dar exemplos, vrias funes so definidas como macros como, por exemplo, getcha() e putchar() alm de muitas outras. Naturalmente deve ter surgido a dvida : Quando usar uma macro em substituio de uma funo? No difcil de ver que ao definirmos algo como uma macro, ganharemos tempo durante o processamento. Isto fica claro se lembrarmos que ao se chamar uma funo, so copiadas as variveis dos parmetros, criada uma rea de trabalho e ao final da execuo, mais informaes podem ter que ser salvas. Na macro substituio tais fatos no acontecem. No entanto, por serem geradas vrias cpias de uma mesma funo, teremos um arquivo executvel maior. Devemos pesar os prs e os contras nesta situao.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
MAIS COMANDOS DO PREPROCESSADOR O preprocessador tem por finalidade permitir que variveis chave ou expresses sejam facilmente modificadas e compartilhadas entre vrios arquivos que constituem seu programa fazendo com que o programa seja minimamente perturbado na sua escrita caso estas variveis ou expresses sejam modificadas. Permitisse assim a compilao condicional, ou seja, definindo o ambiente onde o programa ser compilado o prprio cdigo (na figura do preprocessador) se arrumar convenientemente. Serve, portanto, para facilitar ainda mais a transportabilidade dos programas escritos em C. No total, os comandos do preprocessador so os seguintes #define #include #if #ifdef #else #endif #error @ #pragma @ # (comando nulo) @ @ Extenso ANSI Os comandos #define e #include j nos so conhecidos e vamos aqui descrever os principais, primeiro de maneira compacta e aps isto faremos um exemplo para esclarecer o uso de alguns destes comandos. #undef nome - remove a definio de nome. Este til, por exemplo, para forar que uma funo seja exatamente isto e no uma macro. #if expresso - testa a expresso, necessariamente inteira. #ifdef nome - testa se nome j foi definido. Se verdadeiro, o que se segue a este comando ser levado em considerao. #ifndef nome - testa se nome no foi ainda definido. Se verdadeiro, o que se segue a este comando ser levado em considerao. #else - se o teste feito em #if, #ifdef ou #ifndef for falso, o que vier depois deste comando ser levado em considerao. #endif - finaliza um comando #if undef #ifndef #line #elif @

Como exemplo, vamos supor que voc esteja desenvolvendo um sistema que trabalhar em vrios computadores, compiladores ou sistemas operacionais. claro que haver diferenas, mas seu programa ser essencialmente o mesmo a no ser por diferenas contidas em bibliotecas, cada uma adaptada ao conjunto sistema operacional/compilador sob o qual voc estiver trabalhando. Voc poderia ento criar um preprocessador do seguinte tipo, caso as opes fossem feitas entre sistemas operacionais:

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#define DOSXX 0 #define DOSYY 1 #define DOSZZ 2 #define SISTEMA_OPERACIONAL DOSXX #if SISTEMA_OPERACIONAL == DOSXX #include <xxgraph.h> #include <xxkb.h> #else #if SISTEMA_OPERACIONAL == DOSYY #include <yygraph.h> #include <yykb.h> #else #include <zzgraph.h> #include <zzkb.h> #endif #endif Observe que basta modificarmos a definio do sistema operacional para que o preprocessador se encarregue de todas as operaes necessrias. Nos exemplos deste livro, sempre temos os comandos do preprocessador antes de qualquer funo. Isto muito comum mas no limitado a apenas esta forma. Podemos colocar os comandos do preprocessador em qualquer ponto. Portanto, poderemos ter um programa camalenico, que assume vrias formas dependendo de parmetros dados inicialmente. Claramente esta possibilidade colabora com a portabilidade de um produto.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VARIVEIS ESTTICAS (static) Sabemos que os valores das variveis locais simplesmente deixam de existir quando samos dos blocos nas quais esto definidas. Com isso se economiza memria pois evita que variveis que, muitas vezes, so utilizadas de maneira marginal, ocupem uma rea de memria inutilmente. No entanto, algumas vezes, interessante que alguma varivel local mantenha o seu valor entre chamadas. Para que isto seja feito, necessrio que voc de a ela a classificao especial que chamaremos de tipo esttica. Para isto, o formato o que se segue: static TIPO nome_da_varivel; Com isto, o valor da varivel ir "sobreviver" entre as chamadas. Faamos um exemplo simples no qual criamos uma funo que gera nmeros aleatrios, ou melhor, pseudo-aleatrios entre 0 e 1. (Para maiores informaes sobre nmeros aleatrios e algoritmos para sua gerao, consultar as referncias [].) #include <stdio.h> float aleatorio(void) { static long semente = 1; float auxiliar; semente = (semente * 32719 + 3) % 32749; auxiliar = semente/32749; return (auxiliar); } main() { int i; float aleatorio(); for (i = 10; i; i--) printf(" numero aleatorio %f\n",aleatorio()); } Na verdade, este exemplo no funcionar! A sada ter apenas valores nulos. Isto no se deve ao funcionamento do static ou do algoritmo. O que esta acontecendo que no momento que esta havendo a diviso semente/32749 dentro da funo, no esto sendo feitas as converses numricas que espervamos. As converses correm so as que se seguem: o nmero 32749 identificado como do tipo int e teremos ento uma operao mista, j que semente long int. H ento a converso de tipo da constante 32749 para long int e ento feita a diviso. Neste momento, no momento de atribuir o resultado da operao varivel auxiliar, haver outra converso, desta vez para o tipo float. Observe que se na diviso o dividendo for menor que o divisor teremos zero como o valor de auxiliar. Para termos o resultado correto deveremos modificar esta converso automtica. Para que nosso programa funcione convenientemente, basta colocar (float) (ver Converso de Tipos) depois da barra de diviso e antes do nmero, ou seja :

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> float aleatorio(void) { static long semente = 1; float auxiliar; semente = (semente * 32719 + 3) % 32749; auxiliar = semente/(float)32749; /* linha modificada */ return (auxiliar); } main { int i; float aleatorio(); for (i = 10; i; i--) printf(" numero aleatorio %f\n",aleatorio()); } O funcionamento agora ser correto j que, o (float) forar que a constante 32749 seja convertida para float. No momento da diviso, teremos a converso automtica de semente para float e ento ser atribudo a auxiliar um valor do mesmo tipo. A sada deste programa ser um conjunto de nmeros os quais, aparentemente, no tem nenhuma relao um com os outros. bom chamar a ateno que o nome aleatrio s tem sentido se considerarmos o conjunto de todos os pontos gerados. Exerccio Experimente executar este programa sem o uso de static.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VARIVEIS EXTERNAS (extern) Um aspecto da linguagem C que ela permite, e at incentiva, que voc crie programas em mdulos compilados separadamente. A vantagem disto est no fato que o tempo de compilao toma boa parte do tempo de desenvolvimento de um determinado projeto. Muitas vezes, o defeito ocorre num determinado ponto ou funo e seria uma grande perda de tempo compilar todo um programa por causa de um erro destes. Compilando mdulos do programa separadamente, poupasse tempo mas cria um problema. J falamos das variveis globais. Imagine que eu precisa dentro de um destes mdulos de uma varivel global que esteja em outro mdulo. Neste caso, ao compilar todo o programa, teremos uma mensagem de erro nos dizendo que h variveis duplicadas se declararmos as mesmas variveis globais em todos os mdulos. Se compilarmos um mdulo separadamente e este no contiver as variveis globais que referencia, haver a emisso de outro erro nos dizendo que existem variveis no declaradas. A maneira de sair desta sinuca justamente o uso da declarao extern. Declarando num mdulo certas variveis como extern, voc avisa que aquelas variveis esto declaradas num outro mdulo do programa. A forma da declarao das variveis extern a que se segue : extern TIPO varivel1, varivel2,..., variveln Esta declarao deve sempre estar contida dentro dos mdulos que necessitem usar as variveis globais definidas em outro mdulo.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
VARIVEIS REGISTRADOR (register) Uma linguagem de programao comum, permite que voc tenha variveis s na memria do computador o que obriga ao compilador, quando trabalha com tais variveis, a ler um valor na memria, leva-lo para uma memria interna do processador central chamada de registrador, pegar outra varivel colocando-a em outro registrador e s ento opera-las. Mas ainda no acabou! Agora o compilador ter que remeter o valor calculado a posio de memria correspondente a varivel que recebe as duas variveis operadas. Ufa! Acontece que C permite que parte deste trabalho no precise ser feito. Voc poder declarar uma determinada varivel para que esta esteja guardada no em memria mas diretamente num registrador. D para perceber que pouparemos um bocado de tempo. A declarao de variveis do tipo register permite esta facilidade. A forma geral a que se segue : register TIPO var1, var2, ..., varn Vamos a um exemplo de utilizao, modificando (mais uma vez) programa feito anteriormente. #include <stdio.h> main() { register int n; float x; x = 235.; for ( n = 1; n <= 10; n++) { x = x + x; if ( x >= 2000.) break; } } O resultado da declarao da varivel n com o do tipo register, que o tempo de execuo do lao for ser menor pois no haver a necessidade de "se ir" memria pegar o valor desta varivel para fazer comparaes e operaes de incremento. Vemos, portanto, que vale mais a pena definirmos uma varivel como register quanto mais usada ela for. O nmero de variveis do tipo register permitidas e seu tipo, so dependentes do computador e do compilador usados. Consulte os manuais de seu compilador. Geralmente se voc define mais variveis do tipo registrador que o conjunto computador-compilador pode trabalhar, o prprio compilador reconverte a varivel para uma do tipo comum. Mais um aviso: No use variveis register quando a varivel for referenciada por endereo. Afinal de contas estas variveis no esto na memria e sim num registrador.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
RESERVANDO MEMRIA PARA USO TEMPORRIO (ou ALOCANDO MEMRIA ) Muitas vezes estamos trabalhando com dados que no precisam estar declarados o tempo todo. Ou ainda, estamos com pouca memria disponvel. A linguagem C permite que reservemos memria para uso temporrio e que, logo que no haja mais a necessidade dela, possamos libera-la. Isto feito pelo uso de algumas funes da biblioteca padro, que so listadas abaixo junto com suas descries : a) void *malloc(unsigned int nmero_de_bytes) - Reserva um determinado nmero de bytes para uso temporrio. Se esta reserva no for possvel, ser devolvido um nulo. b) void *calloc(int nmero_de_elementos, int tam_dos_elementos) - Reserva memria determinada pelo produto do nmero de elementos pelo tamanho de cada elemento (em bytes). A memria reservada ser preenchida com zeros. Devolve um ponteiro no nulo em caso de operao correta. c) void free(void *pt) - Disponibiliza para outros usos a memria reservada pelo uso de malloc() ou calloc(). A referncia feita pelo ponteiro devolvido pelas funes. A rea de memria reservada se encontra em posies contguas na memria livre do computador. Damos abaixo um exemplo de uso destas funes. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> #include <dos.h> #define NLINHAS 24 #define NCOLUNAS 80 #define SLEEPTIME 3 void main(void) { char **tela, *s, ch; int linha, nchar; s = (char *)malloc(NCOLUNAS + 1);

tela = (char **)malloc(NLINHAS); puts("\nInicie o texto\n\n"); linha = 0; do { gets(s); nchar = strlen(s); tela[linha] = (char *)malloc(nchar); strcpy(tela[linha], s); linha++; } while (linha < NLINHAS);

Departamento de Cincia da Computao-UFF

Nmeros de pgina
puts("\n\n\n Aperte C(ontinue) para ver a tela"); do { ch = toupper(getch()); } while (ch != 'C'); linha = 0; do { strcpy(s, tela[linha]); puts(s); linha++; } while (linha < NLINHAS); sleep(SLEEPTIME); puts("\n\n\n Liberando a memoria"); for (linha = 0; linha < NLINHAS; linha++) free(tela[linha]); sleep(SLEEPTIME); } Neste programa guardamos os dados de uma tela que fornecida pelo usurio. Aps isto, escrevemos esta tela. Para isto, criamos um ponteiro para ponteiro do tipo char com a finalidade de criarmos um vetor de ponteiros para caracteres (no caso a varivel tela) alm de um ponteiro para caracteres (s) que servir para guardar temporariamente cada linha fornecida. Repare na moldagem feita no ponteiro devolvido por malloc() para os tipos que necessitamos. Se no lugar de um vetor de ponteiros tivessemos colocado uma matriz com o tamanho da tela (80x24), estaramos ocupando tanto para uma tela cheia quanto para uma tela vazia, o mesmo espao em memria. Com a estratgia aqui adotada, se tivermos uma tela vazia, para cada linha s ser ocupada por um nico caracterer, no caso o caracter \0 terminador de cadeia de caracteres. Assim o espao reservado ser dinamicamente adaptado s necessidades da tarefa que tiver sendo processada. Observe ainda o uso da funo sleep() que faz o processamento se interromper por um determinado nmero de segundos. Esta funo est referenciada no arquivo de incluso dos.h sendo, portanto, dependente no s de implementao como tambm do sistema operacional. FRAGMENTAO Um fenmeno que surge se temos um programa que faz uso de alocao de memria de forma sistemtica, a chamada fragmentao. Este fenmeno pode nos levar a acreditar que no temos mais memria disponvel. Para melhor compreenso, vamos exemplificar. Suponhamos que voc inicialmente disponha de 100KB de memria e faa uma reserva de 30KB, seguida de outra de 50KB. Teremos 20KB de memria livre para outros usos. Se voc libera os primeiros 30KB, teremos, claro, o total de 50KB. Caso voc tente reservar neste momento 40KB, receber a indicao das funes de alocao de memria que no h espao disponvel. Observe que isto ocorre porque o espao alocado poder estar em posies contguas na memria. Portanto se voc fizer uso sistemtico de alocao dinmica de memria, faa um planejamento de seu uso durante o projeto do sistema.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ESTRUTURAS Estruturas so colees de variveis que podem ser referenciadas pelo mesmo nome. Estas variveis so ligadas logicamente. A forma geral a que se segue struct nome_da_estrutura { TIPO varivel1; TIPO varivel2; . . TIPO variveln; } estrutura1, estrutura2,...,estruturan; A referncia a cada elemento feita atravs do operador .(ponto), da seguinte forma : estrutura.elemento Chamemos a ateno que se for definida s uma estrutura, no h necessidade de nome_da_estrutura. Tambm, se voc quiser, poder declarar as estruturas atravs de seu nome, como se fosse um tipo mas antes escrevendo a palavra reservada struct. Exemplos: a) struct { char nome_do_ocupante[20]; int apartamento; char bloco; } conjunto_j_k; conjunto_j_k.nome_do_ocupante = "Edeunilde Braga"; conjunto_j_k.apartamento = 301; b) struct agenda { char nome[20]; char telefone[8]; char endereo[30]; char assunto[40]; char dia[9]; }; struct agenda comercial, pessoal; pessoal.nome = "Marieta Lanolina"; pessoal.telefone = "266-5672"; pessoal.endereo = " Anfilofio Benevides, 30"; pessoal.assunto = "Teatro as 21.00" pessoal.dia = "22/04/89"

Departamento de Cincia da Computao-UFF

Nmeros de pgina
c) struct pais { char municipio[20]; long int populao; char unidade_federativa[3]; long int populao_por_u_f; } argentina, brasil, paraguai, uruguai; brasil.municipio = "Niteroi"; brasil.populao = 1350000; brasil.unidade_federativa = "RJ"; brasil.populao_por_u_f = 13250000; Com estes exemplos, principalmente no caso de agenda, d a impresso de certa limitao j que aparentemente no se conseguiria cadastrar mais de uma pessoa. O que se pode fazer declarar um vetor de estruturas. Por exemplo, struct agenda pessoal_todo_ano[365]; A referncia aos elementos feita de maneira anloga, por exemplo pessoal_todo_ano[120].nome = "Briareu Titanita"; Voc poder, ainda, criar estruturas que contenham estruturas. Por exemplo, struct endr { char rua[20]; int nmero; char complemento[10]; }; struct dados { char nome[30]; struct endr endereo; }dados_pessoais; Voc referenciar os elementos da seguinte maneira dados_pessoais.endereo.rua = "Temostenes Guaranhaes"; Com a definio de uma estrutura de dados, fica, por exemplo, mais fcil e clara a definio de campos num determinado conjunto de informaes.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Estruturas e Manipulao de Bits Voc j viu que C pode manipular a nvel de bits e tambm pode usar as estruturas para facilitar o seu trabalho. Vamos supor que voc est comandando um dispositivo (uma impressora, um traador de grficos, etc) cujo controle seja feito pela manipulao dos bits 0, 1, 6 e 7, ficando os bits 2, 3, 4 e 5 sem funo. Podemos criar a seguinte estrutura: struct acionador { unsigned up: 1; unsigned down: 1; int : 4; int side: 2; }; As variveis declaradas como inteiras, so armazenadas em complemento de dois e o seu bit de mais alta ordem trabalha como bit de sinal. Portanto tenha cuidado quanto trabalhar com estruturas deste tipo; pode ocorrer fenmenos estranhos caso haja algum engano. Observemos que acionador uma struct de um byte. Alguns compiladores criam restries quanto o tamanho das variveis struct. Tambm aqui consideramos que a ordem de preenchimento da varivel a do bit de mais baixa ordem para o de mais alta. Podem haver variaes.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ESTRUTURAS E FUNES Pode surgir a pergunta: Como passar uma estrutura ou seus elementos para uma funo? Se voc que passar os elementos, deve proceder de maneira habitual j que cada elemento da estrutura uma varivel comum. Por exemplo, seja a estrutura struct teste { int i; float x; char ch; } teste1; Se voc que passar o elemento i para a funo fatorial(), deve ento escrever fatorial(teste1.i); A coisa se complica um pouco se queremos passar toda a estrutura. No caso do C padroK & R no h como fazer isto. O que se faz passar um ponteiro para a estrutura e seus elementos. Na verso ANSI C, tal operao permitida. Observe que estruturas no so vetores, apesar de trabalhados de forma semelhante pelas funes, j que o identificador de uma estrutura no por si um ponteiro para um objeto homogneo como vetores. Vamos supor que temos a mesma estrutura acima mas voc queira "passar toda a estrutura" para uma funo. A referencia dos elementos seria feita por ponteiros, da seguinte forma (*nome_da_estrutura).elemento_da_estrutura Os parnteses so necessrios devido a precedncia dos operadores. Vejamos um exemplo. #include <stdio.h> struct teste { int i; float x; char ch; } teste1; funcao(struct teste *e) { (*e).x = (*e).x * (*e).i; return((*e).x); } main() { puts(" Entre com um numero inteiro e um flutuante separados por branco"); scanf("%d %f",teste1.i,teste1.x); printf("sada da funcao %f",funcao(&teste1)); } Poderamos simplificar a escrita usando o operador seta que, na verdade, equivalente ao conjunto da atuao do operador * e o . em estruturas. Com este operador, a referncia a um elemento via ponteiro fica nome_da_estrutura->elemento_da_estrutura Reescrevendo o exemplo temos #include <stdio.h>
Departamento de Cincia da Computao-UFF

Nmeros de pgina
struct teste { int i; float x; char ch; } teste1; funcao(struct teste *e) { e->x = e->x * e->i; return(e->x); } main() { /* Decimo programa, segunda versao */ puts(" Entre com um numero inteiro e um flutuante separados por branco"); scanf("%d %f",teste1.i,teste1.x); printf("sada da funcao %f",funcao(&teste1)); } Assim fica menos tedioso de escrevermos as referncias entre os elementos da estrutura.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
UNIES Uma unies funciona de uma maneira semelhante a struct porm, as variveis selecionadas compartilham o mesmo espao em memria. Sempre reservado espao suficiente para a mais longa das variveis definidas. A idia, claro, poupar memria e organizar certas estruturas de dados. A forma de declarao a que se segue union NOME_DA UNIO { TIPO VARIVEL1; TIPO VARIVEL2; . . TIPO VARIVELn; } UNIO1,UNIO2,...,UNIOn; Novamente, NOME_DA_UNIO e UNIO1...UNIOn so opcionais. A referncia a um dos elementos feita de maneira anloga a das estruturas. Referenciando um elemento, fazemos uso do operador ponto, como abaixo UNIO1.ELEMENTO e tambm podemos manipula-las pela obteno do seu endereo via operador &. Podemos, ainda, ter como elementos da unio no s variveis como tambm estruturas. Vamos a um exemplo. Lembre-se que C tem a inteno de substituir o assembler, sempre que possvel. Vamos, ento, supor que voc v trabalhar num computador no qual a CPU trabalhe com bytes, palavras de dois bytes e palavras de quatro bytes e seus registradores sejam de quatro bytes. Observe que o contedo dos registadores pode ento ser trabalhados em bytes, palavras ou palavras duplas mas, o contedo do registrador ser um s. Podemos, ento, criar uma unio da seguinte forma union registrador { long int dword; int word; short int byte; }; A varivel union ter o tamanho do maior dos elementos contidos na sua declarao, que o long int de quatro bytes. A referncia ao byte de mais baixa ordem ser feita da seguinte forma registrador.byte = 10; Esta declarao ir atribuir ao byte de mais baixa ordem do registrador o valor 10. Da mesma forma, se queremos saber o valor do contedo total do registrador poderamos escrever printf(" Conteudo do registrador = %ld ",registrador.dword);

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Um exemplo no to sofisticado daremos abaixo. #include <stdio.h> #define CHAR 0 #define INT 1 #define FLOAT 2 #define DOUBLE 3 union Var { char c; int i; float f; double d; }; main() { union Var v; int tipo; printf(" Entre com o tipo de variavel "); tipo = getch(); printf("\n"); switch (tipo) { case CHAR: scanf(%c, &(v.c)); break; case INT : scanf( %d\n", &(v.i)); break; case FLOAT: scanf( %f\n", &(v.f)); break; case DOUBLE: scanf( %lf\n", &(v.d)); break: default : exit(0); } while(1); } Observe que temos uma varivel definida (tipo) que apesar de estar correta, est colocada de uma forma feia pois os valores que ela deve tomar so int mas no todos os inteiros representveis por int. A funo desta varivel servir para especificar um dos tipos de entrada e sua definio como est no documenta o que ocorre. Alm disto, no feita uma filtragem para impedir que entremos com valores invlidos. No final das contas, este mais um daqueles programas que no fazem nada a no ser esclarecer alguns aspectos da linguagem C.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
INICIALIZANDO ESTRUTURAS E UNIES Se tivermos estruturas ou unies globais, poderemos inicializa-las de forma similar a que usamos para vetores. Seja, por exemplo, uma estrutura chamada coordenada inicializada com as coodenadas de Niteri struct coordenada { float latitude, float longitude } = { 23.1, 43.14 } DETERMINANDO TAMANHOS DE UNIES, ESTRUTURAS E VARIVEIS Aqui veremos mais uma palavra reservada que, na verdade, uma funo que trabalha em tempo de compilao sendo esta a razo desta no estar numa biblioteca. Voc a utiliza da seguinte maneira sizeof(void) O valor devolvido por sizeof() ser o comprimento em bytes da varivel que esta entre parnteses declarada void por no ter a priori um tipo especfico. Voc poder colocar qualquer coisa entre estes parnteses, includo unies e estruturas. Um exemplo: include <stdio.h> int a,b; char letras[10] = "abcdef" main() { a = sizeof(letras); b = sizeof(a); printf(" %d %d ", a, b); } Em a teremos o nmero de elementos a cadeia de caracteres e em b teremos o tamanho da varivel a, ou seja, respectivamente 10 e 2. Alguns devem estranhar o resultado, j que letras s tem seis elementos (sete contando o nulo). Observem que sizeof() no d o espao ocupado de forma til mas sim o espao que foi reservado para a varivel. O uso desta funo em variveis conveniente quando queremos fazer programas que sejam independentes das mquinas. Por exemplo, em certos computadores o nmero inteiro "bsico" pode no ser o de dois bytes mas sim um inteiro de quatro bytes. Isto pode surgir em equipamentos nos quais o processador central seja de 32 bits, ou seja, 4 bytes. Portanto usando a funo sizeof(), podemos criar programas que se adaptam s mquinas onde estiverem sendo executados.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ENUMERAO Uma situao comum quando voc cria uma srie de dados que deve ser compreendidos como de um determinado tipo e ao mesmo tempo dar a eles valores que os distingam. Suponhamos que voc pretenda definir uma tabela de cores para trabalhar com uma interface grfica. Uma forma de fazer isto da seguinte forma int preto marrom vermelho laranja amarelo verde = 5, azul branco = 0, = 1, = 2, = 3, = 4, = 6, = 7;

Uma maneira elegante e poderosa de fazer o mesmo utilizando a palavra reservada enum. A forma de uso a que se segue enum nome { enum1, enum2, enum3,...}; onde nome designa o novo tipo com elementos enum1, enum2, enum3, etc, que so internamente representados por variveis do tipo int iniciando em zero e indo at n-1, onde n o nmero de elementos. Nosso exemplo acima ficaria escrito, atravs do enum como enum cores { preto, marrom, vermelho, laranja, amarelo, verde, azul, branco}; Observe que a nossa situao agora ainda mais favorvel j que podemos definir variveis do tipo cores. Por exemplo, se uma varivel deve ter as cor de fundo da tela e outra as cores das letras, poderamos escrever enum cores cor_de_fundo, cor_das_letras; Isto indiscutivelmente facilita nosso trabalho. Mas na verdade temos mais flexibilidade ainda. Em certas situaes pode no nos interessar que a numerao dos elementos do enum seja seqencial partir de 0. Digamos que criemos uma outra enumerao que represente os dias da semana desta forma que segunda-feira receba o valor 2, tera-feira valor 3 e assim por diante e o sbado valor 7 e domingo 8. Neste caso poderamos fazer o que se segue enum dias {segunda = 2, terca = 3, quarta = 4, quinta = 5, sexta = 6, sabado = 7, domingo = 8}; Mas podemos ser mais concisos escrevendo da seguinte maneira enum dias {segunda = 2, terca, quarta, quinta, sexta, sabado, domingo}; A varivel terca ter o valor de segunda mais um, quarta o valor de terca mais um e assim por diante. Resumindo: Voc tem ento trs opes de uso para o enum : a) enumerao automtica a partir de zero; b) enumerao forada atravs do operador =; c) enumerao forada inicialmente. Vamos pegar um exemplo dado anteriormente e (novamente!) modifica-lo:

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> enum Tipo { CHAR, INT, FLOAT, DOUBLE}; union Var { char c; int i; float f; double d; }; main() { union Var v; Tipo tipo; printf(" Entre com o tipo de variavel "); tipo = getch(); printf("\n"); switch (tipo) { case CHAR: scanf(%c, &(v.c)); break; case INT : scanf( %d\n", &(v.i)); break; case FLOAT: scanf( %f\n", &(v.f)); break; case DOUBLE: scanf( %lf\n", &(v.d)); break: default : exit(0); } while(1); } Em comparao com a outra verso do programa, temos uma escrita mais compacta, clara e o uso da enumerao para especificar melhor o que fazemos. A declarao de tipo no deixa dvidas para que esta varivel foi criada e quais os valores vlidos.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
DEFINIO DE TIPOS Aqui veremos mais uma palavra chave que nos permitir definir novos tipos de variveis. Definiremos um novo tipo de varivel da seguinte forma: typedef TIPO NOME; Voc poderia redefinir tipos j existentes como, por exemplo typedef float real; Com isso, voc poderia, a partir daqui, escrever real a,b; Mas typedef no se limita ao que foi mostrado. Voc poder criar estruturas de dados mais complexas e defini-las como um novo tipo. Por exemplo : typedef struct endr { char rua[20]; int numero; char bloco; }; Assim, podemos definir o seguinte tipo endr endereo; exatamente como usaramos para definir uma varivel qualquer. O efeito seria o mesmo que se tivssemos escrito struct endr endereo; As idias bsicas por traz do uso do typedef so facilitar a documentao do programa e podermos parametrizar o mesmo com finalidade de se usarmos uma varivel dependente de mquina, o que ser necessrio mudarmos no programa ao migrarmos de mquina para mquina, ser apenas os typedef. Ou ainda, criar tipos que se "adaptam" a determinadas particularidades do programa. Como exemplo disto, aqui temos um programa que calcula a distncia entre dois pontos mas que os dados de entrada podem ser pontos no plano ou no espao, isto sendo definido por um #define.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <math.h> #define _3D typedef struct ponto Ponto; struct ponto { float x, y; #ifdef _3D float z; #endif }; float distancia(Ponto p0, Ponto p1) { float d; d = (p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p0.y) * (p1.y - p0.y); #ifdef _3D d = d + (p1.z - p0.z) * (p1.z - p0.z); #endif return (sqrt(d)); } main() { Ponto pa, pb; pa.x = 1; pa.y = 2; pa.z = 1; pb.x = 0; pb.y = 1; pb.z = 5; printf(" d = %f", distancia(pa, pb)); }

Acima, alm do exemplo de utilizao do typedef, temos o uso do preprocessador de uma maneira at agora no apresentada. Aqui _3D est definido e, como conseqncia, o clculo ser com trs dimenses. Caso queira definir as operaes apenas no plano, basta deixar _3D sem definio.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
TRABALHANDO COM ARQUIVOS EM DISCO Funes de Alto Nvel Obviamente, C tambm pode trabalhar com arquivos em disco. Isto tambm, claro, deixado a responsabilidade de funes. Existem duas maneiras de acessarmos arquivos em disco. Uma atravs de funes de alto nvel e a outra atravs das funes de baixo nvel, estas ltimas chamadas as vezes de "tipo Unix". Aqui falaremos das de alto nvel. Mas, antes disto, falaremos de um tipo de varivel especial e duas constantes predefinidas teis e necessrias no trabalho de arquivos. O tipo denominado FILE, e atravs dele que identificamos os arquivos. Sua forma de uso dada abaixo FILE *ponteiro_arquivo1, *ponteiro_para_arquivo2,.....; Ainda temos as constantes : EOF - marcador de fim de arquivo. NULL - usada sob variados aspectos. As funes bsicas so as que se seguem: a)FILE *fopen(char *nome_arquivo, char *modo) Esta funo devolve um ponteiro para arquivo. O modo pode ser um dos abaixo: r - ler w- escrever a - adicionar ao final de arquivo j existente b - formato binrio Dependendo do compilador que voc esteja usando, poder haver outros modos. Voc poder agrupar mais de um modo simplesmente juntando as letras acima. Por exemplo, se o arquivo for do tipo que vai ser lido e modificado, use como modo rw (leitura e escrita). Caso o arquivo no exista, ele ser automaticamente criado. Se no for possvel abrir o arquivo, o ponteiro devolvido ser o NULL. b)int fclose(FILE *ponteiro_arquivo) Esta funo fecha o arquivo aberto por fopen(). A referencia a qual arquivo ser fechado dado pelo ponteiro que devolvido por fopen(). Devolve EOF se alguma anormalidade ocorrer. c)int getc(FILE *ponteiro_arquivo) Devolve um caracter do arquivo referenciado pelo ponteiro dado como parmetro. Se chegar ao final do arquivo, devolve o EOF. d)int putc(int caracter, FILE *ponteiro_caracter) Escreve o caracter dado como parmetro no arquivo referenciado pelo ponteiro dado como segundo parmetro. Devolve o caracter escrito se no houver problemas e EOF caso hajam.

e)int fseek(FILE *ponteiro_arquivo, long int deslocamento, int origem) Posiciona, para leitura ou escrita, o ponteiro que marcar o caracter a ser trabalhado pelas funesgetc () e putc(). Temos como parmetros um ponteiro para arquivo, o deslocamento a partir

Departamento de Cincia da Computao-UFF

Nmeros de pgina
do ponto origem e um parmetro que d a posio identificada como a origem Inicialmente a origem no incio do arquivo e existem alguns valores definidos para esta varivel : 0 - deslocamento a partir do incio do arquivo 2 - deslocamento a partir do fim do arquivo 1 - deslocamento a partir do ltimo deslocamento

Como de suspeitar pela tabela acima, a varivel deslocamento poder ser negativa. O acesso informao feita desta maneira chamada de acesso aleatrio ou acesso direto ou ainda acesso randmico (embora esta palavra no signifique nada tanto em portugus como em ingls) . Devolve zero se o deslocamento foi feito e diferente de zero caso haja algum problema. Damos agora um exemplo de como usar as funes acima. Simplesmente criaremos um arquivo denominado arquivo1 e escreveremos nele 100 letras a. #include <stdio.h> FILE *fp; main() { int i; char ch = 'a'; if ((fp = fopen(arquivo1,"w")) == NULL) { puts("No foi possivel abrir este arquivo\n"); exit(); } for (i = 0; i < 100; i++) putc(ch, fp); fclose(fp); } Observe que testamos se o ponteiro devolvido foi nulo. Caso no seja possvel criar o arquivo (falta de espao em disco, um disquete no se encontra no drive, etc.), a reao ser a impresso do aviso. Se insistirmos em gravar nestes casos, podemos ter reaes imprevisveis. Um programa que leria os dados escritos pelo programa anterior, dado abaixo. #include <stdio.h> FILE *fp; main() { int i; if ((fp = fopen(arquivo1,"r")) == NULL) { puts("Nao foi possivel abrir este arquivo\n"); exit(); } for (i = 0; i < 100; i++) putchar(getc(fp)); fclose(fp); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
Podemos ainda fazer um programa que leia um campo arbitrrio em vez de ler seqencialmente. Isto pode ser feito usando a funo fseek(). O programa abaixo, escreve num arquivo em disco uma frase que voc teclar. #include <stdio.h> FILE *fp; main() { char vetor[80]; if ((fp = fopen(arquivo1,"w")) == NULL) { puts("Nao foi possivel abrir este arquivo\n"); exit(); } gets(vetor); while (*vetor) putc(*vetor++, fp); fclose(fp); } A recuperao destes dados, pode ser feita de forma aleatria (ou direta) usando o programa que se segue #include <stdio.h> FILE *fp; main() { if ((fp = fopen(arquivo1,"r")) == NULL) { puts("Nao foi possivel abrir este arquivo\n"); exit(); } fseek(fp,20L,0); putchar(getc(fp)); fclose(fp); } Este programa ler o vigsimo caracter a partir do incio do arquivo. At o momento, sempre sabamos a quantidade de elementos que estvamos lendo. Muitas vezes, esta informao desconhecida e, sem querer, poderamos sair lendo informaes falsas. H uma maneira de ler determinadas informaes sem o perigo de atropelarmos outras. Basta verificar se chegamos ao final do arquivo comparando cada caracter lido com a constante pr-definida EOF (End Of File) que, como o nome diz, marca o fim do arquivo. A seguir um exemplo de uso.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> FILE *fp; main() { char ch; if ((fp = fopen(arquivo1,"r")) == NULL) { puts("Nao foi possivel abrir este arquivo\n"); exit(); } while (ch = getc(fp)) != EOF) putchar(ch); } Observemos mais uma vez que as entradas e sadas em C so voltadas para trabalho com caracteres e, nem sempre queremos nos preocupar com ler ou escrever valores em arquivos, fazendo a converso deles para caracteres. Temos, para estes casos, duas funes que facilitam o nosso trabalho: int fprintf( *FILE , *char, par1,..., parn); int fscanf( *FILE, *char, *par1, .., *parn); O funcionamento idntico printf() e scanf(). Obviamente, usar estas funes no trazem s vantagens. Elas so lentas e relativamente grandes. Se voc necessitar de velocidade ou economia de espao, pode ser que a comodidade saia caro. Caso velocidade seja a palavra chave, voc dever criar suas prprias funes especficas. Vamos a um exemplo de uso destas funes #include <stdio.h> FILE *fp; main() { int i = 1; char x = 'a'; if ((fp = fopen(arquivo1,"rw")) == NULL) { puts("Nao foi possivel abrir este arquivo\n"); exit(); } fprintf(fp, "%d%c", i, x); fscanf(fp, "%d%c", i, x); printf("\n inteiro %d, caracter %c \n", i, x); fclose(fp); } EXERCCIO I) Faa um programa que leia um arquivo e crie outro com extenso .sai, onde do arquivo original todo e qualquer caracter que no seja uma letra seja substitudo por um espao em branco. Dica : veja as funes do arquivo de incluso ctype.h.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
ARQUIVOS PADRES E COMO USAR A IMPRESSORA Mesmo que voc no trabalhe com nenhum arquivo de maneira explcita, sempre que um programa em C inicia a sua execuo, so abertos trs arquivos : stdin - entrada padro stdout - sada padro stderr - sada padro para erros Estes arquivos so, geralmente, associados a console mas podem ser "desviados" quando houver necessidade. O que permitir ou proibir este desvio, so o sistema operacional que voc estiver usando como tambm do compilador com o qual voc estiver trabalhando. Consulte os manuais tcnicos para saber de suas limitaes. Estes arquivos, da mesma forma que so abertos ao iniciar a execuo do programa, so fechados ao trmino do mesmo. C enxerga qualquer dispositivo como um arquivo sem se preocupar exatamente com "o que ele ". Isto facilita muito a vida do programador quando necessrio desviar a entrada ou a sada de um programa como j foi dito acima. Com as idias apresentadas, poderamos escrever putchar() a partir da funo putc(): putchar(ch) char ch; { putc(ch,stdout); } Existem outros arquivos padro. Um o arquivo correspondente a impressora normalmente denominado stdprn. Atravs dele, voc poder desviar a sada do seu programa para a impressora usando a funo fprintf() bastando especificar como arquivo o j citado stdprn. Assim, poderamos pensar na printf() como um caso especial da fprintf() quando o arquivo referenciado o stdout. Exemplificaremos este uso com o seguinte programa, uma variao da Torre de Hani com sada para impressora : #include <stdio.h> #define TITULO "\nTorre de Hanoi. Digite o nro. de discos." char posicao[][11] = {"a esquerda","o meio","a direita"}; void mova(n, daonde, auxiliar, praonde) short n, daonde, auxiliar, praonde; { if(n != 1) mova(n - 1, daonde, praonde, auxiliar); fprintf(stdprn, "Mova o disco d%-s para %-s", posicao[daonde], posicao[praonde]); if(n != 1) mova(n - 1, auxiliar, daonde, praonde); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main() { enum {ESQUERDA, MEIO, DIREITA}; char *cadeia[20]; short discos; while ((puts(titulo), discos = atoi(gets(cadeia))) != 0) { puts("Coloque os discos a esquerda e faca os movimentos"); fprintf(stdprn, "Execute os seguintes movimentos"); mova(discos, ESQUERDA, MEIO, DIREITA); puts("Ok! Pilha da esquerda totalmente transferida para direita"); } } Note as diferenas desta verso e da anterior. Alm do uso do fprintf() temos o uso do enum para definir ESQUERDA, MEIO e DIREITA. Alm disto, veja a soluo para a "monstruosidade" da atribuio de uma cadeia de caracteres um ponteiro sem reservar memria para tal: Um simples define. Resultado: um programa mais limpo e mais claro.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
FUNES DE BAIXO NVEL Temos aqui um outro conjunto de funes de entrada e sada para trabalho com arquivos. A denominao "Baixo Nvel", obviamente, no tem nenhum carretar pejorativo. A razo desta denominao est no fato que no conjunto anterior de funes (as de "Alto Nvel"), voc no se preocupava de especificar e separar uma determinada regio de memria para servir de rea intermediria entre seu programa e o arquivo em disco (esta rea de memria muitas vezes chamada de Buffer). No conjunto de funes que descreveremos abaixo (as de "Baixo Nvel"), deveremos fazer a especificao destas reas intermedirias. Em troca de um pouco mais de trabalho, teremos maior velocidade, flexibilidade e uma atuao otimizada. Devemos chamar a ateno para o fato que estas funes no trabalham com ponteiros associados aos arquivos mas com inteiros denominados "descritores". Vamos s funes: a) int creat(char *nome_arquivo, int modo_arquivo) Esta funo permite criar um arquivo que ter o nome nome_arquivo e ser aberto num modo especificado por modo_do_arquivo. O padro ANSI tem definidos vrios modos entre os quais os abaixo : O_RDONLY O_WRONLY O_RDWR - somente para leitura - somente para gravao - para leitura e gravao

As definies destes modos esto nos arquivos <fcntl.h> (UNIX V) ou <sys/file.h> (BSD). Devolve um inteiro (o descritor) associado ao arquivo criado. Se o arquivo no puder ser criado, devolvido -1. b) int open(char *nome_arquivo, int modo) Abre um arquivo j existente num determinado modo especificado como os acima mostrados em creat (). Devolve o descritor do arquivo aberto. Se o arquivo no puder ser aberto, devolvido -1. OBS: Dependendo do compilador que voc estiver usando, a funo open() poder fazer as vezes o papel da funo creat(). c) int close(int descritor) Fecha o arquivo associado ao descritor dado como argumento. Se no for possvel fechar o arquivo solicitado, ser devolvido -1. d) int read(int descritor, void *buffer, int tamanho_do_buffer) Le informaes do arquivo em disco para a rea de memria especificada por buffer. O tamanho desta rea especificada por tamanho_do_buffer. Caso a leitura seja feita, a funo devolver o nmero de caracteres lidos. Caso haja algum problema, ser devolvido -1. Caso o valor devolvido seja 0 (zero), isto indicar que o arquivo chegou ao seu final. e) int write(int descritor, void *buffer, int tamanho_do_buffer) Escreve informaes do arquivo em disco identificado pelo descritor para a rea de memria especificada por buffer. O tamanho desta rea dado por tamanho_do_buffer. Se no houver nenhum problema ao se escrever no arquivo o pretendido, a funo devolver o nmero de caracteres escritos. Caso haja algum problema, ser devolvido -1. *OBS : Poder haver restries quanto ao tamanho do buffer nas funes read() e write(). Pode ser que o tamanho do espao reservado seja um mltiplo de uma determinada potncia de dois ou algo anlogo. Em caso de dvida, consulte seus manuais.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
f) long lseek(int descritor, long deslocamento, int origem) Faz o acesso aleatrio ao arquivo associado ao descritor fornecido. O deslocamento ser contado a partir dos valores tomados pelo referenciado em origem com os seguintes efeitos: origem Efeito 0 deslocamento em relao ao incio do arquivo 1 deslocamento em relao a posio atual 2 deslocamento em relao ao final do arquivo Caso no seja possvel o acesso ao arquivo, ser devolvido um -1. g) int unlink(char *nome_arquivo) Apaga o arquivo referenciado por nome_arquivo. Caso no seja possvel apagar o arquivo, ser devolvido -1. Vamos a um exemplo de utilizao destas funes para compreendermos melhor o seu funcionamento. #include <stdio.h> #include <fcntl.h> #define BUFFERSIZE 64 main() { int desc; char buffer[64]; int i; if(( desc = open("arquivo1", O_WRONLY)) == -1) { puts(" Nao foi possivel abrir este arquivo "); exit(); } puts(" Entre com um texto "); gets(buffer); puts(" Gravando em disco "); write(desc, buffer, BUFFERSIZE); close(desc); } Este programa escreve em disco uma mensagem dada pelo usurio. O tamanho do buffer foi escolhido aqui de maneira arbitrria. Agora vamos a outro pequeno programa que recupera a informao gravada pelo programa anterior.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
#include <stdio.h> #include <fcntl.h> #define BUFFERSIZE 64 main() { int desc; char buffer[64]; int i; if(( desc = open("arquivo1", O_RDONLY)) == -1) { puts(" Nao foi possivel abrir este arquivo "); exit(); } puts(" Lendo a frase no disco"); read(desc, buffer, BUFFERSIZE); puts(" frase dada inicialmente: "); puts(buffer); close(desc); } Um exemplo mais complexo dado abaixo, onde temos um programa que codifica um arquivo dado. Observe que este programa envolve vrios conceitos j vistos. #include <stdio.h> #include <string.h> #include <fcntl.h> #define BUFSIZE 16384 #define KEYSIZE 16 #define MENSAGEM1 Nao pode ser aberto o arquivo #define MENSAGEM2 Nao pode ser criado o arquivo int fdin, fdout; /* descritores */ int count; char *inbuf, *key; encrip(char *buffer, int num, char *code) { /* funcao codificadora */ int i, keylength; keylength = strlen(code); for (i = 0; i <= num; i++) buffer[i] = buffer[i] ^ code[i % keylength]; }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
main(argc, argv) int argc; char *argv[]; { int i, n; inbuf = (char *) malloc(BUFSIZE); key = (char *) malloc(KEYSIZE);

if (argc < 4) { puts(" encrip <arquivo original> <novo arquivo> <chave1>"); exit(); /* emite a forma de uso correto do programa, caso o usuario o tenha */ /* feito de maneira incorreta */ } if ((fdin = open(argv[1],O_RDONLY)) == -1) { printf(" MENSAGEM1 %s", nome1); exit(1); } if ((fdout = creat(argv[2],0_WRONLY)) == -1) { printf(" MENSAGEM2 %s", nome2); exit(1); } do { printf(" Lendo o arquivo a ser processado "); count = read(fdin, inbuf, BUFSIZE); printf(" Gerando o arquivo processado."); write(fdout, inbuf, count); } while(count == BUFSIZE);

printf(" OK "); free(key); /* liberando area de memoria */ free(inbuf); close(fdin); close(fdout); }

Departamento de Cincia da Computao-UFF

Nmeros de pgina
O processo de codificao feito atravs de manipulao de bits dos caracteres do arquivo de entrada. Isto feito usando o operador lgico ou exclusivo (ver o captulo sobre operadores e o apndice I). Fazemos uma operao lgica entre cada caracter do texto a ser codificado e uma ou mais cadeias de caracteres chamadas chaves. Aps esta operao, gravamos o arquivo codificado. A nica maneira de conseguirmos recuperar a informao codificada executando novamente o programa e entrando com as chaves de codificao. Portanto, o arquivo codificado s se torna acessvel a quem souber das chaves. Observo ainda que este programa, no processo de codificao, pode gerar o caracter EOF. Logo, no processo de decodificao poderemos ter uma perda de informao. EXERCCIO I) Suponha que os caracteres do seu arquivo a ser codificado se restrinjam ao cdigo ASCII. Modifique o programa de modo que no se perca informao pela possvel gerao do EOF durante o processo de codificao. Dica: lembre-se que a tabela ASCII s usa os sete bits menos significativos para os seus cdigos.

Departamento de Cincia da Computao-UFF

Nmeros de pgina
APNDICE I) Tabelas de funes lgicas a) ~ (Negao ou No Lgico) 0~1 1~0 b) & (E Lgico) 0&00 0&10 1&00 1&11 c) | (Ou Lgico) 0|00 0|11 1|01 1|11 d) ^ (Ou Exclusivo) 0^00 0^11 1^01 1^10

Departamento de Cincia da Computao-UFF

Vous aimerez peut-être aussi