Você está na página 1de 78

Introduo linguagem C

Centro Nacional de Alto Desempenho CENAPAD-SP

Prof. Jorge L. Daz Calle

Novembro de 1998

Centro Nacional de Alto Desempenho em So Paulo CENAPAD-SP. Noviembre de 1998.

2 Introduo A primeira experincia e a primeira compilao Fundamentos da linguagem C Varivel e constante


Constantes em C Tipo de armazenamento das variveis

4 6 9 9
12 13

Operadores aritmticos e de atribuio Operadores Relacionais e Lgicos Operadores Lgicos Bit a Bit Operadores vrgula e sizeof Precedncia e associao
Precedncia Associao

15 16 17 18 18
19 19

Introduo s funes Biblioteca padro de entrada e sada Estruturas de controle Instrues condicionais
O Comando if O comando if-else-if Switch O condicional ? :

20 22 27 27
27 28 30 31

Instrues em loops
O lao for O lao while O lao do-while break, continue

33
33 35 36 37

Instruo de desvio incondicional


O comando goto

38
38

Vetores, Matrizes e Strings Vetores Strings


gets( ... ) strcpy ( ... ) strcat ( ... ) strlen ( ... ) strcmp ( ... )

40 40 41
42 43 43 43 44

Matrizes
Matrizes bi-dimensionais Matrizes de strings Matrizes multidimensionais Inicializao de matrizes

45
45 45 46 46

Os ponteiros Operadores de ponteiros : & e * Operadores aritmticos e de comparao com ponteiros Ponteiros para ponteiros Ponteiros e vetores
Vetores como ponteiros Ponteiros como vetores Ponteiros e strings Ponteiros para ponteiros. Vetores de ponteiros

48 49 50 51 52
52 54 54 56

Mais sobre funes O comando return Prottipos de Funes O tipo void Funes em arquivo cabealho. Escopo dos parmetros. Chamada por Valor e Chamada por Referncia Argumentos da funo main Recursividade Ponteiros para funes Alocao dinmica de memria malloc realloc free Alocao Dinmica de Vetores Alocao Dinmica de Matrizes Estrutura, unio e enumerao Estrutura
Matrizes de estruturas Atribuindo estruturas Estruturas como argumentos de funes Ponteiros para estruturas

57 57 58 59 59 60 62 62 63 63 64 65 65 66 67 69 69
70 71 71 72

Unio Enumeraes Referncias Um pouco da historia nos Laboratrios Bell

72 74 75 76

Introduo
Este curso visa ensinar ao aluno os conceitos bsicos da linguagem de programao C, cujas virtudes mais importantes so a sua versatilidade, confiabilidade, regularidade e fcil uso ( uma linguagem amigvel). Uma das grandes vantagens do C que ele possui tanto caractersticas das linguagens de programao de "alto nvel" quanto de "baixo nvel", isto , a linguagem C um software voltado para o desenvolvimento de programas robustos e eficientes. Para aprend-lo no necessrio o conhecimento de nenhuma outra linguagem de programao prvia, embora facilite a aprendizagem uma boa familiaridade com computadores. A genealogia do C simples. O primeiro ancestral do C a linguagem Algol60, desenvolvida por um Comit Internacional em 1960. O Algol60 apareceu poucos anos aps o Fortran, embora seja muito mais sofisticado do que o Fortran. Apesar de suas virtudes, como regularidade da sintaxe e a sua estrutura modular, foi considerado abstrato e geral demais. Em 1963, entre Cambridge e a Universidade de Londres, foi criado o CPL, ou Linguagem de Programao Combinada, o que foi a primeira tentativa de trazer o Algol60 terra. Continuou grande e complexo. O BCPL, Linguagem bsico de programao combinada, tentou resolver o problema levando ao CPL as suas caractersticas bsicas. Seu inventor foi Martin Richards, em Cambridge, no ano de 1967. Em 1970, nos Laboratrios Bell, Ken Thompson derivou a linguagem B, mais uma simplificao do CPL. Nos mesmos Laboratrios da implementou o C pela primeira Ritchie com o C foi baseado utilizando habilmente os tipos procurada pelo BCPL e o B. Companhia Telefnica Bell, Dennis Ritchie, em 1972, vez rodando o sistema operacional UNIX. O sucesso de na recuperao da generalidade perdida, principalmente de dados e sem sacrificar a simplicidade grandemente

O C tem a coerncia das linguagens de programao pensadas por uma nica pessoa, como o BCPL, o B, o Lisp, o Pascal etc. Foi formada uma organizao para elaborar e manter um padro do C, o ANSI C. Neste curso, estuda-se principalmente os comandos desta padronizao chamada ANSI C ou C padronizado pela ANSI. Na primeira aula, faz-se entrega de uma coleo de exemplos, j digitados, para que o aluno analise, compile e faa rodar. Sugere-se que o aluno realmente trabalhe com todos estes exemplos, o que lhe permitir ganhar maior experincia de programao. O aluno pode modificar vontade o cdigo exemplo fornecido. Fazendo isto, provvel que ele gere programas executveis com um outro compilador, achando outros problemas particulares da sua mquina, e estas dvidas devem ser apresentadas na prxima aula. Isto no significa que o aluno deve reduzir o seu estudo aos exemplos dados. Para aprender a programar em C, alm do domnio da linguagem em si, necessria familiaridade com o compilador e experincia em achar "bugs" nos programas.

5 Isto , o conhecimento do C ou de uma outra linguagem de programao transcende o conhecimento de estruturas e funes. Ento, importante que o aluno digite, compile e execute os programas decorrentes dos exerccios que sero apresentados como trabalhos para fora das aulas. Se o aluno tem um computador a sua disposio e no tem um compilador C, observe o seguinte: 1. No caso de mquinas Unix, elas tm junto o compilador cc. s vezes, este no padro ANSI C, ento aconselha-se utilizar o compilador gcc da GNU. 2. Caso seja DOS ou Windows, as melhores opes so o ambiente Borland ou o Visual C. 3. Existe um catlogo de compiladores de domnio pblico, http://www.idiom.com/freecompilers/

A primeira experincia e a primeira compilao


Apresenta-se nesta seo o primeiro programa exemplo, que muito simples, e permitir conhecer a biblioteca padro para entrada e sada de dados, a idia inicial do formato de um programa em C e permitir efetuar a nossa primeira compilao. Leia bem, eu disse primeira compilao e no primeira complicao. Inicia-se esta seo, indicando que o C caso sensitivo, isto , as maisculas e minsculas so diferenciadas. Por exemplo, uma varivel chamada contador diferente de uma outra chamada Contador, e tambm de contadoR, CONTAdor, etc. O exemplo est no arquivo ex01.c. Abra-o com o editor de texto da sua preferncia, leia, pergunte e compile. Antes de responder as perguntas sobre a compilao, deve-se entender o texto do arquivo exemplo. O objetivo do programa exemplo mostrar na tela uma mensagem de boas vindas. O arquivo foi gerado por um editor de texto comum, e nele estse tentando dizer ao computador que mostre a mensagem. A linguagem utilizada para transmitir as instrues a linguagem C, que acessvel aos humanos, mas no acessvel diretamente para o computador. O computador apenas executa as instrues que tem no seu repertrio a nvel de mquina, isto , as instrues que utilizam os programadores a nvel da linguagem assembler. Para utilizar C necessrio um programa tradutor de instrues em C nas suas equivalentes a nvel mquina. Estes programas tradutores so chamados de compiladores. O escrito nos arquivos exemplos utilizando a linguagem C so chamados de cdigos fonte. Ento a tarefa do compilador traduzir o cdigo fonte em instrues que o computador possa entender e executar. O produto gerado pelo compilador um arquivo com o chamado cdigo executvel, isto o mesmo programa original na forma que o computador possa ler e executar. Um programador em C no apenas utiliza a sintaxe da linguagem para gerar um cdigo fonte, ele tambm utiliza abreviaturas para simplificar o cdigo. Estas abreviaturas no so conhecidas pelo compilador, ento devem ser expandidas antes de passar o cdigo para o compilador. Existe em C, um pr-processador C, que toma o cdigo fonte de um programa e gera um outro cdigo fonte expandido, deixando-o pronto para ser traduzido pelo compilador. Por outro lado, quando se implementa um programa visando algum objetivo especifico, geralmente utilizam-se vrios arquivos de cdigo, e ento necessrio ligar todos eles. Esta tarefa do link-editor, ele ligar todo o cdigo necessrio gerando apenas um arquivo executvel. Este arquivo executvel chamado a.out quando nada especificado. Felizmente, tudo isto escondido para os simples mortais, e todos os passos mencionados, aps ter editado o cdigo fonte, so executados no Unix simplesmente digitando uma das seguintes linhas de comandos:

7 1. 2. 3. 4. cc ex01.c cc ex01.c o nomeexecutavel cc -o executavel -lm ex01.c ...(outros adicionais) xlc -o executavel ex01.c

A primeira linha de comando compila o cdigo fonte ex01.c e gera um executvel chamado a.out. Na Segunda e na terceira linha a opo o permite atribuir um nome ao arquivo executvel, e na terceira linha lm fora ao link-editor a considerar a biblioteca pronta do C com funes matemticas especiais. O programa executvel pode ou no realizar o que o programador projetou. Quando no tenha sucesso, procure o erro no cdigo fonte com um editor de texto, pois provvel que exista um erro lgico no programa. Aps, salve as mudanas, compile e execute novamente. Editor de texto : Compilando : Cdigo fonte em C Pr-processador C Cdigo fonte expandido em C Compilador C Cdigo em assembler Montador Cdigo objeto do programa e Biblioteca de arquivos Link-editor Cdigo executvel Executar : Nome do executvel.

E o cdigo exemplo ex01.c? Abra-o e analise-o. Observe o formato utilizado e as partes que podem ser identificadas nesse formato. Como mencionado acima, existem vrias bibliotecas prontas para facilitar a entrada e sada de dados ou informao de e para o computador. Na linguagem C, existe a biblioteca padro de entrada/sada, que includa simplesmente digitando a expresso #include <stdio.h>. Esta biblioteca um conjunto de funes que permitem a interface com o usurio, isto , neste arquivo existem definies de funes teis para entrada e sada padronizada de dados. Toda vez que se queira usar uma destas funes deve-se incluir este comando. O C possui diversos arquivos-cabealhos. Por exemplo, em stdio.h foi definida a funo printf(...) para dizer ao computador o que e como ele deve mostrar na tela alguma informao. A funo printf( ) mostra na tela a

8 cadeia de caracteres (string) Alo pessoal que passada como argumento. O \n uma constante chamada de constante barra invertida. O \n de "new line" e ele interpretado como um comando de mudana de linha, isto , aps imprimir a string, o cursor passar para a prxima linha. /*Comentrios referentes ao programa */ #include <stdio.h> main( ) { printf("Alo pessoal. \n"); }

Compile o exemplo ex01.c. Analise o cdigo e verifique se o programa faz o que voc espera. Antes de iniciar o estudo da sintaxe da linguagem C estabelecem-se algumas convenes : 1. expresso : a representao de uma ou vrias aes qualquer. Exemplo : soma = a + b 2. instruo ou sentena : uma expresso terminada em ponto e vrgula. a menor unidade independente em C. Exemplo : soma = a + b; 3. funo : um conjunto de instrues logicamente encadeadas. 4. programa : um conjunto de funes, contendo uma funo (principal) chamada main( ). O programa ao ser executado comea com a primeira instruo dentro da funo main(). No primeiro exemplo, ex01.c, observam-se as seguintes partes: 1. Linhas de comentrios, que so necessrias a fim de explicitar o objetivo do cdigo, ajudando a elucidar o funcionamento do mesmo. Os comentrios so dados utilizando /* e */ . O compilador C desconsidera qualquer coisa que esteja comeando com /* e terminando com */, mesmo tendo vrias linhas. 2. Diretivas para o pr-processador. Elas iniciam-se com o smbolo #. No exemplo, #include <stdio.h>, indica ao pr-processador que deve incluir o cdigo (pronto) do arquivo de cabealho stdio.h no lugar dessa linha. Ento, o resultado de se incluir um arquivo cabealho o mesmo que se fosse incluso o texto do arquivo naquela posio. Isto economiza espao. 3. A linha main( ) define uma funo de nome main. Todo programa em C deve ter uma funo main, pois esta funo que ser chamada quando o programa for executado. O contedo da funo delimitado por chaves { }. O cdigo que estiver dentro das chaves ser executado seqencialmente quando a funo for chamada. Ao compilar os primeiros exemplos, algum compilador C pode dar mensagens de aviso do tipo warning, que no impedem o sucesso da compilao. Nos exemplos dados pode acontecer isto porque, por default, toda funo em C retorna um inteiro. Quando no retornado este inteiro, o compilador pode mandar uma mensagem do tipo "Function should return a value.". Por enquanto, esquea estas mensagens, mais tarde aprender como escrever funes direito.

Fundamentos da linguagem C
Estuda-se aqui a sintaxe da linguagem C, a sintaxe o conjunto de regras detalhadas para cada construo vlida na linguagem C.

Varivel e constante
Os dados ou valores a manipular em um programa podem ser variveis ou constantes. Em C uma constante um espao de memria cujo valor no deve ser alterado durante a execuo de um programa. E uma varivel um espao de memria que recebeu um nome e armazena um valor que pode ser modificado. Os nomes utilizados para referenciar variveis, funes ou vrios outros objetos definidos pelo usurio so chamados de identificadores. Os primeiros 32 caracteres so significativos, diferenciando-se as maisculas das minsculas. Os identificadores devem satisfazer duas condies: comear com uma letra ou sublinhado (_), e os caracteres subseqentes devem ser letras, nmeros ou sublinhado (_). O identificador de uma varivel tem mais duas restries, no pode ser igual a uma palavra reservada da linguagem C (palavra-chave), nem igual ao nome de uma funo declarada pelo programador ou pelas bibliotecas do C. As palavras-chave so identificadores predefinidos que possuem significados especiais para o compilador.

Palavras-chave asm auto break case char const continue default do double else enum extern far float for goto if int long Near Register Return Short Signed sizeof static struct switch typedef union unsigned void volatile while

10 Os tipos de dados definem as propriedades dos dados manipulados em um programa. Quando voc declara um identificador d a ele um tipo de dado. Um tipo de dado determina como o valor desse dado ser representado, que valores pode expressar e as operaes que podem ser executadas com estes valores. Todas as variveis e as constantes possuem uma caracterstica comum, um tipo de dado associado. No necessrio especificar o tipo de dado de uma constante pois ele determinado pelo seu valor. Entretanto, ao declarar um varivel, alm de escolher um nome apropriado para ela, deve-se dizer ao compilador que tipo de informao deseja-se armazenar nela.

Tipos de dados Tipo de dado char int long int unsigned ... float double pointer Armazenamento 1 byte 2 bytes 4 bytes idem 4 bytes 8 bytes 2 (4) bytes Intervalo de valores Observaes

