Você está na página 1de 72

Programar em C/Estudo

Programar em C/Estudo
PROGRAMAR EM C
Manual autodidacta,

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

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

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

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

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

Programar em C/Estudo

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

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

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

char unsigned char signed char int unsigned int signed int short int unsigned short int signed short int long int signed long int unsigned long int float double long double

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

Programar em C/Estudo

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

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

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

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

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

| 456.67| | 2.67| |Ola |

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

Programar em C/Estudo >= < <= == != Maior ou igual a Menor do que Menor ou igual a Igual a Diferente de

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

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

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

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

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

Programar em C/Estudo

10

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

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

11

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

Programar em C/Estudo Se a condio for avaliada como zero, a declarao no ser executada if (condio) declarao_1; else declarao_2; Se a condio for avaliada como diferente de zero, a declarao_1 ser executada. caso contrrio ser a declarao_2. Repare que estamos a garantir que uma das declaraes ser sempre executada if (condio_1) declarao_1; else if (condio_2) declarao_2; . else if (condio_n) declarao_n; else declarao_default o programa comea a testar as condies comeando pela 1 e continua a testar at que ele ache uma expresso cujo resultado d diferente de zero. Neste caso ele executa a declarao correspondente. S uma declarao ser executada, ou seja, s ser executada a declarao equivalente primeira condio que der diferente de zero. A ltima declarao (default) a que ser executada no caso de todas as condies darem zero e opcional. Podemos ter ainda os if aninhados que basicamente um if dentro do outro. #include <stdio.h> int main () { int num; printf ("Digite um numero: "); scanf ("%d",&num); if (num==10) { printf ("\n\nVoce acertou!\n"); printf ("O numero e igual a 10.\n"); } else { if (num>10) { printf ("O numero e maior que 10."); } else { printf ("O numero e menor que 10."); } } return 0; } Mais abreviaturas!!! Continuo a dizer que uma estupidez isto das abreviaturas para no escrever uns poucos de caracteres!!

12

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

13

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

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

Programar em C/Estudo { case 9: printf ("\n\nO numero e igual a 9.\n"); break; case 10: printf ("\n\nO numero e igual a 10.\n"); break; case 11: printf ("\n\nO numero e igual a 11.\n"); break; default: printf ("\n\nO numero nao e nem 9 nem 10 nem 11.\n"); } return 0; }

14

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

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

Programar em C/Estudo O ponto-e- vrgula final obrigatrio. A estrutura do-while executa a declarao, testa a condio e, se esta for verdadeira, volta para a declarao. Garante que a declarao ser executada pelo menos uma vez. Um dos usos da estrutura do-while em menus #include <stdio.h> #include <stdlib.h> int main () { int i; do { printf ("\n\nEscolha a fruta pelo numero:\n\n"); printf ("\t(1)...Mamao\n"); printf ("\t(2)...Abacaxi\n"); printf ("\t(3)...Laranja\n\n"); scanf("%d", &i); } while ((i<1)||(i>3)); switch (i) { case 1: printf ("\t\tVoce escolheu Mamao.\n"); break; case 2: printf ("\t\tVoce escolheu Abacaxi.\n"); break; case 3: printf ("\t\tVoce escolheu Laranja.\n"); break; } system (pause); return 0; }

15

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

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

16

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

O Comando continue
Quando o comando continue encontrado, o loop pula para a prxima iterao, sem o abandono do loop. melhor ver o exemplo:
#include <stdio.h> #include <stdlib.h> int main() { int opcao; while (opcao != 5) { printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5)||(opcao <1)) continue; switch (opcao) { case 1: printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); /* se Opcao invalida: volta ao inicio do loop */

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

17

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

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

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

Programar em C/Estudo

18

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

