Sie sind auf Seite 1von 63

Curso de Sistemas de Informao

Linguagem de Programao C Apostila com conceitos bsicos

Linguagem C

Prof Msc Rafael Gasto Maro de 2011

Linguagem de Programao C

ULBRA/SJ

Introduo

A linguagem "C" foi criada nos laboratrios Bell por Brian W. Kernighan e Dennis Ritchie em 1972. Esta linguagem, teve suas idias iniciais originadas da linguagem BCPL (Basic Combined Programming Language), desenvolvida por Martin Richards. Esta influncia do BCPL se deu atravs de outra linguagem, chamada "B" e criada por Thompson em 1970 para o primeiro sistema operacional UNIX no PDP-11. A partir de sua criao a linguagem "C" sofreu uma longa evoluo sendo que uma de suas primeiras utilizaes foi a de reescrever o sistema operacional UNIX (1973) que estava escrito em linguagem assembly do PDP-11. Por este motivo que se tem associado a linguagem ao S.O. UNIX, visto que o UNIX composto, quase na sua totalidade, de programas escritos em "C" (Sistema Operacional, utilitrios, compiladores, ...). Entretanto isto no implica que o "C" seja uma linguagem "amarrada" a um sistema operacional ou mquina. Devido evoluo do "C", que seguia apenas o padro descrito por Kernighan e Ritchie, tornouse necessria uma padronizao mais rgida para a linguagem, permitindo a portabilidade dos softwares escritos nesta linguagem. Isto foi feito pelo ANSI (American National Standard Institute), criando assim o padro C ANSI. 1.1 Caratersticas da Linguagem O "C" uma linguagem de propsitos gerais e que tem como caractersticas principais:

Controle de fluxo e estrutura de dados adotando conceitos modernos de linguagens de programao; Poderoso conjunto de operadores e tipos de dados; Permite a gerao de um cdigo bem otimizado e compacto (quase to otimizado quanto o assembly); Grande portabilidade de programas (a maioria das mquinas existentes no mercado suportam a linguagem "C"); uma linguagem de nvel "relativamente baixo", mas com recursos de alto nvel; Apresenta facilidade de manipulao direta do hardware da mquina; Grande "liberdade" para o programador.

Uso de bibliotecas de funes, expandindo as pontencialidades da linguagem;

Cap 1: Introduo

pgina 2

Linguagem de Programao C

ULBRA/SJ

Dados e Operadores do C

O C possui, como toda linguagem, um conjunto bsico de dados, os operadores e um esqueleto bsico de programa. 2.1 Tipos de Dados Bsicos Em C, os tipos bsicos de dados so:

char: um nico caracter, sendo represesentado por apenas 1 byte. int: um valor inteiro, podendo ser 2 (DOS) ou 4 (Linux, Windows) bytes. float: um valor real de preciso simples; double: um valor real de preciso dupla; "ponteiros": usados para indicar o endereo de alguma varivel ou rea de dados

Ao contrrio do que ocorre nas maioria das linguagens de programao, como Pascal, C no possui um tipo para cadeias de caracteres (strings). Para utilizar strings em C necessrio declarar uma varivel como sendo um vetor de caracteres. Desta forma para armazenar um nome de 30 caracteres usa-se o seguinte trecho de programa:
char nome[31]; */ /* Alm dos 30 caracteres, deve-se reservar espao para o final de string: 0, '\0' ou NULL

2.2

Definio de Variveis

Para declarar uma varivel ou um conjunto de variveis, basta especificar o tipo e a seguir a lista de variveis:
<tipo> <nomev> [ {, <nomev> } ] ;

como por exemplo:


int val; char a,b; double d1,d2,x,y;

Como a linguagem C distingue letras maisculas de minsculas, os nomes de variveis so sensveis ao contexto. Variveis podem ser declaradas fora do escopo das funes (neste caso so consideradas globais) ou no incio de um bloco (neste caso) so locais e seus escopos esto restritos aos blocos em que foram declaradas). A declarao de variveis locais deve obrigatoriamente ser a primeira parte de um bloco, ou seja, deve vir logo aps um caracter de "abre chaves", '{'; e no deve ser intercalada com instrues ou comandos.
Cap 2: Dados e Operadores do C pgina 3

Linguagem de Programao C

ULBRA/SJ

/* int v; /* ...

A declarao de variveis a primeira coisa */ que deve ser feita dentro de um bloco */

Quanto aos nomes ou identificadores usados na declarao de variveis, deve-se considerar as seguintes regras: nomes de variveis comeam com uma letra ('A'..'Z', 'a'..'z') ou pelo underscore ('_'); aps podem ser seguidos dgitos, letras e underscores. No caso do TC podemos ter at 32 caracteres definindo um identificador; evite o uso do '_' (underscore) no primeiro caracter do identificador de uma varivel, pois este tipo de identificadores de uso do sistema; so diferenciados os caracteres minsculos dos maisculos no nome de qualquer varivel. 2.3 Definio de Constantes

Constantes no C podem ser definidas com a palavra reservada #define, com a seguinte sintaxe: #define <nome_da_constante> valor Da mesma forma como nas outras linguagems, uma constante no faz parte do cdigo, ou seja, no gera cdigo. Na verdade uma constante como se fosse um comando Substituir existente em quase todos os editores de texto. Exemplos:
#define PI 3.14 #define ARQUIVO lixo.dat

Na definio de uma constante no h o ; no final. Se for colocado, este far parte do valor associado constante. Veja exemplos a seguir:
/* Cdigo digitado */ #define PI 3.14 #define SOMA 100+120; ... a = PI; x = SOMA + 4; /* Cdigo que ser compilado, depois do pr processador do C resolver as constantes */ ... a = 3.14; x = 100+120; + 4; /* Erro na linha acima!!!!! */

Pelo exemplo fica claro como o C trata uma constante. Como foi colocado um ; no final da constante SOMA, esta passa a ser parte do valor da mesma, causando um erro na penltima linha do cdigo, erro que ser difcil descobrir, pois, a primeira vista, esta linha est perfeita (o compilador C

Cap 2: Dados e Operadores do C

pgina 4

Linguagem de Programao C

ULBRA/SJ

informar o erro existente na penltima linha e no na definiao de constantes). Muito cuidado com o uso de constantes no C (assim como em outras linguagens). 2.4 Operadores Aritmticos Operam sobre nmeros e expresses, resultando valores numricos. =atribuio +soma -subtrao *multiplicao /diviso %mdulo da diviso (resto da diviso inteira) -sinal negativo (operador unrio) Alguns Exemplos:
int x, vet[10], z; char a; ... x = 20; z = 30; vet[5] = x + y; a = x % 2; /* O C permite atribuio para tipos diferentes !! */

2.5

Operadores Relacionais e lgicos Operam sobre expresses, resultando valores lgicos de TRUE ou FALSE.

Cuidado! no existem os operadores relacionais: "=<", "=>" e "<>". No confunda a atribuio ("=") com a comparao ("=="). Os operadores lgicos so os seguintes:

Estes operadores lgicos so usados quando se deseja que mais de uma ou apenas uma condio seja VERDADEIRA. No confundir com operadores binrios AND e OR, que mexem diretamente com os bits dos dados (visto adiante).

Cap 2: Dados e Operadores do C

pgina 5

Linguagem de Programao C

ULBRA/SJ

Estes operadores possuem o conceito de short-circuit, ou seja, uma forma inteligente de testar expresses. O C testa apenas o que necessrio e se j tem condies de avaliar toda a expresso, para com o teste. Exemplos de "short circuit":
(a == b) && (b == c) /* Se a != b no avalia o resto da expresso */ (a == b) || (b == c) /* Se a == b no avalia o resto da expresso */

O C tambm permite algumas liberdades no uso destes operadores. Na maioria das linguagens, estes operadores so de uso exclusivo para comandos de seleo ou controle de laos. Os operadores relacionais e lgicos so tipicamente utilizados em comandos de seleo (Se-ento-seno) e de laos (enquanto-faa), mas o C trata cada comando relacional como uma expresso que pode ser usada inclusive em atribuies. Os exemplos estranhos mostrados a seguir so perfeitamente aceitos pelo C e possuem uma lgica (estudado mais adiante):
int x=20, y=0, z=100; y = x > 100; y = ((x > 0)&&(z > x));

2.6

Operadores para manipulao de Bits

A manipulao feita em todos os bits da varivel a qual no pode ser do tipo float ou double. Estes operadores executam a tabela verdade para todos os bits envolvidos na operao.

Para entender exatamente o que estes operadores fazem, deve-se conhecer como os nmeros so representados em binrio, bem como o tamanho de cada varivel. Considere variveis do tipo char, que possuem 8 bits (1 byte) para os exemplos a seguir:

Cap 2: Dados e Operadores do C

pgina 6

Linguagem de Programao C

ULBRA/SJ

char a,b,c; b = 21; c = 9; a = b & c; /* em binrio: 0001 0101 */ /* em binrio: 0000 1001 */ /* Esta-se fazendo: AND = a receber 1 */ /* Esta-se fazendo: OR = a receber 29 */ /* Esta-se fazendo: XOR = a receber 28 */ 0001 0101 (21) 0000 1001 (9) 0000 0001 (1)

a = b | c;

0001 0101 (21) 0000 1001 (9) 0001 1101 (29)

a = b ^ c;

0001 0101 (21) 0000 1001 (9) 0001 1100 (28)

a = c << 1;

/* Os bits de c so rotacionados uma vez para a esquerda, perdendo-se o bit mais significativo (ultimo da esquerda) e colocando-se 0 no bit menos significativo (mais a direita): 0000 1001 (9) <<1 = 0001 0010 (18)

*/ a = ~c;

a receber 18

/* todos os bits de c so invertidos: 0000 1001 (9) 1111 0110 (246 ou -10) a receber 246 (se no for considerado sinal!!) se for usado sinal, a receber -10 */

Se as variveis fossem do tipo inteira, com 2 bytes (16 bits) no caso de um compilador 16bits como os compiladores para DOS, deve-se levar em conta que a operao ser feita sobre TODOS os oito bits. Se fosse inteiro para Linux ou Windows, seriam sobre 32 bits. 2.7 Operadores de Assinalamento , Pr e Ps-incremento

Alguns operadores (+, -, *, /, %, >>, <<, &, ^ , |) podem ser combinados com a atribuo, quando um dos operandos a prpria varivel que recebe a atribuio. Estes operadores diminuem o cdigo digitado, mas causam algumas confuses quanto a semntica.

Cap 2: Dados e Operadores do C

pgina 7

Linguagem de Programao C

ULBRA/SJ

Exemplos:
i +=2; i -=3; /* equivalente a: i = i + 2 */ /* Equivale a: i = i-3; */

O C tambm possui os chamados operadores de pr/prs incremento ou decremento. Quando operado com variveis inteiras, pode ser interpretado como soma 1 ou diminui 1. Cuidado porm, porque eles tem utilidade bem distinta em apontadores! Exemplos:
i i z z a a = = = = = = i + 1 i - 1 a; a = a; a = a + 1; a - 1; -> -> z = z = z = z = i = ++i i = --i a++ a-++a --a -> ou simplesmente ++i; -> ou simplesmente --i;

a a z z

+ = =

1 1 a a

-> -> -> ->

Existe uma grande diferena entre colocar o operador antes ou depois da varivel. Conceitualmente, executar a++ no o mesmo que executar ++a. No exemplo anterior, esta diferena fica bem clara nas ltimas quatro linhas. 2.8 Operadores de Endereo Usados com ponteiros (pointers), para acesso a endereos de memria. &Obtm o endereo de uma varivel. Exemplos: int var, x; x = &var; /* x recebe o endereo de var. ateno: esto omitidos detalhes. Na prtica, x no pode receber endereos, por ser do tipo int */ *contedo do endereo especificado. Exemplo: var = *x; 2.9 Funo sizeof()

