Escolar Documentos
Profissional Documentos
Cultura Documentos
Apostila Linguagem C PDF
Apostila Linguagem C PDF
Faculdade de Computao
Apostila de Linguagem C
(Conceitos Bsicos)
Faculdade de Computao
Pg.: 1/82
Faculdade de Computao
Alta portabilidade inerente da padronizao ANSI, ou seja, possvel tomar um cdigofonte escrito para uma mquina, compil-lo e rod-lo em outra com pouca ou nenhuma
alterao;
Uso de chaves ({ }) para agrupar comandos pertencentes a uma estrutura lgica (ex: ifelse, do-while, for, etc.) ou a uma funo;
tipo_dado funcN(lista_parametros)
{
Bloco de comandos;
}
Prof. Luiz Gustavo Almeida Martins
Pg.: 2/82
Faculdade de Computao
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.
Vale lembrar que, durante a compilao do programa, os comentrios so descartados pelo
compilador.
Exemplo:
/* esta e uma linha de comentrio em C */
2. DIRETIVAS
Durante a confeco do cdigo-fonte, alm dos comandos normais, o programador poder utilizarse 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.
CdigoFonte (.C)
Pr-Processamento
da Compilao
Cdigo-Fonte +
Arqs. de Incluso
Compilao
Cdigo-Objeto (.OBJ)
Pr-Processamento
da Linkedio
Linkedio
Programa-Executvel (.EXE)
Pg.: 3/82
Faculdade de Computao
#include <stdio.h>
#include stdio.h
ARQUIVOS
ARQUIVO
assert.h
ctype.h
errno.h
float.h
limits.h
locale.h
math.h
setjmp.h
signal.h
stdarg.h
stddef.h
stdio.h
stdlib.h
string.h
time.h
DE
CABEALHO
PADRO
ANSI C
FINALIDADE
Contm a macro assert que usada para incluir diagnstico em programas
Declara funes para testar caracteres
Define o nmero de erro do sistema
Define os valores em ponto flutuante especficos do compilador
Define tipos de valores-limites especficos do compilador
Utilizada para adaptar as diferentes convenes culturais
Define as funes matemticas e macros usadas para operaes matemticas em
linguagem C
Prov uma forma de evitar a seqncia normal de chamada e retorno de funo
profundamente aninhada
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
Define macros para acessar argumentos quando uma funo usa um nmero
varivel de argumentos
Define funes, constantes, macros e estruturas usadas para operaes padres
de entrada e sada
Contm funes, tipos e macros de entrada e sada
Contm funes para converso de nmeros, alocao de memria e tarefas
similares
Define funes para manipulao de strings
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.
Na criao de um identificador, o primeiro argumento indica o nome do identificador e o segundo
define o contedo que ser associado a este identificador, como no exemplo abaixo:
#define NOME Fulano de Tal
Neste caso, o pr-processador da compilao substituir toda a apario subseqente do
identificador NOME, em qualquer lugar do cdigo-fonte, pela string Fulano de Tal. Vale ressaltar
que, se o pr-processador encontrar a palavra NOME dentro de uma string, isto , entre aspas
(ex: Digite o seu NOME), no ser realizada nenhuma substituio.
Prof. Luiz Gustavo Almeida Martins
Pg.: 4/82
Faculdade de Computao
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:
# define AVISO Este texto muito grande, \
assim estou utilizando a barra invertida para mant-lo dentro da minha tela
No existe nenhuma regra de substituio de strings. Assim, qualquer seqncia de caracteres
ser literalmente e exatamente inserida no cdigo-fonte, onde quer que o identificador
correspondente seja encontrado. A nica exceo o macete de continuao de linha, j que a
barra invertida no parte integrante da string. A verificao sinttica (sentido contextual) da
substituio ser realizada posteriormente durante a fase de compilao propriamente dita.
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:
#define CUBO(X) ((X)*(X)*(X))
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:
#
#define mkstr(s) # s
printf(mkstr(Processamento de Dados));
##
#define concat(a,b) a ## b
printf(concat(Processamento, de Dados);
Pg.: 5/82
Faculdade de Computao
3. PROTTIPOS DE FUNES
O padro ANSI C expandiu a declarao tradicional de funo, permitindo que a quantidade e os
tipos dos argumentos das funes sejam declarados no incio do programa. Esta definio
expandida chamada de prottipo da funo.
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.
A forma geral de uma definio de prottipo de funo :
tipo nm_funo (tipo nm_arg1, tipo nm_arg2, ... , tipo nm_argN);
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.
Devido necessidade de compatibilidade com a verso original do C, a qual no possui prottipo
de funes, algumas informaes adicionais devem ser consideradas:
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.
Prof. Luiz Gustavo Almeida Martins
Pg.: 6/82
Faculdade de Computao
4. TIPOLOGIA DE DADOS
Durante a compilao do cdigo-fonte, o compilador C procura alocar o espao de memria
necessrio para a execuo do programa. Embora o compilador possa determinar os tipos de
dados e espao de memria exigidos para as constantes encontradas no cdigo, no caso das
variveis, isto s possvel se houver a declarao prvia do tipo de dado empregado.
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.
TIPOS BSICOS
Tipos
char
int
float
double
void
Descrio
Caractere
Inteiro
Ponto flutuante
Ponto flutuante de preciso dupla
Sem valor
DE
DADOS
DO
Tamanho Aprox.
8 bits = 1 byte
16 bits = 2 bytes
32 bits = 4 bytes
64 bits = 8 bytes
Faixa Mnima
-127 a 127
-32.767 a 32.767
7 dgitos de preciso
10 dgitos de preciso
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.
Pg.: 7/82
Faculdade de Computao
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():
TIPOS
DE
DADOS MODIFICADOS
Formato
Num de bits para leitura
com scanf
Tipo
Intervalo
Inicio
Fim
Char
%c
-128
127
unsigned char
%c
255
Signed char
%c
-128
127
Int
16
%i ou %d
-32.768
32.767
unsigned int
16
%u
65.535
signed int
16
%i ou %d
-32.768
32.767
Short int
16
%hi ou %hd
-32.768
32.767
16
%hu
65.535
16
%hi ou %hd
-32.768
32.767
Long int
32
%li ou %ld
-2.147.483.648
2.147.483.647
32
%li ou %ld
-2.147.483.648
2.147.483.647
32
%lu
4.294.967.295
Float*
32
%f, %e ou %g
3,4E-38
3.4E+38
Double*
64
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
1 bit de sinal + 7 bits
de dados
int
1 bit de sinal + 15 bits de
dados
unsigned char
8 bits de dados
unsigned int
16 bits de dados
float
Pg.: 8/82
Faculdade de Computao
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.
Os identificadores podem ter qualquer tamanho. Entretanto, de acordo com o uso do
identificador (interno ou externo chamado por outros programas), ele deve se diferenciar dos
demais (possuir um nome diferente de todos os outros identificadores utilizados) dentro de uma
margem pr-estabelecida (6 caracteres para nomes externos e 31 caracteres para nomes
internos).
Alm disso, um identificador no pode ser igual a uma palavra-chave de C ou a um nome de
funo j criada pelo usurio ou existente na bliblioteca padro.
OBS: Lembre-se que o C case-sensitive. Portanto, letras minsculas e maisculas so tratadas
diferentemente (ex: cont, CONT e Cont so trs identificadores diferentes).
Pg.: 9/82
Faculdade de Computao
Todas as variveis do C devem ser declaradas antes de serem usadas. A forma geral de uma
declarao :
tipo lista_de_variveis;
Sendo:
Tipo: um tipo de dado vlido em C mais quaisquer modificadores; e
Lista_de_variveis: um ou mais nomes de identificadores separados por vrgula.
Exemplos:
int nro, idade, ndice;
short int idade;
double salario, pagamento;
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.
Ex:
void func1()
{
int NRO;
NRO = 100;
}
void func2()
{
int NRO;
NRO = 0;
}
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.
Pg.: 10/82
Exemplo:
void func()
{
int NRO;
scanf (%d, &NRO);
if (NRO = = 0)
{
char NOME[80];
printf (Entre com o nome:);
gets(NOME);
}
}
Faculdade de Computao
Contra-Exemplo:
void func()
{
int NRO;
NRO = 0;
char NOME[80];
gets(NOME);
}
/* Funo com declarao incorreta,
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.
Prof. Luiz Gustavo Almeida Martins
Pg.: 11/82
Faculdade de Computao
VARIVEIS GLOBAIS
Variveis globais so aquelas declaradas no incio do programa, fora de qualquer funo.
Ao contrrio das variveis locais, este tipo de varivel tem seus valores guardados durante toda a
execuo do programa, portanto, so reconhecidas no programa inteiro e podem ser usadas
por qualquer pedao de cdigo.
O armazenamento de variveis globais se encontra em uma regio fixa da memria, separada
para esse propsito pelo compilador C. Variveis globais so teis quando o mesmo dado
usado em muitas funes em seu programa. No entanto, deve-se evitar o uso desnecessrio
deste tipo de varivel, uma vez que elas ocupam memria durante todo o tempo de execuo do
programa e no apenas quando so necessrias.
/*Programa 1*/
int x, y;
char z;
main(void)
{
}
func1()
{
x = 24;
y = x+3;
}
/*Programa 2*/
extern int x, y;
extern char z;
main(void)
{
}
Func2()
{
y = 33;
x = y-10;
}
STATIC
Variveis static so variveis permanentes. Isto , apesar de no serem reconhecidas fora de
suas funes ou arquivos, mantm seus valores entre as chamadas. Essa caracterstica torna
Prof. Luiz Gustavo Almeida Martins
Pg.: 12/82
Faculdade de Computao
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.
Quando aplicado a uma varivel local, o compilador cria um armazenamento permanente,
quase da mesma forma que cria para variveis globais. A diferena fundamental entre elas que
a varivel local static reconhecida somente no bloco em que est declarada.
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
Por definio, as variveis no C so da classe automtica. O compilador automaticamente cria a
varivel cada vez que a funo chamada e elimina-a quando a funo finalizada.
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 &.
Pg.: 13/82
Faculdade de Computao
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.
Constantes de caracter so delimitadas por apstrofo [] (ex: a, %, 1). Caracteres
delimitados por aspas [] so tratados como constantes string (cadeia de caracteres),
portanto, ocupam um byte a mais na memria (ex: a = a + \0). Constantes inteiras so
especificadas como nmeros sem componentes fracionrios (ex: 69, -10). Constantes reais
podem ser expressas na forma decimal, neste caso requerido o ponto decimal [.] seguido da
parte fracionria (ex: 3.141, -7.33); ou atravs de notao cientfica (ex: 4.34e-3).
Pg.: 14/82
Faculdade de Computao
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:
0XEF
0x12A4
03212
034215432
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;
Sendo expresso uma combinao de variveis, constantes e operadores.
5.1. OPERADORES
O C possui um grande conjunto de operadores que podem ser utilizados na manipulao de
variveis e constantes durante a execuo do programa.
OPERADORES ARITMTICOS
Os operadores aritmticos so usados para desenvolver operaes matemticas. A lista dos
operadores aritmticos do C apresentada a seguir:
OPERADORES ARITMTICOS
Operador
DO
Finalidade
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
Prof. Luiz Gustavo Almeida Martins
Pg.: 15/82
Faculdade de Computao
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.
Abaixo so apresentados alguns exemplos de utilizao dos operadores aritmticos:
int A = 1, B = 2, C;
float X = 2.0, Y = 3.5, Z;
C = (2 * A + B) / B;
/* C receber o valor 2 */
Z = X * Y + C;
C = (A + B) % B;
/* C receber o valor 1 */
C = -B;
/* C receber o valor -2 */
/* Equivalente expresso X = X + 1 */
/* Equivalente expresso X = X + 1 */
/* Equivalente expresso X = X - 1 */
/* Equivalente expresso X = X - 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:
int X = 10, Y;
Y = ++X;
/* Neste caso Y e X = 11 */
int X = 10, Y;
Y = X++;
/* Neste caso Y = 10 e X = 11 */
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 :
varivel operador = expresso;
EXEMPLO
DE
UTILIZAO
DE
EXPRESSES REDUZIDAS
Expresses Reduzidas
Expresses Normais
A += 5;
A = A + 5;
B - = 2.5;
B = B 2.5;
Pg.: 16/82
Faculdade de Computao
Expresses Reduzidas
Expresses Normais
C *= 2;
C = C * 2;
D /= 7;
D = D / 7;
E %= 3;
E = E % 3;
Note que este tipo de expresso muito til para a manipulao de variveis contadoras e/ou
acumuladoras (que envolve somatrias, totais, etc.).
OPERADORES RELACIONAIS E LGICOS
Os operadores relacionais realizam comparaes entre expresses, e seus resultados sempre
so valores lgicos (falso valores iguais a zero ou verdadeiro valores diferentes de zero).
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 */
Prof. Luiz Gustavo Almeida Martins
Pg.: 17/82
Faculdade de Computao
Pg.: 18/82
Faculdade de Computao
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.
PRECEDNCIA
DOS
OPERADORES
Operadores
Sentido de Execuo
() []
Esquerda p/ Direita
++ -- - (unrio) (cast)
Direita p/ Esquerda
* / %
+ - (binrio)
< > <= >=
== !=
Esquerda p/ Direita
!
&&
||
?: (expresso condicional)
= += *= /= - =
Direita p/ Esquerda
6. FUNES DISPONVEIS NO C
6.1. FUNES MATEMTICAS
Na tabela abaixo so apresentadas algumas das funes matemticas disponveis na linguagem
C. Para utiliza-las necessrio declarar o arquivo de cabealho math.h.
FUNES MATEMTICAS
DO
Funo
Sintaxe
Descrio
ceil()
ceil(REAL)
cos()
cos(NRO)
exp()
exp(NRO)
fabs()
fabs(NRO)
floor()
floor(REAL)
log()
log(NRO)
log10()
log10(NRO)
Pg.: 19/82
Faculdade de Computao
Funo
Sintaxe
Descrio
pow()
Pow(BASE,EXP)
sin()
sin(NRO)
sqrt()
sqrt(NRO)
tan()
tan(NRO)
X =X
1
3
Pg.: 20/82
Faculdade de Computao
Caracteres diversos que sero impressos na tela (correspondem ao texto que ser
exibido);
Pg.: 21/82
Faculdade de Computao
ALGUNS ESPECIFICADORES
%d
%i
%o
%x
%X
%u
%c
%s
Tipo do
Argumento
int
int
int
int
int
int
int
char
%f
float
%e
float ou double
%E
float ou double
%g
float ou double
%G
float ou double
%%
Especificador
DE
FORMATO
DA
FUNO
PRINTF()
Descrio
Valor inteiro decimal
Valor inteiro decimal
Valor inteiro octal
Valor inteiro hexadecimal
Valor inteiro hexadecimal
Valor inteiro decimal sem sinal (unsigned int)
Um caracter em formato ASCII (cdigo binrio correspondente)
Uma cadeia de caracteres (string) terminada em \0
Valor em ponto flutuante no formato [-]m.dddddd, onde o N de
ds dado pela preciso (padro 6)
Valor em ponto flutuante em notao exponencial no formato
[-]m.ddddd exx, onde o N de ds dado pela preciso
Valor em ponto flutuante em notao exponencial no formato
[-]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
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
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:
Pg.: 22/82
Faculdade de Computao
Exemplos:
4
%4d
2
2
%-4d
%7.2f
%-7.2f
2
2
15.25
584.13
0021
21
3456.780
3456.8
47 /
Pg.: 23/82
Faculdade de Computao
7.2. SCANF()
A funo scanf() l caracteres da entrada padro (teclado), interpreta-os segundo a especificao
de formato e armazena os resultados em variveis declaradas no programa. Sua forma geral :
scanf (string de controle, lista de endereos de memria das variveis);
A representao do endereo de memria das variveis que recebero os dados de entrada
fornecido pelo operador &, seguido pelo nome da varivel. Alm disso, assim como na
funo printf(), esta funo tambm utiliza os caracteres especiais de controle via cdigos de
barra invertida (veja seo 7.7) e de formatao % (conforme apresentado na tabela abaixo).
ALGUNS ESPECIFICADORES
Especificador
%d
%i
%u
%c
%s
%f, %e, %g
%lf, %le, %lg
%o
%x ou %X
DE
FORMATO
DA
FUNO
SCANF()
Descrio
Indica um valor inteiro decimal
Indica um valor inteiro (podendo estar na base
decimal, octal com inicial 0 (zero) ou hexadecimal
com inicial 0X
Indica um valor inteiro decimal sem sinal
Indica um caracter (char)
Indica uma string
Indica um valor em ponto flutuante de preciso
simples (float)
Indica um valor em ponto flutuante de preciso dupla
(double)
Indica um valor inteiro octal (com ou sem zero inicial)
Indica um valor inteiro hexadecimal (com ou sem
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
Prof. Luiz Gustavo Almeida Martins
Pg.: 24/82
Faculdade de Computao
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>.
A forma bsica de utilizao desta macro :
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.4. GETCH() e GETCHE()
As funes getch() e getche() retornam o caracter pressionado. A funo getche() imprime o
caracter na tela antes de retorn-lo, enquanto que a funo getch() apenas retorna o caracter
sem imprimi-lo. Ambas so definidas no arquivo de cabealho conio.h, portanto, no pertencem
ao padro ANSI. A sintaxe destas funes similar a da macro getchar().
Pg.: 25/82
Faculdade de Computao
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.
A seguir apresentado um programa exemplo da funo gets().
# 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);
Esta funo reconhece os mesmos caracteres especiais de escape (backslash \) que a
funo printf(). Entretanto, uma chamada funo puts() requer menos esforo de
processamento que a mesma chamada para a funo printf(), uma vez que a primeira pode
enviar somente strings, enquanto que a segunda, alm de strings, pode enviar outros tipos de
dados, bem como fazer converso de formatos. Portanto, a funo puts() ocupa menos espao
de memria e executada mais rapidamente que a funo printf(). A seguir apresentado um
exemplo de sua utilizao.
#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 :
Prof. Luiz Gustavo Almeida Martins
Pg.: 26/82
Faculdade de Computao
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));
}
CDIGOS
Cdigo
\a
\b
\n
\f
\r
\t
\v
\0
\\
\
\
\?
\N
\xN
DE
BARRA INVERTIDA
Significado
Sinal sonoro ("beep")
Retrocesso (backspace)
Mudana de linha (new line)
Avano de pgina (form feed)
Retorno do carro da impressora
Tabulao horizontal (tab)
Tabulao vertical
Nulo (0 em decimal)
Impresso da barra invertida
Impresso das aspas
Impresso do apstrofo
Impresso do ponto de interrogao
Constante octal (N o valor ASCII em octal)
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().
Pg.: 27/82
Faculdade de Computao
Pg.: 28/82
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;
Prof. Luiz Gustavo Almeida Martins
Pg.: 29/82
Faculdade de Computao
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
Na linguagem C, assim como j apresentado em algoritmos, possvel utilizar-se de
aninhamentos de comandos if. Isto , um comando if pode conter outros comandos ifs dentro de
seus blocos de comandos.
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.);
}
}
8.1.3. CLUSULA ELSE IF
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)
{
Prof. Luiz Gustavo Almeida Martins
Pg.: 30/82
Faculdade de Computao
else if (condioN)
{
bloco de comandos N; /* Executado quando a condioN for verdadeira */
}
else
{
bloco de comandos (N+1);
/* Executado quando todas as condies
anteriores forem falsas */
}
Nesta estrutura, as expresses condicionais so avaliadas em ordem e, se alguma delas for
verdadeira, o bloco de comandos correspondente ser executado e o encadeamento ser
finalizado (nenhuma outra condio da estrutura ser testada).
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.);
}
8.2. COMANDO SWITCH
Em um comando switch, o computador compara uma varivel sucessivamente contra uma
lista de constantes. Caso uma das constantes descrita nas clusulas case seja igual ao valor
Prof. Luiz Gustavo Almeida Martins
Pg.: 31/82
Faculdade de Computao
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.
O comando switch freqentemente utilizado na construo de menus, como ilustrado no
exemplo a seguir:
/* construcao de menu utilizando o comando switch - case */
#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')
Prof. Luiz Gustavo Almeida Martins
Pg.: 32/82
Faculdade de Computao
{
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.
Pg.: 33/82
Faculdade de Computao
Pg.: 34/82
Faculdade de Computao
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.
9.3. COMANDO FOR
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 :
for (inicializao ; condio ; incremento)
{
bloco_comandos;
}
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 (;).
A seqncia de operao do comando for a seguinte:
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;
Prof. Luiz Gustavo Almeida Martins
Pg.: 35/82
Faculdade de Computao
Pg.: 36/82
Faculdade de Computao
COMANDO BREAK
Pg.: 37/82
Faculdade de Computao
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).
10.2.
COMANDO RETURN
COMANDO CONTINUE
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>
Prof. Luiz Gustavo Almeida Martins
Pg.: 38/82
Faculdade de Computao
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().
10.4.
COMANDO GOTO
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.
A sintaxe geral do comando :
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.
No exemplo apresentado abaixo, se for digitado o nmero 3, o controle do programa ser
desviado para o label sada.
# include <stdio.h>
Prof. Luiz Gustavo Almeida Martins
Pg.: 39/82
Faculdade de Computao
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);
}
10.5.
FUNO EXIT()
A funo exit() encontrada na biblioteca padro do C. Sua utilizao limitada, pois provoca o
encerramento imediato de um programa, retornando ao sistema operacional.
Abaixo apresentado um programa que utiliza esta funo.
# 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);
}
}
11. ESTRUTURAS DE DADOS COMPOSTAS
11.1.
VETORES E MATRIZES
Pg.: 40/82
Faculdade de Computao
sendo:
Pg.: 41/82
Faculdade de Computao
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.
Abaixo dado um exemplo do uso de matriz. Nele, a estrutura MATRIZ preenchida,
seqencialmente, com os nmeros de 1 a 200.
#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.
Um exemplo tradicional um registro de folha de pagamento, onde um empregado descrito por
um conjunto de atributos, tais como: nome, endereo, nmero do seguro social, salrio, etc.
Alguns desses atributos tambm podem ser uma estrutura, tal como o endereo que possui os
atributos: logradouro, nmero, bairro, complemento, etc.
Pg.: 42/82
Faculdade de Computao
DECLARAO DE REGISTROS
Na linguagem C, um registro declarado atravs da palavra reservada struct. Esta palavra-chave
introduz uma lista de declaraes entre chaves. Um identificador opcional chamado nome da
estrutura (structure tag) pode seguir a palavra struct. Ele d nome ao registro e pode ser
usado como uma abreviatura para a declarao do registro em outras partes do programa.
As variveis de um registro so chamadas de membros do registro. Um membro pode ter o
mesmo nome de uma outra varivel do programa, desde que esta no pertena ao mesmo
registro. Entretanto, por uma questo de clareza, esta prtica no recomendada.
A seguir apresentado um exemplo com a declarao do registro folha de pagamento.
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.
Uma declarao de registro sem a lista de variveis s descreve o formato da estrutura,
mas no aloca rea de armazenamento. Se o registro tem nome (identificador), este pode ser
usado depois, na definio de ocorrncias do registro, como no exemplo:
struct FOLHA FOLHA_PAGTO;
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).
Prof. Luiz Gustavo Almeida Martins
Pg.: 43/82
Faculdade de Computao
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:
struct FOLHA FOLHA_PAGTO = {Jos,{Rua x, 13, Pacaembu, Casa B }, 123, 900.00};
MANIPULAO DE MEMBROS DE UM REGISTRO
Um membro de registro referenciado em uma expresso atravs da construo:
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:
printf (O nome do funcionrio %s, FOLHA_PAGTO.NOME);
DESCONTO_IR = FOLHA_PAGTO.SALARIO * 0.25;
scanf (%li, FOLHA_PAGTO.NRO_SEG);
printf (O bairro do funcionrio %s, FOLHA_PAGTO.ENDERECO.BAIRRO);
Como pode ser observado no ltimo exemplo, a referncia de membros de registros
aninhados feita atravs da identificao ordenada dos registros envolvidos, do mais
externo at o mais interno, todos separados por ponto; seguida pelo nome do membro do
registro mais interno que ser utilizado.
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;
Este comando copia o registro2 para o registro1, campo a campo.
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:
struct nome_registro nome_variavel [tamanho1] [tamanho2]... [tamanhoN];
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.
Prof. Luiz Gustavo Almeida Martins
Pg.: 44/82
11.3.
Faculdade de Computao
COMANDO TYPEDEF
Pg.: 45/82
Faculdade de Computao
12. STRINGS
12.1.
MANIPULAO DE 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 :
char nome_da_string [tamanho];
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.
12.2.
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()
Pg.: 46/82
Faculdade de Computao
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:
printf (string); /* Este comando simplesmente imprimir a string.*/
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);
A seguir apresentamos um exemplo de uso da funo strcpy():
#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);
Abaixo segue um exemplo da utilizao da funo strcat():
#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 */
Prof. Luiz Gustavo Almeida Martins
Pg.: 47/82
Faculdade de Computao
printf ("\n\n%s",str2);
return(0);
}
STRLEN()
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);
Um exemplo da sua utilizao apresentado a seguir:
#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.");
}
Prof. Luiz Gustavo Almeida Martins
Pg.: 48/82
Faculdade de Computao
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 :
var_int = atoi (string);
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 :
var_double = atof (string);
Um exemplo da sua utilizao apresentado a seguir:
#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);
}
Ambas as funes esto declaradas no arquivo de cabealho <stdlib.h>.
12.3.
VETORES DE STRINGS
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:
char nome_da_varivel [numero_de_strings][comprimento_das_strings];
Assim, para acessar um nica string, basta usar o primeiro ndice, como apresentado abaixo:
printf(%s, nome_da_varivel [ndice]);
Abaixo apresentado um programa exemplo que l 5 strings e as exibe na tela:
#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");
Prof. Luiz Gustavo Almeida Martins
Pg.: 49/82
Faculdade de Computao
for (count=0;count<5;count++)
printf ("%s\n",strings[count]);
}
13. FUNES
Funes so as estruturas que permitem ao usurio separar seus programas em blocos de
construo. A principal caracterstica das funes o fato de, uma vez escritas e depuradas
(testadas), elas poderem ser reutilizadas quantas vezes forem necessrias, inclusive por outros
programas.
Na linguagem C, uma funo tem a seguinte forma geral:
tipo_retorno nome_da_funo (declarao_parmetros)
{
corpo_da_funo
}
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.
A declarao_parmetros uma lista de nomes de variveis separadas por vrgulas que
recebem os valores dos argumentos quando a funo chamada. Ela possui a seguinte forma
geral:
tipo nome1, tipo nome2, ... , tipo nomeN
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.
O corpo_da_funo contm a declarao das variveis locais, bem como a seqncia de
comandos que sero executados, ou seja, o bloco de comandos da funo.
Quando se encontra um comando return, a funo encerrada imediatamente e, se algum
dado informado aps o return, seu valor retornado pela funo. importante lembrar que, o
valor de retorno fornecido tem que ser compatvel com o tipo de retorno declarado para a
funo. Uma funo pode ter mais de um comando return, entretanto, somente um ser
executado por chamada da funo. Isto se torna claro quando pensamos que a funo
terminada quando o programa chega ao primeiro comando return.
Abaixo esto dois exemplos de funes:
/* Programa-Exemplo 1 */
#include <stdio.h>
int square (int a)
{
return (a*a);
}
void main ()
{
Prof. Luiz Gustavo Almeida Martins
Pg.: 50/82
Faculdade de Computao
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.
13.1.
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:
void nome_da_funo (declarao_parmetros); /* Sem retorno */
tipo_retorno nome_da_funo (void);
/* Sem argumentos de entrada */
void nome_da_funo (void);
/* Sem argumentos formais e retorno */
Prof. Luiz Gustavo Almeida Martins
Pg.: 51/82
Faculdade de Computao
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.
13.2.
CHAMADA DE FUNO
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:
PASSAGEM DE PARMETROS POR VALOR
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.
O programa abaixo ilustra uma passagem de parmetros por valor
Pg.: 52/82
Faculdade de Computao
#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.
PASSAGEM DE PARMETROS POR REFERNCIA
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
Prof. Luiz Gustavo Almeida Martins
Pg.: 53/82
Faculdade de Computao
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.
Na concepo de uma funo recursiva, a primeira providncia definir um critrio de parada
para a recurso, ou seja, determinar quando a funo dever parar de chamar a si mesma. Isto
impede que a recurso se torne infinita e, consequentemente, o programa no tenha fim.
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.
13.4.
Pg.: 54/82
Faculdade de Computao
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:
1. Declarando-o como uma matriz;
Tipo_retorno nome_funo (tipo_matriz nome_matriz [dim1] [dim2]... [dimN]);
2. Especificando uma matriz sem dimenso;
Tipo_retorno nome_funo (tipo_matriz nome_matriz [] []... []);
3. Declarando-o como um ponteiro, o que o mais comum.
Tipo_retorno nome_funo (tipo_matriz *nome_matriz [dim2]... [dimN]);
Como exemplo, seja o vetor de inteiros matrx [50] o argumento de uma funo func(). Podemos
declarar func() das seguintes maneiras:
void func (int matrx[50]);
void func (int matrx[]);
void func (int *matrx);
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.
13.5.
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 :
tipo_retorno main (int argc,char *argv[]);
O argc (argument count) um inteiro e possui o nmero de palavras digitadas na linha de
comando. Seu valor sempre, pelo menos, igual a 1, pois o nome do programa contado
como sendo o primeiro argumento. Ele tem por finalidade indicar quantos elementos temos
em argv.
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>
Prof. Luiz Gustavo Almeida Martins
Pg.: 55/82
Faculdade de Computao
CONSIDERAES FINAIS
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.
Por fim, quando um programa deve apresentar um bom desempenho (performance) de
processamento, seria bom implement-lo sem nenhuma (ou com o mnimo de) chamadas a
funes, porque uma chamada a uma funo consome tempo e memria.
14. ARQUIVOS
O sistema de manipulao de arquivos do ANSI C composto por uma srie de funes
interelacionadas, cujos prottipos esto reunidos em stdio.h. Todas estas funes trabalham com
o conceito de "ponteiro de arquivo. Um ponteiro de arquivo aponta para uma estrutura que
contm informaes sobre o arquivo, como o local de um buffer, a posio do caracter corrente
no buffer, se o arquivo est sendo lido ou gravado e se foram encontrados erros ou o fim do
arquivo. Entratanto, o programador no presisa saber os detalhes desta estrutura, pois a sua
Pg.: 56/82
Faculdade de Computao
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;
14.1.
ARQUIVOS PR-DEFINIDOS
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).
Quando se comea a execuo de um programa, o sistema automaticamente abre alguns
arquivos pr-definidos:
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:
ch =getc(stdin);
putc(ch, stdprn);
14.2.
ABERTURA DE ARQUIVOS
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.
Pg.: 57/82
VALORES VLIDOS
Faculdade de Computao
PARA O
MODO
Modo
DE
ABERTURA
DE
ARQUIVOS
Significado
"r"
Abre um arquivo texto para leitura. O arquivo deve existir antes de ser aberto.
"w"
Abrir um arquivo texto para gravao. Se o arquivo no existir, ele ser criado. Se j
existir, o contedo anterior ser destrudo.
"a"
Abrir um arquivo texto para gravao. Se ele j existir, os dados sero adicionados no
fim do arquivo ("append"). Caso contrrio, um novo arquivo ser criado.
"r+"
Abre um arquivo texto para leitura e gravao. O arquivo deve existir e pode ser
modificado.
"w+"
Cria um arquivo texto para leitura e gravao. Se o arquivo existir, o contedo anterior
ser destrudo. Se no existir, ser criado.
"a+"
Abre um arquivo texto para gravao e leitura. Se o arquivo j existir, os dados sero
adicionados no fim do mesmo. Caso contrrio, um novo arquivo ser criado.
Acrescenta dados ou cria uma arquivo binrio para leitura e escrita (similar ao modo
"a+").
if (!fp)
printf ("Erro na abertura do arquivo.");
A condio !fp testa se o arquivo foi aberto com sucesso porque, no caso de um erro, a funo
fopen() retorna um ponteiro nulo (NULL).
OBS: A macro NULL definida em stdio.h como \0.
O padro ANSI determina que pelo menos 8 arquivos podem ser abertos simultaneamente.
Entretanto, a maioria dos compiladores permitem muito mais que isso.
14.3.
FECHAMENTO DE ARQUIVOS
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().
Prof. Luiz Gustavo Almeida Martins
Pg.: 58/82
Faculdade de Computao
Aps a abertura de um arquivo, possvel ler ou escrever nele utilizando as funes que sero
apresentadas a seguir.
ESCREVENDO CARACTERES EM ARQUIVOS
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:
putc (ch, fp);
fputc (ch, fp);
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)
{
Prof. Luiz Gustavo Almeida Martins
Pg.: 59/82
Faculdade de Computao
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.
LENDO CARACTERES DE ARQUIVOS
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:
var = getc (fp);
var = fgetc (fp);
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.
Pg.: 60/82
Faculdade de Computao
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:
fgets (str, tam, stdin);
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.
Pg.: 61/82
Faculdade de Computao
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:
Prof. Luiz Gustavo Almeida Martins
Pg.: 62/82
Faculdade de Computao
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 :
fwrite (buffer, num_bytes, cont, fp);
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.
O programa-exemplo abaixo escreve e, em seguida, l um double, um int e um long int em um
arquivo do disco. Note que, neste exemplo, foi utilizado o operador sizeof(). Ele retorna o
tamanho em bytes de uma varivel ou 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 */
Prof. Luiz Gustavo Almeida Martins
Pg.: 63/82
Faculdade de Computao
Pg.: 64/82
Faculdade de Computao
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 */
}
14.5.
REMOO DE ARQUIVOS
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);
Prof. Luiz Gustavo Almeida Martins
Pg.: 65/82
Faculdade de Computao
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);
}
}
14.6.
LIMPEZA DO BUFFER
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.
O exemplo abaixo ilustra o uso desta funo:
# 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);
}
Pg.: 66/82
14.7.
Faculdade de Computao
FUNES DE REPOSICIONAMENTO
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()
A funo rewind() reposiciona o indicador de posio do arquivo (ponteiro) para o incio do
arquivo especificado pelo ponteiro passado como argumento. Sua sintaxe :
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 :
fseek (fp, num_bytes, origem);
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.
NOMES
DAS
MACROS UTILIZADAS
Nome
SEEK_SET
SEEK_CUR
SEEK_END
Valor
0
1
2
NA
FUNO FSEEK()
Significado
Incio do arquivo
Ponto atual no arquivo
Fim do arquivo
Pg.: 67/82
Faculdade de Computao
}
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);
}
14.8.
OUTRAS FUNES
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);
Deste modo , podemos reescrever o programa anterior:
#include <stdio.h>
#include <stdlib.h>
Prof. Luiz Gustavo Almeida Martins
Pg.: 68/82
Faculdade de Computao
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;
Prof. Luiz Gustavo Almeida Martins
Pg.: 69/82
Faculdade de Computao
Pg.: 70/82
Faculdade de Computao
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().
Pg.: 71/82
Faculdade de Computao
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.
Na verdade, existe uma associao forte entre vetores e ponteiros. Na declarao:
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:
v+0
v+1
v+2
...
v+N
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).
No C a declarao de um ponteiro feita pela seguinte sintaxe:
Tipo_dado *nome_variavel[tamanho1][tamanho2]...[tamanhoN];
A seguir apresentado o exemplo de vetores reescrito para utilizar ponteiro.
#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);
Prof. Luiz Gustavo Almeida Martins
Pg.: 72/82
Faculdade de Computao
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.
15.1.
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.
No C quando declaramos ponteiros ns informamos ao compilador para que tipo de varivel
vamos apont-lo. Um ponteiro int aponta para um inteiro, isto , guarda o endereo de um inteiro.
15.2.
Pg.: 73/82
Faculdade de Computao
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!
Para atribuir um valor a um ponteiro recm-criado poderamos igual-lo a um valor de memria.
Mas, como saber a posio na memria de uma varivel do nosso programa? Seria muito difcil
saber o endereo de cada varivel que usamos, mesmo porque estes endereos so
determinados pelo compilador na hora da compilao e realocados na execuo. Podemos ento
deixar que o compilador faa este trabalho por ns. Para saber o endereo de uma varivel basta
usar o operador &. Veja o exemplo:
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.
Uma observao importante: apesar do smbolo ser o mesmo, o operador * (multiplicao) no
o mesmo operador que o * (referncia de ponteiros). Para comear o primeiro binrio, e o
segundo unrio pr-fixado.
Aqui vo dois exemplos de usos simples de ponteiros:
#include <stdio.h>
/* Exemplo 1 */
int main ()
{
int num,valor;
int *p;
num=55;
p=# /* Pega o endereco de num */
valor=*p;
/* Valor e igualado a num de uma maneira indireta */
printf ("\n\n%d\n",valor);
printf ("Endereco para onde o ponteiro aponta: %p\n",p);
Prof. Luiz Gustavo Almeida Martins
Pg.: 74/82
Faculdade de Computao
#include <stdio.h>
/* Exemplo 2 */
int main ()
{
int num,*p;
num=55;
p=# /* Pega o endereco de num */
printf ("\nValor inicial: %d\n",num);
*p=100; /* Muda o valor de num de uma maneira indireta */
printf ("\nValor final: %d\n",num);
return(0);
}
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.
As prximas operaes, tambm muito usadas, so o incremento e o decremento. Quando
incrementamos um ponteiro ele passa a apontar para o prximo valor do mesmo tipo para o qual o
ponteiro aponta. Isto , se temos um ponteiro para um inteiro e o incrementamos ele passa a
apontar para o prximo inteiro. Esta mais uma razo pela qual o compilador precisa saber o tipo
de um ponteiro: se voc incrementa um ponteiro char * ele anda 1 byte na memria e se voc
incrementa um ponteiro double* ele anda 8 bytes na memria. O decremento funciona
semelhantemente. Supondo que p um ponteiro, as operaes so escritas como:
p++;
p--;
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;
E se voc quiser usar o contedo do ponteiro 15 posies adiante:
*(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,
Prof. Luiz Gustavo Almeida Martins
Pg.: 75/82
Faculdade de Computao
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.
15.3.
PONTEIROS E VETORES
Pg.: 76/82
Faculdade de Computao
{
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);
}
Podemos reescrev-lo usando ponteiros:
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;
/* as operacoes a seguir sao invalidas */
vetor = vetor + 2; /* ERRADO: vetor nao e' variavel */
vetor++;
/* ERRADO: vetor nao e' variavel */
vetor = ponteiro;
/* ERRADO: vetor nao e' variavel */
Teste as operaes acima no seu compilador. Ele 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.
/* as operacoes abaixo sao validas */
Prof. Luiz Gustavo Almeida Martins
Pg.: 77/82
Faculdade de Computao
ponteiro = vetor;
/* CERTO: ponteiro e' variavel */
ponteiro = vetor+2; /* CERTO: ponteiro e' variavel */
PONTEIROS COMO VETORES
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);
}
Podemos ver que p[2] equivale a *(p+2).
ENDEREOS DE ELEMENTOS DE VETORES
Nesta seo vamos apenas ressaltar que a notao
&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:
int *pmatrx [10];
No caso acima, pmatrx um vetor que armazena 10 ponteiros para inteiros.
15.4.
INICIALIZANDO PONTEIROS
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 (string,"String constante.");
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().
Prof. Luiz Gustavo Almeida Martins
Pg.: 78/82
Faculdade de Computao
O que isto tem a ver com a inicializao de ponteiros? que, para uma string que vamos usar
vrias vezes, podemos fazer:
char *str1="String constante.";
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.
15.5.
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;
Algumas consideraes: **nome_da_varivel o contedo final da varivel apontada;
*nome_da_varivel o contedo do ponteiro intermedirio.
No C, podemos declarar vrios nveis de aninhamentos de ponteiros para ponteiros, ou seja,
ponteiros para ponteiros; ponteiros para ponteiros para ponteiros; e assim por diante. Para fazer
isto basta aumentar o nmero de asteriscos na declaraco.
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);
}
15.6.
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:
int main () /* Errado - Nao Execute */
{
int x,*p;
x=13;
*p=x;
return(0);
Prof. Luiz Gustavo Almeida Martins
Pg.: 79/82
Faculdade de Computao
}
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).
16. ALOCAO DINMICA
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.
16.1.
FUNO MALLOC()
A funo malloc() serve para alocar um espao de memria. Sua sintaxe geral :
ptr = malloc (num);
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).
Veja um exemplo de alocao dinmica com malloc():
#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;
}
No exemplo acima, alocada memria suficiente para se armazenar a nmeros inteiros. O
operador sizeof() retorna o nmero de bytes de um inteiro. Ele util para se saber o tamanho de
tipos. O ponteiro void* que malloc() retorna convertido para um int* pelo operador cast e
Prof. Luiz Gustavo Almeida Martins
Pg.: 80/82
Faculdade de Computao
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)].
16.2.
FUNO CALLOC()
A funo calloc() tambm serve para alocar memria, mas sua sintaxe um pouco diferente:
ptr = calloc (num, size);
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;
}
16.3.
FUNO REALLOC()
A funo realloc() serve para redefinir a alocao de memria. Sua sintaxe geral :
ptr = realloc (ptr, num);
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.
No programa abaixo, a alocao existente no ponteiro p, realizada pela da funo malloc(),
alterada atravs da funo realloc().
#include <stdio.h>
Prof. Luiz Gustavo Almeida Martins
Pg.: 81/82
Faculdade de Computao
FUNO FREE()
Pg.: 82/82