Escolar Documentos
Profissional Documentos
Cultura Documentos
NotasAula PDF
NotasAula PDF
Maio 2008
Parte I
Programao Bsica em C
Estas notas de aula apresentam os conceitos bsicos da Linguagem C e se prope a abordar apenas o que
importante para a compreenso bsica de programas de computadores.
1
1 Programas C
Um programa C consiste de uma ou mais partes chamadas funes. Um programa em C consiste de pelo
menos uma funo chamada main. Esta funo marca o ponto de incio de execuo do programa.
Programas C tem a seguinte estrutura geral:
#include <stdio.h>
definio de constantes
funes
int main()
{
declarao de variveis
....
sentenas
....
}
Simples:
x = 3;
Composta:
{
i = 3;
printf("%d\n", i);
i = i + 1;
}
1.2 Variveis em C
Uma varivel uma informao qe voc pode usar dentro de um programa C . Esta informao est associada
com um lugar especfico da memria (isso feito pelo compilador). O nome da varivel e o endereo da
memria onde a informao est armazenada esto associados. O nome e o endereo no mudam. Mas, o
valor da informao pode mudar (o valor do que est dentro da caixa pode mudar, embora o tipo seja sempre
o mesmo). Cada varivel tem um tipo associado. Alguns tipos de variveis que discutiremos incluem int,
char e float.
Cada varivel usa uma determinada quantidade de armazenamento em memria. A maneira como sabe-
mos quantos bytes so utilizados pelo tipo da varivel. Variveis do mesmo tipo utilizam o mesmo nmero
de bytes, no interessando qual o valor que a varivel armazena.
2
Um dos tipos utilizados para armazanar nmeros o int. Ele usado para armazenar nmeros inteiros.
Outro tipo o char, usado para armazenar caracteres. Um caracter um smbolo (uma letra do alfabeto,
um dgito, um smbolo de pontuao, etc). Um char armazenado em 1 byte de memria. Cada caracter
associado com um valor entre 0 e 255. O compilador C faz a traduo para voc, portanto voc no precisa
saber estes nmeros. Em C , um caracter representado entre apstrofes (). Por exemplo, C, a, 5, $.
Note que 5 um caracter, e no o inteiro 5.
qualquer sequncia de letras, digitos, e _, MAS DEVE COMEAR com uma letra ou com _.
Por exemplo, hora_inicio, tempo, var1 so nomes de variveis vlidos, enquanto 3horas, total$ e
azul-claro no so nomes vlidos;
Maisculas 6= Minsculas;
sempre uma boa idia ter certas regras (para voc mesmo) para nomear variveis para tornar o pro-
grama mais legvel:
Pode-se usar letras maisculas ou _ para juntar palavras. Por exemplo, horaInicio ou hora_inicio.
Use o que voc preferir, mas SEJA CONSISTENTE em sua escolha.
3
auto break case char const continue
default do double else enum extern
float for goto if int long
main register return short signed sizeof
static struct switch typedef union unsigned
void volatile while
int main()
{
int pera;
char qualidade;
float peso;
pera = 3;
qualidade = A;
peso = 0.653;
...
}
Quando variveis so definidas, elas no possuem valores ainda. Ns damos valores s variveis usando
o operador de atribuio (=). Variveis tambm podem ser inicializadas para conter valores quando so
definidas. Usando esta forma, o program acima ficaria:
int main()
{
int pera = 3;
char qualidade = A;
float peso = 0.653;
...
}
um tipo: diz quantos bytes a varivel ocupa, e como ela deve ser interpretada.
um nome: um identificador.
Em C , nomes de variveis devem ser declarados antes de serem usados. Se no for declarado, ocorrer
um erro de compilao.
Devem ser dados valores s variveis antes que sejam utilizadas. Se voc tentar utilizar a varivel
antes de especificar o seu valor, voc obter lixo (o que quer que esteja armazenado no endereo da
varivel na memria quando o programa comea sua execuo), culminando com falha na execuo
do programa.
1.4 Constantes
Em C , alm de variveis, ns podemos usar tambm nmeros ou caracteres cujos valores no mudam. Eles
so chamados de constantes. Constantes no so associados a lugares na memria.
Assim como variveis, constantes tambm tm tipos. Uma constante pode ser do tipo int, char, etc.
Voc nao tem que declarar constantes, e pode utiliz-las diretamente (o compilador reconhece o tipo pela
maneira que so escritos). Por exemplo, 2 do tipo int, e 2.0 do tipo double. Por conveno, todas as
constantes reais so do tipo double.
5
1.7 Exibindo informaes na tela: printf()
printf() pode ser utilizado para imprimir mensagens e valores em uma variedade de formatos. Por enquanto,
printf() melhor descrito atravs de exemplos.
#include <stdio.h>
#define PRECO 1.99
int main()
{
int pera = 3;
char qualidade = A;
float peso = 2.5;
A linha #define PRECO 1.99 no incio do programa define uma macro. Ou seja, definimos que
PRECO um sinnimo para 1.99 e, portanto, toda ocorrncia de PRECO no programa substitudo por
1.99 antes que ele seja compilado.
Ns tambm podemos especificar o tamanho utilizado para impresso da seguinte forma:
#include <stdio.h>
int main()
{
printf(Exemplo errado: %d\n, 3.14159);
}
#include <stdio.h>
int main()
{
int idade;
Este programa mostrar no monitor: Entre sua idade: e aguardar que um nmero seja digitado e a
tecla ENTER. Depois disso, a varivel idade conter o valor digitado pelo usurio.
Assim como com o printf(), o primeiro argumento o especificador de formato. Os prximos argumen-
tos correspondem a o que est sendo especificado pelo primeiro argumento.
Note o & precedendo a varivel idade. Simplesmente lembre-se que voc geralmente precisar colocar
um & precedendo nomes de variveis em um scanf(). Voc sempre precisar us-lo antes de variveis do
tipo primrio como os discutidos at este momento (int, char, float, e suas verses long e unsigned).
Mais de um valor pode ser lido por um mesmo scanf(). Considere o seguinte exemplo:
#include <stdio.h>
int main()
{
int dia, mes, ano;
Este exemplo funciona exatamente como o exemplo anterior. Um nico scanf() l os 3 nmeros quando
estes nmeros so separados por espaos (espaos em branco, tabulao, novas linhas). Ento voc pode
teclar ENTER depois de cada nmero, ou colocar espaos ou tabulaes entre os nmeros. Os espaos so
ignorados pelo scanf(). Os brancos na especificao de formato do scanf(), %d %d %d so simplesmente
para facilitar a leitura do programa, e no tem nada a ver com os espaos ignorados pelo scanf(). Se tivsse-
mos escrito %d%d%d, o scanf() funcionaria da mesma forma. Os espaos em branco simplesmente so
necessrios para saber quando termina um nmero e comea o outro.
Porm se o scanf() estiver lendo caracteres (%c), os espaos no so ignorados, j que espaos so
caracteres vlidos na linguagem. Por exemplo, o cdigo ASCII para espao em branco e 32.
/* Definir variaveis */
int Raio;
float Perim, Area, PI;
PI = 3.14159;
/* Exibir Resultados */
Escreva("O perimetro da circunferencia de raio", Raio, "eh", Perim);
Escreva("e a area eh ",Area);
/* Terminar Programa */
FIM_ALGORITMO PERIMETRO_AREA
Programa em C
int main()
{
/* Definir variaveis */
int Raio;
float Perim, Area;
/* Exibir Resultados */
printf("O perimetro da circunferencia de raio %d eh %.2f \n", Raio, Peri
printf("e a area eh %.2f", Area);
}
9
2 Operaes Aritmticas e Expresses.
Operaes Relacionais.
2.1 Operaes Aritmticas
Em C , ns podemos executar operaes aritmticas usando variveis e constantes. Algumas operaes mais
comuns so:
+ adio
- subtrao
* multiplicao
/ diviso
% resto (mdulo)
Estas operaes podem ser usadas como mostram os exemplos abaixo, assumindo que as variveis ne-
cessrias j esto declaradas:
i = i + 1;
Esta operao usada quando queremos encontrar o resto da diviso de dois inteiros. Por exemplo, 22
dividido por 5 4, com resto 2 (4 5 + 2 = 22).
Em C , a expresso 22 % 5 ter valor 2.
Note que % s pode ser utilizados entre dois inteiros. Usando ele com um operando do tipo float
causa um erro de compilao (como em 22.3 % 5).
Expresses aritmticas podem ser usadas na maior parte dos lugares em que uma varivel pode ser usada.
O exemplo seguinte vlido:
int raio = 3 * 5 + 1;
int yucky + 2 = 5;
Em C , h operadores que podem ser usados para comparar expresses: os operadores relacionais.
H seis operadores relacionais em C :
== igual a
!= no igual a (6=)
int main()
{
int idade;
idade = 17;
printf("Pode tirar carteira de motorista? %d\n", idade >= 18);
idade = 35;
printf("Pode tirar carteira de motorista? %d\n", idade >= 18);
}
11
2.2.1 Precedncia dos operadores relacionais
Operadores aritmticos tem precedncia maior que os operadores relacionais. Por exemplo, a expresso
3 + 5 < 6 * 2 o mesmo que (3 + 5) < (6 * 2).
Se por alguma razo voc quer que o resultado do uma operao relacional em uma expresso aritmtica,
necessrio usar parntesis. Por exemplo, a expresso score + (score == 0) ser sempre igual ao
valor de score, exceto quando o valor de score seja 0. Neste caso, o valor da expresso 1 (porque
(score == 0) igual a 1).
Uma observao sobre valores booleanos embora voc possa assumir que o valor de uma operao
relacional 0 ou 1 em C , qualquer valor diferente de zero considerado verdadeiro. Falaremos sobre
isso mais tarde durante o curso.
#include <stdio.h>
int main() {
int score = 5;
#include <stdio.h>
int main() {
int n1, n2, n3;
a * b / c + 30 >= 45 + d * 3 ++e == 10
12
#include <stdio.h>
int main() {
int numero;
Exemplo 2: escreva um programa que leia 3 nmeros inteiros e calcule a soma, mdia, e produto.
#include <stdio.h>
int main() {
int n1, n2, n3;
int soma;
Operador Associatividade
13
3 Expresses como valores
Em C , todas as expresses so avaliadas. O resultado da avaliao um valor e pode ser usado em quaisquer
lugares.
i = j = k = 0;
int quadrado, n = 2;
14
causa no apenas que o valor 4 seja impresso, como a avaliao da expresso relacional dentro do printf()
faz com que o nmero 4 seja copiado para o endereo de memria associado com a varivel quadrado.
Note que necessrio usar parnteses em quadrado = n * n j que = tem menor precedncia que o
operador relacional <.
Agora compare o exemplo anterior com o prximo, no qual o valor 4 impresso, mas sem nenhum
efeito colateral:
int quadrado, n = 2;
Note que agora no h necessidade de parnteses para a expresso n * n porque * tem maior prece-
dncia que o operador relacional <.
15
4 Ordem sequencial de execuo de sentenas
o comando condicional: if and if - else
A execuo de um programa C comea com a funo main(). Em todos os exemplos que vimos at este
momento, sentenas so executadas sequencialmente. A ordem sequencial de execuo de senteas pode
ser alterada se certas condies forem satisfeitas durante a execuo do programa. Isto chamado desvio
condicional.
Todas as linguagens de programao oferecem comandos para o desvio condicional. O mais simples a
sentea if. Em C , ele tem o formato:
if (expressao)
sentenca
Quando uma sentena if encontrada em um programa,
2. Se o valor da expresso de teste for DIFERENTE de zero, a sentena que segue a expresso de teste
executada.
Figura 1: O comando if
Considere o seguinte exemplo que converte uma frao digitada pelo usurio (numerador e denomina-
dor) em decimal e imprime o resultado:
#include <stdio.h>
main(void)
{
int a, b;
Exemplo 1:
#include <stdio.h>
main(void)
{
int a, b;
if (b != 0)
printf("A fracao em decimal eh %f\n", 1.0 * a / b);
}
Exemplo 2: Programa que l dois nmeros e ordena o par caso o primeiro nmero digitado for maior que
o segundo.
#include <stdio.h>
main() {
int num1, num2, aux;
O programa do Exemplo 1 acima ficaria ainda melhor se ao invs de no fazer nada no caso do denomi-
nador ser zero, imprimirmos uma mensagem de erro ao usurio, explicando o que h de errado.
A sentena em C que permite fazermos isso o if - else. O formato do if-else :
if (expressao)
sentenca1
else
sentenca2
17
Figura 2: O comando if-else
Primeiro, a expressao (que usualmente chamamos de condio) avaliada. Caso a condio seja ver-
dadeira (o que equivalente a dizer que o valor diferente de zero), entao a sentenca1 executada. Caso
contrrio, a sentenca2 executada.
Note que uma sentena pode ser simples ou composta. Se voc quiser agrupar diversas sentenas para
serem executadas, voc pode coloc-las entre chaves ({ e }).
Por hora, vamos continuar com nosso exemplo simples e torn-lo mais explicativo:
Exemplo 3:
#include <stdio.h>
main(void)
{
int a, b;
if (b != 0)
printf("A fracao decimal e %f\n", 1.0 * a / b);
else
printf("Erro: denominador zero!\n");
}
Exemplo 4: Considere agora o exemplo j visto que pede que um usurio entre com um nmero e verifique
se o nmero par. Porm agora, queremos que o programa imprima o numero e par ou o numero e
impar.
#include <stdio.h>
main(void)
{
int num;
18
/* obtem um numero do usuario */
printf("Entre com um inteiro: ");
scanf("%d", &num);
if (saldo == 1)
printf("Voce esta quebrado! \n");
else
printf("Seu saldo e %d\n", saldo);
Como a sentena saldo = 2000 inicializa o valor da varivel saldo com 2000, a expresso
saldo == 1 tem valor 0. Portanto, a sentea que segue o else ser executada, e a mensagem
ser impressa.
Agora, suponha que, devido a um erro, voc tenha colocado = ao invs de ==:
if (saldo = 1)
printf("Voce esta quebrado! \n");
else
printf("Seu saldo e %d\n", saldo);
Agora, a expresso saldo = 1 tem valor 1. Portanto, a sentena que segue o if ser executada, e
a mensagem
ser impressa. Alm disso, a atribuio causar um efeito colateral, e alterar o valor de saldo para 1.
Tal uso do operador de atribuio no ilegal, e no ser detectado pelo compilador como erro. Portanto,
tome cuidado com o uso de atribuio no lugar de igualdade. Tal erro muito comum, e no fcil de achar.
Como regra geral, NO utilize atribuies dentro de outras sentenas.
19
5 Aninhando senteas if e if-else
Como era de se esperar, possvel colocar uma sentena condicional dentro de outra. Por exemplo, se
quisermos imprimir uma mensagem apropriada caso um nmero seja positivo ou negativo e par ou mpar,
ns poderamos escrever o seguinte:
#include <stdio.h>
main(void)
{
int num;
Regra de associao: Um else est associado com a ltima ocorrncia do if sem else.
O exemplo seguinte est errado porque associa o else ao if "incorreto":
#include <stdio.h>
main(void)
{
int num;
20
/* Imprime uma mensagem dizendo se o numero de peras e 0 ou 1
(*** isto esta errado !! ***) */
if (num != 0)
if (num == 1)
printf("Voce tem uma pera.\n");
else
printf("Voce nao tem nenhuma pera.\n");
}
Neste exemplo, o if tem o seguinte significado, segundo a regra de associao:
#include <stdio.h>
main(void)
{
int num;
main(void)
{
int num;
21
6 Operadores Lgicos
Todos os programas at agora consideraram if com condies de teste simples. Alguns exemplos de testes
simples: b != 0, contador <= 5. Estas expresses testam uma condio. Portanto, quando mais de
uma condio precisa ser testada, precisamos usar sentenas if e if-else aninhadas.
A linguagem C , assim como a maioria das linguagens de programao de alto nvel suportam opera-
dores lgicos que podem ser usados para criar operaes lgicas mais complexas, combinando condies
simples. O valor de uma expresso lgica ou VERDADEIRO ou FALSO. Lembre que no h constantes
lgicas VERDADEIRO e FALSO em C ; em expresses lgicas 0 interpretado como FALSO, e qualquer
valor diferente de zero interpretado como VERDADEIRO.
Os operadores lgicos so
! NO lgico, operao de negao (operador unrio)
&& E lgico, conjuno (operador binrio)
|| OU lgico, disjuno (operador binrio).
Por exemplo, se quisermos testar se um nmero num positivo e par, e imprimir uma mensagem como
no exemplo anterior, podemos escrever:
if (num >= 0)
if (num % 2 == 0)
printf("Numero par nao negativo.\n");
Os dois operadores binrios operam sobre duas expresses lgicas e tem o valor 1 (verdadeiro) or 0
(falso). Os exemplos abaixo mostram o seu uso:
a==0 && b==0 (verdadeiro se ambos a == 0 e b == 0, portanto se a e b so 0)
a==0 || b==0 (verdadeiro se pelo menos uma das variveis a or b for 0)
Uma expresso usando && verdadeira somente se ambos os operadores forem verdadeiros (no
zero).
Uma expresso usando || falsa somente se ambos os operadores forem falsos (zero).
22
expr1 expr2 expr1 && expr2 expr1 || expr2
verdadeiro verdadeiro verdadeiro verdadeiro
verdadeiro f also f also verdadeiro
f also verdadeiro f also verdadeiro
f also f also f also f also
Como a precedncia dos operadores lgicos menor que a dos operadores relacionais, no necessrio
usar parnteses em expresses como:
Operador Associatividade
No prximo exemplo, o programa verifica se as trs variveis lado1, lado2, e lado3, podem ser
lados de um tringulo reto. Ns usamos o fato que os trs valores devem ser positivos, e que o quadrado de
um dos lados deve ser igual a soma dos quadrados dos outros lados (Teorema de Pitgoras) para determinar
se o tringulo reto.
23
#include <stdio.h>
main() {
7 Exemplos
7.1 IF - ELSE
Assuma as seguintes declaraoes de variveis:
int x = 4;
int y = 8;
1. if (y = 8)
if (x = 5)
printf( "a " );
else
printf( "b " );
printf( "c " );
printf( "d\n" );
==> a c d
24
2. mude = para ==
==> b c d
Assuma x = 5 e y = 8
(a) a
(b) a d
Assuma x = 5 e y = 7
(a) b c d
1. Assuma x = 5 e y = 8.
if (x == 5 && y == 8)
printf( "a\n" );
else
printf( "b\n" ); ==> a
2. Assuma x = 4 e y = 8.
if (x == 5 || y == 8)
printf( "a\n" );
else
printf( "b\n" ); ==> a
if (x == 5 || y == 8 && z == 10)
equiv.
if (x == 5 || (y == 8 && z == 10))
25
8 A construo else-if
Embora ela no seja um tipo diferente de sentena, a seguinte construo bastante comum para programar
decises entre diversas alternativas:
if (expressao1 )
sentenca1
else if (expressao2 )
sentenca2
else if (expressao3 )
sentenca3
..
.
else if (expressaon1 )
sentencan1
else
sentencan
As expresses lgicas so avaliadas em ordem, comeando com a expressao1 . Se uma das expres-
ses for verdadeira, a sentena associada ser executada. Se nenhuma for verdadeira, ento a sentena,
sentencan , do ltimo else ser executada como opo default. Se a opo default no for necessria,
ento a parte
else
sentencan
pode ser removida.
Exemplo 9: O seguinte exemplo mostra um else-if de trs opes. O programa l dois nmeros e diz
se eles so iguais ou se o primeiro nmero menor ou maior que o segundo.
#include <stdio.h>
main(void)
{
int num1, num2;
No programa acima, se (num1 == num2) for verdadeiro, ento os nmeros so iguais. Seno,
verificado se (num1 < num2). Se esta condio for verdadeira, ento o primeiro nmero menor. Se
isso no for verdadeiro, ento a nica opo restante que o primeiro nmero maior.
Exemplo 10: Este programa l um nmero, um operador e um segundo nmero e realiza a operao
correspondente entre os operandos dados.
#include <stdio.h>
main(void)
{
float num1, num2;
char op;
28
9 A sentena switch
A sentena switch outra maneira de fazer decises mltiplas. Ele pode ser usado para testar se uma dada
expresso igual a um valor constante e, dependendo do valor, tomar determinadas aes.
O formato da sentena switch :
switch (expressao) {
case expressao-constante 1:
sentencas 1
case expressao-constante 2:
sentencas 2
..
.
default:
sentencas n
}
A sentena switch primeiro avalia a expresso. Se o valor da expresso for igual a uma das expresses
constantes, as sentenas que seguem o case so executados. Se o valor da expresso no for igual a
nenhuma das constantes, as sentenas que seguem default so executadas.
As sentenas que seguem o case so simplesmente uma lista de sentenas. Esta lista pode conter mais
de uma sentena e no necessrio coloc-las entre chaves ({ e }). A lista de sentenas tambm pode ser
vazia, isto , voc pode no colocar nenhuma sentena seguindo o case.
Tambm no obrigatrio colocar o default. S o use quando for necessrio.
Note no diagrama acima que TODAS as sentenas que seguem a constante com o valor igual ao da
expresso sero executados. Para que se execute APENAS as sentenas que seguem o case que seja igual
ao valor da expresso precisamos usar a sentena break, que veremos em seguida.
10 A sentena break
O break faz com que todas as sentenas que o seguem dentro da mesma sentena switch sejam ignora-
dos. Ou seja, colocando a sentena break no final de uma sentena case faz com que as sentenas que
seguem os cases subsequentes no sejam executadas. Em geral, este o comportamento desejado quando
se usa o switch, e cases sem o break no final so de pouca utilidade. Portanto, o uso de sentenas
case sem o break devem ser evitados e quando utilizados devem ser comentados ao lado com algo como
/* continua proxima sentenca - sem break */.
Com a sentena break o diagrama de fluxo fica:
29
Note a similaridade com o diagrama da sentena else-if e a diferena com o diagrama da sentena
switch acima.
O prximo programa tem a mesma funo de calculadora do programa anterior, porm utilizando a
sentena switch.
Exemplo 11:
#include <stdio.h>
main(void)
{
float num1, num2;
char op;
switch (op) {
case +:
printf(" = %.2f", num1 + num2);
break;
case -:
printf(" = %.2f", num1 - num2);
break;
case *:
printf(" = %.2f", num1 * num2);
break;
case /:
printf(" = %.2f", num1 / num2);
break;
default:
printf(" Operador invalido.");
break;
}
printf("\n");
30
}
Como mencionado anteriormente, possvel no colocar nenhuma sentena seguindo um case. Isso
til quando diversas sentenas case (diversas constantes) tm a mesma ao.
Por exemplo, podemos modificar o programa acima para aceitar x e X para multiplicao e \ para
diviso. O programa fica ento:
#include <stdio.h>
main(void)
{
float num1, num2;
char op;
switch (op) {
case +:
printf(" = %.2f", num1 + num2);
break;
case -:
printf(" = %.2f", num1 - num2);
break;
case *:
case x:
case X:
printf(" = %.2f", num1 * num2);
break;
case /:
case \\:
printf(" = %.2f", num1 / num2);
break;
default:
printf(" Operador invalido.");
break;
}
printf("\n");
}
Exerccio 2: Ler mes e ano e imprimir o numero de dias do mes no ano digitado.
31
11 Funes
11.1 Funes: o que so e por que us-las
Quando queremos resolver um problema, em geral tentamos dividi-lo em subproblemas mais simples e re-
lativamente independentes, e resolvemos os problemas mais simples um a um. A linguagem C dispe de
construes (abstraes) que auxiliam o projeto de programas de maneira top-down. Uma funo cria uma
maneira conveniente de encapsular alguns detalhes de processamento, ou seja, como algum resultado ob-
tido. Quando esta computao necessria, a funo chamada, ou invocada. Desta forma, quando uma
funo chamada o usurio no precisa se preocupar como a computao realizada. importante saber
o que a funo faz (qual o resultado da execuo de uma funo) e tambm como se usa a funo. Criando
funes, um programa C pode ser estruturado em partes relativamente independentes que correspondem as
subdivises do problema.
Voc j viu algumas funes: printf(), scanf(), getchar(), sqrt(). Elas so funes
de uma biblioteca padro (do C ). Voc no sabe como elas foram escritas, mas j viu como utiliz-las. Ou
seja, voc sabe o nome das funes e quais informaes especficas voc deve fornecer a elas (valores que
devem ser passados para as funes) para que a funo produza os resultados esperados.
Quando nos referirmos a uma funo neste texto usaremos a maneira frequentemente utilizada que o
nome da funo seguido de ().
Tomemos como exemplo o programa abaixo, que calcula o produto de 2 nmeros inteiros positivos
apenas se ambos forem primos:
Observe que o cdigo que verifica se um nmero primo teve que ser reproduzido dentro do programa
por duas vezes (para testar se os nmeros fornecidos pelo usurio eram primos).
Um dos benefcios mais bvios de usar funes que podemos evitar repetio de cdigo. Em outras
palavras, se voc quiser executar uma operao mais de uma vez, voc pode simplesmente escrever a funo
uma vez e utiliz-la diversas vezes ao invs de escrever o mesmo cdigo vrias vezes. Outro benefcio que
se voc desejar alterar ou corrigir alguma coisa mais tarde, mais fcil alterar em um nico lugar.
O exemplo acima poderia ser simplificado pela criao de uma funo chamada ehPrimo, que dado
um nmero n, d como resultado 1 se este nmero primo, ou 0 (zero) se o nmero no primo:
O exemplo pode ser ento alterado e simplificado com o uso da funo ehPrimo():
Como pode ser observado, sejam quais forem os 2 nmeros fornecidos, no precisa escrever um cdigo
similar ao mostrado na funo ehPrimo acima para cada nmero.Basta chamar a funo ehPrimo(),
passar os valores necessrios para verificar a primalidade de cada nmero, e utilizar os resultados.
Evitar repetio de cdigo a razo histrica que funes foram inventadas (tambm chamado de proce-
dimento ou subrotinas em outras linguagens de programao). A maior motivao para utilizar funes nas
linguagens contemporneas a reduo da complexidade do programa e melhoria da modularidade do pro-
grama. Dividindo o programa em funes, muito mais fcil projetar, entender e modificar um programa.
Por exemplo, obter a entrada do programa, realizar as computaes necessrias e apresentar o resultado ao
usurio pode ser implementado como diferentes funes chamadas por main() nesta ordem.
Funes podem ser escritas independentemente uma da outra. Isto significa que, em geral, variveis
usadas dentro de funes no so compartilhadas pelas outras funes. Assim sendo, o comportamento da
funo previsvel. Se no for assim, duas funes completamente no relacionadas podem alterar os dados
uma da outra. Se as variveis so locais a uma funo, programas grandes passam a ser mais fceis de serem
escritos. A comunicao entre funes passa a ser controlada elas se comunicam somente atravs pelos
valores passados as funes e os valores retornados.
32
11.2 Definindo funes
Um programa C consiste de uma ou mais definies de funes (e variveis). H sempre uma funo
chamada main. Outras funes tambm podem ser definidas. Cada uma pode ser definida separadamente,
mas nenhuma funo pode ser definida dentro de outra funo. Abaixo, mostramos um exemplo simples
de um programa que consiste de duas funes: main() e alo(). Quando executado, este programa
imprimir a mensage Alo! trs vezes.
#include <stdio.h>
i = 1;
while (i <= 3)
{
alo();
i += 1;
}
}
Todas as funes devem ser declaradas antes de serem usadas. As funes da biblioteca padro, tais
como printf(), scanf() e getchar(), so pr-definidas, mas memsmo assim devem ser declaradas
(deve ser anunciado ao compilador que elas existem). por isso que inclumos a linha #include <stdio.h>
no incio do cdigo fonte.
O formato geral da definio de uma funo
A primeira linha da definio o cabealho da funo. Ela tm trs partes principais: o nome da
funo, o tipo do resultado (que um valor) que a funo computa e retorna, e entre parnteses uma lista
de parmetros (tambm chamado de argumentos formais). Se a funo no retorna nenhum valor, o tipo
chamado de void, e esta palavra escrita no cabealho na frente do nome da funo. Se a funo no tiver
argumentos formais, a palavra void escrita no lugar da lista de argumentos formais entre os parnteses.
Para simplificar a exposio, falaremos sobre o tipo do retorno e os argumentos formais mais tarde. Eles
servem para permitir que as funes troquem informaes entre si.
33
11.3 Funes simples
Para comear, vamos utilizar funes na seguinte forma:
void nome-da-funo(void)
{
declaraes e senteas (corpo da funo)
}
O primeiro void significa que esta funo no tem tipo de retorno (no retorna um valor), e o segundo
significa que a funo no tem argumentos (ela no precisa de nenhuma informao externa para ser exe-
cutada). Isso no significa que a funo no faz nada. Ela pode realizar alguma ao, como imprimir uma
mensagem. O exemplo abaixo mostra um programa que usa uma funo como essa:
void alo(void);
main()
{
alo();
}
void alo(void)
{
printf("Alo.\n");
}
Neste exemplo, o programa consiste de duas funes, main() e alo(). A ordem em que as funes
so definidas no importante, desde que prottipos de funes so usadas. A linha
void alo(void);
no topo do programa um prottipo de funo para a funo alo(). Um prottipo usado para declarar
uma funo. Um prottipo passa ao compilador informaes sobre uma funo que definida dentro de
um programa em algum lugar. Prottipos so sempre colocados prximo ao incio do programa, antes do
comeo da definio de funes.
A funo alo() imprime a mensagem Alo. quando chamada. A sentena printf() o corpo
da funo. Dentro da funo main() h uma chamada a funo alo(). A funo chamada pelo seu
nome seguido de () (j que a funo alo no tem argumentos, nenhuma expresso escrita dentro dos
parnteses). A funo alo() no retorna um valor, ela chamada simplesmente para realizar uma ao
(imprimir a mensagem). A chamada de funo uma sentena vlida em C , portanto deve ser terminada
por ponto e vrgula (;).
alo();
Outra coisa que voc deve ter notado que main() tambm uma funo. A funo main() no difere
em nada das demais funes, com a exceo de que contm o programa principal. Alm disso, no
necessrio declarar o prottipo da funo main().
11.3.1 Argumentos
Nosso prximo exemplo pede que o usurio digite suas iniciais, e ento chama a funo cumprimenta()
para imprimir a mensagem Ola junto com as iniciais digitadas. Estas iniciais (seus valores) so passadas
para a funo cumprimenta(). A funo cumprimenta() definida de forma que ela imprimir a
mensagem incluindo quaisquer iniciais passadas.
34
#include <stdio.h>
int main()
{
char primeiro, segundo;
Note que h uma correspondncia entre o nmero e tipo dos valores que main() passa (estes so chamados
de parmetros reais ou argumentos reais) e os argumentos listados no cabealho da funo cumprimenta().
return expresso;
A expresso avaliada e o seu valor convertido ao tipo de retorno da funo (o tipo da funo dado
no cabealho da funo antes do nome da funo).
Considere o seguinte exemplo. O programa consiste de duas funes: main() e quadrado. O
programa pede que o usurio digite trs nmeros e verifica se eles podem ser os lados de um tringulo reto.
int quadrado(int);
35
int main()
{
int s1, s2, s3;
main()
{
printf("cinco = %d\n", cinco() );
}
int cinco(void)
{
return 5;
}
A sada do programa ser
cinco = 5
36
porque o valor de cinco() dentro da sentena printf() 5. Olhando na sentena return, 5 a
expresso retornada para o chamador.
Outro exemplo:
int obtem_valor(void);
main()
{
int a, b;
a = obtem_valor();
b = obtem_valor();
int obtem_valor(void)
{
int valor;
return valor;
}
Este programa obtm dois inteiros do usurio e mostra a sua soma. Ele usa a funo obtem valor()
que mostra uma mensagem e obtm o valor do usurio.
Um exemplo de sada deste programa :
Entre um valor: 15
Entre um valor: 4
soma = 19
int obtem_valor_positivo(void)
{
int valor;
37
if (valor >= 0)
return valor;
return -valor;
}
Em uma funo void, return; (s com ;) pode ser usado para sair de uma funo. O exemplo
seguinte, pede instrues ao usurio. Se o usurio reponder nao, a funo termina. Do contrrio, ele
imprime as instrues e depois termina.
void instrucoes(void)
{
int ch;
/* Mostra instrucoes */
printf("As regras do jogo sao . . . ");
.
.
.
return;
}
O return final (antes de fechar as chaves do corpo da funo) na funo opcional. Se omitido, a
funo atingir o final da funo e retornar automaticamente. Note que o return opcional somente para
funes void.
int abs(int);
main()
{
int n;
38
printf("Entre um numero: ");
scanf("%d", &n);
return x;
}
A funo abs() tem um argumento do tipo int, e seu nome x. Dentro da funo, x usado como
uma varivel x.
Uma vez que abs() tem um nico argumento, quando ela chamada, h sempre um valor dentro do
parnteses, como em abs(n). O valor de n passado para a funo abs(), e antes da execuo da funo,
o valor de n atribudo a x.
Aqui est um exemplo de uma funo que converte uma temperatura de Farenheit para Celsius:
float fahr_para_cels(float f)
{
return 5.0 / 9.0 * (f - 32.0);
}
Como voc pode ver, esta funo tem somente um argumento do tipo float. Um exemplo de chamada
desta funo poderia ser:
fervura = fahr_para_cels(212.0);
O resultado da funo fahr para cels(212.0) atribudo a fervura. Portanto, depois da
execuo desta sentena, o valor de fervura (que do tipo float) ser 100.0.
O exemplo seguinte possui mais de um argumento:
Esta funo possui dois argumentos do tipo float. Para chamar uma funo com mais de um argumento,
os argumentos devem ser separados por vrgula. A ordem em que os argumentos so passados deve ser na
mesma em que so definidos. Neste exemplo, o primeiro valor passado ser a largura e o segundo a altura.
Um exemplo de chamada seria
tamanho = area(14.0, 21.5);
Depois desta sentena, o valor de tamanho (que do tipo float) ser 301.0.
Quando passar os argumentos, importante ter certeza de pass-los na ordem correta e que eles so do
tipo correto. Se isso no for observado, pode ocorrer erro ou aviso de compilao, ou resultados incorretos
podem ser gerados.
Uma ltima observao. Os argumentos que so passados pelo chamador podem ser expresses em geral
e no somente constantes e varivies. Quando a funo chamada durante a execuo do programa, estas
expresses so avaliadas, e o valor resultante passado para a funo chamada.
39
11.7 Chamada por valor
Considere novamente a funo quadrado(). Se esta funo chamada de main() como
p = quadrado(x);
somente o valor (no o endereo) de x passado para quadrado. Por exemplo, se a varivel tem valor
5, para a funo quadrado(), quadrado(x) ou quadrado(5) so o mesmo. De qualquer forma,
quadrado() receber somente o valor 5. quadrado() no sabe se na chamada da funo o 5 era uma
constante inteira, o valor de uma varivel do tipon int, ou alguma expresso como 625/25 - 4 * 5.
Quando quadrado() chamado, no interessa qual a expresso entre parnteses, ela ser avaliada e o
valor passado para quadrado().
Esta maneira de passar argumentos chamada de chamada por valor. Argumentos em C so passados
por valor. Portanto, a funo chamada no pode alterar o valor da varivel passada pelo chamador como
argumento, porque ela no sabe em que endereo de memria o valor da varivel est armazenado.
void obtem_int(void);
main()
{
obtem_int();
void obtem_int(void)
{
int x;
printf("Obrigado!\n");
}
A funo main() usou um nome x, mas x no definido dentro de main; ele uma varivel local a
get int(), no a main(). Este programa gera erro de compilao.
Note que possvel ter duas funes que usam variveis locais com o mesmo nome. Cada uma delas
restrita a funo que a define e no h conflito. Analise o seguinte programa (ele est correto):
int obtem_novo_int(void);
40
main()
{
int x;
x = obtem_novo_int();
int obtem_novo_int(void)
{
int x;
printf("Obrigado!\n");
return x;
}
A funo obtem novo int() usa uma varivel local chamada x para armazenar o valor digitado e
retorna como resultado o valor de x. main() usa outra varivel local, tambm chamada de x para receber
o resultado retornado por obtem novo int(). Cada funo tem sua prpria varivel x.
11.9 Prottipos
Os prottipos servem para dar ao compilador informaes sobre as funes. Isso para que voc possa chamar
funes antes que o compilador tenha a definio (completa) das funes. O prottipo de uma funo
idntico ao cabealho da funo, mas o nome dos argumentos podem ser omitidos e ele terminado com
uma vrgula. Prottipos declaram uma funo ao invs de defini-las. O formato dos prottipos :
tipo-de-retorno nome-da-funo(lista-dos-tipos-dos-argumentos);
Definindo prottipos, voc no precisa se preocupar com a ordem em que define as funes dentro do
programa. A principal vantagem de definir prottipos que erros de chamada de funes (como chamar
uma funo com o nmero incorreto de argumentos, ou com argumentos de tipo errado) so detectados pelo
compilador. Sem prottipos, o compilador s saberia que h erro depois de encontrar a definio da funo.
Em verses antigas do compilador C , programas com tais erros compilariam sem erros, o que tornava a
depurao de erros mais difcil.
Abaixo, mostramos duas funes e seus prottipos:
Suposies o que voc assume ser verdade para que a funo funcione apropriadamente
Estas informaes devem ser colocadas como comentrio antes da definio da funo.
11.11 Comentrios
Voc pode colocar comentrios no seu programa para documentar o que est fazendo. O compilador ignora
completamente o que quer esteja dentro de um comentrio.
Comentrios em C comeam com um /* e terminam com um */. Alguns exemplos:
/* Este e
um comentario
que usa
diversas linhas
*/
/* Este e
* um comentario
* de diversas linhas
* mais bonito
*/
Note que no podemos aninhar comentrios dentro de comentrios. Um comentrio termina no primeiro
* / que encontrar. O comentrio abaixo ilegal:
/* Este e um comentario /* illegal */ ilegal */
42
Regras para comentrio
sempre uma boa idia colocar comentrios em seu programa das coisas que no so claras. Isto vai
ajudar quando mais tarde voc olhar o programa que escreveu j h algum tempo ou vai ajudar a entender
programas escritos por outra pessoa.
Um exemplo de comentrio til:
seria mais informativo neste ponto. Ou seja, voc deve comentar o cdigo, e no codificar o comentrio.
/* Incrementa i */
i++;
/* funcao instrucoes()
* acao: mostra instrucoes do programa
* entrada: nenhuma
* saida: nenhuma
* suposicoes: nenhuma
* algoritmo: imprime as instrucoes
* /
void instrucoes(void)
{
/* mostra instrucoes */
printf("O processo de purificacao do Uranio-235 e . . . ");
.
.
}
43
12 O pr-processador
O pr-processador um programa que faz alguns processamentos simples antes do compilador. Ele exe-
cutado automaticamente todas as vezes que seu programa compilado, e os comandos a serem executados
so dados atravs de diretivas do pr-processador.
Estas diretivas so colocadas em linhas que contm somente a diretiva (elas no so cdigo da linguagem
C , portanto as regras para elas so um pouco diferentes). As linhas que comeam com um # so comandos
para o pr-processador. A linha inteira reservada para este comando (nenhum cdigo C pode aparecer
nesta linha e comandos do pr-processador no podem estar separados em diversas linhas).
#define PI 3.14159265
int main()
{
double raio;
Lembre-se que o nome PI no um nome de varivel. Ele um nome que o pr-processador substituir
pelo texto especificado pelo #define (mais ou menos da mesma forma que o comando pesquisa-e-substitui
do editor de texto). O compilador nunca v ou sabe sobre PI. O compilador v o seguinte printf() do
programa acima depois do pr-processador ser executado:
12.3 Comentrios
De um modo geral, o pr-processador dos compiladores existentes remove todos os comentrios do arquivo
fonte antes do programa ser compilado. Portanto, o compilador nunca v realmente os comentrios.
1. o nome da funo;
2. a lista de parmetros formais (argumentos) com seus nomes e tipos. Se no houver argumentos, a
palavra void escrita entre os parnteses.
3. o tipo do resultado que a funo retorna atravs da sentena return ou void se a funo no retorna
nenhum valor. Lembre-se que somente um valor pode ser retornado por uma sentena return.
4. o corpo da funo, que uma sentena composta (comea e termina com chaves ({ }) contendo
definio de variveis e outras sentenas. Em C , no se pode definir uma funo dentro de outra.
45
Para funes com argumentos: uma funo chamada dando o seu nome e uma lista de argumentos
(expresses que so avaliadas e cujos valores so atribudos para os correspondentes parmetros formais da
funo).
Por exemplo, suponha que triang area() e circ area() sejam funes que calculam a rea de
tringulos e crculos, respectivamente. Seus prottipos so:
Estas funes podem chamadas de dentro de outras funes. Os argumentos reais com os quais elas so
chamadas podem ser expresses constantes, or variveis locais, ou qualquer expresso cuja avaliao resulte
em valores do tipo float (inteiros so convertidos para float da mesma forma que ocorre com atribuio
de inteiros para variveis do tipo float). Alguns exemplos de chamadas:
A ltima sentena do exemplo acima atribui a varivel area5 a rea de um tringulo cuja base igual
ao valor da varivel raio e a altura igual a area de um crculo de raio igual ao valor da varivel raio.
Quando um programa executado, somente uma nica funo tem o controle em determinado momento.
Falaremos mais sobre o que acontece quando uma funo chamada mais tarde nestas notas de aula.
Variveis Locais Variveis que so definidas dentro de uma funo so variveis locais desta funo.
Parmetros formais de uma funo so variveis locais da funo. Variveis locais so privativas a
funo na qual so definidas. Somente esta funo pode enxerg-las (ela conhece o endereo das variveis e
pode usar e modificar o seu contedo). Nenhuma outra funo pode acessar variveis locais de outra funo
sem permisso (uma funo pode acessar variveis locais de outra se esta passar o endereo da varivel
local como argumento este assunto ser tratado em notas de aula futuras). O fato de cada funo manter
variveis locais escondidas do resto do mundo torna mais fcil a tarefa de escrever programas estruturados
e modulares. Quando voc est escrevendo uma funo, voc pode dar as suas variveis locais o nome que
quiser. Voc tambm no precisa se preocupar se outra pessoa escrevendo outra funo ter acesso ou altera
variveis locais a sua funo.
Variveis locais que so definidas dentro da funo devem ser inicializadas com algum valor antes
de serem usadas. Caso contrrio, o seu valor indefinido.
J que parmetros formais (argumentos) so variveis locais da funo, eles podem ser usados no corpo
da funo. Eles no devem ser definidos dentro da funo (sua definio j est no cabealho da funo). Os
parmetros formais no precisam ser inicializados. Seus valores so fornecidos pelo chamador da funo
atravs dos argumentos reais.
Considere o seguinte exemplo:
/*****************************************************************
* Um programa que calcula a area de triangulos e circulos.
* A base, altura e raio sao fornecidos pelo usuario.
46
* A saida do programa e a area do triangulo e circulo.
*****************************************************************/
#include <stdio.h>
#define PI 3.1415
/*******************
prototipos
*******************/
float triang_area(float, float);
float circ_area(float);
/*******************
definicao de funcoes
*******************/
main(void)
{
/* definicao das variaveis locais */
float base, altura, raio;
/* dialogo de entrada */
printf("\nEntre com a base e altura do triangulo: ");
scanf("%f %f", &base, &altura);
printf("\nEntre com o raio do circulo: ");
scanf("%f", &raio);
/*****************************************************************
* funcao: triang_area
* calcula a area de um triangulo dada a base e altura
* Entrada: base e altura do triangulo
* Saida: area do triangulo
*****************************************************************/
float triang_area(float base, float alt)
{
return 0.5*base*alt;
}
/*****************************************************************
* funcao: circ_area
* calcula a area de um circulo dado o raio
* Entrada: raio do circulo
* Saida: area do circulo
47
*****************************************************************/
float circ_area(float r)
{
return PI*r*r;
}
Variveis Globais
At este momento, todas as variveis que vimos so definidas dentro de funes (no corpo da funo
ou como parmetros formais). possvel tambm definir variveis fora das funes. Tais variveis so
chamadas de variveis globais ou externas. O formato da definio de variveis globais o mesmo da
definio de variveis locais. A nica diferena onde a varivel definida: variveis globais so definidas
fora de qualquer funo. Ao contrrio das variveis locais, variveis globais podem ser vistas por todas as
funes definidas aps a definio das variveis globais.
Ns temos usado declaraes globais este tempo todo por exemplo, as declaraes de prottipos de
funes. Elas so declaradas fora de qualquer funo e podem ser vistas por qualquer funo que esto aps
sua declarao.
No exemplo seguinte, uma varivel saldo que atualizada por trs funes diferentes definida como
uma varivel global. As trs funes que a atualizam no chamam uma a outra.
/*****************************************************************
* Caixa eletronico simples
* o saldo e o valor a ser alterado e entrado pelo usuario
* a saida do programa e o saldo atualizado, incluindo juros
*****************************************************************/
#include <stdio.h>
/*******************
prototipos
*******************/
void credito(float);
void debito(float);
void juros(void);
/*******************
globais
*******************/
float saldo; /* saldo atual;
48
* Alterada em: credito(), debito(), juros(), main()
* Lida em:
*/
/***********************
definicao de funcoes
***********************/
main(void)
{
float valor; /* valor a ser depositado/retirado */
/*****************************************************************
* Deposita um valor; atualiza a variavel global saldo
* Entrada: valor a ser depositado
* Saida: nenhum
*****************************************************************/
void credito(float val)
{
saldo += val;
}
/*****************************************************************
* Debita um valor; atualiza a variavel global saldo
* Entrada: valor a ser debitado
* Saida: nenhum
*****************************************************************/
void debito(float val)
{
saldo -= val;
}
/*****************************************************************
* Acumula juros; atualiza a variavel global saldo; juros: RATE
* Entrada: nenhuma
* Saida: nenhuma
49
*****************************************************************/
void juros(void)
{
saldo += (saldo * JUROS);
}
Variveis globais devem ser usadas SOMENTE quando muitas funes usam muito as mesmas vari-
veis. No entanto, o uso de variveis globais perigoso (e no recomendado) porque a modularidade do
programa pode ser afetada. Uma varivel global pode ser alterada de dentro de uma funo, e esta alterao
pode influir no resultado de uma outra funo, tornando-a incorreta (em um exemplo dado posteriormente
nestas notas, duas chamadas a funo soma_y() com o mesmo argumento (zero) produz resultados dife-
rentes, 100 e 300).
Quando variveis globais so utilizadas, deve ser dado a elas nomes descritivos e um breve comentrio
qual a finalidade da varivel e quais funes a acessam.
Neste curso, voc utilizar variveis globais SOMENTE QUANDO FOR DADO PERMISSO PARA
FAZ-LO. Caso contrrio, no permitido utiliz-las (ou seja, sero descontados pontos).
Escopo de Variveis
Como j discutimos anteriormente, uma varivel uma abstrao de dados que ns usamos em um
programa. A varivel representa um endereo de memria onde os valores so armazenados. Durante a
execuo do programa, valores diferentes poder ser armazenados neste endereo. Quando uma varivel
definida, o nome da varivel atrelada a um endereo especfico na memria. At este momento, j
discutimos o que o nome de uma varivel, seu endereo, tipo e valor. Outra caracterstica que apresenta-
resmo agora o escopo. O escopo de uma varivel refere-se a parte do programa onde podemos utilizar a
varivel. Em outras, palavras, uma varivel visvel dentro do seu escopo.
O escopo de uma varivel local a funo na qual ela definida. Os parmetros formais de uma funo
tambm so tratados como variveis locais.
O escopo de uma varivel global a poro do programa depois da definio da varivel global (a partir
do ponto onde ela definida at o final do programa).
Se o nome de uma varivel global idntico a uma varivel local de uma funo, ento dentro desta
funo em particular, o nome refere-se a varivel local. (Embora tais conflitos devem ser evitados para
evitar confuso).
Por exemplo, considere o seguinte programa:
int valor = 3; /* definicao da variavel global */
int main()
{
/* definicao local de valor */
int valor = 4;
printf("%d\n", valor);
}
50
A sada do programa acima ser 4 j que valor refere-se a definio local.
Considere outro exemplo:
#include <stdio.h>
int soma_y(int);
int soma_yy(int);
int y = 100; /* variavel global */
main(void)
{
int z = 0; /* variavel local */
printf("%d\n", soma_y(z));
printf("%d\n", soma_yy(z));
printf("%d\n", soma_y(z));
}
int soma_y(int x)
{
return x + y; /* x e variavel local, y e global */
}
int soma_yy(int x)
{
y = 300; /* y e variavel global */
return x + y; /* x e variavel local */
}
Vamos seguir a execuo deste programa. Primeiro, a varivel global y criada e inicializada com 100.
Ento, a execuo da funo main() comeca: alocado espao na memria para a varivel local z. Esta
varivel inicializada com 0. Considere a primeira sentena printf():
printf("%d\n", soma_y(z));
Esta uma chamada para a funo da biblioteca padro printf(). Os parmetros reais desta chamada
so o string "%d\n" e a expresso soma_y(z). A ltima expresso a chamada da funo soma_y().
O valor desta expresso o resultado retornado por soma_y(). Qual o resultado? A funo soma_y
chamada com o parmetro real z. Como z = 0, este o valor que ser passado para a funo soma_y;
o 0 copiado para o parmetro formal x da funo soma_y(). Portanto, durante a excuo da primeira
chamada a funo soma_y(), o valor da expresso x + y ser 0 + 100, que 100. Portanto, o valor
da primeira chamada soma_ y(z) 100, e este nmero ser impresso com o primeiro printf() em
main(). Agora considere a segunda sentena:
printf("%d\n", soma_yy(z));
Quando a funo soma_y(z) chamada, o valor de z ainda 0, portanto, 0 copiada para o parmetro
formal int x da funo soma_y(). Quando soma_ y() executada pela segunda vez, a varivel
global y foi modificada para 300, portanto o valor de x + y 0 + 300. Portanto, o valor da chamada
soma_yy(z) 300, e este nmero ser impresso pelo terceiro printf() em main().
Portanto, a sada da execuo deste programa ser
100
300
300
Por exemplo, para achar a raiz quadrada inteira de 42 (usando diviso inteira que trunca a parte fracional
do nmero)
a0 = 1, a1 = (42/1 + 1)/2 = 21, a2 = (42/21 + 21)/2 = 11, a3 = (42/11 + 11)/2 = 7, a4 =
(42/7 + 7)/2 = 6.
Uma vez que a24 = 62 = 36 42 < (a4 + 1)2 = 72 = 49, o processo termina e a resposta 6.
(No necessrio voc entender por que este algoritmo funciona portanto no se preocupe se no
conseguir entend-lo)
52
int raizInteira(int); /* prototipo */
/**************************************************************
* function: raizInteira(x)
* acao: dado x, retorna a raiz quadrada inteira de x
* in: inteiro positivo x
* out: raiz quadrada inteira de x
* suposicoes: x >= 0
* algoritmo: metodo de dividr e calcular media: comecando com
* um palpite de 1, o proximo palpite e calculado como
* (x/palpite_ant + palpite_ant)/2. Isso e repetido
* ate que palpite^2 <= x < (palpite+1)^2
***************************************************************/
int raizInteira(int x)
{
int palpite = 1;
Note que usando a lei de DeMorgan, podemos re-escrever a expresso teste do while em uma forma
equivalente:
Deve estar claro neste ponto a diferenca entre ao e algoritmo. Uma pessoa que quer usar esta funo
precisa saber somente a ao, no o algoritmo. tambm importante especificar os dados que so esperados
pela funo e retornados por ela para outras pessoas poderem us-la. As suposies devem esclarecer as
restries da funo sobre quando a funo pode falhar ou produzir resultados errados. Neste caso, um
nmero negativo produziria um erro, j que nmeros negativos no possuem raiz quadrada.
No h necessidade de ir em muitos detalhes em qualquer parte da documentao da funo. Embora ela
deva conter informao suficiente para que algum (que no possa ver o cdigo) saber utiliz-la. Detalhes
sobre implementao e detalhes menores sobre o algoritmo devem ser colocados como comentrios no
prprio cdigo.
53
14 Estruturas de Repetio
A linguagem C possui comandos para repetir uma sequncia de instrues. Estas estruturas de repetio,
tambm conhecidas como laos (do ingls loops). A principal construo que veremos o while2
A expresso teste inicialmente avaliada para verificar se o lao deve terminar. Caso a expresso seja
verdadeira (isto , diferente de 0 (zero)), o corpo da repetio executado. Depois desta execuo, o
processo repetido a partir da expresso teste. O corpo do lao, por sua vez, pode ser uma sentena simples
ou composta.
int contador = 0;
printf("ACABOU !!!!\n");
Sada:
contador = 0
contador = 1
contador = 2
contador = 3
contador = 4
ACABOU !!!!
2
Existem outras estruturas de repetio: for (seo 15.1) e do ... while (seo 15.3).
54
Neste exemplo, a expresso de teste contador < 5, e o corpo do lao a sentena printf().
Se examinarmos cuidadosamente este exemplo, veremos que a varivel contador inicializada com
0 (zero) quando definida. Depois disso, a expresso de teste verificada e, como 0 < 5 verdadeiro,
o corpo da repetio executado. Assim, o programa imprime contador = 0, e incrementa contador de
um (atravs do ps-decremento indicado no argumento de printf()). Em seguida, a expresso de teste
verificada novamente e todo o processo se repete at que contador seja 4 e contador = 4 seja impresso.
Depois disso, contador incrementado para 5 e o teste executado. Mas desta vez, 5 < 5 falso, ento
o lao no continua. A execuo do programa continua na sentena que segue o lao (no caso, imprimir a
frase ACABOU !!!).
Aps a execuo do while, a varivel contador tem valor 5.
No exemplo acima, h uma sentena simples no corpo da repetio. Quando este for definido por uma
sentena composta (bloco), no se deve esquecer de usar as chaves ({ e }) para delimitar o bloco da sentena
composta.
O exemplo seguinte mostra um uso mais apropriado do comando while: Em situaes onde o nmero
de repeties no conhecido antes do inico do comando while:
Exemplo 1: Este programa pede nmeros ao usurio at que a soma de todos os nmeros digitados for
pelo menos 20.
#include <stdio.h>
main( ){
total += num;
}
Exemplo de sada:
Total = 0
Entre com um numero: 3
Total = 3
Entre com um numero: 8
Total = 11
Entre com um numero: 15
Final total = 26
Inicialmente, dado o valor 0 varivel total, e o teste verdadeiro (0 < 20). Em cada iterao, o total
impresso e o usurio digita um nmero que somado a total. Quanto total for maior ou igual a 20, o teste
do while torna-se falso, e a repetio termina.
55
14.2 Estilo de formatao para estruturas de repetio
A regra principal ser consistente. Assim, seu programa ser mais legvel.
while (expressao)
{
sentenca;
}
while (expressao)
{
sentenca;
}
while (expressao) {
sentenca;
}
APENAS UM DESTES ESTILOS deve ser consistentemente usado para as sentenas for, while e do ...
while. Use o estilo com o qual voc se sentir mais confortvel.
while( i < 5 )
i += 1;
Embora as chaves possam ser omitidas, h uma nica razo para coloc-las sempre:
while( i < 5 ) {
i += 1;
}
Quando voc adicionar algo ao programa, voc poder adicionar uma sentena para um lao com apenas
uma sentena. Se voc fizer isso, vital que voc tambm adicione chaves. Se voc no fizer isso, a segunda
sentena do lao no ser considerada como parte do lao. Por exemplo:
while( i < 5 )
i += 1;
j += 1;
while( i < 5 )
i += 1;
j += 1;
56
enquanto a inteno era na realidade:
while( i < 5 ) {
i += 1;
j += 1;
}
while (i<5)
ou
while (i<5)
ou
while( i < 5 )
Isto tambm uma escolha pessoal. Porm seja consistente em sua escolha !
Exemplo 2:
#include <stdio.h>
int main( ){
linha = 1;
while (linha < 5)
{
coluna = 1;
while (coluna < 5)
{
printf( "%3d", linha * coluna );
coluna += 1;
}
linha += 1;
}
printf( "\n" );
}
Sada:
57
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
No exemplo acima, para cada iterao do lao externo, o lao interno imprime uma linha com nmeros
e depois pula de linha.
Exemplo 3: Este exemplo parecido com o anterior, exceto que o printf() colocado dentro do lao
interno. Como era de se esperar uma nova linha impressa aps cada valor ao invs de ser depois de 4
valores.
#include <stdio.h>
int main( ){
linha = 1;
while (linha < 5)
{
coluna = 1;
while (coluna < 5)
{
printf( "%3d", linha * coluna );
printf( "\n" );
coluna += 1;
}
linha += 1;
}
}
Sada:
1
2
3
4
2
4
6
8
3
6
9
12
4
8
12
16
58
Exemplo 4:
#include <stdio.h>
int main( ){
printf("\n");
linha = 1;
while (linha < 8)
{
printf( "\t" );
coluna = 1;
while (coluna < linha)
{
printf( "*" );
coluna += 1;
}
printf( "\n" );
linha += 1;
}
}
Sada:
*
**
***
****
*****
******
*******
********
59
15 Outras Estruturas de Repetio
A linguagem C possui outras estruturas de repetio alm do while (seo 14.1). So elas o for e o
do...while.
int i;
Este lao pode ser expresso de forma diferente utilizando a estrutura de repetio for. A estrutura acima
pode ser expressa de forma equivlente com um for da seguinte forma:
int i;
Como pode-se observar, h 4 partes no lao for: inicializao, expresso de teste, expresso de incre-
mento e o corpo do lao. O formato do lao for :
for (inicializao; expresso de teste; incremento){
corpo da repetio
}
A inicializao executada uma nica vez no incio do lao. A expresso teste ento avaliada para
verificar se o lao deve terminar. Caso a expresso seja verdadeira (isto , diferente de Zero), o corpo da
repetio executado. Depois desta execuo, a expresso de incremento executada e o processo repetido
a partir da expresso teste. O corpo da repetio, por sua vez, pode ser uma sentena simples ou composta.
60
Veja abaixo um exemplo simples do lao for:
int contador;
printf{"ACABOU !!!!\n");
Sada do programa:
contador = 0
contador = 1
contador = 2
contador = 3
contador = 4
ACABOU !!!!
Se voc examinar cuidadosamente este exemplo, poder ver precisamente o que est acontecendo. Pri-
meiro, a inicializao executada, que a sentena contador = 0. Isso modifica o valor da varivel con-
tador para 0. Ento, o teste executado. como 0 < 5 verdadeiro, o lao continua. Assim, o corpo da
repetio executado, imprimindo a primeira linha da sada, contador = 0. Depois disso, o incremento
executado, que a sentena contador++, que altera o valor da varivel contador para 1.
Esta a 1a iterao do lao. Ento, o teste executado novamente (como 1 < 5 verdadeiro, o lao
continua), o corpo da repetio mostra contador = 1, e contador incrementado novamente.
Este processo continua at que contador seja 4 e contador = 4 seja impresso. Depois disso, contador
incrementado para 5 e o teste executado. Mas desta vez, 5 < 5 falso, ento o lao no continua. A
execuo do programa continua na sentena que segue o lao (no caso, imprimir a frase ACABOU !!!).
Aps a execuo do lao, a varivel contador tem valor 5.
Ao invs de usar o teste contador < 5, voc poderia tambm ter usado a expresso contador <= 4. O
resultado seria o mesmo. Use a expresso que voc preferir. Outra expresso que tambm poderia ter sido
usada contador != 5. Porm esta expresso torna o programa menos legvel (no to evidente que o valor
61
de contador est sendo incrementado at atingir o valor 5). Alm disso, isso poderia causar problemas se
mudssemos a inicializao para um valor maior que 5. Por exemplo, se a inicializao for contador = 25
e a expresso teste for contador != 5 o lao nunca terminaria, pois o contador comea com 25 e a cada
iterao o valor incrementado, o que nunca tornaria o teste falso.
Tambm poderamos ao invs de usar contador += 1 como a expresso de incremento, usar ++contador,
contador++ e contador = contador + 1. O resultado seria o mesmo (neste caso, o uso de ps- e pr-
incremento no faz diferena).
Se voc quisesse incrementos de dois, voc poderia escrever contador += 2 (ou contador = contador + 2).
Exemplo 1:
#include <stdio.h>
#include <stdio.h>
main( void ){
Sada:
contador = 0, total = 0
contador = 1, total = 1
contador = 2, total = 3
contador = 3, total = 6
contador = 4, total = 10
contador = 5, total = 15
contador = 6, total = 21
contador = 7, total = 28
contador = 8, total = 36
contador = 9, total = 45
Exemplo 2: Um programa que imprime todos os nmeros entre 30 e 5 (nesta ordem) divisveis por 3, e
no final imprime sua soma.
62
#include <stdio.h>
main( ){
int i, soma;
soma = 0;
for( i = 30; i >= 5; i -= 1 ){
if( (i % 3) == 0 ){
printf( "\t%2d\n", i );
soma += i;
}
}
printf( "\t soma = %d\n", soma );
}
Sada do programa:
30
27
24
21
18
15
12
9
6
soma = 162
equivalente a:
inicializacao;
while( teste ) {
sentenca;
incremento;
};
63
15.3 A Estrutura de Repetio do...while
H outro comando de repetio em linguagem C . O do...while bastante parecido com while, com a
diferena que a expresso de teste avaliada DEPOIS que o corpo da repetio executado.
O formato do do...while :
do
corpo da repetio
while (expresso teste)
int contador = 0;
do {
printf( "contador = %d\n", contador );
contador += 1;
} while( contador < 5 );
printf("ACABOU !!!!\n");
A execuo deste programa idntico ao primeiro exemplo mostrado para o comando while, com a
expresso de teste mudada para o final.
Sada:
contador = 0
contador = 1
contador = 2
contador = 3
contador = 4
ACABOU !!!!
O do...while usado quando o corpo da repetio deve ser executado pelo menos uma vez. Um exemplo
comum disto o processamento da entrada de um programa.
Exemplo 3: Neste exemplo, o teste do lao baseado no valor digitado pelo usurio. O lao deve ser
executado pelo uma vez antes que o teste sobre o valor seja executado.
#include <stdio>
main( ){
64
int num;
do{
scanf( "%d", &num );
} while( num % 2 != 0 );
printf( "Obrigado.\n" );
}
Exemplo de execuo:
Neste caso, o valor da varivel num digitado pelo usurio. Depois disso, o teste executado para
verificar se o nmero par (o teste num % 2 != 0 falso se num par j que o resto da diviso de
qualquer nmero par por 2 zero).
possvel escrever o programa acima usando while:
#include <stdio>
main( ){
num = 1;
while( num % 2 != 0 ){
scanf( "%d", &num );
}
printf( "Obrigado.\n" );
}
O problema com este programa que a varivel num deve ser inicializada com um valor que torne
o teste do lao verdadeiro. Neste exemplo, simples encontrar tal valor. Para uma expresso teste mais
complicada, isso pode no ser to fcil.
65
16 Ativao de funo
Uma funo comea sua execuo assim que for chamada. Cada execuo da funo chamada de ativao
da funo. Como j mencionamos em notas de aula anteriores, variveis locais so locais a ativao da
funo: cada ativao possui suas prprias variveis locais. No comeo da ativao, memria alocada para
as variveis locais e no final da execuo, elas so dealocadas.
Definies de funes em C no podem ser aninhadas, mas ativaes de funo podem: uma funo,
digamos A, pode chamar uma outra funo, digamos B (dizemos que A chama B). Nos referimos a A como
o chamador e B como a funo chamada.
O que acontece quando uma funo chama outra (quando A chama B)? Um registro especial, chamado
registro de ativao criado. A informao neste registro necessria para a ativao da funo chamada e
para a reativao do chamador depois que a execuo da funo chamada termina.
1. C usa chamada-por-valor, ou seja, o chamador avalia as expresses que so os parmetros reais e passa
seus valores para a funo chamada.
5. O valor retornado para a funo chamadora atravs de um return colocado em um lugar especial
para que a funo chamadora possa encontr-lo. O controle retorna a funo chamadora.
#include <stdio.h>
main(void)
{
int a, b, temp;
66
/* Troca a com b */
temp = a;
a = b;
b = temp;
temp = a;
a = b;
b = temp;
possvel escrever uma funo que executa esta operao de troca? Considere a tentativa abaixo de escrever
esta funo:
#include <stdio.h>
temp = x;
x = y;
y = temp;
}
main(void)
{
int a, b;
/* Troca a e b */
troca(a, b);
67
Se voc executar este programa, ver que ele no funciona:
Como voc j viu nas notas anteriores, em C os argumentos so passados por valor. Uma vez que
somente os valores das variveis so passados, no possvel para a funo troca() alterar os valores
de a e b porque troca() no sabe onde na memria estas variveis esto armazenadas. Alm disso,
troca() no poderia ser escrito usando a sentena return porque s podemos retornar um valor (no
dois) atravs da sentena return.
int i = 5;
char c = G;
A varivel inteira i est armazenada no endereo 1342. Ela usa dois bytes de memria (quando um
objeto usa mais de um byte, seu endereo onde ele comea neste caso, 1342 e no 1343). A varivel do
tipo char c est armazenada no endereo 1346 e usa um byte de memria. O compilador que controla
do local de armazenamento destas variveis em memria.
68
17.3 Tipo ponteiro
Em C , uma varivel que contm um endereo de memria uma varivel do tipo ponteiro. Um valor, que
um endereo (como &a) um valor de ponteiro. Quando um ponteiro (a varivel) contm um determinado
endreo, dizemos que ele aponta para o endereo de memria (ou se este endereo de memria for associado
a uma varivel, dizemos que ele aponta para esta varivel).
H um tipo distinto de ponteiro para cada tipo bsico C (como int, char e float). verdade que
todos os endereos tem o mesmo tamanho (em Dev-C++ so 5 bytes), mas ns tambm precisamos saber
algo sobre o que armazenado no endereo de memria apontado (quantos bytes ocupa e como os bytes
devem ser interpretados). Por exemplo, um tipo ponteiro usado para apontar para inteiros chamado
ponteiro para int e isso denotado por um *. Variveis do tipo ponteiro para int so usados para armazenar
endereos de memria que contem valores do tipo int.
Por exemplo, dadas as definies de i e c acima, ns podemos definir duas novas variveis pi e pc,
ambos do tipo ponteiro.
int *pi;
char *pc;
Nesta definio no inicializamos as variveis com nenhum valor. Podemos inicializ-las com:
pi = &i;
pc = &c;
Depois destas atribuies, o valor de pi seria 1342, e o valor de pc seria 1346.
Note que nesta definio da varivel int *pi, pi o nome da varivel e int * o tipo de pi
(ponteiro para int).
#include <stdio.h>
temp = *px;
*px = *py;
*py = temp;
}
main(void)
{
int a, b;
Basicamente, se a funo precisa alterar o valor de uma varivels da funo chamadora, ento passamos
o endereo da varivel como parmetro real, e escrevemos a funo de acordo, ou seja, com um ponteiro
como parmetro formal.
Operador Associatividade
71
18 Arrays
Considere o seguinte programa. Este programa pede ao usurio notas de 4 estudantes, calcula a mdia e
imprime as notas e a mdia.
int main()
{
int nota0, nota1, nota2, nota3;
int media;
float tamanho[42];
72
O primeiro exemplo um array de 5 inteiros (o tipo int) com o nome total. Como a numerao de
arrays comea com 0, os elementos da array so numerados 0, 1, 2, 3 e 4.
O segundo exemplo um array de 42 elementos do tipo float com ndices de 0 a 41.
Cada elemento do array total do tipo inteiro e pode ser usado do mesmo jeito que qualquer varivel
inteira. Para nos referirmos a um elemento do array, usamos colchetes tambm ([ e ]). O valor dentro
dos colchetes pode ser qualquer expresso do tipo inteiro. Quando um array definido, armazenamento
suficiente (bytes contnuos na memria) so alocados para conter todos os elementos do array.
O nome do array representa um endereo de memria3 constante que aponta para o incio do espao de
armazenamento (o primeiro byte do bloco de bytes). O array, como um todo, no tem um valor, mas cada
elemento individual tem um valor.
Note na tabela de precedncia abaixo que [ ] tem precedncia maior que todos os demais operadores.
Operador Associatividade
x = total[3];
i = 4;
total[4]++;
tamanho[17] = 2.71828;
sala = 3;
scanf("%f", &tamanho[41]);
Agora, podermos reescrever o programa que calcula a mdia de uma classe de 4 alunos:
int main()
3
Este valor de endereo de memria conhecido em C como ponteiro. Este conceito ser visto com detalhes nos prximos
captulos.
73
{
int indice, nota[4], total = 0;
printf("Notas: ");
for (indice = 0; indice < 4; indice++) {
printf("%d ", nota[indice]);
total += nota[indice];
}
printf("\nMedia: %d\n", total/4 );
}
Exemplo de Sada:
Entre a nota do estudante 0: 93
Entre a nota do estudante 1: 85
Entre a nota do estudante 2: 74
Entre a nota do estudante 3: 100
Notas: 93 85 74 100
Media: 88
O programa consideravelmente mais curto. Note que um & ainda precede o elemento do array passado
para o scanf(). No necessrio usar parnteses porque [] tem maior precedncia que &.
O nico problema que ainda no fcil modificar o programa para cem alunos porque 4 est em vrios
pontos do programa. Ns podemos usar o #define para manter o tamanho do array como uma constante
simblica ao invs de utilizar uma constante numrica.
#define ESTUDANTES 4
int main()
{
int nota[ESTUDANTES], indice, total = 0;
indice = 0;
while (indice < ESTUDANTES) {
printf("Entre a nota do estudante %d: ",indice);
scanf("%d", ¬a[indice]);
indice += 1; /* mesma coisa que "indice = indice + 1" */
}
printf("Notas: ");
indice = 0;
while (indice < ESTUDANTES) {
printf("%d ", nota[indice]);
total = total + nota[indice];
indice += 1; /* mesma coisa que "indice = indice + 1" */
}
printf("\nMedia: %d\n", total / ESTUDANTES );
}
74
18.2 Inicializao de arrays
Os arrays podem ser inicializados quando so definidos. Se o array no for inicializado, ento ele contem
valores indefinidos (tambm conhecidos como lixo).
Para inicializar um array, um valor para cada elemento deve ser especificado. Estes valores devem estar
entre chaves ({ e }) e so separados por vrgula (,). Alguns exemplos:
int erro[5];
H mais uma restrio na inicializao de um array. Os valores devem ser todos constantes nenhuma
varivel ou expresso permitida. O seguinte trecho de programa produz um erro porque um dos valores de
inicializao uma varivel:
int x = 21;
int yy[3] = { 1, 2, x }; /* ISTO ESTA ERRADO */
int main()
{
int ops[10], i;
Este programa conta de 0 a 10, inicializando cada elemento do array com 0. O problema ocorre quando
i tem o valor 10. Neste ponto, o programa coloca 0 em ops[10]. Isto pode produzir resultados indefinidos
(e desastrosos) embora o compilador no gere nenhum erro.
Entre um inteiro: 73
Entre um inteiro: 85
Entre um inteiro: 42
Entre um inteiro: -103
Entre um inteiro: 15
O maior e 85
Em main() a chamada para array max() tem valor como seu argumento, que copiado para o
parmetro formal a, que um array de inteiros. Note que o tamanho no foi especificado, somente o nome
do array, a. Porm tambm correto incluir o tamanho (isto uma questo de estilo escolha o que voc
preferir):
76
At este ponto, parece que no h diferena entre passar uma varivel simples e um array como argu-
mento para uma funo. Mas h uma diferena fundamental: QUANDO PASSAMOS O ARRAY COMO
ARGUMENTO, ALTERAES NO ARRAY FEITAS DENTRO DA FUNO ALTERAM O CON-
TEDO DO ARRAY PASSADO COMO PARMETRO REAL.
Note que isto no acontece quando uma varivel simples passada como argumento. Considere o
exemplo seguinte:
x=10
v[0]=60 v[1]=70 v[2]=80
O valor da varivel x do programa principal no se altera porque como j vimos nas notas de aula 7,
quando a funo troca chamada, o valor do argumento real x avaliado, que 10, este valor copiado
para o parmetro formal a da funo troca e a funo ento executada. O parmetro a da funo
tratada como varivel local, portanto quando atribumos 20 a a, estamos atribuindo 20 a uma varivel
local. Terminada a funo, a execuo retorna ao programa principal, que imprime o valor de x, que no foi
alterado, ou seja, imprime x=10.
Quando a funo troca_vet chamada, o array v passado como argumento e copiado para o
parmetro formal vet. A funo ento executada, e os elementos do array so alterados para 60, 70,
80. Como mencionado anteriormente, quando passamos um array como parmetro, as alteraes feitas no
array dentro da funo alteram o array passado como parmetro. Portanto, quando a funo termina e a
execuo continua no programa principal com a impresso dos valores dos elementos de v, ser impresso
60, 70, 80, os novos valores alterados de dentro da funo troca_vet.
Vamos entender por que quando passamos s o nome do array como argumento as alteraes afetam
o array passado como parmetro real. Como j mencionamos anteriormente, quando um array definido,
como v no programa principal acima, alocado espao suficiente na memria para conter todos os elementos
do array. Na ilustrao abaixo, so alocados 6 bytes de memria a partir do endereo 1342 para conter
o array. O array como um todo no tem um valor, mas cada elemento do array tem (neste caso, foram
inicializados com 30, 40, 50). O nome do array, na verdade, contm o endereo onde comea o array,
neste caso, o endereo 1342.
Portanto, quando passamos o nome do array como argumento para uma funo estamos na realidade
passando como argumento o endereo de memria onde comea o array. No exemplo anterior, 1342
passado como argumento para o parmetro formal vet da funo troca_vet. Portanto, da mesma forma
que no caso da varivel simples, o valor de v, que o endereo 1342, copiado para o parmetro vet
de troca_vet. Ento, quando a funo troca_vet executada, vet um array de elementos do tipo
int que comea no endereo 1342. Quando atribumos o valor 60 a vet[0], estamos atribuindo 60 ao
primeiro elemento do array que comea no endereo 1342. Como este o mesmo endereo onde comea
o array v do programa principal, quando a funo troca_vet termina, o array v enxergar o valor dos
elementos do array que comea no endereo 1342, que foram alterados pela funo.
Quando passamos variveis simples como argumento para uma funo estamos passando somente o
valor da varivel, portanto, de dentro da funo no possvel saber qual o endereo da varivel para poder
alter-la.
Lembre-se que o endereo s passado para a funo quando passamos o array COMO UM TODO
(ou seja, o nome do array, sem ser indexado por um elemento). Se passarmos como argumento apenas um
elemento do array, o comportamento o mesmo que se passssemos uma varivel simples. Ou seja, o nome
do array indexado por um valor entre colchetes refere-se ao valor do elemento do array, enquanto o nome
do array sozinho refere-se ao endereo onde comea o array. Assim, no programa abaixo:
77
A sada do programa :
v[0]=30
v[0]=60 v[1]=70 v[2]=80
Outro exemplo: a funo inicializaArray abaixo inicializa todos os elementos do array valor
com um valor passado como argumento pelo programa principal.
Como as alteraes feitas por inicializaArray so vistas do programa principal, depois da funo
inicializaArray ser executada, no programa principal todos os elementos do array valor tero o
valor 42.
18.5.1 O Problema
Escreva uma funo pesquisa linear que tem como argumento de entrada: um array de inteiros a
ser pesquisado, o tamanho do array, e uma chave (um valor inteiro) a ser procurado. A funo retorna um
inteiro: o ndice do elemento do array (se a chave for achada) ou -1 caso contrrio.
1. Prottipo:
2. Definio:
78
18.6 Exemplo: somar os elementos de dois arrays
18.6.1 O Problema
Escrever uma funo que some dois arrays de floats, do mesmo tamanho. Dar o resultado em um terceiro
array.
1. Prottipo:
1 --> 1 5 2 7 3 8 9 4 6
2 --> 1 2 5 7 3 8 9 4 6
3 --> 1 2 3 7 5 8 9 4 6
4 --> 1 2 3 4 5 8 9 7 6
5 --> 1 2 3 4 5 8 9 7 6
6 --> 1 2 3 4 5 6 9 7 8
7 --> 1 2 3 4 5 6 7 9 8
8 --> 1 2 3 4 5 6 7 8 9
Note que mesmo que se comessemos com um array ordenado de 9 elementos, ainda assim o algoritmo
dado faz 8 passagens sobre o array.
na primeira passagem sobre o array: comeando do ltimo elemento do array at o segundo elemento,
compare o valor de cada elemento com o valor do elemento anterior a ele. Se os elementos comparados
estiverem fora de ordem, trocar os seus valores. Depois que esta primeira passada terminar, o que
acontece que o menor elemento do array torna-se o primeiro elemento do array.
na segunda passagem pelo array: comeando com o ltimo elemento do array at o terceiro elemento,
compare o valor de cada elemento com o valor do elemento anterior a ele. Se os dois elementos
comparados estiverem fora de ordem, trocar os seus valores. Depois que esta passagem sobre o array
terminar, o segundo menor elemento do array ser o segundo elemento do array.
repetir a passagem sobre o array de maneira similar at que a ltima passagem ser simplesmente uma
comparao dos valores do ltimo elemento com o elemento anterior.
2 --> 1 2 9 8 7 6 5 4 3
3 --> 1 2 3 9 8 7 6 5 4
4 --> 1 2 3 4 9 8 7 6 5
5 --> 1 2 3 4 5 9 8 7 6
6 --> 1 2 3 4 5 6 9 8 7
7 --> 1 2 3 4 5 6 7 9 8
8 --> 1 2 3 4 5 6 7 8 9
Note que, tambm aqui, mesmo que se comessemos com um array ordenado de 9 elementos, ainda
assim o algoritmo dado faz 8 passagens sobre o array.
Isto pode ser melhorado da seguinte forma: Antes de comear cada passagem, inicializamos uma vari-
vel ordenado com 1. Se durante a passagem uma troca de valores ocorrer, trocamos o valor da varivel
para 0. Assim, se depois da passagem, o valor da varivel continuar sendo 1, isso significa que nenhuma
troca ocorreu e que o array est ordenado.
80
18.8.1 Algoritmo Bubble Sort otimizado
Enquanto o array nao estiver ordenado
2. Definicao
int main(void){
int arr1[4] = {10, 20, 30, 40};
int arr2[4];
81
19 Arrays Multidimensionais
Nas notas de aula anteriores, apresentamos arrays unidimensionais. Em C , possvel tambm definir arrays
com 2 ou mais dimenses. Eles so arrays de arrays. Um array de duas dimenses podem ser imaginado
como uma matriz (ou uma tabela).
Como voc deve ter imaginado, para definir e acessar arrays de dimenses maiores, usamos colchetes
adicionais ([ e ]). Por exemplo:
int tabela[3][5];
Define um array bidimensional chamado tabela que uma matriz 3 por 5 de valores do tipo int (15
valores no total). Os ndices da primeira dimenso vo de 0 a 2, e os ndices da segunda dimenso vo de 0
a 4.
Abaixo apresentamos um programa que imprime os elementos de um array bidimensional.
#include <stdio.h>
#define ALTURA 5
#define LARGURA 5
int main()
{
int x; /* numero da coluna */
int y; /* numero da linha */
int matriz [ALTURA] [LARGURA]; /* array 2-D [num_lins, num_cols] */
printf("Coordenadas: ");
scanf("%d,%d", &y, &x);
printf("\n");
printf("Coordenadas: ");
scanf("%d,%d", &y, &x);
}
}
Neste exemplo, matriz um array bidimensional. Ela tem nmero de elementos igual a ALTURAxLARGURA,
sendo cada elemento do tipo int.
O exemplo abaixo preenche os elementos de um array bidimensional com os valores que representam a
taboada e imprime a matriz.
ATENO: a partir daqui os exemplos usam a estrutura de controle for. Veja a explicao sobre esta
estrutura (uma variao do while()) na Seo 15.1.
#include <stdio.h>
#define LIN 10
#define COL 10
int main()
{
int x; /* numero da coluna */
int y; /* numero da linha */
int tabela[LIN] [COL]; /* tabela de taboada */
/* preenche a tabela */
printf("%6d", 0);
83
for (x=1; x < COL; x+=1)
printf("%3d", x);
printf("\n");
A sada do programa :
Tabela de Multiplicacao
0 1 2 3 4 5 6 7 8 9
------------------------------
0| 0 0 0 0 0 0 0 0 0 0
1| 0 1 2 3 4 5 6 7 8 9
2| 0 2 4 6 8 10 12 14 16 18
3| 0 3 6 9 12 15 18 21 24 27
4| 0 4 8 12 16 20 24 28 32 36
5| 0 5 10 15 20 25 30 35 40 45
6| 0 6 12 18 24 30 36 42 48 54
7| 0 7 14 21 28 35 42 49 56 63
8| 0 8 16 24 32 40 48 56 64 72
9| 0 9 18 27 36 45 54 63 72 81
19.1 Inicializao
Arrays multidimensionais podem ser inicializados usando listas aninhadas de elementos entre chaves. Por
exemplo, um array bidimensional tabela com trs linhas e duas colunas pode ser inicializado da seguinte
forma:
Quando o array inicializado, o tamanho da primeira dimenso pode ser omitido. A definio de array
abaixo equivalente a dada anteriormente.
84
double tabela[][2] = { {1.0, 0.0}, /* linha 0 */
{-7.8, 1.3}, /* linha 1 */
{6.5, 0.0} /* linha 2 */
};
pode ser imaginado como um array unidimensional de 4 elementos do tipo int[], ou seja, arrays de int;
cada um dos 4 elementos um array de 5 elementos do tipo int:
Quando uma funo com um parmetro formal do tipo array chamada, na chamada da funo somente
o nome do array passado como parmetro real. O tipo (e portanto a dimenso) do array passado como
parmetro real deve ser consistente com o tipo (e portanto a dimenso) do array que o parmetro formal.
O programa abaixo mostra o exemplo da tabela de multiplicao escrita usando funes.
85
/* Exemplo de array 2-D - tabela de multiplicacao */
#include <stdio.h>
#define LIN 10
#define COL 10
int main()
{
int tabela[LIN] [COL];
inicializa_arr(tabela, LIN);
imprime_arr(tabela, LIN);
}
/* preenche o array */
printf("%6d", 0);
for (x=1; x < COL; x+=1)
printf("%3d", x);
printf("\n");
86
printf(" ");
for (x=0; x < 3*COL; x+=1)
printf("_");
printf("\n");
#include <stdio.h>
#define ALTURA 5
#define LARGURA 5
87
printf("\nEntre com as coordenadas na forma y,x (2,4).\n");
printf("Use numeros negativos para terminar.\n");
while (1)
{
printf("Coordenadas: ");
scanf("%d,%d", &y, &x);
if (x >= 0 && y >= 0)
{
matriz[y][x]=\xB1; /* preenche o elemento com quadrado */
imprime_matriz(matriz, nlin); /* imprime a matriz */
}
else
break;
}
}
printf("Triangulo\n");
pontos(matriz, nlin);
for (y = 0; y < nlin; y+=1)
for (x = 0; x <= y; x+=1)
matriz[y][x] = \xB1;
}
88
printf("Flipado ao longo da diagonal principal.\n");
for (y = 0; y < nlin; y+=1)
for (x = 0; x <= y; x+=1){
temp = matriz[y][x];
matriz[y][x] = matriz[x][y];
matriz[x][y] = temp;
}
}
pontos(matriz, ALTURA);
seleciona_elem(matriz, ALTURA);
espera_entrada();
flip(matriz, ALTURA);
imprime_matriz(matriz,ALTURA);
espera_entrada();
89
Parte II
Tpicos Avanados
As sees seguintes apresentam temas mais avanados da linguagem C , no abordadas em sala de aula.
Elas podem ser estudadas pelo aluno como atividade complementar, pois apresentam mecanismos bastantes
teis em programas mais complexos (tratamento de arquivos e textos, manipulao dinmica de memria,
etc.).
90
20 Operadores e Expresses Especiais
20.1 Operao de Atribuio Aritmtica
freqente em programas C expresses do tipo:
x = x * (y + 1);
j = j - 1;
C fornece operadores adicionais que podem ser usados para tornar estes tipos de atribuies mais curtos.
H um operador de atribuio para cada operao aritmtica listada anteriormente:
Cada uma dessas operaes podem ser usadas para tornar as expresses anteriores mais curtas:
tudo += parte;
tamanho *= 2.5;
x *= y + 1;
j -= 1;
k = k + 1;
j = j - 1;
Estes operadores adicionais, que so ++ and --, podem ser usados para encurtar as operaes acima:
k++;
j--;
91
++k;
--j;
O fato do operador de incremento ser colocado antes ou depois da varivel no altera o efeito da operao
o valor da varivel incrementada ou decrementada de um. A diferena entre os dois casos QUANDO
a varivel incrementada. Na expresso k++, o valor de k primeiro usado e ento incrementada isto
chamado ps-incremento. Na expresso ++k, k incrementado primeiro, e ento o valor (o novo valor) de
k usado isso chamado pr-incremento.
A diferena ilustrada nos seguintes exemplos:
int main()
{
int k = 5;
Para o programa:
int main()
{
int k = 5;
Os operadores de atribuio no podem ser usados com expresses aritmticas. Por exemplo, as expres-
ses
(ack + 2)++;
(nope + 3) += 5;
resultaro em erros de compilao.
Finalmente, quando usar o operador de incremento em um printf(), tome cuidado para no fazer o
seguinte:
92
printf("%d %d\n", ++uhoh, uhoh * 2);
Embora isso seja perfeitamente legal em C , os resultados no so garantidados que sejam consistentes.
A razo para isso que no h garantia que os argumentos do printf() sejam avaliados em uma deter-
minada ordem. O resultado do printf() ser diferente dependendo se ++uhoh avaliado primeiro ou
depois de uhoh * 2.
A soluo para este problema escrever o seguinte:
++uhoh;
printf("%d %d\n", uhoh, uhoh * 2);
93
21 Mais sobre tipos: converso implcita e explcita
Expresses no tem somente um valor, mas tambm tem um tipo associado.
Se ambos os operandos de uma operao aritmtica binria so do mesmo tipo, o resultado ter o mesmo
tipo. Por exemplo:
3 + 5 8, e o tipo int
3.5 + 2.25 5.75, e o tipo double
30 / 5 6
31 / 5 6
29 / 5 5
3 / 5 0
Lembre-se de evitar escrever algo como 1 / 2 * x significando 21 x. Voc sempre obter o valor 0
porque 1 / 2 * x (1 / 2) * x que 0 * x que 0. Para obter o resultado desejado, voc poderia
escrever 1.0 / 2.0 * x.
O sinal de < significa que o tipo da esquerda promovido para o tipo da direita, e o resultado ser do
tipo mais a direita. Por exemplo:
3.5 + 1 4.5
4 * 2.5 10.0
Esta regra estende-se para expresses envolvendo mltiplos operadores, mas voc deve se lembrar que
a precedncia e associatividade dos operadores pode influenciar no resultado. Vejamos o exemplo abaixo:
int main()
{
int a, b;
Basicamente, se o valor da expresso do lado direito da atribuio de um tipo que no cabe no tamanho
do tipo da varivel do lado esquerdo, resultados errados e no esperados podem ocorrer.
(nome-do-tipo)expressao
95
O parnteses NO opcional na expresso acima.
Podemos usar o cast de tipos da seguinte forma:
int fahr = 5;
float cels;
Agora que conhecemos o operador de cast de tipo podemos reescrever o programa que faz a converso
de frao para decimal.
int main()
{
int a, b;
O cast de tipo tem a maior precedncia possvel, portanto podemos fazer o cast de a ou de b para ser do
tipo float, e no h necessidade de parnteses extra. No exemplo acima, o cast causa o valor da varivel
a ser convertido para float, mas no causa mudana no tipo da varivel a. O tipo das variveis definido
uma vez na declarao e no pode ser alterado.
96
22 Tipo Enumerado
Em muitos programas, variveis do tipo int so utilizadas no por suas propriedades numricas, mas para
representar uma escolha dentre um pequeno nmero de alternativas. Por exemplo:
A utilizao de cdigos para representar os valores que uma varivel poder assumir, certamente com-
promete a clareza da estrutura de dados do programa, tornando sua lgica obscura e inconsistente. Por
exemplo:
cor = 3;
if( sexo == 2 ) ...
cor = cor + sexo;
for( cor = 1; cor < 10; cor ++ )...
Um tipo enumerado permite definir uma lista de valores que uma varivel deste tipo poder assumir. A
definio de um tipo enumerado feita da seguinte forma:
var1 = AMARELO;
var3 = QUI;
um erro usar valores no definidos na declarao do tipo. A expresso var2 = AZUL; causa erro de
compilao.
Internamente, o compilador trata variveis enumeradas como inteiros. Cada valor na lista de valores
possveis corresponde a um inteiro, comeando com 0 (zero). Portanto, no exemplo enum TpCores,
VERMELHO armazenado como 0, AMARELO armazenado como 1, e VERDE armazenado como 2.
97
#include <stdio.h>
int main(){
int diaPgto, mesPgto, anoPgto;
int diaSem;
Este programa ficaria mais legvel se ao invs de codificar os dias da semana como inteiros e colocar a
codificao como comentrio, utilizar tipos enumerados. O programa ficaria ento
#include <stdio.h>
int main(){
int diaPgto, mesPgto, anoPgto;
int diaSem;
Note que a funo diaDaSemana agora retorna apenas um dos valores da lista SEG, TER, QUA,
QUI, SEX, SAB, DOM e portanto, no programa principal ao invs de testar se o diaSem == 5 pode-
mos escrever diaSem == SAB, o que torna o programa muito mais legvel.
98
23 Entrada e Sada Padro
A forma com que um programa em C se comunica com o mundo externo atravs de entrada e sada de
dados: o usurio fornece dados via teclado e o programa imprime mensagens na tela. Todos os programas
vistos at agora lem suas entradas do teclado e produzem suas sadas na tela.
Em C toda entrada e sada feita com fluxos (streams) de caracteres organizados em linhas. Cada
linha consiste de zero ou mais caracteres e termina com o caracter de final de linha. Pode haver at 254
caracteres em uma linha (incluindo o caracter de final de linha). Quando um programa inicia, o sistema
operacional automaticamente define quem a entrada padro (geralmente o teclado) e quem a sada
padro (geralmente a tela).
As facilidades de entrada e sada no fazem parte da linguagem C . O que existe uma biblioteca padro
de funes para manipular a transferncia de dados entre programa e os dispositivos (devices) de sada
e entrada padro. Algumas destas funes so: scanf(), printf(), getchar(), puts(),
gets(). Estas funes so declaradas no arquivo <stdio.h>. Existem funes teis para converso
e teste de caracteres declaradas no arquivo <ctype.h>.
As funes de entrada e sada operam sobre streams (fluxos) de caracteres. Toda vez que uma funo se
entrada chamada (por exemplo, getchar(), scanf()) ela verifica pela prxima entrada disponvel
na entrada padro (por exemplo, texto digitado no teclado). Cada vez que uma funo de sada chamada,
ela entrega o dado para a sada padro (por exemplo, a tela).
As funes para leitura da entrada padro e para escrita na sada padro que tm sido usadas at agora
so:
#include <stdio.h>
99
int main()
{
char ch;
ch = getchar();
Outro exemplo:
#include <stdio.h>
int main()
{
char ch;
ch = getchar();
putchar(ch);
#include <stdio.h>
main(){
100
int ch;
ch = getchar();
}
getchar() obtm sua entrada do teclado. Portanto, quando o programa acima executado, o programa
espera que o usurio digite alguma coisa. Cada caractere digitado mostrado no monitor. O usurio pode
digitar diversos caracteres na mesma linha, inclusive backspace para corrigir caracteres j digitados. No
momento que ele teclar RETURN , o primeiro caractere da sequncia digitada o resultado da funo
getchar(). Portanto, na instruo do programa acima o caractere (ou melhor, o seu cdigo ASCII) atribudo
a varivel ch. Note que o usurio pode ter digitado diversos caracteres antes de teclar RETURN , mas a
funo getchar() s comear a ler o que foi digitado depois que for teclado RETURN . Alm disso, com
uma chamada da funo getchar() s o primeiro caractere da sequncia digitada lida.
Voc deve saber que o caractere de nova linha, \n, que tem o cdigo ASCII 10, automaticamente
adicionado na sequncia de caracteres de entrada quando o RETURN teclado. Isso no tem importncia
quando a funo getchar() chamada uma nica vez, mas isto pode causar problemas quando ele usado
dentro de um lao.
No iniccio de qualquer programa que usa getchar(), voc deve incluir
#include <stdio.h>
Esta diretiva do pr-processador diz ao compilador para incluir informaes sobre getchar() e EOF
(mais sobre EOF mais tarde.).
Considere o seguinte programa:
#include <stdio.h>
int main(){
int ch;
#include <stdio.h>
int main()
{
S para esclarecer: voc deve teclar RETURN depois de entrar com o comando ^D (ou ^Z no MS-
Windows).
102
/* Pula o restante da linha */
while( getchar() != \n );
Note que isso no necessrio aps todas as chamadas a getchar() ou scanf(). S depois daquelas
chamadas que precedem getchar() (ou scanf()), especialmente em um lao.
A funo scanf() na realidade retorna um inteiro que o nmero de itens (valores) lidos com sucesso.
Voc pode verificar se o scanf() funcionou testando se o valor retornado igual ao nmero de especificado-
res de formato no primeiro argumento da funo.
int main(){
103
24 Arquivos
O armazenamento de dados em variveis e arrays temporrio. Arquivos so usados para armazenamento
permanente de grandes quantidades de dados (e programas) em dispositivos de armazenamento secundrio,
como discos.
s vezes no suficeinte para um programa usar somente a entrada e sada padro. H casos em que
um programa deve acessar arquivos. Por exemplo, se ns guardamos uma base de dados com endereos de
pessoas em um arquivo, e queremos escrever um programa que permita ao usurio interativamente buscar,
imprimir e mudar dados nesta base, este programa deve ser capaz de ler dados do arquivo e tambm gravar
dados no mesmo arquivo.
No restante desta seo discutiremos como arquivos de texto so manipulados em C . Como ser visto,
tudo ocorre de maneira anloga ao que acontece com entrada e sada padro.
FILE *fp;
fp = fopen("data.txt", "r");
#include <stdio.h>
....
FILE *fp;
char fnome[13];
char fmodo[3];
No exemplo acima se o arquivo no puder ser aberto com sucesso, uma mensagem apropriada exibida
na sada padro e o programa termina. Caso contrrio uma mensagem indicando o sucesso na abertura do
arquivo exibida e o programa continua sua execuo.
Cada arquivo aberto possui seu prprio file pointer. Por exemplo, se um programa vai manipular dois
arquivos diferentes arq1 and arq2 simultaneamente (um para leitura e outro para escrita), dois file pointers
devem ser usados:
Os valores de file pointer (FILE *) so chamados streams. Eles estabelecem conexo entre o programa
e o arquivo aberto. A partir do momento de abertura, o nome do arquivo irrelevante para o programa. Todas
as funes que operam sobre o arquivo usam o file pointer associado.
105
Terminada a manipulao do arquivo o programa deve fechar o arquivo. A funo padro fclose()
usada com este propsito. Ela quebra a conexo entre o file pointer e o arquivo. Esta funo toma como
argumento o file pointer que representa o arquivo a ser fechado. file to be closed. O prottipo de fclose() :
fclose() retorna 0, se h sucesso ou EOF em caso contrrio. Abaixo um exemplo de uso de fclose():
fclose(pf1);
fclose(pf2);
getc() returns the next character read from the file represented by the stream fp, or EOF if error or end of
file occurs. putc() writes the character ch in the file represented by the stream fp. It returns the character
written or EOF.
Abaixo segue um exemplo de programa que l um arquivo caracter a caracter e imprime o que foi lido
na sada padro (a tela do computador):
/************************************************************************
* L um caracter por vez de um arquivo e
* o imprime na sada padro
************************************************************************/
#include <stdio.h> /* para funes padro de E/S */
main()
{
FILE *fp;
char fnome[13];
int ch;
A funo fgets() l do arquivo conectado ao stream fp no mximo (n - 1) caracteres para o array str,
parando a leitura se o caracter \n (uma mudana de linha) encontrado; O caracter \n includo no
array e ao elemento do array seguinte atribudo \0 (final de string). A funo fgets() retorna str ou
o ponteiro nulo, NULL, se um erro ou final de arquivo ocorre.
A funo fputs() escreve no arquivo conectado ao stream fp a string str, retornando um nmero
no-negativo, ou EOF em caso de erro.
No exemplo a seguir usada a funo fgets() (e no gets(). Voc pode dizer por qu?) para salvar
em um arquivo um texto digitado atravs da entrada padro (stdin). Para sinalizar pelo teclado que voc
terminou de entrar o texto, deve-se teclar ^D (^Z Turbo C ) e ento a tecla ENTER.
/************************************************************************
* Escreve texto digitado em stdin em um arquivo
***********************************************************************/
#include <stdio.h> /* funes padro de E/S */
main()
{
FILE *fp;
char fnome[13];
char linha[81];
A funo fscanf() l do arquivo representado pelo stream fp sob controle de um string de formato format.
O string de formato geralmente contm converses (como %d, %s, %f) que dirigem a interpretao da
entrada. Os valores convertidos so atribudos para os argumentos subsequentes, cada qual devendo ser
um ponteiro. A funo fscanf() retorna quando o string de formato foi totalmente interpretado. O valor
retornado por fscanf() EOF se o final do arquivo foi atingido ou um erro ocorre, caso contrrio retorna a
quantidade de itens convertidos e atribudos.
A funo fprintf() escreve no arquivo conectado ao stream fp sob controle de um string de formato
format. O string de format contm dois tipos de objetos: caracteres ordinrios que so copiados do jeito
que so, e especificadores de converso que causam a converso e impresso dos argumentos seguintes de
fprintf(). O valor de retorno o nmero de caracteres escritos, ou negativo em caso de ocorrncia de erros.
Abaixo segue um exemplo simples de base de dados. Os dados so armazenados permanentemente em
um arquivo, agentes.txt. A base de dados contm registros de agentes secretos famosos. Para cada agente
um apelido e um nmero de cdigo so armazenados. Uma vez que ser usada e/s formatada, deve-se
conhecer o formato no qual os dados esto armazenados no arquivo da base de dados. Este formato : os
dados de diferentes agentes esto em linhas separadas; para cada agente em uma linha, tem-se primeiro o
apelido e ento o cdigo numrico, separados por espao.
O programa orientado a menu abaixo lista todos os registros e adiciona novos itens.
/****************************************************************
* programa com menu para operar uma base de dados de no mximo 50
108
* agentes secretos; a base de dados guardada permanentemente em
* um arquivo em disco.
****************************************************************/
109
/****** MAIN *********/
main()
{
struct pessoal agentes[50]; /* array de 50 estruturas */
int n; /* indice para o ultimo registro ativo */
char ch;
Uma amostra de uma execuo do programa segue abaixo. Inicialmente o contedo do arquivo agentes.txt
:
Klara 89
Edward 888
ZipZap 109
110
Uma amostra de execuo:
A seguir, apresenta-se a implementao das quatro funes cargadb(), novonome(), listatudo(), and sal-
vadb().
/***********************************************************************
* l a base de dados do arquivo (at EOF) no array em memria
* ENTRADA: um array de do tipo struct pessoal
* RETORNO: nmero de elementos lidos
* SUPOSIES: o tamanho da base de dados deve ter no mximo 50 registros
***********************************************************************/
int cargadb(struct pessoal pessoa[])
{
int i = 0;
FILE *fp; /* define ptr to FILE */
fp = fopen(FNOME, "r");
111
/*************************************************************************
* adiciona novo elemento ao indice n no array pessoa[],
* o valo da estrutura obtido da entrada padro
* ENTRADA: array pessoa[] -- to store the structure value
* n -- indice do elemento, incrementado a
* cada novo elemento
*************************************************************************/
int novonome(struct pessoal pessoa[], int n)
{
if (n < NUM)
{
printf("Digite nome e cdigo: ");
scanf("%s %d", pessoa[n].nome, &pessoa[n].agnum);
n++;
}
else
printf("No h mais espao\n");
return n;
}
/*************************************************************************
* imprime a base de dados na tela
* ENTRADA: array pessoa[] a imprimir
* n nmero de registros para imprimir
*************************************************************************/
void listatudo(struct pessoal pessoa[], int n)
{
int j;
/**************************************************************************
* Pergunta ao usurio se quer salvar a base de dados. Se a resposta SIM (s)
* abre o arquivo para escrita e grava o array no arquivo
* ENTRADA: array pessoa[] a ser salvo
* n nmero de registros a ser salvo
**************************************************************************/
void salvadb(struct pessoal pessoa[], int n)
{
int i;
FILE *fp;
112
fp = fopen(FNOME, "w");
printf("Salvando...");
for (i = 0; i < n; i++)
{
fprintf(fp, "%s %d\n", pessoa[i].nome, pessoa[i].agnum);
}
fclose(fp);
printf("Feito.\n");
}
else
printf("Alteraes no foram salvas.\n");
}
113
25 Array de Caracteres
Nas notas de aula anteriores, enfatizamos arrays de nmeros. Em geral, podemos ter arrays com elementos
de qualquer um dos tipos vistos at agora (incluindo arrays visto nas notas de aula 9). Nesta seo,
apresentaremos arrays com elementos do tipo char.
Abaixo, apresentamos um exemplo de programa que define e inicializa um array de caracteres, e depois
imprime o array em ordem reversa.
#include <stdio.h>
int main(void)
{
char arr1[] = {c,i,2,0,8};
int i;
Arrays de caracteres so usados para armazenar texto, mas muito inconveniente se tivermos que colocar
cada caractere entre apstrofes. A alternativa dada pela linguagem C
Neste caso, ci208 um string de caracteres ou uma constante do tipo string. Ns j usamos strings
antes, com as funes printf() e scanf() (constantes do tipo string esto sempre entre aspas - "):
26 Strings
Strings so arrays de caracteres (arrays com elementos do tipo char) que DEVEM terminar com \0 (o
caracter NULL). Se voc usa o nome NULL em seu programa, ento necessria a definio #define NULL \0.
No exemplo acima, embora no tenhamos escrito explicitamente o caracter NULL, o compilador au-
tomaticamente o colocou como o ltimo elemento do array arr2[]. Portanto, o tamanho de arr2[]
6: 5 para os caracteres que digitamos (ci208) e 1 para o caractere NULL que o compilador introduziu
automaticamente. As definies abaixo so equivalentes.
/* a maneira tediosa */
char name1[] = { j,o,s,e, ,s,i,l,v,a,\0 };
/* e a maneira facil */
char name2[] = "jose silva";
114
Embora o primeiro exemplo seja um string, o segundo exemplo mostra como strings so geralmente
escritos (como constantes). Note que se voc usar aspas quando escreve uma constante, voc no precisa
colocar \0, porque o compilador faz isso para voc.
Quando voc for criar um array de caracteres de um tamanho especfico, lembre-se de adicionar 1 para
o tamanho mximo de caracteres esperado para armazenar o caractere NULL. Por exemplo, para armazenar
o string programar e divertido, voc precisa de um array de tamanho 22 (21 caracteres + 1 para
o NULL).
int main()
{
char mensagem[] = "tchau";
printf("ola\n%s\n", mensagem);
}
ola
tchau
A funo puts() simplesmente imprime um string e depois pula de linha. Nenhuma opo de for-
matao pode ser definida. A funo puts() somente pega um string como argumento e o imprime. O
programa abaixo tem a mesma sada que o programa anterior.
int main()
{
char mensagem[] = "tchau";
puts("ola");
puts(mensagem);
}
A funo gets() l uma linha de texto digitado no teclado e a armazena em um string. Veja o exemplo
abaixo:
#include <stdio.h>
int main(void)
{
char nome[100];
115
Exemplo de execuo
Passando um nome de array para a funo gets(), como ilustrado no programa acima, coloca a linha
inteira digitada pelo usurio no array nome (tudo at que seja teclado enter). Note que se o usurio digitar
caracteres demais (neste caso, mais de 99 caracteres), isso causar um erro de acesso fora dos limites (que
pode ser PERIGOSO !!)
A funo scanf() pode ser usada de maneira similar. A nica diferena que o scanf() l somente
a primeira palavra (tudo at que de digite um separador um espao em branco, tabulao, ou enter). Alm
disso, como estamos passando um array como argumento para o scanf(), O & QUE GERALMENTE
PRECEDE O ARGUMENTO NO DEVE ESTAR PRESENTE.
#include <stdio.h>
int main()
{
char nome[100];
Exemplo de execuo
Note que somente o primeiro nome lido pelo scanf() porque a funo pra no primeiro espao em
branco que encontra (enquanto gets() pra quando encontra um enter).
#include <stdio.h>
int main()
{
char nomes[NUM_NOMES][TAM] = {"Jose Silva",
"Maria Silva",
"Antonio dos Santos",
"Pedro dos Santos",
"Joao da Silva"};
int i;
116
for(i = 0; i < 5; i += 1)
printf("%s\n", nomes[i]);
}
Jose Silva
Maria Silva
Antonio dos Santos
Pedro dos Santos
Joao da Silva
A funo strlen() tem como argumento um string. Ela retorna um inteiro que o comprimento do
string (o nmero de caracteres do string, no contando o caractere NULL). Por exemplo, o comprimento do
string alo 3.
#include <stdio.h>
#include <string.h>
int main(void)
{
char nome[100];
int comprimento;
Um exemplo de execuo:
return comprimento;
}
A ordem relativa do trs conjuntos (dgitos, letras maisculas e letras minsculas) depende do computador
utilizado.
Se s1 e s2 so strings, o resultado da chamada de funo strcmp(s1, s2) :
se s1 =s s2, strcmp() retorna 0
se s1 <s s2, strcmp() retorna um nmero negativo (< 0)
se s1 >s s2, strcmp() retorna um inteiro positivo (> 0)
(onde =s , <s e >s so =, < e > para strings)
s1 <s s2 significa s1 vem antes de s2 no dicionrio. Exemplos: tudo menor que xadrez,
calor menor que calorao, frio menor que quente, e claro o string vazio , NULL,
menor que qualquer string.
Considere o exemplo abaixo que usa strcmp():
#include <stdio.h>
#include <string.h>
int main(void)
{
char palavra1[100], palavra2[100];
int resultado;
if (resultado == 0)
printf("igual\n");
else if (resultado > 0)
printf("o primeiro e maior\n");
118
else
printf("o segundo e maior\n");
}
while (1)
{
if (s1[i] == NULL && s2[i] == NULL)
return 0;
else if (s1[i] == NULL)
return -1;
else if (s2[i] == NULL)
return 1;
else if (s1[i] < s2[i])
return -1;
else if (s1[i] > s2[i])
return 1;
else
++i;
}
}
Na biblioteca padro, a funo strcmp() faz distino entre letras maisculas e minsculas. Se voc
no quer que a funo faa esta distino, voc pode modificar o seu string para ter apenas letras minsculas
(ou maisculas) antes de pass-lo como argumento para a funo strcmp(). Para fazer isso, voc pode
usar a funo da biblioteca padro tolower(), que tem como argumento um caractere. Se o caractere
passado uma letra maiscula, ele retorna esta letra minscula; caso contrrio, retorna o mesmo caractere.
Por exemplo: tolower(A) a, tolower(1) 1, tolower(a) a.
#include <stdio.h>
#include <string.h>
int main(void)
{
char pal[100], palCopia[100];
Embora este programa pudesse ter sido escrito sem usar strcpy(), o objetivo mostrar que se pode
usar strcpy() para fazer atribuio de strings.
A funo strcpy() poderia ter sido implementada da seguinte forma:
120
27 Estruturas
A estrutura de dados array usada para conter dados do mesmo tipo junto. Dados de tipos diferentes
tambm podem ser agregados em tipos chamados de estruturas ou registros (tipo struct em linguagem
C). Primeiro, o tipo estrutura declarado (precisamos especificar que tipos de variveis sero combinados
na estrutura), e ento variveis deste novo tipo podem ser definidas (de maneira similar que usamos para
definir variveis do tipo int ou char).
struct facil {
int num;
char ch;
};
Esta declaracao cria um novo tipo chamado struct facil que contm um inteiro chamado num e
um caracter chamado ch.
Tal definio est associada com a alocao de memria: memria suficiente ser alocada para guardar
um int e um char (nesta ordem). Como qualquer outra varivel, fac1 tem um nome, um tipo, e um
endereo associados.
Variveis de estruturas possuem tambm valores, e como outras variveis locais, se elas no tem atribu-
das um valor especfico, seu valor indefinido.
possvel definir variveis durente a declarao do tipo estrutura:
struct facil {
int num;
char ch;
} fac1;
121
Note-se que sem conflito, nomes de membros (tais como num e ch) podem ser usados como nomes de
outras variveis independentes (fora do tipo estrutura definido) or como nomes de membros em outros tipos
estrutura. No entanto, deve-se evitar situaes que criem confuso.
27.3 Acesso a membros de uma estrutura: ponto (.), o operador membro de estrutura
Dada uma varivel de estrutura, um membro especfico referenciado usando o nome da varivel seguida de
. (ponto) e pelo nome do membro da estrutura. Assim, as seguintes referncias a membros de uma estrutura
so vlidas:
fac1.num se refere ao membro com nome num na estrutura fac1;
fac1.ch se refere ao membro com nome ch na estrutura fac1.
Membros de estrutura (como fac1.num) so variveis, e podem ser usadas como valores (no lado
direito de uma atribuio, em expresses como argumentos para funes), ou como lvalues (no lado esquerdo
de atribuies, com operadores de incremento/decremento ou com o operador de endereo (&)). O exemplo
abaixo mostra alguns exemplos do uso de membros de estrutura:
fac1.ch = G;
fac1.num = 42;
fac1.num++;
if (fac1.ch == H) {
printf("%d\n", fac1.num);
}
struct facil {
int num;
char ch;
};
main()
{
122
struct facil fac1, fac2;
fac1.num = 3;
fac1.ch = C;
Lembre-se que este tipo de atribuio ilegal com arrays. Tentar fazer isto com dois arrays causa um
erro de compilao (uma vez que nomes de arrays so ponteiros constantes).
struct facil {
int num;
char ch;
};
main()
{
struct facil fac1 = { 3, C }, fac2;
fac2 = fac1;
}
Uma lista de valores separados por vrgula fica entre chaves ({ and }). Os valores de inicializao devem
estar na mesma ordem dos membros na declarao da estrutura.
#define LEN 50
struct endereco {
char rua[LEN];
char cidade_estado_cep[LEN];
};
return ender;
}
main()
{
struct endereco residencia;
No exemplo acima, a estrutura struct endereco contm dois arrays de tamanho 50. Dentro da
funo obtem_endereco(), a varivel ender declarada como sendo do tipo struct endereco.
Aps usar gets() para o fornecimento da informao, o valor de ender retornado para main(), de
onde a funo obtem_endereco() foi chamada. Este valor ento passado para a funo imprime_endereco(),
onde o valor de cada membro da estrutura exibido na tela.
Este programa pode ser comparado ao programa abaixo, que usa valores do tipo int no lugar de valores
do tipo struct endereco (claro que a informao lida e exibida um simples valor numrico, e no
um nome de rua, etc.):
int obtem_int(void);
void imprime_int(int);
int obtem_int(void)
{
int i;
return i;
}
124
void imprime_int(int i)
{
printf("%d\n", i);
}
main()
{
int valor;
valor = obtem_int();
printf("\nSeu valor:\n");
imprime_int(valor);
}
#define LEN 50
#define NUM 10
struct endereco {
char rua[LEN];
char cidade_estado_cep[LEN];
};
main()
{
struct endereco residencias[NUM];
int i;
125
for (i = 0; i < NUM; i++) {
printf("Entre o endereco da pessoa %d:\n", i);
obtem_endereco(residencias,i);
}
Neste programa, o array residencias passado para obtem_endereco(), juntamente com o in-
dice onde deve ser guardado o novo endereo. Depois, cada elemento do array passado para imprime_endereco()
um por vez.
Observe-se ainda na funo obtem_endereco() como os membros de cada elemento do array po-
dem ser acessados. elements da estrutura em can be accessed as well. Por exemplo, para acessar a rua do
elemento residencias[0] usa-se:
printf("%s\n", residencias[0].rua);
printf("%s\n", residencias[0].cidade_estado_cep);
#define LEN 50
struct endereco {
char rua[LEN];
char cidade_estado_cep[LEN];
};
struct student {
char id[10];
int idade;
struct endereco casa;
struct endereco escola;
};
Dadas estas definies, pode-se potencialmente acessar os seguintes campos de pessoa, uma varivel
do tipo struct student:
pessoa.id
pessoa.casa.rua
pessoa.casa.cidade_estado_cep
pessoa.escola.rua
pessoa.escola.cidade_estado_cep
int i = 5;
char c = G;
A varivel inteira i est armazenada no endereo 1342. Ela usa dois bytes de memria (quando um
objeto usa mais de um byte, seu endereo onde ele comea neste caso, 1342 e no 1343). A varivel do
tipo char c est armazenada no endereo 1346 e usa um byte de memria. O compilador que controla
do local de armazenamento destas variveis em memria.
tipo *nome_var;
4
O operador sizeof() pode ser usado para determinar o tamanho de um ponteiro. Por exemplo, sizeof (char *)
127
Esta declarao indica que est sendo definido um ponteiro para tipo chamado nome_var. Por exemplo,
um tipo ponteiro usado para apontar para inteiros chamado ponteiro para int e isso denotado por um
int *. Variveis do tipo ponteiro para int so usadas para armazenar endereos de memria que contem
valores do tipo int.
Dadas as definies de i e c acima, ns podemos definir duas novas variveis pi e pc, ambos do tipo
ponteiro.
int *pi;
char *pc;
Nesta definio as variveis no foram inicializadas com nenhum valor. Podemos inicializ-las com:
pi = &i;
pc = &c;
Depois destas atribuies, o valor de pi seria 1342, e o valor de pc seria 1346.
Note que nesta definio da varivel int *pi, pi o nome da varivel e int * o tipo de pi
(ponteiro para int).
*pi no lado esquerdo do = refere-se ao endereo de memria para o qual pi aponta. *pi no lado direito
do = refere-se ao valor armazenado no endereo apontado por pi. A sentena *pi = *pi + 5; faz com
que o valor armazenado no endereo apontado por pi seja incrementado de 5. Note que o valor de *pi
muda, no o valor de pi.
Neste exemplo, os valores das variveis i e c poderiam ter sido alterados sem a utilizao de ponteiros
da seguinte forma:
printf("Valor = %d, Char = %c\n", i, c);
i = i + 5;
c = H;
Os exemplos acima ilustram como uma varivel pode ser acessada diretamente (atravs do seu nome)
ou indiretamente (atravs de um ponteiro apontando para o endereo da varivel).
p1 = &x; /* Correto */
p2 = p1; /* Correto */
p3 = p1; /* Incorreto. Compilador acusa "Warning". */
No exemplo acima, a linguagem C admite a atribuio de um ponteiro para outro de outro tipo (p3 = p1;),
mas a compilao acusa uma mensagem de aviso. Posteriormente sero vistas situaes em que a atribuio
de ponteiros de tipos diferentes devem ocorrer e como devem ser manipuladas em C.
pn = nome;
pstr = str;
pnum = num;
Um ltimo ponto a respeito de operaes sobre ponteiros: Adicionar um ponteiro a outro no produz
nenhum resultado prtico ou vlido.
Se um ponteiro aponta para um array, pode-se usar indistintamente as formas abaixo para acessar os
elementos do array:
int val[10],
x,
*ptr;
*(ptr + 3) = 7; /* val[3] = 7 */
ptr[3] = 10; /* val[3] = 10 */
/* Equivalente a *(ptr + 3) = 10 */
ptr += 4;
ptr[3] = 20; /* ATENCAO: val[7] = 20 */
main()
{
/* definioes de variaveis */
struct facil fac, /* uma variavel do tipo "struct facil" */
*pfac; /* um ponteiro para "struct facil" */
pfac = &fac;
Basicamente, use o operador . se voc tem uma varivel de tipo struct, e o operador -> caso voc
tenha um ponteiro para um tipo struct.
#include <stdio.h>
temp = x;
x = y;
y = temp;
}
main(void)
{
int a, b;
131
/* Troca a e b */
troca(a, b);
Quando a e b so passados como argumentos para troca(), na verdade, somente seus valores so
passados. A funo no pode alterar os valores de a e b porque ela no conhece os endereos de a e b.
Mas se ponteiros para a e b forem passados como argumentos ao invs de a e b, a funo troca() seria
capaz de alterar seus valores; ela saberia ento em que endereo de memria escrever. Na verdade, a funo
no sabe que os endereos de memria so associados com a e b, mas ela pode modificar o contedo destes
endereos. Portanto, passando um ponteiro para uma varivel (ao invs do valor da varivel), habilitamos a
funo a alterar o contedo destas variveis na funo chamadora.
Uma vez que endereos de variveis so do tipo ponteiro, a lista de parmetros formais da funo deve
refletir isso. A definio da funo troca() deveria ser alterada, e a lista de parmetros formais deve
ter argumentos no do tipo int, mas ponteiros para int, ou seja, int *. Quando chamamos a funo
troca(), ns no passamos como parmetros reais a e b, que so do tipo int, mas &a e &b, que so
do tipo int *. Dentro da funo troca() dever haver mudanas tambm. Uma vez que agora os
parmetros formais so ponteiros, o operador de dereferncia, *, deve ser usado para acessar os objetos.
Assim, a funo troca() capaz de alterar os valores de a e b remotamente.
O programa abaixo a verso correta do problema enunciado para a funo troca():
#include <stdio.h>
temp = *px;
*px = *py;
*py = temp;
}
main(void)
{
int a, b;
main()
{
char ender[20];
char vals[20];
func_1(ender, vals);
func_2(ender, vals);
}
Observe no exemplo acima que a passagem dos arrays ao se chamar as funes func_1() e func_2()
feita da mesma forma: Usa-se o NOME das variveis declaradas como arrays. Note tambm o uso dos
argumentos formais nas funes: vet e ivet podem ser usadas como ponteiros ou como nomes de arrays
(com a notao indexada por []).
133
28.8.2 Ponteiros para estruturas como argumentos de funes
Quando estruturas so passadas como argumentos para funes o valor de todo o objeto agregado passado
literalmente. Alm disso, se este valor alterado na funo, ele deve ser retornado (via return), o que
implica em copiar de volta toda a estrutura. Isto pode ser bastante ineficiente no caso de uma estrutura
grande (com muitos membros, com membros de tamanho grande como arrays, etc.). Assim, em alguns
casos melhor passar ponteiros para estruturas. Repare a diferena com arrays passados como argumentos
para funes vista na seo anterior.
O programa abaixo um exemplo do uso de passagem de ponteiros de estruturas para funes:
#define LEN 50
struct endereco {
char rua[LEN];
char cidade_estado_cep[LEN];
};
main()
{
struct endereco residencia;
printf("\nSeu endereco:\n");
imprime_endereco(residencia);
}
Neste caso, main() passa para a funo obtem_endereco() um ponteiro para a variavel residencia.
obtem_endereco() pode ento alterar o valor de residencia remotamente. Este valor, em main(),
ento passado para imprime_endereco(). Note-se que no necessrio passar um ponteiro para a
estrutura se seu valor no ser mudado (como o caso da funo imprime_endereco()).
De um modo geral, melhor passar ponteiros para estruturas ao invs de passar e retornar valores
de estruturas. Embora as duas abordagens sejam equivalentes e funcionem, o programa ir apenas passar
134
ponteiros ao invs de toda uma estrutura que pode ser particularmente grande, implicando em um tempo
final de processamento maior.
Operador Associatividade
135
29 Strings e Ponteiros
Vimos anteriormente que strings em C nada mais so que arrays de elementos do tipo char cujo ltimo
elemento o caracter \0 (nulo). At agora a declarao e inicializao de um string tem sido como abaixo:
No exemplo acima, cada conjunto de caracteres limitados por um par de aspas denominado de uma
constante string. Como toda expresso em C , uma constante string tem um valor e um tipo associado.
O compilador C automaticamente aloca espao no programa para armazenar este string. Esta constante
na verdade est definindo um array que est armazenado em um endereo de memria mas que no tm um
nome de varivel associado a ele. O valor produzido para esta constante string um ponteiro para este array
annimo. E o tipo da constante um ponteiro para char.
Assim, na maior parte dos casos de programas em C , mais conveniente representar um string atravs
de um ponteiro para char. O valor inicial deste ponteiro deve ser sempre o endereo do primeiro caracter
do string ou uma constante string. O exemplo acima pode ser reescrito como abaixo, que deve ser a forma
preferida para se declarar e inicializar strings em programas em linguagem C :
30 Arrays de ponteiros
Da mesma forma que se pode ter arrays de tipos bsicos (e.g. int, char) e de estruturas, pode-se tambm
definir arrays de ponteiros:
char *frases[60];
puts(frases[3]);
No exemplo acima cada elemento do array frase um ponteiro para char. Em cada uma das atri-
buies, cada elemento do array recebe como valor um ponteiro para um string (veja seo anterior sobre
constantes string).
136
O primeiro argumento, denominado argc por conveno, representa o nmero de argumentos digitados
na linha de comando, incluindo o nome do programa na linha de comando, que definido como o primeiro
argumento. Desta forma o valor de argc sempre maior ou igual a 1 (um).
Em UNIX, um argumento considerado como uma sequncia de caracteres at a primeira ocorrncia do
caracter de espaamento, que pode ser um espao em branco, tab ou o caracter de mudana de linha5 .
O segundo argumento de main o array de ponteiros para caracteres chamado argv, por conveno.
O primeiro ponteiro em argv, argv[0], aponta para o nome do programa em execuo. Os elementos
sucessivos do array, argv[1], argv[2], ..., argv[argc - 1], contm ponteiros para os argumentos
digitados na linha de comando aps o nome do programa.
Como um exemplo, considere a execuo do programa chamado nroff. Se a linha de comando abaixo
digitada:
ento o valor de argc ser 4 (o nome do programa mais os trs argumentos que se seguem), e o
argumento argv ser um array de ponteiros para caracteres. O primeiro elemento deste array aponta para
o string "nroff", o segundo para -mm", o terceiro para -TXR" e finalmente o quarto elemento aponta para
o string "memo1". Isto pode ser melhor visualizado na Figura 3.
#include <stdlib.h>
As funes malloc() e calloc() so usadas para alocar espao para seus dados uma vez que voc
determinou quanto espao voc precisa. E se sua estimativa se revela muito grande ou muito pequena, voc
pode mudar o tamanho deste espao alocado com a funo realloc. Finalmente, uma vez que o programa
terminou de usar o espao alocado, a funo free pode ser usada para liberar o espao previamente alocado.
Observe-se a diretiva #include que necessria para o uso das funes de alocao.
#include <stdlib.h>
char buf[30],
*memchar;
int *memint, i;
....
if (memchar == NULL) {
printf ("Erro em alocacao de memoria\n");
exit (1);
}
else {
memcpy (memchar, buf, sizeof(buf)); /* copia o contedo de
* buf para memchar
*/
}
if (memint[i]) {
memint = (int *) realloc (memint, (i + 2) * sizeof(int));
if (memint == NULL)
break;
}
} while (memint[i++]);
}
Oberve no exemplo acima o uso de cast na chamada de calloc. Como o ponteiro retornado ser usado
para apontar para inteiros, o retorno da funo deve ser convertido de acordo com o cast.
Quando no h sucesso na alocao de memria, as funes malloc e calloc retornam um ponteiro
nulo, representado pela constante NULL. Assim, toda vez que um programa usa estas funes, deve-se
testar o valor retornado para verificar se houve sucesso na alocao. o que acontece no exemplo acima ao
se testar os valores de memchar e memint imediatamente aps a chamada das funes de alocao.
32.2 realloc
A funo realloc usada para redimensionar um espao alocado previamente com malloc ou calloc.
Seus argumentos so um ponteiro para o INCIO de uma rea previamente alocada, e o novo tamanho, que
pode ser maior ou menor que o tamanho original.
realloc retorna um ponteiro para a nova rea alocada. Este ponteiro pode ser igual ao ponteiro
original se o novo tamanho for menor que o tamanho original, e diferente do ponteiro original se o novo
tamanho for maior que o tamanho original. Neste ltimo caso, realloc copia os dados da rea original
para a nova rea.
O ltimo bloco else no exemplo da seo anterior um exemplo de uso da funo realloc. Neste
bloco, usa-se a funorealloc para aumentar de 1 (um) o tamanho do array dinmico representado por
memint.
139
Referncias
[1] Allen I. Holub. Enough Rope to Shoot Yourself in the Foot: rules for C and C++ programming.
McGraw-Hill, 1995.
[2] Brian W. Kernighan and Dennis M. Ritchie. A Linguagem de Programao C. Editora Campus, 1986.
[3] Victorine Viviane Mizrahi. Treinamento em Linguagem C. Prentice-Hall, Inc., 2a edition, 2008.
140