Trata-se de uma funo embutida da linguagem que fornece o tamanho em bytes de um tipo, varivel ou estrutura. O valor retornado por sizeof() do tipo size_t. Este tipo est definido em diversos arquivos de cabealho, como, por exemplo, stdio.h, e compatvel com o tipo inteiro. Sua sintaxe : sizeof (objeto)

Cap 2: Dados e Operadores do C

pgina 8

Linguagem de Programao C

ULBRA/SJ

Exemplos:
double d; int x, z; /* variavel do tipo double */

x = sizeof(int) + sizeof(d); /* x deve receber o tamanho de um inteiro, mais o da variavel d */

2.10 Tabela de Operadores do "C" (Resumo) Op + ! ~ & * size of ++ -* / / % + >> << > >= < <= == != & | ^ && || = Funo menos unrio mais unrio negao lgica (TRUE ou FALSE) inverte todos os bits endereo de varivel referncia a ponteiro tamanho de variveis ps ou pr incremento ps ou pr decremento multiplicao diviso inteira diviso real resto da diviso soma subtrao shift right shift left maior que maior ou igual que menor que menor ou igual que igual a (TRUE ou FALSE) diferente de (TRUE ou FALSE) AND bit a bit (tabela verdade) OR bit a bit (tabela verdade) XOR bit a bit (tabela verdade) AND lgico (TRUE ou FALSE) Ou lgico (TRUE ou FALSE) atribuio a a ! a a a a = = x = = = = Exemplo "C" -b +b ~b &b *ptr sizeof(b)

++a ou a++ --a ou a-a = b * c a = b / c a = b / c a = b % c a = b + c a = b - c a = b >> n a = b << n a > b a >= b a < b a <= b a == b a != b a = b & c a = b | c a = b ^ c flag1 && flag2 flag1 || flag2 a = b

Cap 2: Dados e Operadores do C

pgina 9

Linguagem de Programao C

ULBRA/SJ

2.11 Exerccio: Considerando o trecho de programa a seguir(procure fazer sem o uso do computador):
int a, b, c, d, e, r; a b c d e = = = = = 36; 35; -2; 0; 3;

Qual o valor de r aps as expresses abaixo? Considere, para sua anlise, um inteiro de 16 bits (2 bytes). O que mudaria se o inteiro fosse de 32 bits (4 bytes) como o caso do windows e linux? Os valores para r seriam os mesmos? a) b) c) d) e) f) g) h) i) j) r r r r r r r r r r = = = = = = = = = = a | b; c | d; a & b; a - c; (a >> e) | b; (a + c) << (3 / 2); c / e * 2; ( (a^b) * (c|d) + 1 + e ) << 3; ~c; (a > b) == d; /* Desafio. O que ser que ocorre? */

Cap 2: Dados e Operadores do C

pgina 10

Linguagem de Programao C

ULBRA/SJ

Estrutura dos programas C

Os comandos de "C" devem ser escritos em minsculas e devem ser seguidos de um ";"., podendo haver mais de um comando na mesma linha, desde que separados por ponto e vrgula (no abuse disto pois torna o cdigo difcil de compreender). Os blocos so definidos de uma "{" at a outra "}" e equivalem aos blocos de Pascal definidos pelo "begin" e "end". Dentro de cada bloco pode-se definir um conjunto de comandos como se fossem um s, sendo que no C pode-se declarar novas variveis (variveis locais ao bloco) independentemente da posio deste bloco no programa. Formato:
{ incio de bloco [ <definio_de_variveis_locais> ] <comandos> } fim de bloco

3.1

Funes

So subrotinas (conjuntos de comando) que podem ser chamados de diferentes partes do programa quando necessrio. Retornam um valor ao final de sua execuo e podem receber parmetros de entrada para a sua execuo. Para executar uma funo basta referenciar o seu nome. A definio de funes NO pode ser "aninhada", ou seja, no podem haver funes dfentro de funes! Exemplo:
/* definio de uma funo */ int soma(int x, int y) { int total; total = x + y; return(total);

Cap 3: Estrutura dos programas C

pgina 11

Linguagem de Programao C

ULBRA/SJ

/* chamada da funo soma */ n = soma(j,k); n = soma (100, 200);

Pelo exemplo fica claro a NECESSIDADE de um retorno pelo uso do comando return. No C o return causa o IMEDIATO trmino da funo, mesmo que existam instrues depois do return. 3.2 Esqueleto de um programa em C

Todo o programa em C deve conter a funo main(). Esta funo responsvel pelo incio da execuo, de forma semelhante ao bloco BEGIN/END. do Pascal. No C, s existem funes e no existe o conceito de procedimentos, ou seja, todas devem retornar algo a quem a chamou, mesmo que o retorno seja do tipo void (sem valor). Os comentrios no C so feitos atravs do par /* e */, sendo um /* usado para abrir um comentrio e um */ para encerr-lo. Um bloco de comandos delimitado por chaves ({ e }), portanto cuidado para no confundir com o pascal!!! Exemplo:
/* No incio do programa, declara-se as bibliotecas usadas */ #include <stdio.h> /* Declarao de variveis globais */ /* Declarao de funes do programador, se for o caso */ /* declarao da funo principal. Sempre necessria */ int main () { /* variaveis locais ao main, se existirem */ // Isto tambm comentrio at o final da linha /* comandos ... */ } return(valor); // funo main tambm retorna valor

Existe, como mencionado anteriormente, uma funo especial nos programas chamada de main. Esta funo chamada sempre que um programa comea a ser executado. Quando o programa for executado pelo computador, ele iniciar a execuo pela funo main, que obrigatoriamente tem que estar presente em qualquer programa "C" (Exceto mdulos lincados). Considerando o exemplo da funo soma, citada anteriormente, o prximo exemplo mostra um programa completo, com cada parte em sua determinada posio.

Cap 3: Estrutura dos programas C

pgina 12

Linguagem de Programao C

ULBRA/SJ

/* Bibliotecas: nenhuma necessria para este exemplo */ /* Variaveis globais: bom ter o hbito de no us-las :-D */ /* Funes: apenas uma, nossa soma: */ int soma(int x, int y) { int total; total = x + y; return (total);

/* Funo principal */ int main() { int j, k, n; j = 40; k = 80; n = soma(j,k); n = soma (100, 200); n = soma(100, 200) + soma(j,k) -3;

3.3

Funes de E/S bsicas

O C no possui nenhum comando interno para E/S, necessitando do uso de uma biblioteca para isso. Basicamente as funes mais comuns de entrada e sada esto na biblioteca stdio.h, que deve ser lincada ao programa atravs do comando #include. Exemplo:
#include <stdio.h>

Uma biblioteca declarada entre < e >, indica que o compilador dever procur-la no diretrio padro das bibliotecas. Se quisermos inserir uma existente no diretrio atual, ou em algum outro, o mesmo feito da seguinte forma:
#include mylib.h

Eis algumas funes importantes da biblioteca stdio.h do C: Funo printf(): Envia os dados para a sada padro (stdout). Sintaxe:
printf("format_string", arg1, arg2,...);

Onde: format_string: formado por caracteres ordinrios mais especificaes de formato (especif_format). Os caracteres ordinrios so copiados diretamente na sada padro. Especificaes de formato so comandos enviados para o printf que indicam se um dado a ser impresso inteiro, char, double, string, etc.

Cap 3: Estrutura dos programas C

pgina 13

Linguagem de Programao C

ULBRA/SJ

Exemplos de formatos: d - o argumento dado na forma inteiro decimal o - o argumento apresentado em octal (inteiro) x - o argumento dado na forma inteiro hexadecimal u - o argumento considerado decimal inteiro sem sinal c - o argumento um caracter nico s - o argumento uma cadeia de caracteres e - o argumento dado como float ou double em notao cientfica (com expoente) f - o argumento dado como float ou double em notao decimal comum (sem expoente) g - usar "e" ou "f", conforme a necessidade p - o argumento um ponteiro (TC) % - reproduz o prprio smbolo "%" O uso do printf no nem um pouco trivial. Exemplo: printf ("Resultado: %3.2f", num); Se a varivel num for, por exemplo, 20.2, esta linha ir imprimir na tela o texto: Resultado: 20.20

No format string tem-se "Resultado: %3.2f". O printf ir interpretar isto como: 1. Imprima a frase Resultado: 2. Espere por um argumento do tipo float (f) e imprima ele reservando 3 casas numericas seguido de duas decimais depois da virgula. Como o printf est esperando por um parmetro, o float, passado ainda a varivel num para satisfazer este parmetro. Haveria impresso de sujeira se nenhum valor fosse passado. Exemplos de erros do printf (mas que compilam!!!)

printf("Idade = %d Salario = %f", idade); /* idade satisfaz o parmetro %d, mas faltou uma varivel para o %f. Imprimir LIXO */

Existem, no C, comandos especiais para o uso em E/S, como por exemplo, para gerar uma quebra de linha na tela.

Cap 3: Estrutura dos programas C

pgina 14

Linguagem de Programao C

ULBRA/SJ

A tabela a seguir traz alguns destes comandos: \nnova linha\bbackspace\ttabulao horizontal\vtabulao vertical\\Imprime uma contra barra\'imprime aspas simples\"imprime aspas duplas Exemplos:
printf(Ex1: Aqui:\nhouve uma \tquebra de linha\n); printf(Ex2: Valor de r = \t %d\n, 200); printf(Ex3: %04d + %04d = %04d\n, 3, 4, soma(3,4));

Sair na tela:
Ex1: Aqui Houve uma quebra de linha Ex2: Valor de r = 200 Ex3: 0003 + 0004 = 0007

Funo scanf(): entrada formatada atravs da entrada padro. Atua de forma anloga sada formatada (printf), mas com sentido inverso. Serve para ler dados. Sintaxe:
[nro_itens_lidos =] scanf ("format_string", &arg1,&arg2,...);

Onde argumento deve ser o endereo da varivel, exceto para arrays. Exemplo:
scanf (%d, &num);

O scanf ir esperar pela digitao de um nmero decimal inteiro com sinal (%d), armazendo este nmero na varivel num. O scanf() precisa do endereo da varivel onde ser armazenado o nmero. Por isso usa-se o sinal & antes do nome da varivel. Outro exemplo (programa completo, combinando printf com scanf e a funo soma citada anteriormente):

Cap 3: Estrutura dos programas C

pgina 15

Linguagem de Programao C

ULBRA/SJ

#include <stdio.h> int soma(int x, int y) { // nova maneira de implementar esta funo: return(x + y); } int main() { int j, k, n; printf("Digite valor para k:\n"); scanf("%d", &k); printf("Digite valor para j:\n"); scanf("%d", &j); printf("Voce digitou k = %d\tj = %d\n", k, j); printf("A soma de %d com %d eh %d\n", k, j, soma(k,j));

3.4

if/else

Execuo condicional de comandos. A instruo if causa a execuo de uma ou de um conjunto de instrues, dependendo do valor resultante de uma expresso avaliada. Sintaxe:
if (expresso) comando1; [ else ] [ comando2; ]

Se a expresso for verdadeira, executa o comando1, caso contrrio executa o comando2. Se comando1 ou comando2 tiverem mais de uma instruo deve-se criar blocos usando { e }.

Cap 3: Estrutura dos programas C

pgina 16

Linguagem de Programao C

ULBRA/SJ

Exemplo:
if (n != 0) { x = a*b/n; } else { n = x*x; x = a*b/n; }

As instrues de execuo condicional dependem da avaliao de uma expresso condicional. Esta expresso composta por vrias operaes, que so efetuadas atravs dos operadores lgicos e relacionais (ou mesmo atribuies e operaes matemticas), devolvendo um valor booleano TRUE ou FALSE. O C no possui o tipo boolean e no atribui conceito abastrado para VERDADEIRO e FALSO. Para o C V ou F represenado por um valor inteiro, com o seguinte significado:
0 - FALSO (FALSE), No Zero - VERDADEIRO (TRUE).

Ou seja, para o C, o valor 0 considerado FALSO, enquanto que todos os demais valores (-1, 1, 2, 1000, -1000, etc) so considerados VERDADEIROS. Quando o C precisa CALCULAR uma expresso, atribuindo o conceito de V ou F, ele usar SEMPRE o valor 1 para Verdadeiro e o valor 0 para FALSO. Assim fica fcil entender o que ocorre nestes exemplos:

Cap 3: Estrutura dos programas C

pgina 17

Linguagem de Programao C

ULBRA/SJ

int x, y, r; x = 20; y = 10; r = x > y; /* Sim, o x maior que y. Ento esta sentena considerada como VERDADEIRA. O C ir avaliar ela como tendo resultado 1 (VERDADEIRO). r recebe 1, portanto */ if (x) { /* x vale 20. 20 para o C conceito de VERDADEIRO. */ r = 0; } r = y + ((x>y) && (y > 0)); /* complicou, no? Temos uma sentena AND que ser calculada como sendo VERDADEIRA (1) ou FALSA (0). Como x maior que y (V, 1) e y maior que 0 (V), a sentena (x>y) && (y > 0) VERDADEIRA, valendo 1. y + 1 = 21. r receber 21. */

Devido clusula else ser opcional, pode existir uma ambigidade no uso de if-else aninhado. O compilador resolve isto associando o else ao ltimo if sem else. Por exemplo:
if (n>0) if (a>b) z=a; else z=b;

Se a>b, ento z=a, quando n>0. Se a<=b, ento z=b, quando n>0. Se n<=0, o valor de z no ser alterado nesta poro de cdigo. Entretanto, se tivermos:
if (n>0) { if (a>b) z=a; } else z=b;

Se a>b, ento z=a, quando n>0. Se n<=0, ento z=b. Se n>0 e a<=b, o valor de z no ser alterado nesta poro de cdigo.

Cap 3: Estrutura dos programas C

pgina 18

Linguagem de Programao C

ULBRA/SJ

3.4.1

Exerccios

1) Escrever um trecho de cdigo que pea ao usurio digitar dois nmeros. Faa uma funo que receba estes nmeros como parmetro e retorne o maior deles. Imprima na tela os dois nmeros digitados, a soma deles, o maior e o menor deles. 2) Escrever uma funo que recebe dois inteiros (a e b) e um cdigo inteiro (cod) e retorne: a+b, se cod >=0; e |a-b|, se cod <0. Observao: |a-b| = abs(a-b). 3.5 while

Enquanto a condio descrita pela expresso for satisfeita (ou seja, verdadeira), o comando ser repedito. O comando somente ser executado se a expresso condicional for verdadeira. A avaliao da expresso realizada da mesma forma que no comando if. O comando while o nico e o principal comando de repetio realmente necessrio, sendo que pode substituir qualquer outra construo do tipo: for, repeat/until, do/while, goto ...). Sintaxe:
while (expresso) comando;