-128 a 127 Pelo menos 8 bits -32 768 a 32 767 Pelo menos 16 bits -2 147 483 648 a 2 147 483 O dobro de um inteiro 647 0 a 2 * medida Sem sinal 3.4e-38 a 3.4e+38 Simples preciso 1.7e-308 a 1.7e+308 Doble preciso ponteiro perto (longe)

s vezes em um compilador pode-se encontrar uma faixa maior do que a mostrada na tabela, mas no uma faixa menor. O C tem cinco tipos bsicos: char, int, float, double, void. O char um tipo de dado numrico, mas associado com o conjunto de caracteres ASCII(como as letras do alfabeto). O int para armazenar valores numricos inteiros. O float e o double so para armazenamento de valores numricos em dgitos de preciso. O float em preciso simples e o double em dupla preciso. O void (vazio em ingls) um tipo especial, e o seu estudo ser feito posteriormente. Exceto o void, os tipos de dados bsicos podem ter vrios modificadores. Um modificador utilizado para alterar o significado de um tipo bsico para adapt-lo s necessidades da situao. Os modificadores de tipo do C so quatro: signed, unsigned, long e short. Os quatro podem ser aplicados a inteiros e caracteres. Ao float no se pode aplicar nenhum e ao double pode-se aplicar apenas o long. A inteno que short e long devam prover tamanhos diferentes de inteiros onde isto for prtico.

11 Uma varivel de um tipo pode ser convertida para um outro tipo utilizando o conversor de tipos ou cast. Porm no so todos os tipos que podem ser convertidos para um outro tipo com segurana. Deve-se ter cuidado com o tamanho de cada tipo de dado. Exemplo, int segundos; double minutos = ((double)segundos)/60.;

As variveis no C devem ser declaradas antes de serem usadas. A forma geral desta declarao : tipo_da_varivel lista_de_variveis; As variveis na lista de variveis tero todas o mesmo tipo e devero ser separadas por vrgula. Como o tipo default do C o int(inteiro), ao declarar variveis int com algum dos modificadores de tipo, basta colocar o nome do modificador de tipo. Assim para um long int basta declarar long, para um unsigned int basta declarar unsigned. Declarar signed para int redundante. Exemplo : int alunos, cursos, iteraes; long fatorial; double custo, valor;

Pode-se inicializar variveis no momento de sua declarao com a seguinte forma geral : tipo_da_varivel nome_da_varivel = constante; Isto importante pois quando criada uma varivel com o C, ela no inicializada, isto , at que um primeiro valor seja atribudo nova varivel, ela tem um valor indefinido e que no pode ser utilizado para nada. Nunca presuma que uma varivel declarada vale zero ou qualquer outro valor. Exemplos de inicializao : char letra = D; int cursos = 3;

12

Constantes em C
Uma constante literal a variedade mais comum. Elas compem-se de valores como 3.14, 2, ou ainda de informaes de string digitados diretamente no texto do programa, Alo pessoal.. O C tambm permite inserir constantes numricas hexadecimais(base dezesseis) ou octais(base oito). As constantes hexadecimais comeam com 0x, exemplo: 0x12A4, e as constantes de base oito comeam com zero (0), exemplo: 01342. Cuidado, nunca digite 021 para referenciar o nmero 21, o compilador C considera isto como 17 (21 em base 8). Uma constante caracter deve ser inserida como c. A forma c, com aspa dupla, utilizada para inserir uma string constante, neste caso, um vetor de dois caracteres, o c e o \0 (caracter nulo finalizando a cadeia). Como o ltimo caracter constante, existem vrias outras constantes de barra invertida que tem significado especial. Alguns deles so: \b retrocesso, \n nova linha, \t tabulao horizontal, \v tabulao vertical, \r retorno de carro, \a alerta(sinal sonoro), \ aspas, \ apstrofo, \\ - barra invertida. Uma constante declarada formada ao anteceder a palavra-chave const definio normal de uma varivel. Observar que deste jeito pode-se especificar tipo de dado, terminar com ponto e vrgula e inicializar a constante. Exemplo: const notamaxima = 10; Uma constante definida dada utilizando uma macro #define. Neste caso, a constante dada pelo pr-processador no cdigo substituindo o nome definido pelo valor de definio. Observe que #define no especifica tipos de dados, no utiliza o smbolo de atribuio (=) e nem termina com ponto e vrgula. Exemplo : #define MAXIMANOTA 10 Ao invs de definir constantes individuais que recebem valores subjacentes, pode-se utilizar constantes enumeradas para criar listas categorizadas que atingem o mesmo objetivo. Utilizando a palavra chave enum diz-se ao compilador que os itens dados devem ser enumerados, isto , associados a nmeros seqenciais iniciando com o zero, quando nada especificado, mas pode atribuir-se um valor expresso e os seguintes assumem valores consecutivos. Um elemento de uma enumerao no tem endereo de armazenamento em memria. Exemplos, enum CORES {vermelho, amarelo, azul, laranja, verde, violeta} cor; enum STATUS{FALSE, TRUE, FAIL=0, OK, NOT_RUN=-1}; STATUS estado;

13

Tipo de armazenamento das variveis


O escopo de uma varivel refere-se aos limites de validade de uma varivel. Apenas no escopo de uma varivel as instrues podem-se referir varivel. Como mencionado acima, antes de utilizar uma varivel ela deve ser declarada. Nesta declarao estabelecido um identificador e um tipo de dado para a varivel. Alm disso, pode-se especificar a forma de armazenamento da varivel, isto tem a ver com seu escopo e se o armazenamento na memria ou nos registros do CPU. Uma varivel automtica armazenada na memria e seu escopo limitado ao bloco no qual aparece, isto , enquanto esse bloco ou qualquer um outro bloco mais interior ao atual est sendo executado a varivel existe. Quando o bloco onde a varivel automtica foi declarada acaba a varivel deixa de existir. Exemplo : auto int segundos; Uma varivel registro uma varivel automtica que pode ser armazenada nos registros do CPU, se existir algum registro livre e de tamanho suficiente para armazenar a varivel. As variveis armazenadas nos registros do CPU so bem mais rpidas, embora devam ser utilizadas apenas para as variveis do seu programa que so muito utilizadas. O nmero de registros do CPU disponveis limitado. Exemplo : register int segundos; Uma varivel esttica so locais como as variveis automticas. A diferena que as variveis estticas no desaparecem quando acaba o bloco ou a funo onde ela foi declarada, o valor da varivel persiste mesmo que no seja disponvel. Se retorna-se ao bloco ou a funo onde foi declarada uma varivel esttica, ela fica novamente disponvel e com o ltimo valor armazenado nela. As variveis estticas inicializam-se apenas uma vez no tempo de compilao, portanto ocupam memria mesmo que no estejam ativas. Exemplo : static int segundos; Uma varivel externa tem escopo global, isto , existem em qualquer bloco e esto disponveis a qualquer funo que necessite utiliz-la. Ela acessvel a todas as instrues no, importando onde estejam localizadas. Exemplo : extern int segundos; Em resumo, as variveis automticas so locais aos seus prprios blocos ou funes e os seus valores desaparecem quando o bloco ou funo termina. As variveis estticas so tambm locais mas os seus valores persistem, e as variveis externas so globais e os seus valores tambm persistem. A linguagem C pragmtica, da que admite valores por default, valores que so assumidos na falta de especificao. A forma de armazenamento de uma varivel tem valor por default: o compilador assumir uma forma de armazenamento pelo contexto quando nada especificado. Isto significa que o valor por default no nico. Se uma varivel definida em um bloco ou funo e no especificada a forma de armazenamento assumida automtica, mas se foi declarada fora de qualquer bloco ou funo ela ser assumida externa pelo compilador.

14

/* Comentrio adequado */ #include <stdio.h> int value; main( ) { int somando = 20; value = 350; value = value + somando; { int vezes; value = value + vezes * somando; { double Imposto; Imposto = .12 * value; printf(Valor do Imposto = %lf \n,Imposto); } value = 0; } printf(Fim do programa.\n); }

15

Operadores aritmticos e de atribuio


Os operadores aritmticos so usados para desenvolver operaes matemticas. A seguir apresenta-se a lista dos operadores aritmticos do C: Operador + * / % ++ -Ao Soma de inteiros e pontos flutuantes Subtrao de inteiros e ponto flutuante ou troca de sinal Multiplicao de inteiros e pontos flutuantes Diviso de inteiros e pontos flutuantes Resto de diviso de inteiros Incremento de inteiros e pontos flutuantes Decremento de inteiros e pontos flutuantes

O C possui operadores aritmticos unrios e binrios. Os unrios agem sobre uma varivel apenas, modificando ou no o seu valor, e retornam o valor final da varivel. O operador como troca de sinal um operador unrio que retorna o valor da varivel multiplicado por 1. Os operadores de incremento e decremento so unrios, incrementando e decrementando de 1 a varivel sobre a qual est aplicado. Estes operadores podem ser pr-fixados ou psfixados. Quando so pr-fixados eles incrementam e retornam o valor da varivel j incrementada. Quando so ps-fixados eles retornam o valor da varivel sem o incremento e depois incrementam a varivel. Exemplos : 1. x++; equivalente a x = x+1; 2. Em x = 23; y = x++; no final tem-se y = 23 e x = 24. 3. Em x = 23; y = ++x; no final tem-se y = 24 e x = 24. Os binrios usam duas variveis e retornam um terceiro valor, sem alterar as variveis originais. A soma, subtrao, multiplicao, diviso e resto so operadores binrios pois pegam duas variveis, somam, subtraem, multiplicam, dividem ou acha o resto dos seus valores sem alterar as variveis, e retornam este resultado. O operador de atribuio do C o sinal de igual, =. O que ele faz pegar o valor direita e atribuir varivel da esquerda. Alm disto ele retorna o valor que atribuiu esquerda, assim sendo so vlidas as seguintes sentenas : double x, valor, custo; x = valor = custo = 123.5;

16 Ao contrrio de outras linguagens, o operador de atribuio em C pode ser utilizado em expresses que envolvem outros operadores, formando os chamados operadores de atribuio compostos. Os operadores compostos condensam sentenas de atribuio da forma : Varivel = Varivel operador expresso; na forma : Varivel operador = expresso; Exemplo : valor = valor + custo; escreve-se na forma : valor += custo; Os operadores atribuio podem ser compostos com os operadores aritmticos (veja a primeira linha da tabela) e podem ser compostos com os operadores bit a bit (segunda linha da tabela). += >>=

-=
<<=

*= &=

/= |=

%= ^=

Operadores Relacionais e Lgicos


Os operadores relacionais do C realizam comparaes entre variveis. Relao refere-se as relaes que os valores podem ter um com o outro. Os operadores relacionais so : > >= < <= == != Maior do que Maior ou igual Menor do que Menor ou igual Igual a Diferente de

Os operadores relacionais retornam o valor 1 para verdadeiro e retornam 0 para falso. Os operadores lgicos fazem operaes com valores lgicos, isto , verdadeiro e falso. Verdadeiro qualquer valor diferente de zero (0), enquanto zero falso. As operaes de avaliao produzem um resultado zero ou um. Os operadores lgicos so: && o E lgico || o OU lgico ! Negao (unrio no)

17

Operadores Lgicos Bit a Bit


O C permite que se faa operaes lgicas "bit a bit" em nmeros. Uma operao bit a bit refere-se a testar, atribuir, ou deslocar os bits efetivos em um byte. Operaes bit a bit no podem ser usadas em float, double, long double, void ou outros tipos mais complexos. Estas operaes so aplicadas aos bits individuais dos operandos, ou seja, o nmero representado por sua forma binria e as operaes so feitas em cada bit dele. Lista de operadores bit a bit. & e bit a bit | ou bit a bit ^ ou exclusivo bit a bit ~ negao bit a bit >> deslocamento de bits a direita << Deslocamento de bits a esquerda Imagine uma varivel inteira de 16 bits, i = 6. A representao binria da i : 0000000000000110. Operando bit a bit com a negao do nmero, ~i, o nmero se transforma em: 1111111111111001. Exemplos: Sejam as variveis unsigned int itens = 5684; em binrio 0001011000110100 unsigned int alunos = 645; em binrio 0000001010000101 Resultado de : itens & alunos 0000001000000100 itens | alunos 0001011010110101 itens ^ alunos 0001010010110001 Tenha muito cuidado no uso dos operadores bit a bit, principalmente para no confundi-los com os operadores lgicos, pois os operadores lgicos sempre retornam o resultado zero ou um, enquanto os operadores bit a bit similares produzem um valor de acordo com a operao especificada. Por exemplo, se na varivel inteira atribudo x = 7; ento x && 8 verdadeiro, enquanto que x & 8 falso. Porqu? Os operadores de deslocamento << e >> movem todos os bits de uma varivel para a direita ou para a esquerda, como especificado. A forma geral de deslocamento : varivel >> nmero de posies de bits. Conforme os bits so deslocados para uma extremidade, zeros so colocados na outra. Lembre-se de que um deslocamento no rotao, isto , os bits que saem por uma extremidade no voltam para a outra. Se uma varivel inteira i = 3; deslocada por i << 3; o valor da varivel i atualmente 24.

18

Operadores vrgula e sizeof


O operador vrgula , usado para encadear diversas expresses que devem ser executadas em forma seqencial. O valor de uma expresso com o operador vrgula dado pela expresso mais a direita, isto til quando utilizado com um operador atribuio, veja o exemplo seguinte: itens = ( numero = 3, numero++, 2*numero); primeiro atribui o 3 para numero, depois numero incrementado para 4 e no final atribui 8 a itens. Os parntesis so necessrios o operador atribuio tem precedncia sobre o operador vrgula. O operador sizeof unrio e retorna o tamanho em bytes da varivel. Exemplo int itens; itens = sizeof(itens); em itens foi atribudo o valor 2. Este operador usado para gerar cdigos portveis que dependem do tamanho dos tipos de dados.

Precedncia e associao
Precedncia refere-se ordem em que o C avalia os operadores quando existem dois ou mais deles em uma sentena. O C tem um conjunto de regras incorporadas para determinar a ordem em que os operadores so avaliados, e preciso decor-las para redigir cdigos que realizem corretamente as operaes. Dizer que um operador tem precedncia maior que um outro operador significa que ser avaliado antes. Como exemplo, os operadores de relao e lgicos tem a precedncia menor que os operadores aritmticos. A associao refere-se ordem de avaliao de operadores de igual precedncia. Eles podem ser avaliados primeiro de direita esquerda ou de esquerda direita. A seguir est uma tabela com os operadores, a sua precedncia e a associao respectiva. A ordem de precedncia de cima para baixo, sendo avaliados primeiro aqueles que esto mais acima.

19

Precedncia ( ) [ ] . -> ! ~ -(unrio) ++ -- *(unrio) &(unrio) (cast) sizeof * / % + << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= (operadores de atribuio) ,

Associao Da esquerda para a direita Da direita para a esquerda Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da esquerda para a direita Da direita para a esquerda Da direita para a esquerda Da esquerda para a direita

