Vous êtes sur la page 1sur 290

Linguagem C

Prof. Elda Regina


elda_etec@hotmail.com
Fevereiro/2011

1
Introdução

2
Introdução
C - Dennis Ritchie (Laboratórios Bell)
 Inicialmente para máquinas do tipo PDP-1
(com UNIX).
 Depois para os IBM PC e compatíveis
(ambientes MS DOS, e MS Windows)

3
O Standard ANSI-C
 Versão C que segue a norma da American
National Standard Institute (ANSI), e da
International Standards Organization (ISO).

 Os Compiladores da Borland foram os


primeiros a oferecer compatibilidade com
esta norma (os compiladores de Turbo C a
partir da versão 2.0).

4
Características
A linguagem C é muito famosa e muito
utilizada:
– pela concisão de suas instruções;
– pela facilidade de desenvolvimento
de Compiladores C;
– pela rapidez de execução de programas;
– e principalmente pelo fato que o poderoso
sistema operacional Unix foi escrito em C.

5
Um Programa Simples
#include <stdio.h>
/*
um programa bem simples
que imprima: Ate logo.
*/

void main ()
{
printf("Ate logo. ");
}

6
Exemplo 1
#include <stdio.h> /* 1 */
#define B 20 /* 2 */
int a,s; /* 3 */

int somar (int x, int y) /* 4 */


{
return (x+y);
}

void main () /* 5 */
{
scanf("%d",&a); /* 6 */
s=somar(a,B); /* 7 */
printf("%d",s); /* 8 */
}

7
Comentários – Ex1
/* 1 */ inclusão de um arquivo da biblioteca de C que
implementa as funções printf, scanf...

/* 2 */ b deve ser substituído por 20

/* 3 */ declaração de duas variáveis inteiras: a e s


/* 4 */ definição de uma função somar
/* 5 */ definição da função principal: main
/* 6 */ leitura do valor de a entrado pelo usuário
/* 7 */ chamada da função somar com argumentos a e b
/* 8 */ impressão do resultado s da soma de a e b

8
Estrutura de um Programa C

Um conjunto de funções


A função principal main é obrigatória.

 A função main é o ponto de


entrada principal do programa.

9
main
void main ()
{
declarações

instruções 1
instruções 2
...
instruções n
}

10
As outras funções
Tipo nome (declaração-de-parâmetros)
{
declarações;

instruções 1;
instruções 2;
...
instruções n;
}

11
Considerações sobre as funções

O tipo da função = tipo do valor que a


função retorna
– Ele pode ser predefinido, ou definido
pelo usuário.
 Por
default o tipo de uma função é
int.

12
Compilação
 Pre-Processadores:

Transformação lexical do texto do programa.


 Compiladores:

Tradução do texto gerado pelo pre-processador e


sua transformação em instruções da máquina.
Observação
Geralmente, o pre-processador é considerado como
fazendo parte integrante do compilador.

13
Tipos Básicos

14
Principais tipos de Dados

 int família dos números inteiros

 float família dos números ponto-


 double flutuantes (pseudo-reais)

 char família dos caracteres

Observação: Não tem o tipo Boolean!


15
Inteiros
 Podem receber dois atributos:
 de precisão (para o tamanho)
 de representação (para
o sinal)
 Atributos de precisão:
– short int : representação sobre 1 byte
– int : representação sobre 1 ou
2 palavra(s)
– long int : representação sobre 2
palavras

16
Inteiros
 Atributos de representação
– unsigned : somente os positivos
– signed : positivos e negativos

17
Combinação de Atributos - Inteiros

 unsigned short int : rep. sobre 8


bits [0, 255]
 signed short int : rep. sobre 7 bits
[-128, 127]
 unsigned int : rep. sobre 16 bits
[0, 65535]
 signed int : rep. sobre 15 bits
[-32768, 32767]
 unsigned long int : rep. sobre 32 bits
[0, 4294967295]
 signed long int : rep. sobre 31 bits
[-2147483648, 2147483647]
18
Os Inteiros
 Em Resumo, temos seis tipos de
inteiros:

– int;
– short int; todos signed
– long int;

e
– unsigned int;
– unsigned short int;
19
– unsigned long int;
Pseudo-Reais

(representação da forma: M * 10EXP)


 Os flutuantes podem ser:
 float :representação sobre 7 algarismos
[-3.4*10-38, 3.4*1038 ]
 double :representação sobre 15 algarismos
[-1.7*10-308, 1.7*10308 ]
 long double : representação sobre 19
algarismos [-3.4*10-4932, 3.4*104932 ]
20
Caracteres

 Um caracter é representado por


seu código ASCII (código numérico).
 Ele pode ser manipulado como um inteiro.
 Um caracter é codificado sobre um byte
⇒ podemos representar até 256
caracteres.
21
Caracteres

O tipo é: Char
char c,b;
c = ’\65’;
b = ’c’;

22
E o tipo String?

 Nãoexiste em C o tipo string


propriamente dito.

 Um substituo:
os vetores de caracteres:
char nome[20]

23
E o tipo Boolean?
 Atenção:

O Boolean não existe em C!

 Isto é geralmente substituído pelo

tipo int:
0 : false
1 : true

24
Em sistemas 32 bits
 char 1 -128 a 127
 signed char 1 -128 a 127
 unsigned char 1 0 a 255
 short 2 -32,768 a 32,767
 unsigned short 2 0 a 65,535
 int 4 -2,147,483,648 a 2,147,483,647
 unsigned int 4 0 a 4,294,967,295
 long 4 -2,147,483,648 a 2,147,483,647
 unsigned long 4 0 a 4,294,967,295
 float 4 3.4E+/-38 (7 dígitos)
 double 8 1.7E+/-308 (15 dígitos)
 long double 10 1.2E+/-4932 (19 dígitos)
25
DECLARAÇÃO DE
VARIÁVEIS

26
Identificadores

 Umidentificador é um meio para


manipulação da informação
 Umnome que indica uma variável,
função, tipo de dados, etc.

27
Identificadores

 Formado por uma combinação de


caracteres alfanuméricos
 Começa por uma letra do alfabeto ou
um sublinhado, e o resto pode ser
qualquer letra do alfabeto, ou
qualquer dígito numérico (algarismo),
ou um sublinhado.
28
Exemplos de identificadores
 Exemplo:

nome_1; _a; Nota; nota;


imposto_de_renda;
!x; 4_nota;
 Atenção:
Os identificadores Nota e nota por
exemplo representam duas variáveis
diferentes.
29
Regras para a nomeação
 Como os compiladores ANSI usam
variáveis que começam por um
sublinhado, melhor então não usá-las.
 De acordo com o standard ANSI-C:
– somente 32 caracteres podem ser
considerados pelos compiladores, os
outros são ignorados.
 Não usar as palavras chaves da

30
linguagem.
Palavras Chaves
(são 32 palavras)

auto double int struct


break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
31
Declarações

tipo lista-de-nomes-de-variáveis

 Exemplo:

int i,a;
double d1, d2;
char c;

32
Inicialização de Variáveis

C não inicializa automaticamente


suas variáveis.
 Isto
pode ser feito no momento da
declaração:
int i=1,j=2, k=somar(4,5);
short int l=1; char c=’\0’;

33
Operações

34
Atribuição
 Exemplo :
i=4;
a=5*2;
d=-b/a;
raiz=sqrt(d);

35
Exemplos de Atribuição

i = (j = k) - 2; (caso k=5)
5 j=5
3 i=3

Atribuição múltipla:

a=b=c=8;

36
Conversão de Tipos (1)
O compilador faz uma conversão
automática
float r1,r2=3.5; int i1,i2=5;
char c1,c2=’A’;
r1=i2; /* r1 ← 5.0 */
i1=r2; /* i1 ← 3 */
i1=c2; /* i1 ← 65 */
c1=i2; /* c1 ← 5 */
r1=c2; /* r1 ← 65.0 */
c1=r2; /* c1 ← 3 */
37
Conversão de Tipos (2)
int a = 2;
float x = 17.1, y = 8.95, z;
char c;

c = (char)a + (char)x;
c = (char)(a + (int)x);
c = (char)(a + x);
c = a + x;

z = (float)((int)x * (int)y);
z = (float)((int)(x * y));
z = x * y;
38
Adição/subtração/multiplicação

Expressão1 + expressão2
Expressão1 - expressão2
Expressão1 * expressão2

 Asduas expressões são avaliadas, depois


a adição/subtração/multiplicação é
realizada, e o valor obtido é o valor da
expressão.
 Pode ter uma conversão de tipos, depois da
avaliação das expressões:
float x,y; int z; x=y+z;
39
Divisão (1)

Expressão1 / expressão2

 Em C, o / designa no mesmo tempo