Exemplo: contagem de bits setados na varivel n.


bits = 0; while (n != 0) { if (n & 1) bits++; n >>= 1; }

Exemplo: imprimir os nmeros impares de 3 at 21 (inclusive).


i = 3; while ( i <= 21 ) { printf(%d\n, i); i = i + 2; }

3.6

for

O comando for serve para a execuo de um nmero fixo de vezes (ou no), enquanto uma varivel percorre uma determinada faixa de valores. Esta varivel chamada de varivel de ndice. Sintaxe:

Cap 3: Estrutura dos programas C

pgina 19

Linguagem de Programao C

ULBRA/SJ

for (inicio; condio; modific) comando;

Onde tem-se incio como sendo uma expresso que ir gerar o valor inicial da varivel de ndice utilizada pelo comando. A "condio" ir indicar uma condio para o prosseguimento do lao (enquanto tal condio for verdadeira ir repetir o lao). E finalmente "modific" ser o comando dado a cada execuo do lao, sendo este realizado ao final de um lao do for, modificando o valor da varivel de ndice, antes de um novo teste da condio. Cada um destes elementos (incio, condio e comando) pode ainda estar dividido em sries de comandos, como nas expresses do if. Exemplo: nmeros mpares de 3 a 21, com for:
for (i = 3; i <= 21; i = i+2){ printf("%d\n", i); }

Para criarmos um lao infinito (sem fim) podemos fazer um comando da seguinte forma: for(;;) - comando sem expresses de controle. O comando for equivalente a seguinte estrutura:
incio; while (condio) comando; modific; } {

3.6.1

Exerccios

1) Fazer uma funo que receba um nmero (do teclado) e retorne o fatorial deste nmero, usando for. (Lembre que 0!=1 e 1!=1). Deve pedir para o usurio repetir a entrada se o nmero que ele digitou for negativo. Deve ser capaz de calcular fatoriais maiores que 65535, limite para um inteiro no DOS. 2) Faa um programa completo que leia do teclado 10 nmeros inteiros, armazene-os em um vetor e depois imprima: a) A soma de todos os elementos b) O maior e o menor elemento 3.7 do/while

O comando do/while o inverso do comando while, ou seja, o teste executado ao final deste ao invs de ser no incio. Este comando tambm equivalente ao comando repeat/until (Pascal) s que a expresso avaliada em um estaria negada em relao ao outro. Sintaxe:
Cap 3: Estrutura dos programas C pgina 20

Linguagem de Programao C

ULBRA/SJ

do

comando; while (expresso);

O comando ser executado e depois ser testada a condio dada pela expresso, e caso esta seja verdadeira (TRUE) ser novamente executado o comando. 3.8 Break e Continue

Break causa a sada no meio de um lao (para comandos: for, while, do/while, switch). Provoca a antecipao do fim do lao. muito usado com o comando case como ser visto mais adiante. continue serve para a re-execuo do lao, a partir do teste. Ir causar a reinicializao do lao (no funciona com o switch). Um exemplo prtico do seu uso pode ser visto no exemplo a seguir:
while ( x <= valmax) { printf ("Entre com valor: "); scanf ("%d",&val); if (val < 0) continue; ... }

Cap 3: Estrutura dos programas C

pgina 21

Linguagem de Programao C

ULBRA/SJ

3.9

switch/case

Faz uma associao de valores com comandos a executar. Conforme o valor dado, executa um certo nmero de instrues. Serve como uma estrutura mais sofisticada que os if's encadeados. Sintaxe:
switch (varivel) { case <valor1> : <comando1>; case <valor2> : <comando2>; <comando3>; ... [ default : comando4; ] }

No comando switch no possvel definir intervalos para os quais o comando ser executado, temos que definir os valores textualmente, um a um. Os valores utilizados no case devem ser do tipo inteiro ou char. O uso do comando break muito importante quando associado ao switch, pois caso este no seja usado a cada case, ser feita a execuo de todos os comandos at encontrar o fim do switch, sendo executado inclusive o default, se houver. Por isso a estrutura comumente usada em um comando case :
case 'A': x++; break; case 'b': case 'B': y++; break; ...

3.10 Expresses Condicionais As expresses condicionais se apresentam da seguinte forma:


expr1 ? expr2 : expr3

Esta expresso equivalente a:


se expr1 ento expr2 seno expr3 Onde: expr1 -> Condio de teste expr2/expr3 -> Valor retornado

Portanto expr2 ser o valor calculado se expr1 for verdadeiro e expr3 ser o valor calculado se expr1 for falso. Exemplo:
Cap 3: Estrutura dos programas C pgina 22

Linguagem de Programao C

ULBRA/SJ

b = ((x == y)?x:y);

3.11 Exerccios 1) Fazer um trecho de programa que conte o nmero de caracteres de espao (' '), tabulao ('\t') e nova linha ('\n') em um string. Contar tambm quantas letras tem a string ao todo. 2) Escrever um trecho de cdigo, usando atribuio condicional, que teste o contedo de duas variveis e atribua o maior valor a uma terceira varivel. 3) O que faz o trecho de cdigo a seguir?
int a,b,c; ... c = (a>=0)?(b>=0):(b<0);

Cap 3: Estrutura dos programas C

pgina 23

Linguagem de Programao C

ULBRA/SJ

Passagem de Parmetros

Todas as funes do C podem conter parmetros, incusive a funo main. A forma como o C passa parmetros de uma funo para outra atravs da pilha e o devido conhecimento desta tcnica ajuda a compreender melhor as limitaes do C em relao a outras linguagens. 4.1 Parmetros de Funes

A passagem de parmetros em "C" feita por valor (tambm chamada de por cpia). No existe no C a passagem por referncia, muito embora isto seja facilmente confundido com a simulao de passagem por referncia, que j existe, por definio, em arrays. O compilador faz uma cpia dos valores passados como parmetros, colocando-os na pilha (stack) do sistema. Os valores dos dados no so alterados ao se retornar de uma rotina, mesmo que seu contedo tenha sido modificado no interior da funo, a alterao s afeta a cpia do dado. (exceto para vetores, cuja exceo ser estudada quando o conceito de ponteiros for visto. Isto inclui strings). Como j foi visto, devemos na definio de uma funo, definir tambm os seus parmetros. Um caso especial na definio de argumentos de uma funo sero os arrays, onde no feita a definio do tamanho do array, apenas de seu nome, que deve ser declarado da seguinte forma:
funo ( char argumento[] );

Passagem de parmetros por referncia no existe, mas pode ser simulada com o uso de ponteiros - visto adiante, exemplo do scanf(). Retorno padro de parmetros em funes: feito atravs do comando return(var) Funes retornam int por default Retorno de outros tipos de dados alm de inteiros e caracteres, sendo necessrio declarar as funes que no retornam valores do tipo int.

Cap 4: Passagem de Parmetros

pgina 24

Linguagem de Programao C

ULBRA/SJ

Exemplo de retorno de um double:


double dobro (double x) { return(x * 2); } main() { double y; y = dobro(3.14); }

Pode-se declarar no inicio do cdigo apenas a funo, com seus parmetros e deixar a implementao para o fim do cdigo. Isto se chamao "prototype". Exemplos de funes com prototypes e suas chamadas:
#include <stdio.h> int soma(int, int); /* no se coloca nomes aqui, apenas o tipo */ double inverso(int); int main() { int x,y,z; double i; x = 20; y = 30; z = soma(x,y); i = inverso(x);

int soma(int a, int a) { return(a+b); } double inverso(int a) { double iv; iv = a; iv = 1/iv; return(iv);

Cap 4: Passagem de Parmetros

pgina 25

Linguagem de Programao C

ULBRA/SJ

4.1.1

Exerccios

1) Faa uma funo para somar dois vetores. A funo deve receber 3 vetores (os 2 vetores a serem somados e o vetor que receber a soma) e o tamanho dos vetores. 2) Faa um programa completo que pea para o usurio digitar 10 valores inteiros. Armazene-os em um vetor de inteiros. Usando uma funo para isto, calcule os 10 elementos de um segundo vetor que suporte casas decimais, sendo que cada elemento resultante da diviso do elemento digitado por 25. Imprima os 10 elementos calculados. Exemplo: usurio digitou 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 qaundo solicitado e isto foi armazenado no vetor de inteiros vet. Dentro do main haver a chamada:
converte(vet, vdouble, 10);

Sendo vet o vetor de inteiros, vdouble o vetor de doubles (que ser gerado) e 10 o tamanho (para que a funo funcione mesmo se fossem 20, 30, etc...). Ao imprimir vdouble depois desta chamada, deve gerar: 0.04, 0.08, 0.12, 0.16, 0.2, 0.24, 0.28, 0.32, 0.36, 0.4 que nada mais que o resultado de 1/25, 2/25, 3/25 ... 10/25 . 4.2 Parmetros do "main"