O problema da precedncia e a associao superado utilizando parnteses no cdigo, e apenas decorando as precedncias mais importantes.

20

Introduo s funes
Uma funo um bloco de cdigo que funciona como uma unidade autnoma para cumprir uma tarefa particular no cdigo do programa. Elas podem ser utilizadas diversas vezes no cdigo do programa e geralmente consistem de chamadas a outras pequenas funes. As funes so concisas, tornando os conceitos fceis de entender, permitindo que o programa fique mais legvel, mais bem estruturado. Para realizar determinadas aes uma funo pode precisar que sejam fornecidos determinados valores. Estes valores(parmetros) so passados funo nos chamados argumentos da funo que so as entradas que a funo recebe. Por outro lado, muitas vezes necessrio fazer com que uma funo retorne um valor, talvez o resultado das operaes particulares que ela realiza. No C, as funes tem um tipo de retorno, que o tipo do valor retornado, isto , pode ser void, char, int, long, double. Quando o tipo de retorno no especificado, as funes retornam um inteiro. Para dizer ao compilador C o que vai ser retornado precisa-se da palavra-chave return.

Modelagem de funo : Embora sejam permitidas outras variaes, sempre que possvel utilize o modelo seguinte para declarar uma funo. tipo_de_retorno nome_da_funo (lista_de_argumentos) { cdigo_da_funo } O nome_da_funo qualquer nome que tenha significado. Se possvel, deve descrever a funo. A lista_de_argumentos contm um nmero finito qualquer de argumentos separados por vrgulas. Cada argumento dado indicando o tipo do argumento e o nome dele.

Agora, fica fcil fazer uma funo para multiplicar dois nmeros. No prximo exemplo, o programa utiliza trs funes. A funo main( ), a funo produto( ) e a funo printf( ) que j est definida na biblioteca padro de entrada e sada.

21 #include <stdio.h> float produto(float fator1, float fator2) { return (fator1*fator2); } main ( ) { float saida; float coeficiente = 14.2; saida = produto(coeficiente, 67.23); printf ("Produto = %f\n", saida); } Na definio da funo produto( ) diz-se que a funo receber dois argumentos float. Quando chamada a funo produto( ), so passados como argumentos a varivel coeficiente e o nmero 67.23. H alguns pontos a observar na chamada a funo. Em primeiro lugar tem-se de satisfazer aos requisitos da funo quanto ao tipo e quantidade de argumentos. Em segundo lugar, no importante o nome da varivel que se passa como argumento, pois, a varivel coeficiente ao ser passada como argumento para produto( ) copiada para a varivel fator1. Dentro de produto( ) trabalha-se apenas com fator1, ento, se fosse mudado o valor do fator1 dentro de produto( ) o valor de coeficiente na funo principal main( ) permanece inalterado. Faa o teste. Repare que os argumentos so separados por vrgula e que deve-se explicitar o tipo de cada um dos argumentos, um a um. Observe que os argumentos passados para a funo no necessitam ser todos variveis porque mesmo sendo constantes sero copiados para a varivel de entrada da funo, neste caso o nmero 67.23 copiado para fator2. A funo main( ) deve retornar um inteiro pois o tipo de retorno dela no foi especificado, mas como no feito nenhum retorno, no momento da compilao deve aparecer uma mensagem warning (cuidado). Acrescente a linha return 0; e compile novamente. Observe a diferena entre o valor de retorno da funo main( ) e a sada procurada pelo programa, o produto de 14.2 e 67.23. Antes de estudar mais sobre as funes no C, apresentam-se vrias funes bsicas da biblioteca de entrada e sada padro do C.

22

Biblioteca padro de entrada e sada


Uma biblioteca contm o cdigo objeto de uma coleo de funes. As bibliotecas so semelhantes a arquivos objetos comuns, com a diferena que apenas parte do cdigo na biblioteca acrescentado a um programa, apenas o cdigo das funes da biblioteca chamadas no programa. A biblioteca padro C fornecida com o compilador. Esta biblioteca padro define um conjunto grande e diversificado de funes. A variedade e a flexibilidade dela pe o C frente de muitas linguagens de programao. Vrias funes na biblioteca padro trabalham com seus prprios tipos de dados especficos, e estes tipos de dados so definidos nos chamados arquivos de cabealho fornecidos e os quais devem ser includos no arquivo que utilize essas funes. Os arquivos de cabealho mais utilizados so: ctype.h para manipulao de caracteres math.h com grande variedade de funes matemticas stdio.h para entrada e sada padro e de/para arquivos stdlib.h diversas declaraes string.h suporta funes de cadeias de caracteres(strings). Restringe-se por enquanto o estudo das principais funes da biblioteca de entrada e sada, as que podem ser utilizadas incluindo o arquivo de cabealho stdio.h. printf( ... ) uma funo que permite escrever no dispositivo padro de sada, geralmente mostra na tela. A sintaxe dela a seguinte: printf( "expresso_de_controle", lista_de_argumentos); A expresso_de_controle, no necessariamente o que ser mostrado na tela, mas tambm descreve tudo que a funo vai colocar na tela, isto , quais as variveis e suas respectivas posies. Para indicar as posies dos valores das variveis utiliza-se a notao %, e junto um cdigo de formatao indicando o formato em que a varivel deve ser impresso nessa posio. O nome da varivel a ser apresentada deve ser dada na lista de argumentos. muito importante que, para cada caractere de controle %, exista um argumento na lista de argumentos. Os argumentos so separados por vrgulas. Caractere de formatao %c %d %e %f %lf %s Significado Caractere simples Inteiro Notao cientfica Ponto flutuante (float) Ponto flutuante(double) Cadeia de caracteres

23 Exemplo: #include <stdio.h> main( ) { printf("Este o numero dois: %d",2); printf("\n\t%s est a %d milhes de milhas do sol", "Vnus", 67); printf ("\nTeste %% %%\t\t"); printf ("%f\n",40.345); printf ("Um caractere %c e um inteiro %d\n",'D',120); printf ("%s e um exemplo\n","\nEste"); printf ("%s%d%%\n","Juros de ",10); } Os prximos exemplos mostram, na ordem, como arredondar, alinhar, indicar tamanho dos campos na impresso e imprimir caracteres. #include <stdio.h> main( ) { printf("\n%4.2f",3456.781); printf("\n%3.2f",3456.781); printf("\n%3.1f",3456.78); printf("\n%10.3f",3456.78); } #include <stdio.h> main( ) { printf("\n%10.2f %10.2f %10.2f",8.0,15.3,584.13); printf("\n%10.2f %10.2f %10.2f",834.0,1500.55,4890.21); } #include <stdio.h> main( ) { printf("\n%2d",350); printf("\n%4d",350); printf("\n%6d",350); printf("\n%04d",21); printf("\n%06d",21); printf("\n%6.4d",21); printf("\n%6.0d",21); } #include <stdio.h> main( ) { printf("%d %c %x %o\n",'A','A','A','A'); printf("%c %c %c %c\n",'A',65,0x41,0101); }

24 A formato ASCII possui 256 cdigos de 0 a 255. Se impresso em formato caractere um nmero maior do que 255, ser impresso o resto da diviso do nmero por 256; se o nmero for 3393 ser impresso A pois o resto de 3393 por 256 65. scanf( ... ) Ela o complemento de printf(...) e nos permite ler dados formatados da entrada padro (teclado), isto , com ela pode-se pedir dados ao usurio. A sintaxe scanf (expresso_de_controle, lista_de_argumentos); O nmero de variveis na lista de argumentos deve ser o mesmo que o nmero de cdigos de formatao na expresso_de_controle. Outra coisa importante, a lista de argumentos deve consistir dos endereos das variveis para armazenamento dos dados lidos. Isto feito colocando o smbolo & antes de cada nome de varivel na lista de argumentos. O smbolo & faz referncia ao operador de endereo que retorna o endereo na memria da varivel de tipo bsico do C. Para entender isto, deve-se saber que a memria do computador dividida em bytes, e eles so numerados de 0 at o limite da memria. O nmero que corresponde ao primeiro byte ocupado pela varivel chamado de endereo da varivel. #include <stdio.h> main( ) { int numero1; double numero2; printf("Digite um nmero = "); scanf("%d",&numero1); printf("\nO nmero %d",numero1); printf("\no endereo e %u",&numero1); printf(\nDigite um segundo nmero = ); scanf(%lf,&numero2); printf(O segundo nmero %lf, ou %f, ou %d\n.,numero2,numero2,numero2,); }

getchar( ) a funo original de entrada para um caractere dos sistemas baseados em UNIX. O caractere armazenado pelo getchar( ) at que a tecla de retorno (enter) seja pressionada. #include <stdio.h> main( ) { char c; c = getchar( ); printf(O caractere inserido %c\n,c); }

25 Existem muitas variantes da getchar( ) bem mais teis em determinadas situaes, como a getch( ) ou a getche( ) que so encontradas no arquivo de cabealho conio.h, mas este arquivo foi implementado para ambiente DOS ou Windows no para ambiente UNIX, e no pertence ao C padro.

putchar( int c) Escreve na tela o argumento de seu caractere na posio corrente. O argumento pode ser um inteiro que transformado em caractere ou pode ser um simples caractere. #include <stdio.h> main( ) { char c = D; printf(\nA varivel c foi inicializada com o valor %c, isto %d.,c,c); printf("\nDigite uma letra minscula "); c = getchar(); putchar(toupper(ch)); putchar('\n'); } H inmeras outras funes de manipulao de char complementares s que foram vistas, como isalpha( ), isupper( ), islower( ), isdigit( ), isspace( ), toupper( ), tolower( ), que so encontradas no arquivo de cabealho ctype.h. Visando utilizar eficientemente estas funes de entrada e sada de dados explicita-se melhor alguns aspectos relativos aos caracteres e s cadeias de caracteres ou strings. Os caracteres ou variveis de tipo char so tratados pelo C como variveis de um byte (8 bits). Os inteiros ou variveis do tipo int tm um nmero maior de bytes, dependendo da implementao do compilador eles tm 2 ou 4 bytes. Assim sendo, um char pode armazenar tanto valores numricos inteiros de 0 a 255 quanto um caractere de texto. Para indicar um caractere de texto usamos apstrofes. Veja o exemplo anterior. Como visto em vrios exemplos anteriores, muitas vezes necessrio manipular vrios caracteres juntos e consecutivos como uma seqncia de caracteres j conhecida como string. Para declarar uma string utiliza-se o seguinte formato geral char nome_da_string[ tamanho_da_string ]; Isto significa declarar um vetor com tamanho_da_string variveis do tipo char. Uma particularidade de uma string que ela termina com o caractere nulo \0, cujo valor igual a zero. Deve-se declarar o comprimento de uma string como sendo, no mnimo, um caractere maior que a maior string que se pretende armazenar. Exemplo. Para declarar uma string de 7 posies escreve-se char nome_amigo[7];

26 inserindo o nome Luis na string, o vetor nome_amigo tem a forma : L u i s \0 ? ?

As duas ltimas clulas do vetor que no foram utilizadas tm valores indeterminados, porque o C no inicializa variveis, cabendo ao programador esta tarefa. Para ler uma string fornecida pelo usurio pode-se usar a funo gets( ), que coloca o caractere \0 no final quando o usurio aperta a tecla "Enter". #include <stdio.h> main ( ) { char string[100]; printf ("Digite uma string: "); gets (string); printf ("\n\nVoce digitou %s",string); printf (\n\nO terceiro caractere digitado %c. Modifique-o.,string[2]); printf (\n\tIngresse um caractere ); scanf (%c, &string[2]); printf (\n\nO terceiro caractere atualmente %c.,string[2]); } Para se acessar um determinado caracter de uma string, basta utilizar o ndice ou nmero de posio do caractere na string. Lembre-se de que os ndices nos vetores no C sempre comeam em zero. Pode-se inicializar a string no momento da sua declarao utilizando uma string constante, que dada sempre entre aspas. Por exemplo, char nome_amigo[7] = Luis;

27

Estruturas de controle
Os comandos de controle de fluxo so a essncia de qualquer linguagem, porque governam o fluxo da execuo do programa. So poderosos e ajudam a explicar a popularidade da linguagem. As estruturas de controle de fluxo so fundamentais porque sem elas s haveria uma maneira do programa ser executado, de cima para baixo e comando por comando. No seria possvel testar condies, fazer repeties ou saltos. A linguagem C possui diversos comandos de controle de fluxo. possvel resolver todos os problemas sem utilizar todas elas, mas deve-se manter a elegncia e facilidade de entendimento fazendo bom uso das estruturas no local certo. Podemos dividir em trs categorias. A primeira consiste em instrues condicionais if, elseif, switch e o condicional ?:. A segunda so os comandos de controle de loop: o while, for e o dowhile. A terceira contm instrues de desvio incondicional goto (a menos elegante).

Instrues condicionais
O Comando if
usado para executar condicionalmente um segmento de cdigo, isto , apenas quando satisfeita uma condio executada uma parte do cdigo. Existe tambm o comando else, que poder ser pensado como complemento do comando if. Quando a condio em if no satisfeita ativado o else executando uma outra parte do cdigo. Sintaxe: if (condio) { bloco_de_comandos_if; } else { bloco_de_comandos_else; } Observe que existem dois blocos de comandos, um bloco para o if e um outro bloco para o else. A condio deve ser VERDADEIRA(no zero) ou FALSA(zero). Se a condio avaliada como verdadeira, o computador executar o comando ou o bloco_de_comandos do if. Se a condio falsa, caso a clusula else existir, o computador executar o comando ou o bloco_de_comandos do else ignorando os comandos do if, que no sero executados.

28 #include <stdio.h> main ( ) { int num; printf ("Digite um numero: "); scanf ("%d",&num); if (num>10) printf ("\n\nO numero e maior que 10"); if (num==10) { printf ("\n\nVoce acertou!\n"); printf ("O numero e igual a 10."); } if (num<10) printf ("\n\nO numero e menor que 10"); } #include <stdio.h> main () { int num; printf ("Digite um numero: "); scanf ("%d",&num); if (num==10) { printf ("\n\nVoce acertou!\n"); printf ("O numero e igual a 10.\n"); } else { printf ("\n\nVoce errou!\n"); printf ("O numero e diferente de 10.\n"); } } importante nunca esquecer que, quando usamos a estrutura if-else, estamos garantindo que uma das duas declaraes ser executada. Nunca sero executadas as duas ou nenhuma delas.