a divisão dos inteiros e dos reais.
 Isto depende então dos valores
retornados pelas expressões:
– Se os dois são inteiros então a divisão é
inteira, mas se um deles é real então
a divisão é real.
40
Divisão (2)
 Caso os dois operandos inteiros são
positivos, o sistema arredonda o
resultado da divisão a zero:
7 / 2 retorna 3
 Caso os dois operandos inteiros são de
sinais diferentes, o arredondamento
depende da implementação mas é
geralmente feito a zero:
-7 / 2 ou 7 / -2
retornam -3 ou -4 (mas geralmente -4)
41
O Módulo %
Expressão1 % Expressão2
 Retorna o resto da divisão inteira.
7 % 2 retorna 1
 Caso um dois operadores seja negativo, o
sinal do módulo depende da
implementação, mas
é geralmente o sinal do primeiro
7 % 2 retornam 1
-7 % 2 retornam -1
7 % -2 retornam 1
42
Operadores Relacionais
Expressão1 op-rel Expressão2
 Onde op-rel é um dos seguintes símbolos:
Operador Semântica
= = igual
!= diferentes
> superior
>= superior ou igual
< inferior
<= inferior ou igual
O resultado da comparação é um valor lógico:
1 (se a comparação é verificada) e 0 (senão)
43
Operadores Lógicos

 Para combinar expressões lógicas


Operador Semântica
&& e
|| ou
! negação

 Exemplos:
a >= b
(a = = 0) || (b != 0)
! ((a = = 0) && (b<3))
44
Operador & Inflação

É o operador de endereçamento
(ponteiro):
 &a é o endereço da variável a

⇒ acesso ao conteúdo da variável a.

45
Incremento/decremento

x = x + 1; /* isto incrementa x */

x++; /* isto incrementa x */


++x; /* isto incrementa x */

s+=i; /* s=s+i*/

z = y++; /* z = y e depois y++ */


z = ++y; /* y++ e depois z = y (novo) */

O decremento é de igual modo


46
Regra

v = v operador expressão

v operador= expressão

47
Condicional

a = (b >= 3.0 ? 2.0 : 10.5 );



if (b >= 3.0)
a = 2.0;
else
a = 10.5;

c = (a > b ? a : b); /* c=maior(a,b) */


c = (a > b ? b : a); /* c=menor(a,b) */

48
Entradas e Saídas

49
Saídas
 Printf("Bom dia");
 puts("Bom dia"); /* para imprimir um string */
 putch(a) /* para imprimir um char */

 printf ("Meu nome é: %s\ne tenho %d anos.", nome,


idade)

50
Entradas
gets(s); /* leitura de um string */
char c;
c = getch();
c = getchar(); /* com echo */

scanf ("%d",&nome);
/* para a leitura da var. nome */

51
Constantes utilizados no printf

\n nova linha
\t tabulação (horizontal)
\v tabulação vertical
\b um espaço para trás
\r um return
\a um bip
\\ backslash
\% o caracter %
\{’’} um apostrofo
\? um ponto de interrogação
52
Conversão de Tipos

d notação de decimal
o notação octal
x notação hexadecimal
e notação matemática
u notação sem sinal
c notação de caracter
s notação de string
f notação de flutuante
53
Formatação

 Cada um desses caracteres de conversão é


utilizado depois do %
para indicar o formato da saída (da
conversão)

 Mas, entre os dois podemos entrar outros


argumentos:

54
Formatação

- justificar a esquerda
+ o sinal sempre aparente
n o comprimento mínimo da saída (senão
brancos)
0n o comprimento mínimo da saída (senão 0s
esquerda)
n.m para separar as partes antes e depois da
virgula total de n dígitos, cujo m são depois
da virgula.
l para indicar um long
55
Exemplo
#include <stdio.h>
int main()
{
int a; long int b; short int c;
unsigned int d; char e; float f;
double g;
a = 1023; b = 2222; c = 123;
d = 1234; e = ’X’;
f = 3.14159;
g = 3.1415926535898;
...
}
56
Exemplo
{ printf("a = %d\n", a); a = 1023
printf("a = %o\n", a); a = 1777
printf("a = %x\n", a); a = 3ff
printf("b = %ld\n",b); b = 2222
printf("c = %d\n", c); c = 123
printf("d = %u\n", d); d = 1234
printf("e = %c\n", e); e = X
printf("f = %f\n", f); f = 3.141590
printf("g = %f\n", g); g = 3.141593
}
57
Exemplo

{
printf("\n");
printf("a = %d\n", a);
printf("a = %7d\n", a);
printf("a = %-7d\n", a);
c = 5;
d = 8;
printf("a = %*d\n", c, a);
printf("a = %*d\n", d, a);
}
58
Exemplo

{
printf("\n");
printf("f = %f\n", f);
printf("f = %12f\n", f);
printf("f = %12.3f\n", f);
printf("f = %12.5f\n", f);
printf("f = %-12.5f\n", f);
}

59
Exemplo

As saídas são:

f = 3.141590
f = 3.141590
f = 3.142
f = 3.14159
f = 3.14159

60
Diretivas de Compilação

61
Diretivas de compilação

 São instruções para o pre-processador.


 Elas não são instruções C, portanto
elas não terminam por uma virgula.
 Paradiferenciá-las das instruções C, elas
são precedidas pelo símbolo especial #
Temos principalmente as seguintes diretivas:
#include
#define
#if, #else, #endif
62
Inclusão de Fontes: #include

#include <nome de arquivo>


 Para a inclusão de um arquivo.
 A busca do arquivo é feita em primeiro no diretório
atual e depois no local das bibliotecas:

Em Unix: /usr/lib/include
Em DOS: APPEND (~ PATH, mas para os
arquivos de dados e não
para os executáveis)
Em Windows: isto é especificado no
ambiente

63
Inclusão de Fontes: #include

#include “nome-de-arquivo”

 Para a inclusão de um arquivo usuário.


 Nestecaso, a busca do arquivo é feita
apenas no diretório atual. Senão, pode se
especificar o caminho completo do
arquivo
64
Exemplo de inclusão

... f2.c
#include "f2.c" for (i=0;i<5;i++)

... printf("Oi");

f1.c
f1.c
...
for (i=0;i<5;i++)
printf("Oi");
...
65
Arquivos headers (.h)
 Arquivos headers standards:
stdlib, stdio.h, conio.h, etc.
 Um arquivo header contém um conjunto
de declarações de variáveis e funções.

 Observação:

Os headers incluídos podem


também incluir outros arquivos (mas
não pode existir uma referencia mútua)
66
Substituições: #define
 #define MAX 60
 #define TRUE 1
 #define FALSE 0
 #define BOOLEAN int

{
BOOLEAN a=FALSE;
if (a = = TRUE) ...; else ...;
}
(uma convenção: usar os maísculos)
67
Macro Substituições: #define

#define maior(A,B) ((A)>(B)?(A):(B))


f()
{
...
maior(i+1,j-1);
...
}

A instrução maior(i+1,j-1) será compilada


como se nos escrevemos:
((i+1)>(j-1)?(i+1):(j-1))
68
Outros Macros

#define quadrado(a) (a)*(a)


#define quadrado(a) a*a
#define adicionar(a,b) ((a)+(b))
#define adicionar(a,b) (a)+(b)

quadrado(3-2); (3-2)*(3-2) = 1
5*adicionar(2+1,4); 5*((2+1)+(4)) = 35
regra geral: colocar todas as variáveis entre
parênteses e o resultado também
69
Substituições parciais
#define MAX 60
... /*aqui MAX=60*/
#undef MAX
#define MAX 45
... /*aqui MAX=45*/

70
Compilação condicional (1)

Objetivo: a otimização

#if (condição)
corpo 1
[#else
corpo 2]
#endif

71
Compilação condicional (2)

#ifdef identificador
corpo 1
#endif

#ifndef identificador
corpo 1
#endif

72
O const
 Para declarar variáveis constantes podemos
usar a palavra chave const
const int N 20
Isto proibe que N seja modificado no programa
(toda tentativa de modificação vai dar erro)

 Diferença para o #define:


- Usando o const teremos uma reserva de um espaço na
memória. Ele se aplica sobre qualquer tipo de var.
- O #define serve somente para o pre-processador que
faz as substituições necessárias antes da compilação.

73
Estruturas de Controle

74
Blocos de instruções
{
declarações
instrução_1
..... /*um bloco de instruções
é delimitado por duas chaves*/
instrução_2
}

Em um bloco pode-se declarar variáveis,


que ficam visíveis somente no bloco.
75
Instrução if...else (1)
If (condição)
instrução_1;
[else
instrução_2;]

Exemplos:
if (a>=b) if (a!=0)
max=a; x=-b/a;
else
max=b;
76
Instrução if...else (2)

 Qualquer instrução simples C pode ser


