Programar em C/Estudo

1

Programar em C/Estudo
PROGRAMAR EM C
Manual autodidacta, …

Começar 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 símbolo “#” é uma chamada de atenção 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-cabeçalho stdio.h (std = standard, padrão em inglês; io = Input/Output, entrada e saída ==> stdio = Entrada e saída padronizadas; entrada temos o keyboard e saída temos o ecrã). A terminação .h vem de header e normalmente tratam-se de bibliotecas. Existem outros arquivos para além do stdlib.h, temos a biblioteca de matemática, a de tempo, etc …. Podemos ver na parte dos anexos algumas que existem e com as funções de cada uma. Mas, nós próprios podemos criar uma biblioteca. E até podemos comprar bibliotecas que existem por aí. Bibliotecas são códigos que alguns programadores fizeram antes. As que citamos primeiramente são as standard, são aquelas que têm as funcionalidades básicas. Veja você que precisamos da biblioteca até para escrever no ecrã (std + out) que nos permite utilizar a função printf. Eu recorri á biblioteca do stdlib.h para poder utilizar a função system (“pause”). Caso contrário, o programa era executado e corrido e nós não víamos nada, a janela abria e fechava-se e nós não perceberíamos a saída "Hello World". int main() É a função principal. Em programação em C temos funções atrás de funções. Esta função main() é a tal com que vamos chamar as outras. Por isso mesmo ela é obrigatória em qualquer programa. Mas ela é a primeira função a correr e quando o programa acabar de correr a função main o programa acaba! é a primeira e ultima. o int significa que a função vai retornar um inteiro. São 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 número real temos mais mas por enquanto vamos continuar.(ver a secção resumo e quadros -anexos…) Note que todas as funções têm ( ), só assim é que o compilador reconhece que é uma função. A idéia de ter funções é permitir o encapsulamento de uma idéia ou operação, dar um nome a isso e depois chamar essa operação de várias partes do programa simplesmente usando o seu nome. Nós podemos chamar funções já existentes do C e também criar as nossas próprias funções

Programar em C/Estudo dando nomes a elas. Uma das grandes vantagens nisto é a economia de código que se pode ter durante a construção de um programa. {} O compilador ao ver as chavetas compreende aquilo como um bloco. O código que estiver dentro das chaves será executado sequencialmente. printf () Podemos utilizar esta função apenas porque pusemos no header a biblioteca stdio . O que esta função nos permite é enviar o que temos entre aspas para o monitor (out). Mas termos a hipótese de poder formatar o texto enviado, por exemplo se acrescentássemos \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 função retorne o valor zero que é o fim da execução do programa. repare-se que podíamos ter posto return (0) que até seria mais correcto, mas como o valor é zero, o compilador aceita. ; o ponto e virgula é essencial para a programação em C, funciona como o ponto final: separa as frases e contextos. No C, o ponto e vírgula está no final da maioria das linhas e determina o final de cada "operação". Na sua ausência teremos erro enquanto compilamos o programa. /*o meu primeiro programa*/ Como qualquer outra linguagem, o C nos permite inserir comentários entrelaçados no código-fonte do programa, ou melhor permite escrever o que pretendermos entre /* e */ que na hora da compilação estes serão descartados: não têm utilidade em nada para o compilador, no entanto são bons para nós programadores, pois pode nos auxiliar a não ficarmos perdidos durante a programação de um programa de código-fonte muito extenso.

2

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

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

3

Declarando variáveis
Vai-nos ocorrer a necessidade do programa guardar certos valores. Assim vamos querer declarar uma variável para depois guardar um valor e mais tarde poder ser utilizado a sua forma geral é: tipo_da_variável lista_de_variáveis; Para poder declarar uma variável podemos escrever int a; Neste caso apenas estamos a declarar uma variável que chamamos “a”. O que é que está a acontecer? Estamos a pedir ao computador para reservar um espaço da sua memória. e que depois associe esse espaço de memória ao nome “a”. Assim o computador tem de saber onde aloca as coisas. É como tivéssemos 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 espaço alocado á variável a; Como primeiro pedimos para reservar memória e depois escrevemos o valor, por isso é que temos que dizer o quanto espaço de memória é que deve ser reservado daí o int antes de a; Agora o que aconteceria se puséssemos a=5,555.. não sei qual é que seria o resultado. Mas ou não compilaria, ou se isso acontecesse estaríamos a alocar mais memória do que pedimos e portanto poderia acontecer estarmos a escrever em memória que pertencia a outro programa, logo podemos estar a corromper os nossos dados, penso que não é coisa que se queira. Podemos declarar uma variável ao mesmo tempo que lhe damos um valor, por exemplo: int a=5; Podemos ainda declarar quantas variáveis queiramos ao mesmo tempo; int a, b,c,d; têm é que ter as vírgulas no meio e ponto e virgula no final Podemos declarar várias variáveis ao mesmo tempo e associar os respectivos valores? int a=5, b=6; sim também podemos fazer isso, não esquecer as virgulas. • Lembre-se sempre que é necessário declarar a variável antes de atribuirmos um valor, caso contrário esse valor irá para um sítio qualquer na memória e é bem possível alterar uma memória já atribuída. • E declarar a variável antes de utilizarmos. • O nome das variáveis terão de ser únicas, como é lógico! • Há diferença entre termos maiúsculas e minúsculas (pois elas têm um valor diferente no código ASCII) logo ter – nome e NOME ou mesmo Nome são todas variáveis diferentes – o C É CASE SENSITIVE • Por fim não podem ter o nome das palavras reservadas que são os nomes das funções e tipologia, (ver quadro) • Utiliza-se as letras números e o sublinhado (_) • Variáveis até 32 caracteres são aceites • é pratica comum usar letras minúsculas para nomes de variáveis e maiúsculas para nomes de constantes.

147.767 32 %li -2.768 32.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 números reais temos as seguintes variantes: float double long double para os inteiros: int short int long int unsigned signed .768 32.483. int.7E-308 1. 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. isto é.967.768 32.768 32. void.4E-4932 3.483. convêm ver a tabela das tipologias.4E-38 3.648 2. mas também existe uma outra razão que tem a ver com as operações. double Quando declaramos uma variável teremos de indicar de que tipologia a variável é. char.535 16 %i -32. uma para ints outra para floats.294.767 16 %hu 0 65. 3.7E+308 80 %Lf 3.647 32 %li -2. float.147.4E+38 64 %lf 1.767 16 %u 0 65.648 2. 5.647 32 %lu 0 4.147. 2.483.535 16 %hi -32. por exemplo.Programar em C/Estudo 4 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 variáveis C tem 5 tipos básicos: 1.483. se tivermos um dado int. e se passarmos para o seguinte nós vamos passar para o próximo int.295 32 %f 3. etc.147. ou seja asseguramos que o número seguinte é um int. e o computador agrega cada tipologia em tabelas distintas. 4.767 16 %hi -32. ou seja uma das razões é realmente a optimização da memória. A razão disto tem a ver que cada tipologia ocupa um número de bits diferentes.