O comando if-else-if
A estrutura if-else-if apenas uma extenso da estrutura if-else, para testar diferentes condies. Sua forma geral pode ser escrita como sendo: Sintaxe: if (condio_1) { bloco_de_comandos_1; } else if (condio_2 ) {

29 bloco_de_comandos_2; } else if (condio_3) { bloco_de_comandos_3; } . . else { bloco_de_comandos_default; } A estrutura acima funciona da seguinte maneira, o programa comea testando a condio_1. Se ela for verdadeira, executa o bloco_de_comandos_1 e cai fora de todos os outros elses. No caso contrrio, testa a condio_2 no primeiro else if; se ela for verdadeira, executa o bloco_de_comandos_2 e cai fora dos prximos elses. Se a condio_2 tambm for falsa, testa a condio_3 do prximo else if, se ela for verdadeira, executa o bloco_de_comandos_3 e cai fora dos prximos elses. Caso contrrio, testa a condio do prximo else if e assim sucessivamente. Apenas quando todas as condies so falsas executado o bloco_de_comandos_default. S um bloco_de_comandos ser executado, isto , s ser executada a declarao equivalente primeira condio que resultar diferente de zero. #include <stdio.h> main ( ) { int num; printf ("Digite um numero: "); scanf ("%d",&num); if (num>10) printf ("\n\nO numero e maior que 10"); else if (num==10) { printf ("\n\nVoce acertou!\n"); printf ("O numero e igual a 10."); } else if (num<10) printf ("\n\nO numero e menor que 10"); } #include <stdio.h> main ( ) { int num; printf ("Digite um numero: "); scanf ("%d",&num); if (num==10) { printf ("\n\nVoce acertou!\n"); printf ("O numero e igual a 10.\n");

30 } else { if (num>10) { printf ("O numero e maior que 10."); } else { printf ("O numero e menor que 10."); } } } possvel ter um if dentro da declarao de um outro if mais externo, como no ltimo exemplo. Isto chamado de ifs aninhados. Porm, voc deve saber exatamente a qual if um determinado else est ligado.

Switch
Pode ocorrer que voc queira testar uma varivel ou uma expresso em relao a vrios valores. Como visto acima, pode-se utilizar if-else-if, mas tambm existe uma outra opo, o comando switch. Uma instruo switch torna-se prtica sempre que um programa necessita selecionar algumas aes dentre as diversas possveis, tendo como base o resultado de uma expresso ou uma varivel, equivalentes a um valor inteiro ou a um caractere. Isto , o comando switch prprio para se testar uma varivel em relao a diversos valores prestabelecidos. Sintaxe: switch (varivel) { case constante_1: bloco_de_comandos_1; break; case constante_2: bloco_de_comandos_2; break; . . case constante_n: bloco_de_comandos_n; break; default: bloco_de_comandos_default; }

31 A diferena fundamental com o if-else-if que a estrutura switch no aceita expresses. Aceita apenas constantes. O switch testa a varivel e executa o bloco_de_comandos cujo case corresponda ao valor atual da varivel. A declarao default opcional e ser executada apenas se a varivel, que est sendo testada, no for igual a nenhuma das constantes. O comando break faz com que o switch seja interrompido assim que uma das declaraes for executada. Mas ele no essencial ao comando switch. Se aps a execuo do bloco_de_comandos no houver um break, o programa continuar executando os prximos blocos de comandos sem testar com o valor das prximas constantes. Isto pode ser til em algumas situaes, mas recomendado muito cuidado. #include <stdio.h> main () { int num; printf ("Digite um numero: "); scanf ("%d",&num); switch (num) { case 9: printf ("\n\nO numero e igual a 9.\n"); break; case 10: printf ("\n\nO numero e igual a 10.\n"); break; case 11: printf ("\n\nO numero e igual a 11.\n"); break; default: printf ("\n\nO numero nao e nem 9 nem 10 nem 11.\n"); } }

O condicional ? :
Quando o compilador avalia uma condio, ele quer um valor de retorno para poder tomar a deciso. Mas esta expresso no necessita ser uma expresso no sentido convencional. Uma varivel sozinha pode ser uma "expresso" e esta retorna o seu prprio valor. Isto quer dizer que teremos as seguintes equivalncias:

32 int num; if (num!=0) .... if (num==0) .... equivale a int num; if (num) .... if (!num) .... Com isto, possvel simplificar algumas expresses simples. A instruo condicional ? : proporciona uma maneira rpida de se escrever uma condio de teste. Como nas anteriores, instrues condicionais so verificadas e aes associadas so executadas conforme a expresso for avaliada como verdadeira ou falsa. Sintaxe: (condio) ? ao_1 : ao_2; Quando avaliada a condio como verdadeira, executada a ao_1, caso contrrio executada a ao_2. Isto significa que a instruo condicional anterior equivalente ao ifelse seguinte: if (condio) { ao_1; } else { ao_2; } O operador ? : tambm conhecido como operador ternrio porque precisa de trs operandos. O operador ? : limitado pois no atende a uma gama muito grande de casos. #include <stdio.h> main( ) { char c; int resp, val1, val2; printf("\nDigite dois valores inteiros : "); scanf("%d %d", &val1, &val2); printf("\nDigite '+' para somar e outra tecla para substrair : "); scanf("%c", c); resp = (c == '+') ? val1+val2 : val1-val2; return 0; }

33

Instrues em loops
A linguagem C contm uma srie-padro de instrues de controle de repetio, os chamados laos for, while, do-while, que compem a segunda famlia de comandos de controle de fluxo. Todos os laos podem terminar naturalmente baseados na condio de teste booleano. No entanto em C, um lao de repetio pode terminar devido a uma condio de erro antecipado usando instrues como break, ou exit. Os laos de repetio podem tambm ter seu fluxo de controle lgico alterado por instrues break e continue.

O lao for
O comando for utilizado para repetir um comando ou um bloco de comandos diversas vezes, de maneira que se possa ter um bom controle sobre o loop ou lao. Sintaxe: for ( inicializao; condio; incremento) { bloco_de_comandos; } Em sua forma mais simples, a inicializao um comando de atribuio que o compilador usa para estabelecer a varivel de controle do lao. A condio uma expresso de relao que testa a varivel de controle do lao contra algum valor para determinar quando terminar as repeties. O incremento define a maneira como a varivel de controle do lao ser alterada cada vez que o computador repetir o lao. Para entender melhor o lao for, observe como ele funciona. O lao for equivalente a se fazer o seguinte: inicializao; if (condio) { bloco_de_comandos; incremento; Volte para o comando if" } Assim, o for executa a inicializao incondicionalmente e testa a condio. Se a condio for falsa ele no faz mais nada. Se a condio for verdadeira ele executa o bloco_de_comandos, faz o incremento e volta a testar a condio. Ele fica repetindo estas operaes at que a condio seja falsa. Um ponto importante que podemos omitir qualquer uma das trs expresses do for, isto , se no necessita-se de uma inicializao pode ser omitida. #include <stdio.h> main ( ) { int count; for ( count = 1; count <= 100; count++) printf ("%d ", count); }

34

O incremento da varivel count feito usando o operador de incremento. Esta a forma usual de se fazer o incremento (ou decremento) em um lao for. Como nenhuma das trs expresses no lao necessria, pode-se ter a forma : Neste caso no existe inicializao, nem teste de condio, nem incremento. for( ; ; ).

Quando no existe teste de condio expressa, o for assume condio sempre verdadeira, o que implica executar o lao para sempre, a no ser que ele seja interrompido. Esta forma chamada de loop infinito. Para interromper um loop como este usa-se o comando break. O comando break vai quebrar o loop infinito e o programa continuar sua execuo normalmente. Como exemplo veja-se o programa que faz a leitura de uma tecla e sua impresso na tela, at que o usurio aperte uma tecla especial, denominada FLAG. O nosso FLAG ser a letra 'X'. #include <stdio.h> main () { int Count; char ch; for ( Count=1; ; Count++ ) { fflush(NULL); scanf("%c",&ch); if (ch == 'X') break; printf("\nLetra: %c\n",ch); } } Ateno ao comando fflush(NULL). O papel deste comando limpar o buffer do teclado para que outros caracteres armazenados no buffer do computador sejam liberados. Desta forma a leitura de caractere que acontece logo aps a sua execuo no ficar prejudicada. Caso o bloco_de_comandos seja vazio, diz-se que o lao sem contedo. Um loop sem contedo tem a forma (observe o ponto e vrgula!) : for ( inicializao; condio; incremento) ; Uma das aplicaes desta estrutura gerar tempos de espera. #include <stdio.h> main ( ) { long int i; printf("\a"); for (i=0; i<10000000; i++); printf("\a"); }

/* Imprime o caracter de alerta (um beep) */ /* Espera 10.000.000 de iteracoes */ /* Imprime outro caracter de alerta */

35 Por outra parte, as expresses no lao for podem ser compostas por vrias instrues. Observe. #include <stdio.h> main( ) { int x, y; for (x=0, y=0; x+y<100; ++x, ++y) printf("%d ",x+y); } Combinando o operador ? : com o for, mostra-se um exemplo interessante, do contador circular. #include <stdio.h> main( ) { int index = 0, counter; char message[5] = "Curso C"; for (counter = 0; counter < 1000; counter++) { printf("%c", message[index]); index = (index == 7) ? (index=0; printf(\n);) : ++index; } } A mensagem Curso C escrita na tela at a varivel counter determinar o trmino do programa. Enquanto isto a varivel index assume os valores 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, ... progressivamente.

O lao while
Uma segunda maneira de executar um lao utilizando o comando while, que significa enquanto. O while permite que o cdigo fique sendo executado numa mesma parte do programa de acordo com uma determinada condio. Sintaxe: while (condio) { bloco_de_comandos; } Para o while tambm o bloco_de_comandos pode ser vazio, simples ou mltiplas instrues. Ele testa a condio antes de executar o lao e executa o bloco_de_comandos desde que a condio seja verdadeira, fazendo o teste novamente e assim por diante. Como o comando for, pode ser utilizado para um loop infinito, bastando para isto dar uma condio eternamente verdadeira como : while(1).

36 Observe como funciona o while. if (condio) { bloco_de_comandos; "Volte para o comando if" }

#include <stdio.h> main ( ) { char c; c = '\0'; while (c!='q') { fflush(NULL); scanf("%c",&c); } }

O lao do-while
Difere tanto do loop for quanto do while pelo fato de ser um lao do tipo ps-teste. Sintaxe: do { bloco_de_comandos; } while(condio); A grande novidade no comando do-while que ele, ao contrrio do for e do while, garante que o bloco_de_comandos ser executado pelo menos uma vez, pois o primeiro teste realizado ao final da primeira iterao. Mesmo que o bloco_de_comandos seja apenas um comando uma boa prtica deixar as chaves. O ponto e vrgula final obrigatrio. O funcionamento do comando do-while como segue. Bloco_de_comandos; if ( condio ) volta para o bloco_de_comandos Observa-se que a estrutura do-while executa o bloco_de_comandos, testa a condio e, se esta for verdadeira, volta para o bloco_de_comandos. Um dos principais usos da estrutura do-while em menus, nos quais voc quer garantir que o valor digitado pelo usurio seja vlido.

37 #include <stdio.h> main ( ) { char c; do { printf ("\n\nEscolha uma alternativa :\n\n"); printf ("\t(1)...Mamao\n\t(2)...Abacaxi\n\t(3)...Laranja\n\n"); fflush(NULL); scanf("%c", &c); } while ((c!='1') && (c!='2') && (c!='3')); switch (c) { case 1: printf ("\t\tVoce escolheu Mamao.\n"); break; case 2: printf ("\t\tVoce escolheu Abacaxi.\n"); break; case 3: printf ("\t\tVoce escolheu Laranja.\n"); break; } }

break, continue
O Comando break tem dois usos. utilizado para quebrar a execuo dos comandos switch, e ele interrompe a execuo de qualquer loop ou lao. O break utilizado para sair de um lao antes que a condio de teste se torne falsa e faz com que a execuo do programa continue na primeira linha seguinte ao lao. Algumas vezes torna- necessrio "pular" uma parte do programa. Para isso utilizado o se continue. O continue fora a prxima iterao do lao e pula o cdigo que estiver em seguida. Ele diferente do break, apenas funciona dentro de um lao. Quando o comando continue encontrado, o lao pula para a prxima iterao, mas no sai do lao. #include <stdio.h> main( ) { int opcao; while (opcao != 5) { printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5)||(opcao <1)) continue; /* Opcao invalida: volta ao inicio do loop */ switch (opcao) { case 1:

38 printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break; } } } O programa acima ilustra uma simples e til aplicao para o continue. Ele recebe uma opo do usurio. Se esta opo for invlida, o continue faz com que o fluxo seja desviado de volta ao incio do loop. Caso a opo escolhida seja vlida o programa segue normalmente.

Instruo de desvio incondicional


O comando goto
O goto o ltimo comando de controle de fluxo. Ele pertence a classe dos comandos de salto incondicional. O goto realiza um salto para um local especificado, que determinado por um rtulo. Um rtulo, na linguagem C, uma marca no programa. Voc d o nome que quiser a esta marca. Sintaxe: nome_do_rtulo : . . . goto nome_do_rtulo; Deve-se declarar o nome do rtulo na posio a qual se gostaria dar o salto e seguido : dois pontos. O goto pode saltar para um rtulo que esteja mais frente ou para trs no programa, embora o rtulo e o goto devam estar dentro da mesma funo.

39 Observar como funciona o goto quando utilizado para substituir um comando for. inicializao; rtulo_do_loop: if (condio) { declarao; incremento; goto rtulo_do_loop; } Recomenda-se utilizar o comando goto com parcimnia, pois o abuso no seu uso pode tornar o cdigo confuso. O goto no um comando necessrio, mas o seu bom emprego pode facilitar o entendimento de algumas funes. O comando goto pode tornar um cdigo muito mais fcil de se entender se ele for bem empregado. Um caso em que ele pode ser til quando temos vrios loops e ifs aninhados e se queira, por algum motivo, sair destes loops e ifs todos de uma vez. Neste caso um goto resolve o problema muito mais elegantemente que vrios breaks, sem contar que os breaks exigiriam muito mais testes. Neste caso o goto mais elegante e mais rpido. No abusar. #include <stdio.h> main( ) { int opcao; while (opcao != 5) { REFAZ : printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5)||(opcao <1)) goto REFAZ; /* Opcao invalida: volta ao rotulo REFAZ */ switch (opcao) { case 1: printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break; } } }

40

Vetores, Matrizes e Strings


Vetores
Os computadores processam dados. Geralmente os dados so organizados em forma ordenada, por exemplo em uma srie temporal, na sada de um experimento que varia quando modificada a entrada, ou em uma lista de nomes em ordem alfabtica. Uma coleo de dados tais que os seus elementos formam uma seqncia ordenada chamada de array ou vetor de dados. Os elementos deste vetor podem ser de qualquer tipo de dado, isto , pode-se ter um vetor de inteiros, de caracteres, etc. A seguir estuda-se as caractersticas, notao, manuseio, diferentes tipos dos vetores. Os vetores so uma estrutura de dados muito utilizada. importante notar que vetores, matrizes bidimensionais e matrizes de qualquer dimenso so caracterizadas por terem todos os elementos pertencentes ao mesmo tipo de dado. Para declarar um vetor utiliza-se a seguinte forma geral: tipo_da_varivel nome_da_varivel_vetor [tamanho]; Na presena de uma declarao como esta o C reserva um espao na memria suficientemente grande para armazenar o nmero de clulas especificadas em tamanho, e todas elas com o espao suficiente para armazenar valores do tipo tipo_da_varivel. tamanho deve ser um valor constante.