multidimensionais
podemos ter ainda conjunto de variveis multidimensionais. tipo_da_varivel nome_da_varivel [tam1][tam2] ... [tamN]; onde a iniciao : tipo_da_varivel nome_da_varivel [tam1][tam2] ... [tamN] = {lista_de_valores}; float vect [6] = { 1.3, 4.5, 2.7, 4.1, 0.0, 100.1 }; int matrx [3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; char str [10] = { 'J', 'o', 'a', 'o', '\0' }; char str [10] = "Joao"; char str_vect [3][10] = { "Joao", "Maria", "Jose" }; Podemos, em alguns casos, inicializar matrizes das quais no sabemos o tamanho a priori. O compilador C vai, neste caso verificar o tamanho do que voc declarou e considerar como sendo o tamanho da matriz. Isto ocorre na hora da compilao e no poder mais ser mudado durante o programa

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

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

19

/* Funo simples: s imprime Ol! */

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

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

20

/* Multiplica 3 numeros */

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

Programar em C/Estudo

21

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

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

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

22

Variveis locais globais e passagem de parmetros por valor e referncia


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

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

Programar em C/Estudo
num=num*num; return num; } /*retorna o num*/

23

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

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

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

24

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

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

Operaes com ponteiros


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

Programar em C/Estudo *(p+15); o contedo do ponteiro 15 posies adiante

25

PONTEIROS COMO PARAMENTROS DE FUNES


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

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

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

26

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

27

Os Argumentos argc e argv


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

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

28

Recursividade
Uma funo pode chamar a si prpria. Uma funo assim chamada funo recursiva #include <stdio.h> #include <stdlib.h> int fat(int n) { if (n) return n*fat(n-1); else return 1; } int main() { int n; printf("\n\nDigite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d e' %d", n, fat(n)); system ("pause"); return 0; }

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

Programar em C/Estudo

29

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

1.A Diretiva #include


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

2.A Diretiva #Define


#define nome_da_macro sequncia_de_caracteres Toda vez que ele encontrar o nome_da_macro no programa a ser compilado, ele deve substitu-lo pela sequncia_de_caracteres fornecida. #include <stdio.h> #define PI 3.1416 #define VERSAO "2.02" int main () { printf ("Programa versao %s",VERSAO); printf ("O numero pi vale: %f",PI); return 0; } Outro exemplo: #define max(A,B) ((A > B) ? (A):(B)) #define min(A,B) ((A < B) ? (A):(B)) x = max(i,j); y = min(t,r);

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

30

3. A Diretivas #undef
A diretiva #undef tem a seguinte forma geral: #undef nome_da_macro Ela faz com que a macro que a segue seja apagada da tabela interna que guarda as macros.O compilador passa a partir deste ponto a no conhecer mais esta macro.

4. As Diretivas #ifdef e #endif


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

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

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

Programar em C/Estudo

31

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

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

Programar em C/Estudo

32

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

Ns podemos generalizar o buble sort mais

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

33

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

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

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

34

/ #define MAX 10 int a[MAX]; int main() { int i,t,x,y; for (i=0; i < MAX; i++) { a[i]=rand(); printf("%d\n",a[i]); } bubble_sort(MAX,a); printf("--------------------\n"); for (i=0; i < MAX; i++) printf("%d\n",a[i]);

/* fill array */

/* print sorted array */

Programar em C/Estudo } Como vimos agora s chamar pela biblioteca que criamos, sempre e quando quisermos.

35

Entradas e Sadas Padronizadas


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

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

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

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

Programar em C/Estudo printf("Entre com o seu nome"); gets(buffer); printf("O nome : %s", buffer); return 0; } se o usurio digitar mais do que 10 caracteres incluindo o "\0", os caracteres adicionais sero colocados na rea de memria subsequente ocupada por ela, escrevendo uma regio de memria que no est reservada string. Este efeito conhecido como "estouro de buffer" e pode causar problemas imprevisveis. Uma forma de se evitar este problema usar a funo fgets (vamos utiliz-la mais tarde)

36

Funo sprintf e sscanf


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

a varivel i "impressa" em string1. Alm da representao de i como uma string, string1 tambm conter "Valor de i=" . #include <stdio.h> #include <stdlib.h> int main() { int i, j, k; char string1[]= "10 20 30"; sscanf(string1, "%d %d %d", &i, &j, &k); printf("Valores lidos: %d, %d, %d", i, j, k); system ("pause"); return 0; }

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

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