O mdulo principal do programa (main) pode receber dois parmetros externos opcionais, so eles: argc e argv. Sintaxe utilizada:
main (int argc,char *argv[])

Onde tem-se:
argc - nmero de apontadores; argv - array de apontadores para cadeias de caracteres.

Tem-se em argc um valor maior que zero caso o programa executvel tenha sido chamado com parmetros. O apontador argv[0] nos d o nome pelo qual o programa foi chamado. O exemplo abaixo mostra a associao dos parmetros:
A> prog_c param1 b:param2 xyz argc=4 e argv[0] argv[1] argv[2] argv[3] = = = = ponteiro ponteiro ponteiro ponteiro para para para para "prog_c" "param1" "b:param2" "xyz"

Cap 4: Passagem de Parmetros

pgina 26

Linguagem de Programao C

ULBRA/SJ

4.2.1

Exerccio

1. Faa um programa que leia um conjunto de argumentos da linha de comando e imprima cada um deles em uma linha separada, informando, ao final, o nmero total de argumentos sem considerar nome do programa em si. 2. Modifique seu programa para calcular fatorial, de forma que ele calcule nmeros passados por parmetros.

Cap 4: Passagem de Parmetros

pgina 27

Linguagem de Programao C

ULBRA/SJ

Classificao e modificadores de variveis

J estudamos como declarar uma varivel e os tipos bsicos do C. As variveis podem ser, como j visto nos exemplos, de dois tipos, quanto ao seu escopo: Externas: so variveis globais declaradas fora de qualquer funo ou bloco, podendo ser referidas em outras partes do programa de forma explcita ou implcita. As variveis externas globais podem ser acessadas de qualquer funo do programa. Internas: so definidas dentro de funes, ou blocos e podem ser do tipo estticas, automticas ou registradores. Estas variveis sero locais ao bloco ou funo em que forem definidas, ou seja, s so acessveis dentro do bloco ou da funo em que esto declaradas. Alm do escopo onde as variveis so vlidas, eles podem ser divididas de outra forma, quanto a sua validade, ou seja, por quanto tempo elas estaro ativas na memria. Neste caso, elas se dividem em outros dois tipos: Automticas: so as variveis comuns internas de funes. Elas existem durante a execuo da referida funo, sendo "apagadas" aps o trmino desta e no caso de uma nova chamada da funo ela no possui mais o seu antigo valor. So variveis dinmicas e so alocadas na pilha do sistema (STACK), as variveis internas e os parmetros passados para as funes so deste tipo. Estticas: podem ser internas ou externas as funes, mas estas mesmo aps o fim da execuo, ao contrrio das automticas, continuam em memria. Permitem tambm a no interferncia em outras funes, ou seja, podem haver outras variveis declaradas com o mesmo nome destas, pois elas sero invisveis sendo vistas apenas na funo onde foram declaradas. So declaradas da seguinte forma:
static <tipo_dado> <nove_var>;

As variveis estticas so armazenadas na rea de dados do sistema que fixa.Toda a varivel global , por natureza, tambm esttica e as variveis internas so automticas a menos que se defina o contrrio com static. Exemplo da declarao de uma varivel esttica.
int calcula_salrio (...){ static cont; cont++; /* cont ira contar o numero de vezes que esta funcao foi chamada */ printf ("Estah funcao foi chamda %d vezes\n", cont); return(1);

Registradores: so variveis muito usadas e que se possvel sero alocadas em um dos registradores do processador. Usa-se como varivel do tipo int, char ou apontadores. Otimizam o acesso a uma varivel, mas deve m ser usadas com muita cautela, pois ao designar-mos um registrador
Cap 5: Classificao e modificadores de variveis pgina 28

Linguagem de Programao C

ULBRA/SJ

para ficar preso com o valor de uma varivel, limitamos os recursos disponveis ao compilador, e ele ter um a menos, fazendo mais trocas entre memria e registradores, tornando o cdigo um pouco maior. Deve ser usado somente para variveis que so muito acessadas. So declaradas da seguinte forma:
register <tipo_dado> <nome_var>;

5.1

Modificadores.

Alm dos tipos bsicos do C, existem alguns modificadores, que alteram a interpretao da varivel. So eles:

unsigned: indica que a varivel deve ser tratada sem o sinal. Por exemplo, se usarmos este modificador para o tipo char, temos que ela poder assumir um valor entre 0 a 255 e no -127 a 127, caso usemos o sinal. signed: indica para usar o sinal. No caso de um int, temos que a varivel pode ir de -32768 a 32768. short: usado para inteiros e indica um inteiro curto. long: usado para inteiros, indica um inteiro longo. Tanto o short como o long, no fazem sentido em algumas arquiteturas. Nos compiladores LINUX, por exemplo, tanto o int como o short como o long utilizam 4 bytes para representar, pois so implementados nos registradores de 32 bits da arquitetura intel. Mas no sistema DOS, que de 16 bits, um inteiro e um short tem apenas 2 bytes, enquanto que um long tem 4. Pode-se verificar o tamanho para determinada arquitetura atravs do uso de sizeof().

Exemplos de modificadores:
unsigned char a; /* um char (1 byte) sem sinal*/

unsigned long int fatorial; /* um inteiro longo sem sinal*/ unsigned long fatorial2; /* como short e long so modificadores do int pode-se suprimir o tipo da varivel */

5.2

Inicializao A princpio todas as variveis so inicializadas de acordo com as seguintes regras:


externas e estticas: inicializadas com "0"; automticas e registradores: contm lixo (isto , no so inicializadas).

Apesar das variveis externas e estticas serem inicializadas com zero, para evitar problemas, conveniente inicializar sempre as variveis.
Cap 5: Classificao e modificadores de variveis pgina 29

Linguagem de Programao C

ULBRA/SJ

5.3

Converso de Tipos

O "C" segue algumas regras para a converso de tipos de dados, converso esta que feita para possibilitar ao compilador de realizar as operaes requeridas em expresses com tipos compatveis entre si. As regras de converso de tipos so as seguintes: Todos os valores no-inteiros ou no-double so convertidos como mostrado na tabela a seguir. Aps isto os dois valores a serem operandos sero do tipo int (incluindo long e unsigned) ou do tipo double. Se um dos operadores do tipo double, o outro operando tambm ser convertido para double. Mas se um dos operandos for do tipo unsigned long, ento o outro ser convertido para unsigned long. Por outro lado, se um dos operandos for do tipo long, ento o outro operando ser convertido para long. Se um dos operandos for do tipo unsigned, ento o outro operando ser convertido para unsigned. Tabela de converso: (TC) TIPO char unsig ned char signe d char short float 5.4 CONVERTIDO PARA int int int int double MTODO USADO extenso de sinal zera o byte mais significativo extenso de sinal se unsigned, ento unsigned int preenche mantissa com 0's

Converso de Tipos Explcita

O programador pode (e as vezes deve) especificar de forma explcita o tipo para o qual o dado deve ser convertido. Basta especificar o tipo entre parenteses, antes da varivel ou expresso. Esta prtica se chama cast.

Cap 5: Classificao e modificadores de variveis

pgina 30

Linguagem de Programao C

ULBRA/SJ

Exemplo:
int x; double y; y=(double)x + 0.5;

Importante: por no fazer uma verificao severa de tipos de dados, o C permite que se escreva expresses usando os mais variados tipos de dados. Outras linguagens, como o pascal, no permitem este tipo de coisa, pois facilmente o programador pode ser induzido ao erro. Veja e pense sobre o exemplo a seguir:
#include <stdio.h> int main() { double a=200; a=(a*0.7)+a; /* pergunta: qual deveria ser o valor de a? */ printf("%d\n",a); /* pergunta: qual eh o valor de a agora? Porque? */

5.5

Exerccios

1) Faa um programa que imprima o tamanho das seguintes estruturas de dados: char, int, long int, short int, float e double. Diga se existe diferenas entre o compilador do DOS e do LINUX (ou Windows). 2) Considere cada uma das atribuies abaixo e determine qual o resultado atribudo:
int i = 3; int j = 2; double a = 3.0; double k; k k k k = = = = i/j; (double)(i/j); (double) i/j; (int) a/2;

Cap 5: Classificao e modificadores de variveis

pgina 31

Linguagem de Programao C

ULBRA/SJ

Matrizes e strings

Atenao! Como mencionado anteriormente, o C no possui o tipo de dado string de forma implcita, como no Pascal, por exemplo. Para manipular este tipo de dado, o prprio programador deve controlar a estrutura de dados. Para uma string, por exemplo, cria-se uma "cadeia de caracteres", ou, como tambm pode ser chamado, um vetor de caracteres. Todas as linguagens que implementam este tipo de dado, precisam ter uma forma de controlar onde esta cadeia termina, ou seja, onde o ltimo elemento da minha string. O Pascal faz isso de forma totalmente implcita e transparente para o programador, guardando na primeira posio do vetor de caracteres o tamanho deste. Por isso que o tamanho de uma string no pascal est limitada a 255 caracteres. Como o C no tem esta transparncia e um vetor de caracteres apenas uma mera seqncia de bytes, sem sentido algum, o programador quem deve fazer este controle de forma explcita. Nada impede que ele o faa da mesma forma que o Pascal. Alias, a instruo gets() do C trabalha com uma cadeia de caracteres cujo primeiro elemento tem o tamanho mximo, o segundo tem o tamanho atual e somente a partir da terceira posio que se tem o texto lido. Mas existem vrias funes no C para trabalhar com o conceito de strings e estas j respeitam um determinado padro e, a menos que o programador esteja disposto a fazer suas prprias rotinas, desprezando as existentes na biblioteca stdio.h e strings.h, recomenda-se que se faa uso deste. O que deve ficar bem esclarecido que o fato do C possuir em suas bibliotecas funes que lidam com strings, no significa que exista este tipo de dado na linguagem. Ento, devidamente entendido desta forma, para a maioria das funes do C uma cadeia de caracteres (strings) sempre termina com o valor decimal 0, isto , o zero mesmo, e no o caractere '0'. Isto possvel porque uma string deve conter apenas letras e nmeros (caracteres imprimveis) e o zero no faz parte deste conjunto. Se quisermos representar o zero no meio de um texto, devemos usar o caractere de scape '\' (exemplo: "teste\0"). Ao declarar uma cadeia de caracteres para se usar como uma string, deve sempre ter-se o cuidado de reservar uma posio a mais: aquela que indicar o final da string. Exemplo:
char texto[10]; /* suporta at 9 caracteres, mas o '\0'*/ /* atribuio: */ texto[0]='A'; texto[1]='B'; texto[2]=0; /* ou texto[2] = '\0'; /* para atribuir um texto, deve-se usar uma funo especfica */

Um exemplo de uma funo que trabalha com este conceito o j visto printf("%s", texto);

Cap 6: Matrizes e strings

pgina 32

Linguagem de Programao C

ULBRA/SJ

Se o programador esquecer de colocar o '\0' no fim, estas funes iro se perder, pois interpretaro como final de string o primeiro '\0' que encontrar, imprimindo talvez coisas indesejveis at l!!! CUIDADO!!! A declarao de uma matriz feita de forma semelhante:

int tabela[4][5]; /* matriz de inteiros 4x5 */ tabela[0][0]=200; tabela[0][1]=3000; ...

Importante ressaltar que matrizes e vetores so na verdade a mesma estrutura, pois a memria do micro um vetor. Pode-se, caso o programador deseje, manipular uma matriz como se fosse um vetor, desde que se saiba o clculo que o C utiliza para calcular a posio [x][y] no vetor. Estes detalhes so exaustivamente estudados em Estruturas de dados. 6.1 Funes de manipulao de strings