Ao declarar : float exemplo [20]; o C ir reservar 20 espaos para pontos flutuantes do tipo double, isto 4x20=80 bytes. Estes bytes so reservados de maneira contgua. Na linguagem C a numerao comea sempre em zero. Isto significa que, no exemplo acima, os dados sero indexados de 0 a 19. Para acessar os elementos do vetor, suficiente chamar o nome_da_varivel_vetor e entre colchetes indicar o nmero do elemento desejado menos um. Em exemplo[0] e exemplo[9], est-se acessando o primeiro elemento e o dcimo elemento do vetor exemplo.

O C no verifica o ndice que o programador informa para acessar os elementos de um vetor. Se o programador no tiver cuidado com os limites de validade para os ndices ele corre o risco de ter sobre-escritas ou de estar alocando fora do vetor e talvez ver o computador travar. Assim, ningum impede que se escreva, exemplo[30];.

41 #include <stdio.h> main ( ) { int num[100]; /* Declara um vetor de inteiros de 100 posicoes */ int count=0; int totalnums; do { printf ("\nEntre com um numero (-999 p/ terminar): "); scanf ("%d",&num[count]); count++; } while (num[count-1]!=-999); totalnums=count-1; printf ("\n\n\n\t Os nmeros que voc digitou foram:\n\n"); for (count=0;count<totalnums;count++) printf (" %d",num[count]); } No exemplo acima, o inteiro count inicializado em 0. O programa pede pela entrada de nmeros at que o usurio entre com o Flag -999. Os nmeros so armazenados no vetor num. A cada nmero armazenado, o contador do vetor incrementado para na prxima iterao escrever na prxima posio do vetor. Quando o usurio digita o flag, o programa abandona o primeiro loop e armazena o total de nmeros gravados. Por fim, todos os nmeros so impressos. bom lembrar aqui que nenhuma restrio feita quanto a quantidade de nmeros digitados. Se o usurio digitar mais de 100 nmeros, o programa tentar ler normalmente, mas o programa os escrever em uma parte no alocada de memria, pois o espao alocado foi para somente 100 inteiros. Isto pode resultar nos mais variados erros em tempo de execuo.

Strings
Strings so vetores de chars, ou caracteres. As strings so o uso mais comum para os vetores. Fique sempre atento para o fato de que as strings tm o seu ltimo elemento como sendo um '\0'. A declarao geral para uma string : char nome_da_string [tamanho]; As strings foram j estudadas anteriormente, mas o seu estudo ampliado aqui, pois a biblioteca do C possui diversas funes que manipulam strings. Estas funes so teis pois no possvel, por exemplo, igualar duas strings : string1 = string2; /* Nunca faa isto */ No captulo dos ponteiros se ver porque. Por enquanto aprenda que as strings devem ser igualadas elemento a elemento, isto , faz-se a cpia dos caracteres de uma string para o vetor da outra string. O caractere \0 que finaliza toda string pode ser aproveitado em muitas situaes, como no exemplo a seguir.

42 #include <stdio.h> main ( ) { int count; char str1[100],str2[100]; printf(\nEntre com uma string (finaliza com :) ); for(count = 0; count < 99; count++) { char c; scanf(%c, &c); if( c == : ) break; str1[count] = c; } str1[count] = \0; for (count=0;str1[count];count++) str2[count]=str1[count]; str2[count]='\0'; .... /* Aqui o programa continua */ } O segundo lao for acima baseado no fato de que a string que est sendo copiada termina em '\0'. Este tipo de raciocnio a base do C e voc deve fazer um esforo para entender como o programa acima funciona. Quando o elemento encontrado em str1[count] o '\0', o valor retornado para o teste condicional falso (nulo). Desta forma a expresso que vinha sendo verdadeira (no zero) torna-se falsa.

Funes bsicas para manipulao de strings


gets( ... ) A funo gets( ) l uma string a partir do teclado. Sintaxe: gets (nome_da_string); #include <stdio.h> main ( ) { char string[100]; printf ("Digite o seu nome: "); gets(string); printf ("\n\n Ola %s",string); } Observe que vlido passar para a funo printf( ) o nome da string. Por outro lado, como o primeiro argumento da funo printf() uma string tambm vlido escrever : printf (string);

43

strcpy ( ... ) A funo strcpy() copia a string-origem para a string- destino. Sintaxe: strcpy (string_destino,string_origem); #include <stdio.h> #include <string.h> main ( ) { char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); strcpy (str2,str1); strcpy (str3,"Voce digitou a string "); printf ("\n\n%s%s",str3,str2); } strcat ( ... ) Com a funo strcat(...) a string de origem permanecer inalterada e ser anexada ao fim da string de destino. Sintaxe: strcat (string_destino,string_origem); #include <stdio.h> #include <string.h> main ( ) { char str1[100],str2[100]; printf ("Entre com uma string: "); gets (str1); strcpy (str2,"Voce digitou a string "); strcat (str2,str1); printf ("\n\n%s",str2); } strlen ( ... ) A funo strlen() retorna o comprimento da string fornecida. O terminador nulo no contado. Sintaxe: strlen (string); #include <stdio.h> #include <string.h> main ( ) { int size; char str[100]; printf ("Entre com uma string: "); gets (str); size=strlen (str); printf ("\n\nA string que voce digitou tem tamanho %d",size); }

44 strcmp ( ... ) A funo strcmp() compara a string1 com a string2. Se as duas forem idnticas a funo retorna zero. Se elas forem diferentes a funo retorna no-zero. Sintaxe: strcmp (string1,string2); #include <stdio.h> #include <string.h> main () { char str1[100],str2[100]; printf ("Entre com uma string: "); gets (str1); printf ("\n\nEntre com outra string: "); gets (str2); if (strcmp(str1,str2)) printf ("\n\nAs duas strings so diferentes."); else printf ("\n\nAs duas strings so iguais."); }

45

Matrizes
Os vetores so matrizes uni-dimensionais. Agora trata-se de matrizes multi-dimensionais.

Matrizes bi-dimensionais
No caso de uma matriz bidimensional, a sua declarao : tipo_da_varivel nome_da_varivel_matriz [tamanho1] [tamanho2]; Nesta estrutura, o primeiro ndice tamanho1, indexa as linhas da matriz e o segundo ndice indexa as colunas da matriz. Quando vamos preencher ou ler uma matriz no C o ndice mais direita varia mais rapidamente que o ndice esquerda. No esquea que os ndices no C variam de zero ao valor declarado, menos um. Tambm no esquea que o C no verifica os limites vlidos da matriz para o usurio. O programador responsvel por manter os ndices na faixa permitida. #include <stdio.h> main ( ) { int matrix [20][10]; int i,j,count; count = 1; for (i=0;i<20;i++) { for (j=0;j<10;j++) { matrix[i][j]=count; count++; printf( %d\t, matrix[i][j]); } printf(\n); } } A matriz preenchida linha por linha, (0 a 19). Cada linha recebe 10 valores (0 a9), iniciando em 1 e terminando em 200.

Matrizes de strings
Matrizes de strings so matrizes bidimensionais. Imagine uma string. Ela um vetor. Se declarado um vetor de strings est-se fazendo uma lista de vetores. Esta estrutura uma matriz bidimensional de chars. Sintaxe: char nome_da_varivel [numero_de_strings][tamanho_das_strings];

46

Para acessar uma string individual, s utilizar o primeiro ndice. Ento, para acessar a segunda string faa, nome_da_varivel [1]. #include <stdio.h> main ( ) { char strings [5][100]; int count; for (count=0;count<5;count++) { printf ("\n\nDigite uma string: "); gets (strings[count]); } printf ("\n\n\nAs strings que voce digitou foram:\n\n"); for (count=0;count<5;count++) printf ("%s\n",strings[count]); }

Matrizes multidimensionais
Estendendo o mencionado acima, pode-se declarar matrizes multi-dimensionais de forma simples. Sintaxe: tipo_da_varivel nome_da_varivel_matriz [tamanho1] [tamanho2] ... [tamanhoN]; Tem-se declarado uma matriz N-dimensional, e o funcionamento dela basicamente como os tipos anteriores de matrizes. O ndice que varia sempre mais rapidamente o ndice mais direita.