mas vai fazer umas substituições antes de o fazer.Programar em C/Estudo 5 printf () A função printf () está dentro da biblioteca stdio. O d é de inteiro. etc e por aí adiante. Aqui vai novamente imprimir no ecrã o texto que está entre aspas. float…) ele vai ser substituído pelo seu valor como inteiro. é uma formatação do valor que vai ser substituído. Por isso. No exemplo tínhamos o valor 5 na variável a. a). a . O “d” a seguir ao %. printf(“amor é mio”). a. independentemente da tipologia que seja o valor “a” (quer seja inteiro. Vê que existe o símbolo % e vai substituir pelo valor da variável “a” que era 5. Aqui o que acontece é pedirmos para fazer a substituição do primeiro % pelo valor da primeira variável.a). printf (“%c\t%f\t%d\t”.h. no nosso caso o ecrã o que está dentro das aspas. em f de float e em d de inteiro. O que ela faz é enviar para “out”. resultado: amor é mio printf (“a variável a = %d”. o segundo % pela segunda. Formatações do printf Constantes de barra invertida Código \b \f \n \t \" \' \0 \\ \v \a \N \xN Código %c %d %i %e %E %f %g %G Significado Retrocesso ("back") Alimentação de formulário ("form feed") Nova linha ("new line") Tabulação horizontal ("tab") Aspas Apóstrofo Nulo (0 em decimal) Barra invertida Tabulação vertical Sinal sonoro ("beep") Constante octal (N é o valor da constante) Constante hexadecimal (N é o valor da constante) Formato Um caracter (char) Um número inteiro decimal (int) O mesmo que %d Número em notação científica com Número em notação científica com Ponto flutuante decimal Escolhe automaticamente o melhor Escolhe automaticamente o melhor o "e"minúsculo o "e"maiúsculo entre %f e %e entre %f e %E . e aqui pedimos para escrever o 5 em c de carácter.

só que justificado a esquerda. . Código Imprime printf ("%-5. • Se se quiser um preenchimento com zeros pode-se colocar um zero antes do número • O alinhamento padrão é à direita.2.67| |Ola | Operações o C permite realmente fazer umas operações entre valores. Justificação e o 3.2f". Ou seja com estas armas podemos fazer operações entre valores. os relacionais e lógicos e os específicos para os bits.67| | 2. Para se alinhar um número à esquerda usa-se um sinal antes do número de casas. printf ("%5.671).456. | 456.Programar em C/Estudo %o %s %u %x %X %% %p Número octal String Decimal "unsigned" (sem sinal) Hexadecimal com letras minúsculas Hexadecimal com letras maiúsculas Imprime um % Ponteiro 6 É possível também indicar o 1. %10."Ola").2f". . temos os operadores aritméticos. .671). Então %-5d será o nosso inteiro com o número mínimo de cinco casas. Operadores Aritméticos e de Atribuição Operador Ação + Soma (inteira e ponto flutuante) Subtração ou Troca de sinal (inteira e ponto flutuante) * Multiplicação (inteira e ponto flutuante) / Divisão (inteira e ponto flutuante) % Resto de divisão (de inteiros) ++ Incremento (inteiro e ponto flutuante) -Decremento (inteiro e ponto flutuante) Operadores Relacionais e Lógicos Operador Ação > Maior do que . • Se o comprimento do inteiro for menor que cinco então o campo terá cinco de comprimento e será preenchido com espaços em branco. Tamanho do campo. variáveis (e os seus valores) e ainda os endereços das variáveis. printf ("%-10s". Número de casas decimais %5d estamos indicando que o campo terá cinco caracteres de comprimento no mínimo • Se o inteiro precisar de mais de cinco caracteres para ser exibido então o campo terá o comprimento necessário para exibi-lo.4f indica um ponto flutuante de comprimento total dez e com 4 casas decimais. 2.

) Operador Ação && AND (E) || OR (OU) ! NOT (NÃO) Operadores Lógicos Bit a Bit Operador Ação & AND | OR ^ XOR (OR exclusivo) ~ NOT >> Deslocamento de bits à direita << Deslocamento de bits à esquerda valor>>número_de_deslocamentos Pergunta: mas como é que impriminos um nº na forma binária. etc. para os operadores de aritmética convém dizer que o C permite algumas abreviaturas.. x&=k. x-=k. x<<=k. no C o = é associativo. x=x-k. o que me parece ser uma estupidez permiti-las uma vez que elas não poupam assim muito. têm o inconveniente de obrigar a memorizar. x=x<<k.a seguir abreviatura de abreviatura X++ equivale a´ x+=1 #include <stdio. .Programar em C/Estudo >= < <= == != Maior ou igual a Menor do que Menor ou igual a Igual a Diferente de 7 (repare no valor = = quando queremos comparar. x=x&k. e “not”. por isso temos o igual igual para fazer a distinção quando queremos é comparar os valores. x=x*k. x/=k. x=x>>k. Ver o quadro dos operadores lógicos. x*=k.. ie. “or”.b. respectivamente – que são os mais estranhos.h> #include <stdlib. especialmente os símbolos: && o || e o ! que são “and”. Expressão Original Expressão Equivalente x=x+k. uma vez que temos operadores binários??? ou melhor: ainda não vi nada sobre os bits. é como dizer atribui o valor. x+=k. x>>=k.h> main() { int a. x=x/k. e claro.

este ultimo adiciona quando for passar para a linha seguinte. ++a+5). Convém ainda dizer que nesta linguagem toda do C há certos símbolos que têm maior precedência.Programar em C/Estudo a = b = 5. return 0. b). ou seja o que vem antes … Maior precedência () [] -> ! ~ ++ -. printf("%d\n". b++ +5). e portanto é a hierarquia dos operadores que vai dar o resultado. 8 . printf("%d\n".j. se eu fizesse isto sequencialmente daria a=12*3 e a=36 se eu der agora precedência de operadores. apanharam o ponto! espero que sim porque é bom ter em mente sempre esta tabela. a).h> main() { int i. em caso de igualdade de precedência predomina a sequencia de enumeração.. system (“pause”).h> #include <stdlib. Menor precedência #include <stdio. isto é perfeitamente normal ter de esclarecer uma vez que admitimos operações aritméticas. printf("%d\n". ficaria a=10+6=16. a=10+(2*3). } Este exemplo mostra que não é bem igual ter ++a ou a++. printf("%d\n". é melhor dar um exmplo: se eu tiver a=10+2*3. se uma pessoa tiver duvidas pode sempre recorrer ao topo e ao fim da hierarquia e fazer sempre eg. e a sequencia nem sempre deve ser seguida. -(unário) (cast) *(unário) &(unário) sizeof * / % + << >> <<= >>= == != & ^ | && || ? = += -= *= /= .

0000 ou seja tenho tenho de converter um dos inteiros (pelo menos) em float. (que o outro se converte). Estou a dizer que ele será inteiro. j = 2. double d_var. é possível fazer a conversão ao contrário de um tipo com mais bits para um com menos bits e isso é truncar. convém agora explicar o ponto comum entre as duas.(3). f = 3. system (“pause”). acontece que se eu declarar int a. i = 5. o resultado sei que é 3. o resultado seria 3. Modeladores (Cast) Ora bem já temos a tipologia de variáveis e agora mesmo as operações aritméticas. Se eu tiver a=10/3. . l_d_var = 1. Ou seja a divisão de dois números inteiros deu um número real. d_var = d_var + f_var. f_var = 1. f).Programar em C/Estudo float f. 3. l_d_var = d_var + f_var. printf("value of f is %f\n". d_var = 1. o resultado continua a ser 3 mas desta vez. forma geral é: (tipo)expressão mas existem umas conversões automáticas: int f(void) { float f_var. return l_d_var. mesmo que eu diga float a. long double l_d_var. então eu poderia fazer a=(float)10/3. return 0. } /*o float é convertido em double*/ /*o float e o double convertidos em long double*/ repare-se que a conversão é feita para o maior. } 9 .0. f = f + j / i.

e agora estamos a dizer para alocar o valor que for teclado nesse espaço. eg. nós depois como queremos dar o nome de a. assim porque escrever & . &a). o que é que vai acontecer. mas vamos ter a resposta daqui a um pouco. e por aí adiante. ou seja. scanf (“%d”. printf ("\no nº impresso foi %d”. Devemos sempre nos lembrar que a função scanf() deve receber ponteiros como parâmetros.. é uma etapa extra sem qualquer necessidade Faço aqui uma questão: vamos imaginar que imprimíamos 12. via teclado e guardar o que foi escrito. o 2º ao 2º endereço. return 0. system (“pause”). neste caso. vamos fazer com que ele aponte para o valor de a.quando nos bastaria escrever a? Isto é um pouco intrigante.h> int main () { int a. scanf (“%d”. ou melhor em formato inteiro e depois colocar esse valor no endereço da variável “a” Na verdade confesso que não percebo bem esta parte o símbolo & é símbolo de endereço físico. Tal como na função printf. Isto significa que as variáveis que não sejam por natureza ponteiros devem ser passadas . Não percebo bem a vantagem disto mas…até faz sentido porque asim não temos de fazer o copy para a nova morada e eliminar a antiga. por exemplo. vamos testar. Neste caso estamos a dizer para a variável ler o que foi teclado.&b.h o que faz é ler o que foi inserido. repare-se que têm de estar separados por virgulas Respondendo á questão intrigante que coloquei aqui. } ele só vai pegar no 12! vou retomar esta função mais tarde por causa do & que convém perceber!!! Protótipo: int scanf (char *str. estamos á espera de um inteiro e associamos ao primeiro endereço. printf ("digite um número”). scanf () O scanf() é uma função que está também na biblioteca stdio. &a.). &a). a).. que vai ser um inteiro.5 e estávamos á espera de receber um int.Programar em C/Estudo 10 . Ou seja nós criamos a variável a antes. scanf (“%d%c%f”. ao escrevermos vai ser reservado um espaço pelo sistema operativo e vai colocar esse valor lá nesse espaço reservado por ele.h> #include <stdlib. penso que a resposta pode ser esta: utiliza-se um intermediário.. podemos receber quantos valores quisermos.&c). #include <stdio.

mas antes vai substituir o %d pelo valor das várias variareis/ system (“pause”). se for verdadeira lê o código do bloco*/ printf ("o valor %d é menor que 10\n". . /*estamos a declarar 1 variáveis que vai ter valores inteiros */ printf (“digite uma valor”).h> /*ao evocarmos esta biblioteca podemos utilizar a função printf e scanf*/ #include <stdlib. /*a função lê algo que se espera que esteja no formato de inteiro e associa ao endereço da variável a.IF #include <stdio.h> /*ao evocarmos esta biblioteca podemos utilizar a função system (“pause”) */ int main() /*declaramos que a função “main” vai retornar um inteiro*/ { int a.Programar em C/Estudo precedidas do operador &. /* com esta função podemos ver o resultado podemos fazer uma pause antes de encerrar */ return 0. para dar mais flexibilidade. ou seja. /*é escrito na tela a frase…*/ scanf (“%d”. a). &a). /*aqui a função printf vai imprimir o texto entre aspas. mas vamos ver algumas variantes. /*a função main vai retornar o valor zero e encerra o programa*/ } A função if é muito parecida com a ideia que nós temos na linguagem comum. escreve o valor recebido na variável b*/ if (a<10) /*verifica a condição a<10. mas o sistema vai ser sempre igual. if (condição) declaração. ie. Código %c %d %i %hi %li %e %f %lf %h %o %s %x %p Formato Um único caracter (char) Um número decimal (int) Um número inteiro Um short int Um long int Um ponto flutuante Um ponto flutuante Um double Inteiro curto Número octal String Número hexadecimal Ponteiro 11 Branching . se (isto)…então (aquilo).

"). else if (condição_n) declaração_n. …. printf ("Digite um numero: ").h> int main () { int num. ou seja. #include <stdio. else declaração_2. } else { if (num>10) { printf ("O numero e maior que 10. } Mais abreviaturas!!! Continuo a dizer que é uma estupidez isto das abreviaturas para não escrever uns poucos de caracteres!! 12 . só será executada a declaração equivalente à primeira condição que der diferente de zero. if (num==10) { printf ("\n\nVoce acertou!\n"). } } return 0."). Neste caso ele executa a declaração correspondente.Programar em C/Estudo Se a condição for avaliada como zero. scanf ("%d".\n"). Repare que estamos a garantir que uma das declarações será sempre executada if (condição_1) declaração_1. Podemos ter ainda os if aninhados que basicamente é um if dentro do outro.&num). A última declaração (default) é a que será executada no caso de todas as condições darem zero e é opcional. a declaração não será executada if (condição) declaração_1. else declaração_default o programa começa a testar as condições começando pela 1 e continua a testar até que ele ache uma expressão cujo resultado dê diferente de zero. else if (condição_2) declaração_2. Só uma declaração será executada. printf ("O numero e igual a 10. a declaração_1 será executada. caso contrário será a declaração_2. Se a condição for avaliada como diferente de zero. } else { printf ("O numero e menor que 10.

bem o retorno atrevo-me a dizer que até é negativo! O Comando switch A forma geral é switch (variável) { case constante_1: declaração_1. printf ("Digite um numero: "). i++) é equivalente a ter é equivalente a ter é equivalente a ter if (num) if (!num) for (i=0. faz com que o switch seja interrompido assim que uma das declarações seja executada #include <stdio. break. switch (num) . default declaração_default. } O comando break. case constante_n: declaração_n. string[i] !=’\0’ . break. break. condição?expressão_1:expressão_2.Programar em C/Estudo if (num!=0) if (num==0) for (i=0 . esta porcaria abreviaturas é que torna isto horrível.&num). b=a>0?-150:150. case constante_2: declaração_2.h> int main () { int num. . string[i]. scanf ("%d". . i++) 13 Comando "?" Temos ainda o operador “?” que é mais outro tipo de abreviaturas if (a>0) b=-150. else b=150. . obriga a um esforço elevado de memorização sem ter um retorno.

\n"). a). ie. Se esta for verdadeira a declaração é executada e faz-se o teste novamente Se eu tiver. A função do while é exactamente igual á do while só que põe a condição depois do bloco. break.\n"). do { declaração. case 11: printf ("\n\nO numero e igual a 11. sujeito a uma condição. } return 0. Basicamente é repetir um conjunto de linhas várias vezes até …ie. b) a=a+1 } Em português seria enquanto a condição for verdadeira repete o código do bloco. Repare-se que é como fosse um if. } while (a<b).\n"). case 10: printf ("\n\nO numero e igual a 10. } 14 LOOP. enquanto o if não! segue sempre em frente. . default: printf ("\n\nO numero nao e nem 9 nem 10 nem 11. Só que o isto é repetido enquanto a condição for verdadeira. break. se a condição for verdadeira faz isto. } while (condição). Volta ao início do bloco.WHILE O que é que é um loop. o que significa que o bloco é executado pelo menos uma vez. while (a<b) { printf (“%d é menor que %d”.a.Programar em C/Estudo { case 9: printf ("\n\nO numero e igual a 9. break. LOOP – DO WHILE do { printf (“%d\n”.\n"). O while() testa uma condição. a=a+1.

h> int main () { int i.Laranja\n\n"). estamos a atribuir o valor 1 a “a”. return 0. a<10. break. se esta for verdadeira. muito compacta e extremamente poderosa a sua forma geral é for (inicialização. } 15 LOOP – FOR Este é para mim a melhor função..condição. &i). • A estrutura do-while executa a declaração.. do { printf ("\n\nEscolha a fruta pelo numero:\n\n").Abacaxi\n").h> #include <stdlib. a=a+1) { código do bloco } o que diz é para “a” igual a 1. testa a condição e. • Garante que a declaração será executada pelo menos uma vez. scanf("%d". for (a=1.incremento) declaração. . volta para a declaração.. • Um dos usos da estrutura do-while é em menus #include <stdio.. case 2: printf ("\t\tVoce escolheu Abacaxi.Programar em C/Estudo • O ponto-e. break. printf ("\t(2).vírgula final é obrigatório. printf ("\t(1).\n"). switch (i) { case 1: printf ("\t\tVoce escolheu Mamao.\n"). break. case 3: printf ("\t\tVoce escolheu Laranja.. printf ("\t(3). executa o código do bloco e depois executa o incremento de uma unidade a “a”.. } while ((i<1)||(i>3)). Volta a repetir o processo até que a fique maior ou igual a 10.\n"). } system (“pause”). note que quando fazemos a=1.Mamao\n"). até que “a” seja menor que 10.

b=1."). /* se Opcao invalida: volta ao inicio do loop */ . várias de finalização e várias de incremento.Programar em C/Estudo a função loop é tão versátil que podemos dar várias condições de iniciação."). break. é melhor ver o exemplo: #include <stdio. • repare que se eliminarmos a condição. sem o abandono do loop. scanf("%d".h> #include <stdlib. case 3: printf("\n --> Terceira opcao.. Repare que estão separadas por virgulas. faz o incremento e volta a testar a condição.incremento) declaração. switch (opcao) { case 1: printf("\n --> Primeira opcao. &opcao).[ for (inicialização. isto é.] temos um loop infinito 16 O Comando break O break faz com que a execução do programa continue na primeira linha seguinte ao loop ou bloco O Comando continue Quando o comando continue é encontrado..h> int main() { int opcao. a=a+1. • Um ponto importante é que podemos omitir qualquer um dos elementos do for. Temos depois duas de finalização com o operador lógico “ou” a primeira das condições que se tornar verdadeira irá parar o ciclo. if ((opcao > 5)||(opcao <1)) continue. case 2: printf("\n --> Segunda opcao. break.. Se a condição for verdadeira ele executa a declaração."). Ele fica repetindo estas operações até que a condição seja falsa. while (opcao != 5) { printf("\n\n Escolha uma opcao entre 1 e 5: "). o loop pula para a próxima iteração. . se não quisermos uma inicialização poderemos omiti-la. b++) { código do bloco } Neste caso temos duas condições de iniciação. for (a=1. a<10 || b>20 .

i++) a[i]=i. } } system (“pause”). Portanto pode ser em qualquer parte do programa. . ie. return 0. a[999]. Aqui vamos! Aqui vamos declarar novamente variáveis. Neste exemplo iríamos ter i até 5 e cada um com um valor já atribuído. Caso a opção escolhida seja válida o programa segue normalmente. Só que desta vez vamos pensar como conseguiríamos criar 1000 variáveis.. ARRAYS – VECTORES Bem podemos dizer que ao entrar agora nesta parte vamos partir para uma segunda etapa. nome_do_rótulo: . a[2]….."). Agora podemos fazer int a[1000].. Este exemplo realmente está engraçado. goto nome_do_rótulo. Como vimos declarar variáveis era apenas int a. repare-se que começa no 0 e não 1. break. } 17 recebe uma opção do usuário. break. o continue faz com que o fluxo seja desviado de volta ao início do loop. é como tivéssemos a criar mais dimensões. Se esta opção for inválida. O Comando goto O goto realiza um salto para um local especificado. Maior parte dos bugs vêm daqui – pensarmos que o índice vai até 1000!!!! Podemos até ter int a[i]. i<5. case 5: printf("\n --> Abandonando.Programar em C/Estudo break. for (i=0.. Porque podíamos brincar …never mind . case 4: printf("\n --> Quarta opcao.. Este local é determinado por um rótulo... Funciona tipo um índice.."). cria o a[0].. a[1].

int matrx [3][4] = { 1. 4.0. Que é: e quando tivermos uma função que irá retornar vários valores de várias tipologias? Será que temos que criar funções separadas para cada tipologia. O compilador C vai. Ter em atenção que: • Índice mais à direita varia mais rapidamente que o índice à esquerda. 5..1 }. 100. char str [10] = { 'J'. "Jose" }. 'a'. 11. Por outro lado permite utilizar várias vezes ao longo do bloco do main. apenas chamando. 8. 2. neste caso verificar o tamanho do que você declarou e considerar como sendo o tamanho da matriz..5. 12 }. Um aparte: Mas existe aqui um problema para mim. 3.3. 9. inicializar matrizes das quais não sabemos o tamanho a priori. Os parâmetros são as variáveis que colocamos dentro das (). em alguns casos. 0. podemos declarar ainda mais variáveis tipo_da_variável nome_da_variável [altura][largura]. "Maria". '\0' }. [tamN] = {lista_de_valores}.. permitir você encapsular um idéia ou operação. Só temos que cumprir com alguns requisitos: necessitamos de dizer que tipologia de retorno vai ter e que parâmetros vão receber. Podemos. as funções não são mais do que pedaços de código que estava na função main() e que resolvemos tirar para fora do main e demos um nome. deverá ser possível a mudança do caminho que uma função faz seu trabalho (como espera-se que o trabalho em si próprio não . char str_vect [3][10] = { "Joao". • Não esquecer os índices variam de zero ao valor declarado. tipo_da_variável nome_da_variável [tam1][tam2] . float vect [6] = { 1.7.1. 4. 2. naturalmente.. 4. Isto tem duas vantagens: Uma primeira porque temos blocos mais pequenos de código e permite uma melhor leitura e detecção de erros. menos um multidimensionais podemos ter ainda conjunto de variáveis multidimensionais. dando um nome a ela. char str [10] = "Joao". Bem até pode fazer sentido! A idéia de uma função está. Isto ocorre na hora da compilação e não poderá mais ser mudado durante o programa FUNÇÕES Como dissemos no início. [tamN]. Isto facilita porque podemos necessitar do resultado de uma função para executar uma outra. 'o'. 10. 'o'. Estando corretamente projetado e estruturado os programas. 7. 6.Programar em C/Estudo 18 Matrizes se nós pensarmos melhor. então chamar que operação de várias partes do resto de seu programa simplesmente usando o seu nome. onde a iniciação é: tipo_da_variável nome_da_variável [tam1][tam2] .

h> int square (int x) /* Calcula o quadrado de x */ { printf ("O quadrado e %d\n\n ".h> #include <stdlib. printf ("Entre com um numero: "). } int main () { mensagem(). return 0. tirávamos para fora do main e escrevíamos da seguinte maneira: #include <stdio.(x*x)).h> int mensagem () { printf ("Ola!\n "). } int main () { int num. system (“pause”). return 0. return 0.h> #include <stdlib. então o que é que fazíamos? criávamos uma função.h> int main () { printf ("Ola!\n "). Exemplo:imaginemos que tínhamos um código do tipo #include <stdio. printf ("Eu estou vivo!\n"). return 0. scanf ("%d".Programar em C/Estudo muda) sem efeito no resto do programa. } 19 /* Função simples: só imprime Olá! */ Ou seja criámos uma função sem argumentos. repare que são as chavetas “()” que dizem que aquilo é uma função vamos agora pegar num exemplo melhorzinho 2º Exemplo criação de função com 1 argumento #include <stdio. system (“pause”).&num). } aqui a nossa função main () vai imprimir apenas duas frases! mas agora imaginemos que o nosso código começava a ter muitas e muitas linhas e necessitávamos de colocar várias vezes o printf ("Ola!\n "). .h> #include <stdlib. printf ("Eu estou vivo!\n").

neste caso. os argumentos são separados por vírgula e que deve-se explicitar o tipo de cada um dos argumentos.y. 1. Em segundo lugar. há ainda um outro requisito. y=12. . Agora não sei bem porquê. . ao ser passada como argumento para square() é copiada para a variável x. a variável num. Dentro de square() trabalha-se apenas com x. Daí surgiram os protótipos.float c) { printf ("%f". ou seja. return 0. . } 20 /* Multiplica 3 numeros */ Neste exemplo temos inputs que podem ser constantes.y. float b. Penso que é porque o compilador tem de saber que tipo de retorno vai ter. return 0.9. é que a função main é aquela que é primeiro executada. um a um. . mult (x.3. que os argumentos passados para a função não necessitam ser todos variáveis porque mesmo sendo constantes serão copiados para a variável de entrada da função. system (“pause”). x=23. e é sequencial.h> int mult (float a.87). é realizado as linhas de código da função square e quando estas terminarem voltamos para o main! 3º Exemplo criação de função com 3 argumento #include <stdio. Repare que. por isso não é necessário serem variáveis! mas convém dizer o seguinte: 1. } int main () { float x. return 0. Apesar de existirem algumas conversões de tipo. . tal como os headers. Em primeiro lugar temos de satisfazer aos requisitos da função quanto ao tipo e à quantidade de argumentos quando a chamamos. system (“pause”). não é importante o nome da variável que se passa como argumento.a*b*c). também.5.Programar em C/Estudo square(num). Note. Se mudarmos o valor de x dentro de square() o valor de num na função main() permanece inalterado 1. 1. que o C faz automaticamente.h> #include <stdlib. } repare no seguinte: quando chamamos a função square() no main estamos a colocar a variável do main! esse valor do num é copiado e vai entrar como input da função square. é importante ficar atento. mas as funções extras têm de ser enumeradas antes do main().

Ou seja a Linguagem c necessita de saber que tem uma função tal que vai retornar um inteiro. como dissemos uma função retorna um valor.y.Programar em C/Estudo 21 FUNCTION PROTOTYPE Bem. int main () { float num. num=Square(num). Ou melhor o void é uma explicitação do programador que aquela função não vai receber nem dar nenhum valor.num). um aparte. printf ("Entre com um numero: ").&num). Isto tem ainda um outro benefício que não me apercebi de imediato que é a questão do C se utilizarmos protótipos. como uma boa execução do programa costuma ser zero. #include <stdio. } A diferença entre parâmetros e argumentos: parametros são o nome das variaveis inputs uma função argumentos são os valores dos argumentos quando uma função é chamada. isto não é mais do que aumentar um pouco a nossa flexibilidade. return 0. por isso para que o resultado .. Aqui as funções protótipo é pegar nas funções e colocá-las depois da função main. com a grande particularidade de termos de copiar a primeira linha da declaração da função e colocá-la antes da função main. o void é utilizado da seguinte forma: void função(void) { … } o que aqui estamos a dizer é que temos uma função que não vai receber parâmetros nenhuns. e z que são todos inteiros. irá confirmar os tipos e a lista de parâmetros. mas a função realmente retorna um valor. Nós nas funções temos de declará-las antes da função main. e pode receber parâmetros. e não vai retornar qualquer valor. } float Square (float a) { return a*a. printf ("\n\nO seu quadrado vale: %f\n". VOID Void significa vazia em ingles acho que posso agora inserir um ponto que é a tipologia void. scanf ("%f".h> float Square (float a). ou float… e vai receber de parâmetros x. isto poderia ser muito bem contornado se em vez de se declarar nome_da_função() fazer tipo_da_função nome_da_função() dentro da função main() ou seja temos a diferença entre declaração e definição de funções exemplo de um prototype. não quero saber dos valores . o valor da função é ignorado.

Não se pode utilziar void na função principal a main().&a). as ultimas são as globais. a=a*a. scanf ("%f".h> #include <stdlib. Mas não precisaremos usá-la pois as variáveis declaradas dentro de um bloco já são consideradas locais • Quando uma função tem uma variável local com o mesmo nome de uma variável global a função dará preferência à variável local. printf ("Entre com um numero: "). float a.sq.Programar em C/Estudo seja interpretado como um erro e bom declarar void. sq=sqr(num). no exemplo anterior. ou seja não são alterados os valores dos parâmetros fora da função. .b). • A palavra reservada do C auto serve para dizer que uma variável é local. printf ("O seu quadrado vale: %f\n". return 0. printf ("\n\nO numero original e: %f\n". } float sqr (float num) { /*descrição da função sqr*/ /*associo o valor inserido á variavel num*/ /*chamo a função sqr e passo o parametro num*/ /*declaro 2 varíaveis: num e sq*/ /*protótipo da função sqr()*/ printf ("\n\nO numero original e: %f\n". tem de retornar um int no mínimo. A função main() é especial. quando chamamos por uma função e damos um parâmetro. apesar de já ter visto isso em alguma bibliografia. int main () { float num.sq).a). printf ("O seu quadrado vale: %f\n". eles tomam apenas uma cópia do valor passado. b=a.h> float sqr (float num).&num). printf ("Entre com um numero: "). 22 Variáveis locais – globais e passagem de parâmetros por valor e referência Quando declaramos as variáveis. Crucial! percebendo isto apanhamos os ponteiros com as pernas ás costas! #include <stdio. passámos a variável float num. Este tipo de chamada de função é denominado chamada por valor. elas estão vigentes em qualquer uma das funções. Isto é importante porquê? Acho aliás que é dos mais importantes. mas ao parâmetros formais é o float a.num). As primeiras são as designadas como locais: só têm validade dentro do bloco no qual são declaradas. b. nós podemos fazê-lo dentro de uma função ou fora de todas as funções inclusive a main(). E estes existem independentemente das variáveis que foram passadas. system (“pause”). scanf ("%f". Mais uma nota sobre o void.

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

23

Este exemplo explica um detalhe que é a meu ver crucial, na compreensão disto tudo! quando a função main() é executada, ela chega a meio e vê uma chamada para a função sqr() e onde é passado o parâmetro “num”. Ora ela já estava á espera pois viu o protótipo. ela então vai executar a função que está depois da função 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 á variável “sq”. depois temos a impressão da variável “num” e “sq”. Ora o que acontece é que o valor do “num” fica igual ao valor antes de entrar na função. eu faço a mesma coisa agora com a variável “a” e “b”, e vemos que agora a função a é alterada. resumindo, o valor variável quando entra numa outra função não é alterado.!! Quando o valor do parâmetro é alterado é denominado por chamada por referência. O C não faz chamadas por referência. mas podemos simular isto com outra arma do C que são os ponteiros. retornamos a este ponto após desta secção seguinte.

POINTERS
Os pointers supostamente são um bicho de 7 cabeças. É supostamente a parte mais difícil. E se uma pessoa compreender isto, torna-se craque em c. Uma variável normal tem uma localização na memória que pode conter um valor. Por exemplo quando declaramos int i; quatro bytes de memória são reservados e a esses 4 bytes chamamos de name i. Um pointer (ou ponteiro, em português) é uma variável que quando declarada, também é reservado um espaço na memória, mas em vez de guardar um valor guarda o endereço de memória de uma outra variável, guarda a direcção de uma outra variável. • Ponteiros guardam endereços de memória. • Um ponteiro também tem tipo Qual é a vantagem dos pointers. Uma das vantagens é a situação, imaginemos que queremos colocar uma quantidade enorme de dados para uma função. É muito mais fácil indicar a localização dos dados do que copiar cada elemento dos dados. Para declarar usamos: tipo_do_ponteiro *nome_da_variável; É o asterisco (*) que faz o compilador saber que aquela variável não vai guardar um valor mas sim um endereço para aquele tipo especificado int *pt; se tivermos vários pointers, temos de colocar o asterisco em cada um; int *pt1, *pt2;

Programar em C/Estudo Ainda não foi inicializado. Isto significa que eles apontam para um lugar indefinido Para saber o endereço de uma variável 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 expressão &count nos dá o endereço de count, o qual armazenamos em pt. A última linha modifica o valor a 12 da variável 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 endereço de i ao ponteiro p*/ /*atribuímos o valor 5 á variável que p aponta*/ /*imprimimos a variável i e para onde aponta p e ainda o endereço 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 são: • A não iniciação dos pointers. dito de outra forma é dizer que realmente temos um pointer e é reservada memória, mas que depois não dizemos o que queremos guardar nesse espaço. p=&i; isso faz com que o p aponte para qualquer sítio na memória. E depois quando fazemos *p=12; estamos a pedir para guardar esse nº 12 nesse local da memória que não 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 variável apontada por p1 tenha o mesmo conteúdo da variável apontada por p2 devemos fazer *p1=*p2

Operações com ponteiros
p++; Quando incrementamos um ponteiro ele passa a apontar para o próximo 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 próximo inteiro. (*p)++; Para incrementar o conteúdo da variável apontada pelo ponteiro p,

Programar em C/Estudo *(p+15); o conteúdo do ponteiro 15 posições adiante

25

PONTEIROS COMO PARAMENTROS DE FUNÇÕES
Os ponteiros dão jeito! Porquê? Vamos por um exemplo: eu tenho 2 variáveis e quero trocar o valor. eu poderia fazer como fiz no 1º programa em baixo ou se quisesse fazer uma função da operação 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 atribuímos á variavel t*/ /*ao valor de b atribuímos á variavel a*/ /*ao valor de t atribuímos á variavel a*/

O único inconveniente é que. portanto. } int main () { int a.b). fora do main. system (“pause”).a. teremos de lembrar de colocar um & na frente das variáveis que estivermos passando para a função. quando usarmos a função. Ou seja vamos passar ponteiros que é mais fácil #include <stdio.int *j) /*como vão ser passados os endereços. *i=*j. ie. Afinal como é que nós podíamos fazer isto. a operação faz-se numa função extra. a operação de troca faz-se na mesma função. e o valor dos parâmetros não é alterado. return 0. } atão.b). a=5. No primeiro caso. Mas. t=*i. printf ("\n\nEles valem %d %d\n".a.&b). Mas existe uma alternativa! é Usando os ponteiros. *j=t. estão sendo modificados! 26 .de ponteiros */ { int t. que.Programar em C/Estudo return 0. quem é este conteúdo? Nada mais que os valores armazenados em num1 e num2. Uma maneira era fazer a troca na função extra e fazer retornar o valor a duas novas variáveis que depois voltaríamos a associar a “a” e “b”. Que está acontecendo é que passamos para a função Swap o endereço das variáveis “a” e “b”. Estes endereços são copiados nos ponteiros “i” e “j”. } Os ponteiros são a "referência" que precisamos para poder alterar a variável fora da função. b=10.b. Através do operador * estamos acessando o conteúdo apontado pelos ponteiros e modificando-o. o que é que está a acontecer??? Lembram-se da história da chamada por valor e chamada por referência. necessitamos de manipular os valores desses endereços daí o uso de "*" . Essa é a resposta. Swap (&a.h> #include <stdlib.h> void Swap (int *i. Mas só estas trocas todas fazem dissuadir de uma pessoa utilizar funções extras. No segundo. /*estamos a passar o endereço em vez dos valores*/ printf ("\n\nEles agora valem %d %d\n".

Veja o exemplo. supondo que o executável se chame data: data 19 04 99 O programa deverá imprimir: 19 de abril de 1999 #include <stdio. char *nome_mes [] = {"Janeiro". o terceiro o mes e o quarto os dois últimos algarismos do ano */ { mes = atoi(argv[2]). Ele é. "Dezembro"}.char *argv[]). • O argv (argument values) é um ponteiro para uma matriz de strings.A string referente ao mes deve ser transformada em um numero inteiro. como já foi dito."Julho". Mas a função main() também pode ter parâmetros formais. /* argv contem strings. todos inteiros"). "Maio". O programa deverá receber da linha de comando o dia. É para saber quantos elementos temos em argv que temos argc. o segundo o dia.Programar em C/Estudo Espere um momento. "Agosto". pois o nome do programa é contado como sendo o primeiro argumento. "Fevereiro". "Março". Por isto passamos para a função o endereço da variável a ser modificada! 27 Os Argumentos argc e argv A função main() como dissemos antes é uma função especial. • O argc (argument count) é um inteiro e possui o número de argumentos com os quais a função main() foi chamada na linha de comando. será que nós já não vimos esta história de chamar uma função com as variáveis precedidas de &? Já! É assim que nós chamamos a função scanf().h> int main(int argc.. . no entanto o programador não pode escolher quais serão int main (int argc.. A função scanf() usa chamada por referência porque ela precisa alterar as variáveis que passamos para ela! Não é para isto mesmo que ela é feita? Ela lê variáveis para nós e portanto precisa alterar seus valores. "Novembro". if(argc == 4) /* Testa se o numero de parâmetros fornecidos está correcto.h> #include <stdlib. "Abril". Exemplo: Escreva um programa que faça uso dos parâmetros argv e argc. e imprimir a data em formato apropriado. char *argv[]) { int mes. "Setembro". O argv[0] sempre aponta para o nome do programa (que. no mínimo 1. é considerado o primeiro argumento). 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. "Outubro". Cada string desta matriz é um dos parâmetros da linha de comando. mês e ano correntes. Mas porquê? Vamos pensar um pouco. o primeiro parâmetro é o nome do programa. "Junho".

scanf("%d". mas repare que é apenas para tipologias de variáveis!! . argv[3]). fat(n)). Assim podemos escrever o código utilizando um nome que nos agrade mais. return 0.Programar em C/Estudo else printf("\n%s de %s de 19%s". printf("\nO fatorial de %d e' %d". Ele é muito parecido com o #define a diferença é que trabalha com a tipologia das variáveis. nome_mes[mes-1]. else return 1. n. Isto vai dizer ao compilador que quando encontrar no código a palavra boolean a vai substituir por int.h> #include <stdlib. } int main() { int n.h> int fat(int n) { if (n) return n*fat(n-1). argv[1]. } else printf("Erro!\nUso: data dia mes ano. system ("pause"). printf("\n\nDigite um valor para n: "). } TYPEDEF Também o typedef deve ser utilizado como header. todos inteiros"). Uma função assim é chamada função recursiva #include <stdio. } 28 Recursividade Uma função pode chamar a si própria. &n). (type+definition) que é (tipologia de variáveis +definição) typedef int boolean.

Se o arquivo estiver nos caminhos de procura pré-especificados do compilador. um arquivo especificado #include "nome_do_arquivo" ou #include <nome_do_arquivo> A diferença entre se usar " " e < > é somente a ordem de procura nos directórios pelo arquivo especificado. ele deve substituí-lo pela sequência_de_caracteres fornecida.PI). que é executado pelo compilador antes da execução do processo de compilação propriamente dito. #ifndef 6. #if 7. entregando para o compilador um programa modificado.B) ((A < B) ? (A):(B)) x = max(i. #undef 4. As directivas podem ser colocadas em qualquer parte do programa. baseado nas Diretivas de Compilação As directivas de compilação são comandos que não são compilados.1416 #define VERSAO "2.h. printf ("O numero pi vale: %f". use " ". #elif 1.VERSAO).h.. 1. o pré-processador modifica o programa fonte. Se você quiser informar o nome do arquivo com o caminho completo. } Outro exemplo: #define max(A. #include <stdio.B) ((A > B) ? (A):(B)) #define min(A. isto é. Todas as directivas de compilação são iniciadas pelo carácter #. se ele for um arquivo do próprio sistema (como é o caso de arquivos como stdio.Programar em C/Estudo 29 DIRETIVAS DE COMPILAÇÃO As Diretivas de Compilação O pré-processador C é um programa que examina o programa fonte escrito em C e executa certas modificações nele.A Diretiva #Define #define nome_da_macro sequência_de_caracteres Toda vez que ele encontrar o nome_da_macro no programa a ser compilado. y = min(t. #ifdef e #endif 5. ou se o arquivo estiver no directório de trabalho.j).. string. #define 3. Portanto.r). return 0. na hora da compilação.h> #define PI 3. #include 2.A Diretiva #include Ela diz ao compilador para incluir.02" int main () { printf ("Programa versao %s". . #else 8. etc. sendo dirigidos ao pré-processador. 2.) use < >.

Será substituída pela linha: x = ((i)>(j) ? (i):(j)). i) não funcionará correctamente porque existe um espaço em branco entre PRINT e (i). Sua forma geral é: #ifndef nome_da_macro sequência_de_declarações #endif A sequência de declarações será compilada se o nome da macro não tiver sido definido 6. Por exemplo. #ifdef nome_da_macro sequência_de_declarações #endif A sequência de declarações será compilada apenas se o nome da macro estiver definido. a macro: #define PRINT (i) printf(" %d \n". não deve ter nenhuma variável. 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. 4. Quando você utiliza a diretiva #define nunca deve haver espaços em branco no identificador. .j). a linha de código: x = max(i. As Diretivas #ifdef e #endif Directivas de compilação condiciona. A Diretiva #if A diretiva #if tem a seguinte forma geral: #if expressão_constante sequência_de_declarações #endif A sequência de declarações será compilada se a expressão-constante for verdadeira.O compilador passa a partir deste ponto a não conhecer mais esta macro. A directiva de compilação #endif é útil para definir o fim de uma sequência de declarações para todas as directivas de compilação condicional. A Diretiva #ifndef A diretiva #ifndef funciona ao contrário da diretiva #ifdef. ou seja. É muito importande ressaltar que a expressão fornecida deve ser constante. 5.Programar em C/Estudo Assim.

. /*linhas de codigo.. #if SISTEMA == DOS #define CABECALHO "dos_io. . . o comando else. A Diretiva #else A diretiva #else tem a seguinte forma geral: #if expressão_constante sequência_de_declarações #else sequência_de_declarações #endif Ela funciona como seu correspondente. ...h" #else #define CABECALHO "unix_io.*/ . #define SISTEMA DOS ..Programar em C/Estudo 31 7. Sua forma geral é: #if expressão_constante_1 sequência_de_declarações_1 #elif expressão_constante_2 sequência_de_declarações_2 #elif expressão_constante_3 sequência_de_declarações_3 . #elif expressão_constante_n sequência_de_declarações_n #endif O funcionamento desta estrutura é idêntico ao funcionamento apresentado anteriormente.h" #endif #include CABECALHO 8. A Diretiva #elif A diretiva #elif serve para implementar a estrutura if-else-if.

têm uma vantagem extra que é que podem ser chamadas por vários códigos que façamos. e que possam ser chamadas várias vezes ao longo do código. } /* print sorted array */ /* fill array */ /* 2º função – repare que recebe um parâmetro/ /*na hora da compilação vai substituir max por 10*/ /*definição de varivel global*/ /*definição de variavel global e afectação de valor*/ /* 1º função*/ Nós podemos generalizar o buble sort mais .t. Vamos criar 2 funções para alem do main #include <stdio. } void bubble_sort(int m) { int x. i < MAX. int rand() { rand_seed = rand_seed * 1103515245 +12345.y. for (x=0. basta-nos chamar.x. y < m-x-1. return (unsigned int)(rand_seed / 65536) % 32768. y++) if (a[y] > a[y+1]) { t=a[y]. for (i=0.h> #include <stdlib. vamos tentar criar a nossa própria biblioteca.a[i]). printf("--------------------\n"). ou conjunto de funções. essas funções estão fora do nosso programa. a[y]=a[y+1]. i < MAX. printf("%d\n". x++) for (y=0. Mas desta vez.y. Elas para além da vantagem. permitem uma organização do código. x < m-1. int rand_seed=10. } bubble_sort(MAX).a[i]). i++) printf("%d\n". Não necessitamos de fazer copy e paste. que alguém já fez. for (i=0.t.Programar em C/Estudo 32 LIBRARIES – BIBLIOTECAS As bibliotecas são tipo funções. i++) { a[i]=rand().h> #define MAX 10 int a[MAX]. int main() { int i. tal como nas funções. Por exemplo. de um código para outro código que façamos mais tarde. a[y+1]=t. fora do main.

y.x.h> #include <stdlib. a). a[y+1]=t. int rand_seed=10.h> #define MAX 10 int a[MAX].y. return (unsigned int)(rand_seed / 65536) % 32768. . e o file do código efectivo Como eu quero criar uma biblioteca para a função do bublesort. a[y]=a[y+1]. } } int main() { int i.a[i]). 1. para receber 2 parametros */ /* recebe agora 2 parâmetros*/ 33 printf("--------------------\n").a[i]). y++) if (a[y] > a[y+1]) { t=a[y]. x++) for (y=0. for (i=0. vou fazer o seguinte 1. i < MAX. . .h as duas linhas que colocámos são protótipos. /* necessito de alterar. de uma forma gera. for (i=0. int rand() { rand_seed = rand_seed * 1103515245 +12345. } bubble_sort(MAX. /*código das funções prototipo para o ficheiro . for (x=0.t. } void bubble_sort(int m.t. types e prototipods de funções disponibilizadas pela biblioteca. contém constantes. i < MAX. i++) { a[i]=rand(). a palavra “extern” representa uma função que mais tarde será linked extern int rand(). Vamos colocar o seguinte código e gravar com o nome de util. x < m-1. header file (que temo “h” sufixo – contem informação sobre a biblioteca. y < m-x-1. } Todas as bibliotecas têm 2 partes: 1. int a[]) { int x.Programar em C/Estudo #include <stdio. i++) printf("%d\n". printf("%d\n".

int main() { int i.t. x++) for (y=0.h */ extern void bubble_sort(int. for (i=0. i++) { a[i]=rand(). return (unsigned int)(rand_seed / 65536) % 32768. y < m-x-1. Agora coloquemos o seguinte programa e gravemos com o nome main. } } 1.c #include <stdio.h> #include "util.h" int rand_seed=10. a[y]=a[y+1]. . printf("%d\n".c */ /* !!!repare que eu aqui já chamo o header criado. .c /* código efectivo para a criação do ficheiro util.y. i < MAX.x.t. /* fill array */ /* print sorted array */ .y. i < MAX.a). y++) if (a[y] > a[y+1]) { t=a[y].int a[]) /*esta é a função buble_sort()*/ { int x. printf("--------------------\n"). agora vamos criar um outro ficheiro com o nome util. for (x=0. } void bubble_sort(int m. x < m-1. for (i=0. a[y+1]=t.h" /* REPARE que eu agora chamo o header file.a[i]). 1. } bubble_sort(MAX.Programar em C/Estudo header util. int rand() /*esta é a função rand()*/ { rand_seed = rand_seed * 1103515245 +12345. 34 • / #define MAX 10 int a[MAX]. int []). mas repare que agora tenho “” em vez de <>*/ #include "util. i++) printf("%d\n".a[i]).

#include <stdio.prints formatted output to stdout . sprintf vamos fazer agora apenas umas notas a estas funções: a função gets (get+string) tem como prototipo char *gets (char *s). o C vai tê-lo como um fluxo.. sempre e quando quisermos. . A 2ª função imprime no ecrã depois de ler.h. vamos. Assim. TEXT FILES Existem 6 comandos de I/O na biblioteca <stdio.) que se estiver trabalhando. terminais.h> printf puts putc scanf gets getc. Todos os fluxos são similares em seu funcionamento e independentes do dispositivo ao qual estão associados.prints a string to stdout . ou melhor.reads formatted input from stdin . Outro aspecto importante.prints a character to stdout . teclados. em primeiro lugar. a função lê a string do teclado. apresentar o seu protótipo. ai armazenar uma string s no ponteiro s: gets(nome_da_string) mas existe um problema que pode ser perigoso. getche – lê um caractere do stdin podemos ver estas duas funções na biblioteca conio.Programar em C/Estudo } Como vimos agora é só chamar pela biblioteca que criamos. Assim um ficheiro tal como outros componentes..h> int main() { char buffer[10]. .troca de informação se possa realizar.reads a string from stdin . as mesmas funções que descrevem o acesso aos discos podem ser utilizadas para se acessar um terminal de vídeo.reads a character from stdin outros: getchar putchar getch. quando se discute a entrada e saída na linguagem C é o conceito de fluxo Seja qual for o dispositivo de entrada e saída (discos. . apenas necessita de ser aberto e depois fechado por forma a que o fluxo. 35 Entradas e Saídas Padronizadas O sistema de entrada e saída da linguagem C está estruturado na forma de uma biblioteca de funções Quando apresentarmos uma função.

&i. escrevendo uma região de memória que não está reservada à string. escrevem ou leem em uma string. j. string1 também conterá "Valor de i=" .h> #include <stdlib. sprintf(string1. &k). k. Uma forma de se evitar este problema é usar a função fgets (vamos utilizá-la mais tarde) 36 Função sprintf e sscanf sprintf e sscanf são semelhantes a printf e scanf. k).h> int main() { int i. return 0.. return 0. %d. system ("pause"). } se o usuário digitar mais do que 10 caracteres incluindo o "\0". #include <stdio. &i). scanf("%d".h> #include <stdlib.h> int main() { int i. char string1[]= "10 20 30"."Valor de i = %d". } /*lê para o string1*/ /*imprime no ecrã*/ foi utilizada a função sscanf para converter a informação armazenada em string1 em seu valor numérico . Além da representação de i como uma string. return 0. printf( " Entre um valor inteiro: ").. char string1[20]. i. printf("Valores lidos: %d. Porém. j. Este efeito é conhecido como "estouro de buffer" e pode causar problemas imprevisíveis. buffer). printf("O nome é: %s". i). os caracteres adicionais serão colocados na área de memória subsequente à ocupada por ela.*/ a variável i é "impressa" em string1. ao invés de escreverem na saída padrão ou lerem da entrada padrão. } /*coloca na string1. gets(buffer). puts(string1). %d". system ("pause").Programar em C/Estudo printf("Entre com o seu nome"). &j. sscanf(string1. #include <stdio. "%d %d %d". a frase.

verifique o conteúdo do arquivo arquivo. que será aberto no diretório corrente). gets(string). .h> int main() { FILE *fp. Protótipo: int getc (FILE *fp). getc Retorna um caractere lido do arquivo. return 0. caractere por caractere em um arquivo em disco (o arquivo arquivo. caractere a caractere */ fclose(fp). i++) putc(string[i]. if(!fp) { printf( "Erro na abertura do arquivo").txt". fp = fopen("arquivo. string[i]. for(i=0. char string[100]. /* Grava a string. A string de origem permanecerá inalterada e será anexada ao fim da string de destino. Seu protótipo é: int putc (int ch.txt (você pode usar qualquer editor de textos). Você verá que a string que você digitou está armazenada nele.O programa a seguir lê uma string do teclado e escreve-a. exit(0). para escrita */ Depois de executar este programa.string_origem).FILE *fp). } /* Arquivo ASCII.string_origem). fp). #include <stdio.h> #include <stdlib. strcpy strcpy (string_destino. } printf("Entre com a string a ser gravada no arquivo:").txt.Programar em C/Estudo 37 Função putc A função putc é a primeira função de escrita de arquivo que veremos. strcat strcat (string_destino. Escreve um caractere no arquivo."w"). system ("pause"). int i.

fprintf . . fseek 10. ferror e perror 7. remove 12. . .closes a text file 3. associado à porta paralela) O sistema de entrada e saída do ANSI C é composto por uma série de funções. fgetc . p será então um ponteiro para um arquivo. fopen . fwrite 9.reads a string from a file 5. mas uma definição usando o comando typedef.opens a text file 2. Podemos declarar um ponteiro de arquivo da seguinte maneira: FILE *p. pelo o que eu estou a perceber o nome “FILE” é tipo um int ou um float ou ainda um typedef 1. Se elas forem diferentes a função retorna não-zero.reads formatted input from a file 14.reads a character from a file' . Abrindo e Fechando um Arquivo arquivos pré-definidos: • • • • • stdin: dispositivo de entrada padrão (geralmente o teclado) stdout: dispositivo de saída padrão (geralmente o vídeo) stderr: dispositivo de saída de erro padrão (geralmente o vídeo) stdaux: dispositivo de saída auxiliar (em muitos sistemas.detects end-of-file marker in a file 4. feof . . retorna o comprimento da string fornecida. . fclose . Este não é um tipo propriamente dito. . fputs . fread 8. .Programar em C/Estudo 38 strlen strlen (string). fputc .h . associado à porta serial) stdprn : dispositivo de impressão padrão (em muitos sistemas. Se as duas forem idênticas a função retorna zero. cujos protótipos estão reunidos em stdio. fscanf . É usando este tipo de ponteiro que vamos poder manipular arquivos no C.h. . . fgets .prints formatted output to a file 13. compara a string 1 com a string 2. . .prints a string to a file 6.string2). strcmp strcmp (string1. rewind 11. Todas estas funções trabalham com o conceito de "ponteiro de arquivo". O terminador nulo não é contado. . . . Esta definição também está no arquivo stdio.prints a character to a file 15. .

O mesmo que "a+" acima. Seu protótipo é: FILE *fopen (char *nome_do_arquivo. como no modo "a" anterior. "w+" Cria um arquivo texto para leitura e gravação. o conteúdo anterior será destruído. no caso de arquivo não existente anteriormente. será criado. "w"-write Abrir um arquivo texto para gravação. "a"-append Abrir um arquivo texto para gravação. O mesmo que "r+" acima. ele será criado. para abrir um arquivo binário para escrita. O arquivo deve existir e pode ser modificado. "r+" Abre um arquivo texto para leitura e gravação. Se não existir. só que o arquivo é binário Poderíamos então. ou um novo arquivo será criado. Os dados serão adicionados no fim do arquivo se ele já existir. Se já existir. "w+b" Cria um arquivo binário para leitura e escrita. se ele já existir. Este nome deve ser válido no sistema operacional que estiver sendo utilizado.char *modo). só que o arquivo é binário. O arquivo deve existir antes de ser aberto. "a+" Abre um arquivo texto para gravação e leitura. "a+b" Acrescenta dados ou cria uma arquivo binário para leitura e escrita. "wb" Cria um arquivo binário para escrita. Se o arquivo não existir. O mesmo que "w+" acima.Programar em C/Estudo 39 1. fopen Esta é a função de abertura de arquivos. escrever: . "r+b" Abre um arquivo binário para leitura e escrita. Se o arquivo existir.read Significado Abre um arquivo texto para leitura. A tabela abaixo mostra os valores de modo válidos: Modo "r". como no modo "w" anterior. O nome_do_arquivo determina qual arquivo deverá ser aberto. ou um novo arquivo será criado. só que o arquivo é binário. Igual ao modo "r" anterior. só que o arquivo é binário. "rb" Abre um arquivo binário para leitura. Os dados serão adicionados no fim do arquivo ("append"). no caso de arquivo não existente anteriormente. o conteúdo anterior será destruído. só que o arquivo é binário. "ab" Acrescenta dados binários no fim do arquivo. O modo de abertura diz à função fopen() que tipo de uso você vai fazer do arquivo. só que o arquivo é binário.

se o ficheiro não existe ele vai ser criado.Repare que abrir um ficheiro no modo w é destrutivo."%d\n"."wb"). fclose(f). fp=fopen ("exemplo. f=fopen("infile". system (“pause”).f)!=NULL) printf("%s".s). Exemplo: #include <stdio. int x.bin". Exemplo 2: #include <stdio. até o enter*/ /*fgets – em vez do fscanf*/ /*abre o file chamado “infile” em modo r*/ /* no caso de abrir mal*/ . x++) fprintf(f. /* enquando for diferente de zero."r").Programar em C/Estudo FILE *fp. ou seja. } /* está criado um ponteiro para o arquivo */ /*a função abre o file out no modo w*/ /*a a função printf para file daí fprintf */ Vai abrir um file chamado de out e escrever os números de 1 a 10. return 0.h> #include <stdlib. if (!f) return 1.ie. Depois fecha o file . vamos poder ler ou escrever nele utilizando as funções que serão apresentadas nas próximas páginas. mas se existe um outro ficheiro o novo ficheiro fica no seu lugar.x)."). f=fopen("out".h> #define MAX 10 int main() { FILE *f. este comando fopen retorna um pointer ao ficheiro que é guardado na variável f. if (!f) return 1. while (fgets(s. fclose(f). for(x=1.1000. char s[1000]. Uma vez aberto um arquivo.bin e está localizado no 40 diretório corrente */ if (!fp) printf ("Erro na abertura do arquivo."w"). /* Declaração da estrutura*/ /* o arquivo se chama exemplo. x<=MAX. se não é possível abrir o ficheiro o f fica com o valor null.h> int main() { FILE *f. A condição !fp testa se o arquivo foi aberto com sucesso porque no caso de um erro a função fopen() retorna um ponteiro nullo (NULL).

return 0. } . para o sistema operacional.de read. A razão para se fazer . o fgets ainda tem a vantagem de acrescentar um 1n em cada linha que lê. fp=fopen ("exemplo. Para utilizá-la deve-se colocar um include para o arquivo de cabeçalho stdlib. ele vai ler até encontar o eof-end of file marker. Mas. exit Protótipo é: void exit (int codigo_de_retorno). exit (1). O ponteiro fp passado à função fclose() determina o arquivo a ser fechado.h.no exemplo ele vai ler 1000 caracteres vamos ver os comandos melhor."). A convenção mais usada é que um programa retorne zero no caso de um término normal e retorne um número não nulo no caso de ter ocorrido um problema.Programar em C/Estudo return 0. o que é este "buffer"? Quando você envia caracteres para serem gravados em um arquivo. Pode ser chamada de qualquer ponto no programa e faz com que o programa termine e retorne. Fechar um arquivo faz com que qualquer caracter que tenha permanecido no "buffer" associado ao fluxo de saída seja gravado. if (!fp) { printf ("Erro na abertura do arquivo. Para tanto usa-se a função fclose(): int fclose (FILE *fp). seu conteúdo é escrito no disco de uma vez. } 41 para ler o file infile mas agora no modo r.. devemos fechá-lo. Quando o "buffer" estiver cheio.bin". Esta função aborta a execução do programa. } 2. estes caracteres são armazenados temporariamente em uma área de memória (o "buffer") em vez de serem escritos em disco imediatamente. o código_de_retorno.h> /* Para a função exit() */ main (void) { FILE *fp.."wb").h> #include <stdlib. fclose Acabámos de usar um arquivo que abrimos. Repare que utilizamos o fgets em vez do fscanf porque este requere que o texto esteja perfeitamente formatado.. A função retorna zero no caso de sucesso.. Fim de programa. . #include <stdio.

as gravações seriam muito lentas. até que o final do arquivo seja atingido. gets(str). fclose(fp). printf("\n\n Entre com um nome para o arquivo:\n").h> int main() { FILE *fp. char c. feof EOF ("End of file") indica o fim de um arquivo. str[30]. exit(0). int i. Seu protótipo é: int feof (FILE *fp). Os caracteres lidos são apresentados na tela: #include <stdio.*/ .. fp = fopen("arquivo. if(!fp) { printf( "Erro na abertura do arquivo").Programar em C/Estudo isto tem a ver com a eficiência nas leituras e gravações de arquivos. c)."w"))) { /* Le um nome para o arquivo a ser aberto: */ /* Caso ocorra algum erro na abertura do arquivo. é necessário verificar se um arquivo chegou ao fim. char c. Para isto podemos usar a função feof().h> #include <stdlib. } /* Enquanto não chegar ao final do arquivo */ /* imprime o caracter lido */ /* Arquivo ASCII.h> #include <string. Às vezes. Assim estas gravações só serão efetuadas quando houver um volume razoável de informações a serem gravadas ou quando o arquivo for fechado. apenas para gravar aquele caracter. caso contrário retorna zero.h> #include <stdlib. #include <stdio. A função exit () fecha todos os arquivos que um programa tiver aberto. } while((c = getc(fp) ) != EOF) printf("%c". para leitura */ Verifique o exemplo. Ela retorna não-zero se o arquivo chegou ao EOF.h> int main() { FILE *p."r"). return 0. if (!(p = fopen(str. Se. caracter por caracter. para cada caracter que fossemos gravar. tivéssemos que posicionar a cabeça de gravação em um ponto específico do disco. O programa a seguir abre um arquivo já existente e o lê. 42 3. Outra forma de se verificar se o final do arquivo foi atingido é comparar o caractere lido por getc com EOF.txt". frase[80] = "Este e um arquivo chamado: ".

stdin). exit(1).imprime no arquivo e o fecha .FILE *fp).p). } fclose(p). ele fará parte da string. porém. fclose(p). o que não acontecia com gets. Portanto. c = getc(p). frase[i]. A função fgets é semelhante à função gets(). 5. c = getc(p)."r"). fgets Para se ler uma string num arquivo podemos usar fgets() cujo protótipo é: char *fgets (char *str. ferror e perror Protótipo de ferror: int ferror (FILE *fp).. p = fopen(str. A função retorna zero. int tamanho. levando em conta que o ponteiro fp pode ser substituído por stdin. uma alternativa ao uso de gets é usar a seguinte construção: fgets (str.Programar em C/Estudo printf("Erro! Impossivel abrir o arquivo!\n"). } strcat(frase. como vimos acima. for (i=0. A função lê a string até que um caracter de nova linha seja lido ou tamanho-1 caracteres tenham sido lidos.*/ /* Abre novamente para leitura */ /* o programa aborta automaticamente */ 43 /* Le o primeiro caracter */ /* Enquanto não se chegar no final do arquivo */ /* Le um novo caracter no arquivo */ 4. A função recebe 3 argumentos: a string a ser lida. Se o caracter de nova linha ('\n') for lido. o que poderia acarretar erros de "estouro de buffer". além dela poder fazer a leitura a partir de um arquivo de dados e incluir o caracter de nova linha na string.c). fputs Protótipo: char *fputs (char *str. que está associado ao arquivo de onde a string será lida. str).FILE *fp). tamanho. } /* Fecha o arquivo */ /* Imprime o caracter na tela */ /* Se nao houve erro. a função gets não tinha este controle. 6.. Como vimos. i++) putc(frase[i]. o limite máximo de caracteres a serem lidos e o ponteiro para FILE. se nenhum erro ocorreu e um número diferente de zero se algum erro ocorreu durante o acesso ao arquivo. while (!feof(p)) { printf("%c". se torna muito útil quando queremos verificar se cada . Escreve uma string num arquivo. ela ainda especifica o tamanho máximo da string de entrada.

pode acabar o espaço em disco enquanto gravamos. Na maioria dos casos. fputs(string. ou o disco pode estar com problemas e não conseguimos ler. Para tanto. O buffer é a região de memória na qual serão armazenados os dados lidos. fread Podemos escrever e ler blocos de dados. ele pode ser lido ou gravado. Quando o arquivo for aberto para dados binários. count indica quantas unidades devem ser lidas. int count. Por exemplo. fclose(pf). de modo que consigamos garantir a integridade dos nossos dados."w")) ==NULL) { printf("\nNao consigo abrir o arquivo ! "). fclose(pf). pf). exit(1). Uma função que pode ser usada em conjunto com ferror() é a função perror() (print error).h> int main() { FILE *pf. se um arquivo pode ser aberto. existem situações em que isto não ocorre. fread pode ler qualquer tipo de dados. int numero_de_bytes. FILE *fp). O número de bytes é o tamanho da unidade a ser lida. Porém. putc('\n'. } 44 7. digite <enter>: ").Programar em C/Estudo acesso a um arquivo teve sucesso. cujo argumento é uma string que normalmente indica em que parte do programa o problema ocorreu. pf). #include <stdio. if((pf = fopen("arquivo. } }while (strlen(string) > 0). O protótipo de fread() é: unsigned fread (void *buffer. gets(string).h> #include <stdlib.txt". temos as funções fread() e fwrite(). etc. Este número pode ser menor que count quando o fim do arquivo for encontrado ou ocorrer algum erro. } do { printf("\nDigite uma nova string. . exit(1). char string[100]. Isto significa que o número total de bytes lidos é: numero_de_bytes*count A função retorna o número de unidades efetivamente lidas. if(ferror(pf)) { perror("Erro na gravacao"). Para terminar.

fclose(pf). O exemplo abaixo ilustra o uso de fwrite e fread para gravar e posteriormente ler uma variável float em um arquivo binário.bin". } if(fwrite(&pi. "rb")) == NULL) /* Abre o arquivo novamente para leitura */ { printf("Erro na abertura do arquivo"). fclose(pf).int numero_de_bytes.h> #include <stdlib. que retorna o tamanho em bytes da variável ou do tipo de dados. 1. porém escrevendo no arquivo. 1. pilido). exit(1). sizeof(float). fwrite A função fwrite() funciona como a sua companheira fread(). float pi = 3. #include <stdio. Este valor será igual a count a menos que ocorra algum erro.1415.Programar em C/Estudo 45 8. . /* Fecha o arquivo */ if((pf = fopen("arquivo. return 0.h> int main() { FILE *pf. A função retorna o número de itens escritos. } /* Le em pilido o valor da variável armazenada anteriormente */ Note-se o uso do operador sizeof. float pilido.FILE *fp).bin". "wb")) == NULL) /* Abre arquivo binário para escrita */ { printf("Erro na abertura do arquivo"). Seu protótipo é: unsigned fwrite(void *buffer. lido do arquivo e': %f".pf) != 1) printf("Erro na leitura do arquivo").pf) != 1) /* Escreve a variável pi */ printf("Erro na escrita do arquivo"). printf("\nO valor de PI. } if(fread(&pilido. if((pf = fopen("arquivo. sizeof(float). exit(1).int count.

frase[] = "Este e um arquivo chamado: ".h> #include <string. a partir de um ponto especificado. O parâmetro origem determina a partir de onde os numbytes de movimentação serão contados. if (!(p = fopen(str. str[i]. exit(1).*/ /* o programa aborta automaticamente */ . 10. O exercício da página anterior poderia ser reescrito usando-se. ou fwrite() e fread(). A seguir apresentamos uma segunda versão que se usa das funções fgets() e fputs().*/ /* 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.. resposta[80]. e o fecha . #include <stdio. fgets() e fputs(). e que acrescenta algumas inovações.h> #include <stdlib. for(i=0. char str[30]. Apaga um arquivo especificado. /* Se nao houve erro. imprime no arquivo. Seu protótipo é: int fseek (FILE *fp."w"))) { printf("Erro! Impossivel abrir o arquivo!\n").Programar em C/Estudo 46 9. i++) if(str[i]=='\n') str[i]=0. remove Protótipo: int remove (char *nome_do_arquivo). rewind A função rewind() de protótipo void rewind (FILE *fp).h e são: Nome SEEK_SET SEEK_CUR SEEK_END Valor Significado Início do arquivo Ponto corrente no arquivo Fim do arquivo 0 1 2 Tendo-se definido a partir de onde irá se contar. por exemplo. fgets(str.h> int main() { FILE *p. retorna a posição corrente do arquivo para o início.. Esta move a posição corrente de leitura ou escrita no arquivo de um valor especificado. int i.long numbytes. Os valores possíveis são definidos por macros em stdio. 11. } fputs(frase.29. fseek Para se fazer procuras e acessos randômicos em arquivos usa-se a função fseek(). p).int origem). numbytes determina quantos bytes de deslocamento serão dados na posição atual. printf("\n\n Entre com um nome para o arquivo:\n").stdin)..

A diferença é que a saída de fprintf() é um arquivo e não a tela do computador. Talvez a forma mais simples de escrever o programa da página 97 seja usando fprintf () e fscanf().. remove(str). fclose(p). while (!feof(p)) { fscanf(p. printf("\n\n%s\n". fgets(resposta. } fprintf(p."r")."r"). Protótipo: int fscanf (FILE *fp.Programar em C/Estudo fputs(str. fclose(p).*/ /* abre novamente para a leitura */ /* Caso ocorra algum erro na abertura do arquivo. if (!(p = fopen(str. exit(1). Fica assim: #include <stdio. fecha . return 0. fclose(p).. /* Se nao houve erro.c). Como já poderíamos esperar. fscanf A função fscanf() funciona como a função scanf(). imprime no arquivo."%c". p).&c).."w"))) { printf("Erro! Impossivel abrir o arquivo!\n")...h> int main() { FILE *p. printf("\n\n Entre com um nome para o arquivo:\n").). a única diferença do protótipo de fscanf() para o de scanf() é a especificação do arquivo destino através do ponteiro de arquivo.).p). /* Fecha o arquivo */ /* Apaga o arquivo */ /* abre novamente e le */ 47 } fprintf A função fprintf() funciona como a função printf(). gets(str).. printf("%c".c. str).... p = fopen(str.*/ /* o programa aborta automaticamente */ /* Le um nome para o arquivo a ser aberto: */ . char str[80]. p = fopen(str.h> #include <stdlib."Este e um arquivo chamado:\n%s\n". A diferença é que fscanf() lê de um arquivo e não do teclado do computador. Protótipo: int fprintf (FILE *fp. 79.char *str. resposta). a única diferença do protótipo de fprintf() para o de printf() é a especificação do arquivo destino através do ponteiro de arquivo. Como já poderíamos esperar.char *str.

Destes. Veja o exemplo: #include <stdio. const O modificador const faz com que a variável não possa ser modificada no programa. pois o conteúdo de um ponteiro pode ser alterado por uma função. Se o programador tentar modificar PI o compilador gerará um erro de compilação. Para tanto. Vamos discutir agora outros modificadores de tipo. Como o nome já sugere é útil para se declarar constantes. b=sqr (&a). num está protegido contra alterações. basta declarar o parâmetro como const. . Dentro da função sqr() o compilador daria uma mensagem de erro. O uso mais importante de const não é declarar variáveis constantes no programa. Poderíamos ter. int b. } 48 Tipos de Dados Avançados Já vimos que uma variável é declarada como tipo_da_variável lista_de_variáveis. Estes modificadores são incluídos na declaração da variável da seguinte maneira: modificador_de_tipo tipo_da_variável lista_de_variáveis. mudam a maneira com a qual a variável é acessada e modificada. } No exemplo. se tentássemos fazer *num=10. long. como o próprio nome indica.h> int sqr (const int *num). por exemplo: const float PI=3. Estes modificam o tipo da variável declarada. já vimos os modificadores signed. Seu uso mais comum é evitar que um parâmetro de uma função seja alterado pela função. return 0. } int sqr (const int *num) { return ((*num)*(*num)). Podemos ver pelo exemplo que as variáveis com o modificador const podem ser inicializadas. unsigned. Modificadores de Acesso Estes modificadores. Isto quer dizer que.141. Mas PI não poderia ser alterado em qualquer outra parte do programa. main (void) { int a=10.Programar em C/Estudo } fclose(p). Vimos também que existem modificadores de tipos. Isto é muito útil no caso de um ponteiro. e short.

em que tipo de variável usar o register? O register não pode ser usado em variáveis globais. num++. Isto é util se quisermos isolar pedaços de um programa para evitar mudanças acidentais em variáveis globais. Este não precisa ser atendido necessariamente. Vamos agora ressaltar vários pontos importantes. o register é um pedido que o programador faz ao compilador. tenhamos uma variável que o BIOS do computador altera de minuto em minuto (um relógio por exemplo). register O computador tem a memória principal e os registradores da CPU. Digamos que. return num. A melhor maneira de se entender esta variável local static é implementando. Em terceiro lugar. ou seja. Esta inicialização só vale para a primeira vez que a função é chamada pois num deve manter o seu valor de uma chamada para a outra. por exemplo. static O funcionamento das variáveis declaradas como static depende se estas são globais ou locais. Os tipos de dados onde é mais aconselhado o uso do register são os tipos char e int. se possível. executando seu próprio programa que use este conceito. Veja por si mesmo. Em primeiro lugar. Veja que a variável local int é inicializada. Variáveis locais static são variáveis cujo valor é mantido de uma chamada da função para a outra. Um exemplo do uso do register é dado: . } Assim. extern float sum. porque usar o register? Variáveis nos registradores da CPU vão ser acessadas em um tempo muito menor pois os registradores são muito mais rápidos que a memória. Seria muito bom que declarássemos esta variável como sendo volatile.Programar em C/Estudo 49 volatile O modificador volatile diz ao compilador que a variável em questão pode ser alterada sem que este seja avisado. Isto implicaria que um registrador da CPU ficaria o tempo todo ocupado por conta de uma variável. int RetornaCount (void) { return count. Isto evita "bugs" seríssimos. } A função count() retorna o número de vezes que ela já foi chamada. Veja o exemplo: int count (void) { static int num=0. mas pode-se usá-lo em qualquer tipo de dado. O modificador register diz ao compilador que a variável em questão deve ser. são variáveis globais que não são (e nem podem ser) conhecidas em outros modulos. O que a função faz é incrementar num a cada chamada e retornar o seu valor. As variáveis (assim como o programa como um todo) são armazenados na memória. usada em um registrador da CPU. Em segundo lugar. Variáveis globais static funcionam como variáveis globais dentro de um módulo. o compilador irá saber que count e sum estão sendo usados no bloco mas que foram declarados em outro.

for (count=0. Abaixo vemos uma tabela de conversões numéricas com perda de precisão. 50 Conversão de Tipos Em atribuições no C temos o seguinte formato: destino=orígem.count++) { .count<10. Nem todas as conversões são possíveis.. O primeiro ponto a ser ressaltado é que o valor de origem é convertido para o valor de destino antes de ser atribuído e não o contrário. para um compilador com palavra de 16 bits: De Para Informação Perdida unsigned char char Valores maiores que 127 são 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 Precisão resultado arredondado double float Precisão . Este vai modificar o modo como a função opera na passagem de parâmetros.. } return 0. A forma geral da função ficaria então: modificador_de_tipo tipo_de_retorno nome_da_função (declaração_de_parâmetros) { corpo_da_função } lembram-se do casting que tínhamos que fazer para a tipologia das variaveis.resultado arredondado Modificadores de Funções A forma geral de uma função é. Isto pode ser entendido de uma outra forma. Nós podemos perder precisão ou no máximo manter a precisão anterior. como já foi visto. É importante lembrar que quando convertemos um tipo numérico para outro nós nunca ganhamos precisão.Programar em C/Estudo main (void) { register int count. } O loop for acima será executado mais rapidamente do que seria se não usássemos o register. Este é o uso mais recomendável para o register: uma variável que será usada muitas vezes em seguida. tipo_de_retorno nome_da_função (declaração_de_parâmetros) { corpo_da_função } Uma função pode aceitar um modificador de tipo.resultado arredondado long double double Precisão . Se o destino e a orígem são de tipos diferentes o compilador faz uma conversão entre os tipos. Quando convertemos um número não estamos introduzindo no sistema nenhuma informação adicional. (quando . Isto implica que nunca vamos ganhar precisão.

Apenas para ficarem com o conhecimento. Além disto. main (void) { char String [20]="Curso de C. return 0. por exemplo. incrementar ou . Não podemos fazer algumas coisas que fazíamos com ponteiros "normais".Programar em C/Estudo tinhamos um int a dividir por um int que dava um número real e só nos aparecia o resultado da divisão como um int. No programa acima. } void PrintString (char *str. ou tipo_de_retorno (*nome_do_ponteiro)(declaração_de_parâmetros). concluímos que o nome de uma função (sem os parênteses) é.". int (*func)(const char *)) { (*func)(str). /* Declaracao do ponteiro para função Funcao apontada e' inteira e recebe como parametro uma string constante */ /* O ponteiro p passa a apontar para a função puts que tem o seguinte prototipo: int puts(const char *) */ /* O ponteiro é passado como parametro para PrintString */ PrintString (String. como. Veja um exemplo do uso de ponteiros para funções: #include <stdio. 51 Ponteiros para Funções O C permite que acessemos variáveis e funções através de ponteiros! Podemos então fazer coisas como. em vez de ser um float. ao ponteiro para funções p. ). Estas formas são equivalentes entre si. não é obrigatório se declarar os parâmetros da função. o endereço da função puts() do C. pois bem aqui é parecido mas com as funções. Repare nos parênteses que devem ser colocados obrigatoriamente. por exemplo.h> #include <string.h> void PrintString (char *str. nós não vamos avançar mais. as duas formas alternativas de se chamar uma função através de um ponteiro. O programador pode então fornecer não só a string mas também a função que será usada para imprimí-la. Disto. func(str). int (*func)(const char *)). Se declaramos: tipo_de_retorno * nome(declaração_de_parâmetros). Um ponteiro para uma função tem a seguinte declaração: tipo_de_retorno (*nome_do_ponteiro)(). e func(str). a função PrintString() usa uma função qualquer func para imprimir a string na tela. o endereço daquela função! Note. podemos atribuir a este ponteiro o endereço de uma função e podemos também chamar a função apontada através dele. p=puts. int (*p)(const char *). p). fizemos esta chamada por: (*func)(str). } /* chamada a função através do ponteiro para função */ /* maneira também válida de se fazer a chamada a função puts através do ponteiro para função func */ Veja que fizemos a atribuição de puts a p simplesmente usando: p = puts. no programa. Porém. No main() vemos como podemos atribuir. Em síntese. ao declarar um ponteiro para função. Estaríamos. passar uma função como argumento para outra função. na realidade. declarando uma função que retornaria um ponteiro para o tipo especificado. na realidade. também.

. pelo que eu percebi de dados dinâmicos é utilizar uma memória que é dinâmica. um vetor ou uma matriz cujo tamanho descobriremos em tempo de execução. O padrão C ANSI define apenas 4 funções para o sistema de alocação dinâmica. int a. } for (i=0. poderemos definir.h> #include <stdlib. p=malloc(a*sizeof(int)). Veja um exemplo de alocação dinâmica com malloc(): #include <stdio.. O ponteiro void * pode ser atribuído a qualquer tipo de ponteiro. A função toma o número de bytes que queremos alocar (num). malloc A função malloc() serve para alocar memória e tem o seguinte protótipo: void *malloc (unsigned int num).Programar em C/Estudo decrementar um ponteiro para função. i<a . Se não houver memória suficiente para alocar a memória requisitada a função malloc() retorna um ponteiro nulo. ie. isto basicamente é a historia de memória ram e rom. /* Determina o valor de a em algum lugar */ /* Aloca a números inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) { printf ("** Erro: Memoria Insuficiente **"). quando utilizamos um programa é utilizada essa memória e depois quando chamamos outro programa a mesma memória que utilizou o programa anterior é agora utilizada para o novo programa. i++) p[i] = i*i. mas dependentes do ambiente e compilador. return 0. int i. /* p pode ser tratado como um vetor com a posicoes */ /* Para usar malloc() */ . .h: No entanto. Assim.. disponíveis na biblioteca stdlib. mas ressalve que os dados importantes do resultado do programa anterior são gravados. aloca na memória e retorna um ponteiro void * para o primeiro byte alocado. existem diversas outras funções que são amplamente utilizadas.h> main (void) { int *p.. exit. Neste curso serão abordadas somente estas funções padronizadas. . 52 Alocação Dinâmica A alocação dinâmica permite ao programador alocar memória para variáveis quando o programa está sendo executado. por exemplo.

O operador sizeof() retorna o número de bytes de um inteiro. vamos ver agora um exemplo. return 0. *p). if (p == 0) { printf("ERROR: Out of memory\n"). p = malloc(sizeof(int)). é alocada memória suficiente para se armazenar a números inteiros. *q). int main() { int *p. } *p = 5.Programar em C/Estudo } 53 No exemplo acima. o que fará com que !p retorne verdadeiro. printf("&d\n". Assim a função malloc retorna 0 se não consegue obter o tal espaço de memória e 1 se consegue. Ele é util para se saber o tamanho de tipos. return 1. . Se a operação tiver sido bem sucedida. int main() { int *p. se consegui arranjar o espaço de memoria ou não. indexando-o de p[0] a p[(a-1)]. Se não tiver sido. Como é um int está a pedir 4 bytes. printf("%d\n". O ponteiro void* que malloc() retorna é convertido para um int* pelo cast e é atribuído a p. Se consegue então aloca um pointer á variável p A seguinte linha de código mostra o valor a nós pelo ecrã qual o valor retornado pela função malloc. *q = 20. q = p. *q. a função malloc pegunta ao Heap (pretence ao sistema operativo) “existe memória disponível para um bloco de memória deste tamanho? e que tamnaho é esse? esse valor é depreendido pela função sizeof(int). p = malloc(sizeof(int)). p terá um valor nulo. } Vamos começar por tentar explicar esta linha de código. p = malloc(sizeof(int)). por exemplo. *p = 10. A declaração seguinte testa se a operação foi bem sucedida. Os programas utilizam muito esta função que reserva um bloco de memória que podemos utilizar á vontade para o nosso programa e quando o bloco de código é executado ele é reciclado pelo sistema operativo. podemos usar o vetor de inteiros alocados normalmente. free(p).

p = malloc(sizeof(int)). 64 bits a primeira declaração torna as coisas mais portáveis. por p = malloc(4). printf("%d\n".. unsigned int size).h> main (void) { int *p. } for (i=0. O ponteiro void * pode ser atribuído a qualquer tipo de ponteiro. i<a . q = malloc(sizeof(int)). p=calloc(a.sizeof(int)). mas como temos sistemas operativos de 16. Se não houver memória suficiente para alocar a memória requisitada a função calloc() retorna um ponteiro nulo.. calloc A função calloc() também serve para alocar memória. aloca memória suficiente para um vetor de num objetos de tamanho size. *p). *q).32. if (!p) { printf ("** Erro: Memoria Insuficiente **"). *q = 20. *p = 10. mas possui um protótipo um pouco diferente: void *calloc (unsigned int num. .h> #include <stdlib. int a.Programar em C/Estudo printf("%d\n". } Outro exemplo int main() { int *p. int i. *p = *q. Retorna um ponteiro void * para o primeiro byte alocado. A funçao aloca uma quantidade de memória igual a num * size. Veja um exemplo de alocação dinâmica com calloc(): #include <stdio. Repare que utilizamos o typecasting (int *) que força a conversão do pointer retornado do malloc que seja um pointer para um int. } /*podemos simplificar por p = malloc(4) */ 54 • o compilador aceita *p=*q porque são ambos int. • o compilador aceita também p=q porque são ambos pointes e apontam para a mesma tipologia Podemos simplificar p = malloc(sizeof(int)). i++) /* p pode ser tratado como um vetor com a posicoes */ /* Determina o valor de a em algum lugar */ /* Aloca a números inteiros p pode agora ser tratado como um vetor com a posicoes */ /* Para usar calloc() */ . *q. exit. isto é.

*/ /* Aloca a números 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*/ . um ponteiro nulo é devolvido e o bloco original é deixado inalterado. a*sizeof(int)). #include <stdio. a = 100.. o que fará com que !p retorne verdadeiro. realloc A função realloc() serve para realocar memória e tem o seguinte protótipo: void *realloc (void *ptr. a = 30.. .. Um ponteiro para o bloco é devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se ptr for nulo. O ponteiro void * que calloc() retorna é convertido para um int * pelo cast e é atribuído a p. o conteúdo do bloco antigo é copiado no novo bloco. i<a . unsigned int num). /* 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. i++) p[i] = a*i*(i-6). int a. O valor de num pode ser maior ou menor que o original. .. i++) p[i] = i*i. p=malloc(a*sizeof(int)).. A funçao modifica o tamanho da memória previamente alocada apontada por *ptr para aquele especificado por num. int i. podemos usar o vetor de inteiros alocados normalmente. se size é zero. A declaração seguinte testa se a operação foi bem sucedida. .. Se isso ocorrer.Programar em C/Estudo p[i] = i*i. if (!p) { printf ("** Erro: Memoria Insuficiente **").. } 55 No exemplo acima. por exemplo. indexando-o de p[0] a p[(a-1)]. } for (i=0. é alocada memória suficiente para se colocar a números inteiros. e nenhuma informação é perdida. por algum motivo . p terá um valor nulo. p = realloc (p. aloca size bytes e devolve um ponteiro. i<a . exit. O operador sizeof() retorna o número de bytes de um inteiro.h> main (void) { int *p. a memória apontada por ptr é liberada. Se a operação tiver sido bem sucedida. Se não houver memória suficiente para a alocação.h> #include <stdlib. Ele é util para se saber o tamanho de tipos. return 0. Se não tiver sido. for (i=0..

} . } Alocação Dinâmica de Vetores e Matrizes Alocação Dinâmica de Vetores A alocação dinâmica de vetores utiliza os conceitos aprendidos na aula sobre ponteiros e as funções de alocação dinâmica apresentados. Basta então passar para free() o ponteiro que aponta para o início da memória alocada. Vamos reescrever o exemplo usado para a função malloc() usando o free() também agora: #include <stdio. return 0.. int a. ele guardou o número de bytes alocados numa "tabela de alocação" interna.. } 56 free Quando alocamos memória dinamicamente é necessário que nós a liberemos quando ela não for mais necessária.Programar em C/Estudo return 0. exit. if (n < 1) { printf ("** Erro: Parametro invalido **\n"). Um exemplo de implementação para vetor real é fornecido a seguir: #include <stdio.h> #include <stdlib.. /* verifica parametros recebidos */ /* ponteiro para o vetor */ . free(p). Mas você pode se perguntar: como é que o programa vai saber quantos bytes devem ser liberados? Ele sabe pois quando você alocou a memória. p=malloc(a*sizeof(int)). . if (!p) { printf ("** Erro: Memoria Insuficiente **").h> /* Para usar malloc e free */ main (void) { int *p. return (NULL)... .. Para isto existe a função free() cujo protótipo é: void free (void *p).h> float *Alocar_vetor_real (int n) { float *v.h> #include <stdlib.

} return (v). utilizando p[] normalmente */ Alocação Dinâmica de Matrizes A alocação dinâmica de memória para matrizes é realizada da mesma forma que para vetores. A indireção múltipla pode ser levada a qualquer dimensão desejada. sizeof(float)).h> #include <stdlib. com a diferença que teremos um ponteiro apontando para outro ponteiro que aponta para o valor final. } int main (void) { float *p. p = Liberar_vetor_real (p). 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). #include <stdio.. int i. return (NULL). . sendo que cada ponteiro aponta para o início de uma linha da matriz. /* ponteiro para a matriz */ /* variavel auxiliar */ if (m < 1 || n < 1) { /* verifica parametros recebidos */ . mas raramente é necessário mais de um ponteiro para um ponteiro... .Programar em C/Estudo } v = calloc (n. } /* outros comandos. } float *Liberar_vetor_real (float *v) { if (v == NULL) return (NULL).. free(v). o que é denominado indireção múltipla. if (v == NULL) { printf ("** Erro: Memoria Insuficiente **"). como descrito anteriormente (compondo o segundo índice da matriz). return (NULL).h> float **Alocar_matriz_real (int m. ou seja é um ponteiro para ponteiro. int n) { float **v. int a. Em cada linha existe um vetor alocado dinamicamente. Um exemplo de implementação para matriz real bidimensional é fornecido a seguir. /* outros comandos. A estrutura de dados utilizada neste exemplo é composta por um vetor de ponteiros (correspondendo ao primeiro índice da matriz).

mat = Alocar_matriz_real (l. j. } for ( i = 0. . /* 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").. } int main (void) { float **mat. for (i = 0. } v = calloc (m.. . inclusive inicializacao para l e c */ mat = Liberar_matriz_real (l. i < l.. sizeof(float *)). i < m. float **v) { int i. return (NULL). j < c. sizeof(float)). i++ ) { v[i] = calloc (n. int n. free (v). return (v). if (v[i] == NULL) { printf ("** Erro: Memoria Insuficiente **"). } . mat). c.. /* outros comandos utilizando mat[][] normalmente */ /* outros comandos. return (NULL). i++) free (v[i]). if (v == NULL) { printf ("** Erro: Memoria Insuficiente **"). c.. } } return (v).Programar em C/Estudo printf ("** Erro: Parametro invalido **\n"). } float **Liberar_matriz_real (int m. j++) mat[i][j] = i+j. . /* 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. int l. i++) for ( j = 0. i<m. return (NULL). } for (i=0. return (NULL). c)..

a=5.. então.b.Programar em C/Estudo 59 Estruturas . podemos atribuir o valor á variável a do grupo fazendo: r. struct rec { int a. formando um novo tipo de dados. } r. chamando essas variáveis todas por um nome apenas. Criando Para se criar uma estrutura usa-se o comando struct. A estrutura.Primeira parte Uma estrutura agrupa várias variáveis numa só. Foram também declaradas duas . mas admito que pode ser útil quando temos um código muito grande e temos muitas variáveis. O nome_do_tipo_da_estrutura é o nome para a estrutura. tipo_n nome_n. A ficha seria uma estrutura. e convém agrega-las numa forma lógica. no nosso exemplo chamámos de “rec” Podemos compactar da seguinte forma. b. são equivalentes. serve para agrupar um conjunto de dados não similares. o c permite que nós agreguemos várias variáveis. float d. float f.b. est é uma estrutura com dois campos. Neste caso. As variáveis_estrutura são opcionais e seriam nomes de variáveis que o usuário já estaria declarando e que seriam do tipo nome_do_tipo_da_estrutura. não vejo grande vantagem.e. tipo_2 nome_2.c. struct rec { int a. struct rec r. . } a.e. }. telefone e endereço.c.. Confesso que não entendi bem isto. Sua forma geral é: struct nome_do_tipo_da_estrutura { tipo_1 nome_1. Um primeiro exemplo: struct est{ int i. i e f. } variáveis_estrutura.f. Funciona como uma ficha pessoal que tenha nome. float d.f.

struct tipo_endereco endereco.h> #include <string. long int CEP.Programar em C/Estudo variáveis. long int telefone. o mesmo acontecendo com b. }. Vemos. #include <stdio. long int CEP. }. }. Vamos criar uma estrutura de endereço: struct tipo_endereco { char rua [50]. char bairro [20]. . char sigla_estado [3]. que uma estrutura pode fazer parte de outra ( a struct tipo_endereco é usada pela struct ficha_pessoal). pelos exemplos acima. a possui os campos i e f. char cidade [30]. int numero. a e b que são do tipo da estrutura. long int telefone. struct ficha_pessoal { char nome [50]. char bairro [20]. Vamos agora criar uma estrutura chamada ficha_pessoal com os dados pessoais de uma pessoa: struct ficha_pessoal { char nome [50]. int numero. 60 Usando Vamos agora utilizar as estruturas declaradas na seção anterior para escrever um programa que preencha uma ficha. char cidade [30]. int main (void) { struct ficha_pessoal ficha. char sigla_estado [3]. isto é. }.h> struct tipo_endereco { char rua [50]. struct tipo_endereco endereco.

numero=10.i ."Luiz Osvaldo Silva"). /* Declara primeira e segunda como structs do tipo est1 */ printf(" Os valores armazenasdos na segunda struct sao : . }.sigla_estado. Vamos ver como ficaria a declaração de um vetor de 100 fichas pessoais: struct ficha_pessoal fichas [100]. copiar uma estrutura.endereco. O exemplo mostra como podemos acessar um elemento de uma estrutura: basta usar o ponto (. criar matrizes de estruturas. escrevemos: ficha. strcpy (ficha.sigla_estado[1]. primeira. portanto.endereco.. Podemos. Poderíamos então acessar a segunda letra da sigla de estado da décima terceira ficha fazendo: fichas[12]. podemos fazer acesso aos campos desta struct interna da seguinte maneira: ficha. Analise atentamente como isto está sendo feito . 61 Matrizes de estruturas Uma estrutura é como qualquer outro tipo de dado no C. estamos acessando. primeiramente.telefone = 4921234.CEP=31340230.nome. para acessar o campo telefone de ficha. return 0. estamos acessando o campo numero e o campo CEP. segunda.Programar em C/Estudo strcpy (ficha. Assim. Desta forma.endereco.rua. strcpy (ficha. Como a struct ficha pessoal possui um campo. ficha."Belo Horizonte"). strcpy (ficha. dentro deste campo."Rua das Flores"). que também é uma struct.telefone=4921234.f = 3. ficha.endereco."Cidade Velha").f). } O programa declara uma variável ficha do tipo ficha_pessoal e preenche os seus dados. primeira. Atribuindo Podemos atribuir duas estruturas que sejam do mesmo tipo. Veja o programa abaixo: struct est1 { int i.endereco. endereco. /* A segunda struct e' agora igual a primeira */ %d e %f ". segunda = primeira. ficha..1415.cidade. campo por campo.). int main() { struct est1 primeira.numero = 10. float f.CEP=31340230.i = 10.endereco.endereco.bairro. ficha.endereco. segunda. na outra. o campo endereco da struct ficha e. segunda. O C irá. neste caso. strcpy (ficha.endereco."MG").

rua. o campo .rua.h> #include <string. buffer). devemos tomar cuidado na atribuição de structs que contenham campos ponteiros. end2 = end1. Note que isto é diferente do que acontecia em vetores.numero). strcpy(end2. onde.rua. pois ao se fazer a atribuição end2 = end1. uma chamada primeira e outra chamada segunda. /* Le o nome da rua em uma string de buffer */ end1. Todos os campos de primeira serão copiados na segunda.numero).numero.rua causara' a modificacao do que e' apontado por end1. /* Aloca a quantidade de memoria suficiente para armazenar a string */ strcpy(end1. end2.Programar em C/Estudo } 62 São declaradas duas estruturas do tipo est1.h> struct tipo_end { char *rua. Nas structs é muito mais fácil! Porém.rua. para fazer a cópia dos elementos de um vetor em outro.numero.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".end2. scanf("%d".rua e end1. &end1. /* ERRADO end2. end1. Veja abaixo: #include <stdio.rua = malloc((strlen(buffer)+1)*sizeof(char)).rua. gets(buffer). "Rua Mesquita"). o que. end1. end2. } Neste programa há um erro grave.numero = 1100. /* A struct possui um campo que é um ponteiro */ int numero. end2. printf("\nEntre o nome da rua:").h> #include <stdlib.rua.numero). end2.rua. int main() { struct tipo_end end1. Os valores de primeira são copiados em segunda apenas com a expressão de atribuição: segunda = primeira. Atribuem-se valores aos dois campos da struct primeira. char buffer[50]. /* Copia a string */ printf("\nEntre o numero:"). }. tínhamos que copiar elemento por elemento do vetor. /* 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.end1. /* Uma modificacao na memoria apontada por end2. esta' errado !!! */ end2.

. da seguinte maneira: struct ficha_pessoal ficha. como em qualquer outra função no C. } /* Exemplo de acesso ao campo telefone da primeira ficha apontada por p */ . free(p).Programar em C/Estudo rua de end2 estará apontando para a mesma posição de memória que o campo rua de end1. Neste comando um elemento de uma estrutura é passado para uma função. Mais uma vez podemos contornar este pormenor usando ponteiros e passando para a função um ponteiro para a estrutura. Devemos observar que. em uma variável local da função PreencheFicha. campo por campo. Para usá-lo. struct ficha_pessoal *p. Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. vimos o seguinte comando: strcpy (ficha. A estrutura que está sendo passada. p = &ficha.rua !!! 63 Passando para funções No exemplo apresentado no ítem usando. haveria duas possibilidades.nome. Este tipo de operação pode ser feita sem maiores considerações.rua estaremos também modificando o conteúdo apontado por end1."Luiz Osvaldo Silva"). malloc(): #include <stdlib. a passagem da estrutura é feita por valor. Isto significa que alterações na estrutura dentro da função não terão efeito na variável fora da função. Assim. p[0]. Vamos ver como poderia ser declarado um ponteiro para as estruturas de ficha que estamos usando nestas seções: struct ficha_pessoal *p. por exemplo.telefone = 3443768. A segunda é alocando memória para ficha_pessoal usando. Veja a seguinte função: void PreencheFicha (struct ficha_pessoal ficha) { .h> main() { struct ficha_pessoal *p. ao se modificar o conteúdo apontado por end2. /* Faremos a alocacao dinamica de 10 fichas pessoais */ p = malloc (a * sizeof(struct ficha_pessoal)). vai ser copiada. Podemos também passar para uma função uma estrutura inteira. int a = 10.. Ponteiros Podemos ter um ponteiro para uma estrutura. A primeira é apontá-lo para uma variável struct já existente. } Como vemos acima é fácil passar a estrutura como um todo para a função.

ocupam o mesmo local da memória.Programar em C/Estudo Há mais um detalhe a ser considerado.CEP Fácil. porque o operador . a memória alocada corresponde ao tamanho da maior variável no union. que é formado por um sinal de "menos" (-) seguido por um sinal de "maior que" (>). não? 64 Declaração Union Uma declaração union determina uma única localização de memória onde podem estar armazenadas várias variáveis diferentes. temos duas variáveis (graus e radianos) que. tipo_2 nome_2. Como exemplo.h> #define GRAUS 'G' #define RAD 'R' union angulo . apesar de terem nomes diferentes. Assim faremos: p->nome A declaração acima é muito mais fácil e concisa. isto é: -> . }. Uniões podem ser feitas também com variáveis de diferentes tipos. ) e quisermos acessar um elemento da estrutura poderíamos fazer: (*p). Nela. float radianos. Para acessarmos o elemento CEP dentro de endereco faríamos: p->endereco. tem precedência maior que o operador * . Neste caso. . Porém. Isto quer dizer que só gastamos o espaço equivalente a um único float. A declaração de uma união é semelhante à declaração de uma estrutura: union nome_do_tipo_da_union { tipo_1 nome_1.. este formato não é muito usado. } variáveis_union.nome Os parênteses são necessários. vamos considerar a seguinte união: union angulo { float graus. tipo_n nome_n.. O que é comum de se fazer é acessar o elemento nome através do operador seta. Veja o exemplo: #include <stdio. Se apontarmos o ponteiro p para uma estrutura qualquer (como fizemos em p = &ficha.

ang. } else if (op == RAD) { ang. float radianos.F). que foi "gravada" como um inteiro. main (void) { union numero N. printf ("%f". N.Programar em C/Estudo { int graus.1415. Tome cuidado! O resultado pode não fazer sentido. }. printf("\nAngulo: %d\n".. } Temos que tomar o maior cuidado pois poderíamos fazer: #include <stdio.*/ 65 O programa acima é muito perigoso pois você está lendo uma região da memória. char op.I = 123. int I.ang. printf("\nAngulo: %f\n". int main() { union angulo ang. return 0.radianos).graus = 180. } /* Vai imprimir algo que nao e' necessariamente 123 .radianos = 3. printf("\nNumeros em graus ou radianos? (G/R):"). } else printf("\nEntrada invalida!!\n"). if (op == GRAUS) { ang. .N.&op).graus). float F.. }. como se fosse um ponto flutuante. scanf("%c".h> union numero { char Ch.

} else { printf ("São dias diferentes. domingo}. criada na primeira página desta aula. O programador diz ao compilador que qualquer variável do tipo dias_da_semana só pode ter os valores enumerados. é associado o número zero. Vamos considerar o seguinte exemplo: enum dias_da_semana {segunda. quarta. Ele retorna o tamanho do tipo ou variável em bytes. domingo}. Sua forma geral é: enum nome_do_tipo_da_enumeração {lista_de_valores} lista_de_variáveis. O compilador pega a lista que você fez de valores e associa. se não . Simples. o segundo ao número 1 e assim por diante. o tamanho de um inteiro pode depender do sistema para o qual se está compilando. if (d1==d2) { printf ("O dia e o mesmo. Então. Seria.d2. Isto quer dizer que poderíamos fazer o seguinte programa: #include <stdio. por exemplo. d1=segunda. O Comando sizeof O operador sizeof é usado para se saber o tamanho de variáveis ou de tipos. um número inteiro. quinta. Por exemplo.").sabado. O sizeof admite duas formas: sizeof nome_da_variável sizeof (nome_do_tipo) Se quisermos então saber o tamanho de um float fazemos sizeof(float). Outra aplicação importante do operador sizeof é para se saber o tamanho de tipos definidos pelo usuário. d2=sexta. uma tarefa um tanto complicada a de alocar a memória para um ponteiro para a estrutura ficha_pessoal. sexta. O sizeof é um operador porque ele é substituído pelo tamanho do tipo ou variável no momento da compilação. Ele não é uma função. sabado. } Você deve estar se perguntando como é que a enumeração funciona. Se declararmos a variável f como float e quisermos saber o seu tamanho faremos sizeof f. terca.h> enum dias_da_semana {segunda. quinta. a cada um. ao primeiro da lista. sexta. terca. uniões e enumerações."). quarta. Devemos usá-lo para garantir portabilidade. As variáveis declaradas são então variáveis int. } return 0. O operador sizeof também funciona com estruturas.Programar em C/Estudo 66 Enumerações Numa enumeração podemos dizer ao compilador quais os valores que uma determinada variável pode assumir. main (void) { enum dias_da_semana d1.

int numero.h> typedef struct tipo_endereco { char rua [50]. struct ficha_pessoal { char nome [50].Programar em C/Estudo fosse o uso de sizeof. O exemplo ficaria: #include <stdio. long int telefone. como as estruturas. .. } TEndereco. }. As estruturas criadas no exemplo da página anterior poderiam ser definidas como tipos através do comando typedef.. struct tipo_endereco endereco. char bairro [20]. }. long int CEP. int main(void) { struct ficha_pessoal *ex = malloc(sizeof(struct ficha_pessoal)). Veja o exemplo: #include <stdio. char sigla_estado [3]. free(ex). } 67 O Comando typedef O comando typedef permite ao programador definir um novo nome para um determinado tipo. int numero. char cidade [30]. Sua forma geral é: typedef antigo_nome novo_nome. O comando typedef também pode ser utilizado para dar nome a tipos complexos. char bairro [20]. Agora podemos declarar o tipo inteiro. long int CEP. typedef struct ficha_pessoal { . Como exemplo vamos dar o nome de inteiro para o tipo int: typedef int inteiro. return 0. char cidade [30]. char sigla_estado [3].h> struct tipo_endereco { char rua [50].

Esta estrutura de dados é criada dinamicamente na memória (utiliza-se malloc() e free()). int main(void) { TFicha *ex. long int telefone. }TFicha. de modo que se torna simples introduzir nós nela. 68 Uma aplicação de structs: as listas simplesmente encadeadas Várias estruturas de dados complexas podem ser criadas utilizando simultaneamente structs e ponteiros.h> typedef struct tipo_produto { /* Estrutura que será usada para criar os nós da lista */ .. Uma destas estruturas é a lista encadeada. ligados entre si através de ponteiros. É este campo que será utilizado para apontar para o próximo nó da lista encadeada. Cada nó contém um ponteiro que aponta para a struct que é a sua sucessora na lista. O programa a seguir faz uso desta struct. }.h> #include <stdlib. Supondo que queiramos criar uma lista encadeada para armazenar os produtos disponíveis em uma loja. Aqui. pois isto geralmente é feito em cursos de algoritmos e estruturas de dados. TEndereco endereco. retirar nós. Não vamos entrar em detalhes sobre todos os algoritmos que poderíamos criar em uma lista encadeada. veremos somente formas de se criar uma lista encadeada em C e também maneiras simples de percorrer esta lista. Esta seqüência pode ser acessada através de um ponteiro para o primeiro nó. para criar uma lista de produtos de uma loja: #include <stdio. double preco. O ponteiro da última struct da lista aponta para NULL. um campo adicional que é um ponteiro para uma struct do tipo Produto. Uma lista encadeada é uma seqüência de structs. . /* Codigo do produto */ /* Preco do produto */ /* Proximo elemento da lista encadeada de Produtos */ Note que esta struct possui. etc. struct Produto *proximo. além dos campos de dados codigo e preco. Poderíamos criar um nó desta lista usando a seguinte struct: struct Produto { int codigo.. } Veja que não é mais necessário usar a palavra chave struct para declarar variáveis do tipo ficha pessoal. ordenar os nós. que são os nós da lista. através de um novo tipo criado por um typedef. indicando que se chegou ao final da lista. Basta agora usar o novo tipo definido TFicha.Programar em C/Estudo char nome [50]. não se incluindo no escopo deste curso. que é a cabeça da lista.

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

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

Qual a razão disto? A razão é que o endereço para o qual a cabeça da lista aponta poderá ser modificado caso se esteja inserindo o primeiro elemento na lista. Toda vez que for necessário criar um novo produto. Também é importante notar que várias outras estruturas de dados complexas podem ser criadas com structs contendo ponteiros que apontam para outras structs.Programar em C/Estudo } É interessante notar que. no programa anterior não existe limite para o número de produtos que se vai armazenar na lista. Ir para o Indice | Exercícios 71 . pois ele possui várias das características presentes em programas que manipulam listas encadeadas. Note que a função inserir recebe o endereço do ponteiro cabeça da lista. memória para ele será alocada e ele será criado no final da lista. Tente entender todos os passos deste programa.

Fontes e Editores da Página 72 Fontes e Editores da Página Programar em C/Estudo  Fonte: http://pt. Marcos Antônio Nunes de Moura. 0/ . Lightningspirit.php?oldid=214409  Contribuidores: Alguém.org/w/index. EvertonS. Edudobay. org/ licenses/ by-sa/ 3. 19 edições anónimas Licença Creative Commons Attribution-Share Alike 3. Jorge Morais.0 Unported http:/ / creativecommons.wikibooks.

Sign up to vote on this title
UsefulNot useful