You are on page 1of 125

Primeira parte: Introdução

Programação
Programação Avançada com C 1. A linguagem de programação C.
2. Funções. A função main.
Avançada com C 3. As instruções do C.
4. Entradas e saídas.
por 5. Tipos de dados.
6. Variáveis.
Pedro Guerreiro 7. Operadores.
pg@di.fct.unl.pt 8. Vectores.
http://ctp.di.fct.unl.pt/~pg/ 9. Operadores ++ e ––
10. Operadores de afectação.
Departamento de Informática
11. Ficheiros.
Faculdade de Ciências e Tecnologia
12. Cadeias de caracteres.
Universidade Nova de Lisboa
13. Ciclo de leitura.

1994-2002 14. Busca e ordenação

Programação Avançada com C 1 Programação Avançada com C 2

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Características do C Bibliografia
• É uma linguagem pequena (49 páginas para 1. Kernighan, Ritchie, The C Programming
o manual de referência + 18 páginas para a Language (ANSI C), 2.ª edição, Prentice-
biblioteca) Hall (1988).
• É uma linguagem com tipos frouxos 2. Hutchison, Just, Programming Using The C
(loosely typed language). Language, McGraw-Hill (1988).
• Tem todas as estruturas de controlo 3. Müldner, Steele, C as a Second Language
modernas: if-else, while-do, do-while, for, for Native Speakers of Pascal, Addison-
case (switch). Wesley (1988).
• Trabalha com caracteres, números inteiros 4. Schildt, C: The Complete Reference,
e números reais. McGraw-Hill (1987).
• Dispõe de construtores de tipos para 5. Schildt, C: Advanced C, 2.ª edição,
quadros e registos (estruturas). McGraw-Hill (1988).
• Serve para manipular objectos ao nível do 6. Galland, Le Langage C: Pratique et
bit. Environnement, Dunod (1989).
• Implementa apontadores muito versáteis. 7. Dewhurst, Stark, Programming in C++,
• Permite a compilação separada. Prentice Hall (1989).
8. Guerreiro, Elementos de Programação com
C, Europa-América (1991), reeditado em
2001 pela FCA.

Programação Avançada com C 3 Programação Avançada com C 4

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O primeiro programa em C Componentes do primeiro programa
#include <stdio.h>
K&R:
K&R: Directiva para “incluir” o ficheiro stdio.h.
“The
“Thefirst
firstprogram
programto
towrite
writeis
isthe
thesame
samefor
for
all languages: stdio.h
all languages:
Ficheiro que contém informação sobre a
Print
Printthe
thewords
words biblioteca de IO do C.
hello,
hello, world
world
main()
”” {
printf("Olá, pessoal\n");
}
A função main. Todos os programas C têm
Versão Lisboa 1991: uma.