Na biblioteca padro stdio.h j existe funes, como o printf e o scanf, que trabalham com este conceito de strings e as mesmas at j foram empregadas em exemplos. Existem outras, mas iremos ver duas mais importantes: sprintf(): lembra do printf e de sua sintaxe? O que o printf faz? Envia o resultado de uma impresso para a sada padro, tipicamente a tela. Bem, o sprintf() exatemante igual ao printf(), fazendo as mesmas coisas e com a mesma forma de uso. Entretanto, o resultado da impresso no vai para a sada padro, mas sim para um vetor de caracteres passado por parmetro. Esta funo, ento, tem apenas um parmetro a mais: o nome da cadeia de caracteres. Temos aqui a funo que precisamos para atribuir textos as nossas strings. Exemplos:
char texto[100]; sprintf(texto, "Isto eh um teste"); sprintf(texto, "O valor de a eh %d", a);

Como visto, o uso igual ao printf. Observe que esta funo j coloca o '\0' no final, livrando o programador desta preocupao. sscanf(): a funo scanf l da entrada padro, certo? Bem, esta semelhante, s que busca os dados de uma string e no do teclado. Na verdade esta funo pouco empregada. Existe no C uma biblioteca especfica para trabalhar com strings. Funs da biblioteca string.h:
Cap 6: Matrizes e strings pgina 33

Linguagem de Programao C

ULBRA/SJ

strcmp(): compara o contedo entre duas strings passadas por parmetro, retornando 0 se elas forem iguais e diferente de zero se diferentes: strcmp(texto, texto2); strlen(): retorna um inteiro que expressa o tamanho atual da string (ou seja, quantos caracteres tem at o '\0'). strcpy(): copia o contedo de uma string para outra. Pode ser feito com o sprinf().

Exemplo:
strcpy(destino, destino); /* pode ser simulado com:*/ sprintf(destino, "%s", origem);

Evidente que destino deve suportar origem, pois se o programador no cuidou deste detalhe, j sabemos que o C no ir cuidar tambm. Cuidado com os erros tpicos de manipulao de strings. A funo sizeof() no retornar o tamanho, no sentido do nmero de caracteres de uma string. Ela serve para retornar o tamanho do tipo e string no um tipo de dado do C. Da mesma forma, j que string no um tipo bsico do C, no possvel fazer comparaes e atribuies de strings, a menos que se use as funes existentes (strcmp e srtcpy). 6.2 Exerccios: 1) Implemente voc mesmo uma funo que faa a mesma coisa que a strlen. 2) Utilize a funo strcmp com vrios exemplos. Observe que ela retorna 0 se as duas strings forem iguais, mas o que retorna se elas forem diferentes? Implemente uma funo que faa a mesma coisa.

Cap 6: Matrizes e strings

pgina 34

Linguagem de Programao C

ULBRA/SJ

Apontadores

Antes de vermos o que so apontadores e como podem ser utilizados no C, vamos fazer uma rpida reviso sobre o conceito de variveis. Uma varivel mostrada nas disciplinas de algoritmos como se fosse uma caixa com um nome e esta caixa possui um valor qualquer.

teste 209

cont 34

text o exe mplo

Bem, esta uma analogia fcil de entender quando lidamos com algoritmos, mas agora devemos entender como as varveis so realmente implementadas pelas linguagens. Uma varivel nada mais que um nome (label) que referencia (aponta) uma posio de memria. A memria pode ser considerada como um array de bytes, sendo que cada palavra tem um endereo nico. Considerando que teste seja do tipo int, cont do tipo char e texto uma string de 10 posies, podemos admitir que os mesmos poderiam estar dispostos da seguinte forma em um trecho de memria hipottico que comea no endereo 200:

Endere oVal 200209 201000 20234 203e 204x 205e 206m 207p 208l 209o

213...

Tem-se, ento, que um label, ou seja, um nome de varivel aponta para o incio de uma rea de memria reservada para si, cujo tamanho depende do tipo. Ou seja, para o tipo int, so reservados 2 bytes (em algumas arquiteturas so 4 ou mesmo 8 bytes), para o tipo char apenas um e para a string texto, so reservados tantos bytes quantos forem necessrios para satisfazer o tamanho requerido. No momento o texto de exemplo no utiliza todos os 10 bytes, sendo que a prxima varivel declarada
Cap 7: Apontadores pgina 35

Linguagem de Programao C

ULBRA/SJ

(xx) ocupar a posio de memria 213 mesmo assim. No desenho tambm est representado o uso do 0 como terminador de uma cadeia de caracteres no C. Anteriormente falamos dos operadores & e * para manipulao de endereos. Agora podemos entender melhor seu funcionamento. Quando referenciamos a varivel pelo nome, o que nos retornado ser simplesmente o valor contido na memria referenciada pela varivel, ou seja, ao referenciarmos a varivel teste, nos ser retornado o valor 209, ao referenciarmos a varivel cont, nos ser retornado o valor 34. Quando, entretanto, referenciar-se a varivel teste com a seguinte sintaxe: &teste Esta-se agora pedindo o endereo ocupado pela varivel, ou seja, receberemos o valor 200 (posio da memria onde a mesma comea). De forma anloga, se usarmos a notao: *teste ser retornado o valor contido na memria cujo endereo est na posio de memria apontada por teste! Complicado? Vejamos no exemplo: A posio de memria apontada pela varivel teste contem o valor 209. Quando usamos *teste, queremos o valor da posio 209, ou seja, nos ser retornado o!! Este o conceito de apontadores: variveis cujo valor o endereo de uma outra varivel ou uma rea de memria reservada para tal. Estes exemplos so hipotticos, pois o C no permite usar o operador *" para variveis que no sejam do tipo pointer. Os apontadores ou pointers so tipos de dados que tem por funo "apontar" (referenciar) variveis atravs de seus endereos fsicos (posio da varivel na memria), permitindo uma maior flexibilidade em nossos programas como: acesso indireto por ponteiros, simulao de passagem de parmetros por referncia, alocao dinmica de memria, criao de listas encadeadas e outras estruturas de dados mais complexas. So declarados da seguinte forma:
int x,y; int *px; double vard, *pd; /* Declarao de dois inteiros */ /* Declarao de um ponteiro para inteiros */ /* um double e um ponteiro para double */

Os ponteiros so definidos em funo do tipo de varivel ao qual ele ser ligado, so do tipo "aponta para um determinado tipo de dado". Como se usa um apontador:

Cap 7: Apontadores

pgina 36

Linguagem de Programao C

ULBRA/SJ

pt y

= &x; = *pt;

/* pt recebe o endereo de x, aponta para x /* y recebe o valor apontado por pt

*/ */ */

*pt = 12; /* O endereo dado por pt recebe o valor 12 /* Se pt = &x ento *pt = 12 igual a x = 12 */

impossvel apontar para registradores e constantes definidas atravs do comando #define do processador de macros. Um uso muito prtico de ponteiros com os arrays, pois este tipo de dado tem caractersticas que se comportam de maneira muito parecida a eles. Na realidade, um array pode ser visto, na maioria das vezes, como sendo um apontador para uma posio onde se encontram alocados os dados. Por isso temos:
char car,a[10],*ptr; ptr = a; /* ptr aponta para o endereo de a[0] */ ptr = &(a[0]); /* igual ao exemplo anterior a = &a[0] */ car = *(ptr); /* car recebe o contedo de a[0] */ int var[5],*pint; pint = var; pint = (var+2); /* pint aponta para var[0] */ /* pint aponta para var[2] */

Como j pode ser visto, a indexao dos arrays feita na mesma maneira em que se trata com ponteiros. Ento, incrementar um ponteiro significa somar ao endereo atual tantas unidades quanto for o tamanho do tipo de dado apontado, assim como o endereo de um elemento de um array pode ser obtido apenas somando-se tantas unidades quanto for o tamanho do elemento do array. Portanto podemos fazer somas e incrementos com ponteiros operando-os como se fossem meros endereos. Sendo que no caso de incrementos, o acrscimo ser feito de acordo com tipo de dado ao qual o ponteiro atua (soma tantos bytes quanto for o tamanho do tipo). Como foi visto, apontadores acessam diretamente a memria do micro, por isso constituem uma ferramenta poderosa mas ao mesmo tempo perigosa, pois um descuido qualquer pode causar srios danos. Sempre que formos usar um ponteiro ele j dever ter sido inicializado, ou seja, j deve ter sido atribudo algum endereo a ele. Arrays, algumas informaes extras ... A diferena entre um array e um ponteiro que quando definimos um array, uma rea de memria reservada para ele e quando definimos um apontador, no h alocao de memria. Exemplo:
char *string -> reserva rea somente para o pointer (2/4 bytes) char string[10] -> reserva rea para 10 caracteres (10 bytes)

Arrays so passados como parmetros para funes como sendo um ponteiro para o incio do array. Como conseqncia do modo de alocao de arrays de mais de uma dimenso, quando for passado como parmetro o array, temos que indicar as outras dimenses, exceto a principal.
Cap 7: Apontadores pgina 37

Linguagem de Programao C

ULBRA/SJ

Isto se d pelo fato de que passado apenas o endereo inicial do array, que tratado como um vetor linear. Sem a indicao das outras dimenses no conseguimos distinguir os elementos de uma ou de outra dimenso. Exemplo:
funo (a) int a[][10] | | opcional obrigatrio

7.1

Apontadores: usando para passagem de parmetros por referncia

Como visto anteriormente, o C s possui passagem de parmetros por valor, atravs da pilha do sistema e no possui passagem por referncia. Esta regra continua sendo verdadeira, mas pode-se simular a passagem por referncia atravs de ponteiros. Exemplo:
int quadrado ( int *a) { *a=(*a)*(*a); return(1); } main () { int a=8; quadrado(&a); printf("A resposta foi %d\n", a); }

Observe que a passagem continua sendo por valor porque o que foi passado no o valor inteiro, mas sim o seu endereo, e este endereo no pode ser mudado dentro da funo. Veja pelo exemplo a seguir:

Cap 7: Apontadores

pgina 38

Linguagem de Programao C

ULBRA/SJ

int x=34; /* variveis globais */ int quadrado (int *a) { *a=(*a)*(*a); a=&x; printf("A tem o valor %d\n", *a); return(1); } main () { int a=8; quadrado(&a); printf("A resposta foi %d\n", a); }

No exemplo acima, o endereo da varivel "a", passada por parmetro, trocado e a impresso dentro da funo gerar o valor 34 (valor de x), mas quando a funo retornar, o valor do endereo de 'a' (passado por parmetro) restaurado, portanto continua sendo por valor, mas o valor absoluto de "a" foi alterado. Outra observao importante que a manipulao de valores dentro de uma funo dever ser feita sempre com o operador "*". Outro exemplo:
int quadrado ( int *a) { *a=(*a)*(*a); a=&x; printf("A tem o valor %d\n", *a); return(1); } main () { int a=8, *b; b=&a; quadrado(b); printf("A resposta foi %d\n", a); printf("A resposta foi %d\n, *b); }

No exemplo acima, criou-se uma varivel do tipo apontador para inteiros e fez-se com que ela recebesse o endereo de a. A varivel b j contem o endereo de uma rea de memria e portando a passagem de parmetro feita simplesmente pelo seu nome. Por fim, imprimir o valor de a ou o valor da posio apontada por b resultar no mesmo valor, pois b aponta para a. Portanto, sempre que desejar passar algum valor por referncia, deve-se passar o endereo deste valor, se desejar-se passar o prprio endereo por referncia, deve-se passar o endereo do endereo :D. Mais adiante sero vistos outras utilidades para ponteiros.

Cap 7: Apontadores

pgina 39

Linguagem de Programao C

ULBRA/SJ

7.2

Exerccios

1) Implemente a funo my_strcmp(), semelhante a strcmp() mas apenas retornando 0 se forem iguais e diferente de zero se no.

2) Faa uma funo para somar dois vetores. A funo deve receber 3 vetores (os 2 vetores a serem somados e o vetor que receber a soma) e o tamanho dos vetores e deve retornar a mdia dos valores somados (int).