Inicializao de matrizes
Assim como as variveis no C podem ser inicializadas, as matrizes tambm podem ser inicializadas. suficiente listar os valores dos elementos da matriz entre chaves. Observe: tipo_da_varivel nome_da_matriz [tam1][tam2] ... [tamN] = { lista_de_valores }; A lista de valores composta por valores (do mesmo tipo_da_varivel) separados por vrgula. Os valores devem ser dados na ordem em que sero colocados na matriz. float vect [6] = { 1.3, 4.5, 2.7, 4.1, 0.0, 100.1 }; int matrix [3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

47 O primeiro exemplo inicializa um vetor. O segundo inicializa uma matriz com 1,2,3 e 4 na sua primeira linha; 5,6,7 e 8 na sua Segunda linha e 9,10,11 e 12 na terceira linha. A seguir so apresentadas formas de inicializar strings. O primeiro e segundo caso so duas formas de inicializar a mesma string, a segunda forma a forma compacta. O terceiro caso inicializa um vetor de strings. char str [10] = { 'J', 'o', 'r', 'g', e, '\0' }; char str [10] = "Jorge"; char str_vect [3][10] = { "Jorge", "Ze Maria", "Curso C" }; Ao inicializar uma matriz possvel no especificar o tamanho dela a priori. O compilador C, na hora da compilao, verifica o tamanho do que voc declarou na inicializao e este mesmo ser o tamanho da matriz. Este tamanho no pode ser mais mudado durante o programa. Isto til para inicializar uma string sem ficar contando o nmero de caracteres que necessrio. char message [] = "Curso C - O primeiro contato com o C."; int matrix [][2] = { 1,2,2,4,3,6,4,8,5,10 }; O tamanho de message 38, enquanto a matriz bi-dimensional da forma matrix[5][2].

48

Os ponteiros
A dificuldade que tm os leigos para aprender a mexer com os ponteiros, tem a ver com a terminologia utilizada no C. Por exemplo, quando um programador em C diz que uma certa varivel um ponteiro, o que significa? Como uma varivel pode apontar para algo? Quando um programador diz que um ponteiro proporciona um valor, como pode um ponteiro proporcionar um valor? Entretanto, no difcil de entender os ponteiros pensando neles da seguinte forma. Uma varivel do tipo int guarda valores inteiros. Uma varivel do tipo float guarda nmeros de ponto flutuante em preciso simples. Se a varivel do tipo char, ela guarda caracteres. Ponteiros guardam endereos de memria. Quando voc anota o endereo de um colega voc est criando um ponteiro. O ponteiro este seu pedao de papel, ele est armazenando um endereo. Quando voc anota o endereo de um colega, depois voc vai usar este endereo para ach-lo. O C funciona assim. Voc anota o endereo de algo numa varivel ponteiro para depois usar. Da mesma maneira, uma agenda, onde so guardados endereos de vrios amigos, poderia ser vista como sendo uma matriz de ponteiros no C. Um ponteiro tambm tem tipo. Quando voc anota um endereo de um amigo voc o trata diferente de quando voc anota o endereo de uma empresa. Mesmo que o endereo dos dois locais tenham o mesmo formato : rua, nmero, bairro, cidade, etc, eles indicam locais cujos contedos so diferentes. Ento os dois endereos so ponteiros mas de tipos diferentes. Assim, ponteiros que apontam para inteiros so de tipo diferente daqueles que apontam para variveis ponto flutuante. Em resumo, um ponteiro uma varivel que contm o endereo de uma outra varivel. Para declarar um ponteiro utiliza-se a seguinte sintaxe: tipo_da_varivel_apontada *nome_da_varivel_ponteiro; A presena do * indica ao compilador que aquela varivel chamada nome_da_varivel_ponteiro, no vai guardar um valor mas sim um endereo para uma outra varivel do tipo especificado. O * no parte do nome do ponteiro. Para declarar um ponteiro para uma varivel de inteiros utilize : int *nomept; Para declarar dois ponteiros em uma mesma declarao, escreva : char *ptr1,*ptr2; Os ponteiros acima declarados ainda no foram inicializados (como toda varivel do C que apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na poro da memria reservada ao sistema operacional do computador. Utilizar o ponteiro nestas circunstncias pode levar a problemas inesperados, geralmente perigosos. Antes de usar um ponteiro, ele deve ser inicializado (apontado para algum lugar conhecido).

49

Operadores de ponteiros : & e *


Para atribuir um valor a um ponteiro basta igual-lo a um valor de memria. Mas, todos os endereos das variveis de um programa so determinados pelo compilador na hora da compilao e alocados novamente na execuo. Para recuperar estes endereos existe o operador & chamado operador de endereo. Assim, um ponteiro pode ser inicializado utilizando o operador de endereo. Exemplo: int count=10; int *ptr; ptr = &count; /*a varivel ptr aponta para a varivel count*/ Observe que os contedos so totalmente diferentes, e o fato de atribuir o endereo de count para ptr no altera o valor de count. Agora ptr j pode ser utilizado com segurana. Desde que os pointers so variveis, eles podem ser manipulados como qualquer varivel. Se ptr1 um ponteiro e ptr2 um outro ponteiro, ambos para inteiros, pode-se fazer a seguinte declarao: ptr1 = ptr2; Neste caso se faz o ponteiro ptr1 apontar para o mesmo lugar que ptr2. Com eles pode-se alterar o contedo da varivel apontada pelo ponteiro. Para isto, preciso utilizar o operador inverso do operador &, ele o operador para desreferencia *. Seu operando o endereo de uma varivel e ele retorna o valor armazenado na varivel, um operador unrio. O smbolo * o mesmo que do operador multiplicao, mas so diferentes operadores. Exemplo: int itens = 15 + *ptr; *ptr = 45; Na varivel itens atribudo o valor da varivel count acrescentado em 15. Observe que no foi colocado parntesis, pois os operadores & e * tm maior precedncia que os operadores aritmticos. Na segunda instruo foi alterado o contedo de count via ponteiro, em count atribudo o valor 45. Observe que, se ptr aponta para a varivel count, ento *ptr pode ser utilizado em qualquer lugar que count seria. #include <stdio.h> main ( ) { int num,valor; int *p; num = 55; p=&num; /* Pega o endereco de num */ valor=*p; /* Valor e igualado a num de uma maneira indireta */ printf ("\n\n%d\n",valor); printf ("Endereco para onde o ponteiro aponta: %p\n",p); printf ("Valor da variavel apontada: %d\n",*p); }

50 #include <stdio.h> main ( ) { int num,*p; num=55; p=&num; /* Pega o endereco de num */ printf ("\nValor inicial: %d\n",num); *p=100; /* Muda o valor de num de uma maneira indireta */ printf ("\nValor final: %d\n",num); } Nos exemplos acima mostra-se o funcionamento dos ponteiros. No primeiro, o cdigo de formato %p usado na funo printf( ) indica funo que ela deve exibir um endereo. #include <stdio.h> main( ) { int x,*ptrx; x = 1; ptrx = &x; printf("x = %d\n", x); printf("ponteiro do x, ptrx = %u\n", ptrx); printf("*ptrx+1 = %d\n", *ptrx+1); printf("ptrx = %u\n",ptrx); printf("*ptrx = %d\n",*ptrx); printf("*ptrx+=1 = %d\n",*ptrx+=1); printf("ptrx = %u\n",ptrx); printf("(*ptrx)++ = %d\n",(*ptrx)++); printf("ptrx = %u\n",ptrx); printf("*(ptrx++) = %d\n",*(ptrx++)); printf("ptrx = %u\n",ptrx); printf("*ptrx++ = %d\n",*ptrx++); printf("ptrx = %u\n",ptrx); }

Operadores aritmticos e de comparao com ponteiros


Com variveis ponteiros possvel utilizar os operadores aritmticos de incremento, decremento, soma e subtrao de inteiros. Mas estas operaes no funcionam como com inteiros. Quando incrementado um ponteiro ele passa a apontar para o prximo valor do mesmo tipo para o qual o ponteiro aponta. Isto , o endereo armazenado no ponteiro no incrementado em um, ele incrementado tanto quanto for preciso para apontar para a prxima varivel contgua quela que estava anteriormente apontando. Se o ponteiro apontava para um inteiro e incrementado ele passa a apontar para o prximo inteiro; o endereo armazenado incrementado em 2 bytes para pular o inteiro anteriormente apontado. Se apontava para um double o endereo ser incrementado em 8 bytes. uma

51 das razes porque o compilador precisa saber o tipo de um ponteiro. O decremento funciona analogamente. necessrio identificar a diferena entre as seguintes instrues: *(ptr++); (*ptr)++; No primeiro incrementa-se o ponteiro apontando para o prximo inteiro e retorna-se o contedo da varivel agora apontada. No segundo caso, retorna-se o contedo da varivel apontada por ptr e este contedo incrementado em um. Na soma ou subtrao de inteiros com ponteiros, faz-se o ponteiro avanar ou retroceder tantas posies do tipo da varivel quanto seja o resultado da operao aritmtica. Se for soma acrescenta-se o endereo em bytes, se for subtrao decrementa-se o endereo. Exemplo: ptr = ptr + 15; /* ptr aponta 15 posies de inteiros aps a posio antiga */ itens = *(ptr + 1); /* atribui-se a itens o contedo do prximo inteiro em relao ao atual */ s vezes til comparar dois ponteiros. Utilizando ptr1 == ptr2, e ptr1 != ptr2, se testa se dois ponteiros so ou no so iguais, isto se esto apontando a mesma posio na memria. No caso de operaes do tipo ptr1 > ptr2, ptr1 < ptr2, ptr1 >= ptr2 e ptr1 <= ptr2, compara-se qual ponteiro aponta para uma posio mais adiante na memria. Voc no pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair nmeros em ponto flutuante a ponteiros.

Ponteiros para ponteiros


Um ponteiro para um ponteiro uma forma de indicao mltipla. Em um ponteiro normal, o valor do ponteiro o valor do endereo da varivel que contm o valor desejado. No caso de ponteiro para ponteiro o primeiro ponteiro contm o endereo do segundo, que aponta para a varivel que contm o valor desejado. Exemplo : double **ptrdeptr; O ptrdeptr um ponteiro para um ponteiro que est apontando a uma varivel do tipo double. #include <stdio.h> main( ) { int x,*p,**q; x=10; p=&x; q=&p; printf("%d",**q); }

52

O erro chamado de ponteiro perdido um dos mais difceis de se encontrar, pois a cada vez que a operao com o ponteiro utilizada, poder estar sendo lido ou gravado em posies desconhecidas da memria. Isso pode acarretar em sobreposies sobre reas de dados ou mesmo rea do programa na memria. Exemplo: int *p; x=10; *p=x; Foi atribudo o valor 10 a uma localizao desconhecida de memria. A conseqncia desta atribuio imprevisvel. Nota : Muito cuidado no uso dos ponteiros. importante voc saber sempre para onde o ponteiro est apontando. Nunca utilize um ponteiro que no foi inicializado.

Ponteiros e vetores
Em C existe um grande relacionamento entre ponteiros e matrizes, sendo que eles podem ser tratados da mesma maneira. As verses com ponteiros geralmente so mais rpidas.

Vetores como ponteiros


Uma idia de como o C trata vetores. Quando voc declara uma matriz da seguinte forma: tipo_da_varivel nome_da_matriz [tam1][tam2] ... [tamN]; o compilador C calcula o tamanho, em bytes, necessrio para armazenar esta matriz. Este tamanho calculado multiplicando : tam1 x tam2 x tam3 x ... x tamN x tamanho_do_tipo. O compilador ento aloca este nmero de bytes em um espao livre de memria. O nome da varivel que voc declarou na verdade um ponteiro para o tipo da varivel da matriz. Este conceito fundamental, porque tendo alocado na memria o espao para a matriz, ele toma o nome da varivel (que um ponteiro) e aponta para o primeiro elemento da matriz. Ento como possvel usar o seguinte? nome_da_matriz[ndice] Isto pode ser facilmente explicado desde que voc entenda que a notao acima absolutamente equivalente a se fazer: *(nome_da_matriz + ndice) Assim funciona um vetor. Fica claro ento, porque que, no C, a indexao comea com zero. porque, ao pegar o valor do primeiro elemento de um vetor, quer-se de fato o contedo do ponteiro nome_da_matriz, isto *nome_da_matriz. Da tem-se um ndice igual a zero. Ento *nome_da_matriz equivalente a nome_da_matriz[0].

53 Cuidado com os limites do vetor. C no verifica o tamanho do vetor. Ele apenas aloca a memria, ajusta o ponteiro do nome do vetor para o incio do mesmo e, quando voc usa os ndices, encontra os elementos requisitados. Para varrer todos os elementos de uma matriz de uma forma seqencial bem mais simples utilizar ponteiros. Considere o seguinte programa para zerar uma matriz. #include <stdio.h> main () { float matrix [50][50]; int i,j; for (i=0;i<50;i++) for (j=0;j<50;j++) matrix[i][j]=0.0; } Uma maneira muito mais eficiente ser, #include <stdio.h> main () { float matrix [50][50]; float *p; int count; p=matrix[0]; for (count=0;count<2500;count++) { *p=0.0; p++; } } Voc consegue ver porque o segundo programa mais eficiente? Simplesmente porque cada vez que se faz matrix[i][j] o programa tem que calcular o deslocamento para dar ao ponteiro. Ou seja, o programa tem que calcular 2500 deslocamentos. No segundo programa o nico clculo que deve ser feito o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro muito mais rpido que calcular 2500 deslocamentos completos. H uma diferena entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro uma varivel, mas o nome de um vetor no uma varivel. Isto significa, que no se consegue alterar o endereo que apontado pelo "nome do vetor". Exemplo: int vetor[10]; int *ponteiro, i; ponteiro = &i;

54 /* as operacoes a seguir sao invalidas */ vetor = vetor + 2; /* ERRADO: vetor nao e' variavel */ vetor++; /* ERRADO: vetor nao e' variavel */ vetor = ponteiro; /* ERRADO: vetor nao e' variavel */ Teste as operaes acima no seu compilador. Ele apresentar uma mensagem de erro. Alguns compiladores diro que vetor no um Lvalue. Lvalue, significa "Left value", um smbolo que pode ser colocado do lado esquerdo de uma expresso de atribuio, isto , uma varivel. Outros diro que se tem "incompatible types in assignment", tipos incompatveis em uma atribuio. /* as operacoes abaixo sao validas */ ponteiro = vetor; /* CERTO: ponteiro e' variavel */ ponteiro = vetor+2; /* CERTO: ponteiro e' variavel */

Ponteiros como vetores


Lembre que o nome de um vetor um ponteiro constante e que pode ser indexado. Identicamente todo ponteiro pode ser indexado, isto , para um ponteiro ptr, a expresso ptr[3] equivale a *(p+3). #include <stdio.h> main ( ) { int values[] = { 11, 21, 31, 44, 55, 66, 77, 88, 99, 101 }; int *p; p = values; printf ("Elemento 4 do vetor values, values[3] = %d",p[3]); } Aproveitando o exposto sobre a indexao de ponteiros, a expresso : &nome_do_ponteiro[ndice] vlida e retorna o endereo da casa do vetor indexado por ndice. Como conseqncia, o ponteiro nome_do_ponteiro tem o endereo &nome_da_varivel[0], o que indica onde na memria est guardado o valor do primeiro elemento do vetor.

Ponteiros e strings
Um caso interessante a inicializao de ponteiros com strings constantes. Toda string inserida em um programa colocada em um banco de strings que o compilador cria. No local onde est uma string no programa, o compilador coloca o endereo do incio daquela string (que est no banco de strings). Como exemplo, se analisa a funo strcpy(...) que pede dois parmetros do tipo char*. Uma declarao da forma strcpy (string, "String

55 constante.") faz com que o compilador substitua a string "String constante." pelo seu endereo no banco de strings. Isto sugere a inicializao de um ponteiro para uma string que vai ser utilizada vrias vezes. char *str1 = "String constante."; Ento em todo lugar que se necessite a string pode se usar a varivel str1. Observe que se o ponteiro alterado perdida a string. Por outro lado, se utilizado o ponteiro para alterar a string pode-se facilmente corromper o banco de strings que o compilador criou. Em resumo, nomes de strings so do tipo char*. Lembre que os ponteiros so poderosos mas, se usados com descuido podem ser uma fonte interminvel de erros. Como exemplo, define-se uma nova funo similar a strcpy(...). #include <stdio.h> #include <string.h> StrCpy (char *destino, char *origem) { while (*origem) { *destino=*origem; origem++; destino++; } *destino='\0'; } main ( ) { char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); StrCpy (str2, str1); StrCpy (str3, "Voce digitou a string "); printf ("\n\n%s%s", str3, str2); } No programa acima, note que se pode passar ponteiros como argumentos de funes. Passando o ponteiro voc possibilita funo alterar o contedo das strings. Voc j fez isto anteriormente. No comando while (*origem) usa-se o fato de que a string termina com '\0' como critrio de parada. Ao fazer origem++ e destino++ o leitor poderia argumentar que estamos alterando o valor do ponteiro base da string, mas como ser explicado na prxima seo os ponteiros passados funo so apenas cpias, assim os ponteiros originais str1 e str2 permanecem inalterados na funo main( ).

56

Ponteiros para ponteiros. Vetores de ponteiros


Podem ser construdos vetores de ponteiros como os vetores de qualquer outro tipo. Uma declarao de um vetor de ponteiros inteiros poderia ser, int *vetorptr [10];. Assim vetorptr um vetor que armazena 10 ponteiros para inteiros. Como existe uma certa equivalncia entre vetores e ponteiros, diz-se que vetorptr um ponteiro para ponteiro. Um ponteiro para um ponteiro como se voc anotasse o lugar (endereo) de uma agenda que tem o endereo da casa do seu amigo. A sintaxe para declarar um ponteiro para um ponteiro : tipo_da_varivel **nome_da_varivel; Se no cdigo do programa utiliza-se a expresso : **nome_da_varivel, est-se referenciando ao contedo final da varivel apontada; enquanto, *nome_da_varivel o contedo do ponteiro intermedirio. Em conseqncia, no C pode-se declarar ponteiros para ponteiros para ponteiros, ponteiros para ponteiros para ponteiros para ponteiros, e assim por diante. Para fazer isto, basta aumentar o nmero de asteriscos na declarao. A lgica a mesma, mas a utilidade?

57

Mais sobre funes


Funes so as estruturas que permitem ao programador separar seus programas em blocos. Se no as tivssemos, os programas geralmente seriam muito compridos e ilegveis. Para fazer programas grandes e complexos melhor constru-los bloco a bloco. A sintaxe foi vista na seo de introduo s funes. Lembre que uma funo tem um tipo-de-retorno que o tipo de varivel que a funo vai retornar. O tipo default int. Os argumentos da funo ou os parmetros informam ao compilador quais sero as entradas da funo. O corpo da funo a sua alma. nele que as entradas so processadas, sadas so geradas ou outras coisas so feitas.

O comando return
Para retornar um valor de uma funo utiliza-se o comando return. A sua sintaxe : return valor_de_retorno; ou simplesmente, return; Quando uma funo est sendo executada e se chega a uma declarao return, a funo encerrada imediatamente e, se o valor de retorno informado, a funo retorna este valor. O valor de retorno fornecido tem que ser, pelo menos, compatvel com o tipo de retorno declarado para a funo. Uma funo pode ter mais de uma declarao return, mas a funo terminada quando o programa chega primeira declarao return. #include <stdio.h> int EPar (int a) { if (a%2) /* Verifica se a e divisivel por dois */ return 0; else return 1; } main ( ) { int num; printf ("Entre com numero: "); scanf ("%d",&num); if (EPar(num)) printf ("\n\nO numero e par.\n"); else printf ("\n\nO numero e impar.\n"); }

58 Os valores retornados pelas funes podem ser aproveitados para fazer atribuies. Mas no se pode fazer: function(a,b) = x; /* Erro */. Se voc no fizer nada com o valor de retorno de uma funo ele ser descartado. Por exemplo, a funo printf(...) retorna um inteiro que em exemplo nenhum foi utilizado, ele descartado. No exemplo vemos o uso de mais de um return em uma funo.

Prottipos de Funes
At agora, as funes apresentadas nos exemplos esto fisicamente antes da funo main( ). Isto foi feito porque ao compilar a funo main( ) onde chamada outra funo, deve-se saber com antecedncia quais so os tipos de retorno e quais so os parmetros dessa funo para gerar o cdigo corretamente. Estando a outra funo antes da funo main( ) o compilador j a teria compilado e j saberia o seu formato. Mas, muitas vezes o programa est espalhado por vrios arquivos. Ou seja, so usadas funes em um arquivo que sero compiladas em outro arquivo. Como manter a coerncia? A soluo so os prottipos de funes. Prottipos so apenas declaraes de funes. Isto , a declarada uma funo que se pode usar, indicando o tipo de retorno e os argumentos de entrada que se precisa. O compilador toma ento conhecimento do formato daquela funo antes de compil-la. Sintaxe: tipo_de_retorno nome_da_funo (declarao_de_argumentos); Aqui o tipo-de-retorno, o nome-da-funo e a declarao-de-argumentos so os mesmos que se pretende usar quando realmente seja dada a funo. Repare que os prottipos tm uma ntida semelhana com as declaraes de variveis. #include <stdio.h> float Square (float a); main ( ) { float num; printf ("Entre com um numero: "); scanf ("%f",&num); num = Square(num); printf ("\n\nO seu quadrado vale: %f\n",num); } float Square (float a) { return (a*a); } Observe que a funo Square(...) est colocada depois de main( ), mas o seu prottipo est antes. Sem isto este programa no funcionaria corretamente. Usando prottipos voc pode construir funes que retornam quaisquer tipos de variveis. bom ressaltar que funes podem tambm retornar ponteiros sem qualquer problema. Usando prottipos o compilador evita erros, no deixando que o programador use funes com os parmetros errados e com o tipo de retorno errado, o que j uma grande ajuda.

59

O tipo void
Void quer dizer vazio e isto mesmo que o void . Ele permite fazer funes que no retornam nada e funes que no tm parmetros! O prottipo de uma funo que no retorna nada ser: void nome_da_funo (declarao_de_parmetros); Neste caso, no existe valor de retorno na declarao return, portanto ele no necessrio na funo. Para funes que no tm parmetros: tipo_de_retorno nome_da_funo (void); Se a funo no tem parmetros e no retornam nada: void nome_da_funo (void); #include <stdio.h> void Mensagem (void); main () { Mensagem(); printf ("\tDiga de novo:\n"); Mensagem(); } void Mensagem (void) { printf ("Ola! Eu estou vivo.\n"); } O compilador acha que a funo main() deve retornar um inteiro quando nada especificado. Isto pode ser interessante quando se precisa que o sistema operacional receba um valor de retorno da funo main( ). Conveno : se o programa retornar zero, significa que ele terminou normalmente, e, se o programa retornar um valor diferente de zero, significa que o programa teve um trmino anormal.

Funes em arquivo cabealho. Escopo dos parmetros.


o momento de falar um pouco mais dos arquivos cabealho, includos sempre em todos os exemplos para poder utilizar funes pr-definidas. Eles no possuem os cdigos completos das funes. Eles s contm prottipos de funes, o que suficiente. O compilador l estes prottipos e, baseado nas informaes l contidas, gera o cdigo correto. O corpo das funes cujos prottipos esto no arquivo-cabealho, no caso das funes do prprio C, j esto compiladas e normalmente so includas no programa no instante da "linkagem". A linkagem o instante em que todas as referncias a funes cujos cdigos no esto nos atuais arquivos fontes so resolvidas, buscando este cdigo nos arquivos de bibliotecas. possvel criar e incluir arquivos cabealho definidos pelo usurio. Isto ser dado em um curso mais avanado.

60 Os parmetros ou parmetros formais de uma funo so declarados como sendo as entradas de uma funo, eles so cpias das variveis passadas para a funo. Os parmetros formais existem independentemente das variveis que foram passadas para a funo. No h motivo para se preocupar com o escopo deles. O parmetro formal como se fosse uma varivel local da funo. Isto , a alterao do valor de um parmetro formal no ter efeito na varivel que foi passada funo.

Chamada por Valor e Chamada por Referncia


Quando chamada uma funo os parmetros formais da funo copiam os valores dos parmetros que so passados para a funo. Isto quer dizer que no so alterados os valores que os parmetros tm fora da funo. Este tipo de chamada de funo chamado chamada por valor. Isto ocorre porque so passados para a funo apenas os valores dos parmetros e no os prprios. #include <stdio.h> float sqr (float num); void main (void) { float num,sq; printf ("Entre com um numero: "); scanf ("%f",&num); sq=sqr(num); printf ("\n\nO numero original e: %f\n",num); printf ("O seu quadrado vale: %f\n",sq); } float sqr (float num) { num=num*num; return num; } No exemplo o parmetro formal num da funo sqr( ) sofre alteraes dentro da funo, mas a varivel num da funo main( ) permanece inalterada, pois uma chamada por valor. Outro tipo de chamada de funo quando se precisa que as alteraes dentro das funes nos parmetros formais alterem os valores dos parmetros que foram passados para a funo. Este tipo de chamada de funo tem o nome de "chamada por referncia". Este nome vem do fato de que, neste tipo de chamada, no se passa para a funo os valores das variveis, mas sim as suas referncias, e ento a funo usa as referncias para alterar os valores das variveis fora da funo. O C s faz chamadas por valor. Isto bom quando queremos usar os parmetros formais vontade dentro da funo, sem estar preocupados em estar mexendo nos valores dos

61 parmetros que foram passados para a funo. Mas isto tambm pode ser ruim s vezes, por exemplo quando o novo valor calculado dentro da funo deve ser priorizado. No C, existe um recurso de programao que pode ser usado para simular uma chamada por referncia. Para alterar as variveis que so passadas para uma funo, deve-se declarar seus parmetros formais como sendo ponteiros. Os ponteiros so a "referncia" que necessria para poder alterar a varivel fora da funo. O nico inconveniente que, para chamar a funo, deve-se lembrar de colocar um & na frente das variveis que estiverem sendo passadas para a funo. Como exemplo lembre da funo scanf(...). Nela passavam-se os nomes das variveis precedidos por &. Isto , a funo scanf(...) utiliza chamada por referncia porque ela precisa alterar as variveis que so passadas para ela.

#include <stdio.h> void Swap (int *a,int *b); void main (void) { int num1,num2; num1=100; num2=200; Swap (&num1,&num2); printf ("\n\nEles agora valem %d %d\n",num1,num2); } void Swap (int *a,int *b) { int temp; temp=*a; *a=*b; *b=temp; } A chamada por referncia muito utilizada para passar matrizes como argumentos. Por exemplo, seja a matriz : int matrix [50]; e seja a funo function(...) que precisa trabalhar com os elementos da matriz, ento existem trs maneiras para declarar a funo com argumento a matriz: void function (int matrix[50]); void function (int matrix[]); void function (int *matrix); Nos trs casos, tm-se dentro de function(...) um int* chamado matrix. Isto significa que no caso de passar uma matriz para uma funo, ela passada atravs de um ponteiro. Ento possvel alterar o valor dos elementos da matriz dentro da funo.

62

Argumentos da funo main


A funo main() pode ter parmetros formais. Mas o programador no pode escolher quais sero eles. A declarao mais completa que se pode ter para a funo main( ) : int main ( int argc, char *argv[]); Os parmetros argc e argv do ao programador acesso linha de comando com a qual o programa foi chamado. O argc (argumento contador) um inteiro e possui o nmero de argumentos com os quais a funo main( ) foi chamada na linha de comando. Ele no mnimo 1, pois o nome do programa contado como sendo o primeiro argumento. O argv (argumento valores) um ponteiro para uma matriz de strings. Cada string desta matriz um dos parmetros da linha de comando. O argv[0] sempre aponta para o nome do programa que considerado o primeiro argumento. para saber quantos elementos temos em argv que temos argc.

Recursividade
Na linguagem C assim como em muitas outras linguagens de programao, uma funo pode chamar a si prpria. Uma funo assim chamada funo recursiva. Todo cuidado pouco ao se fazer funes recursivas. A primeira coisa a se providenciar um critrio de parada. Este vai determinar quando a funo dever parar de chamar a si mesma. Isto impede que a funo se chame infinitas vezes. Um bom exemplo a funo fatorial(int n). #include <stdio.h> int fatorial(int n) { int ret; if (n) return n*fatorial(n-1); else return 1; } void main( ) { int n; printf("\n\nDigite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d e' %d", n, fatorial(n)); } Um perigo ao utilizar funes recursivas que a memria do computador pode ser esgotada rapidamente.

63

Ponteiros para funes


O C permite acessar variveis e funes atravs de ponteiros. Esta mais uma caracterstica que mostra a fora da linguagem de programao C. Pode-se ento fazer coisas como, por exemplo, passar uma funo como argumento para uma outra funo. Sintaxe para o ponteiro para funo. tipo_de_retorno (*nome_do_ponteiro)( ); tipo_de_retorno (*nome_do_ponteiro)(declarao_de_parmetros); Veja que no obrigatrio declarar os parmetros da funo. #include <stdio.h> #include <string.h> void PrintString (char *str,int (*fptr)()); main (void) { char String [20]="Curso de C."; int (*p)(); p=puts; PrintString (String,p); return 0; } void PrintString (char *str,int (*fptr)()) { (*fptr)(str); } No programa acima, a funo PrintString() usa uma funo qualquer fptr para imprimir a string na tela. O programador pode ento fornecer no s a string mas tambm a funo que ser usada para imprim-la. No main( ) observa-se como pode ser atribudo ao ponteiro para funes p o endereo da funo puts( ) do C.

Alocao dinmica de memria


A alocao dinmica permite ao programador criar variveis em tempo de execuo, ou seja, alocar memria para novas variveis quando o programa est sendo executado. Esta outra ferramenta que mostra o poder do C. O padro C ANSI define apenas 4 funes para o sistema de alocao dinmica, disponveis na biblioteca stdlib.h: malloc calloc realloc free No entanto, existem diversas outras funes que so amplamente utilizadas, mas dependentes do ambiente e compilador. Neste curso so abordadas apenas as funes bsicas mencionadas.

64

malloc
A funo malloc( ) serve para alocar memria e tem o seguinte prottipo: void *malloc (unsigned int num); A funo toma o nmero de bytes que se quer alocar (num), aloca na memria e retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribudo a qualquer tipo de ponteiro. Se no houver memria suficiente para alocar a memria requisitada a funo malloc( ) retorna um ponteiro nulo. #include <stdio.h> #include <stdlib.h> main (void) { int *p; int n; ... /* Determina o valor de a em algum lugar */ p=(int *)malloc(n*sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } ... return 0; } No exemplo acima, alocada memria suficiente para se colocar n nmeros inteiros. O operador sizeof( ) retorna o nmero de bytes de um inteiro. Ele util para se saber o tamanho de tipos. O ponteiro void* que malloc( ) retorna convertido para um int* pelo cast e atribudo a p. A declarao seguinte testa se a operao foi bem sucedida. Se no tiver sido, p ter um valor nulo, o que far com que !p retorne verdadeiro. Se a operao tiver sido bem sucedida, pode-se usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)]. calloc A funo calloc( ) tambm serve para alocar memria, mas possui um prottipo diferente: void *calloc (unsigned int num, unsigned int size); A funao aloca uma quantidade de memria igual a num * size, isto , aloca memria suficiente para uma matriz de num objetos de tamanho size. Retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribudo a qualquer tipo de ponteiro. Se no houver memria suficiente para alocar a memria requisitada a funo calloc( ) retorna um ponteiro nulo.

65 #include <stdio.h> #include <stdlib.h> main (void) { int *p; int a; ... p=(int *)calloc(a, sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } ... return 0; }

realloc
A funo realloc() serve para realocar memria e tem o seguinte prottipo: void *realloc (void *ptr, unsigned int num); A funao modifica o tamanho da memria previamente alocada apontada por *ptr para aquele especificado por num. O valor de num pode ser maior ou menor que o original. Um ponteiro para o bloco devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se isso ocorrer, o contedo do bloco antigo copiado no novo bloco, e nenhuma informao perdida. Se ptr for nulo, aloca size bytes e devolve um ponteiro; se size zero, a memria apontada por ptr liberada. Se no houver memria suficiente para a alocao, um ponteiro nulo devolvido e o bloco original deixado inalterado.

free
Quando alocamos memria dinamicamente necessrio que ns a liberemos quando ela no for mais necessria. Para isto existe a funo free( ) cujo prottipo : void free (void *p); Basta ento passar para free( ) o ponteiro que aponta para o incio da memria alocada. Mas voc pode se perguntar, como que o programa vai saber quantos bytes devem ser liberados? Ele sabe pois quando voc alocou a memria, ele guardou o nmero de bytes alocados numa "tabela de alocao" interna.

66 #include <stdio.h> #include <alloc.h> main (void) { int *p; int a; ... p=(int *)malloc(a*sizeof(int)); if (!p) { printf ("** Erro: Memoria Insuficiente **"); exit; } ... free(p); ... return 0; }

Alocao Dinmica de Vetores


A alocao dinmica de vetores utiliza os conceitos aprendidos na aula sobre ponteiros e as funes de alocao dinmica apresentados. Um exemplo de implementao para vetor real: #include <stdio.h> #include <stdlib.h> float *Alocar_vetor_real (int n) { float *v; /* ponteiro para o vetor */ if (n < 1) { /* verifica parametros recebidos */ printf ("** Erro: Parametro invalido **\n"); return (NULL); } /* aloca o vetor */ v = (float *) calloc (n+1, sizeof(float)); if (v == NULL) { printf ("** Erro: Memoria Insuficiente **"); return (NULL); } return (v); /* retorna o ponteiro para o vetor */ } float *Liberar_vetor_real (int n, float *v) { if (v == NULL) return (NULL); if (n < 1) { /* verifica parametros recebidos */ printf ("** Erro: Parametro invalido **\n");

67 return (NULL); } free(v); /* libera o vetor */ return (NULL); /* retorna o ponteiro */ } void main (void) { float *p; int a; ... /* outros comandos, inclusive a inicializacao de a */ p = Alocar_vetor_real (a); ... /* outros comandos, utilizando p[] normalmente */ p = Liberar_vetor_real (a, p); }

Alocao Dinmica de Matrizes


A alocao dinmica de memria para matrizes realizada da mesma forma que para vetores, com a diferena de que se tem um ponteiro apontando para outro ponteiro que aponta para o valor final, o que denominado indireo mltipla. A indireo mltipla pode ser levada a qualquer dimenso desejada, mas raramente necessrio mais de um ponteiro para um ponteiro. Um exemplo de implementao para matriz real bidimensional fornecido a seguir. A estrutura de dados utilizada neste exemplo composta por um vetor de ponteiros (correspondendo ao primeiro ndice da matriz), sendo que cada ponteiro aponta para o incio de uma linha da matriz. Em cada linha existe um vetor alocado dinamicamente, como descrito anteriormente (compondo o segundo ndice da matriz). #include <stdio.h> #include <stdlib.h> float **Alocar_matriz_real (int m, int n) { float **v; /* ponteiro para a matriz */ int i; /* variavel auxiliar */ if (m < 1 || n < 1) { /* verifica parametros recebidos */ printf ("** Erro: Parametro invalido **\n"); return (NULL); } /* aloca as linhas da matriz */ v = (float **) calloc (m+1, sizeof(float *)); if (v == NULL) {

68 printf ("** Erro: Memoria Insuficiente **"); return (NULL); } /* aloca as colunas da matriz */ for ( i = 0; i <= m; i++ ) { v[i] = (float*) calloc (n+1, sizeof(float)); if (v[i] == NULL) { printf ("** Erro: Memoria Insuficiente **"); return (NULL); } } return (v); /* retorna o ponteiro para a matriz */ } float **Liberar_matriz_real (int m, int n, float **v) { int i; /* variavel auxiliar */ if (v == NULL) return (NULL); if (m < 1 || n < 1) { /* verifica parametros recebidos */ printf ("** Erro: Parametro invalido **\n"); return (v); } for (i=0; i<=m; i++) free (v[i]); /* libera as linhas da matriz */ free (v); /* libera a matriz */ return (NULL); /* retorna um ponteiro nulo */ } void main (void) { float **mat; /* matriz a ser alocada */ int l, c; /* numero de linhas e colunas da matriz */ ... /* outros comandos, inclusive inicializacao para l e c */ mat = Alocar_matriz_real (l, c); ... /* outros comandos utilizando mat[][] normalmente */ mat = Liberar_matriz_real (l, c, mat); ... }