#include
#include <stdio.h>
<stdio.h> printf
main()
main() Uma função de biblioteca que serve para
escrever coisas.
{{
printf("Olá, "Olá, pessoal\n"
printf("Olá, pessoal\n");
pessoal\n");
}} Uma cadeia de caracteres.
'\n'
A representação do carácter newline.
Programação Avançada com C 5 Programação Avançada com C 6

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Problema dos pontos na Fórmula 1 Problema dos pontos (1.ª solução)
/* Pontos numa corrida de F1; primeira versão */
#include <stdio.h>
Escrever um programa para calcular o
número de pontos que ganha um piloto no int main ()
campeonato mundial de fórmula 1, ao chegar {
ao fim num grande prémio. O programa int place;
pergunta em que lugar chegou o piloto e printf("Qual é o lugar? ");
responde afixando o número de pontos. scanf("%d", &place);

if (place == 1)
printf("Ganhou 10 pontos.\n");
1.º 10 pontos else if (place == 2)
2.º 6 pontos printf("Ganhou 6 pontos.\n");
else if (place == 3)
3.º 4 pontos printf("Ganhou 4 pontos.\n");
else if (place == 4)
4.º 3 pontos printf("Ganhou 3 pontos.\n");
else if (place == 5)
5.º 2 pontos printf("Ganhou 2 pontos.\n");
else if (place == 6)
6.º 1 ponto printf("Ganhou 1 ponto.\n");
else
7.º em diante 0 pontos printf("Não ganhou pontos.\n");
return 0;
}

Programação Avançada com C 7 Programação Avançada com C 8

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Os comentários A função main
Aparecem entre /* e */ int
int main
main ()
()
{{
...
Exemplos: ...
/* Pontos numa corrida de F1; primeira versão return
*/ return 0;
0;
}}
/* Escrito por Pedro Guerreiro */
/* Data: 91.03.19 */
/* Modificado em 91.03.22, por PG */
int
int main
main ()
()

Habilidades com comentários: A função main não tem parâmetros e


devolve um valor inteiro.

/***********************************************
* *
* *** * * * return
return 0;
0;
* * * * * *
* * *** **** *** * ** ** *
* * * * * * * * ** * * *
* * ***** * * * * * * * * Convencionalmente a função main devolve
* * * * * * * * * * * *
* *** *** **** *** * * * * 0 (zero), quando tudo corre bem!
* *
***********************************************/

Programação Avançada com C 9 Programação Avançada com C 10

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O tipo int A função printf
int int
int main
main ()
()
int main
main ()
() {{
{{
int int
int place;
place;
int place;
place;
... printf("Qual
printf("Qual éé oo lugar?
lugar? ");
");
... ...
}} ...
if
if (place
(place ==
== 1)
1)
printf("Ganhou
printf("Ganhou 1010 pontos.\n");
int
int main
main ()
() ...
pontos.\n");
...
}}
A função main devolve um número inteiro.
printf("Qual
printf("Qual éé oo lugar?
lugar? ");
");

int
int place;
place; printf("Ganhou
printf("Ganhou 10
10 pontos.\n");
pontos.\n");
place é uma variável inteira. A função printf escreve cadeias de
caracteres (e outras coisas também) no
ecrã do terminal.
O tipo int representa os números inteiros As cadeias de caracteres vêm entre aspas.
entre -32768 e 32767, nos computadores de \n é uma sequência de escape que
16 bits, e entre -2147483648 e 2147483647, representa o carácter newline.
nos computadores de 32 bits.
Para poder usar a função printf, é preciso
incluir o ficheiro <stdio.h>.
Programação Avançada com C 11 Programação Avançada com C 12

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A função scanf A igualdade ==
int
int main
main () int
() int main
main ()
()
{{ {{
int
int place;
place; ...
...
if
if (place
(place ==
== 1)
1)
printf("Qual
printf("Qual éé oo lugar?
lugar? ");
"); ...
...
scanf("%d",
scanf("%d", &place);
&place); else
else if
if (place
(place ==
== 2)
2)
...
...
...
... }}
}}

== é o operador relacional de igualdade


scanf("%d",
scanf("%d", &place);
&place);
place
place ==
== 11
A função scanf “lê” números, caracteres,
e cadeias de caracteres digitados no
teclado do terminal. expr1 == expr2
"%d" é uma cadeia de formato, que contém Vale 1 (um), se o valor de expr1 é igual ao
o especificador de conversão %d, de expr2; vale 0 (zero), se o valor de expr1
indicando que a função scanf está à
é diferente do valor de expr2.
espera de ler um número inteiro decimal.
A variável que vai receber o número != é o operador relacional de desigualdade
digitado tem que ser precedida pelo
operador &.
NB: == é diferente de =
Para poder usar a função scanf, é preciso
incluir o ficheiro <stdio.h>.
Programação Avançada com C 13 Programação Avançada com C 14

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A instrução if-else Instruções if-else em cascata
...
... if
if (place
(place ==
== 1)
1)
if
if (place
(place ==
== 1)
1) printf("Ganhou
printf("Ganhou 10 10 pontos.\n");
pontos.\n");
printf("Ganhou
printf("Ganhou 10 10 pontos.\n");
pontos.\n"); else
else if (place == 2)
if (place == 2)
else
else if
if (place
(place ==
== 2)
2) printf("Ganhou
printf("Ganhou 66 pontos.\n");
pontos.\n");
printf("Ganhou
printf("Ganhou 66 pontos.\n");
pontos.\n"); else
else if
if (place
(place ==
== 3)
3)
else
else if
if (place
(place ==
== 3)
3) printf("Ganhou
printf("Ganhou 44 pontos.\n");
pontos.\n");
printf("Ganhou
printf("Ganhou 44 pontos.\n");
pontos.\n"); else
else if
if (place
(place ==
== 4)
4)
else
else if
if (place
(place ==
== 4)
4) printf("Ganhou
printf("Ganhou 33 pontos.\n");
pontos.\n");
printf("Ganhou
printf("Ganhou 33 pontos.\n");
pontos.\n"); else
else if
if (place
(place ==
== 5)
5)
else
else if
if (place
(place ==
== 5)
5) printf("Ganhou
printf("Ganhou 22 pontos.\n");
pontos.\n");
printf("Ganhou
printf("Ganhou 22 pontos.\n");
pontos.\n"); else
else if
if (place
(place ==
== 6)
6)
else
else if
if (place
(place ==
== 6)
6) printf("Ganhou
printf("Ganhou 11 ponto.\n");
ponto.\n");
printf("Ganhou
printf("Ganhou 11 ponto.\n");
ponto.\n"); else
else
else
else printf("Não
printf("Não ganhou
ganhou pontos.\n");
pontos.\n");
printf("Não
printf("Não ganhou
ganhou pontos.\n");
pontos.\n");
...
...
}}
Dispor o código assim:
if (expressão)
instrução_1 if (expressão_1)
else instrução_1
instrução_2 else if (expressão_2)
instrução_2
Avalia-se a expressão; se tiver um valor else
diferente de 0 (zero) executa-se a instrução_3
instrução_1; se tiver um valor igual a 0
(zero) executa-se a instrução_2.

Programação Avançada com C 15 Programação Avançada com C 16

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A instrução return Problema dos pontos (2.ª solução)
#include <stdio.h>
int
int main
main ()
()
{{ int main ()
... {
... int place;
return
return 0;
0;
}} int pts;

printf("Qual é o lugar? ");


scanf("%d", &place);
return
return 0;
0; if (place == 1)
pts = 10;
else if (place == 2)
pts = 6;
else if (place == 3)
return expressão; pts = 4;
else if (place == 4)
pts = 3;
Avalia-se a expressão; logo a seguir, else if (place == 5)
termina a função; o valor calculado pela pts = 2;
else if (place == 6)
função é o valor que resultou da avaliação pts = 1;
da expressão. else
pts = 0;
if (pts > 1)
printf("Ganhou %d pontos.\n", pts);
else if (pts == 1)
printf("Ganhou 1 ponto.\n");
else
printf("Não ganhou pontos.\n");
return 0;
}

Programação Avançada com C 17 Programação Avançada com C 18

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Declaração de variáveis Instrução de afectação =
int int
int main
main ()
int main
main ()
() {{
()
{{
int int
int place;
int place;
place; int
place;
int
int pts;
pts; int pts;
pts;
...
...
... if
if (place
(place ==
== 1)
1)
... pts = 10;
}} pts = 10;
else
else if
if (place
(place ==
== 2)
2)
pts = 6;
pts = 6;
...
...
As variáveis têm que ser declaradas antes }}
de ser usadas.
E1 = E2
Uma declaração especifica um tipo e o
nome de uma variável:
Avalia-se a expressão E2; o resultado
int passa a constituir o valor de E1.
int place;
place;
int
int pts;
pts;
A afectação é uma expressão. O valor de
E1 = E2 é o valor afectado a E1.
ou de uma lista de variáveis:
int O operador = associa da direita para a
int place,
place, pts;
pts;
esquerda. Logo:
Nota: Normalmente é preferível declarar uma variável de E1=E2=E3 é o mesmo que E1=(E2=E3)
cada vez.
Programação Avançada com C 19 Programação Avançada com C 20

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A função printf A função printf
com vários argumentos (1) com vários argumentos (2)
...
...
if
if (pts
(pts >> 1)
1)
Exemplos:
printf("Ganhou
printf("Ganhou %d
%d pontos.\n",
pontos.\n",
pts);
pts);
else A cadeia de formato reduz-se à conversão:
else
...
...
}} printf("%d",
printf("%d", pts);
pts);
printf("Ganhou
printf("Ganhou %d
%d pontos.\n",
pontos.\n", pts);
pts);
O número de parâmetros suplementares é arbitrário:
Primeiro argumento:
printf("Lugar:
printf("Lugar: %d.
%d. Pontos:
Pontos: %d.\n",
%d.\n",
"Ganhou place, pts);
place, pts);
"Ganhou %d
%d pontos.\n"
pontos.\n"
É a cadeia de formato. Contém caracteres Duas cadeias constantes de seguida são concatenadas:
que vão ser escritos tal e qual:
printf("Ganhou
printf("Ganhou %d
%d pontos,
pontos, porque
porque ""
"Ganhou
"Ganhou •• pontos.\n"
pontos.\n" "ficou em %d.º lugar.\n",
"ficou em %d.º lugar.\n",
pts,
pts, place);
place);
e especificadores de conversão:
Pode especificar-se a largura do campo:
%d
%d Conversão inteira decimal
printf("%8d
printf("%8d %5d",
%5d", pts,
pts, place);
place);
Há um especificador para cada argumento
suplementar.
Programação Avançada com C 21 Programação Avançada com C 22

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operadores relacionais Problema dos pontos (3.ª solução)
... ...
... printf("Qual é o lugar? ");
if
if (pts
(pts >> 1)
1)
printf("Ganhou scanf("%d", &place);
printf("Ganhou %d
%d pontos.\n",
pontos.\n", switch (place) {
pts);
pts);
else case 1:
else
...
... pts = 10;
}} break;
case 2:
>> maior que. pts = 6;
break;
case 3:
pts = 4;
>=
>= maior ou igual. break;
case 4:
pts = 3;
break;
<< menor que. case 5:
pts = 2;
break;
<=
<= menor ou igual. case 6:
pts = 1;
break;
default:
e ainda: pts = 0;
break;
}
==
== igual. if (pts > 1)
printf("Ganhou %d pontos.\n", pts);
!=
!= diferente.
}
else ...

Programação Avançada com C 23 Programação Avançada com C 24

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A instrução switch A instrução break
switch (expressão) { break;
case expr-const: instruções
A execução da instrução break faz
case expr-const: instruções terminar a execução da instrução switch
... (ou while, ou for, ou do) de que a
case expr-const: instruções instrução break faz parte.
default: instruções Usa-se muito com instruções switch,
} para evitar que se passe das instruções
de um caso para as instruções do caso
seguinte.
A execução da instrução switch começa
pela avaliação da expressão; se um dos switch (expressão) {
casos tem um valor igual ao da case expr-const_1: instrução_1_1;
expressão, a execução da instrução ...;
switch continua pelas instruções break;
case expr-const_2: instrução_2_1
desse caso; se nenhum dos casos tiver ...;
um valor igual e houver um caso break;
default, então a instrução continua ...
pelas instruções do caso default; se default: instrução_0_1
não houver, não acontece mais nada. ...;
break;
}
Programação Avançada com C 25 Programação Avançada com C 26

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Definição de funções Utilização de uma função declarada
#include <stdio.h>
int points (int place)
{ int points (int); protótipo
switch (place) { protótipoda
dafunção
funçãopoints.
points.
case 1: main ()
return 10; {
case 2: int place;
return 6; int pts;
case 3:
return 4; printf("Qual é o lugar? ");
case 4: scanf("%d", &place);
return 3; Chamada
Chamadada dafunção;
função;aa
case 5: pts = points(place); variável
variável placeééoo
place
return 2; argumento.
argumento.
case 6: if (pts > 1)
return 1; printf("Ganhou %d pontos.\n", pts);
default: else if (pts == 1)
return 0; printf("Ganhou 1 ponto.\n");
} else
} printf("Não ganhou pontos.\n");
points é uma função inteira com uma return 0;
}
variável (parâmetro) inteira, denotada pelo
identificador place. int points (int place) Aqui
{ placeééooparâ-
Aquiplace parâ-
metro
metroda
dafunção;
função;não
não
Forma da definição de uma função: switch (place) {
confundir
confundircom
comaavariável
variável
case 1:
tipo_de_retorno
tipo_de_retorno nome_da_função(decl.
nome_da_função(decl. return 10; placeda
place dafunção
funçãomain.
main.
parâmetros)
parâmetros)
{{ ...
declarações default:
declarações return 0;
instruções
instruções
}} }
27 } 28
Programação Avançada com C Programação Avançada com C

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Variantes para a função points Operadores aritméticos
Instrução switch com casos múltiplos:
** multiplicação.
int
int points
points (int
(int place)
place)
{{
switch
switch (place)
(place) {{ // divisão (quociente).
case
case 1:
1: case
case 2:
2:
return
return 1414 -- 44 ** place;
place;
case
case 3:
3: case
return
case 4:
4: case
case 5:5: case
case 6:
6: %% resto da divisão inteira.
return 77 -- place;
place;
default:
default:
}}
return
return 0;0; ++ adição.
}}
–– subtracção.
Instrução if-else (3 ramos):

int
int points
points (int
(int place)
place) Não há potenciação!
{{
if
if (place
(place <=
<= 2)
2)
return
return 14
14 -- 44 ** place;
place; Operadores com a mesma priori-
else
else if
if (place
(place <=<= 6)
return
6) dade (* / %, + -) avaliam-se da
return 77 -- place;
place;
else
else esquerda para a direita.
return
return 0;
0;
}}
A divisão ( / ) de dois números
inteiros é a divisão inteira.

Programação Avançada com C 29 Programação Avançada com C 30

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Uma função para calcular potências A expressão condicional

Protótipo: Outra maneira (melhor) de declarar a função


ipow:
int
int ipow(int,
ipow(int, int);
int);
int
int ipow
ipow (int
(int base,
base, int
int n)
n)
Declaração: {{
return
return nn ==
== 00 ??
int
int ipow
ipow (int
(int base,
base, int
int n)
n) 11 ::
{{ base
base ** ipow(base,
ipow(base, nn -- 1);
1);
if
if (n
(n ==
== 0)
0) }}
return
return 1;1;
else
else
return Expressão condicional:
return base
base ** ipow
ipow (base,
(base, n-1);
n-1);
}}
expr_1 ? expr_2 : expr_3
O C aceita funções recursivas (funções que
se chamam a elas próprias, directamente ou
indirectamente). Avalia-se expr_1; se der dife-
rente de 0, avalia-se expr_2; se
Recursividade indirecta: der 0 avalia-se expr_3; o valor
int
int f(int
f(int x)
x) int
int g(int
g(int x)
x) da expressão condicional é o
{{ {{ valor de expr_2 ou de expr_3,
... ...
...
g(...);
...
f(...); consoante a que tiver sido
g(...); f(...); avaliada.
...;
...; ...;
...;
}} }}
Programação Avançada com C 31 Programação Avançada com C 32

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Outras funções elementares Exemplos de aplicação
int
int max(int,
max(int, int);
int); int
int points
points (int
(int place)
place)
int
int min(int, int);
min(int, int); {{
int
int ntrl(int);
ntrl(int); return
return place
place <=
<= 22 ??
int
int pstv(int);
pstv(int); 14
14 - 44 ** place
- place ::
ntrl
ntrl (7
(7 -- place);
place);
int }}
int max(int
max(int x,x, int
int y)y)
{{
return
return xx >=
>= yy ?? xx :: y;
y; int
int min3
min3 (int
(int x,
x, int
int y,
y, int
int z)
z)
}} {{
return
return min(min(x,
min(min(x, y),
y), z);
z);
int
int min(int
min(int x,x, int
int y)y) }}
{{
return
return xx <=
<= yy ?? xx :: y;
y;
}} int
int max3
max3 (int
(int x,
x, int
int y,
y, int
int z)
z)
{{
int return
return max(max(x,
max(max(x, y),
y), z);
z);
int ntrl(int
ntrl(int x)x) }}
{{
return
return xx >=
>= 00 ?? xx :: 0;
0;
}} int
int mid
mid (int
(int x,x, int
int y,y, int
int z)
z)
{{
int
int pstv(int
pstv(int x);
x); return
return xx ++ yy ++ zz --
{{ min3(x,
min3(x, y, y, z)
z) --
return
return xx >=
>= 11 ?? xx :: 1;
1; max3(x, y,
max3(x, y, z); z);
}} }}
Programação Avançada com C 33 Programação Avançada com C 34

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Os inteiros long Misturando int e long
Os inteiros long são pelo menos tão Quando, numa operação aritmética, um dos
“extensos”como os inteiros int. operando é int e o outro é long, o ope-
Normalmente vão de -2147483648 a rando int é promovido a long antes da
2147483647, nos computadores de 32 bits. operação.
Problema: Fazer um programa para calcular A afectação de um int a um long faz-se
potências que possam ser números “naturalmente”.
grandes. A afectação de um long a um int faz-se
desprezando os bits mais significativos (e
#include <stdio.h> portanto o resultado pode ser inesperado se
long lpow(long, int); alguns desses bits estiverem a 1).

main() long x; Um
Um int:
int: 452
452
{ int i; OO long:
long: 452
452
long x; printf("Um int: ");
int i; scanf("%d", &i);
printf("Indique a base e o expoente: "); x = i; Um
scanf("%ld%d", &x, &i); printf("O long : %d\n", Um int:
int: 214523
214523
OO long:
long: 32767
32767
printf("%ld elevado a %d vale %ld\n", i);
x, i, lpow(x, i));
return 0;
} long x; Um
Um long:
long: 186
186
int i; OO int
printf("Um long: "); int :: 186
186
long lpow(long base, int n)
scanf("%ld", &x);
{ i = x;
return n ? base * lpow(base, n-1) : 1; Um
Um long:
long: 523214
523214
printf("O int : %d\n", OO int
} i); int :: -1074
-1074

Programação Avançada com C 35 Programação Avançada com C 36

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Os operadores lógicos Utilização dos operadores lógicos
int
int isbtwn(int
isbtwn(int x,x, int
int a,
a, int
int b)
b)
{{
&&
&& conjunção (“e”). return
return aa <=
<= xx &&
&& xx <=
<= b;
b;
}}

||
|| disjunção (“ou”). void
void greetint(int
greetint(int h)
h)
{{
printf
printf
!! negação (“não”). (isbtwn(h,
(isbtwn(h, 4,11)
4,11) ?? "Bom
"Bom dia"
dia" ::
isbtwn(h,12,19)
isbtwn(h,12,19) ? "Boa tarde" ::
? "Boa tarde"
"Boa
"Boa noite");
noite");
Os operadores && e || avaliam-se da }}
esquerda para a direita e a avaliação pára
logo que o resultado da expressão fica int
int isleap(int
isleap(int y)y)
determinado: {{
return
return y%4
y%4 ==
== 00 &&
&& y%100
y%100 !=
!= 00 ||
||
Avalia-se x; se der 0 a expressão vale 0; y%400 == 0;
x && y se der diferente de 0 avalia-se y e o valor y%400 == 0;
}}
da expressão é 0 se o valor de y for 0 e 1
se não.
int
int daysof(int
daysof(int y) y)
Avalia-se x; se der diferente de 0 a {{
x || y expressão vale 1; se der 0 avalia-se y e o return
return 365
365 ++ isleap(y);
isleap(y);
valor da expressão é 0 se o valor de y for
}}
0 e 1 se não.

Programação Avançada com C 37 Programação Avançada com C 38

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Simplificação das expressões Precedência dos operadores
relacionais
Operadores Sentido
Em vez de x == 0 pode usar-se !x.
() a)
Em vez de x != 0 pode usar-se x. ! + - b)
* / %
Exemplos: + -
< <= > >=
int
int isleap(int
isleap(int y)
y) == !=
{{
return &&
return !(y%4)
!(y%4) &&
&& y%100
y%100 ||!(y%400);
||!(y%400);
}} ||
?:
=
int
int ipow
ipow (int
(int base,
base, int
int n)
n)
{{
return
return nn ?? a) () chamada de função.
base
base ** ipow(base,
ipow(base, n-1)
n-1) :: b) + e - unários.
1;
1;
}} Nota: esta tabela está incompleta, porque há
outros operadores em C que ainda não
foram apresentados.
Programação Avançada com C 39 Programação Avançada com C 40

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O tipo double Lendo e escrevendo double

Os números reais (floating point) em C double x;


...
aparecem em dois tipos: float e double.
printf("Valor de x = ");
scanf("%lf", &x);
As funções matemáticas da biblioteca usam
double. %lf especificador
%lf especificadorde
deconversão
conversãopara
para
double, na função
double, na funçãoscanf.
scanf.

O tipo das constantes floating point é double. ...


printf("O quadrado de x é %f\n",
x * x);
Exemplos de constantes double:
%f
%f especificador
especificadorde
deconversão
conversãopara
para
3.141592653 double, na função printf. Escrito
double, na função printf. Escrito
1e-2 na
naforma
forma[-]m.dddddd.
[-]m.dddddd.
10E10
0.1e-1 ...
printf("O quadrado de x é %e\n",
Operações aritméticas: x * x);
%e especificador
especificadorde
deconversão
conversãopara
para
** multiplicação. // divisão. %e
double, na função printf. Escrito
double, na função printf. Escrito
na
naforma
forma[-]m.dddddde±xx.
[-]m.dddddde±xx.
++ adição. –– subtracção.
Programação Avançada com C 41 Programação Avançada com C 42

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Largura do campo e precisão O ficheiro <math.h>
#include <stdio.h>
main() double sin(double);
{ double cos(double);
double x; double tan(double);
printf("Valor de x = ");
scanf("%lf", &x); double asin(double);
printf("O quadrado de x é %f\n", x * x); double acos(double);
printf("O quadrado de x é %e\n", x * x); double atan(double);
printf("O quadrado de x é %6.2f\n", x * x);
printf("O quadrado de x é %6.2e\n", x * x);
}
double sinh(double);
double cosh(double);
double tanh(double);
Valor
Valor de
de xx == 12.25
12.25
OO quadrado
quadrado dede xx éé 150.062500
150.062500
OO quadrado double exp(double);
quadrado dede xx éé 1.500625e+02
1.500625e+02
OO quadrado
quadrado dede xx éé 150.06
150.06
double log(double);
OO quadrado
quadrado dede xx éé 1.50e+02
1.50e+02 double log10(double);
double pow(double, double);
Valor
Valor de
de xx == 0.182
0.182
double sqrt(double);
OO quadrado
quadrado dede xx éé 0.033124
0.033124
OO quadrado
quadrado dede xx éé 3.312400e-02
3.312400e-02 double ceil(double); a)
OO quadrado
quadrado dede xx éé 0.03
0.03 double floor(double); b)
OO quadrado
quadrado dede xx éé 3.31e-02
3.31e-02
double fabs(double);
Valor
Valor de
de xx == 1024
1024 ...
OO quadrado
quadrado dede xx éé 1048576.000000
1048576.000000
OO quadrado
quadrado dede xx éé 1.048576e+06
1.048576e+06 a) ceil(x) é menor inteiro >= x, em double.
OO quadrado
quadrado dede xx éé 1048576.00
1048576.00
OO quadrado
quadrado dede xx éé 1.05e+06
1.05e+06 b) floor(x) é o maior inteiro <= x, em double.

Programação Avançada com C 43 Programação Avançada com C 44

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Conversão entre inteiros e reais Exercícios (1)
A conversão de int ou long para double faz- O factorial de um número natural n, denotado
se “naturalmente”. por n! pode ser definido recursivamente
assim:
A conversão de double para int ou long faz-
n! = n * (n-1)!, se n>0;
se desprezando a parte decimal. Se o
resultado não for representável, não se sabe o 0! = 1.
que acontece (é um erro de programação). Para números grandes, a função de Sterling,
É melhor assinalar a conversão de real para definida a seguir, dá uma boa aproximação do
inteiro explicitamente, usando a função ceil, factorial:
a função floor, ou os operadores de sterling(n) = 2 * š * n * (n/e) n
conversão (int) ou (long).

Pretende-se um programa C que aceite um


Exemplo: número inteiro do terminal e afixe o seu
factorial e o seu número de sterling, para
printf("Valor de x = "); comparar.
scanf("%lf", &x);
printf("ceil(x) = %f\n", ceil(x));
printf("floor(x) = %f\n", floor(x));
printf("(int) x = %d\n", (int) x); Sugestões: o arcotangente de 1.0 é š/4.

Valor e é a exponencial de 1.0.


Valor
Valor de
de xx == 34.7
34.7 Valor de
de xx == -12.4
-12.4
ceil(x) ceil(x)
ceil(x) == --
ceil(x) == 35.000000
35.000000 12.000000
floor(x)
floor(x) == 34.000000
34.000000 12.000000
(int) floor(x)
floor(x) == --
(int) xx == 3434 13.000000
13.000000
(int)
(int) xx == -12
-12 45 46
Programação Avançada com C Programação Avançada com C

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Exercícios (2) Problema da classificação
A área de um triângulo é dada pela seguinte Problema:
expressão: Escrever um programa para registar a
s * (s - a) * (s - b) * (s - c) chegada dos pilotos, juntamente com o
tempo, e para afixar a classificação final,
onde s é o semiperímetro e a, b, e c são as ordenada, com número, tempo, média
medidas dos lados. (em km/h e mph), e pontos conquistados.

1. Pretende-se um programa que, dadas as


medidas dos lados, calcule a área do Subproblemas:
triângulo.
1.º subproblema:
A distância entre dois pontos de coorde- Escrever uma função para dar o número
nadas (x1, y1) e (x2, y2) é dada pela de pontos obtidos por um piloto, dado o
seguinte expressão: lugar em que ficou (já está).

2.º subproblema:
2 2
(x1 - x2) + (y1 - y2) Escrever um programa para registar a
ordem de chegada dos pilotos.

2. Pretende-se um programa que, dados os 3.º subproblema:


três vértices por meio das suas Escrever um programa para registar o
coordenadas, calcule a área do triângulo. tempo de cada piloto, à chegada à meta.

Programação Avançada com C 47 Programação Avançada com C 48

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Quadros Constantes simbólicas
Quadros:
Sequências de valores do mesmo tipo, Directiva #define:
acessíveis por indexação.
#define
#define MAX_ARRIVALS
MAX_ARRIVALS 26
26
Vectores: quadros unidimensionais.
int
int arrivals[MAX_ARRIVALS
arrivals[MAX_ARRIVALS ++ 1];
1];
Matrizes: quadros bidimensionais.

Outros exemplos:
Declaração de um vector para registar a
chegada de 26 corredores:
#define
#define pi
pi 3.141592653
3.141592653
#define
#define e 2.718281828
e 2.718281828
int
int arrivals[26];
arrivals[26];
double
double sterling(int
sterling(int n) n)
Em C, os índices começam em 0. Por vezes, {{
convém ocupar o vector só a partir da return
return sqrt(2
sqrt(2 ** pi
pi ** n)
n) **
posição 1: pow(n / e, n);
pow(n / e, n);
int arrivals[27]; }}
/* índice 0 não utilizado */
Forma da declaração de um vector: As directivas (#define, #include) são
interpretadas pelo preprocessador.
tipo
tipo nome
nome [dimensão];
[dimensão];

Programação Avançada com C 49 Programação Avançada com C 50

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Registando a ordem de chegada Funções sem parâmetros
#include <stdio.h>

#define MAX_ARRIVALS 26
int arrivals [MAX_ARRIVALS + 1]; Protótipo:
/* índice 0 não utilizado */
int n_arrivals; int
int get_arrivals (void); int get_arrivals
get_arrivals (void);
(void);
main ()
{ Chamada:
n_arrivals = get_arrivals ();
printf("Número de chegadas: %2d\n"
"Número de desistentes: %2d\n", n_arrivals
n_arrivals == get_arrivals
get_arrivals ();
();
n_arrivals, MAX_ARRIVALS -
n_arrivals);
return 0; Noutras circunstâncias, o valor da função
}
poderia ser descartado:
int get_arrivals (void)
{ ...;
...;
int i; get_arrivals
int number; get_arrivals ();
();
for (i = 1; i <= MAX_ARRIVALS; ++i) ...;
...;
{
printf("%2d.º lugar (0 p/ terminar): ",
i);
scanf("%d", &number); Se nunca quiséssemos o resultado (ou não
if (!number) houvesse mesmo um resultado para
break; devolver), o tipo da função seria void:
arrivals [i] = number;
}
return i - 1;
void
void get_arrivals
get_arrivals (void);
(void);
}

Programação Avançada com C 51 Programação Avançada com C 52

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Concatenação de cadeias constantes A instrução for
main () int
int i;i;
{ int
int number;
number;
n_arrivals = get_arrivals (); for
for (i(i == 1;
1; ii <=
<= MAX_ARRIVALS;
MAX_ARRIVALS; ++i)
++i)
printf("Número de chegadas: %2d\n" {{
printf("%2d.º
printf("%2d.º lugarlugar (0
(0 p/
p/ terminar):
terminar): ",
",
"Número de desistentes: %2d\n" , i);
n_arrivals, MAX_ARRIVALS - i);
scanf("%d",
scanf("%d", &number);
&number);
n_arrivals); if
if (!number)
(!number)
return 0; break;
break;
} arrivals
arrivals [i][i] == number;
number;
}}

for (expr_1; expr_2; expr_3)


A justaposição de cadeias constantes instrução
indica a sua concatenação.
Usa-se para dispor o código de maneira expr_1 : expressão de inicialização.
mais agradável. exp2_2 : expressão de continuação.
expr_3 : expressão de progressão.
1. Avalia-se expr_1;
2. Avalia-se expr_2; se der 0, a instrução
for termina. Se não:
3. Executa-se a instrução;
4. Avalia-se a expr_3;
5. Recomeça-se pelo ponto 2.

Programação Avançada com C 53 Programação Avançada com C 54

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Exemplos de instruções for A instrução composta
Problema: listar as potências de 2, com
Uma instrução composta, ou bloco, é
inteiros long.
formada por uma lista de instruções
#include colocadas entre chavetas { e }. Usa-se
#include <stdio.h>
<stdio.h> quando a sintaxe requer uma instrução e
long
long lpow(long,
lpow(long, int);
int); a lógica do programa requer várias.

main()
main() Exemplos:
{{
int for
for (i
(i == 1;
1; ii <=
<= MAX_ARRIVALS;
MAX_ARRIVALS; ++i)
int i;
i; {{
++i)
for
for (i == 1;
(i 1; ii <=
<= 31;
31; ii == ii ++ 1)
1) printf("%2d.º
printf("%2d.º lugarlugar (0
(0 p/
p/ terminar):
terminar): ",
", i);
printf("%ld\n",
printf("%ld\n", lpow(2,
lpow(2, i));
i)); scanf("%d", &number);
i);
return scanf("%d", &number);
return 0;
0; if
if (!number)
(!number)
}} break;
break;
arrivals
arrivals [i][i] == number;
number;
long
long lpow(long
lpow(long base,
base, int
int n)n) }}
{{
long
long p;p;
int
int i;i;
pp == 1; if
if (number
(number >> MAX_NUMBER)
MAX_NUMBER)
1; {{
for
for (i(i == 1;
1; ii <=
<= n;
n; ii == ii ++ 1)
1) printf("ERRO:
pp == pp ** base; printf("ERRO: Número
Número inválido.\n");
inválido.\n");
base; ii == ii -- 1;
1;
return
return p; p; }}
}} else
else
arrivals
arrivals [i] [i] == number;
number;
Nota: Isto não fica assim!
Programação Avançada com C 55 Programação Avançada com C 56

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A instrução break Os operadores de incrementação
(para terminar ciclos)

Normalmente, a instrução for termina for


quando a condição de continuação dá 0. for (i
(i == 1;
1; ii <=
<= MAX_ARRIVALS;
MAX_ARRIVALS; ++i++i ))
{{
“Excepcionalmente”, podemos antecipar o printf("%2dº.
printf("%2dº. lugarlugar (0
(0 p/
p/ terminar):
terminar): ",
", i);
i);
fim, com a instrução break. scanf("%d", &number);
scanf("%d", &number);
if
if (!number)
(!number)
Exemplo: break;
break;
arrivals
arrivals [i][i] == number;
number;
for }}
for (i
(i == 1;
1; ii <=
<= MAX_ARRIVALS;
MAX_ARRIVALS; ++i)
++i)
{{
printf("%2d.º
printf("%2d.º lugarlugar (0
(0 p/
p/ terminar):
terminar): ",
", i);
i);
scanf("%d", &number);
scanf("%d", &number);
if
if (!number)
(!number) ++n vale n+1, e incrementa n.
break;
break;
arrivals
arrivals [i][i] == number;
number; n++ vale n, e incrementa n.
}}

x = 5;
printf("%d\n", x); 55
printf("%d\n", ++x); 66
Nota: printf("%d\n", x); 66
!number é o idioma C para number == 0. x = 5;
printf("%d\n", x); 55
printf("%d\n", x++); 55
printf("%d\n", x); 66
Programação Avançada com C 57 Programação Avançada com C 58

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Os operadores de decrementação A instrução while

––n vale n-1, e decrementa n. while (expr)


instrução
n–– vale n, e decrementa n.
expr: expressão de continuação.
Exemplo:
#define
1. Avalia-se expr; se der 0, a
#define MAX_NUMBER
MAX_NUMBER 3939 instrução while termina. Se não:
...
...
if 2. Executa-se a instrução;
if (number
(number >> MAX_NUMBER)
MAX_NUMBER)
{{ 3. Recomeça-se pelo ponto 1.
printf("ERRO:
printf("ERRO: Número
Número inválido.\n");
inválido.\n");
––i;
––i; Exemplo:
}}
else
else
long
long lpow(long
lpow(long base,base, int
int n)
n)
arrivals
arrivals [i]
[i] == number;
number;
{{
long
long p;p;
int
int i;i;
x = 5; pp == 1;
1;
printf("%d\n", x); 55 ii == 1;
44 1;
printf("%d\n", ––x); while
while (i (i <=
<= n)n)
printf("%d\n", x); 44 {{
pp == pp ** base;
base;
x = 5; ++i;
printf("%d\n", x); 55 ++i;
55 }}
printf("%d\n", x––); return
printf("%d\n", x); 44 return p; p;
}}
Programação Avançada com C 59 Programação Avançada com C 60

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Exemplos de instruções while Equivalência entre for e while
Mais uma versão para a potenciação: while (expr) for ( ; expr; )
long
long lpow(long
lpow(long base,
base, int
int n)
n) instrução instrução
{{
long
long p;
int
p; for (expr_1; expr_1;
int i;i; expr_2; while (expr_2)
pp == 1;
1;
ii == n;
n; expr_3) {
while
while (i––)
(i––) instrução instrução
pp == pp ** base;
base; expr_3;
return
return p; p;
}} }
int
int placeof(int
placeof(int number)
number)
Uma função para dar o lugar em que chegou {{
int
int i;
um concorrente (ou 0 se não tiver chegado): for
i;
for (i
(i == 1;
1;
i<=n_arrivals
i<=n_arrivals && && number
number != != arrivals
arrivals [i];
[i];
int
int placeof(int
placeof(int number)
number)
{{ ++i)
++i)
int Notar a ;;
int i;i; return
ii == 1;
1;
avaliação return ii <=<= n_arrivals
n_arrivals ?? ii :: 0;
0;
while sequencial }}
while (i (i <=
<= n_arrivals
n_arrivals &&
&&
number do
number !=!= arrivals
arrivals [i])
[i]) int
++i;
++i; operador int placeof(int
placeof(int number)
number)
return {{
return ii <= <= n_arrivals
n_arrivals ?? ii :: 0;
0; &&
}} int
int i;
i;
for
for (i
(i == n_arrivals;
n_arrivals;
ii >=
>= 11 &&
&& number
number !=
!= arrivals
arrivals [i];
[i];
i = n_arrivals; ––i)
Ou: while (i >= 1 && number != arrivals ;;
––i)
[i]) return
––i; return i;i;
}}
return i;
Programação Avançada com C 61 Programação Avançada com C 62

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A instrução vazia Incrementação do índice
int
int get_arrivals
get_arrivals (void)
(void)
{{
int
int i;i;
int
int number;
number;
ii == 0;
0;
while
while (i (i << MAX_ARRIVALS)
MAX_ARRIVALS)
{{
printf("%2d.º
printf("%2d.º lugarlugar (0
(0 para
para terminar):
terminar): ",
",
ii ++ 1);
1);

;
scanf("%d",
scanf("%d", &number);
&number);
if
if (!number)
(!number)
break;
break;
if
if (number
(number >> MAX_NUMBER)
MAX_NUMBER)
printf("ERRO:
printf("ERRO: Número
Número inválido.\n");
inválido.\n");
else
else
arrivals
arrivals [++i]
[++i] == number;
number;
}}
return
return i; i;
}}

arrivals [++i] = number;

É assim porque decidimos não usar a posição 0. Se


usássemos ficava:

arrivals [i++] = number;

Programação Avançada com C 63 Programação Avançada com C 64

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Registando os tempos (1) Registando os tempos (2)
...
#include <stdio.h> int get_times(void)
{
#define MAX_NUMBER 39 int n = 0;
long times[MAX_NUMBER + 1]; int number;
int hrs;
long millisecs(int, int, int, int); int mins;
int get_times(void); int secs;
void display_times(void); int mils;
for (;;)
main () {
printf("Número do carro (0 p/ terminar): ");
{
scanf("%d", &number);
int n_cars; if (!number)
n_cars = get_times(); break;
printf("Números de carros que terminaram: " if (number > MAX_NUMBER)
"%d\n", n_cars); {
printf("\n"); printf("ERRO: número inválido.\n");
display_times(); continue;
} }
if (times[number])
long millisecs {
(int hrs, int mins, int secs, int mils) printf("ERRO: número repetido.\n");
{ continue;
return (((hrs * 60L) + mins) * 60L }
+ secs) * 1000L + mils; printf("Tempo (h m s mil): ");
} scanf("%d%d%d%d",
... &hrs, &mins, &secs, &mils);
times[number] =
millisecs(hrs, mins, secs, mils);
++n;
}
Constantes long return n;
}
Programação Avançada com C 65 ...
Programação Avançada com C 66

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Inicialização na declaração Os operadores de afectação
int get_times(void) +=
+= x += y x = x + y
{
int n = 0;
int number; -=
-= x -= y x = x - y
...
}
*=
*= x *= y x = x * y
Outro exemplo:
long
long lpow(long
lpow(long base,
base, int
int n)n) /=
/= x /= y x = x / y
{{
long
long pp == 1;
1;
int
int i;
i;
for %=
%= x %= y x = x % y
for (i
(i == 1;
1; ii <=
<= n;
n; ii == ii ++ 1)
1)
pp == pp ** base;
base;
return
return p;p;
}} Exemplo:
long
long lpow(long
lpow(long base,
base, int
int n)
n)
As
Asvariáveis
variáveisexternas
externas(isto
(istoé,é, {{
declaradas
declaradas fora das funções)são
fora das funções) são long
automaticamente long pp == 1;
1;
automaticamenteinicializadas
inicializadasaa0.0. int
int ii == n;
n;
while
while (i––)
(i––)
pp *=
*= base;
base;
É o caso do vector times. return
return p;p;
}}
Programação Avançada com C 67 Programação Avançada com C 68

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ciclos infinitos A instrução continue

for (;;) (ou while (1) ) for


for (;;)
(;;)
{{
...
...
Saída por break: if
if (!number)
(!number)
break;
break;
for (;;) if
if (number
(number >> MAX_NUMBER)
MAX_NUMBER)
{ {{
... printf("ERRO:
printf("ERRO: número
número inválido.\n");
inválido.\n");
if (...) continue;
continue;
break; }}
... if
if (times[number])
(times[number])
} {{
printf("ERRO:
printf("ERRO: número
número repetido.\n");
repetido.\n");
continue;
continue;
Saída por return: }}
...
...
for (;;) }}
{
...
if (...)
return ...; A execução duma instrução continue faz
... terminar o passo corrente da iteração.
}

Programação Avançada com C 69 Programação Avançada com C 70

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Registando os tempos (3) As afectações são expressões
... if (time = times[i])
void display_times(void)
{
{
int i; ...
int hrs; }
int mins;
int secs;
int mils; O valor da expressão de afectação
long time; time = times[i] é o valor da variável
for (i = 1; i <= MAX_NUMBER; ++i) time, depois de ter sido afectada.
if (time = times[i])
{ Alternativamente:
mils = time % 1000; for (i = 1; i <= MAX_NUMBER; ++i)
time = time / 1000; {
secs = time % 60; time = times[i];
time = time / 60; if (time != 0) /* ou if (time)*/
mins = time % 60; {
hrs = time / 60; ...
printf("%3d %d:%02d:%02d.%03d\n", }
i, hrs, mins, secs, mils); }
}
}
Não confundir:
time = times[i]
Ciclo for de enumeração:
e
for (i = 1; i <= MAX_NUMBER; ++i)
time == times[i]
Programação Avançada com C 71 Programação Avançada com C 72

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Enchimento de campos numéricos Validação dos inputs
long
long scantime(void)
scantime(void)
printf("%3d %d:%02d:%02d.%03d\n", {{
i, hrs, mins, secs, mils); int
int hrs;
hrs;
int
int mins;
mins;
int
int secs;
secs;
Número do carro (0 p/ terminar): 4 int
Tempo (h m s mil): 1 53 6 12 int mils;
mils;
return
return scanf("%d%d%d%d",
scanf("%d%d%d%d",
Número do carro (0 p/ terminar): 26 &hrs,&mins,&secs,&mils)
Tempo (h m s mil): 1 53 34 45 &hrs,&mins,&secs,&mils) == == 44 &&
&&
isbtwn(hrs,
isbtwn(hrs, 0, 0, 23)
23) &&
&&
Número do carro (0 p/ terminar): 1 isbtwn(mins,
Tempo (h m s mil): 1 53 59 823 isbtwn(mins, 0, 0, 59)
59) &&
&&
isbtwn(secs,
isbtwn(secs, 0, 0, 59)
59) &&
&&
Número do carro (0 p/ terminar): 0 isbtwn(mils,
Números de carros que terminaram: 3 isbtwn(mils, 0, 0, 999)
999) ??
(((hrs
(((hrs ** 60L)+mins)
60L)+mins) ** 60L
60L ++
1 1:53:59.823 secs)
4 1:53:06.012 secs) ** 1000L
1000L ++ mils
mils ::
0;
0;
26 1:53:34.045 }}
Nota: scanf devolve o número de argumentos lidos.
...
...
printf("%3d %d:%2d:%2d.%3d\n", printf("Tempo
printf("Tempo (h(h mm ss mil):
mil): ");
");
i, hrs, mins, secs, mils); if (!(time = scantime()))
if (!(time = scantime()))
{{
printf("ERRO:
printf("ERRO: tempo
tempo inválido.\n");
inválido.\n");
continue;
continue;
Números de carros que terminaram: 3 }}
1 1:53:59.823 times[number]
times[number] == time;
time;
4 1:53: 6. 12 ++n;
++n;
26 1:53:34. 45 }}
return
return n;
n;
}}

Programação Avançada com C 73 Programação Avançada com C 74

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Escrevendo os tempos O operador vírgula ,
int
int printtime(long
printtime(long time)time)
{{ expr_1, expr_2
int
int hrs;
hrs;
int
int mins;
int
mins; 1. Avalia-se expr_1;
int secs;
secs;
int
int mils;
mils
mils; 2. Avalia-se expr_2; o resultado
mils == time
time %% 1000;
1000;
time
time = time // 1000;
= time 1000; constitui o valor da expressão.
secs
secs == time
time %% 60;
60;
time
time == time
time // 60;
60;
mins if
if (time
(time == scantime(),
scantime(), !time)
mins == time
time %% 60;
60; {{
!time)
hrs
hrs = time // 60;
= time 60;
return printf("ERRO:
printf("ERRO: tempo
tempo inválido.\n");
return printf("%d:%02d:%02d.%03d",
printf("%d:%02d:%02d.%03d", continue;
inválido.\n");
hrs,
hrs, mins,
mins, secs,
secs, mils);
mils); continue;
}} }}

Nota: printf devolve o número de caracteres escritos.

void
void display_times(void)
display_times(void)
Muitas vezes descarta-se o valor:
{{
int
int i;
i; long
long lpow(long
lpow(long base,
base, intint n)
n)
for
for (i
(i == 1;
1; ii <=
<= MAX_NUMBER;
MAX_NUMBER; ++i)
++i) {{
if
if (times[i])
(times[i]) long
long p;
p;
{{
int
int i;
i;
printf("%2d
printf("%2d ", ", i);
i); for
for (p
(p == 1,
1, ii == 1;
1; ii <=
<= n;
n; ii == ii ++ 1)
1)
printtime(times[i]);
printtime(times[i]); pp == pp ** base;
base;
printf("\n");
printf("\n"); return
return p;p;
}}
}}
}}

Programação Avançada com C 75 Programação Avançada com C 76

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Calculando a classificação Calculando as médias
Os tempos registam-se por ordem de #define ONE_MILE 1609.3
chegada:
double kmh(long meters, long millisecs)
int get_results(void) {
{ return
int n = 0; (meters / 1000.0) /
... (millisecs / (double)(1000L * 60L * 60L));
long time; }
long prev_time = 0;
while (n < MAX_ARRIVALS) double mph(long meters, long millisecs)
{ {
... return kmh(meters, millisecs) /
printf("Tempo (h m s mil): "); (ONE_MILE / 1000);
if (time = scantime(), !time) }
{
printf("ERRO: tempo inválido.\n");
continue;
} Aritmética
Aritméticamista
mista(inteiros
(inteiroseereais):
reais):
if (time < prev_time) 1. Conversão automática.
1. Conversão automática.
{ 2.
2. Conversão
Conversãopor
poroperador
operador(double).
(double).
printf("ERRO: tempo fora de 3. Constantes (ex.:
ordem.\n"); 3. Constantes (ex.:1000.0,
1000.0,1000L).
1000L).
continue;
}
arrivals[++n] = number; double kmh(long meters, long millisecs)
times[number] = prev_time = time; {
} return
return n; (meters / 1000) /
} (millisecs / (1000L * 60L * 60L));
}

Programação Avançada com C 77 Programação Avançada com C 78

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Escrevendo números reais Calculando a classificação (1)
long length; #define MAX_CARS 26
...
long times[MAX_CARS + 1];
main () int cars [MAX_CARS + 1];
{ int n_cars;
printf("Comprimento do circuito (metros): ");
scanf("%ld", &length);
... Posição de um carro no vector cars, dado o
}
número:
void display_results(void)
{
int placeof(int n)
int i;
{
long time;
int i;
for (i = 1; i <= n_cars; ++i)
for (i = n_cars; i >= 1 && cars[i] != n; ––i)
{
printf("%2d.º %2d ", i, arrivals[i]); ;
printtime(time = times[arrivals[i]]); return i;
printf(" %7.3f %7.3f\n", }
kmh(length, time),
mph(length, time)); Para evitar repetições:
}
}
...
if (placeof(number))
printf(" %7.3f %7.3f\n", ... {
printf("ERRO: número repetido.\n");
continue;
}
precisão (número de ...
largura do campo
casas decimais)
Programação Avançada com C 79 Programação Avançada com C 80

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Calculando a classificação (2) Bubblesort
void get_results(void)
{ Usa-se
Usa-seaavariável
variável
int number; externa n_cars,por
externan_cars, por Bubblesort:
long time; causa
causada
dachamada
chamada
while (n_cars < MAX_CARS) placeof(number).
placeof(number). de um vector de inteiros a, com n
{
printf("Carro número: (0 p/ terminar): ", elementos (posição 0 desocupada):
n_cars + 1);
scanf("%d", &number);
if (!number) void bubblesort(int a[], int n)
break;
if (number > MAX_NUMBER) {
{ int i;
printf("ERRO: número inválido.\n"); int j;
continue; int m;
}
if (placeof(number)) for (i = 2; i <= n; ++i)
{ for (j = n; j >= i; ––j)
printf("ERRO: número repetido.\n"); if (a[j-1] > a[j])
continue; {
}
printf("Tempo (h m s mil): "); m = a[j-1];
if (time = scantime(), !time) a[j-1] = a[j];
{ a[j] = m;
printf("ERRO: tempo inválido.\n"); }
continue;
} }
cars[++n_cars] = number;
times[n_cars] = time;
}
}

Programação Avançada com C 81 Programação Avançada com C 82

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ordenando os resultados Variáveis estáticas
Bubblesort do vector times, com
reordenação em paralelo do vector cars: Uma outra maneira de programar a função
points:
void
void sortcars(void)
sortcars(void) int
int points(int
points(int i) i)
{{ {{
int static
static int
int table
table [] [] == {10,
{10, 6,
6, 4,
4, 3,
3, 2,
2, 1};
int i;
i; return
1};
int j; return ii <=
<= 66 ?? table[i
table[i -- 1]
1] :: 0;
0;
int j; }}
int
int my_car;
my_car;
long
long my_time;
my_time;
for
for (i
(i == 2;
2; ii <=
<= n_cars;
n_cars; ++i)
++i) As variáveis estáticas não “morrem”
for
for (j = n_cars; j >= i; ––j)
(j = n_cars; j >= i; ––j) quando termina a função em que
if (times[j-1] > times[j])
if (times[j-1] > times[j]) estão declaradas.
{{
my_time
my_time == times[j-1];
times[j-1];
times[j-1] = times[j];
times[j-1] = times[j];
times[j] Inicialização de um vector na
times[j] == my_time;
my_time;
my_car = cars[j-1]; declaração:
my_car = cars[j-1];
cars[j-1]
cars[j-1] == cars[j];
cars[j]; double
double IRStax[]
IRStax[] ==
cars[j]
cars[j] = my_car;
= my_car; {0.160,
{0.160, 0.200,
0.200, 0.275,
0.275, 0.350,
0.350, 0.400};
0.400};
}} long
long IRSlimits[]
IRSlimits[] ==
{450,
{450, 850,
850, 1250,
1250, 3000,
3000, 1000000000};
}} 1000000000};

Programação Avançada com C 83 Programação Avançada com C 84

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Mostrando os resultados Exercícios (3)
void display_results(void) 1. Escrever um programa para simular o jogo
{ do “preço certo”. O programa começa por
int i; pedir o preço secreto; depois aceita os palpi-
long time; tes dos quatros jogadores; finalmente anuncia
for (i = 1; i <= n_cars; ++i)
{
qual ganhou, ou informa que ninguém ganhou,
printf("%2d.º %2d ", i, cars[i]); porque todos rebentaram. (Ganha o jogador
printtime(time = times[i]); que mais se aproxime do preço secreto, mas
printf(" %7.3f %7.3f %2d\n", sem ultrapassar.)
kmh(length, time),
mph(length, time), 2. Escrever um programa para jogar aos da-
points(i)); dos. O trabalho do programa vai ser lançar os
} dados, registar os resultados, e anunciar o
}
vencedor. Em cada jogo, lança-se os dados
A função main: três vezes (no máximo) para cada jogador.
Depois de cada lançamento (excepto depois do
main ()
último), o jogador pode escolher os dados que
{
quer guardar e que não serão lançados na vez
printf("Comprimento do circuito (metros): ");
seguinte. O resultado de cada jogador é a
scanf("%ld", &length);
combinação de números com que fica depois
get_results();
do último lançamento. Ganha o jogador que
printf("\n");
ficar com mais números iguais, de maior valor.
printf("Números de carros que terminaram: "
Para fazer os lançamentos, usar a função de
"%d\n", n_cars);
geração de números aleatórios rand(), do
printf("\n");
ficheiro <stdlib.h>.
sortcars();
display_results(); 3. Modificar o programa anterior para que o
} programa seja também um dos jogadores.
Programação Avançada com C 85 Programação Avançada com C 86

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ficheiros Operações sobre ficheiros (escrever)
Os programas C manipulam ficheiros através FILE *fp;
de objectos de tipo FILE. O tipo FILE está
definido no ficheiro <stdio.h>. Abrir um ficheiro para escrever, fopen:
fp = fopen("RESULTS", "w");
Declaração de um ficheiro:
Atenção ao
Abrir um ficheiro para acrescentar, fopen:
FILE *fp;
asterisco
fp = fopen("HISTORY", "a");

Declaração de funções parametrizadas por Fechar o ficheiro, fclose:


ficheiros:
fclose(fp);
void fprint_results(FILE *f)
{
... Escrever no ficheiro, fprintf:
}
fprintf(fp,"%2d.º %2d ",i,cars[i]);
int fprinttime(FILE *f, long time) fprintf(fp, "%d:%02d:%02d.%03d",
{ hrs, mins, secs, mils);
...
}

Programação Avançada com C 87 Programação Avançada com C 88

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Guardando os resultados (1) Guardando os resultados (2)
int fprinttime(FILE *f, long time) main ()
{ {
int hrs; FILE *pf;
int mins;
int secs; printf("Comprimento do circuito (metros): ");
int mils; scanf("%ld", &length);
mils = time % 1000; scan_results();
time = time / 1000; printf("\n");
... printf("Números de carros que terminaram: "
return fprintf(f, "%d:%02d:%02d.%03d",
"%d\n", n_cars);
hrs, mins, secs, mils);
} sortcars();
printf("\n");
pf = fopen("RESULTS", "w");
fprint_results(pf);
void fprint_results(FILE *f) fclose(pf);
{
int i; }
long time;
for (i = 1; i <= n_cars; ++i)
{
fprintf(f, "%2d.º %2d ", i, cars[i]);
fprinttime(f, time = times[i]);
fprintf(f, " %7.3f %7.3f %2d\n",
kmh(length, time),
mph(length, time),
points(i));
}
}

Programação Avançada com C 89 Programação Avançada com C 90

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações sobre ficheiros (ler) Ficheiros stdin, stdout, stderr
FILE *fp; Quando um programa C arranca, o ficheiro
stdin fica associado ao teclado do terminal
Abrir um ficheiro para ler, fopen: e os ficheiros stdout e stderr ao ecrã. Em
alguns ambientes, os ficheiros stdin e
fp = fopen("RESULTS", "r"); stdout podem ser redirigidos para outros
ficheiros ou para pipes, no momento da
chamada do programa.
A função scanf lê do ficheiro stdin; a
Fechar o ficheiro, fclose: função printf escreve no ficheiro stdout. O
ficheiro stderr é usado para receber as
fclose(fp); mensagens de erro.
Para mudar no programa uma associação
estabelecida por defeito, usa-se a função
Ler no ficheiro, fscanf: freopen para “reabrir” o ficheiro. Exemplo:
int n;

fscanf(fp, "%d", &n); if (freopen("RESULTS", "w", stdout) == NULL)


fprintf(stderr, "ERRO: impossível reabrir.\n");
else
int i; {
int j; ...
long x; printf("Isto vai para o ficheiro RESULTS");
...
fscanf(fp, "%d%d%ld", &i, &j, &x); }

Programação Avançada com C 91 Programação Avançada com C 92

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Problema dos pilotos (1) Cadeias de caracteres
Problema: Os nomes são cadeias de caracteres.
A lista dos concorrentes ao grande
prémio existe num ficheiro de texto, Em
chamado "CARS_TODAY". Cada linha EmC,C,as
ascadeias
cadeiasdedecaracteres
caracteressão
sãovectores
vectores
de
decaracteres
caractereseesão
sãoterminadas
terminadaspelo
pelocarácter
carácter
deste ficheiro contém o número do carro e de
o nome (apelido) do piloto. Escrever um decódigo
código0,0,escrito
escrito'\0'.
'\0'.
programa para saber qual é o nome do
piloto que conduz determinado carro. O
char name[16]; 1.1.índices
índicesentre
entre00ee15.
programa funciona ciclicamente e termina 15.
quando se der uma resposta vazia 2.2.capacidade útil 15;
capacidade útil 15;
(newline), em vez do número do carro. uma
umaposição
posiçãoserá
será
ocupada
ocupadapor
por'\0'.
'\0'.

Exemplo. Comprimento de uma cadeia:


Solução: int
Usar dois vectores paralelos, um com os int strlen(char
strlen(char s[]);
s[]);
{{
números dos carros, outro com os nomes int
int i;
i;
dos pilotos; carregá-los a partir do for
for (i
(i == 0;
0; s[i];
s[i]; ++i)
++i)
ficheiro; pesquisar o número dado no ;;
return
return i;
vector dos números; devolver o nome que }}
i;
está na posição correspondente.
OOficheiro <string.h>da
ficheiro<string.h> dabiblioteca
bibliotecacontém
contém
muitas funções para trabalhar com cadeias.
muitas funções para trabalhar com cadeias.

Programação Avançada com C 93 Programação Avançada com C 94

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Lendo cadeias de caracteres Vectores de cadeias
%s
%s especificador
especificadorde
deconversão
conversãopara
para
cadeia, nas funções scanfeefscanf.
cadeia, nas funçõesscanf fscanf. Um
Umvector
vectorde
decadeias
cadeiasééum
umvector
vectorde
de
vectores.
vectores.
char name[16];
int number;
FILE *f; #define MAX_CARS 32
...
fscanf(f, "%d%s", &number, name); char names [MAX_CARS + 1][16];
... int numbers [MAX_CARS + 1];
int n_cars;

Quando
Quandoum
umargumento
argumentoda dafunção scanfou
funçãoscanf ou
fscanf é uma cadeia, não se usa o &.
fscanf é uma cadeia, não se usa o &. Pode-se
Pode-seler
leruma
umalinha
linhado
doficheiro
ficheirodirecta-
directa-
mente
mentepara
paraas
asposições
posiçõesnos
nosvectores:
vectores:

Para
Paraler
leruma
umalinha
linhainteira
inteirado
doterminal
terminal(até
(atéao
ao
fim da linha inclusive), usa-se a função
fim da linha inclusive), usa-se a funçãogets.
gets. fscanf(f, "%d%s",
OOnewline
newlineéésubstituído
substituídopor
por'\0'.
'\0'. &numbers[i], names[i])

...
printf("Qual é o nome? ");
gets(name);
...
Programação Avançada com C 95 Programação Avançada com C 96

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ciclo de leitura Verificando se o ficheiro existe
O ficheiro existe, de certeza (e pode ser lido):
AAfunção fscanfdevolve
funçãofscanf devolveoonúmero
númerode de main
argumentos main ()
argumentos lidos, ou EOF, quandohá
lidos, ou quando háum
EOF, ()
um {{
erro ou o ficheiro acabou. EOF é uma
erro ou o ficheiro acabou. EOF é uma FILE
FILE *fcars;
*fcars;
constante
constantesimbólica
simbólicadefinida
definidano
noficheiro
ficheiro
...
...
<stdio.h> fcars
fcars == fopen("CARS_TODAY",
fopen("CARS_TODAY", "r");
"r");
<stdio.h> n_cars
n_cars = loadcars(fcars);
= loadcars(fcars);
fclose(fcars);
fclose(fcars);
...
...
}}

int loadcars(FILE *f) Se a abertura do ficheiro não se puder fazer, a


{ função fopen devolve NULL (constante
int i; simbólica definida no ficheiro <stdio.h>):
for (i = 1; #define
#define NO_SUCH_FILE
NO_SUCH_FILE 11
fscanf(f, "%d%s", main
main ()()
&numbers[i], {{
FILE
FILE *fcars;
names[i]) != EOF; ...
*fcars;
...
++i) if
if ((fcars=fopen("CARS_TODAY",
((fcars=fopen("CARS_TODAY", "r"))
"r")) ==
== NULL)
NULL)
{{
; printf("Ficheiro CARS_TODAY inacessível.");
printf("Ficheiro CARS_TODAY inacessível.");
return i - 1; return
return NO_SUCH_FILE;
NO_SUCH_FILE;
}}
} n_cars
n_cars == loadcars(fcars);
loadcars(fcars);
fclose(fcars);
fclose(fcars);
...
...
}}

Programação Avançada com C 97 Programação Avançada com C 98

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Tratamento de erros Controlando a forma dos inputs
A função sscanf é análoga à fscanf, mas o
As mensagens de erros de um programa C primeiro argumento é uma cadeia. Os carac-
devem ser escritas no ficheiro stderr, com a
teres são “lidos” a partir dessa cadeia.
função fprintf.
A função exit termina o programa em caso main
main ()
()
de erro irrecuperável e transmite o valor do {{
FILE
FILE *fcars;
seu argumento (de tipo int) ao processo que char
*fcars;
char reply[81];
reply[81];
chamou o programa que agora termina. A int
int n;
n;
função exit vem declarada no ficheiro int
int i;
i;
<stdlib.h>. if
if ((fcars
((fcars == fopen("CARS_TODAY",
fopen("CARS_TODAY", "r"))
"r")) ==
== NULL)
NULL)
{{
printf("Ficheiro CARS_TODAY inacessível.");
printf("Ficheiro CARS_TODAY inacessível.");
#define
#define NO_SUCH_FILE
NO_SUCH_FILE 11 return
return NO_SUCH_FILE;
NO_SUCH_FILE;
}}
main
main () n_cars
() n_cars == loadcars(fcars);
loadcars(fcars);
{{ fclose(fcars);
fclose(fcars);
FILE
FILE *fcars;
*fcars;
... while
... while (printf("Número:
(printf("Número: "), "), strlen(gets(reply)))
strlen(gets(reply)))
if
if ((fcars=fopen("CARS_TODAY", "r"))
((fcars=fopen("CARS_TODAY", "r")) ==
== NULL)
NULL) {{
{{ if
if (!sscanf(reply,
(!sscanf(reply, "%d",
"%d", &n))
&n))
fprintf(stderr,
fprintf(stderr, printf("Número inválido.\n");
printf("Número inválido.\n");
"Ficheiro
"Ficheiro CARS_TODAY
CARS_TODAY inacessível."); else
inacessível."); else ifif ((i
((i == indexof(n))
indexof(n)) ==
== 0)
0)
exit(NO_SUCH_FILE); printf("Não
exit(NO_SUCH_FILE); printf("Não participa.\n");
participa.\n");
}} else
else
n_cars
n_cars == loadcars(fcars); printf("%s\n",
loadcars(fcars); printf("%s\n", names[i]);
names[i]);
fclose(fcars);
fclose(fcars); }}
... return
... return 0;0;
}} }}

Programação Avançada com C 99 Programação Avançada com C 100

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Buscas com sentinela Buscas dicotómicas
Para procurarmos um número no vector Como o vector numbers está ordenado,
numbers podemos começar por colocá-lo na podemos fazer uma busca dicotómica. Em
posição de índice 0, que está desocupada. cada passo do ciclo, comparamos o número
Depois faz-se uma busca linear, de cima para procurado com o elemento que está no meio
baixo. O elemento será encontrado de do intervalo de busca. Das três uma: ou são
certeza. Se for encontrado na posição 0, isso iguais, ou o primeiro é menor que o segundo,
quer dizer que ele não existia: ou o segundo é menor que o primeiro:

int
int indexof(int
indexof(int n) n)
int
int indexof(int
indexof(int n)
n) {{
{{ int
int int a;
a;
int i;
i; int
int ii == 1;
1;
for
for (numbers[0]
(numbers[0] == n,
n, ii == n_cars;
n_cars; int j = n_cars;
numbers[i] != n; int j = n_cars;
numbers[i] != n; while
while (i(i <=
<= j)j)
––i)
––i) if
;; if (n == numbers[a=(i+j)/2])
(n == numbers[a=(i+j)/2])
return
return a; a;
return
return i;
i; else
}} else ifif (n(n << numbers[a])
numbers[a])
jj == aa -- 1;
1;
else
else
ii == aa ++ 1;
1;
O número colocado na posição 0 funciona return
return 0;0;
}}
como sentinela.

Programação Avançada com C 101 Programação Avançada com C 102

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Buscas dicotómicas (propriamente ditas) Problema dos pilotos (2)
Podemos fazer uma busca dicotómica com Problema:
uma só comparação em cada passo do ciclo. Como no problema anterior, a lista dos
O número procurado ou é menor ou igual ao concorrentes ao grande prémio existe
elemento que está no meio do intervalo de num ficheiro de texto, chamado
busca, ou não é. Mas temos que esperar que "CARS_TODAY". Cada linha deste ficheiro
o intervalo de busca se reduza a um único contém o número do carro e o nome
elemento: (apelido) do piloto. Agora queremos
escrever um programa para saber qual é o
int
int indexof(int
indexof(int n) n) carro conduzido por determinado piloto. O
{{ programa funciona ciclicamente e termina
int
int a;
a; quando se der uma resposta vazia
int
int ii == 1;
1; (newline), em vez do número do carro.
int j = n_cars;
int j = n_cars;
while
while (i(i << j)
j)
if
if (n <=
(n <= numbers[a=(i+j)/2])
numbers[a=(i+j)/2]) Solução:
jj == a;
a; Como antes, usar dois vectores paralelos,
else
else um com os números dos carros, outro
ii == aa ++ 1;
1; com os nomes dos pilotos; carregá-los a
return
return (n == numbers[i]
(n == numbers[i] ?? ii :: 0);
0); partir do ficheiro; pesquisar o nome dado
}} no vector dos nomes; devolver o número
que está na posição correspondente.

Programação Avançada com C 103 Programação Avançada com C 104

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Copiando cadeias Comparando cadeias
As cadeias não se podem afectar
directamente. Usa-se a função strcpy: As cadeias não se podem comparar
directamente. Usa-se a função strcmp.
Exemplo: strcmp(s1, s2) dá um número < 0 se
s1 < s2, 0 se s1 == s2, e um número >0 se
char s1 > s2.
char s0[32];
s0[32];
char
char s1[80];
s1[80];
...
... char
char s0[32];
s0[32];
strcpy(s0,
strcpy(s0, s1);
s1); /*
/* s0
s0 == s1
s1 */
*/ char
char s1[80];
s1[80];
...
... ...
...
if
if (strcmp(s0,
(strcmp(s0, s1)
s1) <=
<= 0);
0);
/*
/* if (s0 <= s1) */
if (s0 <= s1) */
...
...
É preciso que a cadeia s1 caiba na cadeia s0
(ou seja, que o comprimento de s1 seja if
if (!strcmp(s0,
(!strcmp(s0, s1));
s1));
inferior à dimensão de s0). Caso contrário, a /*
/* if
if (s0
(s0 ==
== s1)
s1) */
*/
memória fica corrompida, porque a cópia vai ...
...
continuar sobre uma zona que já não
pertence à cadeia s0.

Programação Avançada com C 105 Programação Avançada com C 106

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Buscas de cadeias com sentinela Ordenação de um vector de cadeias
int
int indexof(char
indexof(char s[])
s[]) void
void bubblesort(void)
{{ bubblesort(void)
{{
int
int i;
i; int
int i;
for
for (strcpy(names[0],s),
(strcpy(names[0],s), i=n_cars;
i=n_cars; i;
int
int j;
strcmp(names[i],s);
strcmp(names[i],s); j;
char
char my_name[16];
––i)
––i) my_name[16];
int
int my_number;
;; for
my_number;
return for (i
(i == 2;
2; ii <=
<= n_drivers;
n_drivers; ++i)
++i)
return i;i; for
for (j = n_drivers; jj >=
(j = n_drivers; >= i;
i; ––j)
––j)
}} if
if (strcmp(names[j
(strcmp(names[j -- 1], 1], names[j])
names[j]) >> 0)
0)
main {{
main ()
()
{{ strcpy(my_name,
strcpy(my_name, names[j
names[j -- 1]);
1]);
FILE
FILE *fcars; strcpy(names[j
char
*fcars; strcpy(names[j - 1],
- 1], names[j]);
names[j]);
char reply[81];
reply[81]; strcpy(names[j],
int
int n;
n; strcpy(names[j], my_name);
my_name);
int i; my_number
my_number == numbers[j
numbers[j -- 1];
1];
int i;
numbers[j
numbers[j -- 1]1] == numbers[j];
numbers[j];
fcars
fcars == fopen("CARS_TODAY",
fopen("CARS_TODAY", "r"); numbers[j]
n_cars
"r"); numbers[j] == my_number;
my_number;
n_cars = loadcars(fcars);
= loadcars(fcars); }}
fclose(fcars);
fclose(fcars); }}
while
while (printf("Nome:
(printf("Nome: "),
"),
strlen(gets(reply)))
strlen(gets(reply)))
if
if ((i
((i == indexof(reply))
indexof(reply)) ==
== 0)
0)
printf("Não participa.\n");
printf("Não participa.\n");
else
else É preciso reordenar em paralelo o vector
printf("%d\n",
printf("%d\n", numbers[i]);
numbers[i]); numbers.
return
return 0;0;
}}

Programação Avançada com C 107 Programação Avançada com C 108

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Busca dicotómica de cadeias (1) Busca dicotómica de cadeias (2)
int
int indexof(char
indexof(char s[]) s[]) Como a função strcmp pode dar três resulta-
{{
int dos (<0, 0, >0) uma busca “tricotómica” pare-
int a;a;
int
int ii == 1;1; ce apropriada. Para usar a instrução switch é
int
int jj == n_drivers;
n_drivers; preciso calcular o sinal do resultado:
while
while (i (i << j)
j)
if
if (strcmp(s,
(strcmp(s, names[a
names[a == (i
(i ++ j)
j) // 2])
2]) <= int
0)
<= int sign(int
sign(int n)n)
0) {{
jj == a;
a;
else return
return (n
(n >=
>= 0)
0) -- (n
(n <=
<= 0);
0);
else }}
ii == aa ++ 1;
1;
return
return (strcmp(s,
(strcmp(s, names[i])
names[i]) ?? 00 :: i);
i);
}} int
int indexof(char
indexof(char name name [])
[])
{{
int
int a;a;
Ou: int
int ii == 1;
1;
int
int jj == n_drivers;
n_drivers;
while
while (i <=
(i <= j)
j)
int
int indexof(char
indexof(char s[]) s[]) switch
{{ switch
(sign(strcmp(name,
(sign(strcmp(name, names[a=(i+j)
names[a=(i+j) // 2])))
2])))
int
int a;
a; {{
int
int ii == 1; case
int
1; case -1:
-1:
int jj == n_drivers;
n_drivers; jj == aa -- 1;
1;
while
while (i(i <=
<= j)j) break;
if break;
if (strcmp(s,
(strcmp(s, names[a
names[a == (i
(i ++ j)
j) // 2])
2]) << 0)
0) case
case 0: 0:
jj == aa -- 1;
1; return
return a;a;
else
else ifif (strcmp(s,
(strcmp(s, names[a])
names[a]) >> 0) 0) case
case 1: 1:
ii == aa ++ 1; ii == aa ++ 1;
1;
1;
else break;
else return
return a; a; }}
break;
return
return 0;0;
}} return
return 0; 0;
}}

Programação Avançada com C 109 Programação Avançada com C 110

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Segunda parte: C avançado Apontadores
1. Apontadores. Variável:

2. Estruturas. Apontadores para estruturas nome...


tipo...
3. Processamento de texto. valor...
localização...
4. Alocação dinâmica.
5. Vectores de apontadores. Operador de endereço (ou referenciação):
6. Estruturas auto-referenciadas: listas e &&
árvores.
7. Recursividade. int n;
...
8. Tabelas de hash. &n /* o endereço da
variável n */
9. Operações sobre bits.
10. Listas de argumentos de comprimento Operador de indirecção (ou desreferenciação):
variável.
11. Argumentos na linha de comando.
**
12. Passagem para o C++. *&n /* a variável cujo
endereço é o endereço
da variável n, ou seja
a variável n */
Programação Avançada com C 111 Programação Avançada com C 112

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Variáveis apontadores Passagem de parâmetros por valor
Declaração de apontadores para int: Em
EmC, C,os
osparâmetros
parâmetrosdasdasfunções
funçõessãosão
passados
passados por valor. Quer dizer,aafunção
por valor. Quer dizer, funçãousa,
usa,
int
int *p;
*p; int n; nos
int *p; noscálculos,
cálculos,uma
umacópia
cópiado
dovalor
valordodoargu-
argu-
mento,
mento,eenão
nãoooargumento
argumentopropriamente
propriamentedito.
dito.
... Por
n = 3; Por isso, a função não podemodificar
isso, a função não pode modificar
directamente
directamenteoovalor
valordos
dosseus
seusargumentos
argumentos(a (a
p = &n; menos que a eles aceda externamente).
*p = 5; menos que a eles aceda externamente).
printf("%d\n", n);
... Quando
Quandoumaumafunção
funçãousa usaum
umparâmetro
parâmetrocomo
como
variável,
variável, está de facto a usar umavariável
está de facto a usar uma variável
local
localdeclarada
declaradaimplicitamente
implicitamentecom comoomesmo
mesmo
tipo
tipo do parâmetro e inicializada com ovalor
do parâmetro e inicializada com o valor
Declaração de apontadores para char: desse
desseparâmetro.
parâmetro.
char
char *c;
*c; int sum(int n) int sum(int n)
{ /* código fictício
int s; */
Declaração de apontadores para double: for (s = 0; {
n > 0; int _n = n;
double
double *d;
*d; ––n) int s;
s += n; for (s = 0;
return s; _n > 0;
Os ficheiros são apontadores: } ––_n)
s += _n;
FILE
FILE *fp;
*fp; return s;
}

Programação Avançada com C 113 Programação Avançada com C 114

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Parâmetros de saída em funções Passagem por apontador
Para
Parauma
umafunção
funçãopoder
podermodificar
modificarpara-
para- Exemplo: uma função para trocar os
metricamente
metricamente o valor deuma
o valor de umavariável,
variável,esta
esta valores de duas variáveis int.
tem
temque
queser
serpassada
passadaporporapontador.
apontador.OOvalor
valor
do argumento, um apontador, não será modi-
do argumento, um apontador, não será modi- void swap (int *x, int *y)
ficado,
ficado,mas
maspor
porindirecção
indirecçãopode
podemodificar-se
modificar-se {
oovalor da variável apontada.
valor da variável apontada. int temp;

temp = *x;
Exemplo: uma função para calcular as raízes *x = *y;
do polinómio ax2+ bx + c, devolvendo 0 se não *y = temp;
houver raízes e 1 se houver: }

int roots(double a, double b, double c,


double *x1, double *x2)
{ Assim estaria errado:
double delta = b * b - 4.0 * a * c;
if (delta >= 0.0)
{ void swap (int x, int y)
*x1 = (-b + sqrt(delta)) / (2.0 * a); {
*x2 = -b / a - *x1; int temp;
return 1;
}
else temp = x;
return 0; x = y;
} y = temp;
}
Programação Avançada com C 115 Programação Avançada com C 116

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Apontadores na função scanf Os quadros são passados
Exemplo: um programa para usar a função por apontador
roots: Em C, os parâmetros que são quadros são
#include <stdio.h> sempre passados por apontador. Assim, não
#include <math.h> há cópia local do valor do argumento, e as
operações fazem-se indirectamente sobre
int roots(double, double, double, esse argumento.
double *, double *);
Por isso é que não aparece o operador & nas
main () funções scanf e fscanf, quando o argu-
{ mento é uma cadeia:
double a, b, c;
double x1, x2; char name[16];
printf("a, b, c: "); int number;
scanf("%lf%lf%lf", &a, &b, &c) ; FILE *f;
if (roots( a, b, c, &x1, &x2) ) ...
printf("x1 = %6.2f x2 = %6.2f\n", x1, x2) ; fscanf(f, "%d%s", &number, name);
else ...
printf("Não há raízes.\n");
return 0; Na função gets também não se usa o &:
}
char reply[81];
int roots(double a, double b, double c, ...
double *x1, double *x2) while (printf("Número: "),
{ strlen(gets(reply)))
... {
} ...
}
Programação Avançada com C 117 Programação Avançada com C 118

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Vectores e apontadores Aritmética de apontadores
Seja:
Em
EmC,
C,há
háuma
umarelação
relaçãoestreita
estreitaentre
entreos
os int a[16];
vectores e os apontadores.
vectores e os apontadores. int *p;
int *q;
Seja: ...
p = &a[i]; p aponta para a[i];
int a[16]; q = &a[j]; q aponta para a[j].
&a[i] é o endereço do i-ésimo elemento do
vector a. Pode ser afectado a um apontador Então:
para int:
p + k p + k é um apontador que
int *p; aponta para a[i + k].
... *(p + k) *(p + k) referencia
p = &a[i]; a[i + k].
Agora p aponta para o i–ésimo elemento do p - q p - q vale i - j.
vector a e *p permite aceder a esse valor.
p <= q p <= q vale i <= j.
Aliás, o nome do vector “vale” o endereço do
primeiro elemento, e portanto E também:
p = a;
é o mesmo que a[i] *(a+i)
p = &a[0];
&a[i] a+i
Programação Avançada com C 119 Programação Avançada com C 120

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Incrementação de apontadores Ciclo de leitura com apontadores (1)
Seja ainda: Com vectores (revisão):
int
int loadcars(FILE
loadcars(FILE *f) *f)
{{
int a[16]; int
int i;
i;
int *p; for
for (i
(i == 1;
1;
... fscanf(f,
fscanf(f, "%d%s",
"%d%s",
&numbers[i],
p = &a[i]; p aponta para a[i]. &numbers[i],
names[i])
names[i]) !=
!=
EOF;
EOF;
Então: ++i)
++i)
;;
return
return ii -- 1;
++p aponta para a[i+1], e incrementa o }}
1;
apontador p (o qual fica a apontar
para a[i+1]). Com apontadores:
int
int loadcars(FILE
loadcars(FILE *f) *f)
*++p referencia a[i+1], e incrementa o {{
apontador p (o qual fica a apontar int
para a[i+1]). int *ip;
*ip;
char
char (*sp)
(*sp) [16];
[16];
aponta para a[i], e incrementa o for
for (ip=numbers+1, sp
(ip=numbers+1, sp == names+1;
names+1;
p++ fscanf(f, "%d%s",
apontador p (o qual fica a apontar fscanf(f, "%d%s",
ip++,
ip++,
para a[i+1]). *sp++)
*sp++) !=!= EOF;
EOF;
*p++ referencia a[i], e incrementa o ))
apontador p (o qual fica a apontar ;;
return
return ipip -- numbers
numbers -- 2;2;
para a[i+1]). }}
Programação Avançada com C 121 Programação Avançada com C 122

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ciclo de leitura com apontadores (2) Buscas lineares com apontadores
Observações: Buscas com sentinela:
char (*sp) [16]; int
int indexof(int
indexof(int n) n)
{{
Declara sp como apontador para uma cadeia
int
int *ip;
*ip;
de 16 caracteres. for
for (*numbers
(*numbers == n,n,
ip
ip == numbers
numbers ++ n_cars;
n_cars;
char *sp [16]; *ip
*ip !=
!= n;
n;
Declararia sp como vector de 16 apontadores ––ip)
––ip)
para char. ;;
return
return ip
ip -- numbers;
numbers;
}}
ip++
No i-ésimo passo, ip++ “vale” um apontador Buscas simples:
que aponta para numbers[i].
int
int indexof(int
indexof(int n) n)
sp++ {{
int
int *ip;
*ip;
No i-ésimo passo, sp++ “vale” um apontador for
que aponta para names[i]. for (ip
(ip == numbers
numbers ++ n_cars;
n_cars;
ip
ip > numbers &&
> numbers && *ip
*ip !=
!= n;
n;
––ip)
––ip)
*sp++ ;;
return
return ip
ip -- numbers;
numbers;
No i-ésimo passo, *sp++ referencia names[i]. }}
Note-se que names[i] é uma cadeia.
Programação Avançada com C 123 Programação Avançada com C 124

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Cadeias e apontadores Funções que devolvem cadeias
Em C, as funções não podem ter vectores
As cadeias de caracteres são vectores e como resultado, mas apontadores sim:
portanto são passadas por apontador. Nas
funções, os parâmetros cadeia aparecem Exemplo. A função strcpy copia o segundo
normalmente declarados char *. argumento para o primeiro e devolve um
apontador para o primeiro:
Exemplo. O comprimento de uma cadeia: char
char *strcpy(char
*strcpy(char *s, *s, char
char *t)
*t)
{{
int
int strlen(char
strlen(char *s);
*s); char
char *p
*p == s;
s;
{{ while
while (*s++
(*s++ == *t++)
*t++)
char ;;
char *p
*p == s;
s; return
while
while (*s)
(*s) return p;p;
}}
++s;
++s;
return Exemplo. A função strchr devolve um
return ss -- p;
p;
}} apontador para a primeira ocorrência do
Exemplo. A comparação de cadeias: segundo argumento (um char) no primeiro
(uma cadeia):
int
int strcmp(char
strcmp(char *s, *s, char
char *t)
*t)
{{ char
char *strchr(char
*strchr(char *s, *s, char
char c)
c)
{{
for
for (( ;; *s
*s ==
== *t;
*t; s++,
s++, t++)
t++) for
if (!*s) for (( ;; *s;
*s; ++s)
++s)
if (!*s) if
if (*s ==
(*s == c)
c)
return
return 0; 0; return
return s;s;
return
return *s - *t;
*s - *t; return NULL;
return NULL;
}} }}
Programação Avançada com C 125 Programação Avançada com C 126

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O ficheiro <string.h> Ler e escrever cadeias (1)
char a)
char *strcpy(char
*strcpy(char *,
*, char
char *);
*); b) Funções declaradas no ficheiro <stdio.h>
char
char *strncpy(char *, char
*strncpy(char *, char *,
*, size_t);
size_t);
char c)
char *strcat(char
*strcat(char *,
*, char
char *);
*);
char
char *strncat(char *, char
*strncat(char *, char *,
*, size_t);
size_t);
d) char
char *fgets(char
*fgets(char *,
*, int,
int, FILE
FILE *);
*); a)
int fputs(char *, FILE *);
int fputs(char *, FILE *); b)
int e)
int strcmp(char
strcmp(char *,
*, char
char *);
*);
int f)
int strncmp(char *, char
strncmp(char *, char *,
*, size_t);
size_t); char c)
char *gets(char
*gets(char *);
*);
char
char *strchr(char
*strchr(char *,
*, int);
int);
g) int
int puts(char *);
puts(char *); d)
char h)
char *strrchr(char
*strrchr(char *,
*, int);
int);
char i)
char *strpbrk(char
*strpbrk(char *,
*, char
char *);
*);
char j) a) Lê no máximo arg_2 - 1 caracteres para
char *strstr(char
*strstr(char *,
*, char
char *);
*);
size_t k) arg_1, até um newline, inclusive; junta
size_t strlen(char
strlen(char *);
*); '\0'. Devolve arg_1, ou NULL, em fim de
...
...
ficheiro ou erro.
size_t é um tipo inteiro, definido em <string.h>.
b) Escreve arg_1 em arg_2. Devolve EOF em
a) copia arg_2 para arg_1; devolve arg_1.
b) copia arg_3 caracteres. caso de erro.
c) concatena arg_2 a arg_1; devolve arg_1.
d) concatena arg_3 caracteres.
c) Lê uma linha do terminal, substituindo
e) compara arg_1 e arg_2: < dá <0; == dá 0; > dá >0; newline por '\0'. Devolve arg, ou NULL,
f) compara arg_3 caracteres. em fim de ficheiro ou erro.
g) apontador para a primeira ocorrência de arg_2 em arg_1.
h) apontador para a última ocorrência de arg_2 em arg_1. d) Escreve arg no terminal e depois newline.
i) apontador para a primeira ocorrência em arg_1 de um Devolve EOF em caso de erro.
dos caracteres de arg_2.
j) apontador para a primeira ocorrência de arg_2 em arg_1.
k) comprimento.
Programação Avançada com C 127 Programação Avançada com C 128

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ler e escrever cadeias (2) Exercícios (4)
Escrever cadeias com printf: 1. Escrever um programa C para listar no
Forma geral da conversão: terminal todas as linhas de um ficheiro que
contenham determinada cadeia de caracteres.
%[–][width][.precision]s O programa começa por pedir a cadeia;
depois, ciclicamente, pede o nome de um
[-] se presente, manda encostar ficheiro e analisa esse ficheiro; termina
à esquerda. quando o utilizador responder com uma
[width] se presente, especifica a cadeia vazia para o nome do ficheiro.
largura mínina do campo de Cada linha afixada vem precedida pelo seu
escrita. Valor por defeito: 1. número de linha no ficheiro.
[.precision] se presente, especifica o nú- 2. Escrever uma variante deste programa que
mero de caracteres a escre- não distinga entre maiúsculas e minúsculas.
ver. (Se não, escreve-se
tudo.) 3. Escrever um programa para numerar à
direita todas as linhas não vazias de um
ficheiro. A numeração deve aparecer encos-
Um asterisco no lugar da largura ou da preci-
tada à coluna 80 e só deve ser escrita se
são manda ir buscar o valor respectivo ao couber.
próximo argumento (que tem que ser int).
4. Escrever um programa para eliminar de
printf("%s%.*s\n", todas as linhas de um ficheiro os caracteres
printf("%s%.*s\n", brancos (espaços e tabs) à direita.
name1,
name1,
32
32 -- (int)
(int) strlen(name1),
strlen(name1),
name2);
name2);
Programação Avançada com C 129 Programação Avançada com C 130

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Estruturas (1) Estruturas (2)
typedef struct
typedef struct { member_type member_name;
{ int number; member_type member_name;
char name [16]; ...
} Car; member_type member_name;
} new_type;
typedef
typedef struct
struct
{{ int
int number;
number; typedef
typedef struct
struct
char
char name
name [16];
[16]; {{ int
int invoice_no;
invoice_no;
}} Car;
Car; long
long invoice_date;
invoice_date;
long
long invoice_amount;
invoice_amount;
#define
#define MAX_CARS
MAX_CARS 26
26 long
long invoice_paid;
invoice_paid;
Car
Car cars [MAX_CARS
cars [MAX_CARS ++ 1];
1]; }} History;
History;
int
int n_cars;
n_cars;
FILE
FILE *f_cars;
*f_cars; typedef
typedef struct
struct
Car temp_car;
Car temp_car; {{ int
int account_no;
account_no;
int
int account_type;
account_type;
int
int fscanfCar(FILE
fscanfCar(FILE *,
*, Car
Car *);
*); char
char name[32];
name[32];
int
int loadCars(FILE *, Car *);
loadCars(FILE *, Car *); char
char street[32];
street[32];
char
char city[16];
city[16];
int
int fprintfCar(FILE
fprintfCar(FILE *,
*, Car
Car *);
*); int
int code;
code;
void
void dumpCars(FILE *, Car *, int);
dumpCars(FILE *, Car *, int); long
long last_payment;
last_payment;
History
History payments[4];
payments[4];
}} Client;
Client;

Programação Avançada com C 131 Programação Avançada com C 132

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Definição de nomes de tipos Acesso aos membros da estrutura
typedef already_known_type new_type; Operador ponto (membro de estrutura):
structure_name . member
typedef
typedef long
long Date;
Date;
typedef
typedef struct
struct
{{ int Exemplo:
int invoice_no;
invoice_no;
Date
Date invoice_date;
invoice_date;
long int
int fprintfCar(FILE
fprintfCar(FILE *,
*, Car);
long invoice_amount;
invoice_amount; int
Car);
Date
Date invoice_paid;
invoice_paid; int fscanfCar(FILE *, Car
fscanfCar(FILE *, Car *);
*);
}} History;
History; int
int fprintfCar(FILE
fprintfCar(FILE *f,
*f, Car
Car c)
c)
... {{
... return
return fprintf(f,
fprintf(f, "%2d
"%2d %-16s",
%-16s",
c.number,
c.number,
c.name);
c.name);
Exemplo. No ficheiro <string.h>: }}

typedef
typedef unsigned
unsigned long
long size_t;
size_t; int
int fscanfCar(FILE
fscanfCar(FILE *f,
*f, Car
Car *p)
*p)
{{
return
return fscanf(f,
fscanf(f, "%d%s",
"%d%s",
char
char *strncpy(char
*strncpy(char *,
*, char
char *,
*, size_t);
size_t); &(*p).number,
&(*p).number,
char
char *strncat(char
*strncat(char *,
*, char
char *,
*, size_t);
size_t);
int (*p).name);
int strncmp(char
strncmp(char *,
*, char
char *,
*, size_t);
size_t); (*p).name);
}}
size_t
size_t strlen(char
strlen(char *);
*);

Programação Avançada com C 133 Programação Avançada com C 134

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Estruturas e funções Apontadores para estruturas
Operador seta (apontador para estrutura):
Quando as estruturas são parâmetros de en-
trada, podem ser passadas por valor. No typedef struct
entanto, normalmente passam-se por aponta- { ... x;
dor, para evitar a sobrecarga da cópia para a ... ...;
variável local. } T;
Exemplo:
T *p;
int
int fprintfCar(FILE
fprintfCar(FILE *,
*, Car
Car *);
*);
int Então:
int fscanfCar(FILE *, Car *);
fscanfCar(FILE *, Car *);

int
int fprintfCar(FILE
fprintfCar(FILE *f,
*f, Car
Car *p)
*p)
(*p).x p –> x
{{
return
return fprintf(f,
fprintf(f, "%2d
"%2d %-16s",
%-16s", Exemplo:
(*p).number, int
(*p).number,
(*p).name); int fprintfCar(FILE
fprintfCar(FILE *f,
*f, Car
Car *p)
*p)
(*p).name); {{
}} return
return fprintf(f,
fprintf(f, "%2d
"%2d %–16s",
%–16s",
p–>number,
p–>number,
int p–>name);
p–>name);
int fscanfCar(FILE
fscanfCar(FILE *f,
*f, Car
Car *p)
*p) }}
{{
return
return fscanf(f,
fscanf(f, "%d%s", int
"%d%s", int fscanfCar(FILE
fscanfCar(FILE *f,
*f, Car
Car *p)
*p)
&(*p).number,
&(*p).number, {{
(*p).name); return
return fscanf(f,
fscanf(f, "%d%s",
"%d%s",
(*p).name); &p–>number,
}} &p–>number,
p–>name);
p–>name);
}}

Programação Avançada com C 135 Programação Avançada com C 136

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Tabela completa dos operadores Problema do Grande Prémio (1)
Pretende-se um programa para calcular a clas-
Operadores Sentido sificação dos pilotos no campeonato do mundo
de fórmula 1, após a realização de um grande
() [] –> . prémio, dada a classificação antes dele e a
! + - * & ++ –– ~ (type) sizeof ordem de chegada na corrida. A classificação
* / %
“antes” vem num ficheiro de texto, onde cada
linha contém três informações: nome do piloto,
+ - nome da equipa, e número de pontos já
<< >> obtidos. Na classificação aparecem todos os
< <= > >= pilotos inscritos no campeonato, mesmo os
== != que ainda têm zero pontos. (Portanto, pode-
mos ter a certeza de que se um piloto entra na
&
corrida, o seu nome está no ficheiro da classi-
^ ficação.) As linhas estão ordenadas decres-
| centemente pelo número de pontos. O nome do
&& piloto é formado pelo apelido, seguido de um
|| espaço, seguido da inicial do nome próprio,
seguida de um ponto. O nome da equipa não
?:
contém espaços. O ficheiro tem uma forma
= += -= *= /= %= &= ^= |= <<= >>= tabular, com os nomes dos pilotos alinhados à
, esquerda na coluna 1, os nomes das equipas
alinhados à esquerda na coluna 21, e os
números de pontos alinhados à direita na
Nota: Os operadores ~, sizeof, <<, >>, &, ^, |, &=, ^=, |=, coluna 44. Os apelidos ocupam no máximo 15
<<=, e >>= não foram estudados. caracteres e os nomes das equipas 19. Por
exemplo:
Programação Avançada com C 137 Programação Avançada com C 138

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Problema do Grande Prémio (2) Problema do Grande Prémio (3)
(continuação)
(continuação:)
Para evitar problemas com a introdução de
Senna
Senna A.
A. McLaren-Honda
McLaren-Honda 78
78 dados no fim de cada corrida, em vez de se dar
Prost
Prost A. Ferrari 69
Berger
A. Ferrari 69 os nomes dos pilotos, indica-se o número do
Berger G.
G. McLaren-Honda
McLaren-Honda 40
40 carro. Para permitir fazer a correspondência
Piquet
Piquet N.
N. Benetton-Ford
Benetton-Ford 35
35
Boutsen
Boutsen T.
T. Williams-Renault
Williams-Renault 32
32 entre o número do carro e o nome do piloto
Mansell
Mansell N.
N. Ferrari
Ferrari 31
31 que o tripula, existe um outro ficheiro de texto,
Patrese
Patrese R. Williams-Renault 22
...
R. Williams-Renault 22 previamente preparado, que em cada linha
... contém essas duas informações, para cada
piloto que entra na corrida: primeiro o número
do carro, depois um ou vários espaços, depois
Em cada corrida, o primeiro classificado o apelido do piloto, exactamente na forma em
ganha 10 pontos, o segundo 6, o terceiro 4, o que aparece no ficheiro da classificação.
quarto 3, o quinto 2, o sexto 1, e todos os (Sabe-se que todos os pilotos têm apelidos
outros não ganham pontos nenhuns. diferentes.) Este ficheiro vem ordenado pelo
(continua) número do carro. (Nem todos os números são
utilizados: por exemplo, ninguém quer correr
com o número 13, pelo sim, pelo não…)
O ficheiro com a classificação “depois” de
cada grande prémio tem a mesma forma do
ficheiro inicial. No caso de dois ou mais
concorrentes ficarem com o mesmo número de
pontos, respeita-se a ordem que eles tinham
antes da corrida.

Programação Avançada com C 139 Programação Avançada com C 140

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Problema do Grande Prémio Operações sobre carros
(estratégia de resolução)

typedef a)
Resolução: typedef struct
struct
{{ int
int number;
number;
Começa-se por carregar os dois ficheiros com char
char name
name [16];
[16];
}} Car;
Car;
os dados para vectores. Depois, durante a
introdução da chegada, para verificar se um int
int fscanfCar(FILE
fscanfCar(FILE *,*, Car
Car b)
carro, dado pelo seu número, participou *);
*);
int
int loadCars(FILE
loadCars(FILE *,
*, Car
Car *);
mesmo na corrida, faz-se uma busca dico- *);
tómica no vector dos carros, obtendo o nome c)
#define
#define MAX_CARS
MAX_CARS 26
26
do condutor. Usando esse nome, faz-se uma Car
Car cars
cars [MAX_CARS
[MAX_CARS ++ 1];
1];
int n_cars;
busca linear no vector dos pilotos e adi- int n_cars;
ciona-se o número de pontos. Trata-se de int d)
int carsindexof(int);
carsindexof(int);
uma busca linear muito simplificada pois há a
garantia de o elemento procurado existir (os
ficheiros de dados não têm erros), mas é a) Definição do tipo Car.
preciso ter cuidado para não registar por b) Declaração de duas funções que operam
engano duas vezes a chegada do mesmo sobre o tipo Car.
piloto. Finalmente, assim que termina a intro- c) Definição do vector cars, com MAX_CARS
dução manual da chegada, ordena-se o vec- + 1 elementos de tipo Car (a posição 0
tor da classificação por pontos e despeja-se não é ocupada).
para o ficheiro resultado. d) Declaração de uma função de busca
dicotómica no vector cars. (Este vector
estará ordenado pelo campo number.)