Programar em C/Estudo

37

Funo putc
A funo putc a primeira funo de escrita de arquivo que veremos. Seu prottipo : int putc (int ch,FILE *fp); Escreve um caractere no arquivo.O programa a seguir l uma string do teclado e escreve-a, caractere por caractere em um arquivo em disco (o arquivo arquivo.txt, que ser aberto no diretrio corrente).
#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char string[100]; int i; fp = fopen("arquivo.txt","w"); if(!fp) { printf( "Erro na abertura do arquivo"); exit(0); } printf("Entre com a string a ser gravada no arquivo:"); gets(string); for(i=0; string[i]; i++) putc(string[i], fp); /* Grava a string, caractere a caractere */ fclose(fp); system ("pause"); return 0; } /* Arquivo ASCII, para escrita */

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

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

strcpy
strcpy (string_destino,string_origem);

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

Programar em C/Estudo

38

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

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

Abrindo e Fechando um Arquivo


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

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

Programar em C/Estudo

39

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

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

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


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

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

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

anterior ser destrudo. Se no existir, ser criado.


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

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

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

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

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

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

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

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

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

40

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

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

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

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

Programar em C/Estudo
return 0; }

41

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

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

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

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

42

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

Verifique o exemplo.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *p; char c, str[30], frase[80] = "Este e um arquivo chamado: "; int i; printf("\n\n Entre com um nome para o arquivo:\n"); gets(str); if (!(p = fopen(str,"w"))) { /* Le um nome para o arquivo a ser aberto: */ /* Caso ocorra algum erro na abertura do arquivo..*/

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

43

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

/* Le um novo caracter no arquivo */

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

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

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

Programar em C/Estudo acesso a um arquivo teve sucesso, de modo que consigamos garantir a integridade dos nossos dados. Na maioria dos casos, se um arquivo pode ser aberto, ele pode ser lido ou gravado. Porm, existem situaes em que isto no ocorre. Por exemplo, pode acabar o espao em disco enquanto gravamos, ou o disco pode estar com problemas e no conseguimos ler, etc. Uma funo que pode ser usada em conjunto com ferror() a funo perror() (print error), cujo argumento uma string que normalmente indica em que parte do programa o problema ocorreu. #include <stdio.h> #include <stdlib.h> int main() { FILE *pf; char string[100]; if((pf = fopen("arquivo.txt","w")) ==NULL) { printf("\nNao consigo abrir o arquivo ! "); exit(1); } do { printf("\nDigite uma nova string. Para terminar, digite <enter>: "); gets(string); fputs(string, pf); putc('\n', pf); if(ferror(pf)) { perror("Erro na gravacao"); fclose(pf); exit(1); } }while (strlen(string) > 0); fclose(pf); }

44

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

Programar em C/Estudo

45

8. fwrite
A funo fwrite() funciona como a sua companheira fread(), porm escrevendo no arquivo. Seu prottipo : unsigned fwrite(void *buffer,int numero_de_bytes,int count,FILE *fp); A funo retorna o nmero de itens escritos. Este valor ser igual a count a menos que ocorra algum erro. O exemplo abaixo ilustra o uso de fwrite e fread para gravar e posteriormente ler uma varivel float em um arquivo binrio.
#include <stdio.h> #include <stdlib.h> int main() { FILE *pf; float pi = 3.1415; float pilido; if((pf = fopen("arquivo.bin", "wb")) == NULL) /* Abre arquivo binrio para escrita */ { printf("Erro na abertura do arquivo"); exit(1); } if(fwrite(&pi, sizeof(float), 1,pf) != 1) /* Escreve a varivel pi */

printf("Erro na escrita do arquivo"); fclose(pf); /* Fecha o arquivo */

if((pf = fopen("arquivo.bin", "rb")) == NULL) /* Abre o arquivo novamente para leitura */ { printf("Erro na abertura do arquivo"); exit(1); } if(fread(&pilido, sizeof(float), 1,pf) != 1) printf("Erro na leitura do arquivo"); printf("\nO valor de PI, lido do arquivo e': %f", pilido); fclose(pf); return 0; } /* Le em pilido o valor da varivel armazenada anteriormente */

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