3) Faa uma funo que copie X caracteres para uma outra cadeia de caracteres. A funo dever retornar o nmero de caracteres efetivamente copiados (pode ser <que x que a string origem terminar antes). Chamada:

char a[50], b[50], tam; /* atribua um texto qualquer a a */ tam=copia(b,a,10); /* copia no maximo 10 caracteres de a para b. Se a string de a tiver s 5, copia apenas os cinco */

7.3

Alocao Dinmica de ponteiros

Um apontador pode referenciar uma rea de memria j alocada, como por exemplo, um inteiro, char ou mesmo um vetor declarado de forma esttica. Entretanto, pode-se reservar uma nova rea de memria e faz-lo apontar para l. Considere um exemplo em que tenhamos que guardar os inteiros digitados pelo usurio para depois imprimi-los todos. Precisamos de um vetor de inteiros para isso, certo? De quantos elementos? Talvez soubssemos de antemo que o usurio ir digitar apenas 20 nmeros, ento, podemos fazer a seguinte declarao:
int vetor[20];

Mas se o usurio quiser digitar mais que 20, no ser possvel ao passo que se ele digitar, digamos, apenas cinco, estamos desperdiando espao (15). E se tivermos uma forma de alocar espao quando tivermos certeza qual o tamanho dele? possvel com o uso de apontadores.

Cap 7: Apontadores

pgina 40

Linguagem de Programao C

ULBRA/SJ

#include <stdio.h> int main() { int *vet; int tamanho,i; printf("Quantos elementos vc vai digitar? "); scanf("%d", &tamanho); vet = (int *)malloc (tamanho * sizeof(int)); if (!vet) { printf("Erro na reserva de memria\n"); return(0); } /* usa-se vet como vet[0], vet[1], ... vet[tamanho-1] */

No fantstico?!! Vetores estticos e dinmicos, como j dito, so praticamente o mesmo para o C, e podem ser manipulados de forma semelhante. Vamos analisar por partes o exemplo acima. Ao criarmos o apontador vet, ele ter algum endereo de memria e podera-mos us-lo. Mas, este espao em memria pode estar sendo usado por outro, pois no o reservamos para nosso uso exclusivo (que o que fazemos com int vet[20]). Poderia ser desastrosa a utilizao nestes termos. Reservar um espao em memria de uso exclusivo nosso o que tenta fazer o comando malloc. Digo tenta, porque ele pode no conseguir, por exemplo, caso no haja memria suficiente. O malloc s reserva espao em bytes e retorna um apontador para este endereo do tipo void. Por isso que para o compilador aceitar, devemos usar um cast no incio para o tipo que estamos usando (no caso, int). Como a reserva em bytes, devemos reservar a quantidade de bytes que reflita a nossa necessidade. No exemplo, temos uma necessidade de tamanho elementos, sendo que cada elemento do tipo int, o espao que precisamos de tamanho*sizeof(int). O malloc retorna o endereo reservado se conseguiu ou '\0', (NULL) se falhou. extremamente recomendvel testar seu retorno antes de usar. No correto afirmar que o malloc cria variveis dinmicas, pois ele apenas reerva espao deixando sobre a inteira responsabilidade do programador o gerenciamento desta rea (e voc esperava que fosse diferente? :-D). Logo, se o programador fizer um mal uso, azar o dele!!! Podemos considerar um pssimo uso o exemplo a seguir:

Cap 7: Apontadores

pgina 41

Linguagem de Programao C

ULBRA/SJ

#include <stdio.h> int main() { int *vet; int tamanho=20,i; vet = (int *)malloc (tamanho * sizeof(int)); if (!vet) { printf("Erro na reserva de memria\n"); return(0); } vet = &i; }

Na linha em destaque, vet recebe o endereo da varivel i. Ento, o que aconteceu com a rea anteriormente reservada? Continua reservada para ns, mas j no temos como us-la pois no sabemos onde est. Para liberar-mos um espao anteriormente alocado, usamos o comando free(): free(vet); No precisamos, neste caso, indicar o tamanho de memria liberada, pois isso o C controla. 7.4 Exerccios:

Faa um programa que crie dois vetores dinmicos de mesmo tamanho. O tamanho deve ser passado por parmetro na execuo do programa. O usurio, ao executar o programa, deve digitar todos os elementos de um dos vetores. Depois que todos os elementos foram digitados, os elementos do segundo vetor deve ser calculado da seguinte forma vet2[0]=tamanho-vet1[0], vet2[n]=tamanho-vet1[n]. Finalmente imprima ambos os vetores.

Cap 7: Apontadores

pgina 42

Linguagem de Programao C

ULBRA/SJ

Estruturas

As estruturas so conjuntos (agrupamentos) de dados, que definem um novo tipo de dado mais complexo, formado por tipos de dados mais simples.

struct nome { tipo_var nome_var; tipo_var nome_var; ... }; OU

Exemplo:
struct data { int dia,mes,ano,dia_ano; char nome_mes[10]; }; struct data today;

A utilizao de estruturas no C semelhante ao record do pascal. Referncia a um elemento da estrutura:


nome_estrut.elemento ==> data.dia = 23;

Caractersticas das estruturas:


No podem ser inicializadas na declarao ou copiadas atravs de atribuio; Podem ser acessadas com o auxlio de pointers. Permitem a criao de listas encadeadas.

Tambm podem ser declarados vetores de estruturas, da mesma forma que um vetor de inteiros ou caracteres. A forma de acesso semelhante:
struct data st[10];/*um vetor da estrutura de 10 elementos */ st[0].dia=2; sprintf(st[0].nome_ms, "Janeiro"); ...

Da mesma forma, podem-se definir apontadores para estruturas e usa-los de forma semelhante ao que j vimos com inteiros:

Cap 8: Estruturas

pgina 43

Linguagem de Programao C

ULBRA/SJ

struct data *st; struct data aux;

/*um apontador para uma estrutura */

aux.dia=2; sprintf(aux.nome_ms, "Janeiro"); st=&aux; printf("%s", st->nome); st=(struct data *)malloc (10 * sizeof(struct data)); ...

Quando acessamos um membro de nossa estrutura e a variavel um apontador para a estrutura, devemos acess-lo da seguinte forma:
(*st).mes=2; /* indica que eh um apontador e o membro ms */

Ou, de forma resumida, atravs do "->". A reserva de um espao para construir uma estrutura de tamanho dinmico tambm se d de forma anloga aos demais tipos de dados. A utilizao de ponteiros e estruturas de forma dinmica constitui umas das coisas mais poderosas no apenas no C, mas em todas as linguagens e ser estudado em um captulo a parte. 8.1 Exerccio

1) Faa um programa que define um tipo para alunos com os seguintes campos: nome (20 caracteres), cdigo (10 caracteres), notas de 3 provas e mdia final. O programa dever ler da entrada padro os dados de 10 alunos, calcular a mdia individual de cada aluno e a mdia geral dos 10 alunos. 2) Modifique o exerccio anterior, mas permita que o usurio fornea antes o nmero total de alunos que deseja digitar.

Cap 8: Estruturas

pgina 44

Linguagem de Programao C

ULBRA/SJ

Estruturas x Apontadores

A utilizao de ponteiros em conjunto com estruturas muito til e indispensvel em alguns casos. Considere o exemplo:
struct aluno { char nome[40]; char idade; char endereco[100]; int nmat; };

Digamos que temos o problema de cadastrar os alunos de uma instituicao e mante-los TODOS na memria. Como faramos? A idia mais simples seria criar um vetor desta estrutura:
int main() { struct aluno a1[200]; int i; for (i=0;i<200;i++){ scanf("%s", a1[i].nome); scanf("%d", &a1[i].idade); scanf("%s", a1[i].endereco); scanf("%d", &a1[i].nmat); } }

O exemplo acima ira ler nome, idade, endereo e nmero de matricula de 200 alunos, sendo que cada aluno ter uma entrada no vetor da estrutura. Como sabemos, todo o vetor na verdade um apontador, de forma que a manipulao dos dados, para o mesmo exemplo acima, poderia ser assim:

Cap 9: Estruturas x Apontadores

pgina 45

Linguagem de Programao C

ULBRA/SJ

int main() { struct aluno a1[200]; struct aluno *aux; int i; aux=a1; for (i=0;i<200;i++){ scanf("%s", aux->nome); scanf("%d", &aux->idade); scanf("%s", aux->endereco); scanf("%d", &aux->nmat); aux++; } }

Veja que quando fazemos aux++ estamos incrementando o endereo de aux, de forma anloga ao que fazamos com inteiros. Lembre-se que isto no eqivale a somar 1 ao valor do endereo, mas sim a pular para a prxima posio de memria onde encontra-se o prximo elemento do meu vetor. No caso de char, o prximo elemento encontra-se na posio de memria subsequente, ou seja, +1, no caso de inteiros, no Borlandc, o prximo elemento est a 2 bytes do atual, ou seja, +2. Se for no LINUX, por exemplo, seria +4. Ou seja, incrementar uma posio de memria de um vetor, eqivale a somar o tamanho do tipo de dado ao seu endereo. No nosso exemplo, aux++ o mesmo que aux=aux+sizeof(struct aluno); E se no soubermos quantos alunos teremos que cadastrar? Se a quantidade de alunos ser fornecida pelo usurio? Podemos usar malloc() para isso da mesma forma que usvamos para vetor de inteiros:

Cap 9: Estruturas x Apontadores

pgina 46

Linguagem de Programao C

ULBRA/SJ