69

Estrutura, unio e enumerao


Estrutura
Uma estrutura agrupa vrias variveis numa s. Funciona como uma ficha pessoal, como registro de vrios dados. O registro a estrutura. Para se criar uma estrutura utiliza-se a palavra-chave struct. Sintaxe: struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variveis_estrutura; O nome_do_tipo_da_estrutura o nome para a estrutura. As variveis_estrutura so opcionais e seriam nomes de variveis que o usurio j estaria declarando e que fazem parte do tipo nome_do_tipo_da_estrutura. Exemplo, uma estrutura para endereo, e outra para dados pessoais: struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; Uma estrutura pode fazer parte de outra. Observe-se um exemplo onde so utilizadas as estruturas antes declaradas.

70 #include <stdio.h> #include <string.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; main (void) { struct ficha_pessoal ficha; strcpy (ficha.nome,"Luiz Osvaldo Silva"); ficha.telefone=4921234; strcpy (ficha.endereco.rua,"Rua das Flores"); ficha.endereco.numero=10; strcpy (ficha.endereco.bairro,"Cidade Velha"); strcpy (ficha.endereco.cidade,"Belo Horizonte"); strcpy (ficha.endereco.sigla_estado,"MG"); ficha.endereco.CEP=31340230; return 0; } No exemplo mostra-se como acessar um elemento de uma estrutura. Basta usar o operador . ponto.