Programação Avançada com C 141 Programação Avançada com C 142

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações sobre pilotos A função para os pontos
typedef a)
typedef struct
struct
{{ char
char name
name [16];
[16]; Declaração:
char initial;
char initial;
char int
int points(int);
char team
team [20];
[20]; points(int);
int pts;
int pts;
}} Driver;
Driver;
int b)
int fscanfDriver(FILE
fscanfDriver(FILE *,*, Driver
Driver *);
*); Definição (usando uma tabela estática):
int
int loadDrivers(FILE
loadDrivers(FILE *,
*, Driver
Driver *);
*);
int
int fprintfDriver(FILE
fprintfDriver(FILE *, *, Driver
Driver *);
*);
void int
int points(int
points(int i) i)
void dumpDrivers(FILE *, Driver *,
dumpDrivers(FILE *, Driver *, int);
int); {{
#define c) static
static int
int table
table [] [] == {10,
{10, 6,
6, 4,
4, 3,
3, 2,
2, 1};
#define MAX_DRIVERS
MAX_DRIVERS 40
40 return
1};
Driver
Driver drivers
drivers [MAX_DRIVERS
[MAX_DRIVERS ++ 1];
1]; return ii <=
<= 66 ?? table[i
table[i -- 1]
1] :: 0;
0;
int }}
int n_drivers;
n_drivers;
int
int driversindexof(char
driversindexof(char *);
*); d)
void
void bubblesortdrivers(void);
bubblesortdrivers(void); e)