int main() { struct aluno *a1; struct aluno *aux; int i, naluno; printf ("Entre com a quantidade de alunos:"); scanf("%d", &naluno); printf ("\nTentando alocar %d espacos na memoria...\n", naluno); a1=(struct aluno *)malloc(naluno*sizeof(struct aluno)); if (a1==NULL){ printf ("\nERRO!!! Malloc nao conseguiu alocar espaco."); return (-1); } aux=a1; for (i=0;i<naluno;i++){ scanf("%s", aux->nome); scanf("%d", &aux->idade); scanf("%s", aux->endereco); scanf("%d", &aux->nmat); aux++; }

Aps alocar espao em memria, a manipulao dos campos feita da mesma forma!! Tambm poderia ser feita:
scanf("%s", aux->nome); scanf("%s", (*aux).nome); scanf("%s", a1[i].nome); /* forma do exemplo */ /*outra forma de enderecar */ /* como se fosse um vetor*/

E se nem mesmo o usurio souber quantos alunos dever cadastrar? Ou se, no dia seguinte, ele deseja manter estes mesmos 200 na memria e cadastrar mais 20? Veja que a facilidade de se fazer referncia aos elementos do nosso vetor est no fato deles estarem em posies subsequentes na memria, ou seja, na memria depois de aux, vem o elemento aux++. Se alocarmos mais 20 posies para o novo cadastro, nada nos garante que estes 20 estaro na memria logo aps os 200 j existentes. Alm do mais, precisaramos de uma outra varivel apontadora, ou ento:

Cap 9: Estruturas x Apontadores

pgina 47

Linguagem de Programao C

ULBRA/SJ

/*precisa alocar mais 20 aos 200 j existentes*/ aux=a1; /*guardando a posio de memria da minha estrutura*/

a1=(struct aluno *)malloc(220*sizeof(struct aluno)); /*realocando 220 posioes*/ if (a1==NULL) {/*consio de erro*/} /* Copiando TODOS os valores j alocados para a nova area*/ for (i=0;i<200;i++){ sprintf(a1[i].nome, "%s", aux[i].nome); a1[i].idade=aux[i].idade; a1[i].nmat=aux[i].nmat; sprintf(a1[i].endereco, "%s", aux[i].endereco); } free(aux); /*liberando espaco na rea antiga*/

Isso no prtico, ao mesmo tempo que no h forma mais simples de se fazer com as estruturas empregadas. Veja que o principal problema que precisamos de espaos contnuos na memria. Ou ser que no? 9.1 Lista encadeada

Para resolver este problema, faz-se uso de listas encadeadas. Os vrios tipos de listas encadeadas existentes so estudados com profundidade em Estruturas de Dados. Veremos a mais simples. Uma lista encadeada quando o prprio elemento de uma estrutura possui um campo com o endereo da outra. Quando temos uma alocao contnua na memria, no precisamos desta informao, pois sabemos que ela ser a posio subsequente na memria, isto , basta pegar o prximo elemento. Na lista encadeada, o elemento anterior possui um campo contento o endereo do prximo elemento. Exemplo:
struct aluno { char nome[40]; char idade; char endereco[100]; int nmat; struct aluno *proximo; };

O membro proximo da estrutura acima, receber o endereo do prximo elemento. Desta forma, podemos alocar espao na memria a medida que formos necessitando:

Cap 9: Estruturas x Apontadores

pgina 48

Linguagem de Programao C

ULBRA/SJ

int main() { struct aluno *a1; struct aluno *aux; int i; a1=(struct aluno *)malloc(sizeof(struct aluno)); if (a1==NULL) { /*ERRO*/ } a1->proximo=NULL; aux=a1; for (i=0;;i++){ scanf("%s", aux->nome); scanf("%d", &aux->idade); scanf("%s", aux->endereco); scanf("%d", &aux->nmat); printf("Deseja cadastrar outro (S/N)"); if (getchar()=='S'){ aux->proximo=(struct aluno *)malloc(sizeof(struct aluno)); if (aux->proximo==NULL) {/*ERRO*/} aux=aux->proximo; } else break; }

A manipulao de listas encadeadas no algo trivial e requer muito cuidado para que alguma rea alocada no seja perdida. Se no controlarmos com cuidado o campo proximo sempre atualizandoo com o endereo do prximo elemento, podemos ir para posies de memria inconsistentes! No exemplo, se quisermos agora imprimir o nome de todos os alunos e liberar a memria:

Cap 9: Estruturas x Apontadores

pgina 49

Linguagem de Programao C

ULBRA/SJ

/* Ao sair do laco, i contera o numero de alunos cadastrados. Mas veja que nem ao menos precisamos desta informao*/ for (aux=a1;aux!=NULL;aux=aux->proximo){ printf ("%s", aux->nome); } /* Tudo foi impresso. Para limpar...*/ for (aux=a1->proximo;aux!=NULL;){ free(a1); a1=aux; aux=a1->proximo; } free(a1);

Observe que agora no podemos mais fazer aux++ pois isso faria com que ele recebesse o que deveria ser o prximo elemento da estrutura, o que pode no ser verdade.

Cap 9: Estruturas x Apontadores

pgina 50

Linguagem de Programao C

ULBRA/SJ

10 Manipulao de Arquivos
Este captulo apresenta um conjunto de funes em C que permite a leitura e escrita de arquivos de forma rpida e padronizada, sem contudo esgotar o assunto. Existem outras funes na prpria biblioteca padro que podem ser utilizadas para manipulao de arquivos. Mais detalhes sobre estas funes podem ser encontrados em livros especializados ou na documentao que acompanha o prprio compilador. Declarao: definio de pointers para arquivos. Sintaxe:
FILE *fopen(), *fp; | | | | | apontador para arquivo | funo que retorna apontador para arquivo tipo de dado definido em stdio.h

A declarao de apontadores e funes de manipulao de arquivo deve ser feita juntamente com a declarao de variveis. Exemplo:
FILE *arq1, *arq2; /* Declaracao de duas variaveis apontadoras para arquivo */

Antes de usar os arquivos, necessrio abri-lo e aps us-lo deve-se fech-lo. 10.1 Funo fopen() As regras para leitura de arquivos em C so simples. Antes de acessar, para leitura ou escrita, um arquivo, o mesmo deve ser aberto pela funo fopen() da biblioteca padro. A funo fopen() recebe o nome do arquivo e o modo de acesso ao arquivo (leitura, escrita, atualizao etc.), e retorna um apontador de arquivo. Este apontador de arquivo corresponde ao endereo de uma estrutura que contm informaes sobre o arquivo, tais como: localizao de um buffer, a posio do caracter corrente no buffer, se o arquivo est sendo lido ou gravado etc. A estrutura recebe o nome FILE e est definida no arquivo stdio.h. O nome do arquivo e o modo so cadeias de caracteres variveis ou constantes. A cadeia de caracteres correspondente ao modo indica como o arquivo vai ser acessado. Entre os modos permitidos esto: leitura ("r"), escrita ("w") ou adio ("a"). Para iniciar o acesso a um arquivo para leitura, podese, por exemplo, utilizar o seguinte trecho de cdigo:

Cap 10: Manipulao de Arquivos

pgina 51

Linguagem de Programao C

ULBRA/SJ

#include <stdio.h> void main() { /* Ponteiro para o arquivo; FILE definido em stdio.h */ FILE *pont_arq; /* Abre o arquivo para leitura. */ pont_arq = fopen ( "NOME.ARQ", "r" ); ...

Observe que, se um arquivo aberto para escrita, o seu contedo antigo descartado. E que, se um arquivo aberto para escrita ou adio, e ele no existe, um arquivo novo ser criado. Por outro lado, se um arquivo for aberto para leitura, ele obrigatoriamente dever existir, caso contrrio um erro ser retornado pela funo fopen(). A funo fopen() retornar NULL (definido em stdio.h) sempre que o arquivo no puder ser aberto no modo especificado. conveniente, portanto, testar o valor retornado pela funo fopen() para garantir o correto funcionamento do programa. O seguinte trecho de cdigo pode ser acrescentado ao arquivo.
... if ( pont_arq == NULL) /* Se pont_arq for igual a NULL, no conseguiu */ /* abrir o arquivo. */ printf ("ERRO: arquivo no pode ser aberto!\n"); else ...

Modos: "r" "w" "a" "r+" "w+" "a+" leitura (open) escrita (rewrite) adio (append) para atualizar um arquivo (read e write) para criar um arquivo para atualizao adio, em arquivo para leitura e escrita

Existem certos tipos de apontadores de arquivos padro que so constantes: stdin stdout stderr entrada padro (normalmente teclado) sada padro (normalmente vdeo) sada padro de mensagens de erro (vdeo)

10.2 Funes fgetc() e fputc() Depois que o arquivo foi aberto com sucesso, ser possvel gravar ou escrever no arquivo. Existem vrias possibilidades para leitura ou escrita do contedo do arquivo, mas as mais simples so as funes fgetc() e fputc(). A primeira l um caracter do arquivo e a segunda escreve um caracter no arquivo.

Cap 10: Manipulao de Arquivos

pgina 52

Linguagem de Programao C

ULBRA/SJ

A funo fgetc() recebe o ponteiro para o arquivo (retornado por fopen()) e retorna o prximo caracter do arquivo. Quando o final de arquivo encontrado a constante EOF (tambm definida em stdio.h) retornada. Por exemplo, para atribuir varivel car o prximo caracter de um arquivo anteriormente aberto, pode-se utilizar a seguinte seqncia:
/* L um caracter do arquivo. */ car = fgetc ( pont_arq );

A funo inversa de fgetc() fputc(), que escreve um caracter na prxima posio do arquivo. fputc() recebe o caracter a ser escrito e o ponteiro para o arquivo. Por exemplo:
/* Escreve um caracter no arquivo. */ fputc ( car, pont_arq );

coloca o caracter car no arquivo aberto anteriormente e cujo ponteiro pont_arq. 10.3 Funo fclose() Aps a abertura com sucesso do arquivo e sua conseqente utilizao necessrio fechar o arquivo, ou seja, informar ao sistema operacional que ele no ser mais necessrio. A funo fclose() recebe o ponteiro para um arquivo aberto por fopen() e fecha o arquivo. Para fechar o arquivo utilizado nas seqncias de cdigo anteriores, poderamos utilizar, por exemplo:
/* Fecha o arquivo pont_arq, aberto com fopen(). */ fclose ( pont_arq );

10.4 Um Exemplo As funes descritas nas sees anteriores podem ser utilizadas para criar programas como o comando TYPE do DOS ou cat do UNIX. O programa a seguir ilustra a impresso de um conjunto de arquivos cujos nomes foram fornecidos a partir da linha de comando.

Cap 10: Manipulao de Arquivos

pgina 53

Linguagem de Programao C

ULBRA/SJ

/* Programa: cat.c */ #include <stdio.h> /* Funo principal. */ /* argc = nm. nomes fornecidos na linha de comando, */ /* argv[] = nomes */ int main ( int argc, char *argv[] ) { FILE *arq; /* Ponteiro para o arquivo. */ int car; /* Recebe caracteres lidos do arquivo. */ int i; /* Contador de nomes de arquivos fornecidos. */ /* Para todos os nomes fornecidos na linha de comando, */ /* menos o nome do executvel ... */ for ( i = 1; i < argc; ++i ) { /* ... tenta abrir o arquivo com o nome fornecido. */ arq = fopen ( argv[i], "r" ); /* Se conseguiu entao ... */ if ( arq != NULL ) { /* ... inicia a impresso do arquivo. */ /* Enquanto fgetc no retornou EOF ... */ while ( ( car = fgetc ( arq ) ) != EOF ) /* ... imprime car na sada padro. */ fputc ( car, stdout ); /* Fecha o arquivo aberto. */ fclose ( arq );

} else /* Se nao conseguiu abrir o arquivo... */ /* ... imprime uma mensagem de erro. */ printf ( "ERRO: Impossvel abrir arquivo %s\n", argv[i] ); } } /* Fim do for(;;). */ /* Fim do main(). */

10.5 Usando fscanf e fprintf Para entrada e sada formatada em arquivos, as funes fscanf() e fprintf() podem ser usadas. Elas so idnticas a scanf() e printf(), exceto que o primeiro argumento um apontador de arquivo que especifica o arquivo a ser lido ou gravado. O formato o segundo argumento e os parmetros aparecem em seguida. Por exemplo, o salvamento de um vetor de registros formados por um inteiro, um real e uma cadeia de caracteres poderia ser feita em um arquivo usando o cdigo a seguir:

Cap 10: Manipulao de Arquivos

pgina 54

Linguagem de Programao C

ULBRA/SJ

... struct regsitro{ int inteiro; double real; char cadeia[20]; }; /* Tipo criado para definio do registro. */ struct registro vet_reg[20]; /* Vetor de 20 registros. */ int i; /* Contador de registros. */ FILE *arq; /* Ponteiro para o arquivo aberto. */ /* Abre o arquivo para escrita. */ arq = fopen ( "ARQUIVO.TXT", "w" ); /* Se conseguiu abrir o arquivo para escrita, ento ... */ if ( arq != NULL ) { /* ... inicia salvamento dos registros. */ /* Para cada um dos 20 registros ... */ for ( i = 0; i < 20; ++i ) { /* ... imprime os seus 3 campos separados por espao, */ /* em uma linha do arquivo. */ fprintf ( arq, "%d %lf %s\n", vet_reg[i].inteiro, vet_reg[i].real, vet_reg[i].cadeia ); } /* Fim for(;;). */ /* Fecha o arquivo. */ fclose ( arq ); } /* Fim if(). */ else /* Se no conseguiu abrir o arquivo... */ /* ... imprime uma mensagem de erro. */ printf ( "ERRO: Impossvel abrir o arquivo para escrita.\n" ); ...