substituída por uma instrução composta:
if (d>0)
{
x1=(-b-sqrt(d))/2*a;
x2=(-b+sqrt(d))/2*a;
}

77
Instrução if...else (3)

Observação: o else refere-se ao último if

If (condição_1)
instrução_1;
else if (condição_2)
instrução_2;
else if (condição_3)
instrução_3;
else
instrução_4;

78
Instrução if...else (4)

int a=3, b=2, c=1, d=0;


if (a>b) if (c<d) b=2*a; else a=2*b;

senão, tem que escrever:

if (a>b)
{
if (c<d) b=2*a;
}
else a=2*b;

79
Instrução if...else (5)

Observação: o else refere ao último if


senão tem que usar chaves (bloco)

If (condição_1)
{
instrução_1;
if (condição_2)
instrução_2;
}
else if (condição_3)
instrução_3;

80
Observação

A condição não é obrigatoriamente uma


comparação. Pode ser qualquer
expressão retornando um valor que pode
ser comparado a zero.

int b=0;
if (b) /* equiv. a: if(b!=0)*/
{
...
81
}
Instrução switch (1)

escolha múltipla

switch (expressão)
{
case constante_1:
instruções
break;
case constante_2:
instruções
break;
...
default:
instruções
82
}
Instrução switch (2)
 Quando as mesmas instruções devem ser
executadas no caso de um conjunto de
valores:
switch (expressão)
{
case val_1:
...
case val_n:
instruções
break;
case val_3:
instruções
break;
default:
instruções
}
83
Instruções de Repetição

84
while
while (condição)
instrução

instrução pode ser simples ou composta.

Exemplo:

fim do while
não
c=’s’; condição!=0 ?
while (c!=’f’)
{
c=getch(); sim
}
instruções
85
do...while (1)

do
instrução(ões)
while (condição)

É o equivalente de repeat do Pascal.


A instruções do laço de repetição são
executadas pelo menos uma vez.

86
do...while (2)

do
c=getch();
while (c==’s’)
instrução

fim do do
sim
não
condição!=0 ?

87
for (1)
for (expressão1, condição, expressão2)
instrução(ões)

 expressão1 é avaliada só uma vez


 depois a condição
– se condição OK, as instruções são executadas
 depois a expressão2, antes de voltar a avaliar
novamente a condição

Exemplo: for (i=1; i<=10; i++)


{
printf(“Vasco da Gama”);
}
88
for (2)

expressão1 inicialização

fim do for
não
condição!=0 ?

sim
instruções

expressão2
89
for (3)
 A novidade é que as expressões podem ser
instruções múltiplas (separadas por virgula)

Exemplo:

for (i=1,j=10; i<=j; i++,j--)


{
instruções;
}

90
break
O break permite parar a instrução
de repetição (for, do ou while)
Se temos vários níveis, o controle volta
à penúltima estrutura de repetição.
for (i=0;i<20;i++)
if (vet[i]==10) break;

91
continue
 Utilizada dentro de uma instrução for, while ou
do
 O continue permite parar a iteração atual e
passar à iteração seguinte.

Exemplo:
for (i=1;i<20;i++)
{
if (vet[i]<0) continue;
...
}
92
Instrução: goto

goto <identificador> ;
permite quebrar a seqüência do programa
Exemplo:

mais:

...

If condição goto mais;


93
Funções

94
Definição de uma função

 Além das funções predefinidas nos arquivos