a) Definição do tipo Driver. Esquema de utilização:


b) Declaração de quatro funções que operam ...
...
sobre o tipo Driver. for
for (i
(i == 1;
1; ii <=
<= 6;
6; ++i)
++i)
c) Definição do vector drivers, com {{
...
MAX_DRIVERS + 1 elementos de tipo Car ...
drivers[...].pts
drivers[...].pts += +=
(a posição 0 não é ocupada). points(i);
points(i);
d) Declaração de uma função de busca linear ...
...
}}
no vector drivers. ...
...
e) Função de ordenação do vector drivers.
Programação Avançada com C 143 Programação Avançada com C 144

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Carregamento dos carros Busca dicotómica nos carros
Ler um Car a partir de um ficheiro:
Calcular o índice do elemento do vector
int
int fscanfCar(FILE
fscanfCar(FILE *f,*f, Car
Car *p)
*p) cars cujo campo number tem um valor
{{ dado. O vector cars está ordenado por esse
return
return fscanf(f,
fscanf(f, "%d%s",
"%d%s",
&p->number, campo e portanto usa-se uma busca
&p->number,
p->name);
p->name); dicotómica:
}}
int
int carsindexof(int
carsindexof(int n) n)
Carregar um ficheiro de Car para memória, {{
extern
extern Car
Car cars[];
dado o endereço de carregamento; devolver o extern
cars[];
extern int n_cars;
int n_cars;
número de elementos lidos: int
int a;
a;
int
int ii == 1;
1;
int
int loadCars(FILE
loadCars(FILE *f, *f, Car
Car *p) int
{{
*p) int jj == n_cars;
n_cars;
while
while (i(i << j)
j)
Car
Car *i
*i == p; if
while
p; if (n <=
(n <= cars[a
cars[a == (i
(i ++ j)
j) // 2].number)
2].number)
while (fscanfCar(f,
(fscanfCar(f, i++) i++) !=
!= EOF)
EOF) jj == a;
a;
;; else
return else
return ii -- 11 -- p;
p; ii == aa ++ 1;
1;
}} return
return (n(n ==== cars[i].number
cars[i].number ?? ii :: 0);
0);
}}
Esquema de utilização:
FILE As duas declarações extern (opcionais se
FILE *f_cars;
*f_cars;
...
... as definições correspondentes estiverem
f_cars
f_cars == fopen("CARS_TODAY",
fopen("CARS_TODAY", "r");
"r"); “antes”) servem para lembrar que as
n_cars
n_cars = loadCars(f_cars, cars
= loadCars(f_cars, cars ++ 1);
1); variáveis cars[] e n_cars são externas.
fclose(f_cars);
fclose(f_cars);
...
...
Programação Avançada com C 145 Programação Avançada com C 146

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Carregamento dos pilotos (1) Carregamento dos pilotos (2)
Ler um Driver a partir de um ficheiro: Carregar um ficheiro de Driver para memória,
int
dado o endereço de carregamento; devolver o
int fscanfDriver(FILE
fscanfDriver(FILE *f,
*f, Driver
Driver *p)
*p) número de elementos lidos:
{{
return
return fscanf(f,
fscanf(f, "%s%*c%c.%s%d",
"%s%*c%c.%s%d",
p->name,
p->name,
&p->initial, int
int loadDrivers(FILE
loadDrivers(FILE *f, *f, Driver
Driver *p)
*p)
&p->initial, {{
p->team,
p->team,
&p->pts); Driver
Driver *i
*i == p;
p;
&p->pts); while
}} while (fscanfDriver(f, i++)
(fscanfDriver(f, i++) !=
!= EOF)
EOF)
;;
return
return ii -- 11 -- p;
p;
Cadeia de formato: }}

"%s%*c%c.%s%d"
cadeia, espaço, carácter, ponto, cadeia, número decimal.
O especificador %s “apanha” cadeias de Esquema de utilização:
caracteres não-brancos. Os caracteres brancos
FILE
FILE *f_drivers;
são o espaço (32), o tab (9), o newline (10), o ...
*f_drivers;
carriage return (13), o tab vertical (11), e o form ...
f_drivers
f_drivers == fopen("DRIVERS_BEFORE",
fopen("DRIVERS_BEFORE", "r");
"r");
feed (12). n_drivers
n_drivers == loadDrivers(f_drivers,drivers+1);
loadDrivers(f_drivers,drivers+1);
fclose(f_drivers);
O especificador %c “apanha” um carácter, ...
fclose(f_drivers);
...
mesmo que seja branco.
Um asterisco num especificador determina a
supressão da afectação.
Um carácter não branco na cadeia de formato
fora dos especificadores de conversão tem que
bater certo com o carácter lido.
Programação Avançada com C 147 Programação Avançada com C 148

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Descarregamento dos pilotos (1) Descarregamento dos pilotos (2)
Escrever num ficheiro n elementos de um
Escrever um Driver num ficheiro: vector de Driver:
int
int fprintfDriver(FILE
fprintfDriver(FILE *f, *f, Driver
Driver *p)
*p)
{{ void
return void dumpDrivers(FILE
dumpDrivers(FILE *f,
*f, Driver
Driver *p,
*p, int
int n)
n)
return fprintf(f,
fprintf(f, "%*s%-20s%4d\n",
"%*s%-20s%4d\n", {{
20
20 -- fprintf(f,
fprintf(f, "%s
"%s %c.",
%c.", while
p->name, while (n––)
(n––)
p->name, fprintfDriver(f,
p->initial),
p->initial), fprintfDriver(f, p++);
p++);
"", }}
"",
p->team,
p->team,
p->pts);
p->pts);
}}

Cadeias de formato: Esquema de utilização:


FILE
FILE *f_drivers;
*f_drivers;
"%s %c." ...
...
cadeia, espaço, carácter, ponto. f_drivers
f_drivers == fopen("DRIVERS_AFTER",
fopen("DRIVERS_AFTER", "w");
"w");
dumpDrivers(f_drivers,
dumpDrivers(f_drivers, drivers+1,
drivers+1,
n_drivers);
n_drivers);
"%*s%-20s%4d\n" fclose(f_drivers);
fclose(f_drivers);
cadeia num campo cuja largura vem na lista de parâmetros ...
...
(é o valor da expressão 20 - fprintf(...)), cadeia
alinhada à esquerda num campo com largura 20, número
decimal num campo de largura 4, mudar de linha.

Programação Avançada com C 149 Programação Avançada com C 150

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Busca linear nos pilotos Bubblesort do vector dos pilotos
Calcular o índice do elemento do vector O bubblesort baseia-se em percursos linea-
drivers cujo campo name tem um valor res no vector. Por isso, pode programar-se
dado. O vector drivers não está ordenado com vantagem usando apontadores:
por esse campo e portanto usa-se uma
busca linear. A busca é garantida, porque o void
void bubblesortdrivers(void)
bubblesortdrivers(void)
nome dado existe de certeza: {{
extern
extern Driver
Driver drivers[];
drivers[];
extern
extern int n_drivers;
int n_drivers;
int
int driversindexof(char
driversindexof(char *name)
*name) Driver
Driver d;d;
{{ Driver
Driver *i;
*i;
extern
extern Driver
Driver drivers[];
drivers[]; Driver
Driver *j;
*j;
Driver *i;
Driver *i; for
for (i
(i == drivers
drivers ++ 2; 2;
for
for (i
(i == drivers
drivers ++ 1;
1; ii <=
<= drivers
drivers ++ n_drivers;
n_drivers;
strcmp(i->name,
strcmp(i->name, name);
name); ++i)
++i)
++i)
++i) for
for (j
(j == drivers
drivers ++ n_drivers;
n_drivers;
;; jj >=
>= i;
i;
return
return ii -- drivers;
drivers; ––j)
––j)
}} if
if ((j -- 1)->pts
((j 1)->pts << j->pts)
j->pts)
{{
dd == *(j
*(j -- 1);
1);
*(j
*(j -- 1)1) == *j;
*j;
*j
*j == d;
d;
}}
}}

Programação Avançada com C 151 Programação Avançada com C 152

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Variáveis da função main() Estrutura da função main()
Fora da função main só ficam as variáveis
que vão ser partilhadas por várias funções.
main()
Os ficheiros e as variáveis de controlo main()
{{
pertencem à função main. FILE
FILE *f_cars;
*f_cars;
...
...
f_cars
f_cars == fopen("CARS_TODAY",
fopen("CARS_TODAY", "r");
"r");
main() n_cars
n_cars == loadCars(f_cars,
loadCars(f_cars, cars
cars ++ 1);
1);
main() fclose(f_cars);
{{ a) fclose(f_cars);
FILE
FILE *f_cars;
*f_cars;
FILE f_drivers
f_drivers == fopen("DRIVERS_BEFORE",
fopen("DRIVERS_BEFORE", "r");
FILE *f_drivers;
*f_drivers; n_drivers
"r");
b) n_drivers == loadDrivers(f_drivers,drivers+1);
loadDrivers(f_drivers,drivers+1);
int fclose(f_drivers);
int this_number;
this_number; fclose(f_drivers);
char
char this_name[16];
this_name[16];
int for
for (i_arrival
(i_arrival == 1;
1; i_arrival
i_arrival <=
<= 6;)
int this_index;
this_index; {{
6;)
int
int these_points;
these_points; ...
...
int c) ++i_arrival;
int i_arrival;
i_arrival; }}
++i_arrival;
int
int has_arrived
has_arrived [MAX_DRIVERS
[MAX_DRIVERS ++ 1]
1] == {0};
{0}; d)
... bubblesortdrivers();
bubblesortdrivers();
...
}}
f_drivers
f_drivers == fopen("DRIVERS_AFTER",
fopen("DRIVERS_AFTER", "w");
"w");
dumpDrivers(f_drivers,
dumpDrivers(f_drivers, drivers+1,
drivers+1, n_drivers);
n_drivers);
a) Declaração dos ficheiros. fclose(f_drivers);
fclose(f_drivers);
b) Declaração de variáveis auxiliares. }}
c) Índice do ciclo principal.
d) Vector de booleanos para registar os
pilotos que já chegaram. É inicializado a 0
na declaração.
Programação Avançada com C 153 Programação Avançada com C 154

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O ciclo principal Exercícios (5)
for
for (i_arrival
(i_arrival == 1;1; i_arrival
i_arrival <=<= 6;)
6;) 1. Escrever um programa para afixar no
{{
printf("%dº. terminal a classificação do campeonato de
printf("%dº. lugar:
lugar: ",
", i_arrival);
i_arrival);
scanf("%d",
scanf("%d", &this_number);
&this_number); construtores. Os pontos de cada construtor
if
if (this_number
(this_number << 0)0) são a soma dos pontos dos pilotos da equipa
break;
else
break; respectiva. O programa processa o ficheiro de
else ifif ((this_index
((this_index == pilotos.
carsindexof(this_number))
carsindexof(this_number)) == == 0)
0)
printf("ERRO:
printf("ERRO: Não
Não participou.\n");
else
participou.\n"); 2. Escrever um programa para criar um
else ifif (has_arrived[this_number])
(has_arrived[this_number])
printf("ERRO:
printf("ERRO: Chegou
Chegou emem %d.º
%d.º lugar.\n",
lugar.\n", ficheiro com a lista dos concorrentes que
has_arrived[this_number]);
has_arrived[this_number]); participam na corrida. Cada linha contém o
else
else
{{ número do carro, o nome do piloto (inicial,
has_arrived[this_number]
has_arrived[this_number] == i_arrival;
i_arrival;
ponto, apelido), e o nome da equipa, e está
strcpy(this_name,
strcpy(this_name, cars[this_index].name);
cars[this_index].name); ordenado por ordem alfabética das equipas, e
these_points
these_points == points(i_arrival);
points(i_arrival); dentro de cada equipa, por ordem alfabética
printf("%s,
printf("%s, %d
%d ponto%s.\n",
ponto%s.\n",
this_name, dos nomes dos pilotos.
this_name,
these_points,
these_points,
these_points 3. Existe um ficheiro onde se registam os
these_points >> 11 ?? "s"
"s" :: "");
""); resultados de todos os grandes prémios que
drivers[driversindexof(this_name)].pts
drivers[driversindexof(this_name)].pts += +=
these_points;
these_points; se vão realizando. Cada linha desse ficheiro
++i_arrival;
++i_arrival; tem o número do carro e uma lista de números
}}
}} (com o mesmo comprimento em todas as
linhas) que representam o lugar em que ficou
o piloto (0 significa que desistiu e -1 que não
Nota: Dar um número de carro negativo manda parar a entrou). Escrever um programa para actualizar
introdução de dados. Usa-se quando chegarem ao fim este ficheiro no fim de uma corrida.
menos de 6 carros.

Programação Avançada com C 155 Programação Avançada com C 156

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações com bits Ver os bits (1)

Só com aritmética:
& conjunção bit a bit
| disjunção bit a bit #define BITS_PER_INT 16

^ disjunção exclusiva bit a bit typedef char Bitstr[BITS_PER_INT+1];


~ complementação de bits
char *bitsimag(Bitstr s, unsigned n)
<< deslocação para a esquerda {
int i;
>> deslocação para a direita
s[i = BITS_PER_INT] = '\0';
while (i––)
s[i] = n % 2 + '0', n /= 2;
return s;
}
Aplicam-se a operandos inteiros (int, long,
short, char) com ou sem sinal. Normal- unsigned bitsval(Bitstr s)
mente, usa-se tipos unsigned, para evitar {
complicações com o bit do sinal na deslo- unsigned n;
cação para a direita >>. for (n = 0; *s; ++s)
n = 2 * n + *s – '0';
return n;
}

Programação Avançada com C 157 Programação Avançada com C 158

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ver os bits (2) Idiomas com bits

Com os operadores de bits: O número cujos bits são todos 0:

#define BITS_PER_INT 16 0

typedef char Bitstr[BITS_PER_INT+1]; O número cujos bits são todos 1:


char *bitsimag(Bitstr s, unsigned n) ~0
{
int i;
s[i = BITS_PER_INT] = '\0'; O número cujos bits são todos 0, excepto o
while (i--) último:
s[i] = (n & 1) + '0', n >>= 1;
return s; 1
}
O número em que os últimos k bits são 1 e
unsigned bitsval(Bitstr s)
{ os restantes 0:
unsigned n; ~(~0 << k)
for (n = 0; *s; ++s)
n = n << 1 | *s - '0';
return n; O valor do i-ésimo bit do número n:
}
NB: os operadores aritméticos têm precedência sobre os (n >> i) & 1
operadores de bits.

Programação Avançada com C 159 Programação Avançada com C 160

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Posicionar bits um a um Adivinhas…
Que fazem as seguintes funções?
Colocar a 1 o i-ésimo bit de uma variável n:
n |= 1 << i unsigned
unsigned mystery1(unsigned
mystery1(unsigned n) n)
{{
return
return nn <<
<< 11 || !!(n
!!(n && ~(~0U>>1));
~(~0U>>1));
Colocar a 0 o i-ésimo bit de uma variável n: }}
n &= ~(1 << i) unsigned
unsigned mystery2(unsigned
mystery2(unsigned n) n)
{{
Trocar o i-ésimo bit de uma variável n: return
return nn >>
>> 11 ||
(n&1
(n&1 ?? ~(~0U>>1)
~(~0U>>1) :: 0);
0);
n ^= 1 << i }}

Colocar a 1 o bit mais significativo de uma unsigned


unsigned mystery3(unsigned
mystery3(unsigned n) n)
variável n: {{
return
return nn <<
<< 11 >>
>> 11 || nn && 1;
1;
n |= ~(~0U >> 1) }}

Colocar a 0 o bit mais significativo de uma unsigned


unsigned mystery4(unsigned
mystery4(unsigned n) n)
variável n: {{
return
return ~(~n
~(~n <<
<< 11 >>
>> 11 || ~n
~n && 1);
1);
n &= ~0U >> 1 }}

Nota: 0U (zero u) é o zero sem sinal (isto é, a constante 0


considerada de tipo unsigned int).
Programação Avançada com C 161 Programação Avançada com C 162

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Conjuntos em C Operações sobre conjuntos
Os conjuntos são um conceito elementar da Pertença:
Matemática, e são muito úteis em progra-
mação. Em C, o que faz falta são os conjuntos int Setisin(Setelem, Set);
de números naturais, entre 0 e uma constante
SET_MAX. Assim, em primeira aproximação, União, intersecção, diferença:
podemos definir um tipo Set assim:
void Setunion(Set, Set);
typedef int Set[SET_MAX + 1]; void Setinter(Set, Set);
void Setdiff(Set, Set);
Dada uma variável s de tipo Set:
Set s; Por convenção, o resultado vem no primeiro
argumento. Mas, tal como nas cadeias, con-
vém devolver também um apontador para o
s[i] valer 1 significará que i pertence ao resultado:
conjunto s; s[i] valer 0 significará que i não
pertence ao conjunto s. typedef int *SetPtr;
A expressão i designa um valor do tipo dos União, intersecção, diferença, devolvendo um
elementos do conjunto. Pode ser unsigned apontador para o resultado:
int, unsigned char, etc. Por exemplo:
SetPtr Setunion(Set, Set);
typedef unsigned char Setelem; SetPtr Setinter(Set, Set);
SetPtr Setdiff(Set, Set);
Programação Avançada com C 163 Programação Avançada com C 164

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Mais operações sobre conjuntos Mais operações sobre conjuntos (cont.)
Complementação:
SetPtr Setcompl(Set); Duplicação:
SetPtr Setcpy(Set, Set);
Inclusão:
int Setissubset(Set, Set); São iguais?
int Setisequal(Set, Set);
Cardinalidade:
int Setcard(Set); Juntar um elemento:
SetPtr Setadd(Set, Setelem);
É vazio?:
int Setisempty(Set); Retirar um elemento:
SetPtr Setrm(Set, Setelem);
Esvaziar:
SetPtr Setclr(Set);
Também seria útil uma função para juntar
vários elementos de uma vez, por exemplo,
todos os números de um certo intervalo. Mas
esta programar-se-á à base da função Setadd.

Programação Avançada com C 165 Programação Avançada com C 166

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Implementação dos conjuntos União, intersecção, diferença
SetPtr
SetPtr Setunion(Set
Setunion(Set s0, s0, Set
Set s1)
s1)
Para indicar a presença de um elemento num {{
conjunto basta um bit! Portanto, os conjuntos SetPtr
SetPtr rr == s0;
s0;
podem implementar-se por meio de vectores int
int i;
i;
de bits. Mas em C não há vectores de bits. Por for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i)
isso, usa-se, por exemplo, vectores de char, *s0++ |= *s1++;
*s0++ |= *s1++;
sabendo que cada char tem 8 bits. Em geral, return
return r;r;
}}
número de caracteres num char é dado pela
constante CHAR_BIT (ficheiro <limits.h>). SetPtr
SetPtr Setinter(Set
Setinter(Set s0, s0, Set
Set s1)
s1)
{{
#define SET_DIM SET_MAX/CHAR_BIT+1 SetPtr
SetPtr rr == s0;
s0;
int
int i;
i;
typedef unsigned char Set[SET_DIM]; for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i)
typedef unsigned char *SetPtr; *s0++ &= *s1++;
*s0++ &= *s1++;
typedef unsigned char Setelem; return
return r;r;
}}
SetPtr
SetPtr Setdiff(Set
Setdiff(Set s0, s0, Set
Set s1)
s1)
Pertença: {{
int Setisin(Setelem x, Set s) SetPtr
SetPtr rr == s0;
s0;
int
int i;
i;
{
for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i)
return s[x/CHAR_BIT] >> *s0++ &= ~*s1++;
x % CHAR_BIT & 1; *s0++ &= ~*s1++;
return
return r;r;
} }}
Programação Avançada com C 167 Programação Avançada com C 168

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Inclusão, cardinalidade Esvaziar, é vazio?, são iguais?
SetPtr
SetPtr Setclr(Set
Setclr(Set s) s)
int
int Setissubset(Set
Setissubset(Set s0, s0, Set
Set s1)
s1) {{
{{ SetPtr
SetPtr rr == s;
s;
int
int i;
i; int
int i;
i;
for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i) for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i)
if (*s0++ & ~*s1++)
if (*s0++ & ~*s1++) *s++ =
*s++ = 0;0;
return
return 0;0; return
return r;r;
return
return 1;1; }}
}}
int
int Setisempty(Set
Setisempty(Set s) s)
{{
int
int Setcard(Set
Setcard(Set s) s) int
int i;
i;
{{ for
for (i == 0;
(i 0; ii << SET_DIM;
SET_DIM; ++i)
++i)
int
int nn == 0;
0; if (*s++)
if (*s++)
Setelem
Setelem x; x; return
return 0;0;
int
int i;
i; return
return 1;1;
for
for (i == 0;
(i 0; ii << SET_DIM;
SET_DIM; ++i)
++i) }}
for
for (x = *s++; x;
(x = *s++; x; xx >>=
>>= 1)
1)
nn += int
int Setisequal(Set
Setisequal(Set s0, s0, Set
Set s1)
s1)
+= xx && 1;
1; {{
return
return n;n;
}} int
int i;
i;
for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i)
if (*s0++ != *s1++)
if (*s0++ != *s1++)
return
return 0;0;
return 1;
return 1;
}}
Programação Avançada com C 169 Programação Avançada com C 170

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Duplicar, juntar, retirar Exemplo: prémios no totoloto
#define
#define TOTOLOTO_MAX
TOTOLOTO_MAX 49
49
SetPtr
SetPtr Setcpy(Set
Setcpy(Set s0, s0, Set
Set s1)
s1) #define
{{ #define SET_MAX TOTOLOTO_MAX
SET_MAX TOTOLOTO_MAX
SetPtr
SetPtr rr == s0;
s0; #define
int #define SET_DIM
SET_DIM SET_MAX/CHAR_BIT+1
SET_MAX/CHAR_BIT+1
int i;
i;
for
for (i
(i == 0;
0; ii << SET_DIM;
SET_DIM; ++i)
++i) typedef
*s0++ = *s1++; typedef unsigned
unsigned char
char Set[SET_DIM];
Set[SET_DIM];
*s0++ = *s1++; typedef
typedef unsigned
unsigned char
char *SetPtr;
*SetPtr;
return
return r;r; typedef
}} typedef unsigned
unsigned char
char Setelem;
Setelem;
int
int hasprize(Set
hasprize(Set play,
play, Set
Set key)
key)
SetPtr {{
SetPtr Setadd(Set
Setadd(Set s, s, Setelem
Setelem x)
x) return
{{ return
s[x/CHAR_BIT] Setcard(
Setcard(
s[x/CHAR_BIT] |= |= Setinter(play,
11 <<
<< xx %% CHAR_BIT;
CHAR_BIT; Setinter(play, key))
key)) >=
>= 3;
3;
return }}
return s;s;
}}
Ou então:
int
int hasprize(Set
hasprize(Set play,
play, Set
Set key)
key)
SetPtr
SetPtr Setrm(Set
Setrm(Set s, s, Setelem
Setelem x)
x) {{
{{ Set
Set tmp;
tmp;
s[x/CHAR_BIT]
s[x/CHAR_BIT] &= &= return
return
~(1
~(1 <<
<< xx %% CHAR_BIT);
CHAR_BIT); Setcard(
Setcard(
return
return s;
s; Setinter(
Setinter(
}} Setcpy(tmp,
Setcpy(tmp, play),
play), key))
key)) >=
>= 3;
3;
}}