Programar em C/Estudo

46

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

0 1 2

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

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

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

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

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

47

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

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

Programar em C/Estudo
} fclose(p); return 0; }

48

Tipos de Dados Avanados


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

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

Programar em C/Estudo

49

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

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

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

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

50

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

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

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

51

Ponteiros para Funes


O C permite que acessemos variveis e funes atravs de ponteiros! Podemos ento fazer coisas como, por exemplo, passar uma funo como argumento para outra funo. Um ponteiro para uma funo tem a seguinte declarao: tipo_de_retorno (*nome_do_ponteiro)(); ou tipo_de_retorno (*nome_do_ponteiro)(declarao_de_parmetros); Repare nos parnteses que devem ser colocados obrigatoriamente. Se declaramos: tipo_de_retorno * nome(declarao_de_parmetros); Estaramos, na realidade, declarando uma funo que retornaria um ponteiro para o tipo especificado. Porm, no obrigatrio se declarar os parmetros da funo. Veja um exemplo do uso de ponteiros para funes:
#include <stdio.h> #include <string.h> void PrintString (char *str, int (*func)(const char *)); main (void) { char String [20]="Curso de C."; int (*p)(const char *); p=puts; /* Declaracao do ponteiro para funo Funcao apontada e' inteira e recebe como parametro uma string constante */

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

PrintString (String, p); return 0; }

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

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

Programar em C/Estudo decrementar um ponteiro para funo.

52

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

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

Programar em C/Estudo
}

53

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

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


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

54

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

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

Programar em C/Estudo
p[i] = i*i; ... return 0; }

55

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

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

Programar em C/Estudo
return 0; }

56

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

Alocao Dinmica de Vetores e Matrizes


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

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

57

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

Alocao Dinmica de Matrizes


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

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

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

58

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

int i, j; ... mat = Alocar_matriz_real (l, c); for (i = 0; i < l; i++) for ( j = 0; j < c; j++) mat[i][j] = i+j; ... /* outros comandos utilizando mat[][] normalmente */ /* outros comandos, inclusive inicializacao para l e c */

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

Programar em C/Estudo

59

Estruturas - Primeira parte


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

Criando
Para se criar uma estrutura usa-se o comando struct. Sua forma geral : struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variveis_estrutura; O nome_do_tipo_da_estrutura o nome para a estrutura. As variveis_estrutura so opcionais e seriam nomes de variveis que o usurio j estaria declarando e que seriam do tipo nome_do_tipo_da_estrutura. Um primeiro exemplo: struct est{ int i; float f; } a, b; Neste caso, est uma estrutura com dois campos, i e f. Foram tambm declaradas duas

Programar em C/Estudo variveis, a e b que so do tipo da estrutura, isto , a possui os campos i e f, o mesmo acontecendo com b. Vamos criar uma estrutura de endereo: struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; Vamos agora criar uma estrutura chamada ficha_pessoal com os dados pessoais de uma pessoa: struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; Vemos, pelos exemplos acima, que uma estrutura pode fazer parte de outra ( a struct tipo_endereco usada pela struct ficha_pessoal).

60

Usando
Vamos agora utilizar as estruturas declaradas na seo anterior para escrever um programa que preencha uma ficha. #include <stdio.h> #include <string.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; int main (void) { struct ficha_pessoal ficha;