Matrizes de estruturas
Um estrutura como qualquer outro tipo de dado no C. Podem ser declaradas, portanto, matrizes de estruturas da seguinte forma. struct ficha_pessoal fichas [100]; Isto criar um vetor de 100 estruturas do tipo ficha_pessoal. Para acessar a segunda letra da sigla de estado da dcima terceira ficha faz-se: fichas[12].endereco.sigla_estado[1];

71

Atribuindo estruturas
Pode-se atribuir duas estruturas que sejam do mesmo tipo. O C ir, neste caso, copiar uma estrutura na outra. Esta operao no apresenta problemas pois ao declarar struct ficha_pessoal ficha; ficha no um ponteiro, mas uma estrutura. void main() { struct ficha_pessoal primeira, segunda; Le_dados(&primeira); segunda = primeira; Imprime_dados(segunda); } So declaradas duas estruturas do tipo ficha_pessoal, uma chamada primeira e outra chamada segunda. Supondo que haja declarada uma funo Le_dados() que faa a leitura de uma estrutura, admitimos que aps a execuo da Segunda linha de main(), a estrutura primeira estar preenchida com dados vlidos. Os valores de primeira so copiados na segunda apenas com a expresso de atribuio. Todos os campos de primeira sero copiados na ficha chamada segunda. Tomar cuidado com a seguinte declarao: struct ficha_pessoal fichas [100]; pois neste caso fichas um ponteiro para a primeira ficha. Para a estrutura completa da nsima ficha usar fichas[n-1];.

Estruturas como argumentos de funes


Em um exemplo acima, utilizou-se o seguinte comando: strcpy (ficha.nome,"Luiz Osvaldo Silva"); Neste comando um elemento de uma estrutura passado para uma funo. Este tipo de operao pode ser feita sem maiores consideraes. Pode-se tambm passar para uma funo uma estrutura inteira, isto da seguinte maneira. void PreencheFicha (struct ficha_pessoal ficha) { ... } fcil passar a estrutura como um todo para a funo. Observar que, como em qualquer outra funo no C, a passagem da estrutura feita por valor. Isto significa que alteraes na estrutura dentro da funo no tero efeito na varivel fora da funo. Mais uma vez

72 podemos contornar este pormenor usando ponteiros e passando para a funo um ponteiro para a estrutura.

Ponteiros para estruturas


Os ponteiros tambm podem ser utilizados para estruturas. A declarao de um ponteiro para uma estrutura : struct nome_do_ponteiro_para_estrutura *estptr; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. H um detalhe especial a ser considerado. Se o ponteiro estptr est apontando para uma estrutura e tenta acessar um elemento ou dado da estrutura deve-se utilizar a sintaxe: (*p).nome_do_dado_da_estrutura; Este formato raramente usado. O que comum de se fazer acessar o elemento ou dado da estrutura atravs do operador seta (->). Assim, o anterior equivalente a: p->nome_do_dado_da_estrutura; A declarao acima muito mais fcil e concisa. Como exemplo, para acessar o dado CEP dentro de endereo, para um ponteiro a uma estrutura do tipo ficha_pessoal, faz-se: p->endereco.CEP

Unio
Uma declarao union determina uma nica localizao de memria onde podem estar armazenadas vrias variveis diferentes. A declarao de uma unio semelhante declarao de uma estrutura: union nome_do_tipo_da_union { tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; } variveis_union; Como exemplo, considerar a seguinte unio: union angulo { float graus; float radianos; };

73 Nela, existem duas variveis (graus e radianos) que, apesar de terem nomes diferentes, ocupam o mesmo local da memria. Isto quer dizer que s gastamos o espao equivalente a um nico float. Unies podem ser feitas tambm com variveis de diferentes tipos. Neste caso, a memria alocada corresponde ao tamanho da maior varivel no union. Veja o exemplo: #include <stdio.h> #define GRAUS 'G' #define RAD 'R' union angulo { int graus; float radianos; }; void main() { union angulo ang; char op; printf("\nNumeros em graus ou radianos? "); scanf("%c",&op); if (op == GRAUS) { ang.graus = 180; printf("\nAngulo: %d\n",ang.graus); } else if (op == RAD) { ang.radianos = 3.1415; printf("\nAngulo: %f\n",ang.radianos); } else printf("\nEntrada invalida!!\n"); } Tomar o maior cuidado possvel ao trabalhar com unies, pois poderia ser feito o seguinte: #include <stdio.h> union numero { char Ch; int I; float F; }; main (void) { union numero N; N.graus = 123; printf ("%f",N.F); return 0; } O programa acima muito perigoso pois voc est lendo uma regio da memria, que foi "gravada" como um inteiro, como se fosse um ponto flutuante.

74

Enumeraes
Numa enumerao diz-se ao compilador quais os valores(constantes) que uma determinada varivel pode assumir. Sintaxe: enum nome_do_tipo_da_enumerao {lista_de_valores} lista_de_variveis; Exemplo: enum dias_da_semana {segunda, terca, quarta, quinta, sexta, sabado, domingo}; O programador diz ao compilador que qualquer varivel do tipo dias_da_semana s pode ter os valores enumerados. Isto quer dizer que poderamos fazer o seguinte programa: #include <stdio.h> enum dias_da_semana {segunda, terca, quarta, quinta, sexta, sabado, domingo}; main (void) { enum dias_da_semana d1,d2; d1=segunda; d2=sexta; if (d1==d2) { printf ("O dia e o mesmo."); } else { printf ("So dias diferentes."); } return 0; } O funcionamento da enumerao simples, o compilador pega a lista de elementos declarada com a palavra enum, e associa a cada um destes elementos, um nmero inteiro. Ao primeiro elemento da lista associado o nmero zero, ao segundo o nmero 1 e assim por diante. Isto significa que os elementos declarados em uma enumerao so todos inteiros. Pode-se tambm modificar o valor inteiro destes elementos atribuindo valores inteiros especficos. Exemplo: enum dias_da_semana {segunda, terca, quarta, quinta, sexta, sabado=-1, domingo}; Neste caso, o valor inteiro de sbado 1. Tenha presente que o valor de um elemento consecutivo a um outro elemento dado, incrementado em uma unidade. Assim sendo, o domingo tem valor zero, ao igual que o elemento segunda. Nota: Vrios elementos de uma enumerao podem assumir o mesmo valor numrico inteiro.

75

Referncias
1. C, completo e total. Herbert Schildt. Traduo Marcos R. A. Morais, Makron, Mc Graw-Hill. So Paulo. 1990. 2. Introduccin al lenguaje C. Les Hancock, Morris Krieger. Traduccin Sebastin Dormido B. Mc Graw-Hill. Espaa. 1988. 3. C Reference Manual. Samuel p. Harbison, Guy L. Steele. Prentice-Hall Inc. Englewood, New Jersey. 1994. 4. A linguagem de programao padro ANSI. Kernighan, B. & Ritchie, D. C. Editora Campus, 1990. 5. Treinamento em linguagem C - Mdulos 1 e 2. Mizrahi, V. V. Editora McGraw-Hill, 1990.

76

Um pouco da historia nos Laboratrios Bell


O final da dcada dos 60 foi uma poca turbulenta para a pesquisa de sistemas de computadores nos Laboratrios Telefnicos Bell. Esta companhia estava saindo do projeto Multics, que iniciou em unio com as empresas MIT e General Eletric. Por volta de 1969, os administradores dos Laboratrios Bell e os pesquisadores, perceberam que o projeto Multics renderia frutos tarde demais e que isto custaria muito dinheiro. Depois que a mquina Multics GE-645 foi removida das premissas, um grupo informal liderado por Ken Thompson iniciou investigaes alternativas. Thompson pretendia criar um ambiente de computador confortvel de acordo com seus projetos, usando todos os mtodos que dispunha. Seus planos incorporavam muitas das inovaes do projeto Multics, como: um sistema de arquivos estruturado em rvore, um interpretador de comandos trabalhando como um programa em nvel de usurio, uma representao simples de arquivos texto, acesso generalizado a dispositivos, etc. Foi usada a linguagem PL/I, utilizada no desenvolvimento do projeto Multics, e ela no satisfazia as necessidades. Foram usadas tambm outras linguagens, como BCPL, mas todas elas perdiam as vantagens dos programas escritos em nvel de assembly. BCPL foi escrito por Martin Richards no meio dos anos 60, enquanto ele estava visitando o MIT, e foi usado durante o incio dos anos 70 em vrios projetos interessantes, como o sistema operacional OS6 em Oxford, e parte de um projeto da Xerox. O compilador original foi transportado para o Multics e para o sistema GE-635 GECOS por Rudd Canaday e outros dos Laboratrios Bell. No muito tempo depois do primeiro Unix rodar em um PDP-7, em 1969, Doug McIlroy criou a primeira linguagem de alto nvel do sistema: uma implementao da linguagem TMG (TMG era uma linguagem usada para escrever compiladores) de McClure. Desafiado pelo feito por McIlroy na reproduo do TMG, Thompson decidiu que o Unix precisaria de uma linguagem de programao. Depois de uma rpida tentativa em Fortran, ele criou sua prpria linguagem, que ele chamou de B. B pode ser imaginado como uma linguagem C, mas sem tipos; mais exatamente, era um BCPL reduzido em 8KB de memria, e filtrado pelo crebro de Thompson. O nome B, mais provavelmente, representa uma contrao do BCPL, embora haja uma teoria que diga que foi derivado da linguagem Bon, uma linguagem no registrada que Thompson criou durante os dias do Multics. Ao final da vida til do Multics nos Laboratrios Bell e depois, B foi a linguagem de escolha entre o grupo de pessoas que mais tarde envolveram-se com o Unix. O compilador B no PDP-7 no gerava instrues de mquina, mas sim "cdigo em linha", um esquema interpretativo no qual a sada do compilador consiste numa seqncia de endereos de fragmentos de cdigo que realizam operaes elementares. As operaes agem sobre a pilha da mquina. Certamente os aspectos menos agradveis do BCPL foram devidos aos prprios problemas tecnolgicos, que foram anulados no projeto da linguagem B. Por exemplo, o BCPL usa um mecanismo de "vetor global" para a comunicao entre programas compilados separadamente. Neste esquema, o programador associa explicitamente o nome de cada procedimento visvel externamente e os objetos de dados com um offset numrico no vetor global; a link-edio

77
completada no cdigo compilado usando estes offset's numricos. B acabou com este inconveniente inicialmente insistindo em que o programa todo seja apresentado de uma vez ao compilador. Ambas as linguagens tm somente um tipo de dado, o 'word' ou 'clula', de tamanho fixo. A memria, nestas linguagens, consiste num vetor linear de muitas clulas, e o significado do contedo de uma clula depende da operao aplicada. Depois que a verso TMG do B estava funcionando, Thompson reescreveu o B pelo prprio B. Durante o desenvolvimento, ele lutou continuamente contra as limitaes de memria: cada linguagem adicional inchava o compilador, mas cada reescrita levava a vantagem da reduo de tamanho. Thompson inventou os operadores ++ e --, que incrementam ou decrementam. Em 1971 foi adicionado linguagem B o tipo caracter e o seu compilador foi reescrito para gerar cdigos de mquina ao invs de cdigos de linha. Assim a transio de B para C foi na mesma poca da criao de um compilador capaz de produzir programas rpidos e pequenos o suficiente para competir com a linguagem assembly. A nova linguagem foi chamada de NB inicialmente. Esta transio do B para o C foi feita pelo Dennis Ritchie em 1972. Posteriormente, para a nomeao da nova linguagem, decidiu-se seguir o estilo da nomenclatura da linguagem anterior, com apenas uma letra, chamando-se de C, e ficando aberta uma questo: se o nome apenas um incremento no alfabeto ou nas letras BCPL. BCPL, B e C esto todos firmemente habilitados na tradicional famlia procedural tipificada por Fortran e Algol 60. Eles so particularmente orientados para sistemas de programao, so menores e compactamente descritos, e so submetidos a transformao por simples compiladores. O esquema de composio de tipos do C deve muito ao Algol 68, embora ele no tivesse surgido de tal forma que os adeptos do Algol pudessem aproveit-lo. A idia central que foi capturada do Algol foi uma estrutura baseada em tipos atmicos (incluindo estruturas), compostos de vetores, ponteiros e funes. Nem o BCPL, nem o B, e nem o C manipulam dados caracter robustamente. Cada uma destas linguagens trata uma string como um vetor de inteiros. Em BCPL, o primeiro pacote de byte contm o nmero de caracteres na string; em B, no h contador e as strings so terminadas por um caracter especial, assim como em C. Por volta de 1982 ficou claro que o C precisava de uma padronizao. A melhor aproximao a este padro, a primeira edio de "Kernighan & Ritchie", descrevia a linguagem em uso atual. Mas isto foi insuficientemente preciso no que dizia respeito a certos detalhes da linguagem, deixando pouco divulgadas certas extenses. Para resolver este problema, ANSI estabeleceu no vero de 1983 o comit X3J11, sob a direo de CBEMA, com a meta de produzir uma linguagem C padro. O X3J11 emitiu o seu relatrio no fim de 1989, e este padro foi aceito pelo ISO como ISO/IEC 9899/1990 e conhecido pelo nome de ANSI C.

Você também pode gostar