Programação Avançada com C 171 Programação Avançada com C 172

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Compilação separada O ficheiro header sets.h
As definições relativas aos conjuntos devem #include
#include <limits.h>
<limits.h>
ser agrupadas num ficheiro, compilável #define
separadamente. Os diversos ficheiros que #define SET_MAX
SET_MAX UCHAR_MAX
UCHAR_MAX
#define
#define SET_DIM SET_MAX/CHAR_BIT+1
SET_DIM SET_MAX/CHAR_BIT+1
compõem o programa são depois reunidos no
momento da linkagem. typedef
typedef unsigned
unsigned int
int Setelem;
Setelem;
typedef
typedef unsigned
unsigned char
char Set[SET_DIM];
Set[SET_DIM];
As outras unidades de compilação que usam typedef
typedef unsigned
unsigned char
char *SetPtr;
*SetPtr;
conjuntos têm que conhecer as definições dos
tipos e as declarações das funções. Por isso, int
int Setisin(Setelem,
Setisin(Setelem, Set);
Set);
cria-se um ficheiro header com essas SetPtr
SetPtr Setunion(Set, Set);
Setunion(Set, Set);
informações, que se inclui (com #include) SetPtr
SetPtr Setinter(Set,
Setinter(Set, Set);
Set);
SetPtr
SetPtr Setdiff(Set, Set);
Setdiff(Set, Set);
onde for preciso. SetPtr
SetPtr Setcompl(Set);
Setcompl(Set);
Assim, haverá dois ficheiros-fonte para os int
int Setissub(Set,
Setissub(Set, Set);
Set);
conjuntos: o ficheiro header sets.h, e o int
int Setcard(Set);
Setcard(Set);
SetPtr
SetPtr Setclr(Set);
Setclr(Set);
ficheiro com as definições das funções int Setisempty(Set);
sets.c. Uma das primeiras linhas do ficheiro int Setisempty(Set);
int
int Setisequal(Set,
Setisequal(Set, Set);
Set);
sets.c será: SetPtr
SetPtr Setcpy(Set,
Setcpy(Set, Set);
Set);
SetPtr
SetPtr Setadd(Set,
Setadd(Set, Setelem);
Setelem);
#include "sets.h" SetPtr
SetPtr Setrm(Set, Setelem);
Setrm(Set, Setelem);
O ficheiro <limits.h> contém as definições de constantes
que determinam certos limites dependentes da implemen-
tação.
Programação Avançada com C 173 Programação Avançada com C 174

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Headers não repetidos Exercícios (6)
Para evitar definir duas vezes a mesma coisa 1. Escrever um programa para escrutinar o
no mesmo programa, por inclusão dupla das Totoloto. O programa pede a chave e o núme-
mesmas definições (não se pode ter dois ro suplementar. As apostas estão registadas
typedefs para o mesmo tipo), usa-se o num ficheiro, uma por linha, precedidas por
esquema da compilação condicional. Por um número de identificação. O programa
exemplo, o ficheiro sets.h deveria começar criará cinco ficheiros, um para os primeiros
assim: prémios, outros para os segundos, etc. Cada
linha de cada um destes ficheiros conterá o
#include
#include <limits.h>
<limits.h> número de identificação e a aposta premiada.
No fim, o programa afixa no terminal o número
#ifndef
#ifndef _H_sets
_H_sets de premiados para cada prémio.
#define _H_sets
#define _H_sets 2. Escrever um programa para gerir o jogo da
#define roda da sorte. O programa começa por pedir a
#define SET_MAX
SET_MAX UCHAR_MAX
UCHAR_MAX frase secreta. Depois aceita sucessivos palpi-
#define
#define SET_DIM SET_MAX/CHAR_BIT+1
SET_DIM SET_MAX/CHAR_BIT+1 tes, indicando de cada vez quantas letras
typedef iguais à indicada existem na frase. Em cada
typedef unsigned
unsigned int
int Setelem;
Setelem; passo o programa mostra as letras já desco-
typedef
typedef unsigned char Set[SET_DIM];
unsigned char Set[SET_DIM];
typedef bertas da frase secreta. Para isso, afixa a
typedef unsigned
unsigned char
char *SetPtr;
*SetPtr; frase, substituindo cada letra ainda não des-
#endif coberta por um sublinhado. (Os espaços apa-
#endif recem como tal.) Enquanto houver consoan-
int Setisin(Setelem, tes por descobrir, não se aceita vogais. O
int Setisin(Setelem, Set);
Set); programa avisa quando acabarem as con-
SetPtr Setunion(Set, Set);
SetPtr Setunion(Set, Set);
... soantes. (Não se pode jogar duas vezes a
... mesma letra.)
Programação Avançada com C 175 Programação Avançada com C 176

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Funções utilitárias sobre conjuntos Juntar cadeia, juntar intervalo
Queremos três funções para juntar vários O ficheiro sets_util.c começa assim:
elementos a um conjunto, com uma só cha-
mada: uma para juntar todos os caracteres de #include
#include "sets_util.h"
"sets_util.h"
uma cadeia, uma para juntar todos os
elementos de um intervalo, e uma para juntar SetPtr
SetPtr Setaddstr(Set
Setaddstr(Set s,
s, char
char *w)
*w)
um número variável de elementos, passados {{
em argumento. while
while (*w)
(*w)
Estas funções não são primitivas: progra- Setadd(s,
Setadd(s, (Setelem)
(Setelem) *w++);
*w++);
return s;
return s;
mam-se por intermédio das funções primiti-
vas do ficheiro sets.h, sem aceder à repre- }}
sentação dos conjuntos. Vamos reuni-las no SetPtr
ficheiro sets_util.c. O ficheiro header SetPtr Setaddrng
Setaddrng
(Set
(Set s,
s, Setelem
Setelem x0,
x0, Setelem
Setelem x1)
x1)
respectivo, sets_util.h, é assim:
{{
while
while (x0
(x0 <=
<= x1)
x1)
#include
#include "sets.h"
"sets.h" Setadd(s, x0++);
Setadd(s, x0++);
return
return s;
s;
SetPtr
SetPtr Setaddstr(Set,
Setaddstr(Set, char
char *);
*); }}
SetPtr Setaddrng
SetPtr Setaddrng
(Set,
(Set, Setelem,
Setelem, Setelem);
Setelem); ...
SetPtr ...
SetPtr Setaddn(Set, int,
Setaddn(Set, int, ...);
...);
Os três pontos na função Setaddn significam que os parâ-
metros não são fixos. Neste caso, o segundo parâmetro
serve para indicar quantos argumentos suplementares
haverá.
Programação Avançada com C 177 Programação Avançada com C 178

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Listas de argumentos Utilização de listas de argumentos
de comprimento variável de comprimento variável (1)
Para programar a função Setaddn recorre-se
ao tipo va_list e às funções va_start, Um programa de teste para aceitar a chave e o
va_arg, e va_end, definidos no ficheiro número suplementar, no Totoloto:
<stdarg.h>. Com o tipo va_list declara-se
uma variável que vai apontar para os suces- #include
#include <stdio.h>
<stdio.h>
sivos argumentos anónimos; com a função
va_start faz-se essa variável apontar para o #include
#include "sets.h"
"sets.h"
primeiro desses argumentos (o segundo argu- #include
#include "sets_util.h"
"sets_util.h"
mento de va_start é o último argumento com
nome); com a função va_arg obtém-se o valor void
void scankey(Set,
scankey(Set, int
int *);
*);
de cada argumento; com a função va_end void printSet(Set);
void printSet(Set);
limpa-se a casa, depois de terminar: main()
main()
#include
#include "stdarg.h"
"stdarg.h" {{
...
... Set
Set key;
key;
int
int sup;
sup;
SetPtr
SetPtr Setaddn(Set
Setaddn(Set s,s, int
int n,
n, ...)
...) scankey(key,
scankey(key, &sup);
&sup);
{{ printf("Chave:
printf("Chave: ");
");
va_list
va_list p;p; printSet(key);
printSet(key);
va_start(p,
va_start(p, n); n); printf("\n");
printf("\n");
while
while (n––)
(n––) printf("Número
printf("Número suplementar:
suplementar: %d\n",
%d\n",
Setadd(s,
Setadd(s, (Setelem)va_arg(p,int));
(Setelem)va_arg(p,int)); sup);
sup);
va_end(p);
va_end(p); }}
return
return s;s; ...
...
}}
Programação Avançada com C 179 Programação Avançada com C 180

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Utilização de listas de argumentos Argumentos na linha de comando
de comprimento variável (2)
Quando se chama um programa C, pode-se
passar-lhe argumentos directamente, escre-
...
... vendo-os na linha de comando, a seguir ao
nome do programa. Para ir buscar esses
void
void scankey(Set
scankey(Set k, k, int
int *ns)
*ns) argumentos, programa-se a função main com
{{ dois parâmetros: o primeiro é o número de
int
int n1,
n1, n2,
n2, n3,
n3, n4,
n4, n5,
n5, n6;
n6; argumentos na linha de comando, contando
printf("A
printf("A chave, sff: ");
chave, sff: "); com o nome do programa; o segundo é um
scanf("%d%d%d%d%d%d",
scanf("%d%d%d%d%d%d", vector de apontadores para esses argu-
&n1,
&n1, &n2,
&n2, &n3,
&n3, mentos (que são cadeias de caracteres).
&n4,
&n4, &n5, &n6);
&n5, &n6);
Setaddn(Setclr(k),
Setaddn(Setclr(k), 6, 6, Exemplo: um programa para ecoar a linha de
n1,
n1, n2, n3, n4,
n2, n3, n4, n5,
n5, n6);
n6); comando:
printf("O número suplementar,"
printf("O número suplementar,"
"" sff:
sff: ");
"); #include
scanf("%d", #include <stdio.h>
<stdio.h>
scanf("%d", ns);
ns);
}} main(int
main(int argc,
argc, char
char **argv)
**argv)
{{
void
void printSet(Set
printSet(Set s) s) while
{{ while (argc––)
(argc––)
printf(argc
printf(argc ?? "%s
"%s "" :: "%s",
"%s",
int
int i;
i; *argv++);
for *argv++);
for (i
(i == 0;
0; ii <=
<= SET_MAX;
SET_MAX; ++i)
++i) printf("\n");
printf("\n");
if (Setisin(i,
if (Setisin(i, s)) s)) }}
printf("%d
printf("%d ", ", i);
i);
}}
Programação Avançada com C 181 Programação Avançada com C 182

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Tempos Bom dia, boa tarde, ou boa noite
O ficheiro <time.h> contém tipos e funções #include
#include <stdio.h>
<stdio.h>
para controlar o tempo: #include
#include <string.h>
<string.h>
#define #include
#include <time.h>
<time.h>
#define CLOCKS_PER_SEC
CLOCKS_PER_SEC 60
60
typedef
typedef unsigned
unsigned long
long clock_t;
clock_t; char
char *hello(int);
*hello(int);
typedef
typedef unsigned
unsigned long
long time_t;
time_t;
struct
main()
main()
struct tm
tm {{ {{
int
int tm_sec;
tm_sec;
int
int tm_min;
tm_min;
time_t
time_t now;
now;
int
int tm_hour;
tm_hour; time(&now);
time(&now);
int
int tm_mday;
tm_mday; printf("%s\n",
int tm_mon; printf("%s\n",
int
int
tm_mon;
tm_year; hello(localtime(&now)
hello(localtime(&now) ->
->
int tm_year; tm_hour));
int
int tm_wday;
tm_wday; tm_hour));
int
int tm_yday;
tm_yday; }}
int
int tm_isdst;
tm_isdst;
}; char
}; char *hello(int
*hello(int hour)
hour)
clock_t {{
clock_t clock(void);
clock(void); switch((hour
double
double difftime(time_t,
difftime(time_t, time_t);
time_t); switch((hour ++ 4)4) // 8){
8){
time_t
time_t mktime(struct
mktime(struct tm
tm *);
*); case 0: case 3:
case 0: case 3:
time_t
time_t time(time_t
time(time_t *); return
*); return "boa
"boa noite";
noite";
char
case 1:
case 1:
char *asctime(struct
*asctime(struct tmtm *);
*); return
char
char *ctime(time_t
*ctime(time_t *);
*); return "bom
"bom dia";
dia";
struct
struct tm
tm *gmtime(time_t
*gmtime(time_t *);
*);
case 2:
case 2:
struct
struct tm *localtime(time_t *);
tm *localtime(time_t *); return
return "bom
"bom tarde";
tarde";
size_t
size_t strftime
strftime }}
(char
(char *,
*, size_t,
size_t, char
char *,
*, struct
struct tm
tm *);
*); }}
Programação Avançada com C 183 Programação Avançada com C 184

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Escrever a data e a hora Exercícios (7)
size_t
size_t strftime
strftime
(char
1. Escrever uma função para verificar se um
(char *,
*, size_t,
size_t, char
char *,
*, struct
struct tm
tm *);
*); programa já caducou. A função compara a
1.º arg: resultado. data do dia com o prazo de validade que vem
2.º arg: capacidade do resultado. inscrito no programa (num #define). Utilize
3.º arg: cadeia de formato. essa função no prólogo de um programa, para
4.º arg: o argumento tempo (apontador).
autorizar o acesso. Permita que um utilizador
Especificadores mais usados: que conheça a palavra mágica se sirva do
%H hora (00-23) programa, mesmo depois do prazo ter expi-
%M minuto (00-56) rado. A palavra mágica escreve-se na linha de
%S segundo (00-61) (???) comando, a seguir ao nome do programa.
%W semana (00-53) (começa à segunda-feira)
%Y ano completo 2. Escreva uma função para construir uma
%y ano módulo 100 (00-99) cadeia com uma data em português por
%m mês (01-12) extenso, a partir de um apontador para struct
%d dia do mês (01-31) tm. Por exemplo:
%W semana (00-53) (começa à segunda-feira)
%w dia da semana (0-6) (domingo é 0) quinta–feira, 25 de abril de 1974
char
char *strdate(char *s,
*strdate(char *s, struct
struct tm
tm *t)
{{
*t) A função deve devolver um apontador para o
strftime(s,
strftime(s, 11,
11, "%Y/%m/%d",
"%Y/%m/%d", t);
t);
resultado.
return s;
}}
return s; 3. Idem para uma cadeia com a data em
formato militar. Por exemplo:
char
char *strtime(char
*strtime(char *s,
*s, struct
struct tm
tm *t)
*t) 25ABR1975
{{
strftime(s,
strftime(s, 9,
9, "%H:%M:%S",
"%H:%M:%S", t);
t); 4. Escreva um programa que aceite um
return
return s;
s; número na linha de comando e o escreva por
}}
extenso no terminal.
Programação Avançada com C 185 Programação Avançada com C 186

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Processamento de texto O ficheiro <stdio.h>
Problemas: ...
...
#define
#define EOF
EOF (-1)
(-1)
1. Contar as palavras de um texto. ...
...
int
int fgetc(FILE
fgetc(FILE *);
*);
2. Determinar o índice de variabilidade voca- char
char *fgets(char
*fgets(char *,
*, int,
int, FILE
FILE *);
*);
bular de um texto (isto é, a razão entre o int fputc(int, FILE *);
int fputc(int, FILE *);
número de palavras diferentes e o número de int
int fputs(char
fputs(char *,
*, FILE
FILE *);
*);
palavras). int getc(FILE *);
int getc(FILE *);
int
int getchar(void);
getchar(void);
3. Apresentar a tabela de frequências de char
char *gets(char
*gets(char *);
*);
palavras de um texto, ordenada alfabetica- int
int putc(int, FILE
putc(int, FILE *);
*);
mente ou por frequência. int putchar(int);
int putchar(int);
int
int puts(char
puts(char *);
*);
4. Criar o índice de um texto, isto é, uma tabela int
int ungetc(int, FILE
ungetc(int, FILE *);
*);
ordenada alfabeticamente onde cada palavra ...
...
de um texto aparece acompanhada pelo
Importante: as funções que devolvem carac-
número das linhas em que ocorre. teres devolvem-nos através do tipo int. Isto é
assim porque em caso de erro ou de fim de
ficheiro elas devolvem EOF, que vale -1.

NB: A função fgets inclui o newline na


cadeia; a função gets não. A função puts
acrescenta um newline ao escrever; a função
fputs não.
Programação Avançada com C 187 Programação Avançada com C 188

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


O ficheiro <ctype.h> Ler palavras
...
...
int Uma palavra é (por hipótese) uma sequência
int isalnum(int);
isalnum(int); “maximal” de letras, algarismos e sublinha-
int
int isalpha(int);
isalpha(int);
int
int iscntrl(int);
iscntrl(int); dos contíguos.
int
int isdigit(int);
isdigit(int);
int isgraph(int); Ao ler uma palavra pode-se chegar ao fim do
int isgraph(int); ficheiro. Convém avisar. Assim, por conven-
int
int islower(int);
islower(int);
int ção, a função devolverá o carácter que já não
int isprint(int);
isprint(int); pertence à palavra, ou EOF.
int
int ispunct(int);
ispunct(int);
int
int isspace(int);
isspace(int);
int
int isupper(int);
isupper(int);
int isxdigit(int); int
int ReadWord(FILE
ReadWord(FILE *f, *f, char
char *s)
*s)
int isxdigit(int); {{
int int
int c;
int tolower(int);
tolower(int); while
c;
int
int toupper(int);
toupper(int); while (c(c == getc(f),
getc(f),
... isalnum(c)
isalnum(c) |||| cc ==
== '_')
'_')
... *s++ = c;
*s++ = c;
Estas funções processam caracteres através *s
*s == '\0';
'\0';
do tipo int. Os argumentos devem ser return
return ungetc(c,
ungetc(c, f);
f);
caracteres unsigned char ou EOF. }}
No código ASCII de sete bits, os caracteres visíveis vão A função ungetc devolve o carácter que “regressa” ao
desde o espaço (32) até ao til (126). Os caracteres de ficheiro, ou EOF.
controlo vão desde NUL (0) a US (31) e ainda incluem o DEL
(127). Os caracteres gráficos são os visíveis excepto o A cadeia apontada pelo argumento s deve ter capacidade
espaço. Os espaços são o espaço, o formfeed, o newline, o para a palavra lida. Senão…
carriage return, o tab, e o tab vertical.
Programação Avançada com C 189 Programação Avançada com C 190

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Saltar os caracteres entre palavras Contar as palavras
Os caracteres entre duas palavras consecuti- No início de um ficheiro pode haver carac-
vas podem ser ignorados. Trata-se de carac- teres para ignorar. Depois desses, o ficheiro é
teres de pontuação (excepto o sublinhado), de uma alternância de palavras e sequências de
controlo, ou espaços. A função devolve o caracteres para ignorar:
carácter que já não ignora ou EOF: ...
...
char
char s[256];
s[256];
int FILE
FILE *f;
int SkipChars(FILE
SkipChars(FILE *f)*f) int
*f;
{{ int n;n;
int int
int c;
int c;
c; ...
c;
while
while (c
(c == getc(f),
getc(f), ...
isspace(c) nn == 0;
isspace(c) ||
|| while
0;
ispunct(c)
ispunct(c) &&
&& cc !=
!= '_'
'_' ||
|| while (c(c == SkipChars(f),
SkipChars(f), cc !=
!= EOF)
EOF)
iscntrl(c)) {{
iscntrl(c)) ReadWord(f,
;; ReadWord(f, s);
s);
return ++n;
return ungetc(c,
ungetc(c, f);
f); }}
++n;
}}
printf("Número
printf("Número de de palavras:
palavras: %d",
%d", n);
n);
...
...
Esta função não dá pelos fins de linha! Podia usar-se um ciclo for:
...
...
for
for (n
(n == 0;
0;
Variantes: contar os caracteres ignorados; contar os fins cc == SkipChars(f),
SkipChars(f), cc !=
!= EOF;
EOF;
de linha. ++n)
++n)
ReadWord(f,
ReadWord(f, s);s);
...
...
Programação Avançada com C 191 Programação Avançada com C 192

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Determinar o índice de variabilidade Apontadores para contadores
Para contar o número de palavras diferentes é Os contadores são estruturas. Ao passá-los
preciso memorizar as palavras que vão como argumentos a funções, vamos usar
aparecendo. Usa-se dois contadores: o das
palavras, como no problema anterior, e o das apontadores:
palavras diferentes, que só é incrementado
quando surge uma palavra nova. typedef
typedef Counter
Counter *Item;
*Item;
Ou então, conta-se o número de ocorrências
de cada palavra. No fim conta-se as palavras e Vai ser preciso ordenar o vector dos contado-
soma-se as ocorrências. Isto dá um pouco res, de duas maneiras. Para maior generali-
mais trabalho, mas serve já para o problema dade, usaremos um vector de apontadores
seguinte (fazer a tabela de frequências). para contadores:
Precisamos de um vector de contadores de
ocorrências de palavras. Cada um destes #define
#define MAX_TABLE
MAX_TABLE 256
256
contadores tem dois campos: a palavra e o
contador: Item
Item items[MAX_TABLE];
items[MAX_TABLE];
int
int n_items;
n_items;
typedef
typedef struct
struct {{
char
char *word;
*word;
int
int count;
count; Os contadores apontados pelos elementos
}} Counter;
Counter; deste vector serão criados dinamicamente.

A palavra é um apontador para uma cadeia


que será criada dinamicamente.

Programação Avançada com C 193 Programação Avançada com C 194

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


As funções malloc e calloc Criação de cadeias dinâmicas

void Uma função para criar uma cadeia nova, igual


void *malloc(size_t
*malloc(size_t size);
size); a uma cadeia s, devolvendo um apontador
para a cadeia criada:
A função malloc devolve um apontador para
um espaço onde cabe um objecto de tamanho char
char *strnew(char
*strnew(char *s)
*s)
size, ou NULL, se não for possível. {{
return
return strcpy(
strcpy(
(char*)malloc(strlen(s)+1),
(char*)malloc(strlen(s)+1), s);
s);
void }}
void *calloc(size_t
*calloc(size_t n,
n, size_t
size_t size);
size);

A função calloc devolve um apontador para Uma função para criar uma cadeia nova, a
um espaço inicializado a zero onde cabem n partir dos n primeiros caracteres de uma
objectos de tamanho size, ou NULL, se não cadeia s, devolvendo um apontador para a
for possível. cadeia criada:
char
char *strnewn(char
*strnewn(char *s,
*s, size_t
size_t n)n)
{{
OOtipo void **ééum
tipovoid umtipo
tipogenérico,
genérico,usado
usadonas
nas return
funções para argumentos e resultados return strncpy(
strncpy(
funções para argumentos e resultados (char*)calloc(n+1,
(char*)calloc(n+1, 1),
1), s,
s, n);
n);
apontadores.
apontadores.Qualquer
Qualquertipo
tipoapontador
apontadorpode
pode }}
ser
ser convertido para void *,eeinversamente,
convertido para void *, inversamente,
sem
semperda
perdade
deinformação.
informação. Não há problemas com o terminador '\0', porque o espaço
é inicializado a zero.

Programação Avançada com C 195 Programação Avançada com C 196

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações sobre contadores A tabela de contadores
Criar um contador novo, a partir de uma
cadeia, devolvendo o apontador. Para São as seguintes as operações que nos inte-
descobrir o tamanho de uma variável de tipo ressam a respeito da tabela dos contadores:
Counter, usa-se o operador sizeof: limpar; contabilizar uma palavra; calcular o
número de ocorrências; calcular o número de
palavras.
Counter
Counter *NewCounter(char
*NewCounter(char *s)*s)
{{ Para evitar que operações definidas noutros
Counter
Counter *x*x == ficheiros mexam directamente na tabela,
(Counter
(Counter *)malloc(
*)malloc( declaramo-la static na sua unidade de
sizeof(Counter));
sizeof(Counter)); compilação table.c:
xx -> word = strnew(s);
-> word = strnew(s);
xx ->
-> count
count == 0;
0; #include
return #include "table.h"
"table.h"
return x;x; #define
#define MAX_TABLE 256
MAX_TABLE 256
}}
static
static Item
Item items[MAX_TABLE];
items[MAX_TABLE];
Incrementar o contador: static
static int n_items;
int n_items;

void
void IncCounter(Counter
IncCounter(Counter *x)
*x) O ficheiro table.h é assim:
{{
++x
++x ->
-> count;
count; void
void ClearTable(void);
ClearTable(void);
}} void
void EnterWord(char *);
EnterWord(char *);
int
int NumberOfItems(void);
NumberOfItems(void);
Estas funções vêm definidas nos ficheiro counter.c, com int
header counter.h. int NumberOfEntries(void);
NumberOfEntries(void);

Programação Avançada com C 197 Programação Avançada com C 198

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


As operações na tabela (1) As operações na tabela (2)
int
int NumberOfItems(void)
NumberOfItems(void) Para contabilizar uma palavra usamos a téc-
{{ nica da sentinela: colocamos um contador
return
return n_items;
n_items; novo na primeira posição livre; procuramos a
}} palavra com a certeza de encontrar; incre-
mentamos o contador respectivo; incrementa-
Em vez de somar todas as ocorrências, é mais mos o contador de ocorrências e o das
prático manter um contador total: palavras, este só a palavra for nova:

static
static int
int n_entries;
n_entries; void
void EnterWord(char
EnterWord(char *s) *s)
{{
int
int i;
i;
int
int NumberOfEntries(void)
NumberOfEntries(void) for
for (i == 0,
(i 0, items[n_items]
items[n_items] ==
{{
NewCounter(s);
NewCounter(s);
return
return n_entries;
n_entries; strcmp(items[i]
strcmp(items[i] -> -> word,
word, s);
s);
}}
++i)
++i)
;;
Limpar a tabela é anular os dois contadores IncCounter(items[i]);
gerais: IncCounter(items[i]);
n_items
n_items +=
+= ii ==
== n_items;
n_items;
void ++n_entries;
++n_entries;
void ClearTable(void)
ClearTable(void) }}
{{
n_items
n_items == 0;
0;
n_entries
n_entries == 0;
0;
}} Ver as variantes a seguir.

Programação Avançada com C 199 Programação Avançada com C 200

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Libertar espaço Percorrer vectores com apontadores

Os contadores criados de novo que não são Normalmente, quando se percorre um vector
utilizados ficam desaproveitados. É melhor linearmente é melhor usar apontadores:
libertar a espaço que eles ocupam. Para isso,
existe a função free:
void
void EnterWord(char
EnterWord(char *s) *s)
{{
void
void free(void
free(void *p);
*p); Item
Item *p;
*p;
for
for (p
(p == items,
items, items[n_items]
items[n_items] ==
A função free desaloca o espaço apontado NewCounter(s);
NewCounter(s);
pelo seu argumento, espaço esse que deve ser strcmp((*p)
strcmp((*p) -> word,
-> word, s);
s);
sido alocado anteriormente pelas funções ++p)
++p)
malloc ou calloc. ;;
IncCounter(*p);
IncCounter(*p);
Para desalocar um contador, primeiro if
if (p
(p ==
== items
items ++ n_items)
n_items)
desaloca-se a cadeia, depois o contador: ++n_items;
++n_items;
else
else
FreeCounter(items[n_items]);
FreeCounter(items[n_items]);
void
void FreeCounter(Counter
FreeCounter(Counter *x)
*x) ++n_entries;
{{ ++n_entries;
}}
free(x
free(x ->
-> word);
word);
free(x);
free(x);
}}
Nota: (*p) -> word é o mesmo que
Exemplo de utilização a seguir. (**p).word.

Programação Avançada com C 201 Programação Avançada com C 202

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Contagens no texto Processar texto linha a linha
O programa para calcular o índice de variabili- É preciso cuidado com a última linha, que
dade deve também dizer quantos caracteres pode não terminar com newline:
tem o texto, e quantas linhas. ...
...
char
char s[256];
s[256];
Para isso, é preciso modificar a função FILE
FILE *f;
*f;
SkipChars, pois ela não repara nos fins de int
int nl,
nl, nc;
nc;
linha. Faz falta uma função que ignore carac- int n;
int n;
int
int c;
teres dentro de uma linha, mas que os conte: int
c;
int np,
np, npd;
npd;
...
...
nl
nl == 0;
0;
int
int SkipCharsInLine(FILE
SkipCharsInLine(FILE *f, *f, int
int *n)
*n) nc
nc == 0;
0;
{{ ClearTable();
ClearTable();
int while
while (ungetc(getc(f),
(ungetc(getc(f), f) f) !=
!= EOF)
int c;
c; {{
EOF)
int
int kk == 0;
0; ++nl;
while ++nl;
while ((c
((c == getc(f))
getc(f)) !=
!= '\n'
'\n' &&
&& while
while (c
(c == SkipCharsInLine(f,
SkipCharsInLine(f, &n), &n),
(isspace(c) ||
(isspace(c) || nc
nc += n,
+= n,
ispunct(c)
ispunct(c) &&
&& cc !=
!= '_'
'_' ||
|| cc !=
!= EOF
EOF &&
&& cc !=
!= '\n')
'\n')
iscntrl(c))) {{
iscntrl(c))) ReadWord(f,
++k; ReadWord(f, s); s);
++k; nc
nc +=
+= strlen(s);
strlen(s);
*n
*n == k;
k; EnterWord(s);
EnterWord(s);
return
return ungetc(c,
ungetc(c, f);
f); }}
}} getc(f);
getc(f);
}}
npd
npd == NumberOfItems();
NumberOfItems();
np
np == NumberOfEntries();
NumberOfEntries();
printf(...);
printf(...);
...
...
Programação Avançada com C 203 Programação Avançada com C 204

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A tabela das frequências As funções de ordem
Para imprimir a tabela das frequências, basta
Ordem alfabética crescente:
ordenar a tabela dos contadores de acordo
com o critério (alfabético crescente ou fre-
int
int itemltword(Item
itemltword(Item x,
x, Item
Item y)
y)
quência decrescente), e escrever os contado- {{
res, um a um. A função de ordenação tem que return
ser parametrizada com a função de ordem: return
strcmp(x->word,
strcmp(x->word, y->word)
y->word) <=<= 0;
0;
static }}
static void
void BubblesortTable
BubblesortTable
(int(*f)(Item,
(int(*f)(Item, Item))
Item))
{{
Item Ordem decrescente das frequências, e depois
Item m;m;
Item
Item *i;
*i; ordem alfabética crescente:
Item
Item *j;
*j;
for
for (i(i == items+1;
items+1; int
int itemgtcount(Item
itemgtcount(Item x,
x, Item
Item y)
y)
ii <=
<= items
items ++ n_items
n_items -- 1;
1; {{
++i)
++i) return
return
for
for (j
(j == items
items ++ n_items
n_items -- 1;
1; x->count
x->count >> y->count
y->count ||
||
jj >= i;
>= i; x->count
x->count == y->count &&
== y->count &&
--j)
--j) strcmp(x->word,
strcmp(x->word, y->word)
y->word) <=<= 0;
0;
if
if (!f(*(j-1), *j))
(!f(*(j-1), *j)) }}
{{
mm == *(j-1);
*(j-1);
*(j-1)
*(j-1) == *j;
*j; Estas funções vêm definidas nos ficheiro item.c, com
*j
*j == m;
m; header item.h.
}}
}}
Programação Avançada com C 205 Programação Avançada com C 206

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Afixar a tabela Operações genéricas para
Uma função com formato paramétrico para
vectores de apontadores (1)
escrever um Item (item_io.c): Bubblesort:
void void
void BubblesortPointers
BubblesortPointers
void printfItem(char
printfItem(char *fmt,
*fmt, Item
Item x)
x) (void
{{ (void **v,
**v,
printf(fmt, size_t
size_t n,n,
printf(fmt, x->word,
x->word, x->count);
x->count); int(*f)(void
}} int(*f)(void *, *, void
void *))
*))
{{
void
void *m;
*m;
A função que afixa a tabela (table.c): void
void **i;
**i;
void
void PrintTable(void)
PrintTable(void) void **j;
void **j;
{{ for
for (i
(i == v+1;
v+1;
int
int ii == n_items;
n_items; ii <=
<= vv ++ nn –– 1;
1;
Item
Item *p = items;
*p = items; ++i)
++i)
while
while (i––)
(i––) for
for (j
(j == vv ++ nn –– 1;
1;
printfItem("%s
printfItem("%s %d\n",
%d\n", *p++);
*p++); jj >=
>= i;
i;
}} ––j)
––j)
if
if (!f(*(j–1), *j))
(!f(*(j–1), *j))
A função que afixa a tabela ordenada: {{
mm == *(j–1);
*(j–1);
void
void PrintTableSorted
PrintTableSorted *(j–1)
(int(*f)(Item, *(j–1) == *j;*j;
(int(*f)(Item, Item))
Item)) *j
*j == m;
m;
{{ }}
BubblesortTable(f);
BubblesortTable(f); }}
PrintTable();
PrintTable();
}} Esta função e as seguintes devem ser reunidas num ficheiro
parray.c, com header parray.h.
Programação Avançada com C 207 Programação Avançada com C 208

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações genéricas para Operações genéricas para
vectores de apontadores (2) vectores de apontadores (3)
Quicksort (só para especialistas…):
Limpar:
void
void QuicksortPointers
QuicksortPointers
(void
(void **v,
**v, void
void ClearPointers
ClearPointers
size_t
size_t n, n, (void
(void **v,
**v, size_t
size_t n)
n)
int(*f)(void
int(*f)(void *, *, void
void *))
*)) {{
{{
void while
while (n––)
(n––)
void **i
**i == v;
v; *v++
void
void **j
**j == vv ++ nn –– 1;
1; *v++ == NULL;
NULL;
void
void *a*a == *(i
*(i ++ (j
(j –– i)i) // 2);
2); }}
void
void *m;
*m;
do
do
{{ Construir um vector de apontadores v para os
while
while (!f(a,
(!f(a, *i))
*i)) n primeiros elementos de um vector p; o
++i;
++i;
while tamanho de cada elemento é s:
while (!f(*j,
(!f(*j, a))a))
––j;
––j;
if
if (i
(i <=
<= j)
j)
{{ void
void BuildPointers
BuildPointers
mm == *i;
*i;
(void
(void **v,
**v,
*i++
*i++ == *j;
*j; void
void *p, size_t
*p, size_t n,
n,
*j––
*j–– == m;m; size_t s)
}} size_t s)
}}
{{
while char
char *i;
*i;
while (i (i <=
<= j);
j);
if (v <
if (v < j)j) for
for (i=(char
(i=(char *)p;
*)p; n;
n; ––n,
––n, i+=s)
i+=s)
QuicksortPointers(v,
QuicksortPointers(v, jj –– vv ++ 1, 1, f);
f); *v++ = (void *)i;
*v++ = (void *)i;
if (i < v + n
if (i < v + n - 1)- 1) }}
QuicksortPointers(i,
QuicksortPointers(i, vv ++ nn -- i, i, f);
f);
}}
Programação Avançada com C 209 Programação Avançada com C 210

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações genéricas para Utilização das operações genéricas
vectores de apontadores (4)
Afixar a tabela:
O primeiro apontador que satisfaz uma função
static
static void
void displayItem(Item
displayItem(Item x)
paramétrica: {{
x)
void
void *FirstPointer
*FirstPointer printfItem("%s
printfItem("%s %d\n",
%d\n", x);
x);
(void }}
(void **v,
**v,
size_t
size_t n,n, void
int(*f)(void void PrintTable(void)
PrintTable(void)
int(*f)(void *))*)) {{
{{ IteratePointers(
IteratePointers(
for (void
(void **)items,
for (;
(; n--;
n--; ++v)
++v) n_items,
**)items,
if (f(*v))
if (f(*v)) n_items,
displayItem);
return displayItem);
return *v;
*v; }}
return
return NULL;
NULL;
}} Ordenar a tabela:
static
static void
void QuicksortTable(int(*f)(Item,
QuicksortTable(int(*f)(Item,
Um iterador (uma função que aplica uma função Item))
Item))
{{
paramétrica a cada elemento de um vector): SortPointers((void**)items,
SortPointers((void**)items, n_items,
n_items, f);
f);
}}
void
void IteratePointers
IteratePointers
(void
(void **v,
**v, void
void PrintTableSorted(int(*f)(Item,
PrintTableSorted(int(*f)(Item, Item))
Item))
size_t {{
size_t n,n, QuicksortTable(f);
QuicksortTable(f);
void(*f)(void
void(*f)(void *))
*)) PrintTable();
PrintTable();
{{ }}
while
while (n--)
(n--)
f(*v++); Importante: o operador de conversão (void **) é indispen-
f(*v++); sável: só as conversões para void * é que são implícitas.
}}
Programação Avançada com C 211 Programação Avançada com C 212

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Exercícios (8) Processar texto linha a linha
com as funções fgets e strtok (1)
1. Aperfeiçoar os programas de contagem para
que eles possam processar devidamente A função fgets lê de uma linha de um ficheiro
programas em C, ignorando as linhas para o f quanto muito n–1 caracteres colocando-os
pré-processador (começam por #), os comen- na cadeia s, parando se encontrar um newline,
tários (entre /* e */), as cadeias de carac- o qual também vai para a cadeia:
teres constantes (entre " e "), e os caracteres
constantes (entre ' e '). char
char *fgets(char
*fgets(char *s,int
*s,int n,FILE
n,FILE *f);
*f);
2. Idem, em relação a textos escritos em
português: não distinguir entre maiúsculas e Às vezes, é preciso retirar o newline (se estiver
minúsculas, ignorar acentos e cedilhas, não lá):
distinguir entre singular e plural, e entre os
tempos de um mesmo verbo (?). char
char *strnlrm(char
*strnlrm(char *s)
*s)
{{
3. Escrever um programa para processar um char
char *p
*p == s;
s;
texto em português, garantindo que entre cada if
if ((s == strchr(s,
((s strchr(s, '\n'))
'\n')) !=
!= NULL)
NULL)
duas palavras consecutivas fica apenas um *s
*s == '\0';
'\0';
return
return p;
espaço. }}
p;

4. Escrever um programa para processar um


texto em português, afixando no terminal Esquema para processar linha a linha:
todas as palavras que ocorram mais que duas
vezes no mesmo parágrafo. Os parágrafos while
while (fgets(line,
(fgets(line, 256,
256, f)
f) !=
!= NULL)
NULL)
estão separados uns dos outros por linhas em {{
strnlrm(line);
branco. Há uma lista de excepções, com as strnlrm(line);
...
...
palavras que não interessa controlar. }}

Programação Avançada com C 213 Programação Avançada com C 214

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Processar texto linha a linha Processar texto linha a linha
com as funções fgets e strtok (2) com as funções fgets e strtok (3)

A função strtok serve para percorrer uma Assim:


cadeia s “extraindo” pedaços (tokens) #define
#define DELIMITERS
DELIMITERS \\
delimitados pelos caracteres da cadeia r: "" \t,.;:()?![]{}<>#%^|+–*/=~&'\"\\"
\t,.;:()?![]{}<>#%^|+–*/=~&'\"\\"
...
...
char
char *strtok(char
*strtok(char *s,*s, char
char *r);
*r); char
char s[256];
s[256];
FILE
FILE *f;
*f;
Para obter todos os tokens de uma cadeia, int
int nl,
nl, nc;
nc;
chama-se a função strtok sucessivamente, int
int np,
np, npd;
npd;
...
até ela dar NULL. Da primeira vez o primeiro ...
nl
nl == 0;
0;
argumento é a cadeia a processar; das seguin- nc
nc == 0;
0;
tes é NULL. O segundo argumento contém os ClearTable();
ClearTable();
while
while (fgets(line,
(fgets(line, 256,256, f)
f) !=
!= NULL)
delimitadores, e pode variar de uma chamada {{
NULL)
para a outra. strnlrm(line);
strnlrm(line);
++nl;
Esquema para obter tokens: ++nl;
nc
nc +=
+= strlen(line);
strlen(line);
#define pp == strtok(line,
strtok(line, DELIMITERS);
#define DELIMITERS
DELIMITERS "" ,.;:?!()"
,.;:?!()" while
DELIMITERS);
...
... while (p (p !=
!= NULL)
NULL)
pp == strtok(line, {{
strtok(line, DELIMITERS);
DELIMITERS); EnterWord(p);
while
while (p (p !=
!= NULL)
NULL) EnterWord(p);
{{ pp == strtok(NULL,
strtok(NULL, DELIMITERS);
DELIMITERS);
printf("%s\n", }}
printf("%s\n", p); p); /*/* por
por exemplo
exemplo */
*/ }}
pp == strtok(NULL, DELIMITERS);
strtok(NULL, DELIMITERS);
}} npd
npd == NumberOfItems();
NumberOfItems();
np
np == NumberOfEntries();
NumberOfEntries();
Importante: a cadeia tokenizada fica modificada, pois a printf(...);
printf(...);
função inscreve '\0' para marcar o fim de cada token. ...
...
Programação Avançada com C 215 Programação Avançada com C 216

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Estruturas auto-referenciadas, listas Estruturas auto-referenciadas, árvores
Árvores:
Listas: typedef
typedef struct
struct lnode
lnode *Tree;
*Tree;
typedef
typedef struct lnode {{
struct lnode
Item
Item value;
value;
typedef
typedef struct
struct lnode
lnode *List;
*List; Tree sub[2];
Tree sub[2];
typedef struct lnode
typedef struct lnode { { }} treenode;
treenode;
Item
Item value;
value;
List
List next;
next; typedef
typedef enum
enum {left,
{left, right}
right} Child;
Child;
}} listnode;
listnode;
4

3 8 4
3 12

2 9 18

Programação Avançada com C 217 Programação Avançada com C 218

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações primitivas com listas Fazer uma lista nova, listnew

List a) Aloca-se o espaço para um nó com a função


List listcpy(List
listcpy(List *,
*, List);
List);
List listnew(Item); b) malloc; coloca-se o item no membro value e
List listnew(Item);
List c)
List listclr(List
listclr(List *);
*); NULL no membro next; devolve-se o apon-
List d)
List listcons(Item, List
listcons(Item, List *);
*); tador para o espaço alocado:
List e)
List listswtl(List s, List
listswtl(List s, List *t);
*t);
int f)
int listlen(List);
listlen(List);
List g) List
List listnew(Item
listnew(Item x)
List liststhd(List,
liststhd(List, Item);
Item); x)
int listnull(List); h) {{
int listnull(List);
Item i) List
List pp ==
Item listhead(List);
listhead(List);
List listtail(List); j) (List)
(List) malloc(sizeof(listnode));
malloc(sizeof(listnode));
List listtail(List);
pp ->
-> value
value == x;
x;
pp -> next = NULL;
-> next = NULL;
a) duplicar a segunda lista para a primeira; return
return p;p;
b) fazer uma lista nova com o item; }}
c) limpar a lista;
d) colocar o item à cabeça da lista; Antes:
e) trocar a cauda da primeira lista com a x
segunda; 8
f) o comprimento da lista;
g) substituir a cabeça da lista pelo item; Depois:
h) a lista é vazia? x
i) o item que está à cabeça da lista; 8 8
j) a cauda da lista.
Programação Avançada com C 219 Programação Avançada com C 220

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Duplicar uma lista, listcpy Listas vazias, listclr, listnull
Se a lista fonte é vazia, a lista destino é vazia;
se não, a cabeça da lista destino é um dupli- Limpar uma lista é fazê-la ser o apontador
cado da cabeça da lista fonte; percorre-se o NULL:
resto da lista fonte, pendurando sucessiva-
mente no fim da lista destino duplicados dos
elementos por onde vai passando: List
List listclr(List
listclr(List *s)*s)
{{
List
List listcpy(List
listcpy(List *s, *s, List
List t)t) return
{{ return *s
*s == NULL;
NULL;
}}
List
List p;p;
if
if (t
(t ==== NULL)
NULL)
*s = NULL;
*s = NULL;
else
else
{{ Uma lista está vazia quando o seu valor for o
*s
*s == listnew(t->value);
listnew(t->value); apontador NULL:
pp == *s;
*s;
tt == t->next;
t->next;
while
while (t(t !=
!= NULL)
NULL) int
int listnull(List
listnull(List s) s)
{{ {{
p->next
p->next == listnew(t->value);
listnew(t->value); return
return ss ==
== NULL;
NULL;
pp == p->next;
p->next; }}
tt == t->next;
t->next;
}}
}}
return
return *s;*s;
}}
Programação Avançada com C 221 Programação Avançada com C 222

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Juntar à cabeça, listcons Trocar a cauda, listswtl
Mudar a cabeça, liststhd Troca-se o membro next da primeira lista
com a segunda lista:
Para juntar um novo item à cabeça de uma List
lista, cria-se um novo nó, pondo o item no List listswtl(List
listswtl(List s,s, List
List *t)
*t)
{{
membro value, e a lista no membro next; List
devolve-se o apontador para o nó recém- - List p;p;
pp == ss ->
-> next;
next;
criado: ss ->
-> next == *t;
next *t;
*t
*t == p;
p;
List
List listcons(Item
listcons(Item x,x, List
List *s)
*s) return
{{ return s; s;
}}
List
List pp == listnew(x);
listnew(x);
pp ->
-> next
next == *s;
*s; Só se a primeira lista não for vazia!
return
return *s*s == p;
p;
}} Antes:
s 1 2
Muda-se o valor da cabeça, mudando o valor t
do membro value do nó apontado: 3 4 5