Programar em C/Estudo strcpy (ficha.nome,"Luiz Osvaldo Silva"); ficha.telefone=4921234; strcpy (ficha.endereco.rua,"Rua das Flores"); ficha.endereco.numero=10; strcpy (ficha.endereco.bairro,"Cidade Velha"); strcpy (ficha.endereco.cidade,"Belo Horizonte"); strcpy (ficha.endereco.sigla_estado,"MG"); ficha.endereco.CEP=31340230; return 0; } O programa declara uma varivel ficha do tipo ficha_pessoal e preenche os seus dados. O exemplo mostra como podemos acessar um elemento de uma estrutura: basta usar o ponto (.). Assim, para acessar o campo telefone de ficha, escrevemos: ficha.telefone = 4921234; Como a struct ficha pessoal possui um campo, endereco, que tambm uma struct, podemos fazer acesso aos campos desta struct interna da seguinte maneira: ficha.endereco.numero = 10; ficha.endereco.CEP=31340230; Desta forma, estamos acessando, primeiramente, o campo endereco da struct ficha e, dentro deste campo, estamos acessando o campo numero e o campo CEP.

61

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

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

printf(" Os valores armazenasdos na segunda struct sao :

Programar em C/Estudo
}

62

So declaradas duas estruturas do tipo est1, uma chamada primeira e outra chamada segunda. Atribuem-se valores aos dois campos da struct primeira. Os valores de primeira so copiados em segunda apenas com a expresso de atribuio: segunda = primeira; Todos os campos de primeira sero copiados na segunda. Note que isto diferente do que acontecia em vetores, onde, para fazer a cpia dos elementos de um vetor em outro, tnhamos que copiar elemento por elemento do vetor. Nas structs muito mais fcil! Porm, devemos tomar cuidado na atribuio de structs que contenham campos ponteiros. Veja abaixo: #include <stdio.h> #include <string.h> #include <stdlib.h> struct tipo_end { char *rua; /* A struct possui um campo que um ponteiro */ int numero; }; int main() { struct tipo_end end1, end2; char buffer[50]; printf("\nEntre o nome da rua:"); gets(buffer); /* Le o nome da rua em uma string de buffer */ end1.rua = malloc((strlen(buffer)+1)*sizeof(char)); /* Aloca a quantidade de memoria suficiente para armazenar a string */ strcpy(end1.rua, buffer); /* Copia a string */ printf("\nEntre o numero:"); scanf("%d", &end1.numero); end2 = end1; /* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */ printf("Depois da atribuicao:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua,end1.numero,end2.rua, end2.numero); strcpy(end2.rua, "Rua Mesquita"); /* Uma modificacao na memoria apontada por end2.rua causara' a modificacao do que e' apontado por end1.rua, o que, esta' errado !!! */ end2.numero = 1100; /* Nesta atribuicao nao ha problemas */ printf(" \n\nApos modificar o endereco em end2:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua, end1.numero, end2.rua, end2.numero); } Neste programa h um erro grave, pois ao se fazer a atribuio end2 = end1, o campo

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

63

Passando para funes


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

Ponteiros
Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia ser declarado um ponteiro para as estruturas de ficha que estamos usando nestas sees: struct ficha_pessoal *p; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. Para us-lo, haveria duas possibilidades. A primeira apont-lo para uma varivel struct j existente, da seguinte maneira: struct ficha_pessoal ficha; struct ficha_pessoal *p; p = &ficha; A segunda alocando memria para ficha_pessoal usando, por exemplo, malloc():
#include <stdlib.h> main() { struct ficha_pessoal *p; int a = 10; /* Faremos a alocacao dinamica de 10 fichas pessoais */

p = malloc (a * sizeof(struct ficha_pessoal)); p[0].telefone = 3443768; free(p); } /* Exemplo de acesso ao campo telefone da primeira ficha apontada por p */

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

64

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

Programar em C/Estudo { int graus; float radianos; }; int main() { union angulo ang; char op; printf("\nNumeros em graus ou radianos? (G/R):"); scanf("%c",&op); if (op == GRAUS) { ang.graus = 180; printf("\nAngulo: %d\n",ang.graus); } else if (op == RAD) { ang.radianos = 3.1415; printf("\nAngulo: %f\n",ang.radianos); } else printf("\nEntrada invalida!!\n"); } Temos que tomar o maior cuidado pois poderamos fazer:
#include <stdio.h> union numero { char Ch; int I; float F; }; main (void) { union numero N; N.I = 123; printf ("%f",N.F); return 0; } /* Vai imprimir algo que nao e' necessariamente 123 ...*/

