Escolar Documentos
Profissional Documentos
Cultura Documentos
Faculdade de Computao
Apostila de Linguagem C
(Conceitos Bsicos)
1.1. HISTRICO
A Linguagem C, criada em 1970 por Dennis Ritchie, uma evoluo da Linguagem B que, por sua
vez, foi uma adaptao feita, a partir da Linguagem BCPL, por Ken Thompson. Ela
estreitamente associada ao sistema operacional UNIX, j que as verses atuais do prprio
sistema foram desenvolvidas utilizando-se esta linguagem.
A filosofia bsica da linguagem C que os programadores devem estar cientes do que esto
fazendo, ou seja, supe-se que eles saibam o que esto mandando o computador fazer, e
explicitem completamente as suas instrues. Assim, ela alia a elegncia e a flexibilidade das
linguagens de alto nvel (ex: suporte ao conceito de tipo de dados) com o poderio das
linguagens de baixo nvel (ex: manipulao de bits, bytes e endereos).
O cdigo-fonte de um programa C pode ser escrito em qualquer editor de texto que seja capaz
de gerar arquivos em cdigo ASCII (sem formatao). Como o ambiente de programao
utilizado (Turbo C) para o sistema operacional DOS, estes arquivos devem ter um nome de no
mximo 8 caracteres e a extenso c (exemplo: NONAME.C).
Uso de chaves ({ }) para agrupar comandos pertencentes a uma estrutura lgica (ex: if-
else, do-while, for, etc.) ou a uma funo;
Diretivas
Declaraes de variveis/constantes globais;
Prototipao de funes;
tipo_dado main(lista_parametros) /* Funo Principal e Obrigatria */
{
Declaraes de variveis/constantes locais;
Comandos;
Estruturas de controle (seleo e/ou repetio);
Comentrios;
Chamada a outras funes;
}
tipo_dado func1(lista_parametros)
{
Bloco de comandos;
}
tipo_dado func2(lista_parametros)
{
Bloco de comandos;
}
tipo_dado funcN(lista_parametros)
{
Bloco de comandos;
}
1.5. COMENTRIOS
Em C, comentrios podem ser escritos em qualquer lugar do texto para facilitar a interpretao do
algoritmo. Para que o comentrio seja identificado como tal, ele deve ter um /* antes e um */
depois.
Exemplo:
2. DIRETIVAS
Durante a confeco do cdigo-fonte, alm dos comandos normais, o programador poder utilizar-
se de diretivas de compilao. As diretivas so prontamente reconhecidas, vista que aparecem
quase sempre no incio dos arquivos-fonte, com o prefixo # seguido pelo nome da diretiva e
seus argumentos. Ao contrrio dos comandos normais, no se usa ponto e vrgula ao final da
declarao da diretiva. Elas direcionam o compilador entre vrios caminhos durante a fase de
pr-processamento.
Pr-Processamento
Cdigo-
Fonte (.C)
da Compilao
Cdigo-Fonte +
Arqs. de Incluso
Compilao
Cdigo-Objeto (.OBJ) Pr-Processamento
da Linkedio
Programa-Executvel (.EXE)
Os arquivos de incluso podem ser agrupados, isto , um arquivo de incluso pode conter outros
arquivos de incluso adicionais, e assim por diante, at o limite de pelo menos 8 nveis, conforme
determinao do padro ANSI (no Turbo C pode haver at 16 nveis de incluses aninhadas).
Na tabela abaixo so apresentados alguns arquivos de incluso que podem ser utilizados na
confeco dos programas.
ARQUIVO FINALIDADE
assert.h Contm a macro assert que usada para incluir diagnstico em programas
ctype.h Declara funes para testar caracteres
errno.h Define o nmero de erro do sistema
float.h Define os valores em ponto flutuante especficos do compilador
limits.h Define tipos de valores-limites especficos do compilador
locale.h Utilizada para adaptar as diferentes convenes culturais
math.h Define as funes matemticas e macros usadas para operaes matemticas em
linguagem C
setjmp.h Prov uma forma de evitar a seqncia normal de chamada e retorno de funo
profundamente aninhada
signal.h Prov facilidades para manipular condies excepcionais que surgem durante a
execuo, como um sinal de interrupo de uma fonte externa ou um uso na
execuo
stdarg.h Define macros para acessar argumentos quando uma funo usa um nmero
varivel de argumentos
stddef.h Define funes, constantes, macros e estruturas usadas para operaes padres
de entrada e sada
stdio.h Contm funes, tipos e macros de entrada e sada
stdlib.h Contm funes para converso de nmeros, alocao de memria e tarefas
similares
string.h Define funes para manipulao de strings
time.h Define funes e estruturas usadas na manipulao de data e hora.
Outra diretiva que tambm muito utilizada a DEFINE. Ela o mecanismo bsico do C para criar
macros e identificadores. A diretiva DEFINE precedida do smbolo # e seguida de dois
argumentos, de acordo com a finalidade desejada.
Quando a string muito longa para ocupar uma nica linha, pode-se empregar o caracter especial
de escape (barra invertida - \) antes do <ENTER> e continuar digitando o texto restante na
prxima linha, como a seguir:
Na criao de macros, a diretiva DEFINE funciona de modo similar a uma funo, exceto pelo fato
de que, como macro, ela permite que argumentos sejam suprimidos, ao contrrio das funes. O
primeiro argumento da diretiva indica o nome da macro e os argumentos que sero utilizados
por ela. J o segundo argumento define como os argumentos da macro sero aplicados
quando o pr-processador encontrar o nome da macro no cdigo-fonte restante. Abaixo
apresentado um exemplo de macro:
Neste exemplo, o pr-processador substituir o texto CUBO(X) pelo valor correspondente a X3,
como nos exemplos abaixo:
CUBO(2) ((2)*(2)*(2)) = 8
CUBO(A+B) ((A+B)*(A+B)*(A+B))
Neste ltimo caso, possvel notar a importncia dos parnteses em torno X na definio da ao
na declarao do DEFINE. Sem eles, a substituio de CUBO(A+B) seria equivocada, visto que o
resultado seria (A+B*A+B*A+B) e a multiplicao tem precedncia sobre a soma.
O ANSI-C fornece dois operadores do pr-processador para serem utilizados em macros definidas
pelo DEFINE. So eles:
printf(mkstr(Processamento de Dados));
printf(concat(Processamento, de Dados);
printf(Processamento de Dados);
Apesar de existirem outras diretivas de compilao, inclusive algumas que permitem compilao
condicional, essas fogem ao escopo do curso e, portanto, no sero discutidas neste material.
3. PROTTIPOS DE FUNES
Prottipos permitem que o C fornea uma verificao mais forte de tipos, ou seja, atravs dos
prottipos, o C pode encontrar e apresentar quaisquer converses ilegais de tipo entre o
argumento usado para chamar a funo (argumento atual) e aquele utilizado na definio de seus
parmetros (argumento formal). Alm disso, o uso de prottipo tambm permite encontrar
diferenas entre o nmero destes argumentos. Portanto, a sua utilizao ajuda na deteco de
erros antes que eles ocorram.
Exemplo:
# include <stdio.h>
unsigned int NUM;
int fat (unsigned int NRO); /* Prottipo da Funo fat() */
void main (void)
{
scanf(%u, &NUM);
printf(\nO fatorial de %u %d, NUM, fat(NUM));
}
Int fat(unsigned int NRO)
{
if NRO < 2
return (1);
else
return (NRO * fat(NRO-1));
}
O uso dos nomes dos parmetros opcional. Porm, eles habilitam o compilador a identificar
qualquer incompatibilidade de tipo atravs do nome quando ocorre um erro.
Quando uma funo no possui argumentos, seu prottipo deve usar void dentro dos
parnteses (ex: int func (void);). Isso infroma ao compilador que a funo no tem
argumentos e qualquer chamada funo com parmetros um erro.
Quando uma funo sem prottipo chamada, todos os char so convertidos em int e
todos os float so convertidos em double. Tais converses, que esto relacionadas com
as caractersticas do ambiente original do C, acarretam uma alocao maior de
memria. No entanto, se a funo tem prottipo, os tipos especificados na declarao
do prottipo so mantidos.
Embora no seja um erro criar programas sem prottipo de funes, o padro ANSI recomenda
fortemente o seu uso.
4. TIPOLOGIA DE DADOS
Cada tipo de dado tem uma exigncia pr-determinada do tamanho de memria e uma faixa
associada de valores permitidos. Por exemplo, um caracter ocupa geralmente 1 byte, enquanto
que um inteiro tem normalmente 2 bytes. Entretanto, para garantir a portabilidade do cdigo C,
esta suposio no pode ser tomada como verdadeira. Isto porque, o tamanho e a faixa desses
tipos de dados variam de acordo com o tipo de processador e com a implementao do
compilador C. O padro ANSI estipula apenas a faixa mnima de cada tipo de dado e no o seu
tamanho em bytes.
H 5 tipos bsicos em C, conforme tabela abaixo. Todos os demais tipos so variaes destes 5
tipos.
Elementos do tipo char so normalmente utilizados para conter valores definidos pelo conjunto de
caracteres ASCII. Elementos do tipo int geralmente correspondem ao tamanho natural de uma
palavra do computador hospedeiro. Assim, numa mquina de 16 bits, int provavelmente ter 16
bits. Numa mquina de 32, int dever ter 32 bits. O formato exato dos elementos em ponto
flutuante depende de como eles so implementados. A faixa dos tipos float e double dada em
dgitos de preciso. J suas grandezas dependem do mtodo usado para representar os nmeros
em ponto flutuante. Qualquer que seja o mtodo, o nmero muito grande (o padro ANSI
especifica a faixa mnima de 1x10-37 a 1x10+37). O tipo void declara explicitamente uma funo
que no retorna valor algum ou cria ponteiros genricos.
Com exceo dos tipos void e float, os demais tipos bsicos podem ser adaptados mais
precisamente s necessidades do problema estudado. Para isto, necessrio preceder o tipo
bsico do dado com um dos modificadores de tipo (signed, unsigned, long e short), visando
alterar o significado do tipo.
Normalmente, ao tipo double pode-se aplicar apenas o modificador long. Esta modificao gera
um tipo de ponto flutuante com maior preciso. Os quatro modificadores podem ser aplicados a
inteiros. Os modificadores short e long visam prover tamanhos diferentes de inteiros (inteiros
menores short int ou maiores long int). Na verdade, cada compilador livre para escolher
tamanhos adequados para o seu prprio hardware, com a nica restrio de que short int e int
devem ocupar pelo menos 16 bits e long int pelo menos 32 bits. Alm disso, short int no pode
ser maior que int, que, por sua vez, no pode ser maior que long int. O modificador unsigned
serve para especificar variveis sem sinal. Um unsigned int ser um inteiro que assumir apenas
valores positivos. J o uso de signed para o tipo int, apesar de permitido, redundante. A seguir
esto listados os tipos de dados permitidos e seu valores mximos e mnimos em um compilador
tpico para um hardware de 16 bits. Tambm nesta tabela est especificado o formato que deve
ser utilizado para ler os tipos de dados com a funo scanf():
Formato Intervalo
Tipo Num de bits para leitura
com scanf Inicio Fim
Char 8 %c -128 127
unsigned char 8 %c 0 255
Signed char 8 %c -128 127
Int 16 %i ou %d -32.768 32.767
unsigned int 16 %u 0 65.535
signed int 16 %i ou %d -32.768 32.767
Short int 16 %hi ou %hd -32.768 32.767
unsigned short int 16 %hu 0 65.535
signed short int 16 %hi ou %hd -32.768 32.767
Long int 32 %li ou %ld -2.147.483.648 2.147.483.647
signed long int 32 %li ou %ld -2.147.483.648 2.147.483.647
unsigned long int 32 %lu 0 4.294.967.295
Float* 32 %f, %e ou %g 3,4E-38 3.4E+38
Double* 64 %lf, %le ou %lg 1,7E-308 1,7E+308
long double* 80 %Lf 3,4E-4932 3,4E+4932
* importante observar que os intervalos de ponto flutuante, na tabela acima, esto indicados
em faixa de expoente, mas os nmeros podem assumir valores tanto positivos quanto negativos.
Por fim, dependendo da implementao de compilador utilizada, poder existir outros tipos de
dados (expandidos ou adicionais), como por exemplo, o uso do unsigned para os tipos de ponto
flutuante. Porm, o uso de tipos de dado no definidos pelo padro ANSI C reduz a
portabilidade de seu cdigo e, portanto, no recomendado.
char int
1 bit de sinal + 7 bits 1 bit de sinal + 15 bits de
de dados dados
float
1 bit de sinal + 8 bits de expoente + 23 bits
de mantissa (magnetude)
double
O padro ANSI introduziu dois modificadores de tipo que controlam a maneira como as
variveis podem ser acessadas ou modificadas. Esses modificadores so const e volatile.
Estes modificadores devem preceder os modificadores de tipo de dado e/ou tipo de dados e o
nome das variveis que eles modificam.
Variveis do tipo const no podem ser modificadas pelo programa, podendo, entretanto,
receber um valor inicial. Por exemplo:
O modificador volatile usado para informar ao compilador que o valor de uma varivel pode
ser alterado de maneira no explicitamente especificada pelo programa. Por exemplo, um
endereo de uma varivel global pode ser passado pra a rotina de relgio do sistema operacional
e usado para guardar o tempo real do sistema. Nessa situao, o contedo da varivel alterado
sem nenhum comando de atribuio explcito no programa.
possvel usar const e volatile juntos. Por exemplo, se 0x30 assumido como sendo o valor de
uma porta que mudado apenas por condies externas, a declarao seguinte ideal para
prevenir qualquer possibilidade de efeitos colaterais acidentais.
Vale ressaltar que, qualquer valor que comece com 0x corresponde a um constante
hexadecimal, bem como valores iniciados com 0 equivalem a constantes octais. Assim, para o
compilador C, o valor 015 diferente de 15.
4.4. IDENTIFICADORES NO C
O padro ANSI define nomes de variveis, funes, rtulos e vrios outros objetos definidos pelo
usurio como identificadores. Esses identificadores podem variar de um a vrios caracteres,
sendo que o primeiro deve ser uma letra ou um caracter especial _ e os demais caracteres
devem ser letras, nmeros ou o caracter especial _.
Vale ressaltar que, assim como na pseudolinguagem, o identificador NO PODE TER espao
nem nenhum outro caracter especial, inclusive letras acentuadas.
Como j visto, uma varivel uma posio de memria de um determinado tipo de dado e que
possui um nome, que usada para guardar valores que podem sofrer mudanas durante a
execuo do programa.
Todas as variveis do C devem ser declaradas antes de serem usadas. A forma geral de uma
declarao :
tipo lista_de_variveis;
Sendo:
Exemplos:
As variveis podem ser declaradas em trs lugares bsicos: dentro de funes, na definio dos
parmetros das funes e fora de todas as funes. Estas so, respectivamente, variveis locais,
parmetros formais e variveis globais.
VARIVEIS LOCAIS
As variveis locais, tambm conhecidas com variveis automticas (pois pode utilizar o
especificador auto para declar-las), so aquelas declaradas dentro das funes e, portanto,
s so reconhecidas pela funo onde foi declarada (lembre-se que o corpo de uma funo
delimitado por chaves).
Variveis locais existem apenas enquanto o bloco de comando em que foram declaradas
est sendo executado. Isto , uma varivel local criada na entrada de seu bloco e destruda na
sada.
No exemplo acima, a varivel NRO declarada duas vezes (uma vez em cada funo) dentro do
programa. Observe que o NRO da func1 no tem nenhuma relao com o NRO da func2, j que
cada uma destas variveis s so reconhecidas nos seus respectivos blocos de comando.
A maioria dos programadores declara todas as variveis usadas por uma funo imediatamente
aps o abre chaves da funo. Porm, as variveis locais podem ser declaradas dentro de
qualquer bloco de comando, sempre aps o abre chaves e antes de qualquer outro
comando.
Exemplo: Contra-Exemplo:
void func() void func()
{ {
int NRO; int NRO;
scanf (%d, &NRO); NRO = 0;
if (NRO = = 0) char NOME[80];
{ gets(NOME);
char NOME[80]; }
printf (Entre com o nome:); /* Funo com declarao incorreta,
gets(NOME); pois as variveis devem ser
declaradas no incio do bloco de
} comandos, antes de qualquer outro
} comando */
Enquanto o trecho programa apresentado no exemplo acima funcionaria sem problemas, o trecho
de programa descrito no contra-exemplo no seria executado na maioria dos compiladores em C.
Uma das vantagens no uso de variveis locais evitar efeitos colaterais indesejados, decorrentes
de alteraes acidentais no valor das variveis fora da funo. Outra vantagem a reduo no
espao de memria alocado pelo compilador para execuo do programa.
PARMETROS FORMAIS
Se uma funo usa argumentos de entrada, ela deve declarar variveis que recebero os
valores desses argumentos. Essas variveis so denominadas parmetros formais da funo
e se comportam como qualquer outra varivel local da funo.
Como mostrado no exemplo abaixo, as declaraes dos parmetros formais de uma funo
ocorrem depois de seu nome e dentro de parnteses:
Exemplo:
int is_in(char *texto, char letra)
{
While (*texto)
{
if (*texto = = letra)
return 1;
else
texto++;
}
return 0;
}
Os parmetros formais declarados devem ser da mesma quantidade e tipo dos parmetros
utilizados na chamada da funo. Se houver alguma discordncia de tipo, resultados
inesperados podero ocorrer, uma vez que a linguagem C geralmente realiza alguma operao,
mesmo em situaes no usuais.
O padro ANSI fornece os prottipos de funes, que pode ser usado para ajudar a verificar se
os argumentos usados para chamar a funo so compatveis com os parmetros. Porm, para
receber esse benefcio extra, os prottipos de funes devem ser includos explicitamente no
programa. Mais detalhes sobre prottipos de funes podem ser vistos no prximo captulo.
VARIVEIS GLOBAIS
EXTERN
Como j visto, uma varivel global s pode ser declarada uma nica vez dentro de um arquivo.
Assim, se declararmos em duplicidade uma varivel dentro do programa, o compilador C pode
apresentar uma mensagem de erro nome de varivel duplicado ou, simplesmente, escolher uma
das variveis. O mesmo problema ocorre quando declaramos variveis globais em cada
arquivo de um projeto. Embora o compilador no emita nenhuma mensagem, uma vez que a
compilao de cada arquivo feita separadamente, estaramos tentando criar duas ou mais
cpias de uma mesma varivel, e durante o processo de linkedio (que junta todos os arquivos
do projeto) seria mostrada a mensagem de erro rtulo duplicado.
O modificador extern utilizado para resolver este problema de duplicao. Isto , declara-se as
variveis globais em um nico arquivo, e nos demais repete-se a declarao da varivel,
precedida do modificador extern, como no exemplo:
STATIC
este tipo de varivel til quando empregada em funes generalizadas e/ou de biblioteca, que
podem ser utilizadas por outros programadores. O especificador static tem efeitos diferentes em
variveis locais e globais.
No exemplo abaixo, a varivel NRO_SERIE mantm seu valor entre as chamadas da funo
obtem_nro_serie. Isso significa que na primeira chamada ser produzido o nmero de srie 1010
e, a cada chamada da funo, um novo nmero ser criado com base no seu antecessor (ex:
1020, 1030, etc.).
Exemplo:
int obtem_nro_serie(void)
{
static int NRO_SERIE = 1000;
NRO_SERIE = NRO_SERIE + 10;
return (NRO_SERIE);
}
Aplicar o especificador static a uma varivel global, informa ao compilador para criar uma
varivel que reconhecida apenas no arquivo na qual a mesma foi declarada. Isto , embora
a varivel seja global, rotinas em outros arquivos no podem reconhec-la ou alterar seu contedo
diretamente.
AUTO
O especificador auto instrui ao compilador para classificar uma varivel como automtica. O
uso deste especificador limitado, pois no se pode us-lo com variveis globais e as variveis
locais so automticas por default (padro).
REGISTER
O especificador register determina que o acesso varivel seja o mais rpido possvel.
Originalmente, quando este especificador era aplicado apenas variveis do tipo int e char, isto
significada colocar o contedo destas variveis nos registradores da CPU (da vem o nome do
especificador). Entretanto, como o padro ANSI ampliou o uso deste especificador aos demais
tipos de dados, tal tratamento foi modificado. No novo conceito do especificador, mantm-se o
armazenamento original para os tipos int e char e, para os demais tipos (ex: matrizes e vetores),
adota-se um tratamento diferenciado, de acordo com a implementao do compilador, visando
tornar rpido o acesso ao seu contedo. Porm, nestes casos no ocorre um aumento substancial
na velocidade de acesso.
Tal especificador s pode ser aplicado a variveis locais e parmetros formais, NO sendo
permitido o seu uso com variveis globais.
Alm disso, como variveis register podem ser armazenadas em um registrador da CPU, as
mesmas no podem ter endereos. Portanto, este especificador impede o uso do operador &.
Na linguagem C, podemos inicializar uma varivel junto com sua declarao. Para isto, basta
colocar um sinal de igual e uma constante aps o nome da varivel, como no exemplo abaixo:
Note que no exemplo acima a varivel NRO ser inicializada com 1000, a varivel LETRA
receber o caracter a e o vetor NOME receber a string Jos. Vale ainda ressaltar que, neste
ltimo caso, cada letra da string ser associada a uma posio do vetor NOME, como ilustrado:
Observe que, apesar da string de inicializao possuir apenas 4 caracteres, inserido um caracter
especial \0 (terminador nulo) na quinta posio do vetor (NOME[4]). Este caracter deve ser
utilizado nos testes condicionais das estruturas de controle (seleo ou repetio) para identificar
o final da string, como no exemplo:
void main(void)
{
char LETRA, FRASE[100];
int I = 0, CONT = 0;
printf(Digite uma frase:);
scanf(%s\n\n,&FRASE);
printf(Digite a letra que ser contada:);
scanf(%c\n\n,&LETRA);
while ((FRASE[I] != \0) && (I <= 100))
{
If (FRASE[I] = LETRA)
{
CONT++; /* Equivalente ao comando CONT = CONT+1 */
}
++I;
}
Printf(A letra %c apareceu %d vezes dentro da frase.\n, LETRA, CONT);
}
4.8. CONSTANTES
Constantes referem-se a valores fixos que o programa no pode alterar. No C, constantes podem
ser de qualquer um dos cinco tipos de dados bsicos e suas variaes.
Por default, o compilador C encaixa uma constante numrica no menor tipo de dado
compatvel que pode cont-la (ex: 10 int, 60000 unsigned int, 100000 log int). A nica
exceo esta regra so as constantes reais, as quais so assumidas como double. Apesar
de esta associao padro ser apropriada maioria dos programas, o programador pode forar a
associao de uma constante a outro tipo de dado, atravs da utilizao de um sufixo. Como por
exemplo, -15L (fora a utilizao do tipo long int) e 3.141F (fora a utilizao do tipo float).
Muitas vezes, precisamos inserir constantes hexadecimais (base dezesseis) ou octais (base oito)
no nosso programa. O C permite que se faa isto. As constantes hexadecimais comeam com
0x. As constantes octais comeam em 0.
Alguns exemplos:
5. COMANDO DE ATRIBUIO
O comando de atribuio do C o sinal de igual (=). Ele utilizado para atribuir varivel da
esquerda o valor da varivel, constante ou expresso contida direita do sinal.
Sintaxe Geral:
Varivel = expresso;
5.1. OPERADORES
OPERADORES ARITMTICOS
OPERADORES ARITMTICOS DO C
Operador Finalidade
+ Soma (inteira e ponto flutuante)
- Subtrao ou Troca de Sinal (inteira e ponto flutuante)
* Multiplicao (inteira e ponto flutuante)
/ Diviso (inteira e ponto flutuante)
% Resto da Diviso (somente de inteiros)
Uma particularidade do C refere-se ao fato do valor retornado por uma expresso aritmtica
ser sempre do maior tipo de dado utilizado na expresso. Assim, uma diviso de nmeros
inteiros retornar a parte inteira do resultado (diviso inteira), enquanto a diviso de nmeros do
tipo float retornar um valor do mesmo tipo (diviso de nmeros reais).
Todos os operadores descritos so binrios (aplicveis sobre duas variveis), exceto pelo
operador - quando aplicado para trocar o sinal de uma varivel. Neste caso o operador simula a
multiplicao da varivel por -1.
int A = 1, B = 2, C;
C = (2 * A + B) / B; /* C receber o valor 2 */
C = (A + B) % B; /* C receber o valor 1 */
Como pode ser notado, estes operadores podem ser pr-fixados ou ps-fixados. A diferena
que quando so pr-fixados, eles incrementam a varivel antes de usar seu valor. Quando so
ps-fixados, eles utilizam o valor da varivel antes de increment-la. Para entender melhor esta
diferena, veja os exemplos abaixo:
EXPRESSES REDUZIDAS
As expresses reduzidas so aplicadas nos casos que uma mesma varivel se encontra em
ambos os lados do comando de atribuio. A forma geral destas expresses :
Note que este tipo de expresso muito til para a manipulao de variveis contadoras e/ou
acumuladoras (que envolve somatrias, totais, etc.).
OPERADORES RELACIONAIS
Operador Finalidade
== Igual
> Maior
< Menor
>= Maior e igual
<= Menor e igual
!= Diferente
Alm dos operadores relacionais, a linguagem C ainda dispe de operadores lgicos. Estes
operadores podem ser utilizados para agrupar duas ou mais operaes relacionais em um nico
teste condicional (operadores binrios AND e OR) ou, ainda, modificar o resultado de uma
operao relacional (operador unrio NOT).
OPERADORES LGICOS
Operador Finalidade
&& AND (E)
|| OR (OU)
! NOT (NO)
Abaixo apresentado um programa exemplo que imprime os nmeros pares entre 1 e 100.
Observe neste exemplo que, quando o nmero par, seu mdulo igual a 0. Portanto, utilizando
o operador NOT, podemos converter este resultado para 1 (verdadeiro), disparando a clusula
ento da estrutura de seleo IF (a qual ser vista mais adiante).
# include <stdio.h>
void main(void)
{
int NRO;
for (NRO = 1; NRO <= 100; NRO++)
{
If ( !(NRO%2)) /* Verifica se o N par ou mpar */
O operador ternrio ? permite a atribuio condicional de valores a uma varivel. Ele fornece
uma forma alternativa de escrever uma tomada de deciso, atravs da seguinte sintaxe:
Nesta estrutura, a condio testada e, caso seja verdadeira, a expresso1 assumida; seno
adotada a expresso2. Para exemplificar sua utilizado, segue abaixo um trecho de um programa
que atribui varivel Z o maior valor entre as variveis A e B:
if (A > B)
Z = A;
else
Z = B;
Quando uma expresso combina variveis de tipos de dado diferentes, o compilador C verifica se
as converses entre estes tipos so possveis. Caso no sejam, o processo de compilao
interrompido e uma mensagem de erro exibida. Caso contrrio, o compilador realiza todas as
converses necessrias, seguindo as seguintes regras:
1. Todos os char e short int so convertidos para int. Todos os float so convertidos para
double.
Alm disso, o programador pode forar que o resultado de uma expresso seja de um
determinado tipo. Para isto, ele deve utilizar operadores cast (modeladores). Sua forma geral :
# include <stdio.h>
void main(void)
{
int NRO;
float RESULTADO;
scanf(%d, &NRO);
RESULTADO = (float) NRO/2; /* Fora a converso de NRO em float */
printf(\nA metade de %d %f, NRO, RESULTADO);
}
Note que, se no tivssemos utilizado o operador cast no exemplo acima, primeiro seria feito a
diviso inteira de NRO por 2 e, depois, o resultado seria convertido para float.
6. FUNES DISPONVEIS NO C
FUNES MATEMTICAS DO C
exemplo: 3
X =X 3
varivel_char = tolower(caracter);
varivel_char = toupper(caracter);
#include <stdio.h>
#include <ctype.h>
void main(void)
{
char LETRA;
LETRA = getchar();
putchar( tolower(LETRA));
putchar( toupper(LETRA));
}
A funo clrscr() utilizada para limpar todo o contedo da tela e posicionar o cursor no seu
incio, ou seja, canto superior esquerdo da tela (linha 1 , coluna 1). Sua forma geral :
clrscr();
Para utilizar esta funo necessrio declarar o arquivo de cabealho conio.h, conforme
apresentado no exemplo abaixo:
#include <stdio.h>
#include <conio.h>
void main(void)
{
A funo gotoxy() utilizada para posicionar o cursor na posio (coordenadas de linha e coluna)
desejada. Sua forma geral :
gotoxy(coluna, linha);
Para utilizar esta funo necessrio declarar o arquivo cabealho conio.h, conforme
apresentado no exemplo abaixo:
#include <stdio.h>
#include <conio.h>
void main(void)
{
int LINHA, COLUNA;
char TEXTO[10];
printf(Digite a linha e coluna onde deseja escrever o texto:);
scanf(%d,%d, &LINHA, &COLUNA);
printf(\nDigite o texto a ser mostrado:);
gets(TEXTO);
clrscr();
gotoxy(COLUNA, LINHA);
puts(TEXTO);
}
7.1. PRINTF()
A string de controle, delimitada por aspas, descreve tudo que a funo ir colocar na tela. Esta
string no mostra apenas os caracteres que devem sero apresentados, mas tambm os
respectivos formatos e posies das constantes, variveis e expresses listadas na lista de
argumentos. Assim, resumidamente, podemos dizer que a string de controle consiste de trs
tipos de caracteres:
Caracteres diversos que sero impressos na tela (correspondem ao texto que ser
exibido);
Tipo do
Especificador Descrio
Argumento
%d int Valor inteiro decimal
%i int Valor inteiro decimal
%o int Valor inteiro octal
%x int Valor inteiro hexadecimal
%X int Valor inteiro hexadecimal
%u int Valor inteiro decimal sem sinal (unsigned int)
%c int Um caracter em formato ASCII (cdigo binrio correspondente)
%s char Uma cadeia de caracteres (string) terminada em \0
Valor em ponto flutuante no formato [-]m.dddddd, onde o N de
%f float
ds dado pela preciso (padro 6)
Valor em ponto flutuante em notao exponencial no formato
%e float ou double
[-]m.ddddd exx, onde o N de ds dado pela preciso
Valor em ponto flutuante em notao exponencial no formato
%E float ou double
[-]m.ddddd Exx, onde o N de ds dado pela preciso
Valor em ponto flutuante no formato %e (quando o expoente
for menor que -4 ou igual a preciso) ou %f (nos demais
%g float ou double
casos). Zeros adicionais e um ponto decimal final no so
impressos
Valor em ponto flutuante no formato %E (quando o expoente
for menor que -4 ou igual a preciso) ou %f (nos demais
%G float ou double
casos). Zeros adicionais e um ponto decimal final no so
impressos
%% - Exibe o caracter %
Entre o caracter % e o caracter especificador do tipo de dado pode vir as seguintes alternativas:
Exemplos: 7
4 2
%4d 2 2 %7.2f 2 . 2 2
%-4d 2 2 %-7.2f 2 . 2 2
#include <stdio.h>
void main(void)
3456.780
7.2. SCANF()
Especificador Descrio
%d Indica um valor inteiro decimal
Indica um valor inteiro (podendo estar na base
%i decimal, octal com inicial 0 (zero) ou hexadecimal
com inicial 0X
%u Indica um valor inteiro decimal sem sinal
%c Indica um caracter (char)
%s Indica uma string
Indica um valor em ponto flutuante de preciso
%f, %e, %g
simples (float)
Indica um valor em ponto flutuante de preciso dupla
%lf, %le, %lg
(double)
%o Indica um valor inteiro octal (com ou sem zero inicial)
Indica um valor inteiro hexadecimal (com ou sem
%x ou %X
0x inicial)
No exemplo abaixo, a funo scanf() ler o nmero digitado como float, armazenando-o no
endereo da varivel NRO. Os nmeros digitados sero somados at que seja digitado CTRL-Z.
Como sada sero apresentados a soma parcial (subtotal) a cada dez nmeros lidos e o total geral
ao final do programa.
# include <stdio.h>
void main(void)
{
float SOMA, NRO;
int CONT;
CONT = SOMA = 0;
printf (\n Entre com os nmeros a serem somados ou digite CTRL-Z para terminar |n);
while (scanf (%f, &NRO) == 1)
{
CONT++;
if (CONT == 10)
{
printf(\t Subtotal: %.2f \n, SOMA += NRO);
CONT = 0;
}
else
SOMA += NRO;
}
printf (\t Total: %.2f \n, SOMA);
}
7.3. GETCHAR()
A macro getchar() retorna o prximo caracter de entrada toda vez que chamada, ou EOF (End
Of File) quando for fim de arquivo. Esta macro interpreta o caracter de entrada pelo seu valor
ASCII, ou seja, seu retorno um nmero inteiro, correspondente ao cdigo binrio do caracter
lido. Ela usa uma tcnica chamada de entrada bufferizada, ou seja, os caracteres digitados
pelo usurio no esto disponveis para a macro getchar() at que seja pressionada a tecla
<ENTER>.
varivel = getchar();
No programa exemplo abaixo, ser impresso o cdigo ASCII correspondente ao caracter digitado.
#include <stdio.h>
#include <conio.h>
void main(void)
{
int NUM;
clrscr();
printf (\nEntre com um caracter ou tecle espao para terminar: );
while ((NUM = getchar()) != )
{
if (NUM != 10)
{
printf(\n\tCaracter\tCdigo ASCII\n\n\t %c \t %d\n, NUM, NUM);
printf(\nEntre com um caracter ou tecle espao para terminar: );
}
}
getchar();
}
Neste exemplo, a linha getchar(); utilizada para manter a exibio da tela do DOS at que um
caracter qualquer seja pressionado.
Obs: o arquivo de cabealho stdio.h contm a declarao do identificador EOF (#define EOF -1).
7.5. GETS()
A funo gets() l uma string e coloca-a no endereo apontado pelo seu argumento ponteiro para
caractere. Para efeito prtico, far parte da string tudo o que for digitado at o retorno do carro
(tecla <ENTER>), sendo este substitudo pelo terminador nulo \0. Sua sintaxe geral :
gets(nome_variavel);
Se for ultrapassado o espao reservado para a string, esta funo sobrepe os valores na
memria, podendo ocasionar um erro grave.
# include <stdio.h>
void main(void)
{
char MSG[21];
printf(\n\nDigite uma mensagem com no mximo 20 caracteres: );
gets(MSG);
printf(\n\nA mensagem digitada : %s, msg);
getchar();
}
7.6. PUTS()
A funo puts() apresenta na tela a string passada como argumento, seguida de uma nova linha
(\n). Ela retorna um valor no negativo se houver sucesso no processo ou, caso contrrio, retorna
EOF. Sua forma geral :
puts(string);
#include <stdio.h>
void main(void)
{
char STRING[10];
puts(Exemplo de utilizao da funo puts.\n Entre com uma string:);
gets(STRING);
puts(\n A string digitada foi:);
puts(STRING);
}
7.7. PUTCHAR()
A macro putchar() coloca um nico caracter na sada padro. Sua sintaxe geral :
putchar(varivel);
A varivel passada como argumento para a macro pode ser do tipo int (contendo o cdigo binrio
do caracter desejado) ou char.
Alm disso, assim como a funo puts(), ela retorna EOF em caso de erro.
No exemplo a seguir, a macro utilizada para apresentar na sada padro (que por default a
tela) os caracteres digitados pelo usurio em letras minsculas.
#include <stdio.h>
#include <ctype.h>
void main(void)
{
char C;
puts(Entre com os caracteres desejados ou tecle CTRL-Z para terminar:\n);
while ((C = getchar()) != EOF)
putchar (tolower(C));
}
Para nos facilitar a tarefa de programar, a linguagem C disponibiliza vrios cdigos chamados
cdigos de barra invertida - backslash ou caracteres de escape. Uma lista com os principais
cdigos dada a seguir:
Cdigo Significado
\a Sinal sonoro ("beep")
\b Retrocesso (backspace)
\n Mudana de linha (new line)
\f Avano de pgina (form feed)
\r Retorno do carro da impressora
\t Tabulao horizontal (tab)
\v Tabulao vertical
\0 Nulo (0 em decimal)
\\ Impresso da barra invertida
\ Impresso das aspas
\ Impresso do apstrofo
\? Impresso do ponto de interrogao
\N Constante octal (N o valor ASCII em octal)
\xN Constante hexadecimal (N o valor ASCII em hexa)
Estes caracteres de controle podem ser usados como qualquer outro dentro das strings de
controle das funes printf() e scanf().
Este tipo de estrutura permite direcionar o fluxo lgico do programa para blocos distintos de
instrues, conforme uma condio de controle (normalmente avaliao de expresses lgicas
e/ou relacionais).
8.1. COMANDO IF
If (condio)
{
bloco de comandos;
}
O bloco de comandos deve ser delimitado por uma chave aberta e uma fechada. No entanto, se o
bloco composto por apenas um nico comando, as chaves se tornam opcionais e podem
ser omitidas.
#include <stdio.h>
int main (void)
{
int a,b;
printf(Digite dois nmeros inteiros: );
scanf(%d %d, &a, &b);
if ((a < 0) && (b < 0))
{
printf(\nForam digitados 2 nmeros negativos.);
}
return 0;
}
A incluso da clusula else permite que um segundo bloco de comandos seja executado, caso a
condio testada pelo comando if seja falsa (igual a zero). Sua forma geral :
If (condio)
{
bloco de comandos 1; /* Executado quando a condio for verdadeira */
}
else
{
bloco de comandos 2; /* Executado quando a condio for falsa */
Prof. Luiz Gustavo Almeida Martins Pg.: 28/82
UFU Universidade Federal de Uberlndia Faculdade de Computao
A seguir apresentado um exemplo da utilizao deste comando. Este programa tem como
entrada um nmero inteiro e sua sada uma frase informando se o nmero fornecido par ou
mpar.
#include <stdio.h>
void main (void)
{
int NRO;
printf(Digite um nmero inteiro: );
scanf(%d, &NRO);
if (NRO%2 == 0)
printf(\nO nmero par);
else
printf(\nO nmero mpar);
}
Neste programa no foram utilizadas as chaves para delimitar os blocos do comando if-else. Alm
disso, observe que os blocos de comandos esto endentados (recuo de linha) estrutura a qual
pertencem. Como j visto em algoritmos, esta endentao no obrigatria, mas fundamental
para uma maior clareza do cdigo, uma vez que permite identificar visualmente o incio e fim de
cada bloco. Todavia, nem sempre o uso da endentao aliado remoo das chaves facilita
a associao dos blocos de comandos a suas respectivas estruturas, como apresentado no
contra-exemplo abaixo:
#include <stdio.h>
void main (void)
{
int TEMP;
printf(Digite a temperatura: );
scanf(%d, &TEMP);
if (TEMP < 30)
if (TEMP > 20)
printf(\nTemperatura agradvel);
else
printf(\nTemperatura muito quente);
}
A idia desse programa era imprimir a mensagem Temperatura agradvel se fosse fornecido um
valor entre 20 e 30 e imprimir a mensagem Temperatura muito quente se fosse fornecido um
valor maior que 30. Entretanto, neste caso, a segunda mensagem ser exibida quando a
temperatura for menor ou igual a 20. Isto porque, na linguagem C, um else associado ao
ltimo if que no tiver seu prprio else. Assim, nos casos onde tal associao no
apropriada, deve-se criar explicitamente os blocos de comando atravs do emprego das
chaves. Abaixo apresentado o programa com as alteraes necessrias ao bom funcionamento:
#include <stdio.h>
void main (void)
{
int TEMP;
printf(Digite a temperatura: );
scanf(%d, &TEMP);
if (TEMP < 30)
{
if (TEMP > 20)
printf(\nTemperatura agradvel);
}
else
printf(\nTemperatura muito quente);
}
8.1.2. NINHOS DE IF
Exemplo:
#include <stdio.h>
void main(void)
{
int TEMP;
printf(Digite a temperatura: );
scanf(&d, &TEMP);
if (TEMP < 20)
{
if (TEMP < 10)
printf(\nTemperatura muito fria.);
else
printf(\nTemperatura fria.);
}
else
{
if (TEMP < 30)
printf(\nTemperatura agradvel.);
else
printf(\nTemperatura quente.);
}
}
A clusula else if usada para escrever decises com escolhas mltiplas (teste de vrias
condies). Funciona de forma similar ao aninhamento de comandos if dentro do blocos de
comandos das clusulas else dos ifs superiores. Sua sintaxe geral :
If (condio1)
{
Abaixo apresentado uma modificao do programa anterior, contendo a clusula else if:
#include <stdio.h>
void main(void)
{
int TEMP;
printf(Digite a temperatura: );
scanf(&d, &TEMP);
if (TEMP < 10)
printf(\nTemperatura muito fria.);
else if (TEMP < 20)
printf(\nTemperatura fria.);
else if (TEMP < 30)
printf(\nTemperatura agradvel.);
else
printf(\nTemperatura quente.);
}
Este tipo de estrutura difere do if, pois s pode testar igualdade, enquanto o if pode testar
qualquer tipo de expresso lgica.
switch (varivel_testada)
{
case valor_1: {
bloco-comandos;
break;
}
case valor_2: {
bloco-comandos;
break;
}
case valor_N: {
bloco-comandos;
break;
}
default: {
bloco-comandos;
}
}
O comando break usado para terminar uma seqncia de comandos. Tecnicamente o seu
uso opcional. Entretanto, quando omitido, a execuo continuar nos comandos do prximo
case at que o computador encontre um break ou o fim do comando switch - case.
A clusula default tambm opcional e serve para tratar os casos que no se enquadram
nas condies testadas nas clusulas case. Se no for utilizado, nenhum comando ser
executado caso todas as opes case falhem.
#include <stdio.h>
#include <conio.h>
main()
{
float x,y,r;
char op;
clrscr();
printf ("Entre com o 1 valor: ");
scanf("%f", &x);
printf ("Entre com o 2 valor: ");
scanf("%f", &y);
op = 0;
while (op !='5')
{
clrscr();
printf ("Menu Principal\n");
printf ("1. Soma \n");
printf ("2. Subtracao \n");
printf ("3. Multiplicacao \n");
printf ("4. Divisao\n");
printf ("5. Sair\n");
printf ("Escolha uma das opes acima:");
op = getche();
clrscr();
if (op!='5')
{
switch (op)
{
case '1': {
printf (" Calculo da Soma \n");
r = x+y;
printf (" %.1f + %.1f = %.1f ", x,y,r);
getch();
break;
}
case '2': {
printf (" Calculo da Subtracao \n");
r = x-y;
printf (" %.1f - %.1f = %.1f ", x,y,r);
getch();
break;
}
case '3': {
printf (" Calculo da Multiplicacao \n");
r = x*y;
printf (" %.1f x %.1f = %.1f ", x,y,r);
getch();
break;
}
case '4': {
printf (" Calculo da Divisao \n");
if (y != 0)
{
r = x/y;
printf (" %.1f / %.1f = %.1f ", x,y,r);
}
else printf(" Divisao por zero!");
getch();
break;
}
default:{
printf("Opo invlida!");
getch();
}
} /* fim do switch */
} /* fim do if */
} /* fim do while */
}
9. ESTRUTURAS DE REPETIO
Permite executar repetidamente um bloco de instrues ate que uma condio de controle seja
satisfeita.
O comando while executa um bloco de comandos enquanto a condio testada for verdadeira
(diferente de zero). Sua sintaxe geral :
Este comando verifica a condio de teste no incio do loop. Isto significa que o seu bloco
de comandos poder ou no ser executado, de acordo com o resultado desta verificao.
# include <stdio.h>
void main(void)
{
int i = 1, soma = 0;
while (i <= 10)
{
soma += i;
i++;
}
printf(A soma dos nmeros inteiros de 1 a 10 %d, soma);
getchar();
}
Caso a condio testada seja um nmero ou uma expresso matemtica, o loop ser executado
at que o valor do nmero ou da expresso seja igual a zero.
Assim como no comando if, se o bloco de comando for formado por uma nica instruo, no
necessrio utilizar as chaves.
O comando do while similar ao comando while, exceto pelo fato que, ele primeiro executa o
bloco de comandos para depois verificar a condio de teste, ou seja, neste comando, as
instrues contidas no loop so executadas pelo menos uma vez. Alm disso, se a condio
testada for verdadeira, o bloco de comandos ser novamente executado at que a condio
de teste se torne falsa. Sua sintaxe geral :
do
{
bloco_comandos;
} while (condio de teste);
# include <stdio.h>
void main(void)
{
int i = 1, soma = 0;
do
{
soma += i;
i++;
} while (i <= 10)
printf(A soma dos nmeros inteiros de 1 a 10 %d, soma);
getchar();
}
O uso deste comando indicado para os casos onde um conjunto de instrues precisa ser
executado pelo menos uma vez e, ento, repetido condicionalmente.
O comando for utilizado quando se deseja executar instrues repetidas que envolvam o
incremento/decremento das variveis de controle do loop (ex: leitura e/ou manipulao de
vetores e matrizes). Sua sintaxe geral :
Onde:
Condio uma expresso relacional que determina o critrio de parada do comando for;
Incremento uma expresso aritmtica que define como a varivel de controle se altera a
cada repetio do comando for.
Estas trs sees devem ser separadas por ponto e vrgula (;).
O comando for continua a execuo enquanto o resultado do teste for verdadeiro. Assim que a
condio for falsa (igual a zero), a execuo do programa ser desviada para os comandos
subseqentes ao comando for. Um exemplo deste comando apresentado abaixo:
# include <stdio.h>
void main(void)
{
int i, soma=0;
VARIAES DO COMANDO
Qualquer uma das trs instrues que formam o comando for pode ser omitida, embora os
ponto e vrgulas devam permanecer.
Se o teste for omitido, o loop se tornar infinito, podendo ser interrompido caso seja usado um
comando de desvio (break, return ou goto) no bloco de comandos do for. Veja o exemplo:
# include <stdio.h>
void main(void)
{
char x;
printf(\nDigite quaisquer caracteres ou tecle A para sair:\n);
for (;;)
{
x=getchar(); /* L um caracter */
if (x == A)
break; /* Sai do loop */
}
printf(\nVoc saiu do Loop);
getchar();
}
# include <stdio.h>
void main(void)
{
int x;
printf(\nDigite quaisquer numeros ou 123 para sair:\n);
for (x=0; x != 123;)
scanf (%d, &x); /* L um nmero */
printf(\nVoc saiu do Loop);
getchar();
}
O comando for permite que haja argumentos mltiplos nas trs sees. Neste caso, os
argumentos adicionais devero ser separados por vrgula (,), como ilustrado no exemplo:
# include <stdio.h>
void main(void)
{
int x, y;
Por fim, tanto a inicializao quanto o incremento podem estar fora dos parnteses de
controle do for, como no exemplo:
# include <stdio.h>
void main(void)
{
int x = 0;
for (; x < 10 ;)
{
printf (x = %d \n, x);
x++;
}
getchar();
}
#include <stdio.h>
void main(void)
{
int NRO, X;
char TEXTO[80];
printf(Digite um nmero inteiro ou zero para sair: );
scanf(%d, &NRO);
while (NRO != 0)
{
TEXTO = O nmero primo;
for (X = 2; X < NRO; X++)
{
if (NRO%X == 0)
{
TEXTO = O nmero no primo;
break;
}
}
puts(TEXTO);
scanf(%d, &NRO);
}
}
Quando executado dentro de uma repetio aninhada (uma estrutura de repetio dentro de
outra), o comando interrompe a repetio mais interna, continuando o processamento na
prxima estrutura de repetio mais interna (estrutura superior imediata).
return (expresso_de_retorno);
Se o comando return estiver dentro de uma estrutura de repetio, ele no s sair da estrutura
como tambm da funo onde foi executado. O exemplo abaixo mostra o return dentro da
funo que eleva um nmero ao quadrado.
# include <stdio.h>
O comando continue pra a seqncia de instrues que est sendo executada (iterao atual),
passando para a prxima iterao, ou seja, voltando para a 1 linha do bloco de comandos do
loop, conforme apresentado no programa-exemplo a seguir.
# include <stdio.h>
void main(void)
{
int x;
printf (Digite um nmero inteiro positivo ou <100> para sair: \n);
do
{
scanf (%d, &x);
if (x < 0)
{
printf (O nmero digitado no ser impresso por ser negativo. \n);
continue;
}
printf(O nmero digitado foi %d. \n, x);
} while (x != 100);
printf (O nmero 100 foi digitado.);
}
Neste exemplo, o nmero digitado s ser impresso na tela se for um nmero positivo. Para isto,
foi empregado o comando continue, o qual provoca a interrupo do bloco de comandos e o
retorno ao comando scanf().
O comando goto desvia a seqncia de execuo lgica de um programa, ou seja, ele passa
o controle de programa (realiza um salto incondicional) para a linha de comando identificada por
um rtulo (label). O rtulo pode ser qualquer nome que atenda s regras de criao de
identificadores e deve ser declarado na posio para onde se deseja saltar, seguido de dois
pontos (:). Tanto o comando goto quanto o seu rtulo devem estar declarados dentro da
mesma funo.
goto nome_rotulo
nome_rotulo:
bloco_comandos;
Este comando no necessrio, podendo sempre ser substitudo por outras estruturas de
controle. Inclusive, o seu uso no recomendvel, pois pode tornar o programa ilegvel e
confuso, principalmente se existirem vrias ocorrncias do comando em diferentes partes do
programa.
Existem algumas situaes muito especficas onde este comando pode tornar o cdigo mais fcil
de entender. Um destes casos quando utilizado para abandonar o processamento de uma
estrutura altamente aninhada (vrios loops e ifs), onde o uso do comando goto mais elegante e
rpido que a utilizao de vrios comandos break.
# include <stdio.h>
void main(void)
{
int x = 0;
printf (Entre com caracteres ou tecle 3 para sair: \n);
for (;;)
{
x = getchar(); /* L um caracter */
if (x == 3)
goto saida; /* Sai do Loop */
else
putchar(x);
}
saida:
printf(Voc saiu do Loop);
}
A funo exit() encontrada na biblioteca padro do C. Sua utilizao limitada, pois provoca o
encerramento imediato de um programa, retornando ao sistema operacional.
# include <stdio.h>
void main(void)
{
char resposta;
printf (Digite um texto qualquer ou <S> para sair: \n);
for (;;)
{
resposta = getchar();
if (resposta == S || resposta == s)
exit();
else
putchar(resposta);
}
}
sendo:
ele ir reservar, de maneira contgua, 20 bytes na memria (2 bytes para cada inteiro vezes 10)
para a varivel NUMEROS e 8 bytes (2 x 2 x 2) para a varivel MATRIZ.
A atribuio ou utilizao do valor de uma das clulas de vetores ou matrizes pode ser feita
atravs da sintaxe:
sendo:
Na linguagem C, esta numerao comea sempre em zero. Isto significa que, no exemplo
anterior, os dados de NUMEROS sero indexados de 0 a 19, enquanto que MATRIZ possuir 04
valores indexados, respectivamente, pelos pares ordenados (0,0), (0,1), (1,0) e (1,1). Entretanto, o
compilador C no verifica se o ndice usado no programa est dentro dos limites vlidos
(este cuidado deve ser tomado pelo programador). Quando no dada a devida ateno aos
limites de validade para os ndices, corre-se o risco de ter variveis sobrescritas ou do computador
travar.
Assim como as demais variveis, estes tipos de estrutura tambm podem ser inicializados na
declarao, como segue:
#include <stdio.h>
void main (void)
{
int num[100]; /* Declara um vetor de inteiros de 100 posies */
int count=0;
int totalnums;
do
{
printf ("Entre com um nmero no nulo ou digite 0 p/ terminar: ");
scanf ("%d",&num[count]);
count++;
} while ((num[count-1] != 0) && (count < 100));
totalnums=count;
printf ("\nOs nmeros digitados foram:\n");
for (count=0 ; count < totalnums ; count++)
printf (" %d",num[count]);
}
Neste programa, a entrada de nmeros feita at que o usurio digite zero ou entre com 100
nmeros no nulos. Os nmeros so armazenados no vetor num. A cada nmero armazenado, a
varivel count, utilizada como ndice/contador do vetor, incrementada. Ao sair do primeiro loop,
a quantidade de nmeros digitados pelo usurio armazenada na varivel totalnums. Por fim,
todos os nmeros so impressos.
#include <stdio.h>
void main (void)
{
int MATRIZ [20][10];
int i,j,cont;
cont=1;
for (i=0;i<20;i++)
for (j=0;j<10;j++)
{
MATRIZ[i][j]=cont;
cont++;
}
}
Vale lembrar que, tanto a leitura quanto o preenchimento de uma matriz, o ndice mais direita
varia mais rapidamente que o seu antecessor e assim por diante at o ndice mais esquerda.
11.2. REGISTROS
Um registro uma estrutura de dados heterognea, ou seja, formada por uma coleo de
variveis que podem assumir tipos diferentes de dados, inclusive os tipos compostos (vetores,
matrizes e registros). Neste tipo de estrutura, as variveis so agrupadas juntas sob um nico
nome para a convenincia de manipulao.
DECLARAO DE REGISTROS
struct FOLHA
{
char NOME[50];
struct END
{
char LOGRADOURO[50];
int NRO;
char BAIRRO[20];
char COMPLEMENTO[20];
} ENDERECO;
long int NRO_SEG;
float SALARIO;
};
Note que, no exemplo acima, dentro do registro FOLHA existe o registro END, o qual foi atribudo
varivel ENDERECO. Isto ocorre porque, como qualquer outra declarao de varivel, aps a
chave de fechamento (}) da lista de membros do registro pode ser declarada a lista de
variveis que recebero este tipo de estrutura.
Exemplo:
struct { ... } x, y, z;
sintaticamente anloga a:
int x, y, z;
no sentido de que cada comando declara x, y e z como variveis do tipo dado e faz com que seja
reservado o espao de memria necessrio ao armazenamento das mesmas.
Esta declarao define a varivel FOLHA_PAGTO como sendo do tipo registro FOLHA.
Normalmente, a declarao do registro feita no incio do programa (declarao global),
enquanto que a declarao das variveis associadas a este tipo de estrutura realizada nas
respectivas funes (declarao local ou argumentos formais).
Durante a declarao de uma varivel do tipo registro, pode-se inicializar os valores dos
membros da varivel. Para isto, basta utilizar o sinal de igual, seguido de um conjunto de
constantes (uma para cada membro, na mesma ordem da declarao do registro) delimitado
por chaves. Um exemplo de inicializao apresentado abaixo:
varivel_registro.nome_membro
O operador ponto . conecta o nome da varivel do tipo registro e o nome do membro que
ser utilizado na expresso. Abaixo apresentado alguns exemplos de uso deste operador:
Na linguagem C, quando trabalhamos com dois registros do mesmo tipo (mesma estrutura),
podemos copiar os valores de um registro para o outro atravs do comando:
registro1 = registro2;
MATRIZES DE REGISTRO
Um registro como qualquer outro tipo de dado no C. Portanto, podemos criar matrizes de
registros. Para isto, devemos utilizar a seguinte declarao:
Sendo que, a quantidade de colchetes que sero utilizados na declarao da matriz de registro
corresponde ao nmero de dimenses da matriz.
Exemplo:
void main(void)
{
struct FOLHA FOLHA_PAGTO[100];
int i;
for (i = 0; i < 100; i++)
FOLHA_PAGTO[i].NRO_SEG = i+1;
}
Neste exemplo, ser alocado espao de memria para 100 instncias do registro FOLHA,
associando este espao varivel FOLHA_PAGAMENTO. Alm disso, o NRO_SEG de cada
instncia receber um nmero equivalente ao ndice da instncia + 1.
No exemplo abaixo, a palavra inteiro passa a representar o tipo int e, portanto, pode ser utilizada
nas declaraes das variveis deste tipo.
#include <stdio.h>
typedef int inteiro;
void main(void)
{
inteiro NRO;
scanf (%d, &NRO);
printf (O nmero digitado foi %i,, NRO);
}
Este comando muito utilizado para dar nome a tipos complexos, como registros, pois facilita a
utilizao desta estrutura de dados no restante do programa.
Exemplo:
#include <stdio.h>
typedef struct tipo_endereco {
char rua [50];
int numero;
char bairro [20];
char cidade [30];
char sigla_estado [3];
long int CEP;
} tp_endereco;
struct tipo_cadastro {
char nome [50];
long int telefone;
tp_endereco endereco;
};
typedef struct tipo_cadastro cadastro;
void main(void)
{
cadastro ALUNOS;
ALUNOS.nome = Joaquim Manoel;
ALUNOS.endereco.cidade = Uberlndia;
...
}
Observe que no mais necessrio usar a palavra chave struct na declarao das variveis para
os registros tipo_endereco e tipo_cadastro. Agora, basta usar os apelidos tp_endereco e cadastro.
12. STRINGS
As strings (cadeia de caracteres) so o uso mais comum para os vetores (string um vetor de
chars). importante lembrar que as strings tm o seu ltimo elemento como um '\0' (terminador
nulo). Portanto, o tamanho de uma string composto pelos caracteres digitados mais o terminador
nulo \0. A declarao geral para uma string :
A biblioteca padro do C possui diversas funes que manipulam strings. Estas funes so
teis, pois no se pode, por exemplo, igualar duas strings diretamente (string1 = string2;). As
strings devem ser igualadas elemento a elemento.
Quando vamos fazer programas que tratam de string muitas vezes podemos fazer bom proveito
do fato de que uma string termina com '\0' (isto , o nmero inteiro 0). Veja, por exemplo, o
programa abaixo que serve para igualar duas strings (isto , copia os caracteres de uma string
para o vetor da outra):
#include <stdio.h>
void main (void)
{
int count;
char str1[100],str2[100];
scanf(%s,&str1);
for (count=0;str1[count];count++)
str2[count]=str1[count];
str2[count]='\0';
printf(Voc digitou: %s,str2);
}
A condio no for acima baseada no fato de que a string que est sendo copiada termina em
'\0'. Quando o elemento encontrado em str1[count] o '\0', o valor retornado para o teste
condicional falso (zero). Desta forma a expresso que vinha sendo verdadeira (no zero)
continuamente, torna-se falsa.
Abaixo sero apresentadas algumas funes bsicas para manipulao de strings. Com exceo
da funo gets(), as demais funes esto contidas no arquivo de cabealho string.h
GETS()
gets (nome_da_string);
#include <stdio.h>
int main ()
{
char string[100];
printf ("Digite o seu nome: ");
gets (string);
printf ("\n\n Ola %s",string);
return(0);
}
Repare que vlido passar para a funo printf() o nome da string. Como o primeiro argumento
da funo printf() uma string, tambm vlido fazer:
STRCPY()
A funo strcpy() copia a string de origem para a string de destino. Seu funcionamento
semelhante ao do programa exemplo apresentado na Manipulao de Strings. Sua forma geral :
strcpy (string_destino,string_origem);
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[100],str2[100],str3[100];
printf ("Entre com uma string: ");
gets (str1);
strcpy (str2,str1); /* Copia str1 em str2 */
strcpy (str3,"Voce digitou a string "); /* Copia "Voce digitou a string" em str3 */
printf ("\n\n%s%s",str3,str2);
return(0);
}
STRCAT()
A funo strcat() concatena (agrega) o contedo da string de origem ao final da string de destino,
permanecendo a string de origem inalterada. Esta funo tem a seguinte forma geral:
strcat (string_destino,string_origem);
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[100],str2[100];
printf ("Entre com uma string: ");
gets (str1);
strcpy (str2,"Voce digitou a string ");
strcat (str2,str1); /* str2 armazenara' Voce digitou a string + o contedo de str1 */
printf ("\n\n%s",str2);
return(0);
}
STRLEN()
strlen (string);
#include <stdio.h>
#include <string.h>
int 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);
return(0);
}
STRCMP()
A funo strcmp() compara as duas strings passadas como argumento. Se elas forem idnticas, a
funo retorna o valor zero. Caso contrrio, a funo retorna um valor diferente de zero. Sua
forma geral :
strcmp (string1,string2);
#include <stdio.h>
#include <string.h>
void main (void)
{
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.");
}
ATOI() E ATOF()
A funo atoi() (abreviatura de alphanumeric to integer) recebe uma cadeia de caracteres que
representa um nmero inteiro em notao decimal e a converte no nmero inteiro correspondente.
Sua forma geral :
De forma similar, a funo atof() (abreviatura de alphanumeric to float) recebe uma cadeia de
caracteres que representa um nmero real em notao decimal e a converte no nmero real (do
tipo double) correspondente. Sua sintaxe :
#include <stdio.h>
#include <stdlib.h>
void main (void)
{
char str1[2] = 12, str2[4] = 2.14;
double soma;
soma = atoi (str1) + atof (str2);
printf ("A soma das strings %lf.",soma);
}
Vetores de strings so matrizes bidimensionais. Isto porque uma string um vetor, portanto,
ao utilizar um vetor de strings estaremos fazendo, na verdade, um vetor de vetores, ou seja, uma
matriz bidimensional do tipo char. Podemos ver a forma geral de um vetor de strings como sendo:
Assim, para acessar um nica string, basta usar o primeiro ndice, como apresentado abaixo:
#include <stdio.h>
void main (void)
{
char strings [5][100];
int count;
for (count=0;count<5;count++)
{
printf ("\n\nDigite a string %d: ", count);
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]);
}
13. FUNES
O tipo_retorno o tipo de varivel que a funo vai retornar. O default o tipo int, ou seja, uma
funo para a qual no se declara o tipo de retorno considerada como retornando um inteiro.
Note que, o tipo deve ser especificado para cada uma das N variveis de entrada. na
declarao de parmetros que informamos ao compilador quais sero as entradas da funo
(assim como informamos a sada no tipo_retorno). Cada varivel descrita na
declarao_parmetros ser tratada como uma varivel local da funo. Quando uma funo
no tiver argumentos de entrada, a lista de parmetros ser vazia. Entretanto, os parnteses da
declarao da funo so obrigatrios.
/* Programa-Exemplo 1 */
#include <stdio.h>
int square (int a)
{
return (a*a);
}
void main ()
{
int num;
printf ("Entre com um numero: ");
scanf ("%d",&num);
num=square(num);
printf ("\n\nO seu quadrado vale: %d\n",num);
}
/* Programa-Exemplo 2 */
#include <stdio.h>
int EPar (int a)
{
if (a%2) /* Verifica se a e divisivel por dois */
return 0; /* Retorna 0 se nao for divisivel */
else
return 1; /* Retorna 1 se for divisivel */
}
int 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");
return 0;
}
No segundo exemplo, pode ser observado o uso de mais de um comando return na funo.
Como as funes retornam valores, podemos aproveit-los para fazer atribuies e comparaes
condicionais (como parte de uma expresso), ou, ainda, na sada de dados. Mas importante
frisar que, em uma atribuio, as chamadas de funes s podem aparecer no lado direito
dos comandos de atribuio.
Alm disso, se uma funo retorna um valor que no precisa ser aproveitado pelo programa, o
mesmo pode ser despresado. Por exemplo, a funo printf() retorna um inteiro que quase nunca
utilizado, portanto, ele descartado.
Em ingls, void quer dizer vazio e este o sentido deste tipo de dado. Ele permite a construo
de funes que no retornam nada (procedimentos) e/ou de funes que no tm
argumentos de entrada. A seguir so apresentados alguns exemplos de prottipos de funes
que no retornam nada e/ou no possuem argumentos formais:
Numa funo sem retorno, o uso do comando return opcional, podendo ser omitido. A seguir
apresentado um exemplo de utilizao do tipo void na declarao de funes:
#include <stdio.h>
void Mensagem (void);
void main (void)
{
Mensagem();
printf ("\tDiga de novo:\n");
Mensagem();
return 0;
}
void Mensagem ()
{
printf ("Ola! Eu estou vivo.\n");
}
Note que na declarao da funo Mensagem(), a palavra void omitida dos parnteses.
A funo main() tambm uma funo e como tal deve ser tratada. Por default, o compilador C
retorna um inteiro para a funo main(). Isto pode ser interessante quando se deseja que o
sistema operacional receba um valor de retorno da execuo do programa. Assim sendo, pode-se
utilizar a seguinte conveno: se o programa retornar zero, significa que ele terminou
normalmente (sem erros); caso contrrio, ou seja, se o programa retornar um valor diferente de
zero, significa que o programa teve um trmino anormal (houve algum erro).
Se no houver interesse no retorno do programa, a funo main() pode ser declarada como
retornando void. Alguns compiladores podem reclamar deste tipo de declarao, dizendo que
main sempre deve retornar um inteiro. Caso isto ocorra, basta fazer main retornar um inteiro.
Uma vez que as funes estejam definidas, pode-se usa-las sem se preocupar como elas foram
escritas. Isto o que denominamos chamada de funo. A sintaxe geral da chamada de uma
funo :
nome_funo(lista_valores);
sendo a lista_valores uma lista com os valores que sero atribudos a cada um dos argumentos
formais declarados na funo. Sempre bom lembrar que a quantidade e o tipo dos valores
declarados na lista_valores da chamada da funo devem ser compatveis com aqueles
apresentados na declarao_parmetros da declarao da funo.
Em geral, os argumentos podem ser passados para as funes de duas maneiras, conforme
segue:
Na passagem de parmetros por valor (ou simplesmente chamada por valor), os valores
dos argumentos que so passados para a funo so copiados nos parmetros formais
correspondentes. Assim, quaisquer alteraes feitas nestes parmetros formais no tm
efeito nas variveis empregadas na chamada da funo.
#include <stdio.h>
float sqr (float num);
void main ()
{
float nro,sq;
printf ("Entre com um numero: ");
scanf ("%f",&nro);
sq = sqr(nro);
printf ("\n\nO numero original e: %f\n",nro);
printf ("O seu quadrado vale: %f\n",sq);
}
float sqr (float num)
{
num = num*num;
return num;
}
No exemplo acima, o parmetro formal num da funo sqr() sofre alteraes dentro da funo,
mas a varivel nro da funo main() permanece inalterada.
Outro tipo de passagem de parmetros para uma funo ocorre quando alteraes nos
parmetros formais, dentro da funo, alteram 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 suas referncias (endereo da varivel na memria). A funo usa estas
referncias para alterar os valores das variveis empregadas na chamada da funo.
Neste tipo de chamada, os parmetros formais de uma funo devem ser declarados como
sendo ponteiros (utilizando o operador *). Os ponteiros so a "referncia" que precisamos para
poder alterar a varivel fora da funo. Alm disso, para que estes ponteiros recebam o endereo
de memria das variveis utilizadas na chamada da funo, necessrio colocar o operador &
na frente de cada uma das variveis, como apresentada no exemplo:
#include <stdio.h>
void sqr (float *num);
void main ()
{
float nro;
printf ("Entre com um numero: ");
scanf ("%f",&nro);
printf ("\n\nO numero original e: %f\n",nro);
sqr(&nro);
printf ("O seu quadrado vale: %f\n",nro);
}
void sqr (float *num)
*num = (*num)*(*num);
Neste exemplo, reescrevemos o programa anterior. Nessa nova verso, passado o endereo de
memria da varivel nro para a funo sqr(). Este endereo copiado no ponteiro num. Atravs
Note que, uma vez que o valor da funo foi armazenado no endereo de memria de nro, no
necessrio nenhum retorno.
Este tipo de chamada a mesma utilizada pela funo scanf(). Isto porque, esta funo precisa
alterar o valor da varivel passada como parmetro, atribuindo o novo valor digitado pelo usurio.
13.3. RECURSIVIDADE
As funes em C podem chamar umas s outras para executar uma tarefa especfica. Um caso
especial de chamada de funes ocorre quando uma funo chama a si mesma. Este processo
denominado de recurso e a funo chamada funo recursiva.
Um bom exemplo de uma funo recursiva a funo que calcule o fatorial de um nmero inteiro:
#include <stdio.h>
unsigned int fat(unsigned int n)
{
if (n > 1 )
return n*fat(n-1);
else
return 1;
}
void main(void)
{
unsigned int NRO;
printf("\n\nDigite um valor inteiro positivo: ");
scanf("%u", &NRO);
printf("\nO fatorial de %d e' %d", NRO, fat(NRO));
}
Neste exemplo, enquanto n > 1, a funo fat() chama a si mesma, cada vez com um valor menor.
O critrio de parada da funo n 1.
Uma funo recursiva quase sempre consome mais memria e tem um processamento
mais demorado que sua verso no recursiva. Isto ocorre porque, a memria consumida na
chamada de uma funo s liberada na concluso da mesma, portanto, o computador aloca
muito mais memria quando a funo recursiva. Entretanto, o cdigo recursivo mais
compacto e, s vezes, mais fcil de entender. Alm disso, h certos algoritmos que so mais
eficientes quando feitos de maneira recursiva. Como exemplo, a construo de alguns tipos de
estruturas de dados abstratos, tais como rvores que at mesmo a sua definio recursiva.
passado, no uma cpia da matriz inteira, ou seja, quando se chama uma funo com argumento
matriz, um ponteiro para o primeiro elemento da matriz passado para a funo.
Existem trs maneiras de se declarar um parmetro que ir receber um ponteiro para matriz:
Como exemplo, seja o vetor de inteiros matrx [50] o argumento de uma funo func(). Podemos
declarar func() das seguintes maneiras:
Nos trs exemplos, existir um ponteiro (int *) chamado matrx dentro de func(). Ao passarmos
uma matriz para uma funo, na realidade estamos passando um ponteiro. Neste ponteiro
armazenado o endereo do primeiro elemento da 1 dimenso. Isto significa que no feita uma
cpia, elemento a elemento da matriz. Assim, possvel alterar o valor dos elementos da matriz
dentro da funo.
Algumas vezes desejvel passar informaes para um programa no incio de sua execuo.
Nestes casos, estas informaes devem ser passadas para a funo main() via argumentos da
linha de comando e recebidas pela funo atravs de seus parmetros formais. A funo main()
pode ter dois parmetros formais j pr-determinados. Os parmetros argc e argv do ao
programador acesso linha de comando do sistema operacional pela qual o programa foi
chamado. A declarao de uma funo main() com estes parmetros :
O argv (argument values) um ponteiro para uma matriz de strings. Cada string desta matriz
um dos parmetros da linha de comando, separados por espaos em branco. O argv[0]
sempre aponta para o nome do programa (que, como j foi dito, considerado o primeiro
argumento).
No exemplo abaixo, foi escrito um programa chamado data que faz uso dos parmentros argv e
argc. Este programa dever receber da linha de comando os inteiros correspondentes ao dia, ms
e ano, no formato abreviado, e imprimi-la por extenso.
#include <stdio.h>
#include <stdlib.h>
data 19 04 1999
19 de abril de 1999
recomendvel implementar funes da maneira mais geral possvel, pois isto facilita a sua
reutilizao e entendimento. Alm disso, sempre que possvel, deve-se evitar o uso variveis
globais nas funes, pois isto dificulta o reaproveitamento da funo por outros programas, bem
como pode causar efeitos colaterais decorrentes da alterao indevida no valor destas variveis
em outras partes do programa.
14. ARQUIVOS
declarao j est definida no arquivo de cabealho stdio.h e chamada FILE. Portanto, para se
cirar um ponteiro de arquivo, basta declar-lo atravs da sintaxe:
FILE *fp;
Onde:
FILE o tipo de dado especial, definido como um typedef dentro do arquivo stdio.h;
Na linguagem C, um arquivo um conjunto de dados que pode ser armazenado e/ou obtido desde
um arquivo em disco at um perifrico (ex: teclado ou impressora).
stdaux: dispositivo de sada auxiliar (em muitos sistemas, associado porta serial)
stdprn : dispositivo de impresso padro (em muitos sistemas, associado porta paralela)
Cada uma destas constantes pode ser utilizada como um ponteiro para FILE, para acessar os
respectivos perifricos associados. Por exemplo:
A abertura ou criao de um arquivo no C feita atravs da funo fopen(). Ela retorna o ponteiro
de arquivo associado ao arquivo. O formato geral da funo :
fp = fopen(nome_arquivo, modo_abertura);
Onde:
nome_arquivo uma string que contm o nome do arquivo que ser manipulado. Este
nome dever ser vlido no sistema operacional que estiver sendo utilizado (ex: no DOS os
nomes devem ter no mximo 12 caracteres, incluindo o ponto e a extenso do arquivo);
modo_abertura uma string que determina como o arquivo ser aberto e, portanto,
utilizado. A tabela abaixo mostra os possveis valores para esta string.
Modo Significado
"r" Abre um arquivo texto para leitura. O arquivo deve existir antes de ser aberto.
Abrir um arquivo texto para gravao. Se o arquivo no existir, ele ser criado. Se j
"w"
existir, o contedo anterior ser destrudo.
Abrir um arquivo texto para gravao. Se ele j existir, os dados sero adicionados no
"a"
fim do arquivo ("append"). Caso contrrio, um novo arquivo ser criado.
Abre um arquivo texto para leitura e gravao. O arquivo deve existir e pode ser
"r+"
modificado.
Cria um arquivo texto para leitura e gravao. Se o arquivo existir, o contedo anterior
"w+"
ser destrudo. Se no existir, ser criado.
Abre um arquivo texto para gravao e leitura. Se o arquivo j existir, os dados sero
"a+"
adicionados no fim do mesmo. Caso contrrio, um novo arquivo ser criado.
"rb" Abre um arquivo binrio para leitura (similar ao modo "r").
"wb" Cria um arquivo binrio para escrita (similar ao modo "w").
"ab" Acrescenta dados ao final do arquivo binrio (similar ao modo "a").
"r+b" Abre um arquivo binrio para leitura e escrita (similar ao modo "r+").
"w+b" Cria um arquivo binrio para leitura e escrita (similar ao modo "w+")
Acrescenta dados ou cria uma arquivo binrio para leitura e escrita (similar ao modo
"a+b"
"a+").
A condio !fp testa se o arquivo foi aberto com sucesso porque, no caso de um erro, a funo
fopen() retorna um ponteiro nulo (NULL).
O padro ANSI determina que pelo menos 8 arquivos podem ser abertos simultaneamente.
Entretanto, a maioria dos compiladores permitem muito mais que isso.
Quando acabamos de usar um arquivo que abrimos, devemos fech-lo. Para tanto usa-se a
funo fclose(). Esta funo fecha um arquivo que tenha sido aberto atravs da funo fopen().
Sua sintaxe geral :
fclose (fp);
Onde fp o nome do ponteiro para o arquivo (ponteiro do tipo FILE) aberto pela funo fopen().
Fechar um arquivo faz com que qualquer caracter que tenha permanecido no "buffer" associado
ao fluxo de sada seja gravado.
A funo retorna um valor inteiro igual a zero quando a operao foi bem sucedida. Qualquer
valor diferente indica erro. Geralmente, os erros retornados esto associados no localizao
da unidade de disco ou inexistncia de espao no disco.
Outra forma de fecharmos arquivos atravs da funo exit(). Esta funo fecha todos os
arquivos abertos do programa.
Aps a abertura de um arquivo, possvel ler ou escrever nele utilizando as funes que sero
apresentadas a seguir.
Existem duas formas equivalentes para escrever caracteres em arquivos: a macro putc() e a
funo fputc(). Estas duas formas so equivalentes e possuem a mesma sintaxe:
Onde:
fp o nome do ponteiro para o arquivo (ponteiro do tipo FILE) aberto pela funo fopen();
Resumidamente, a funo grava o caracter ch no arquivo apontado pelo ponteiro fp, avana o
ponteiro de posio no arquivo e retorna o caracter escrito. Se a funo definir um ponteiro de
erro, ser retornado EOF.
O programa a seguir l uma string do teclado e a escreve, caractere por caractere, em um arquivo
em disco (string.txt).
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *fp;
char string[100];
int i;
fp = fopen("string.txt","w"); /* Arquivo ASCII, para escrita */
if(!fp)
{
printf( "Erro na abertura do arquivo");
exit(0);
}
printf("Entre com a string a ser gravada no arquivo:");
gets(string);
for(i=0; string[i]; i++)
putc(string[i], fp); /* Grava a string, caractere a caractere */
fclose(fp);
}
Aps a execuo deste programa, o arquivo string.txt criado no diretrio corrente e seu o
contedo pode ser acessado atravs de qualquer editor de textos.
Assim como no caso anterior, tambm existem duas formas equivalentes para ler caracteres de
um arquivo: a macro getc() e a funo fgetc(). As duas formas so equivalentes e possuem a
mesma sintaxe:
Onde:
fp o nome do ponteiro para o arquivo (ponteiro do tipo FILE) aberto pela funo fopen();
var a varivel (do tipo int ou char) que receber o caracter lido.
O programa abaixo, ilustrando a aplicao da macro getc(), l qualquer arquivo ASCII, informado
como argumento na chamada do programa, e mostra o seu contedo na tela:
/* Programa: imp_txt.c */
# include <stdio.h>
# include <stdlib.h>
void main (int argc, char *argv[])
{
FILE *fp;
char ch;
if (argc != 2)
{
puts (Sintaxe: imp_txt nome_arq_ASCII);
exit(1);
}
Para se ler uma string num arquivo podemos usar a funo fgets(). Sua sintaxe :
Onde:
Esta funo l a string at que um caracter de nova linha seja lido ou tamanho-1 caracteres
tenham sido lidos. Se o caracter de nova linha ('\n') for lido, ele far parte da string, o que no
acontecia com a funo gets(). A string resultante sempre terminar com '\0' (por isto somente
tamanho-1 caracteres, no mximo, sero lidos).
A funo fgets() semelhante funo gets(), porm, alm dela poder fazer a leitura a partir de
um arquivo de dados e incluir o caracter de nova linha na string, ela ainda especifica o tamanho
mximo da string de entrada. Como j visto, a funo gets() no tinha este controle, o que
poderia acarretar erros de "estouro de buffer". Portanto, levando em conta que o ponteiro fp pode
ser substitudo por stdin, como vimos acima, uma alternativa ao uso de gets() usar a seguinte
construo:
Sendo:
tam o nmero mximo de caracteres que podem ser lidos (tam = tamanho de str - 1). Esta
subtrao se d por causa do caracter \0.
Para se escrever uma string num arquivo podemos usar a funo fputs(). Sua sintaxe :
A funo fputs() acessa os caracteres da string str (exceto o caracter especial terminador nulo
\0) e grava-os no arquivo de sada apontado pelo ponteiro fp. Ela retorna um valor negativo se
no tiver um ponteiro de erro definido, caso contrrio, retorna EOF.
# include <stdio.h>
# include <stdlib.h>
void main (void)
{
char str[80];
FILE *fp;
fp = fopen(TESTE, w);
if (fp == NULL)
{
printf (O arquivo no pode ser aberto \n);
exit(1);
}
do {
printf (Entre com uma string ou tecle <ENTER> para terminar: \n);
fgets (str, 79, stdin);
fputs (str, fp);
} while (*str != \n);
fclose (fp);
}
Para ler e escrever tipos de dados maiores que um byte, o sistema de arquivo ANSI C fornece
as funes fread() e fwrite(). Essas funes permitem a leitura e a escrita de blocos de qualquer
tipo de dado.
Funo fread()
Onde:
buffer um ponteiro para uma regio de memria que receber os dados lidos do arquivo;
num_bytes o tamanho (em bytes) de cada dado que ser lido do arquivo;
Assim, a quantidade total de bytes normalmente lidos do arquivo dada pela expresso:
num_bytes * cont
A funo fread() devolve o nmero de elementos efetivamente lidos. Este valor pode ser menor
que cont se o final de arquivo for atingido ou ocorrer algum erro durante a leitura.
Funo fwrite()
A funo fwrite() possui os mesmos argumentos que a funo fread(), contudo, neste caso, os
argumentos possuem significados diferentes. A sintaxe da funo :
Onde:
num_bytes o tamanho (em bytes) de cada dado que ser escrito no arquivo;
A funo fwrite() devolve o nmero de elementos efetivamente escritos. Este valor ser igual a
cont, a menos que ocorra algum erro durante a processo.
Quando o arquivo for aberto para dados binrios, as funes fread() e fwrite() podem ler e
escrever qualquer tipo de dado.
# include <stdio.h>
# include <stdlib.h>
void main (void)
{
FILE *fp;
double d = 12.23;
int i = 101;
long int l = 1230232;
if ((fp = fopen(TEST, wb)) = = NULL)
{
printf (O arquivo no pode ser aberto \n);
exit(1);
}
if ((fwrite (&d, sizeof(double), 1, fp)) != 1)
puts(Houve um erro na escrita do arquivo varivel double);
if ((fwrite (&i, sizeof(int), 1, fp)) != 1)
puts(Houve um erro na escrita do arquivo varivel int);
if ((fwrite (&l, sizeof(long int), 1, fp)) != 1)
puts(Houve um erro na escrita do arquivo varivel long int);
rewind(fp); /* Volta para o incio do arquivo */
Como pode ser observado, o buffer pode ser, e geralmente , simplesmente a memria usada
para armazernar uma varivel (por isso a necessidade de utilizar o operador &).
O uso das funes fread() e fwrite() muito til para os programas que envolvem a leitura e
escrita de tipos de dados definidos pelo usurio, especialmente registros. Por exemplo, para a
varivel folha definida como:
Podemos escrever seu contedo em um arquivo apontado por fp atravs da seguinte sentena:
FLUXOS PADRO
Funo fprintf()
A funo fprintf() funciona como a funo printf(). A diferena que a sada de fprintf() um
arquivo e no a tela do computador. Sua sintaxe :
Funo fscanf()
A funo fscanf() funciona como a funo scanf(). A diferena que fscanf() l de um arquivo e
no do teclado do computador. Sua sintaxe :
Novamente, a nica diferena da sintaxe desta funo para a da funo scanf() a especificao
do ponteiro para o arquivo de destino (fp).
Abaixo apresentado um exemplo de utilizao destas funes. Neste programa, so lidos uma
string e um nmero inteiro do teclado, os quais so gravados no arquivo. Em seguida, se l a
string e o nmero inteiro do arquivo e os apresenta na tela.
# include <stdio.h>
# include <stdlib.h>
void main(void)
{
FILE *fp;
char str[80];
int t;
if ( !( fp = fopen(TESTE,"w") ) ) /* Abre o arquivo para gravao */
{
printf("Arquivo no pode ser aberto. \n");
exit(1);
}
printf (Entre com uma string e um numero: \n);
fscanf (stdin, %s %d, str, &t); /* L do teclado */
fprintf (fp," %s %d", str, t); /* Escreve no arquivo */
fclose (fp); /* Fecha o arquivo */
if ( !( fp = fopen(TESTE,"r") ) ) /* Abre o arquivo para leitura */
{
printf("Arquivo no pode ser aberto. \n");
exit(1);
}
fscanf (fp,"%s %d", str, &t); /* L do arquivo */
fprintf (stdout, "%s %d", str, t); /* Escreve na tela */
fclose (fp); /* Fecha o arquivo */
}
remove (nm_arquivo);
Sendo:
Esta funo retorna zero caso a remoo seja bem sucedida e um valor diferente de zero, caso
contrrio.
Como esta operao no pode ser desfeita, recomendado confirmar, junto ao usurio, a sua
execuo antes da efetiva remoo. Abaixo, apresentado um programa que apaga um arquivo
especificado na linha de comando.
# include <stdio.h>
# include <stdlib.h>
# include <ctype.h>
void main (int argc, char *argv[])
{
char CONFIRMA;
if (argc != 2)
{
printf (O nome do arquivo no foi informado. \n);
exit(1);
}
printf (Confirma a remoo do arquivo %s (S/N) \?, argv[1]);
scanf(%c, &CONFIRMA);
if ( toupper(CONFIRMA) = = S )
if ( remove(argv[1]) != 0)
{
puts (O arquivo no pode ser apagado.);
exit(1);
}
}
A funo fflush() usada para esvaziar o contedo de um buffer de sada. Sua sintaxe :
fflush(fp);
Sendo:
Esta funo escreve o contedo de qualquer dado existente no buffer do arquivo associado a fp.
Se no for informado o ponteiro fp, a funo descarrega todos os arquivos abertos para sada.
Quando a operao realizada com sucesso, a funo retorna zero. Caso contrrio, retorna EOF.
# include <stdio.h>
# include <string.h>
void main (void)
{
FILE *stream;
static char msg[] = Isto um teste ;
if ( (stream = fopen (ARQ.DAT, w) ) == NULL)
{
printf (O arquivo ARQ.DAT no pode ser aberto. \n);
exit(1);
}
fwrite (msg, strlen(msg), 1, stream);
puts (Pressione qualquer tecla para descarregar o arquivo ARQ.DAT);
getchar();
if ( fflush (stream) == 0 )
puts (O arquivo foi descarregado com sucesso.);
else
puts (O arquivo no pode ser descarregado.);
fclose (stream);
}
Toda vez que estamos trabalhando com arquivos, h uma espcie de posio atual no arquivo.
Esta a posio de onde ser lido ou escrito o prximo caractere. Normalmente, num acesso
seqncial, no precisamos mexer nesta posio, pois quando lemos um dado, a posio no
arquivo automaticamente incrementada. Entretanto, num acesso randmico, muitas vezes
necessrio reposicionar o indicador de posio do arquivo para a obteno do dado
desejado.
FUNO REWIND()
rewind(fp);
O uso desta funo provoca o mesmo efeito do fechamento e abertura do arquivo. Entretanto, o
uso desta funo mais eficiente quanto performance.
FUNO FSEEK()
Para se fazer procuras e acessos randmicos em arquivos usa-se a funo fseek(). Ela modifica o
ponteiro de posio do arquivo. Sua sintaxe :
Onde:
Num_bytes um inteiro longo que determina o nmero de bytes a partir de origem que
ser a nova posio do ponteiro;
origem uma das macros descritas na tabela abaixo, e que esto definidas no arquivo de
cabealho stdlib.h.
# include <stdio.h>
# include <stdlib.h>
void main (int argc, char *argv[])
{
FILE *fp;
if (argc != 3)
{
printf (O nome do arquivo e/ou o nmero de bytes no informados. \n);
exit(1);
}
if ( ( fp = fopen(argv[1], r) ) == NULL)
{
printf (Arquivo %s no pode ser aberto \n, argv[1]);
exit(1);
}
if ( fseek (fp, atol(argv[2]), SEEK_SET) )
{
printf (Erro na busca. \n);
exit(1);
}
printf (O %d.o caracter %c \n, (atol(argv[2]) + 1), getc(fp));
fclose(fp);
}
MACRO FEOF()
EOF ("End Of File") indica o fim de um arquivo. Assim, possvel descobrir se o programa
chegou ao final do arquivo atravs da comparao do caracter lido com o identificador EOF,
descrito no arquivo stdio.h. Esta comparao ilustrada no exemplo abaixo:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *fp;
char c;
fp = fopen("arquivo.txt","r"); /* Arquivo ASCII, para leitura */
if(!fp)
{
printf( "Erro na abertura do arquivo");
exit(0);
}
while((c = getc(fp) ) != EOF) /* Enquanto no chegar ao final do arquivo */
printf("%c", c); /* imprime o caracter lido */
fclose(fp);
}
Outra forma de descobrir se o fim de arquivo foi atingido atravs do uso da macro feof(). Ela
retorna um valor diferente de zero quando for encontrado o final do arquivo associado ao ponteiro
de arquivo especificado. Caso contrrio, retornar zero. Sua sintaxe :
feof (fp);
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char c;
fp = fopen("arquivo.txt","r"); /* Arquivo ASCII, para leitura */
if(!fp)
{
printf( "Erro na abertura do arquivo");
exit(0);
}
while( !feof(fp) ) /* Enquanto no chegar ao final do arquivo */
{
c = getc(fp);
printf("%c", c); /* imprime o caracter lido */
}
fclose(fp);
return 0;
}
FUNO CLEARERR()
A funo clearerr() limpa o erro ou sinal de fim de arquivo associado ao ponteiro de arquivo. Esta
funo possui a seguinte sintaxe:
clearerr(fp);
Sendo:
MACRO FERROR()
A macro ferror() determina se uma operao com arquivo produziu um erro. Ela retorna zero
se nenhum erro ocorreu ou um nmero diferente de zero se houve algum erro durante o acesso
ao arquivo. Sua sintaxe geral :
ferror(fp)
Esta macro muito til quando desejamos verificar se o acesso a um arquivo teve sucesso,
garantindo, assim, a integridade dos nossos dados. Na maioria dos casos, se um arquivo pode ser
aberto, ele pode ser lido ou gravado. Porm, existem situaes em que isto no ocorre. Por
exemplo, pode acabar o espao em disco enquanto gravamos, ou o disco pode estar com
problemas e no conseguimos ler, etc.
No programa-exemplo a seguir, fazemos uso das macros ferror(), feof() e da funo clearerr().
Este programa l strings do arquivo TESTE e as grava no arquivo TESTE1.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *input, *output;
if ( ferror(output) )
{
puts (Erro na gravao do arquivo \a);
clearerr (output);
}
}
fclose(input);
fclose(output);
}
}
}
FUNO PERROR()
A funo perror() (Print Error) exibe uma mensagem de erro correspondente ao nmero inteiro
equivalente ao cdigo de erro identificado (errno). A mensagem ser exibida na sada padro de
erro definida em stderr.
A sua sintaxe :
perror(str);
Onde:
str uma string que indica em que parte do programa ocorreu o erro.
No exemplo abaixo,
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *fp;
char string[100];
if((fp = fopen("arquivo.txt","w")) ==NULL)
{
printf("\nNao consigo abrir o arquivo ! ");
exit(1);
}
do
{
printf("\nDigite uma nova string. Para terminar, digite <enter>: ");
gets(string);
fputs(string, fp);
putc('\n', fp);
if(ferror(fp))
{
perror("Erro na gravacao");
fclose(fp);
exit(1);
}
} while (strlen(string) > 0);
fclose(fp);
}
Como pode ser observado no exemplo acima, normalmente, a funo perror() usada em
conjunto com a macro ferror().
LEITURA COMPLEMENTAR
15. PONTEIROS
Sempre que utilizamos a funo scanf() para ler o elemento de um vetor, passamos o endereo
de memria deste elemento. Para isto, como j visto, utilizamos o operador &, o qual retorna o
endereo de memria alocado para uma varivel. Portanto, se v[i] representa o i-simo
elemento do vetor, &v[i] representa o endereo da memria em que esse elemento est
armazenado.
int v[10];
a varivel v, a qual representa o vetor, uma constante que representa seu endereo inicial, ou
seja, v sem indexao aponta para o primeiro elemento do vetor. Portanto, a linguagem C suporta
aritmtica de ponteiros, isto , pode-se somar e subtrair ponteiros, desde que o valor resultante
aponte para a rea reservada para o vetor.
Se p representa um ponteiro para um inteiro, p+1 representa um ponteiro para o prximo inteiro
armazenado na memria. Este incremento equivale a somar 2 bytes (tamanho de um int) no
endereo de memria armazenado no ponteiro p. Com isso, em um vetor temos as seguintes
equivalncias:
Portanto, escrever &v[i] equivalente a escrever (v + i). De maneira anloga, escrever v[i]
equivalente a escrever *(v + i). Isto porque, o operador * retorna o valor contido na posio de
memria indicada por (v + i).
Tipo_dado *nome_variavel[tamanho1][tamanho2]...[tamanhoN];
#include <stdio.h>
#include <conio.h>
void main (void)
{
int *num; /* Declara um ponteiro para valores inteiros */
int count=0;
int totalnums;
clrscr();
do
{
printf ("Entre com um nmero no nulo ou digite 0 p/ terminar: ");
scanf ("%d",num+count);
count++;
} while ((*(num+count-1) != 0) && (count < 100));
totalnums=count-1;
printf ("\nOs nmeros digitados foram:\n");
for (count=0; count <= totalnums; count++)
{
printf (" %d",*num);
num++;
}
Apesar da forma indexada ser mais clara, o uso de ponteiros muito importante,
principalmente por possibilitar a alocao dinmica de memria (reservar espao em memria
para as variveis durante a execuo do programa). Apesar disso, tanto ponteiros quanto
alocao dinmica no fazem parte do escopo deste curso.
O C altamente dependente dos ponteiros. Por isso, para ser um bom programador em C
fundamental que se tenha um bom domnio deles.
O tipo int guardam inteiros. O tipo float guardam nmeros de ponto flutuante. O tipo char
guardam 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 tem anotado um endereo. Qual o sentido disto? Simples. Quando voc anota o endereo
de um colega, depois voc vai usar este endereo para ach-lo. O C funciona assim. Voce 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. Veja: quando voc anota um endereo de um amigo voc o trata
diferente de quando voc anota o endereo de uma firma. Apesar de o endereo dos dois locais
ter o mesmo formato (rua, nmero, bairro, cidade, etc.) eles indicam locais cujos contedos so
diferentes. Ento os dois endereos so ponteiros de tipos diferentes.
tipo_do_ponteiro *nome_da_varivel;
o operador asterisco (*) que faz o compilador saber que aquela varivel no vai guardar um
valor, mas sim um endereo para aquele tipo especificado. Vamos ver exemplos de declaraes:
int *pt;
char *temp,*pt2;
O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para
caracteres. Eles 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. Usar o ponteiro nestas
circunstnicias pode levar a um travamento do micro, ou a algo pior.
O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado! Isto
de suma importncia!
int count=10;
int *pt;
pt=&count;
Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A expresso &count
nos d o endereo de memria de count, que armazenado em pt. Note que no alteramos o
valor de count, que continua valendo 10.
Como ns colocamos um endereo em pt, ele est agora "liberado" para ser usado. Podemos, por
exemplo, alterar o valor de count usando pt. Para tanto vamos usar o operador "inverso" do
operador &, ou seja, o operador *. No exemplo acima, uma vez que fizemos pt=&count a
expresso *pt equivalente ao prprio count. Isto significa que, se quisermos mudar o valor de
count para 12, basta fazer *pt=12.
Vamos fazer uma pausa e voltar nossa analogia para ver o que est acontecendo.
Digamos que exista uma firma. Ela como uma varivel que j foi declarada. Voc tem um papel
em branco onde vai anotar o endereo da firma. O papel um ponteiro do tipo firma. Voc ento
liga para a firma e pede o seu endereo, o qual voc vai anotar no papel. Isto equivalente, no C,
a associar o papel firma com o operador &. Ou seja, o operador & aplicado firma equivalente
a voc ligar para a mesma e pedir o endereo. Uma vez de posse do endereo no papel voc
poderia, por exemplo, fazer uma visita firma. No C voc faz uma visita firma aplicando o
operador * ao papel. Uma vez dentro da firma voc pode copiar seu contedo ou modific-lo.
No primeiro exemplo, o cdigo %p usado na funo printf() indica funo que ela deve imprimir
um endereo.
Podemos fazer algumas operaes aritmticas com ponteiros. A primeira, e mais simples,
igualar dois ponteiros. Se temos dois ponteiros p1 e p2 podemos igual-los fazendo p1=p2.
Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que
a varivel apontada por p1 tenha o mesmo contedo da varivel apontada por p2 devemos fazer
*p1=*p2. Basicamente, depois que se aprende a usar os dois operadores (& e *) fica fcil
entender operaes com ponteiros.
Mais uma vez insisto. Estamos falando de operaes com ponteiros e no de operaes com o
contedo das variveis para as quais eles apontam. Por exemplo, para incrementar o contedo da
varivel apontada pelo ponteiro p, faz-se:
(*p)++;
Outras operaes aritmticas teis so a soma e subtrao de inteiros com ponteiros. Vamos
supor que voc queira incrementar um ponteiro de 15. Basta fazer:
p=p+15; ou p+=15;
*(p+15);
A subtrao funciona da mesma maneira. Uma outra operao, s vezes til, a comparao
entre dois ponteiros. Mas que informao recebemos quando comparamos dois ponteiros? Bem,
em primeiro lugar, podemos saber se dois ponteiros so iguais ou diferentes (== e !=). No caso de
operaes do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posio
mais alta na memria. Ento uma comparao entre ponteiros pode nos dizer qual dos dois est
"mais adiante" na memria. A comparao entre dois ponteiros se escreve como a comparao
entre outras duas variveis quaisquer:
p1>p2
H entretanto operaes que voc no pode efetuar num ponteiro. Voc no pode dividir ou
multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair float ou double de ponteiros.
o compilador C calcula o tamanho, em bytes, necessrio para armazenar esta matriz. Este
tamanho :
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. Eis 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.
Mas a surge a pergunta: ento como que podemos usar a seguinte notao?
nome_da_varivel[ndice]
Isto pode ser facilmente explicado desde que voc entenda que a notao acima absolutamente
equivalente a se fazer:
*(nome_da_varivel+ndice)
Agora podemos entender como que funciona um vetor! Vamos ver o que podemos tirar de
informao deste fato. Fica claro, por exemplo, porque que, no C, a indexao comea com
zero. porque, ao pegarmos o valor do primeiro elemento de um vetor, queremos, de fato,
*nome_da_varivel e ento devemos ter um ndice igual a zero. Ento sabemos que:
Outra coisa: apesar de, na maioria dos casos, no fazer muito sentido, poderamos ter ndices
negativos. Estaramos pegando posies de memria antes do vetor. Isto explica tambm porque
o C no verifica a validade dos ndices. Ele no sabe 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.
Vamos ver agora um dos usos mais importantes dos ponteiros: a varredura sequencial de uma
matriz. Quando temos que varrer todos os elementos de uma matriz de uma forma sequencial,
podemos usar um ponteiro, o qual vamos incrementando. Qual a vantagem? Considere o seguinte
programa para zerar uma matriz:
int main ()
{
float matrx [50][50];
int i,j;
for (i=0;i<50;i++)
for (j=0;j<50;j++)
matrx[i][j]=0.0;
return(0);
}
int main ()
{
float matrx [50][50];
float *p;
int count;
p=matrx[0];
for (count=0;count<2500;count++)
{
*p=0.0;
p++;
}
return(0);
}
No primeiro programa, cada vez que se faz matrx[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". Seja:
int vetor[10];
int *ponteiro, i;
ponteiro = &i;
Teste as operaes acima no seu compilador. Ele dar uma mensagem de erro. Alguns
compiladores diro que vetor no um Lvalue ("Left value"). Isto significa que, um smbolo que
pode ser colocado do lado esquerdo de uma expresso de atribuio, ou seja, uma varivel.
Outros compiladores diro que tem-se "incompatible types in assignment", tipos incompatveis
em uma atribuio.
Sabemos agora que, na verdade, o nome de um vetor um ponteiro constante. Sabemos tambm
que podemos indexar o nome de um vetor. Como consequncia podemos tambm indexar um
ponteiro qualquer. O programa mostrado a seguir funciona perfeitamente:
#include <stdio.h>
int main ()
{
int matrx [10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *p;
p=matrx;
printf ("O terceiro elemento do vetor e: %d",p[2]);
return(0);
}
&nome_da_varivel[ndice]
vlida e retorna o endereo do ponto do vetor indexado por ndice. Isto seria equivalente a
nome_da_varivel + indice. interessante notar que, como consequncia, o ponteiro
nome_da_varivel tem o endereo &nome_da_varivel[0], que indica onde na memria est
guardado o valor do primeiro elemento do vetor.
VETORES DE PONTEIROS
Podemos construir vetores de ponteiros como declaramos vetores de qualquer outro tipo. Uma
declarao de um vetor de ponteiros inteiros poderia ser:
Podemos inicializar ponteiros. Vamos ver um caso interessante dessa inicializao de ponteiros
com strings.
Precisamos, para isto, entender como o C trata as strings constantes. Toda string que o
programador insere no programa colocada num 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). por isto que podemos usar strcpy() do seguinte modo:
strcpy() pede dois parmetros do tipo char*. Como o compilador substitui a string "String
constante." pelo seu endereo no banco de strings, tudo est bem para a funo strcpy().
O que isto tem a ver com a inicializao de ponteiros? que, para uma string que vamos usar
vrias vezes, podemos fazer:
A poderamos, em todo lugar que precisarmos da string, usar a varivel str1. Devemos apenas
tomar cuidado ao usar este ponteiro. Se o alterarmos vamos perder a string. Se o usarmos para
alterar a string podemos facilmente corromper o banco de strings que o compilador criou.
Mais uma vez fica o aviso: ponteiros so poderosos, mas, se usados com descuido, podem ser
uma tima fonte de dores de cabea.
Um ponteiro para um ponteiro como se voc anotasse o endereo de um papel que tem o
endereo da casa do seu amigo. Podemos declarar um ponteiro para um ponteiro com a seguinte
notao:
tipo_da_varivel **nome_da_varivel;
Para acessar o valor desejado apontado por um ponteiro para ponteiro, o operador asterisco deve
ser aplicado duas vezes, como mostrado no exemplo abaixo:
#include <stdio.h>
int main()
{
float fpi = 3.1415, *pf, **ppf;
pf = &fpi; /* pf armazena o endereco de fpi */
ppf = &pf; /* ppf armazena o endereco de pf */
printf("%f", **ppf); /* Imprime o valor de fpi */
printf("%f", *pf); /* Tambem imprime o valor de fpi */
return(0);
}
O principal cuidado ao se usar um ponteiro deve ser: saiba sempre para onde o ponteiro est
apontando. Portanto, nunca use um ponteiro que no foi inicializado. Um pequeno programa
que demonstra como no usar um ponteiro:
Este programa compilar e rodar. O que acontecer? Ningum sabe. O ponteiro p pode estar
apontando para qualquer lugar. Voc estar gravando o nmero 13 em um lugar desconhecido.
Com um nmero apenas, voc provavelmente no vai ver nenhum defeito. Agora, se voc
comear a gravar nmeros em posies aleatrias no seu computador, no vai demorar muito
para travar o micro (se no acontecer coisa pior).
A alocao dinmica permite ao programador alocar memria para variveis quando o programa
est sendo executado. Assim, pode-se definir, por exemplo, um vetor ou uma matriz cujo tamanho
definido em tempo de execuo.
Existem diversas outras funes que so amplamente utilizadas, mas dependentes do ambiente e
compilador. No entanto, o padro ANSI C define apenas quatro funes para o sistema de
alocao dinmica, disponveis no arquivo de cabealho stdlib.h, e descritas a seguir.
A funo malloc() serve para alocar um espao de memria. Sua sintaxe geral :
Esta funao aloca na memria o nmero de bytes definido por num. Ela retorna um ponteiro ptr
do tipo void * para o primeiro byte alocado. Este ponteiro pode ser atribudo a qualquer tipo de
ponteiro. Se no houver memria suficiente para a alocao requisitada, a funo retorna um
ponteiro nulo (NULL).
#include <stdio.h>
#include <stdlib.h> /* Para usar malloc() */
void main (void)
{
int *p;
int a, i;
a = 100;
/* O comando abaixo atribui ao ponteiro p o espao necessrio para guardar a N
inteiros, assim, p agora pode ser tratado como um vetor com 100 posies */
p = (int *) malloc (a*sizeof(int) );
if (!p)
{
printf ("** Erro: Memoria Insuficiente **");
exit;
}
for (i = 0; i < a ; i++)
p[i] = i * i;
}
atribudo a p. O comando seguinte testa se a operao foi bem sucedida. Se no tiver sido, p ter
um valor nulo, portanto, !p ser verdadeiro. Se a operao tiver sido bem sucedida, podemos usar
o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)].
A funo calloc() tambm serve para alocar memria, mas sua sintaxe um pouco diferente:
Esta funao aloca uma quantidade de memria igual a num * size, ou seja, aloca memria
suficiente para um vetor de num objetos de tamanho size. Assim como no caso anterior, ptr
tambm um ponteiro do tipo void * para o primeiro byte alocado. Se no houver memria
suficiente para alocar a memria requisitada a funo calloc() retorna um ponteiro nulo.
Portanto, o programa anterior pode ser rescrito para realizar a alocao dinmica com calloc():
#include <stdio.h>
#include <stdlib.h> /* Para usar calloc() */
void main (void)
{
int *p;
int a, i;
a = 100;
p=(int *)calloc(a,sizeof(int));
if (!p)
{
printf ("** Erro: Memoria Insuficiente **");
exit;
}
for (i = 0; i < a ; i++)
p[i] = i * i;
}
A funo realloc() serve para redefinir a alocao de memria. Sua sintaxe geral :
Esta funao modifica o tamanho da memria previamente alocada e apontada por ptr para aquele
especificado por num. O valor de num pode ser maior ou menor que o tamanho original. Um
ponteiro para a nova alocao devolvido. Isto ocorre porque realloc() pode precisar mover o
bloco alocado originalmente para aumentar seu tamanho, ou seja, alterar o endereo de memria
dos bytes alocados. Se isso ocorrer, o contedo do bloco antigo copiado no novo bloco, e
nenhuma informao perdida. Se ptr for nulo, aloca num bytes e devolve um ponteiro. Se
num zero, a memria apontada por ptr liberada. Se no houver memria suficiente para a
realocao, um ponteiro nulo devolvido e o bloco original deixado inalterado.
#include <stdio.h>
free (ptr);
Atravs deste ponteiro, o programa busca numa "tabela de alocao interna" a quantidade de
bytes que devem ser liberados.
No exemplo abaixo, o espao alocado para p pela funo malloc() liberado pela funo free()
ao final do programa:
#include <stdio.h>
#include <stdlib.h> /* Para usar malloc() e free() */
void main (void) {
int *p;
int a, i;
a = 100;
p = (int *) malloc (a*sizeof(int) );
if (!p) {
printf ("** Erro: Memoria Insuficiente **");
exit; }
for (i = 0; i < a ; i++) {
p[i] = i * i;
printf (\nO elemento %d possui o valor %d., i, p[i]); }
/* O comando abaixo libera o espao de memria alocado para o ponteiro p */
free(p);
}