List
List liststhd(List
liststhd(List s,s, Item
Item x)
x)
{{ Depois:
ss ->
-> value
value == x;
x;
return
return s;s; s 1 2
}}
t
Só para listas não vazias! 3 4 5
Programação Avançada com C 223 Programação Avançada com C 224

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


A cabeça, listhead O comprimento, listlen
A cauda, listtail
O comprimento é o número de nós na lista.
O valor da cabeça é o valor do membro value Para o calcular, percorre-se a lista até ao fim,
do nó apontado pela lista: contando os nós:

Item
Item listhead(List
listhead(List s) s) int
int listlen(List
listlen(List s) s)
{{ {{
return
return ss ->
-> value;
value; int
int n;
n;
}} for
for (n
(n == 0;
0; ss !=
!= NULL;
NULL; ++n)
++n)
ss == ss -> next;
-> next;
Só para listas não vazias! return
return n;n;
}}

A cauda de uma lista é o membro next do nó


apontado pela lista: Ou então…:
int
int listlen(List
listlen(List s) s)
List
List listtail(List
listtail(List s) s) {{
{{ return
return
return ss ==
== NULL
NULL ?? 00 ::
return ss ->
-> next;
next; 11 ++ listlen(s
}} listlen(s ->
-> next);
next);
}}