65

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

Programar em C/Estudo

66

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

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

Programar em C/Estudo fosse o uso de sizeof. Veja o exemplo: #include <stdio.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; int main(void) { struct ficha_pessoal *ex = malloc(sizeof(struct ficha_pessoal)); ... free(ex); return 0; }

67

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

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

68

Uma aplicao de structs: as listas simplesmente encadeadas


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

Note que esta struct possui, alm dos campos de dados codigo e preco, um campo adicional que um ponteiro para uma struct do tipo Produto. este campo que ser utilizado para apontar para o prximo n da lista encadeada. O programa a seguir faz uso desta struct, atravs de um novo tipo criado por um typedef, para criar uma lista de produtos de uma loja: #include <stdio.h> #include <stdlib.h> typedef struct tipo_produto { /* Estrutura que ser usada para criar os ns da lista */

Programar em C/Estudo int codigo; */ double preco; */ struct tipo_produto *proximo; /* Proximo elemento da lista encadeada de Produtos */ } TProduto; void inserir(TProduto **cabeca); /* Prototipos das funcoes para inserir e listar produtos */ void listar (TProduto *cabeca); int main() { TProduto *cabeca = NULL; /* Ponteiro para a cabeca da lista */ TProduto *noatual; /* Ponteiro a ser usado para percorrer a lista no momento de desalocar seus elementos*/ char q; /* Caractere para receber a opcao do usuario */ do { printf("\n\nOpcoes: \nI -> para inserir novo produto;\nL -> para listar os produtos; \nS -> para sair \n:"); scanf("%c", &q); /* Le a opcao do usuario */ switch(q) { case 'i': case 'I': inserir(&cabeca); break; case 'l': case 'L': listar(cabeca); break; case 's': case 'S': break; default: printf("\n\n Opcao nao valida"); } fflush(stdin); /* Limpa o buffer de entrada */ } while ((q != 's') && (q != 'S') ); noatual = cabeca; /* Desaloca a memoria alocada para os elementos da lista */ while (noatual != NULL) { cabeca = noatual->proximo; free(noatual); noatual = cabeca; } } void listar (TProduto *noatual) elementos presentes na lista encadeada */ /* Lista todos os /* Preco do produto /* Codigo do produto

69

Programar em C/Estudo { int i=0; while( noatual != NULL) /* Enquanto nao chega no fim da lista */ { i++; printf("\n\nProduto numero %d\nCodigo: %d \nPreco:R$%.2lf", i, noatual->codigo, noatual->preco); noatual = noatual->proximo; /* Faz noatual apontar para o proximo no */ } } void inserir (TProduto **cabeca) /* Funcao para inserir um novo no, ao final da lista */ { TProduto *noatual, *novono; int cod; double preco; printf("\n Codigo do novo produto: "); scanf("%d", &cod); printf("\n Preco do produto:R$"); scanf("%lf", &preco); if (*cabeca == NULL) /* Se ainda nao existe nenhum produto na lista */ { *cabeca = malloc(sizeof(TProduto)); /* cria o no cabeca */ (*cabeca)->codigo = cod; (*cabeca)->preco = preco; (*cabeca)->proximo = NULL; } else { noatual = *cabeca; /* Se ja existem elementos na lista, deve percorre-la ate' o seu final e inserir o novo elemento */ while(noatual->proximo != NULL) noatual = noatual->proximo; /* Ao final do while, noatual aponta para o ultimo no */ novono = (TProduto *) malloc(sizeof(TProduto)); /* Aloca memoria para o novo no */ novono->codigo = cod; novono->preco = preco; novono->proximo = NULL; noatual->proximo = novono; /* Faz o ultimo no apontar para o novo no */ }

70

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

71

Fontes e Editores da Pgina

72

Fontes e Editores da Pgina


Programar em C/Estudo Fonte: http://pt.wikibooks.org/w/index.php?oldid=214409 Contribuidores: Algum, Edudobay, EvertonS, Jorge Morais, Lightningspirit, Marcos Antnio Nunes de Moura, 19 edies annimas

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