Da mesma forma que a escrita ou criao, a recuperao poderia ser feita com a funo correspondente de leitura, ou seja, fscanf(). Deve-se, no entanto, tomar os mesmos cuidados tomados com a funo scanf(). No h, por exemplo, como ler cadeias de caracteres com espaos em branco, uma vez que, para scanf() ou fscanf(), o caracter de espao ou final de linha o separador de entradas. Isto significa que a leitura de uma cadeia de caracteres ser interrompida to logo um espao ou um caracter de nova linha seja encontrado. A prxima entrada ser lida pela prxima funo scanf() ou fscanf(). Considerando que as cadeias de caracteres do exemplo anterior no apresentam espaos em branco, a sua recuperao poderia ser feita com o seguinte trecho de programa.

Cap 10: Manipulao de Arquivos

pgina 55

Linguagem de Programao C

ULBRA/SJ

... struct registro { int inteiro; double real; char cadeia[20]; }; /* Tipo criado para definio do registro. */ struct registro vet_reg[20]; /* Vetor de 20 registros. */ int i; /* Contador de registros. */ FILE *arq; /* Ponteiro para o arquivo aberto. */ double auxval; /* Varivel auxiliar p/ leitura de doubles. /* Abre o arquivo para leitura. */ arq = fopen ( "ARQUIVO.TXT", "r" ); /* Se conseguiu abrir o arquivo para leitura, ento ... */ if ( arq != NULL ) { /* ... inicia recuperao dos registros. */ /* Para cada um dos 20 registros ... */ for ( i = 0; i < 20; ++i ) { /* ... l os seus 3 campos do arquivo. */ fscanf ( arq, "%d %lf %s", &(vet_reg[i].inteiro), &auxval, vet_reg[i].cadeia ); vet_reg[i].real = auxval; } /* Fim for(;;). */ /* Fecha o arquivo. */ fclose ( arq ); /* Fim if(). */

*/

else /* Se no conseguiu abrir o arquivo... */ /* ... imprime uma mensagem de erro. */ printf ( "ERRO: Impossvel abrir o arquivo para leitura.\n" ); ...

10.6 Entrada e Sada de Linhas Em algumas situaes, como a leitura de cadeias de caracteres com espaos, a funo fscanf limitada. Nestas situaes possvel utilizar a funo fgets() da biblioteca padro. Esta funo l uma linha inteira do arquivo e a coloca em um vetor de caracteres. A funo fgets() recebe o vetor de caracteres onde a linha ser depositada, o tamanho mximo de caracteres que dever ser lido da linha e o ponteiro para o arquivo do qual os caracteres sero lidos. Desta forma, no trecho de programa a seguir, a funo fgets() l um mximo de MAXLINHA-1 caracteres (incluindo o caracter de novalinha) e os coloca no vetor linha.

Cap 10: Manipulao de Arquivos

pgina 56

Linguagem de Programao C

ULBRA/SJ

#define MAXLINHA 256 ... char linha [ MAXLINHA ]; FILE *arq; ... fgets ( linha, MAXLINHA, arq );

A linha lida ser automaticamente terminada por '\0'. A medida que as linhas forem sendo lidas, fgets() retornar o prprio vetor linha. No final do arquivo, fgets() retornar NULL. Caso seja necessrio ler cadeias de caracteres com espaos em branco, a funo fgets() poderia ser utilizada. A seguir o programa deveria localizar as diversas entradas sobre a prpria cadeia de caracteres lida. A funo anloga para escrever linhas em um arquivo fputs(), usada da seguinte forma:
fputs ( linha, arq );

10.7 Funo feof() Esta funo retorna 0 se o final do arquivo foi alcanado.

10.8 Exerccios 1) Faa um comando chamado copiarq, em C, que copia um arquivo especificado sobre outro arquivo. Sintaxe: copiarq arquivo1 arquivo2

Cap 10: Manipulao de Arquivos

pgina 57

Linguagem de Programao C

ULBRA/SJ

11 Definio de Novos Tipos de Dados


Sintaxe:
typedef <descrio do tipo> <novo tipo>

Exemplos:
typedef typedef inteiro dias long inteiro; enum {seg,ter,qua,qui,sex,sab,dom} dias; var_int; hoje;

Cap 11: Definio de Novos Tipos de Dados

pgina 58

Linguagem de Programao C

ULBRA/SJ

12 MACROS DO PR-PROCESSADOR
As macros so instrues analisadas por um pr-processador (CPP - C pr-processador) que realiza as operaes definidas. As principais macros existentes so:

#include #define #line #undef

#if

#else

#ifdef #ifndef

#endif #elif

Nas sees a seguir so apresentadas a utilizao, sintaxe e exemplos destas macros. 12.1 #define Fora a substituio dos nomes dados pelo texto de reposio, dentro do cdigo fonte do programa. H tambm a macro substituio com parmetros, na qual os argumentos so trocados posicionalmente (de acordo com sua posio) e por correspondncia. Esta operao realizada antes do incio da compilao, sendo transparente ao compilador. Sintaxe:

#define <nome> <texto_de_reposio> ou #define <nome> ( argumento, argumento, ...) <texto_de_reposio>

As definies podem ser bem extensas podendo continuar em outras linhas com o auxlio de uma "\" no final da linha. Exemplos:

#define #define #define #define #define

MAXVAL 999 then begin { end } max(a,b) ((a>b)? a:b )

Fonte antes do CPPFonte aps o CPPif (num == valor)


then begin i++; x = MAXVAL;' end else x = max (num,valor);if (num == valor) { i++; x = 999;
Cap 12: MACROS DO PR-PROCESSADOR pgina 59

Linguagem de Programao C

ULBRA/SJ

Pode-se, portanto, declarar um trecho de cdigo completo atravs de um #define, como o exemplo do max(a,b) anterior. Antes usava-se o #defeine apenas como uma forma de declarar constantes, mas seu uso muito mais amplo do que isso. Pode ser usado quase como uma funo (entretanto, no retorna valor). Qual seria a vantagem de se declarar um bloco inteiro de cdigo atravs de um #define? Consideremos o seguinte exemplo:

} else x = ((num<valor)? num:valor);

#define tela(a) { clrscr(); printf("Apenas um teste\n"); printf("Digite um inteiro: "); scanf("%d", &a); }

\ \ \ \

int main() { int valor; tela(valor); printf("Voce digitou %d\n", valor); return(1); }

Um define compreende tudo que existe at o final da linha atual. Para facilitar a visualizao, interessante que ele se extenda por vrias linhas, quando o usamos desta forma. Para "dizer" ao compilador que nosso define continua na linha seguinte, devemos terminar a linha atual com '\'. Evidentemente, a ltima linha de nosso comando no ter este caracter. Mas, voltemos a nossa pergunta inicial: qual seria a vantagem e desvantagem de se usar um define neste caso, e no uma funo? Lembre-se que, de fato, NO SER este o cdigo que efetivamente ser compilado, mas sim o seguinte:

Cap 12: MACROS DO PR-PROCESSADOR

pgina 60

Linguagem de Programao C

ULBRA/SJ

int main() { int valor; { clrscr(); printf("Apenas um teste\n"); printf("Digite um inteiro: "); scanf("%d", &valor);

printf("Voce digitou %d\n", valor); return(1);

Isto , onde houver o nome tela, este ser substitudo antes de compilar, pelo nossos comandos, fazendo devidamente a substituio de a pelo nome passado nos parnteses. Qual a vantagem? Evidente: dispensa o custo que teramos em uma chamada de funo, logo bem mais rpido que uma funo!! E qual seria a desvantagem? Aumento no tamanho do cdigo. Se usarmos tela umas 10 vezes, sendo ela uma funo ocupar apenas uma vez espao em memria pelo seu cdigo. Sendo ela um define, ocupar 10 vezes mais. Esta tcnica conhecida como MACROS e alguns compiladores C possui um modificador na funo chamdo inline, que faz algo parecido. 12.2 #undef Desfaz a definio, faz com que o pr-processador "esquea" a definio feita anteriormente. Sintaxe:
#undef <identificador>

Exemplo:
#undef MAXVAL

Como no possvel redefinir uma constante ou macro, necessrio desfaz-la antes. 12.3 #include Causa a substituio da linha pelo contedo do arquivo informado. Inclui um trecho de um programa contido no arquivo especificado, a partir da linha onde foi dado o include. Sintaxe:

Cap 12: MACROS DO PR-PROCESSADOR

pgina 61

Linguagem de Programao C

ULBRA/SJ

#include "nome_do_arq" #include <nome_do_arq>

/* ou */ /* so aceitas tanto as "" como <> */

Observao: no TC as "" indicam que o arquivo include se encontra no diretrio corrente e o <> indica que o arquivo deve ser procurado no diretrio definido como diretrio de arquivos include. Exemplos:
#include "arquivo" /* Inclui o texto do "arquivo" no texto fonte */ #include <stdio.h> /* Inclui a biblioteca de rotinas stdio.h */

J usamos muito o include para adicionar bibliotecas em nossos cdigos, mas agora que entendemos seu significado, podemos perceber que podemos us-lo para adicionar qualquer coisa em nosso cdigo, inclusive funes j definidas em algum .c que criamos anteriormente (cuidado para no existir mais de um main: o seu e o do cdigo inserido. Lembre-se que o include como um Copy And Paste). 12.4 Compilao Condicional H um conjunto de instrues que so definidas para permitir uma compilao condicional do programa fonte. Certas funes podem ser ou no compiladas conforme estas diretivas.

#if <expr_constante> #ifdef <identificador> #ifndef <identificador> #else #elif #endif

// // // // //

// Se != 0 ento gera o cdigo que segue Se ident foi definido gera cdigo Se ident no foi def. gera cd. Funciona em com o #if. (caso oposto) Associao de um else com if Final do trecho de cdigo condicional

Exemplo:

#define versao 2.3 #if versao >= 2.1 //Inclui o arquivo "ext_21" caso a versao #include <ext_21.c>// seja maior ou igual a 2.1 #endif

Estas funes so vastamente usadas no C por programadores experientes, principalmente quando escreve-se um cdigo que deve compilar com sucesso em vrias arquiteturas (DOS e Linux, por exemplo) e existem pequenas diferenas entre elas. Pr exemplo, a biblioteca stdlib.h no existe no Linux (o clrscr(), gotoxy() esto nesta biblioteca). Como resolver? Uma soluo seria esta:

Cap 12: MACROS DO PR-PROCESSADOR

pgina 62

Linguagem de Programao C

ULBRA/SJ

#ifdef LINUX #include "my_stdlib.h" #else #include <stdlib.h> #endif

E em my_stdlib.h programa-se as funes inexistentes no Linux. Para compilar no DOS, procederia-se normalmente. Mas para compilar no Linux, usaria-se o gcc com a seguinte sintaxe: gcc -DLINUX nome.c -o nome Pense nas inmeras possibilidades que isto proporciona!! Exemplos bastante comuns em cdigos abertos, onde o usurio tem a possibilidade de compilar e/ou modificar o cdigo, so no uso da lngua usada nas mensagens de erro:

#ifdef PT #include "portugues.h" #endif #ifdef EN #include "english.h" #endif

12.5 Exerccio 1) Escrever sob forma de macro as seguintes atribuies: a) menor valor de dois nmeros; b) o quadrado de um nmero; c) o dobro de um nmero; d) retornar 1 se o nmero for par e zero em caso contrrio; e) retornar 1 se o nmero for impar e zero em caso contrrio; f) retornar 1 se os dois valores tiverem o mesmo sinal ou zero em caso contrrio (considerar zero positivo); g) retornar 1 se o segundo nmero for divisor do primeiro e zero em caso contrrio.

Cap 12: MACROS DO PR-PROCESSADOR

pgina 63

Das könnte Ihnen auch gefallen