Só para listas não vazias!

Programação Avançada com C 225 Programação Avançada com C 226

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Concatenação de listas Exercícios (9)
Concatenar duas listas não é uma operação 1. Escrever funções utilitárias sobre listas
primitiva. Programa-se melhor sem mexer nos
para:
apontadores, recorrendo apenas às funções
anteriores. a) devolver um apontador para o i-ésimo
Para concatenar duas listas troca-se a cauda elemento da lista;
do pé da primeira lista com a segunda lista. O b) partir uma lista em duas, antes do i-ésimo
pé de uma lista (não vazia) é a lista unitária elemento (i >= 2);
que está no fim: c) inserir um elemento novo, a seguir ao i-
ésimo elemento;
List
List listfoot(List
listfoot(List s)s)
{{ d) inserir uma lista noutra, a seguir ao i-ésimo
while elemento;
while (!listnull(listtail(s)))
(!listnull(listtail(s))) e) devolver o apontador para o primeiro
ss == listtail(s);
listtail(s);
return elemento com um certo valor, ou NULL, se
return s;s; não houver;
}}
f) devolver o apontador para o primeiro
Concatenação (append): elemento maior ou igual a um certo valor,
List ou NULL, se não houver;
List listcat(List
listcat(List *s,
*s, List
List t)
t)
{{ g) devolver o apontador para o elemento
if
if (listnull(*s))
(listnull(*s)) anterior ao primeiro elemento maior ou
*s
*s == t;
t; igual a um certo valor, ou para o pé da lista
else
else se não houver, tudo na hipótese de a
listswtl(listfoot(*s),
listswtl(listfoot(*s), &t);&t); cabeça da lista ser menor que esse valor;
return
return *s;
*s; h) inserir um elemento numa lista ordenada.
}} i) inverter uma lista.
Programação Avançada com C 227 Programação Avançada com C 228

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Operações primitivas com árvores Fazer uma árvore nova, treenew
Tree a) Aloca-se o espaço para um nó com a função
Tree treenew(Item);
treenew(Item); b)
Tree
Tree treeclr(Tree
treeclr(Tree *);
*); malloc; coloca-se o item no membro value e
Tree c) NULL nos dois membros sub; devolve-se o
Tree treecpy(Tree *, Tree);
treecpy(Tree *, Tree); d)
Tree
Tree treecons(Item,
treecons(Item, Tree
Tree *,
*, Child);
Child); apontador para o espaço alocado:
Item treeroot(Tree); e)
Item treeroot(Tree); f)
Tree
Tree treestrt(Tree,
treestrt(Tree, Item);
Item); Tree
Tree treenew(Item
treenew(Item x) x)
Tree g) {{
Tree treechld(Tree, Child);
treechld(Tree, Child); h)
int
int treenull(Tree);
treenull(Tree); Tree
Tree pp == (Tree)
(Tree)
Tree i) malloc(sizeof(treenode));
Tree treeins(Item,
treeins(Item, Tree
Tree *);
*); malloc(sizeof(treenode));
pp ->
-> value
value == x;
x;
void j) pp ->
-> sub[left] ==
sub[left]
void treepre
treepre (Tree,
(Tree, void(*)(Item));
void(*)(Item)); k)
void
void treein
treein (Tree,
(Tree, void(*)(Item));
void(*)(Item)); l) pp ->
-> sub[right]
sub[right] == NULL;
NULL;
void
void treepost(Tree, void(*)(Item));
treepost(Tree, void(*)(Item)); return
return p;p;
}}
a) fazer uma árvore nova com o item;
b) limpar a árvore; Antes:
c) duplicar a segunda árvore para a primeira; x
d) fazer um novo nó para a raiz, colocando lá o
item, e pendurando a árvore no lado indicado; 8
e) o item que está na raiz da árvore;
f) substituir a raiz da árvore pelo item; Depois:
g) a subárvore do lado indicado;
h) a árvore é vazia? x
i) inserir um elemento na árvore; 8 8
j) percorrer a árvore prefixamente;
k) percorrer a árvore infixamente;
l) percorrer a árvore posfixamente. *(p->sub+1)
*(p->sub+1) == *p->sub
*p->sub == NULL;
NULL; ?
Programação Avançada com C 229 Programação Avançada com C 230

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Duplicar uma árvore, treecpy Árvores vazias, treeclr, treenull
Se a árvore é vazia, o resultado é uma árvore
vazia; se não, cria-se um nó novo com o valor Limpar uma árvore é fazê-la ser o apontador
da raiz e depois duplica-se cada uma das NULL:
subárvores para as subárvores do nó recém-
criado:
Tree
Tree treeclr(Tree
treeclr(Tree *s)*s)
Tree {{
Tree treecpy(Tree
treecpy(Tree *s,*s, Tree
Tree t)
t) return
{{ return *s
*s == NULL;
NULL;
if }}
if (t
(t ==
== NULL)
NULL)
*s
*s = NULL;
= NULL;
else
else
{{
*s
*s == treenew(t
treenew(t ->-> value);
value);
treecpy(&(*s) Uma árvore está vazia quando o seu valor for
treecpy(&(*s) -> sub[left],
-> sub[left], o apontador NULL:
tt ->
-> sub[left]);
sub[left]);
treecpy(&(*s)
treecpy(&(*s) -> -> sub[right],
sub[right],
tt -> sub[right]);
-> sub[right]); int
}} int treenull(Tree
treenull(Tree s) s)
{{
return
return *s;
*s; return
}} return ss ==
== NULL;
NULL;
}}