header da biblioteca (no diretório LIB, usando
o #include)

 O usuário pode definir novas funções.


 Exemplos de funções predefinidas da biblioteca:
stdio.h, math.h, conio.h,
stdlib.h e dos.h

95
Ex. de funções: (stdio.h)

Principalmente:
puts
gets

printf
scanf

96
Ex. de funções: (math.h)

abs módulo de int. int abs(int)


fabs módulo de real double fabs(double)
exp exponencial double exp(double)
ceil arredondar ao max double ceil(double)
floor arredondar ao min double floor(double)
pow xy double pow(double x,double y)
pow10 10x double pow10(double)
hypot hipotenusa double hypot(double, double)
sqrt raiz quadrada double sqrt(double)

97
Ex. de funções: (conio.h)

clrscr apaga a tela void clrscr ()


clreol apaga o resto da linha void clreol ()
delline apaga a linha atual void delline ()
insline insere uma linha void insline ()
gotoxy posiciona o cursor void gotoxy (int x, int y)
kbhit testa se tecla int kbhit ()
putch imprima um char c int putch (int c)
getch leia char sem echo int putch ()
herex posição x do cursor int wherex ()
herey posição y do cursor int wherey ()
98
Ex. de funções: (stdlib.h)

min retorna o min type min(type, type)

max retorna o max type max(type, type)

rand ret. num. alea. int rand()

exit ret. nível de erro void exit(int)

random ret. num. alea de 0 a n-1 int random(int n)

randomize inicializa o gen. al. int randomize()


precisa também de <time.h>

99
Ex. de funções: dos.h
delay
sound
nosound parar o som
sleep suspende em seg void sleep(unsigned s)

exemplo:
{ sound(300); /* emitir um som de 300 Hz*/
delay(2000); /* esperar 2 segundos */
nosound(); /* antes de parar */
}
100
Exemplos de funções

 graphics.h

rectangle desenha um retângulo


void rectangle (int e, int c, int d, int b)

 string.h

101
Definição de uma função

tipo identificador ([lista de id.])

[lista de declarações 1]
{
[lista de declarações 2]

lista de instruções
}

102
Semântica
tipo: é o tipo do valor devolvido pela função

identificador: nome da função


lista dos identificadores: os parâmetros formais
lista de declarações 1: é a lista de declaração dos
parâmetros formais
lista de declarações 2: declaração das variáveis
locais a função
lista de instruções: são as instruções a executar
quando a função é chamada.

103
Exemplo

float media (a,b)


float a,b; /* declaração dos
parâmetros formais */
{
float resultado; /* var. locais */
resultado=(a+b)/2;

return (resultado); /* retornar o


res. ao remetente */
}
104
Os parâmetros formais - opcionais

double pi () /* não temos param.


formais */
{ /* não temos var. locais */

return (3.14159);

105
Observação

 A prototipagem da função pode ser feita de uma


vez:
int min (int a, int b)
{
if (a<b)
return (a);
else
return (b);
}
106
Chamada de uma função
identificador (lista de expressões)

As expressões são avaliadas, depois passadas


como parâmetros efetivos à função de nome
identificador.

{
int m_1, m_2; float p, d=4.5;
m_1 = max (4,3); m_2 = max(6*2-3,10);
p = d * pi();
}
107
Procedimentos
 Funções que não retornam valores: usando o
tipo especial void
void linha ()
{
printf("-------------------------\n");
}
Observação: não temos aqui a instrução return
(o tipo especial void não existia nas
primeiras versões da linguagem)

108
Omissão do tipo da função!

C considera por defaut o tipo int.

somar (int a, int b)


{
return (a+b);
}

 Melhor não usar esta possibilidade.

109
Passagem dos Parâmetros

int somar (int x, int y)


{
return (x+y);
}

void main ()
{
int a=8, b=5, s;
s = somar(a,b); /*os parâmetros efetivos*/
}

110
Passagem por Valor

Na chamada temos passagem de parâmetros.

Mas, as modificações dos parâmetros formais

não afetam os parâmetros efetivos.

111
Passagem por Referência
(ou por endereço)
 Usar o operador de endereçamento &
void somar (int x, int y, int * som)
{
*som = x+y;
}
void main ()
{
int a=5, b=6, s;
somar(a,b, &s);
printf("%d + %d = %d",a,b,s);
}
112
Outro uso do void
void f (void)
{
...
}

é para indicar que a função não tem


parâmetros

113
Declaração de Funções

 Uma função F é conhecida implicitamente


por uma outra função G se elas são
definidas no mesmo arquivo, e que F é
definida antes de G.

 Foradesse caso, e para um controle de tipo


e um bom link, é preciso declarar as
funções antes de usar.

114
Exemplo

void main (void)


{
int maior (int, int);
maior (2,8);
}
...
int maior (int x, int y)
{
return (x>y?x:y);
}
/*aí main pode ser definida antes*/
115
Dois formatos para a declaração

int maior (); 1a

int maior (int x, int y); 2a

Mas, melhor usar o segundo formato


que é mais completo

116
Funções Iterativas

exemplo do fatorial

long int fat (long int n)


{
long int i,res=1;

for (i=1;i<=n;i++)
res=res*i;
return (res);
}
117
Funções Recursivas
exemplo do fatorial

long int fat (long int n)


{
if (n == 1)
return 1;
else
return (n*fat(n-1));
}
118
Exercícios

 Escreva as funções real_dólar e


dólar_real de conversões Real-Dólar e vice
versa.
 Escreva as versões recursiva e iterativa
da função soma que retorna a soma dos
n primeiros termos:
S(n) = 1 + 1/2 + 1/3 + ... + 1/(n-1) + 1/n

 Escreve a função S(n) tal que:


S(n) = 1/n - 2/(n-1) +3/(n-2) + ... - (n-1)/2 + n/1
119
Declarações

120
Escopo das declarações

são 4 escopos:
– Um identificador declarado fora das funções, tem
um escopo em todo o programa.
– Um parâmetro formal de uma função, tem
um escopo local à função.
– Um identificador declarado em um bloco
tem um escopo local ao bloco.
– Uma etiqueta de instrução, tem escopo em
toda a função onde ela é declarada.
121
Visibilidade dos identificadores

int i=1;
int j=1;
void p()
{
int i=2; /* este i cobre o precedente */
int k;
if (a==b)
{
int i=3; /*cobre o i precedente*/
int j=2; /* cobre o j precedente*/
int k; /*cobre o k precedente*/
}
}
122
Duração vida de uma variável
 Variáveis Estáticas:
Alocação no início do programa e liberação
no final da execução do programa.

 Variáveis Automáticas
Alocação na entrada de uma instrução
composta (um bloco) e liberação na saída.

 Variáveis Dinâmicas
A declaração e liberação são explicitamente
realizadas pelo programador (usando malloc
e free). Mas, essas funções são da biblioteca
e não fazem parte integrante da linguagem.
123
Duração vida de uma variável

A duração de vida de uma variável depende


normalmente de seu escopo de declaração:

variável global ⇒ var. estática


variável local ⇒ var. automática

124
Duração vida de uma variável

 As variáveis globais são variáveis estáticas.


 As variáveis locais podem ser estáticas ou
automáticas (dependendo do programador),
usando os qualificadores de classes de
armazenagem: static e auto.
Por default, uma variável local é automática.

125
Indicadores de classes de memória

Em C, temos quatro indicadores de classes


de memória:

auto
static
register
extern

126
auto
{auto int i; ... }
 Este tipo de indicador é autorizado somente para
as variáveis locais a uma instrução composta
(um bloco).
 Ele indica que a variável tem uma duração
de vida local ao bloco.

 O uso deste indicador é quase inexistente, pois


por default toda variável local é automática

127
static

{static int i; ... }


 O static indica que a variável tem uma
duração de vida ao longo da execução do
programa. Mas que ela fica local ao bloco
(ela fica desconhecida fora de seu bloco).
 Portanto, a variável guardará seu valor para
cada chamada ao bloco.
 Este indicador pode ser usado para as
declarações de variáveis e funções.
128
exemplo de uso de static
void f ()
{
static int num_chamadas = 0;
num_chamadas++; /* para contar
quantas vezes f foi chamada */
}
 A variável num_chamada é inicializada
somente uma vez. Tem duração de vida ao
longo da execução do programa. Mas ela tem
escopo local.
129
register

{register int a,b; ... }


 Este tipo de indicador é autorizado somente para
as variáveis locais a uma instrução composta (um
bloco) e para as declarações de parâmetros de
funções.
 Ele tem o significado do auto, além de provocar o
armazenamento da variável
em um registro e não na memória.
⇒ acesso mais rápido.
 Claro que isto depende das capacidades da
máquina (número e tamanho dos registros)
130
extern
int a;
void f() {... extern int a; ...}
 Os outros indicadores são relacionados às
variáveis locais.
Uma variável global é definida fora de
qualquer corpo de função.
 O extern permite usar (acessar) uma variável
que é definida fora.
 Este indicador é autorizado para as
declarações de variáveis e funções.
131
Classes de memória (1)
 Na execução de um programa, existe três
zonas de memória:

– Zona das variáveis estáticas ;


– Zona das variáveis automáticas
(organizada em forma de pilhas, pois as funções podem
ser recursivas)
– Zona das variáveis dinâmicas (organizada em forma de
“montão”
montão ou “tas / stack”)

132
Classes de memória (2)

 Esta divisão existe nas outras linguagens,


mas a alocação é feita pelo compilador.
Ao contrário, em C a declaração da classe
da variável é feita pelo próprio
programador.
 auto, static e register são
indicadores de classe de memória (mas

133
o extern não é realmente)
Tabelas

134
Tabelas

 O objetivo da estrutura de tabela é


agrupar um conjunto de informações de
mesmo tipo em um mesmo nome.

 Uma tabela pode ser multidimensional ou


unidimensional (vetor).

135
Declaração

float vet[10];
long int v1[8], v2[15];
/* v1 é um vetor de 8 long int
e v2 é um vetor de 15 long int */

 Os elementos são indexados de 0 a


N-1

136
Dimensão

 Na prática, é recomendado definir sempre


uma constante que indica o número de
elementos:
#define N 60
short int v[N];
/* declara um vetor de 60
inteiros indexado de 0 a 59 */

137
Acesso aos elementos

Sintaxe:
nome-variável [expressão]
expressão deve retornar um inteiro (o indexador)

Exemplos:
vet[0]=6;
vet[4+1]=-2;
x=3*vet[2*a-b];

138
Inicialização
#define N 4
int v[N]={1,2,3,4};
 Incialização de somente uma parte do vetor:
#define N 10
int v[N]={1,2}; //o resto será zerado
 Inicialização pelo mesmo valor:
for (i=0;i<=9;i++) v[i]=2;

 Inicialização pelo usuário:


for (i=0;i<=9;i++)
scanf("%d",&v[i]);

139
Operadores abrangentes

lembramos que:
x++ incrementa x mas retorna o valor inicial
++x incrementa x e retorna o valor incrementado
i=0;
v[i++]=0; /* v[0]=0 e i=i+1 */
v[i++]=0; /* v[1]=0 e i=i+1 */
i=1;
v[++i]=0; /* i=i+1 e v[2]=0 */
v[++i]=0; /* i=i+1 e v[3]=0 */
 idem para o operador --
140
Uso da instrução nula

 Inicialização de um vetor:
for (i=0;i<10;v[i++]=1);
Isto é equivalente a:
for (i=0;i<10;i++) v[i]=1;
 Pesquisar em um vetor:

for (i=0; i<N && t[i]!=10; i++);


/* usando a instrução nula */
Isto é equivalente a:
for (i=0; i<N; i++)
if t[i]==10 break;
141
Tabelas MultiDimensionais

Matrizes
Declaração:
int mat [3][4]; /* matriz bidimensional de
3 linhas 4 colunas */
Inicialiazação:
int mat [3][4] =
{
{5,6,8,1},
{4,3,0,9},
{12,7,4,8},
142 }
Exemplo
/* Declaração: */
#define L 4;
#define C 3;
int mat[L][C];
/* leitura: */
for (i=0;i<=L;i++)
for (j=0;j<=C;j++)
{
printf("digite o elemento [%d,%d]: ",i,j);
scanf("%d",&mat[i][j]);
}
143
Observação 1

for (i=0,j=0;i<L && j<C;i++,j++)


{
printf("digite o elemento [%d,%d]: ",i,j);
scanf("%d",&mat[i][j]);
}
não é a mesma coisa que:
for (i=0;i<=L;i++)
for (j=0;i<=C;j++)
{
printf("digite o elemento [%d,%d]: ",i,j);
scanf("%d",&mat[i][j]);
}
144
Observação 2

int sum (int n)


{
int res=0;
for (;n>0;n--) /*n é inicializada na chamada*/
res=res+n;
return (res);
}
chamada: sum(5)
O que faz este código?

145
Exercícios
 A.1 Escreva o procedimento ini_num_dias que
inicializa um vetor num_dias[12] que indica para cada
mês do ano seu número de dias: (num_dias[i] indica o
número de dias do mês i do ano), sabendo que:
Se i=2 então num_dias=28;
Se (i par e i<=7) ou (se i impar e i>7) então
num_dias=30 Senão num_dias=31.
A.2 Escreva o procedimento imp_num_dias que
imprima os números de dias de todos os meses do ano.
 B. Escreva a função ordenar que ordena um vetor.
 C. Escreva a função palindromo que determina se um
vetor é um palindromo.
146
Tipos Enumerados

147
enum
 A enumeração permite de agrupar um conjunto
de constantes (compartilhando a mesma
semântica)
 exemplos:
enum dias {Domingo, Segunda, Terça,
Quarta, Quinta, Sexta, Sábado};
 declaração:
dias d1,d2=Quinta;
 enum defini um novo tipo cujo os elementos
são numerados automaticamente de pelo
compilador: 0 1 2 ...
148
Exemplo
#include <stdio.h>
enum dias {Segunda,Terça,Quarta,Quinta,
Sexta,Sábado,Domingo} d;
// d é uma variável declarada de tipo dias

void main (void)


{ // dias d; é uma outra maneira de declarar
for(d = Segunda ; d < Domingo ; d++)
printf(”O código do dia é: %d\n", d);
}
Vai imprimir os valores dos dias: 0, 1 até 6.
149
enum
 Essa numeração permite comparar os elementos
do tipo: if (d1<=d2) ...

 Portanto podemos mudar essa numeração:


enum boolean {true=1,false=0};

 Quando um item não é numerado ele pega


o valor de seu precedente:
enum temperatura
{baixa=2,media=4,alta};

150
Ponteiros

151
Variáveis Dinâmicas

 Todas as variáveis vistas até agora são


estáticas (reserva imediata na memória),
contudo precisamos de variáveis dinâmicas

 Um ponteiro é uma referência sobre


um objeto na memória (é um endereço).

152
Variáveis Dinâmicas

 Exemplo de Declaração de Ponteiros:

float * pf
/* declara que pf é um ponteiro sobre um real */

pf

4.6

153
Os Operadores & e * (1)

O operador de endereçamento &


se aplica sobre uma variável e permite
retornar seu endereço memória

O operador de indireção *
se aplica sobre um ponteiro e permite
retornar (manipular) o objeto
apontado.
154
Os Operadores & e * (2)

*P

P V
(Ponteiro) (Variável)

&v

155
Exemplo
int i,j;
int *pi;
/* pi é um ponteiro sobre um inteiro */
pi &i
&j
i=5;
pi=&i; 56 i
*pi=6;
j=*pi-2; 4 j
pi=&j;
156
Exercício

1. Declare um inteiro i e um ponteiro p


sobre um inteiro
2. Inicialize o inteiro com um valor qualquer
3. Aponte o ponteiro sobre a variável i
4. Imprima o valor de i
5. Modifique o valor de i usando o ponteiro
6. Imprima o novo valor de i
157
int i; /* 1 */
int * p; /* 1 */
i=8; /* 2 */
p=&i; /* 3 */
printf("i= %d\n",i); /* 4 */
*p=5; /* 5 */
printf("novo i= %d\n",i); /* 6 */

158
Passagem de Parâmetros
 Passagem de parâmetros (dois tipos):
– por valor
– por referência (passar o endereço da variável)

 Estratégias adotadas pelas linguagens:


– Todo é feito por referência (FORTRAN, PL/1)
– Temos a escolha entre a passagem por valor
ou por referência (caso de PASCAL)
– Toda passagem é feita por valor (caso de C)
159
Estratégia de C

 Em C, toda passagem de parâmetros é


portanto feita por valor.
 Como então fazer as passagens por
referência? A solução é de declarar os
parâmetros formais de tipo ponteiro.
 Istoé feito pelo uso do operador &
(usado na chamada a função).

160
Exemplo

void adicionar (int a, int b, int * res)


{
} *res=a+b; observe a declaração de res
como um ponteiro sobre um
inteiro: um endereço

void main (void)


{
observe que na chamada tem
int i,j,k; que passar o endereço da
adicionar (i,j,&k); variável onde deseja-se
recuperar o resultado

}
161
Um outro exemplo

A função troca
void troca (int * x, int * y)
{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
chamada da função
troca(&a,&b); /* passagem por referência */
162
Funções Genéricas
O problema é que esta função troca que nós definimos
se aplica somente sobre os inteiros

void troca (void * x, void * y)


{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
Infelizmente isto não pode ser feito em C
Mas o problema é resolvido em C++

163
Tabelas e Ponteiros

164
Relação entre Tabela e Ponteiros

 O nome de uma tabela não é um identificador de


dados mas é um ponteiro:
#define N 45
int t[N];
t é o endereço do primeiro elemento: t[0]
⇒ t=&t[0]
t[i] = *(t + i)
t + i: Incrementa de i vezes o tamanho
dos objetos do vetor (e não é de i unidades)
165
Conseqüência 1
 Três
conseqüências principais da
consideração que uma tabela é um
ponteiro:

O operador de indexação [ ] não é útil:

 Masele foi adotado pela linguagem parar


não quebrar os costumes dos
programadores
166
Conseqüência 2

Manipulação de sub-tabelas:
int t[10]; p
int * p; t
p=&t[2];

0 1 2 3 9

podemos escrever então p[0], p[1]...


(p[0]=t[2],...)

167
Conseqüência 3

O operador de indexação é comutativo:


t[i] = i[t]

isto porque a adição é comutativa:


t[i] *(t+i)

i[t] *(i+t)
 Mas,por razões de legibilidade do
programa esta possibilidade é muito
pouco utilizada.
168
Aritmética dos Ponteiros

 Podem ser efetuadas operações de adição


e de subtração sobre os ponteiros ou entre
ponteiros e inteiros.

 Esta possibilidade é usada para


manipulação de tabelas

169
Ilustração do Cálculo de Ponteiros

#define N 10
int t[N];
int *p, *q, *r, *s;

p=&t[0]; /*aponta sobre o 1o elem do vetor*/


q=p+(N-1); /*aponta sobre o último elem do vetor*/

r=&t[N-1]; /*aponta sobre o último elem do vetor*/


s=r-(N-1); /*aponta sobre o 1o elem do vetor*/

170
Passagem de Parâmetros
 Em C uma tabela pode ser passada como
parâmetro a uma função

 Quando isto é feito, em realidade, é o endereço


do primeiro elemento que é passado como
parâmetro.
 A passagem é então feita por referência.
 O parâmetro formal da função chamada
deve então ser declarado de tipo ponteiro
171
1a Abordagem

void imp_tabela (int * t, int num_elem)


{
int i;

for (i=0;i<num_elem;i++)
printf("%d",*(t+i));
}

chamada: imp_tabela(tab,N); /* que é equiv. a: */


imp_tabela(&tab[0],N);
172
Crítica

Este método apresenta inconvenientes:

A declaração e a chamada não parecem


muitos naturais para os programadores

 Na declaração int * t não mostra se t


aponta sobre um inteiro ou sobre uma zona de
inteiros (a tabela)

173
2a Abordagem
 Por esta razão, C permite uma declaração mais
“natural” dos parâmetros formais:
void imp_tabela (int t[],int n)
{
int i;
for (i=0;i<n;i++)
printf("%d",t[i]);
}
Atenção: Não é preciso conhecer o tamanho exato da
tabela. o tamanho é geralmente passado como
parâmetro separado.

chamada: imp_tabela(tab,L);
174
Tabelas Multi Dimensionais

 No caso de uma tabela multidimensional


somente a primeira dimensão pode não
estar especificada:
int min (int m[][C], int l)
{ int i,j,min=m[0][0];
for(i=0,j=0;i<l&&j<C;i++,j++)
if (m[i][j]<min) min=m[i][j];
return(min);
}
 chamada: min(mat,L);

175
Algumas considerações
 A função min é aplicada a uma tabela que tem qualquer
número de linhas, mas deve ter um exato C de colunas.
 As outras dimensões (≠ 1a), devem ser especificadas por
que o compilador precisa para gerar o código que permite o
acesso aos elementos:
 No caso de uma matriz binária t por exemplo, o endereço de
t[i][j] é: t+(i*C+j)*T (T é o tamanho de um elemento de
t, C é o número de colunas)
 Representação da tabela na memória:

11 12 13 14 21 22 23 24 31 32 33 34 41...
176
Modificação dos elementos
void f (int t[],int nb_elem)
{
int i;

for (i=0;i<nb_elem;i++)
t[i]++;
}
a chamada: f(tab,L);
/* já que a passagem de parâmetros é feita por referência
então qualquer modificações sobre o vetor é física */

O que faz esta função?


177
Tabelas de ponteiros
É possível declarar uma tabela de ponteiros:
 exemplo:
#define N 15
char * tab[N]; /* declara um vetor de ponteiros
sobre elementos de tipo caracter */

 acesso:
i
*tab[i] tab

Objeto
178
*tab[i] apontado
Ponteiro de Ponteiro

179
Ortogonalidade do operador *
 Já que um ponteiro é uma variável como as
outras, é muito normal que um ponteiro aponte
sobre um ponteiro
 Um exemplo é um ponteiro sobre um ponteiro
sobre mais um inteiro

declaração:
int **ppint
int
ppint

180
Argumentos do main
 Um programa C pode ser chamado
externamente com alguns parâmetros:

A função main recebe sempre dois


parâmetros:
– argc (argument count) que determine o número
de argumentos da linha de comando; e
– argv (argument vector), que é um ponteiro sobre
uma tabela de ponteiros sobre strings:
181
argv

É um ponteiro sobre um vetor de ponteiros


sobre strings:
char * argv[];
 argv[0] aponta sobre o nome do arquivo
(nome do programa)
 argv[i] vai apontar sobre os argumentos
passados ao programa na linha de comandos.

182
exemplo
 Suponha: programa achar_max pega como parâmetros
um conjunto qualquer de strings e que deve determinar e
imprimir a maior string:
argv
achar_max\0
\0

\0
\0
 argv[0] é o nome do programa: achar_max,e os outros
argv[i] são os argumentos passados ao programa na
linha de comandos.
183
#include <stdio.h>
#include <string.h>
void main (int argc, char * argv[])
{
int com_max=0, arg_max=0; argc--;
while (argc>=1)
{
if (strlen(argv[argc])>com_max)
{
com_max=strlen(argv[argc]);
arg_max=argc;
}
argc--;
}
if (arg_max) printf("O string maior é: %s", argv[arg_max]);
}
184
Strings

185
String ~ Vetor
 Um string é um conjunto de caracteres.
 Em C, um string é uma estrutura
equivalente à estrutura de vetor,
 A única diferença é que o string termina
sempre pelo caractere ’\0’
Isto para facilitar o tratamento dos strings
(para poder detectar o fim do string)

186
Declaração

O tipo string não existe em C,


 Portanto existe duas maneiras para
simular este tipo de variáveis:

– Como um vetor de char, ou


– Como um ponteiro sobre uma zona
de chars
187
Como Vetor de Caracteres

 Declaração:
#define N 20
char ch [N];

 Os strings declarados como vetor de chars


têm um tamanho limite fixo (o tamanho do vetor).
 Se o tamanho do string é menor do que o tamanho
do vetor, o compilador C completa pelo caractere
especial ’\0’ para indicar o fim do string.

188
Inicialização de Vetor de Caracteres

 A inicialização de um vetor de char pode ser feita,


no momento da declaração, de duas maneiras:

1. Atribuindo um conjunto de caracteres:


char ch [20]={’e’,’x’,’e’,’m’,’p’,’l’,’o’};
(como é normalmente feito para inicializar qualquer tipo
de vetor).

2. Atribuindo um string (é mais prática):


char ch[20]= "exemplo";
O tamanho pode ou não estar especificado:
char nome[]="este tem 23 caracteres";
189
Acesso aos Elementos

 É feito de uma maneira normal, como para


qualquer tipo de vetor:
#define N 3 O i \0
char ch [N]={’O’,’i’};

ch[0]= ’H’ /* refere ao 1o caractere */


ch[1] /* refere ao 2o caractere */ H i \0
...
ch[N-1] /* refere ao Nésimo caractere */

190
Como Ponteiro sobre Caracteres

 A manipulação dos strings como vetores de


caracteres pode aparecer como pouco prática.
Portanto, podemos criar strings de tamanho
dinâmico usando um ponteiro sobre um char:

char * ch = "exemplo";
 Contrariamente à outra maneira, a reserva do
espaço memória não é feita no momento da
declaração, mas dinamicamente no momento da
atribuição.
191
Inicialização e Atribuição

A Inicialização é feita diretamente por uma string:


char * ch = "exemplo";
C completa o string pelo caracter ’\0’ indicando
o fim do string.

A Atribuição também é direta (contrariamente ao vetor de char):

ch
ch = "uma mudança"; uma mudança\0
ch = "outra mudança";
outra mudança\0
Isto não é uma copia mas uma atribuição de ponteiros.

192
Manipulação de Strings

As funções de manipulação de strings são


definidas no arquivo da biblioteca string.h
Temos principalmente as seguintes
funções:
strcpy, strncpy
strlen
strcat, strncat
strcmp, strncmp
strchr, strrchr
193
Strcpy/strncpy
 A função especial strcpy permite atribuir um
valor texto a uma variável de tipo texto:
 O strcpy apresenta dois formatos de uso:
strcpy(string1,string2);
strncpy(string1,string2,N);
exemplo:
char *ch1="boa", *ch2="noite";
strcpy(ch1,"isto é um exemplo");
strncpy(ch2,ch1,4); /* ch2 vai pegar "isto" */

194
strlen

strlen permite retornar o tamanho de um


string: número de chars que compõem o
string (o ’\0’ não faz parte)
exemplo:
int a;
char * nome;
strcpy(nome,“brasil");
a=strlen(nome); /* ⇒ a=6 */

195
strcat/strncat
strcat se aplica sobre dois strings e retorna
um ponteiro sobre a concatenação dos dois.
exemplo:
char *ch1="boa", *ch2="noite",
*ch3, *ch4;
ch3=strcat(ch1,ch2);
ch4=strncat(ch1,ch2,3);
printf("%s %s",ch3,ch4);
/* vai imprimir boanoite boanoi*/

196
strcmp/strncmp
Lembramos que as letras são ordenadas dando
seu código: ’a’< ... ’z’< ’A’...<’Z’
strcmp compara dois strings s1 e s2 e
retorna um valor: negativo se s1 < s2
0 se s1 == s2
positivo se s1 > s2
exemplo:
char *ch1="boa tarde",
*ch2="boa noite";
int a,b;
a=strcmp(ch1,ch2);
b=strncmp(ch1,ch2,4);
/* a vai receber um valor >0 e b 0*/
197
strchr/strrchr
strchr procura por um caractere em um string e
retorna um ponteiro sobre sua última ocorrência, senão
retorna null.
exemplo:
char ch[]="informática";
char *pc, c='f';
pc=strchr(ch,c);
if (pc) /* i.e. if pc!=null */
printf("%d",*pc);
else
printf("Caractere inexistente");
O strrchr faz a busca no senso inverso.

198
touppar/tolower
toupper converte um caractere minúsculo
em maiúsculo.
tolower faz o contrário.

# include <ctype.h>

char c='a';
c=toupper(c); /* c= 'A' */
c=toupper(c); /* c já esta ='A' */
c=tolower(c); /* c volta a ser 'a' */

199
Exercícios 1

A. Defina a função ocorrência que retorna o


número de ocorrências de um caractere em um string.

B.1 Defina a função tamanho1 que pega como


parâmetro um vetor de caracteres e retorna seu
comprimento.

B.2 Defina tamanho2 que implementa a mesma função


mas que pega como parâmetro um ponteiro sobre uma zona
de caracteres.

B.3 Defina o main que chama essas duas funções.

200
int tamanho1 (char s[]) /* com um vetor */
{
int i=0;
while (s[i]) /* equiv. while (s[i]!= '\0' ) */
i++;
return (i);
}

int tamanho2 (char * s) /*com os ponteiros*/


{
int i=0;
while (*s) /* equiv. while (*s!= '\0 ') */
{i++;s++;}
return (i);
}
201
void main (void)
{
char ch[]="São Luis";
int a,b;
a=tamanho1(ch); /* a= 8*/
b=tamanho2(ch); /* b= 8*/

printf("O tamanho de %s é: %d\n",ch,a);


printf("O tamanho de %s é: %d\n",ch,b);
}

202
Exercícios 2: Criptografia Simples

1. Defina as funções Criptar e Decriptar que


codificam e decodificam um caractere aplicando o
seguinte algoritmo de criptografia:
- Um caractere é substituído por um outro
aplicando um shift de 3 (por exemplo a seria
substituído por D).
- Apenas os caracteres do alfabeto são codificados.
- Os maísculos passam a ser minúsculos e
vice-versa.
2. Defina a função main que leia e codifica ou decodifica
uma mensagem usando as funções definidas acima.

203
Tipos usuários

204
typedef
Podemos definir novos tipos usando o
typedef:
typedef <tipo> <sinônimo>
 exemplo:
typedef float largura;
typedef float comprimento;

largura l;
comprimento c=2.5;
l=c; //* ⇒ warning *//
205
typedef e struct

O typedef se usa mais com o tipo struct


(as estruturas)

206
Estruturas

207
Declaração: método 1

struct cliente {
int cpf;
char nome [20];
char endereco[60];
};

struct cliente c1,c2,c3;

208
Declaração: método 2
Podemos criar estruturas sem nome:
struct {
int cpf;
char nome [20];
char endereco[60];
} c1,c2;
Problema:
para criar uma outra variável de mesmo
tipo em outro lugar do programa é preciso
rescrever tudo.
209
Declaração: método 3

Podemos, no mesmo tempo, dar um nome


à estrutura e declarar as variáveis:
struct cliente {
int cpf;
char nome [20];
char endereco[60];
} c1,c2;

struct cliente c3;


210
Declaração: método 4

Definindo um tipo estrutura


typedef struct {
int cpf;
char nome [20];
char endereco[60];
} Cliente;

Cliente c1,c2={28400,"Maria","Rua
Liberdade, N. 140"};
211
Acesso aos Campos

Usando o operador de seleção: . (o ponto)

nome-estrutura.nome-do-campo

exemplo:

c2.nome retorna o campo nome da


estrutura c1: Maria
c2.nome=" Jeane" Muda o conteúdo de
nome
212
Exemplo

void imprimir_cliente (Cliente c)


{
printf("cpf: %d\n%s\n%s",
c.cpf,c.nome,c.endereco);
}

chamada da função:
imprimir_cliente (c2);

213
Combinação de Estruturas

 Podemos definir estruturas de


estruturas, estruturas de vetores,
vetores de estruturas....
 exemplo

typedef struct {int num; char * rua;


char * bairro; int cep} End;

typedef struct {int cpf; char * nome;


End endereco} Pessoa;
214
Vetores de Estruturas
 Podemos criar um vetor de estruturas (dois
métodos):
exemplo:
struct cliente vet[100]; /* seguindo o mét. 1*/
Cliente vet[100]; /*seguindo o mét. 4*/
declara um vetor de 100 clientes.

Referencia aos elementos:


Para referenciar o nome do ièsimo cliente do
vetor: vet[i].nome
215
Exercício

Escreve um programa C que:


– Declara um vetor de alunos: turma (um aluno é
definido pelo cpd, nome, três notas, média, e conceito)
– Preenche esse vetor (campos: cpd, nome, e notas).
– Preenche os campos média e conceito (bom se
média≤ 8, regular se ≥ 7 e ruim senão);
– Define a função que imprime todos o elementos
do vetor.
– Define a função que pesquisa um elemento do vetor
(pesquisa pelo nome, pelo cpd, ou pelo conceito).
216
Estruturas e Ponteiros

217
Estruturas dinâmicas

Usando as estruturas e os ponteiros


podemos entrar no mundo das estruturas
auto referenciais:

Listas, Arvores, Grafos, etc.

218
Ponteiros sobre uma Estrutura

typedef struct {
char * nome;
int idade;
} Pessoa ;
Pessoa pess;
/* pess é uma variável de tipo pessoa */
Pessoa *pp;
/* p é um ponteiro sobre uma pessoa */
pp=&pess;
/* p agora aponta sobre a pessoa pess */
219
Ponteiros sobre uma Estrutura
pess.idade=18;
pess.nome="joao";
Para acessar aos campos da estrutura apontada
por pp usamos o operador ->
pp->nome; /* equiv. a pess.nome */
pp->idade=25; /* ⇔ pess.idade=18 */
printf("%s",pp->nome);
// escreveria João
printf("%d",pp->idade);
// escreveria 25
220
Observação sobre o acesso
 Para acessar o campo nome de pessoa
apontada por um ponteiro pp:
*pp.nome
 Mas o operador de seleção . é mais prioritário do
que o operador de indireção * ⇒ isto é equiv. a:
*(pp.nome)
o que está errado (pois pp não é uma estrutura).
 Deveremos escrever:
(*pp).nome
Mas esta notação é um pouco complicada.
Por isso temos um novo operador -> (pp->nome)
221
Estrutura apontando sobre uma
outra Estrutura (1)

exemplo:
struct pessoa {
...
struct pessoa * next;
} ;

/* next é um ponteiro sobre


uma outra pessoa */

222
Estrutura apontando sobre uma
outra Estrutura (1)

exemplo:
typedef struct pessoa * PtrPessoa;
// PtrPessoa é um ponteiro sobre a estrutura pessoa
typedef struct pessoa {
char nome[30];
int idade;
PtrPessoa next;
} Pessoa;
/* next é um ponteiro sobre uma outra pessoa */
PtrPessoa pp;

223
Ilustração
CPF
nome
endereco
next
CPF
nome
endereco
next
224
Acessos aos elementos de uma
Estrutura Apontada

PtrPessoa p;
...
printf("Entra com o nome: ");
scanf("%s",&p->nome);

printf("Entra com a idade: ");


scanf("%d",&p->idade);

p->next=NULL;
225
Tamanho alocado à uma Estrutura

 O tamanho alocado a um tipo pode ser


conhecido usando o operador sizeof (a
medida é feita em número de bytes).
 sizeof pode ser usada de duas maneiras
(aplicação a um tipo ou à uma variável)
 exemplos:
int tamanho,i;
tamanho=sizeof i;
tamanho=sizeof (short i);
tamanho=sizeof (Pessoa);
226
Listas
 O fato de que uma estrutura pode apontar para
uma outra permite criar listas encadeadas.
 Uma lista encadeada é útil principalmente
quando nós não sabemos a priori (no momento da
compilação) o número de seus elementos, o que é
uma necessidade no caso do uso do tipo vetor.
 Contrariamente aos vetores, uma lista
encadeada é uma estrutura dinâmica: os
elementos são criados dinamicamente:
227
Alocação e Liberação de Memória

 Para poder criar listas é preciso poder


alocar memória dinamicamente:
 A alocação e liberação de memória é feita
usando as funções malloc e calloc:
– malloc para a alocação do espaço memória
para um elemento; e
– calloc para a alocação do espaço memória
para um conjunto de elementos.

228
malloc
malloc pega um único parâmetro que
é o tamanho (em bytes) do espaço memória
do elemento que nós desejamos criar, e
retorna um ponteiro sobre o elemento criado.
Obs. O tamanho do elemento é obtido usando
o operador sizeof.
exemplo:
Pessoa * p;
p=malloc(sizeof(Pessoa));

p=malloc(sizeof(char));
229
calloc
 calloc pega dois parâmetros:
- o número de elementos que
desejamos criar;
- e o tamanho de um elemento (em
bytes).
O objetivo é de alocar espaços para
vários
elementos de só uma vez.

230
calloc
 exemplo:
Pessoa * p;
int num_elementos=4;
...
p=calloc(num_elemenetos,
sizeof(Pessoa));
Podemos, depois, usar p[0],
p[1], ...
P[num_elementos - 1]

231
Observação

As funções malloc e calloc são definidas


no arquivo <alloc.h> da biblioteca.

Geralmente antes do malloc tem que


especificar o tipo da estrutura cujo o
ponteiro que será criado vai apontar:
pat=(PtrPessoa)malloc(sizeof(Pessoa));
p=(char *)malloc(sizeof(char));

232
null

Quando não tem mais espaço livre, as


funções malloc e calloc retornam
o valor null

Este valor pode servir também para


inicializar uma ponteiro exprimindo
que ele aponta sobre nada.

233
Liberação de Memória

Podemos liberar o espaço memória alocado para


uma variável dinâmica:
free(nome-do-ponteiro);
int * p;
...
p=(int *)malloc(sizeof(int));
// 2 bytes
...
free(p); /* liberação de p */

234
As Estruturas como Parâmetros

 C não permite passar uma estrutura como


parâmetro à uma função.

 Masnada proibe passar um ponteiro


sobre uma estrutura:

235
Exemplo

typedef struct {int dia,mes,ano;} data;


/* função de comparação entre duas datas */
int comp_datas(data * d1, data * d2)
{
if (d1->ano==d2->ano) && ...
return 1;
else return 0;
}

data d1,d2; ...


comp_datas (&d1,&d2);
236
Funções retornando Estruturas

Uma função não pode retornar uma estrutura


mas pode retornar um ponteiro sobre uma
estrutura:
/ * função retornando um ponteiro sobre uma data 01/01/2000 */

data * d2001 ()
{ data * p; p=malloc(sizeof(data));
p->dia=1; p->mes=1; p->ano=2000;
return (p);
}
data * d; d=d2001();
237
Observação

Pessoa * p;
p=malloc(...)
Normalmente o malloc não retorna um
ponteiro sobre uma estrutura. Isto pode
causar um warning.
Uma maneira correta de escrever é:
p=(Pessoa*)malloc(sizeof(Pessoa))
Uma outra solução é:
Pessoa * malloc();
238
Exercício

 Defina a função proximo_aniv que pega


em entrada a data de nascimento de uma
pessoa e retorna uma estrutura designando a
data de seu próximo aniversário.

239
data * proximo_anniv (data * p)
{
data * anniv;
anniv=(data*)malloc(sizeof(data));
anniv->ano=p->ano + 1;
anniv->mes=p->mes;
anniv->dia=p->dia;
return (anniv);
}
data d_nasci,d_anniv; ...
d_anniv=proximo_anniv(&d_nasci);
240
Criação de Listas
typedef struct {
char nome[20];
int peso;
Cabeça Pessoa * seguinte;
} Pessoa;

nome,peso nome, peso nome, peso


next next ... NULL

241
Pessoa * cabeca,pant,patu; char resp;
patu=(Pessoa *)malloc(sizeof(Pessoa));
patu->peso=30; ...
cabeca=patu;
puts("mais uma pessoa (s/n): "); resp=getch();

while (toupper(resp)!=’N’)
{
pant=patu;
patu=(Pessoa *)malloc(sizeof(Pessoa));
... /* leitura dos dados da nova pessoa */
pant->seguinte=patu; patu->seguinte=NULL:
puts("mais uma pessoa (s/n): ");resp=getch();
}
242
Modos FIFo Vs. LIFO

Existe dois modos para a criação das listas:


FIFO: First In First Out
LIFO: Last In First Out

Exercício:

1. Qual é o modo de criação de listas


aplicado no código da página anterior?
2. Cria uma lista usando o outro modo.

243
Listas
 Conjunto de elementos individualizados
em que cada um referencia um outro
elemento distinto como sucessor

244
Listas
Lista de Tarefas
Começo em 3
Item Próximo
1. Pagar as contas no banco 6
2. Comprar os livros na livraria 4
3. Deixar o carro no estacionamento 8
4. Pegar algumas fitas na videolocadora Final
5. Enviar carta pelo correio 1
6. Buscar as fotos reveladas 2
7. Autenticar documentos no cartório 5
8. Passa na banca de jornais 7
245
Inserção no meio da Lista

Antes

Farmácia (9)

Foto (6) Livraria (2)

246
Inserção no meio da Lista

1º passo

Farmácia (9)

Foto (6) Livraria (2)

247
Inserção no meio da Lista

2º passo

Farmácia (9)

Foto (6) Livraria (2)

248
Inserção no meio da Lista

 Inserir o item farmácia entre Foto e


Livraria
 1º Passo
– lista[9].prox = lista[6].prox;
 2º Passo
– Lista[6].prox = 9;
249
Inserção no fim da Lista

Antes

Farmácia (9)

Locadora (4) 0

250
Inserção no fim da Lista

1º passo

Farmácia (9)

Foto (6) 0

251
Inserção no fim da Lista

2º passo

Farmácia (9)

Foto (6) 0

252
Inserção no fim da Lista

 Inserir o item farmácia depois de


locadora
 1º Passo
– lista[9].prox = lista[4].prox;
 2º Passo
– Lista[4].prox = 9;
253
Inserção no início da Lista

Antes

Farmácia (9)

começo Estacionam (3)

254
Inserção no início da Lista

1º passo

Farmácia (9)

começo Estacionam (3)

255
Inserção no início da Lista

2º passo

Farmácia (9)

começo Estacionam (3)

256
Inserção no fim da Lista

 Inserir o item farmácia no inicio da


lista (antes do primeiro item)
 1º Passo
– lista[9].prox = comeco;
 2º Passo
– comeco = 9;
257
Inserção em uma lista
void insere(int novo, int antecessor)
{
lista[novo].prox = antecessor;
antecessor = novo;
}

258
Remoção em uma Lista

Antes

Farmácia (9)

começo Estacionam (3)

259
Remoção em uma Lista

1º passo

Farmácia (9)

começo Estacionam (3)

260
Remoção em uma lista
void remove(int velho, int antecess)
{
antecess = lista[velho].prox;
}

261
Filas (FIFO)
É também uma Lista
 Regra: todo o elemento que entra na
lista, entra no final e todo o elemento
que sai da lista, sai do início dela.
 FIFO (First In, First Out)

262
Filas (FIFO)
#define MAX 100
char *p[MAX]; int rpos=0, spos=0;
void armazena(char *c) {
if (spos==MAX) {
printf(“Lista Cheia\n”);
else
p[spos] = c;
spos++;
}
}
263
Filas (FIFO)
char *retira()
{
if (rpos==spos)
printf(“Sem eventos\n”);
return NULL;
else
rpos++;
return(p[rpos-1]);
}
264
PILHA (LIFO)
É o inverso de uma fila
 Regra: todo o elemento que entra na
lista, entra no final e todo o elemento
que sai da lista, sai do final dela.
 Último a entrar, primeiro a sair
 LIFO (Last In, First Out)
 push/pop (empilha/desempilha)

265
PILHA (LIFO) - vetor
int p[100], top=0;

void push(int i)
{
if (top>=100)
printf("pilha cheia\n");
else {
p[top] = i;
top++;
}
266
}
PILHA (LIFO) - vetor
int pop();
{
top--;
if (top<0) {
printf("pilha vazia");
return 0; }
else
return(p[top]);
}
267
PILHA (LIFO) - ponteiro
int *p, *top, *b;

p = (int*)malloc(MAX*sizeof(int));
top = p;
b = p+MAX-1;

268
PILHA (LIFO) - ponteiro
void push(int i)
{
if (p>b)
printf("pilha cheia\n");
else {
*p = i;
p++;
}
}
269
PILHA (LIFO) - ponteiro
int pop();
{
p--;
if (p<top) {
printf("pilha vazia");
return 0; }
else
return(*p);
}
270
Árvores Binárias
 Rapidezna pesquisa, inclusão e
exclusão (qdo ordenadas)
info

info info

0 0 0 0
271
Árvores - conceitos
 Raiz
 Nós
 Nó terminal
 Sub-árvore
 Altura

272
Transversalização
d

b f

a c e g

 Ordenada abcdefg
 Preordenada dbacfeg
 Pós-ordenada acbegfd
273
Uniões de Tipos

274
Objetivo
 Todas variáveis que nos vimos até agora
possuam um único tipo.
 As vezes é interessante atribuir vários tipos
a uma variável (uma mesma zona memória).
 Isto pode ser feito através do mecanismo
de uniões de tipos usando a palavra chave
union.
 Portanto, uma variável teria, a um dado
instante, um único tipo, porém pode mudar.
275
Declaração
Exemplo:
/* declaração de um tipo que una os inteiros e os reais */
typedef union {
int i;
float f;
} número;
numero n;
 Podemos então escrever:
n.i=20; /* para atribuir um inteiro */
n.f=3.14159; /* para atribuir um real */
276
Observação

A declaração é parecida às estruturas, mas


nas uniões somente um campo é atribuído
um valor.
O problema é que nos não podemos saber a
um dado instante do programa, qual é o tipo do
atual valor da variável.
 Porisso que na prática a união é associada a
um indicador de tipo (o tipo atual) e os dois são
combinados em uma estrutura:
277
Utilização Prática das Uniões

#define INTEIRO 0
#define REAL 1
typedef struct
{
int tipo_var;
union
{
int i;
float f;
} número;
} aritmética;
278
Utilização Prática das Uniões

/* declaração */
aritmética a1,a2;

a1.tipo_var=INTEIRO;
a1.tipo_var=REAL

a1.número.i=10;
a1.número.i=10;
279
Facilitar o acesso aos campos

O acesso aos campos da união dentro da


estrutura não é muito prático:
a1.número.i=10;

Isto pode ser resolvido usando as substituições e


o define:
#define I número.i;
#define R número.f;
Agora podemos escrever:
a1.I=10;ou a2.R=8.5;

280
Arquivos

281
Streams
 Stream de texto
– Sequência de caracteres
 Stream binária
– Sequência de bytes com uma
correspondência de 1 para 1 com aqueles
encontrados no dispositivo externo
282
Funções mais comuns
Nome Função

fopen() Abre um arquivo


fclose() Fecha um arquivo
putc(), Escreve um caractere em um
fputc() arquivo

getc(), Lê um caractere em um arquivo


fgetc()

#include <stdio.h>
283
Funções mais comuns
Nome Função
fseek() Posiciona o arquivo em um bytes
específico
fprintf() = printf – console
fscanf() = scanf – console
feof() Final de arquivo?
ferror() Ocorreu um erro?
rewind() Indicador de posição no início do arquivo

remove() Apaga um arquivo


284
Ponteiro de arquivo

 Ponteiro para informações que


definem várias coisas sobre o
arquivo:
– Nome, Status, Posição atual, ...
 Ponteiro do tipo FILE
FILE *fp;
285
Abrindo um arquivo

FILE *fopen(nomearq, modo);


 fopen devolve um ponteiro de
arquivo (em caso de erro o ponteiro
retornado é nulo)

286
Abrindo um arquivo

r  r+

w
 w+
a
 a+
 rb
 r+b
 wb
 w+b
 ab

 a+b
287
Abrindo um arquivo

file *fp;
if ((fp=fopen(“test”,”w”))==null)
{
printf(“não pode ser aberto”);
exit(1);
}
288
Fechando um arquivo

 Função fclose()
 Exite uma quantidade máxima de
arquivos que podem ser abertos 
FOPEN_MAX (exemplo: 20)

289
Lendo / Escrevendo
void carrega_arquivo(char s[10000],
char nome_arquivo[1000]) {
FILE *fp; int i=0;
if ((fp =
fopen(nome_arquivo,"r"))==NULL) {
printf("erro\n");
exit(1); }
for (i=0; i<9999 && s[i]!=EOF;i++)
s[i] = getc(fp);
fclose(fp); }
290