Escolar Documentos
Profissional Documentos
Cultura Documentos
Cadeia de caracteres
W. Celes e J. L. Rangel
6.1. Caracteres
Efetivamente, a linguagem C no oferece um tipo caractere. Os caracteres so
representados por cdigos numricos. A linguagem oferece o tipo char, que pode
armazenar valores inteiros pequenos: um char tem tamanho de 1 byte, 8 bits, e sua
verso com sinal pode representar valores que variam de 128 a 127. Como os cdigos
associados aos caracteres esto dentro desse intervalo, usamos o tipo char para representar
caracteres1. A correspondncia entre os caracteres e seus cdigos numricos feita por uma
tabela de cdigos. Em geral, usa-se a tabela ASCII, mas diferentes mquinas podem usar
diferentes cdigos. Contudo, se desejamos escrever cdigos portteis, isto , que possam
ser compilados e executados em mquinas diferentes, devemos evitar o uso explcito dos
cdigos referentes a uma determinada tabela, como ser discutido nos exemplos
subseqentes. Como ilustrao, mostramos a seguir os cdigos associados a alguns
caracteres segundo a tabela ASCII.
Alguns caracteres que podem ser impressos (sp representa o branco, ou espao):
0
30
40
50
60
70
80
90
100
110
120
(
2
<
F
P
Z
d
n
x
)
3
=
G
Q
[
e
o
y
sp
*
4
>
H
R
\
f
p
z
!
+
5
?
I
S
]
g
q
{
"
,
6
@
J
T
^
h
r
|
#
7
A
K
U
_
i
S
}
$
.
8
B
L
V
`
j
t
~
%
/
9
C
M
W
a
k
u
&
0
:
D
N
X
b
l
v
'
1
;
E
O
Y
c
m
w
nul
bel
bs
ht
nl
cr
del
null: nulo
bell: campainha
backspace: voltar e apagar um caractere
tab ou tabulao horizontal
newline ou line feed: mudana de linha
carriage return: volta ao incio da linha
delete: apagar um caractere
Alguns alfabetos precisam de maior representatividade. O alfabeto chins, por exemplo, tem mais de 256
caracteres, no sendo suficiente o tipo char (alguns compiladores oferecem o tipo wchar, para estes casos).
6-1
Em C, a diferena entre caracteres e inteiros feita apenas atravs da maneira pela qual so
tratados. Por exemplo, podemos imprimir o mesmo valor de duas formas diferentes usando
formatos diferentes. Vamos analisar o fragmento de cdigo abaixo:
char c = 97;
printf("%d %c\n",c,c);
Alm de agregar portabilidade e clareza ao cdigo, o uso de constantes caracteres nos livra
de conhecermos os cdigos associados a cada caractere.
Independente da tabela de cdigos numricos utilizada, garante-se que os dgitos so
codificados em seqncia. Deste modo, se o dgito zero tem cdigo 48, o dgito um tem
obrigatoriamente cdigo 49, e assim por diante. As letras minsculas e as letras maisculas
tambm formam dois grupos de cdigos seqenciais. O exemplo a seguir tira proveito desta
seqncia dos cdigos de caracteres.
Exemplo. Suponhamos que queremos escrever uma funo para testar se um caractere c
um dgito (um dos caracteres entre '0' e '9'). Esta funo pode ter o prottipo:
int digito(char c);
6-2
Exerccio. Escreva uma funo para determinar se um caractere uma letra, com prottipo:
int letra(char c);
Exerccio. Escreva uma funo para converter um caractere para maiscula. Se o caractere
dado representar uma letra minscula, devemos ter como valor de retorno a letra maiscula
correspondente. Se o caractere dado no for uma letra minscula, devemos ter como valor
de retorno o mesmo caractere, sem alterao. O prottipo desta funo pode ser dado por:
char maiuscula(char c);
Se o caractere '\0' no fosse colocado, a funo printf executaria de forma errada, pois
no conseguiria identificar o final da cadeia.
Como as cadeias de caracteres so vetores, podemos reescrever o cdigo acima
inicializando os valores dos elementos do vetor na declarao:
int main ( void )
Estruturas de Dados PUC-Rio
6-3
{
char cidade[ ] = {'R', 'i', 'o', '\0'};
printf("%s \n", cidade);
return 0;
}
s1[] = "";
s2[] = "Rio de Janeiro";
s3[81];
s4[81] = "Rio";
Desta forma, se o usurio digitar a letra r, por exemplo, o cdigo associado letra r ser
armazenado na varivel a. Vale ressaltar que, diferente dos especificadores %d e %f, o
especificador %c no pula os caracteres brancos2. Portanto, se o usurio teclar um espao
2
Um caractere branco pode ser um espao (' '), um caractere de tabulao ('\t') ou um caractere de
nova linha ('\n').
Estruturas de Dados PUC-Rio
6-4
antes da letra r, o cdigo do espao ser capturado e a letra r ser capturada apenas numa
prxima chamada da funo scanf. Se desejarmos pular todas as ocorrncias de caracteres
brancos que porventura antecedam o caractere que queremos capturar, basta incluir um
espao em branco no formato, antes do especificador.
char a;
...
scanf(" %c", %a);
...
J mencionamos que o especificador %s pode ser usado na funo printf para imprimir
uma cadeia de caracteres. O mesmo especificador pode ser utilizado para capturar cadeias
de caracteres na funo scanf. No entanto, seu uso muito limitado. O especificador %s
na funo scanf pula os eventuais caracteres brancos e captura a seqncia de caracteres
no brancos. Consideremos o fragmento de cdigo abaixo:
char cidade[81];
...
scanf("%s", cidade);
...
Devemos notar que no usamos o caractere & na passagem da cadeia para a funo, pois a
cadeia um vetor (o nome da varivel representa o endereo do primeiro elemento do vetor
e a funo atribui os valores dos elementos a partir desse endereo). O uso do especificador
de formato %s na leitura limitado, pois o fragmento de cdigo acima funciona apenas para
capturar nomes simples. Se o usurio digitar Rio de Janeiro, apenas a palavra Rio ser
capturada, pois o %s l somente uma seqncia de caracteres no brancos.
Em geral, queremos ler nomes compostos (nome de pessoas, cidades, endereos para
correspondncia, etc.). Para capturarmos estes nomes, podemos usar o especificador de
formato %[...], no qual listamos entre os colchetes todos os caracteres que aceitaremos
na leitura. Assim, o formato "%[aeiou]" l seqncias de vogais, isto , a leitura
prossegue at que se encontre um caractere que no seja uma vogal. Se o primeiro caractere
entre colchetes for o acento circunflexo (^), teremos o efeito inverso (negao). Assim,
com o formato "%[^aeiou]" a leitura prossegue enquanto uma vogal no for encontrada.
Esta construo permite capturarmos nomes compostos. Consideremos o cdigo abaixo:
char cidade[81];
...
scanf(" %[^\n]", cidade);
...
A funo scanf agora l uma seqncia de caracteres at que seja encontrado o caractere
de mudana de linha ('\n'). Em termos prticos, captura-se a linha fornecida pelo usurio
at que ele tecle Enter. A incluso do espao no formato (antes do sinal %) garante que
eventuais caracteres brancos que precedam o nome sero pulados.
Para finalizar, devemos salientar que o trecho de cdigo acima perigoso, pois, se o
usurio fornecer uma linha que tenha mais de 80 caracteres, estaremos invadindo um
espao de memria que no est reservado (o vetor foi dimensionado com 81 elementos).
Estruturas de Dados PUC-Rio
6-5
Para evitar esta possvel invaso, podemos limitar o nmero mximo de caracteres que
sero capturados.
char cidade[81];
...
scanf(" %80[^\n]", cidade);
...
/* l no mximo 80 caracteres */
6-6
A funo copia os elementos da cadeia original (orig) para a cadeia de destino (dest).
Uma possvel implementao desta funo mostrada abaixo:
void copia (char* dest, char* orig)
{
int i;
for (i=0; orig[i] != '\0'; i++)
dest[i] = orig[i];
/* fecha a cadeia copiada */
dest[i] = '\0';
}
6-7
A funo que chama duplica fica responsvel por liberar o espao alocado.
Funes recursivas
Uma cadeia de caracteres pode ser definida de forma recursiva. Podemos dizer que uma
cadeia de caracteres representada por:
uma cadeia de caracteres vazia; ou
um caractere seguido de uma (sub) cadeia de caracteres.
Estruturas de Dados PUC-Rio
6-8
Isto , podemos dizer que uma cadeia s no vazia pode ser representada pelo seu primeiro
caractere s[0] seguido da cadeia que comea no endereo do ento segundo caractere,
&s[1].
Vamos reescrever algumas das funes mostradas acima, agora com a verso recursiva.
Exemplo. Impresso caractere por caractere.
Uma verso recursiva da funo para imprimir a cadeia caractere por caractere mostrada a
seguir. Como j foi discutido, uma implementao recursiva deve ser projetada
considerando-se a definio recursiva do objeto, no caso uma cadeia de caracteres. Assim, a
funo deve primeiro testar se a condio da cadeia vazia. Se a cadeia for vazia, nada
precisa ser impresso; se no for vazia, devemos imprimir o primeiro caractere e ento
chamar uma funo para imprimir a sub-cadeia que se segue. Para imprimir a sub-cadeia
podemos usar a prpria funo, recursivamente.
void imprime_rec (char* s)
{
if (s[0] != '\0')
{
printf("%c",s[0]);
imprime_rec(&s[1]);
}
}
Algumas implementaes ficam bem mais simples se feitas recursivamente. Por exemplo,
simples alterar a funo acima e fazer com que os caracteres da cadeia sejam impressos em
ordem inversa, de trs para a frente: basta imprimir a sub-cadeia antes de imprimir o
primeiro caractere.
void imprime_inv (char* s)
{
if (s[0] != '\0')
{
imprime_inv(&s[1]);
printf("%c",s[0]);
}
}
6-9
fcil verificar que o cdigo acima pode ser escrito de forma mais compacta:
void copia_rec_2 (char* dest, char* orig)
{
dest[0] = orig[0];
if (orig[0] != '\0')
copia_rec_2(&dest[1],&orig[1]);
}
De forma ilustrativa, o que acontece que, quando o compilador encontra a cadeia "Rio",
automaticamente alocada na rea de constantes a seguinte seqncia de caracteres:
'R', 'i', 'o', '\0'
6-10
"Rio" */
Com esta varivel declarada, alunos[i] acessa a cadeia de caracteres com o nome do
(i+1)-simo aluno da turma e, conseqentemente, alunos[i][j] acessa a (j+1)-sima
letra do nome do (i+1)-simo aluno. Considerando que alunos uma varivel global,
uma funo para imprimir os nomes dos n alunos de uma turma poderia ser dada por:
void imprime (int n)
{
int i;
for (i=0; i<n; i++)
printf("%s\n", alunos[i]);
}
Estruturas de Dados PUC-Rio
6-11
A funo para capturar os nomes dos alunos preenche o vetor de nomes e pode ter como
valor de retorno o nmero de nomes lidos:
int lenomes (char** alunos)
{
int i;
int n;
do {
scanf("%d",&n);
} while (n>MAX);
A funo para liberar os nomes alocados na tabela pode ser implementada por:
void liberanomes (int n, char** alunos)
{
int i;
for (i=0; i<n; i++)
free(alunos[i]);
}
6-12
Uma funo para imprimir os nomes dos alunos pode ser dada por:
void imprimenomes (int n, char** alunos)
{
int i;
for (i=0; i<n; i++)
printf("%s\n", alunos[i]);
}
a varivel argc receber o valor 4 e o vetor argv ser inicializado com os seguintes
elementos: argv[0]="mensagem", argv[1]="estruturas", argv[2]="de", e
argv[3]="dados". Isto , o primeiro elemento armazena o prprio nome do executvel e
os demais so preenchidos com os nomes passados na linha de comando. Esses parmetros
podem ser teis para, por exemplo, passar o nome de um arquivo de onde sero capturados
os dados de um programa. A manipulao de arquivos ser discutida mais adiante no curso.
Por ora, mostramos um exemplo simples que trata os dois parmetros da funo main.
#include <stdio.h>
int main (int argc, char** argv)
{
int i;
for (i=0; i<argc; i++)
printf("%s\n", argv[i]);
return 0;
}
Se este programa tiver seu executvel chamado de mensagem e for invocado com a linha
de comando mostrada acima, a sada ser:
mensagem
estruturas
de
dados
Estruturas de Dados PUC-Rio
6-13