Programação Avançada com C 231 Programação Avançada com C 232

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Crescer pela raiz, treecons A raiz, treeroot
Os ramos, treechld
Para crescer pela raiz, cria-se um novo nó, Mudar a raiz, treestrt
pondo o item no membro value, e pendura- - O valor da raiz é o valor do membro value do
se a árvore no lado indicado; devolve-se o nó apontado pela árvore:
apontador para o nó recém-criado:
Item
Item treeroot(Tree
treeroot(Tree s) s)
Tree
Tree treecons(Item
treecons(Item x,Tree
x,Tree *s,Child
*s,Child c)
c) {{
{{ return
return ss ->-> value;
value;
Tree
Tree tt == treenew(x);
treenew(x); }}
tt ->
-> sub[c]
sub[c] == *s;
*s;
return *s =
return *s = t;t; O ramo da esquerda ou da direita é membro
}} sub[left] ou sub[right] do nó apontado
pela árvore:
treecons(9, s, left); Tree
Tree treechld(Tree
treechld(Tree s, s, Child
Child c)
c)
Antes: Depois: {{
return
return ss ->
-> sub[c];
sub[c];
s s }}
6 9
Muda-se o valor da raiz, mudando o valor do
3 8 6 membro value do nó apontado pela árvore:
Tree
Tree treestrt(Tree
treestrt(Tree s, s, Item
Item x)
x)
{{ Tudo só
7 3 8 para
ss ->
-> value
value == x;x; árvores
return
return s;s; não
}} vazias!
7
Programação Avançada com C 233 Programação Avançada com C 234

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Árvores ordenadas Inserir um item numa árvore
Uma árvore ordenada é uma árvore em que a Se a árvore for vazia, constrói-se uma nova
raiz de cada subárvore é maior que todas as árvore só com o item, e devolve-se; se não, se
raízes de subárvores à esquerda e menor que o item for igual à raiz, não se faz nada (não há
todas as raízes de subárvores à direita. Por repetições), mas devolve-se a árvore; se não,
exemplo: insere-se na subárvore esquerda ou na direita
conforme o item for menor ou for maior que a
raiz.
6 Tem que haver uma função de ordem para o
tipo Item, e uma função de igualdade:

3 8 int
int itemlt(Item,
itemlt(Item, Item);
Item); /*
/* <=
<= */
*/
int
int itemeq(Item, Item); /* == */
itemeq(Item, Item); /* == */
1 7 10 Por exemplo, com os itens usados no
problema do processamento de texto, seria:
int
int itemlt(Item
itemlt(Item x,x, Item
Item y)
14 {{
y)
return
return
strcmp(x
strcmp(x ->
-> word,
word, yy ->
-> word)
word) <=
<=
Ao inserir um item numa árvore ordenada, é 0;
0;
preciso garantir que a árvore continua orde- }}
nada. Por exemplo, o item 5 seria pendurado int
int itemeq(Item
itemeq(Item x,x, Item
Item y)
y)
à direita do 3; o item 9 à esquerda do 10; o {{
return
item 12 à esquerda do 14. return
strcmp(x
strcmp(x ->
-> word,
word, yy ->
-> word)
word) ==
==
0;
0;
}}
Programação Avançada com C 235 Programação Avançada com C 236

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Inserir um item numa árvore, treeins Percorrer as árvores
Percurso prefixo:
Tree
Tree treeins(Item
treeins(Item x, x, Tree
Tree *s)
*s) void treepre(Tree
{{ void treepre(Tree s,
void(*p)(Item)) s,
{{ void(*p)(Item))
if
if (*s
(*s ==
== NULL)
NULL) if
return if{(s(s !=
!= NULL)
NULL)
return treecons(x,
treecons(x, s, s, left);
left); { p(s -> value);
else
else if (itemeq(x, (*s) ->
if (itemeq(x, (*s) -> value))
value)) p(s -> value);
treepre(s -> sub[left], p);
return *s; treepre(s ->
treepre(s -> sub[right],
sub[left], p);
p);
return *s; }} treepre(s -> sub[right], p);
else
else if
if (itemlt(x,(*s)->value))
(itemlt(x,(*s)->value)) }}
return
return
treeins(x,
treeins(x, &(*s)->sub[left]);
&(*s)->sub[left]); Percurso infixo:
else
else void treein(Tree
{void treein(Tree s, s, void(*p)(Item))
void(*p)(Item))
return
return { if (s != NULL)
treeins(x,
treeins(x, &(*s)->sub[right]);
&(*s)->sub[right]); if{ (s != NULL)
}} { treein(s -> sub[left], p);
treein(s
p(s -> -> sub[left], p);
value);
p(s -> value);
treein(s -> sub[right], p);
Ou então: }} treein(s -> sub[right], p);
}}
Tree
Tree treeins(Item
treeins(Item x,x, Tree
Tree *s)
*s)
{{ Percurso pós-fixo:
if
if (*s
(*s ==
== NULL)
NULL)
return void treepost(Tree
treepost(Tree s,
return treecons(x,
treecons(x, s,s, left);
left); voidvoid(*p)(Item)) s,
else
else if
if (itemeq(x,
(itemeq(x, (*s)
(*s) ->
-> value))
value)) void(*p)(Item))
{{
return
return *s; if
else
*s; if{(s
(s !=
!= NULL)
NULL)
else { treepost(s -> sub[left], p);
return
return
treeins(x, treepost(s ->
-> sub[left], p);
p);
treeins(x, (*s)
(*s) ->
-> sub
sub ++ treepost(s
treepost(s -> sub[right],
sub[right], p);
value));
!itemlt(x,
!itemlt(x, (*s)
(*s) ->
-> ?! p(s -> value);
}} p(s -> value);
value)); }}
}}
Programação Avançada com C 237 Programação Avançada com C 238

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Ver as árvores Exercícios (10)
1. Escrever funções utilitárias sobre árvores
Listar todos os nós, por ordem (árvore de int): para:
void
void treeprint(Tree
treeprint(Tree s) s) a) calcular a altura;
{{
if b) contar os nós;
if (s
(s !=
!= NULL)
NULL)
{{ c) ver se uma árvore não vazia é uma folha;
treeprint(s
treeprint(s ->-> sub[left]);
sub[left]); d) contar as folhas;
printf("%d\n",
printf("%d\n", ss ->-> value);
value); e) eliminar as folhas;
treeprint(s -> sub[right]);
treeprint(s -> sub[right]); f) eliminar os nós a partir de certo nível;
}}
}} g) calcular o grau de uma árvore não vazia (o
grau é o número de filhos);
É um percurso infixo h) calcular o grau de equilíbrio (isto é, a
diferença entre as alturas dos filhos, ou o
Listar a raiz com margem m e as subárvores grau de equilíbrio do filho mais desequi-
com margem m + d (árvore de int): librado, consoante for maior);
void
void treeprintm(Tree
treeprintm(Tree s,int
s,int m,int
m,int d)
d) i) calcular o grau de equilíbrio perfeito (isto é,
{{ a maior diferença entre o número de nós
if
if (s
(s !=
!= NULL)
NULL) das duas subárvores de um nó na árvore);
{{ j) criar uma árvore perfeitamente equilibrada
printf("%*s%d\n",m,"",s->value);
printf("%*s%d\n",m,"",s->value); (isto é, uma árvore com grau de equilíbrio
treeprintm(s->sub[left],
treeprintm(s->sub[left], m+d,d);
m+d,d); perfeito inferior ou igual a 1) a partir de um
treeprintm(s->sub[right],m+d,d);
treeprintm(s->sub[right],m+d,d); vector ordenado;
}} i) percorrer uma árvore por nível (breadth-
}} first).
Programação Avançada com C 239 Programação Avançada com C 240

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


C++ O que é uma classe?
Objectivos do C++: A classe Point
• ser um C melhor. Uma
Umaclasse
classeééuma
umaestrutura
estruturacom
comfunções,
funções,
com uma parte privada e outra pública…
com uma parte privada e outra pública…
• suportar a abstracção de dados
• suportar a programação orientada pelos data members
objectos. class
class Point{
Point{
private:
private:
int
int xx;
xx;
O C e o C++: int member functions
int yy;
yy;
O C++ tem os mesmos mecanismos linguís- public:
public:
ticos básicos que o C (funções, variáveis, Point(void);
Point(void);
expressões, instruções, vectores, apontado- void
void Set(int,
Set(int, int);
int);
Cons- void
void Move(int,
Move(int, int);
int);
res, estruturas, input/output, bibliotecas trutor double
double DistanceTo(Point);
DistanceTo(Point);
standard). void
void Display(void);
Display(void);
};
};
Então, o que é que o C++ tem que o C não
tinha? Os membros da secção private só podem
Tem classes (e conceitos associados: heran- ser acedidos pelas funções-membro da
ça, funções virtuais, classes derivadas, etc.) classe; os da secção public constituem a
interface dos objectos da classe.
E também umas coisitas para melhorar o C:
por exemplo, o operador new, conversão de EEonde
tipos, referências, funções inline, etc. ondeééque
quedefinimos
definimosas
asfunções?
funções?

Programação Avançada com C 241 Programação Avançada com C 242

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


As funções-membro da classe Point Níveis de acesso
Point::Point(void) O construtor, chamado Normalmente, na zona pública só há funções.
Point::Point(void) automaticamente quando um Os membros que são valores ficam na zona
{{ objecto é criado, garante
xx privada, e só são acedidos através das fun-
xx == 0;
0; explicitamente que não há
ções públicas. Por vezes também aparecem
yy = 0;
yy = 0; objectos não inicializados.
}} funções na zona privada, e essas só podem
ser usadas dentro da classe.
void
void Point::Set(int
Point::Set(int newX,
newX, int int newY)
newY)
{{ Em esquema:
xx Point:: é
xx == newX;
newX; o class
class AccessLevels
AccessLevels {{
yy
yy == newY;
newY; qualificador private:
}} private:
int
int noAccess;
noAccess;
int
int readOnly;
readOnly;
void Aqui, o especifi-
void Point::Move(int
Point::Move(int dX,
dX, int
int dY)
dY) int
int readWrite;
readWrite; cador const
{{ int
int writeOnly;
writeOnly; significa que as
xx
xx +=
+= dX;
dX; void
void PrivateFunction(void);
PrivateFunction(void); funções deixam
yy
yy += dY;
+= dY; public:
public: o objecto na
}} int
int GetReadOnly(void)
GetReadOnly(void) const;
const; mesma.
void
void SetReadWrite(int);
SetReadWrite(int);
double int
int GetReadWrite(void)
GetReadWrite(void) const;
double Point::DistanceTo(Point
Point::DistanceTo(Point p)
p) void SetWriteOnly(int);
const;
{{ void SetWriteOnly(int);
};
return
return sqrt(sqr(xx-p.xx)+
sqrt(sqr(xx-p.xx)+ };
sqr(yy-p.yy));
sqr(yy-p.yy));
}}
Definições na página seguinte…

etc. double sqr(double x)


Programação Avançada com{C 243 Programação Avançada com C 244
return x * x;
}
© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002
Níveis de acesso (2) Construtores
Definições: Normalmente todas as classes têm um (ou
int
int AccessLevels::GetReadOnly(void)
AccessLevels::GetReadOnly(void) const
const vários) construtores. O construtor é chamado
{{ sempre que um objecto é criado.
return
return readOnly;
readOnly;
}} Um objecto pode ser criado em quatro
É preciso
void AccessLevels::SetReadWrite(int value)repetir o circunstâncias:
void AccessLevels::SetReadWrite(int value) const aqui
{{
readWrite = value; nas
readWrite = value; definições.
}}
1 Quando a sua declaração é processada
int
int AccessLevels::GetReadWrite(void)
AccessLevels::GetReadWrite(void) const
const (objectos automáticos).
{{
return
return readWrite;
readWrite; 2 No início do programa (objectos estáticos).
}}
3 Por meio do operador new (objectos dinâ-
void
void AccessLevels::SetWriteOnly(int
AccessLevels::SetWriteOnly(int value)
value) micos, acedidos por meio de apontadores).
{{
writeOnly
writeOnly == value;
value; 4 Quando é criado um objecto “maior” ou um
}}
vector, do qual o objecto faz parte (objec-
void
void AccessLevels::PrivateFunction(void)
AccessLevels::PrivateFunction(void) tos-membro).
{{
//
// ...
...
}} Tal como está, esta
função não serve para
nada… Por vezes, as classes também têm destru-
tores, que são chamados automaticamente
… e o membro noAccess está mesmo inacessível. quando os objectos são destruídos.

Programação Avançada com C 245 Programação Avançada com C 246

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Um driver para a classe Point Os objectos podem ser definidos
int
int main(void)
main(void)
só quando são precisos
{{ int
int main(void)
main(void)
{{
int
int x;
x; int
int int x;
x;
int y;
y; int
int y;
y;
Point
Point p;
p; Point
Point p;p;
Point q;
Point q; double
Point double d;
d;
Point origin;
origin;
printf("Duas
printf("Duas coordenadas:\n");
coordenadas:\n");
double scanf("%d%d",
scanf("%d%d", &x,
&x, &y);
double d;
d; p.Display();
&y);
p.Display(); Definições no meio
p.Set(x,
p.Set(x, y);
y);
printf("Duas
printf("Duas coordenadas:\n");
coordenadas:\n"); p.Display();
p.Display();
das instruções
scanf("%d%d",
scanf("%d%d", &x,
&x, &y);
&y);
p.Display(); Point
Point origin;
origin;
p.Display(); dd == p.DistanceTo(origin);
p.DistanceTo(origin);
p.Set(x,
p.Set(x, y);
y); printf("Dist
printf("Dist 'a 'a origem:
origem: %lf\n",
%lf\n", d);
d);
p.Display();
p.Display(); p.Move(3,
p.Move(3, 7);
7);
dd == p.DistanceTo(origin); p.Display();
p.Display();
p.DistanceTo(origin);
printf("Dist
printf("Dist 'a'a origem:
origem: %lf\n",
%lf\n", d);
d); Point
p.Move(3, Point q; q;
p.Move(3, 7);
7); qq == p;
p;
p.Display();
p.Display(); q.Display();
q.Display();
return
return 0; 0;
afectação de objectos }}
qq == p;
p; da mesma classe:
q.Display();
q.Display(); membro a membro Outro exemplo típico:
return
return 0;0; for(int
}} for(int i=0;
i=0; ii << n;
n; i++)
i++)
{{
...
...
}}
Programação Avançada com C 247 Programação Avançada com C 248

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002


Exercícios 11
1. Enriquecer a classe Point com funções
para reflectir o ponto no eixo dos xx, no eixo
dos yy, na diagonal principal, para rodar o
ponto de um certo ângulo, etc.

2. Declarar uma classe Segment, represen-


tando segmentos de recta definidos por dois
pontos, com funções para o comprimento do

continue;
segmento, para calcular o ponto médio,
mover o segmento, esticar o segmento, etc.

3. Programar uma classe Triangle, com


funções para calcular a área, mover, etc.

A área de um triângulo é dada pela seguinte expressão:


s * (s - a) * (s - b) * (s - c)
onde s é o semiperímetro e a, b, e c são as medidas dos
lados.

Escrever também drivers para testar as


classes.

Programação Avançada com C 249 Programação Avançada com C 250

© Pedro Guerreiro 1994-2002 © Pedro Guerreiro 1994-2002

You might also like