Buscar

Apostila C



Continue navegando


Prévia do material em texto

1
1. INTRODUÇÃO 
 
1.1. Características da linguagem 
 
• linguagem de médio nível; 
• linguagem estruturada; 
• versátil (aplicação em várias áreas); 
• portável; 
• código eficiente; 
• compilação condicional; 
• compilação separada (funções em vários arquivos); 
• linguagem voltada para o programador. 
 
1.2. Histórico 
 
• BCPL ⇒ B ⇒ C ⇒ C++ 
• C - Kernighan e Ritchie (K & R) - 1971 
• C ANSI (American National Standards Institute) - 1989 
 
1.3. Características de um programa em C 
 
• um programa em C é composto por pelo menos uma função, a função main; 
• todo programa em C começa a sua execução a partir da função main; 
• normalmente, um programa em C é uma coleção de chamadas de funções de pequeno 
tamanho; 
• toda função tem um tipo; 
• as palavras reservadas e funções da biblioteca do C são sempre escritas com letras 
minúsculas. 
 
1.4. Formato de um programa em C 
 
definições 
função_1 
função_2 
... 
função_n 
função_main 
 
Obs.: A ordem das funções dentro de um programa pode ser qualquer. 
 
1.5. Exemplo de um programa 
 
# include <stdio.h> /* instrução para o pré-processador */ 
void main ( ) /* início da função main */ 
{ /* início de bloco */ 
 int i ; /* declaração de variável */ 
 i = 10 ; /* inicializa */ 
 printf ( " i = %d \ n", i ) ; /* chama função de impressão */ 
 } /* fim de bloco */ 
 
 2
Obs.: 
• toda instrução termina por ';' 
• o protótipo da função printf está em stdio.h (standard IO) 
 
1.6. O conceito de protótipo de uma função 
 
• Protótipo em C 
• Protótipo em C ANSI 
 
2. IDENTIFICADORES 
 
São usados em nomes de funções e variáveis. 
 
ANSI: 31 caracteres para variáveis locais 
6 para funções e variáveis globais 
 
A maioria dos compiladores reconhece 31 caracteres tanto para variáveis quanto para 
funções. 
 
• devem começar por letra ou ' _ '; 
• normalmente se usam letras minúsculas; 
• não podem ser iguais às palavras reservadas; 
• minúsculas diferentes de maiúsculas: 
ABC ≠ Abc ≠ ABc ≠ abc 
 
3. TIPOS DE DADOS PARA VARIÁVEIS E FUNÇÕES 
 
3.1. Standard (já definidos na linguagem) 
 
Tipo Nome Tamanho Faixa 
 em Bytes em Bits 
caracter char 1 8 0 a 255 
inteiro int 2 16 -32768 a 32767 
 int 4 32 -2.147.483.648 a 2.147.483.647
real float 4 32 6 dígitos 
 double 8 64 12 dígitos 
nada void 0 0 sem valor 
 
Obs.: O tipo inteiro possui o tamanho da palavra da CPU. 
 
3.2. Modificadores de tipo 
 
Alteram o significado do tipo standard. 
 
• signed (com sinal) 
• unsigned (sem sinal) 
• long (passa inteiro de 2 bytes para 4 bytes) 
• short (passa inteiro de 4 bytes para 2 bytes) 
 
 
 3
Se tipo int possuir 2 bytes: 
long int = long ⇒ 4 bytes 
short int = int ⇒ 2 bytes 
 
Ex.: 
 signed char ⇒ -128 a 127 
 unsigned int ⇒ 0 a 65535 
 
4. DECLARACÃO DE VARIÁVEIS 
 
Evidentemente, as variáveis devem ser declaradas antes de serem usadas. 
 
Formato: <tipo> <lista de variáveis>; 
 
Ex.: 
int a, b, c; 
float soma; 
double somatorio, raiz_quadrada; 
char sim, nao; 
 
As variáveis podem ser declaradas dentro de qualquer bloco: 
{ 
int x, y; 
comandos; 
} 
 
As variáveis podem ser locais, globais e parâmetros formais 
 
4.1. Variáveis locais 
 
São aquelas declaradas dentro de um bloco e só são reconhecidas dentro deste bloco. 
 
Exemplo 1 Exemplo 2 
# include <stdio.h> 
void main ( ) 
{ 
int i, j ; 
i = 2 ; 
j = 2 * i ; 
printf ( "0 dobro de %d = %d \ n", i, j ); 
} 
 
No exemplo acima, 'i' e 'j' são variáveis locais da 
função "main". 
void f1 ( ) 
{ 
int x ; 
x = 5 ; 
. . . 
if ( a > 10 ) 
{ 
int x, y ; 
x = 20 ; 
y = 40 ; 
. . . 
} 
 } 
Neste exemplo, a variável 'x' tem o 
valor 5 quando sair do bloco mais 
interno. 
 
 4
4.2. Variáveis globais 
 
São reconhecidas por todas as funções de um programa ou de um arquivo. 
 
Ex.: 
# include <stdio.h> 
int cont; 
void f ( void ) ; 
void main ( ) 
 { 
cont = 100 ; 
printf ("cont em main = %d \ n", cont ) ; 
cont = cont + 1 ; 
f ( ) ; 
} 
 
void f ( ) 
 { 
 printf ( "cont em f = %d \ n", cont ) ; 
 } 
 
4.3. Parâmetros formais 
 
São os parâmetros da declaração função e considerados como variáveis locais desta 
função. 
 
Obs.: O C só passa parâmetros por valor, isto é, o que é passado para a função é uma 
cópia do conteúdo dos parâmetros efetivos (que aparecem na chamada para 
execução da função). 
 
Ex.: 
int soma ( int a, int b ) 
{ 
return ( a + b ); 
} 
 
No exemplo, 'a' e 'b' são parâmetros formais e a função "soma" poderia ser assim 
chamada: j = soma ( 2, 3 ) ; 
x = soma ( a, b ) ; 
 
4.4. Modificadores de classe de armazenamento 
 
Indicam como as variáveis devem ser armazenadas. 
Exemplo do mapa da memória de um programa em C em um equipamento genérico: 
 
Área de Código 
Área de Variáveis 
Estáticas e Globais 
Stack 
Heap 
 5
A área de stack possue entre 2 a 4 Kbytes e armazena as variáveis locais. 
Heap é o que sobra da memória e é onde são feitas as alocações dinâmicas. 
 
4.4.1. Variáveis automáticas – auto 
 
São as variáveis locais declaradas normalmente. 
 
Ex.: 
 int x ; <=> auto int x ; 
 
A palavra auto não é necessária. 
 
4.4.2. Variáveis estáticas - static 
 
As variáveis estáticas são armazenadas na área de variáveis globais e por isto, não perde 
o seu valor dentro da função ou arquivo. 
 
Variável estática (ou global) é inicializada com 0 (zero). 
 
Este tipo de modificador é mais empregado para variáveis de grandes dimensões 
(vetores, matrizes, strings etc.). 
 
Quando uma função é definida como static, ela só pode ser ativada por uma função que 
esteja no mesmo arquivo. 
 
4.4.3. Variáveis externas - extern 
 
São as variáveis globais definidas em arquivos diferentes 
 
Ex.: 
arquivo 1: 
int x, y ; 
void main ( ) 
 { 
 y = 30 ; 
 x = 10 ; 
 f ( ) ; 
 . . . 
 } 
 
arquivo 2: 
 . . . 
 extern int x, y ; 
 void f ( ) 
 { 
 . . . 
 printf ( " %d %d \ n ", x, y ) ; 
 . . . 
 } 
 
 
4.4.4. Variáveis register 
 6
 
Neste caso, a variável é colocada em um dos registradores da CPU. 
 
Ex.: 
register int i, j ; 
 
Deve ser bem escolhida, isto é, deve ser mais usada em casos nos quais a velocidade é 
importante, como, por exemplo, em variáveis que controlam loops. 
 
5. FUNCÕES printf e scanf 
 
Ao contrário de outras linguagens, no C as operaçães de leitura e impressão, tanto na tela 
quanto em arquivos, é totalmente feita por funções. Os protótipos destas funções se 
encontram no arquivo stdio.h, o qual deve sempre ser incluído quando forem usadas 
funções de IO (entrada e saída). 
 
5.1. printf 
 
Principal função de impressão no vídeo. 
 
Formato Geral: int printf ( formato, argumentos ) ; 
 
A função printf retoma o número de caracteres impressos. 
 
Obs.: 
• Em C, o valor de retorno de uma função pode ser ignorado. 
• Em C, as funções podem ter um número variável de parâmetros. 
 
Ex.: printf ( " \ n FIM \ n " ) ; 
 
Principais formatos de impressão: 
• %d - inteiro 
• %c - caracter 
• %f - float 
• %ld - long int 
• %lf - double 
• %s - string 
• %e - notação científica 
 
A saída pode ser formatada através do tamanho do campo: 
• %3d - inteiro com 3 dígitos 
• %-3d - - inteiro com 3 dígitos ajustados à esquerda do campo• %7.2f - float com dois dígitos após o ponto decimal 
 
5.2. scanf 
 
Principal função de leitura do teclado 
 
Formato: int scanf ( formato, parâmetros ) ; 
 
Retoma o número de variáveis lidas corretamente ou EOF em caso de erro grave. 
 7
 
Principais formatos: 
• %d - inteiro 
• %c - caracter 
• %f - float 
• %ld - long int 
• %lf - double 
• %s - string 
 
Para a função scanf, o branco é considerado delimitador. 
 
Ex: 
 scanf ( " %d %d ", &a, &b ) ; 
 scanf ( " %d , %d ", &a, &b ) ; 
 
5.3. Exercício: Ler dois números e imprimir a soma. 
 
# include < stdio.h > 
void main ( ) 
{ 
int a, b, c ; 
printf ( " Entre 2 numeros: " ) ; 
scanf ( " %d %d ", &a, &b ) ; 
c = a + b ; 
printf ( " Soma = %d \n ", c ) ; 
/* printf ( " Soma = %d \n ", a + b ) ; */ 
} 
 
6. INSTRUÇÕES DE CONTROLE 
 
6.1. while 
 
Formato Geral: while (condição) 
 bloco; 
 
Executa o bloco enquanto a condição for verdadeira. 
 
Obs.: 
• Em C, verdadeiro é tudo o que for diferente de zero. 
• Se o bloco possuir mais de uma instrução é necessário { }. 
 
 
 
 
 
 
 
 
 
Ex.: Imprimir a soma dos números de 1 a 5 com while 
 
 8
Solução 1 Solução 2: 
# include < stdio.h > 
void main ( ) 
{ 
int i, soma ; 
i = 1 ; 
soma = 0 ; 
while ( i <= 5 ) 
 { 
soma = soma + i ; 
i = i + 1 ; 
 } 
printf ( " \n Soma = %d \n ", soma ) ; 
} 
 
# include < stdio.h > 
void main ( ) 
{ 
int i = 1, soma = 0 ; 
while ( i <= 5 ) 
 { 
soma += i ; 
i ++ ; 
 } 
printf ( " \n Soma = %d \n ", soma ) ; 
} 
 
 
Obs.: 
• Incremento usando pré-operador (++ i ⇒ incrementa antes de usar a variável i) ou pós-
operador (i ++ ⇒ incrementa depois de usar a variável i): 
supondo a= 1: para b = ++ a ; ⇒ b = 2 e a = 2 
para b = a ++ ; ⇒ b = 1 e a = 2 
• Forma reduzida de var = var op exp ; ⇒ var op = exp ; 
x = x + 5 ; ⇒ x += 5 ; 
x = x / ( a + b ) ; ⇒ x /= a + b ; 
 
6.2. for 
 
Formato Geral: for ( inicialização; condição; incremento ) 
bloco; 
 
Exemplo: Imprimir os números de 1 a 10 
# include < stdio.h > 
void main ( ) 
 { 
int i ; 
for ( i = 1; i <= 10; i ++ ) 
printf ( " %d \n ", i ) ; 
 } 
 
Obs.: Todos os campos do for são opcionais. 
 
# include < stdio.h > 
void main ( ) 
 { 
int i = 1 ; 
for ( ; i <= 10 ; ) 
printf ( " %d \n ", i ++ ) ; 
 } 
Exercício: Calcular o fatorial de um número inteiro e não negativo lido usando for. 
Solução 1: 
# include < stdio.h > 
 9
void main ( ) 
 { 
int n, i, fator = 1 ; 
printf ( " Entre o numero: " ) ; 
scanf (" %d ", &n ) ; 
for ( i = 1; i <= n; i ++ ) /* poderia ser ainda: */ 
 fator *= i ; /* for (i = 1, fator = 1; i <= n; fator *= i ++ ) ; */ 
printf ( " Fatorial de %d = %d \n ", n, fator ) ; 
} 
 
Solução 2: 
# include < stdio.h > 
void main ( ) 
{ 
int n, i, fat ; 
printf ( " Entre o numero: " ) ; 
scanf ( " %d ", &n ) ; 
i = n ; 
for ( fat = 1; n > 1; n -- ) 
fat *= n ; 
printf ( " Fatorial de %d = %d \n ", i, fat ) ; 
 } 
 
6.3. do while 
 
Formato Geral: do 
bloco; 
while (condição); 
 
Repete o bloco enquanto a condição for verdadeira. 
 
Ex.: Imprimir os números de 1 a 10 
# include < stdio.h > 
void main ( ) 
 { 
int i = 1; 
do 
 { 
 printf (" %d \n ", i ); 
 i ++ ; 
 } 
while (i <= 10); 
 } 
 
 
 
 
6.4. if e derivados 
 
Formatos: 
 10
if (teste) 
 bloco; 
 
if (teste) 
 bloco1; 
else 
 bloco2; 
 
if (teste1) 
 bloco1; 
else if (teste2) 
 bloco2; 
else if (teste3) 
 bloco3; 
else 
 bloco4; 
 
Exercício: Ler 2 números e indicar o maior 
# include < stdio.h > 
void main ( ) 
{ 
int a, b ; 
printf ( " Entre 2 numeros: " ) ; 
scanf ( " %d %d ", &a, &b ) ; 
if ( a = = b ) /* teste de igualdade é com = = */ 
 printf ( " Sao iguais \n " ) ; 
else if ( a > b ) 
 printf ( " O primeiro é maior \n " ) ; 
else 
 printf ( " O segundo é maior \n " ) ; 
} 
 
Exercício: Ler uma série de números reais e indicar o maior e o menor. 
# include < stdio.h > 
void main ( ) 
{ 
float min, max, x ; 
printf ( " Entre os numeros: \n " ) ; 
scanf ( " %f ", &x ) ; 
min = max = x ; /* atribuição múltipla */ 
while ( scanf ( " %f ", &x ) = = 1 ) 
 if ( min > x ) 
 min = x ; 
 else if ( max < x ) 
 max = x ; 
printf ( " O maior é: %f \n ", max ) ; 
printf ( " O menor é: %f \n ", min ) ; 
} 
 
6.5. if com ? 
 
Útil para pequenos if’s. 
 
 
 
if ( a = = 10 ) Poderia ser: b = ( a = = 10 ) ? 20 : 30 ; 
b = 20; ⇓ ⇓ 
else V F 
 11
b = 30; 
 
6.6. switch 
 
 Substitui grandes if’s. 
 
switch ( exp ) 
 { 
case ctel : 
 bloco l ; 
 break ; 
case cte2 : 
 bloco 2 ; 
 break ; 
case cte3 : 
 bloco 3 ; 
 break ; 
. . . 
. . . 
case cten : 
 bloco n ; 
 break ; 
default : 
 bloco ; 
 break ; /* não é necessário break aqui */ 
 } 
Obs.: A expressão 'exp' deve fornecer resultado inteiro. 
 
6.7. continue 
 
Passa o controle para o próximo incremento em um for, ignorando as instruções restantes 
dentro do bloco do for. 
 
. . . 
for ( i = 0 ; i < 10 ; i ++ ) 
{ 
if ( i = = 0 ) 
 continue ; /* evita divisão por zero */ 
printf ( " %f \n ", ( float ) 10 / i ) ; 
 } 
. . . 
 
Obs.: O cast ( float ) 10 / i converte para real. 
 Ex.: ( float ) 10 / 3 ⇒ 3.333... 
 ( float ) (10 / 3) ⇒ 3.0 
 
 
6.8. break
 
Interrompe a execução de um for, while, do . . . while ou switch. O fluxo de execução do 
programa é desviado para primeira instrução fora do bloco que possui o break. 
 12
 
Ex.: . . . 
while ( 1 ) /* loop infinito */ 
{ 
scanf ( " %d ", &a ) ; 
if ( a != 0 ) 
processa ( ) ; 
else 
break; 
} 
. . . 
 
Obs.: Deve-se usar um break por loop, quando embutidos. 
 
6.9. goto label 
 
Desvia o fluxo de execução do programa para um label em outro ponto do programa. 
 
. . . 
goto Label_1 ; 
. . . 
Label_l : 
. . . 
 
Obs.: Para label sem instruções deve-se usar “ ; ” no final: Erro_Fatal : ; 
 
6.10. Exercícios 
 
6.10.1. Fazer um programa que simule uma calculadora com as quatro operações e ainda, teste 
divisão por zero e sinal de operação inválido. 
 
# define FALSE 0 /* definição de constantes */ 
# define TRUE 1 
 
# include < stdio.h > 
void main ( ) 
{ 
float vl, v2, res ; 
char op ; 
int erro = FALSE ; /* usa int como variável lógica */ 
 
printf ( " Entre com a expressão : \n " ) ; 
scanf ( " %f %c %f ", &vl, &op, &v2 ) ; /* le a expressão */ 
 
 
 
 
switch ( op ) 
{ 
case ‘+’ : 
res = vl + v2 ; 
 13
break ; 
 
case ‘-’ : 
res, = vl - v2 ;break ; 
 
case ‘*’ : 
res = vl * v2 ; 
break ; 
 
case ‘/ ’ : 
if ( v2 = = 0.0 ) 
{ 
printf ( " \n Erro: Divisão por zero \n " ) ; 
erro = TRUE ; 
} 
else 
res = vl / v2 ; 
break ; 
 
default : 
erro = TRUE ; 
printf ( " \n Sinal inválido \n " ) ; 
break ; /* este break não é necessário */ 
} 
 
if ( !erro ) /* não precisa fazer erro = = TRUE */ 
printf ( " \n O resultado é : %10.4f \n ", res ) ; 
} 
 
6.10.2. Fazer um programa que leia um valor n inteiro e, em seguida, leia n valores reais, 
calcule e imprima o quadrado de cada um. 
# include < stdio.h > 
void main ( ) 
{ 
float x ; 
int n ; 
printf ( " Numero de valores a serem lidos : " ) ; 
scanf ( " %d ", &n ) ; 
while ( n-- ) 
{ 
printf ( " Valor : " ) ; 
scanf ( " %f ", &x ) ; 
printf ( " Quadrado de %f = %f \n ", x, x * x ) ; 
} 
} 
Obs.: Redirecionainento para entrada e/ou saída, usando arquivos DOS ou UNIX: na 
chamada do programa indicar um arquivo de entrada e/ou um de saída. Por 
exemplo, no DOS: 
 C:\> nome_do_programa <arq_entrada >arq_saida 
 14
 
7. FUNÇÕES 
 
Formato Geral: 
tipo nome_função ( parâmetros ) 
{ 
declaração de variáveis locais ; 
corpo da função ; 
{ 
 
• tipo é o tipo do valor que a função retorna; 
• se uma função não retoma nada, o seu tipo é void; 
• os parâmetros são sempre passados por valor, isto é, uma cópia do valor do parâmetro 
efetivo é passado para a função; 
• para arrays (vetores e matrizes) é passado o valor do endereço da sua primeira posição; 
• é aconselhável usar sempre o protótipo da função; 
• variáveis char são transformadas em int e variáveis float são transformadas em double. 
 
Obs.: O formato da função varia entre o padrão K & R e o ANSI, sendo que o padrão 
K & R pode ser usado no ANSI. 
 
Formato K & R Formato ANSI 
int soma ( a, b ) 
int a, b ; 
{ 
return ( a + b ) ; 
} 
int soma ( int a, int b ) 
{ 
return ( a + b ) ; 
{ 
 
O valor retornado por uma função pode ser ou não utilizado, como por exemplo na 
função printf ( ). 
 
Pode-se retornar de qualquer ponto do corpo da função, bastando para isto usar a 
instrução return. 
 
Exercício: Fazer um programa para ler um número e imprimir o seu fatorial utilizando 
uma função para leitura do número, uma para o cálculo do fatorial e uma para impressão 
do resultado. 
 
# include < stdio.h > 
long fatorial ( long ) ; 
long leval ( void ) ; 
void printfat ( long ) ; 
 
 
 
 
void main ( ) 
{ 
long fat, val ; 
val = leval ( ) ; 
 15
fat = fatorial ( val ) ; 
printfat ( fat ) ; 
} 
 
long leval ( ) 
{ 
long x ; 
printf ( " Entre o valor a ser calculado : " ) ; 
scanf ( " %ld ", &x ) ; 
return ( x ) ; 
} 
 
void printfat ( long y ) 
{ 
printf ( " Fatorial = %ld \n ", y ) ; 
} 
 
long fatorial ( long a ) 
{ 
long fator = 1 ; 
long i ; 
for ( i = 1; i <= a; i++ ) 
fator *= i ; 
return ( fator ) ; 
} 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8. OPERADORES 
 
Um operador é um caracter ou grupo de caracteres que causará uma manipulação 
matemática ou lógica. 
 
 16
Em C, os operadores podem ser aritméticos, relacionais, lógicos e entre bits. 
 
8.1. aritméticos 
+ => adição 
- => subtração e menos unário
* => multiplicação 
/ => divisão 
% => resto divisão 
-- => decremento 
++ => incremento 
+= => soma 
-= => subtração 
*= => multiplicação 
/= => divisão 
 
8.2. lógicos ou boleanos 
&& => e 
|| => ou 
! => não 
 
8.3. relacionais 
> => maior 
< => menor 
= = => igual 
!= => diferente 
>= => maior ou igual 
<= => menor ou igual 
8.4. entre bits 
& => e 
| => ou 
∧ => ou exclusivo 
~ => complemento (NOT) 
>> => deslocamento para direita 
<< => deslocamento para esquerda 
 
8.5. outros operadores 
* => retorna o valor contido em um endereço 
& => retoma o endereço de uma variável 
sizeof => retoma o tamanho de uma variável 
ou de um tipo 
? => avalia se uma expressão é verdadeira 
, => equivalente a “faça isto e isto e isto . . .” 
. => membro de estrutura ou união 
→ => ponteiro para membro de estrutura ou 
união 
( type ) => cast - altera tipo do dado ou da 
expressão 
 
 
8.6. precedência (da maior para a menor) 
 
( ) [ ] -> 
! ~ ++ - - (type) * & sizeof 
* / % 
+ - 
<< >> 
< <= > >= 
= = != 
& 
∧ 
| 
&& 
|| 
? 
= += -= *= /= 
, 
 
9. ARRAYS UNIDIMENSIONAIS ( VETORES ) 
 
Definição de vetores unidirnensionais: 
 17
<tipo> <nome> [ <tam> ] ; 
 
Exemplo: 
int vet [ 5 ] ; ⇒ define vet como um vetor inteiro de 5 posições, sendo que os 
índices variam de 0 (zero) a 4, isto é, a posição do primeiro 
elemento é vet [ 0 ] e do último é vet [ 4 ]. 
 
Exemplos de acesso: 
int vet [ 5 ] ; 
 . . . 
vet [ 0 ] = 10 ; 
vet [ 1 ] = 20 ; 
vet [ 2 ] = 40 ; 
 . . . 
 for ( i = 0; i < 5; i++ ) 
vet [ i ] = 0 ; /* zera o vetor */ 
 . . . 
 
Inicialização na definição: 
 
para vetor local: 
static int vet [ ] = { 1, 3, 5 } ; ⇒ assume que o vetor vet possui 3 elementos 
 
static float vet [ 10 ] ; ⇒ assume que o vetor vet tem 10 elementos, os quais 
serão inicializados com zero (0) 
 
static int vet [ 5 ] = { 1, 2, 3 } ; ⇒ assume que o vetor vet tem 5 elementos, 
sendo que o quarto e o quinto serão 
inicializados com zero (0) 
 
para vetor global: 
int vet [ ] = { 10, 20, 40, 50 } ; assume que o vetor vet tem 4 elementos 
 
Obs.: 
• Quando um vetor 'extern' ou 'static' for inicializado sem dimensão, a dimensão será a 
do número de valores da inicialização. 
• Caso seja fornecida a dimensão e faltem elementos na inicialização, os elementos não 
inicializados passam a valer zero. 
 
Exercício: Fazer uma programa que leia o número de componentes (n <= 100) e os n 
componentes de um vetor de double e retome o seu maior elemento. 
 
# include < stdio.h > 
double maxval ( double [ ], int ) ; /* Os protótipos são obrigatórios quando */ 
void levet ( double [ ], int ) ; /* o corpo da função vem depois de sua */ 
 /* chamada */ 
void main ( ) 
{ 
double vet [ 100 ] ; 
int i, n ; 
 18
printf ( " Numero de elementos do vetor : " ) ; 
scanf ( " %d ", &n ) ; 
levet ( vet, n ) ; 
printf ( " Maior valor = %lf ", maxval ( vet, n ) ) ; 
} 
 
void levet ( double v [ ], int n ) 
{ /* double v [ ] ; não precisa dar a dimensão */ 
int i ; 
double aux ; 
printf ( " Entre os elementos do vetor: " ) ; 
for ( i = 0; i < n; i++ ) 
{ 
scanf ( " %lf ", &aux ) ; 
v [ i ] = aux ; /* poderia ter lido v [ i ] diretamente na scanf */ 
} 
} 
 
double maxval ( double v [ ], int n ) 
{ 
int i ; 
double maior ; 
maior = v [ 0 ] ; /* maior começa como sendo o primeiro */ 
for ( i = 1; i < n; i++ ) 
if ( v [ i ] > maior ) 
maior = v [ i ] ; 
return ( maior ) ; 
} 
 
9.1. Strings 
 
São arrays de caracteres. 
 
Obs.: Em C, toda string acaba com o caracter '\0'. 
 
Definição de um vetor de caracteres: char str [ 7 ] ; 
 
A declaração acima, define str como sendo um vetor de 6 caracteres mais o 
caracter de final de string. 
 
 
 
 
 
 
 
 
Exemplo: 
char str [ 7 ] ; 
str [ 0 ] = 'C' ; 
str [ l ] = 'a' ; 
str [ 2 ] = 'r' ;19
str [ 3 ] = '1' ; 
str [ 4 ] = 'o' ; 
str [ 5 ] = 's' ; 
str [ 6 ] = '\0' ; 
 
Na memória ficaria: 
C 0 
a 1 
r 2 
l 3 
o 4 
s 5 
\0 6 
 
Pode-se inicializar uma string de duas formas: 
 
1. Como um vetor: 
static char str [ ] = { 'C', 'a', 'r', '1', 'o', 's', '\0' } ; 
assume 6 caracteres mais '\0' = 7 
2. Deixar que o compilador inicialize: 
static char str [ ] = "Carlos" ; 
 
Obs.: Não é possível a atribuição direta: 
char str [ 7 ] ; 
str = "Carlos" ; /* Erro: não vale a atribuição */ 
 
Ex.: Fazer um programa para ler um nome e dar um bom dia para o dono do nome. 
 
Solução 1: 
# include < stdio.h > 
# define TAM 100 
void main ( ) 
{ 
char linha [ TAM ] ; 
int c, i ; 
printf ( " \n Qual é o seu nome ? " ) ; 
for ( i = 0; ( c = getchar ( ) ) != '\n'; i++ ) 
linha [ i ] = c ; 
linha [ i ] = '\0' ; 
printf ( " Tenha um bom dia " ) ; 
for ( i = 0; linha [ i ] != '\0'; i++ ) 
putchar ( linha [ i ] ) ; 
putchar ( '\n' ) ; 
} 
Obs.: A função getchar retorna um caracter do teclado e a função putchar coloca 
um caracter no vídeo. Estas funções, bem como as outras de IO podem ser 
redirecionadas. Em caso de erro, a função getchar retorna EOF. 
 
Solução 2: 
# include < stdio.h > 
 20
# define TAM 100 
void main ( ) 
{ 
char linha [ TAM ] ; 
printf ( " \n Qual é o seu nome ? " ) ; 
gets ( linha ) ; 
printf ( " \n Tenha um bom dia " ) ; 
puts ( linha ) ; 
} 
 
Obs.: 
• A função gets tem por finalidade ler uma string e a puts imprimir uma string. 
• A função puts fornece automaticamente uma mudança de linha após a 
impressão. 
• Não é aconselhável usar getchar ou gets após o scanf, pois ocorrerá problema 
na leitura do Enter ('\n' ). 
• Para ler um número inteiro com a gets, pode-se usar: 
. . . 
char buf [10] ; 
int n ; 
printf ( " Valor de n = " ) ; 
gets ( buf ) ; 
n = atoi ( buf ) ; /* atoi transforma um número de string para int 
*/ 
. . . 
 
Formatos: 
char* gets ( char* buf ) ; 
retorna ponteiro para a string lida ou NULL em caso de erro. 
 
int puts ( char* buf ) ; 
retoma '\n' se imprimiu corretamente ou EOF em caso de erro. 
 
Obs.: Para atribuição de strings, deve-se usar a função strcpy, que possui o formato: 
char* strcpy ( char* destino, char* origem ); 
 
Exemplo: 
strcpy ( str, "casa" ); 
str passa a conter "casa" 
 
Obs.: Para saber o tamanho de uma string, usa-se a função strlen que possui o formato: 
int strlen ( char* st ) ; 
 
 
 
Exercício: Construir a função strlen. 
 
Solução 1: 
int strlen ( char cadeia [ ] ) 
{ 
 21
int len = 0, i = 0 ; 
while ( cadeia [ i ] != '\0' ) 
{ 
++len ; 
++i ; 
} 
return ( len ) ; 
} 
Solução 2: 
int strlen ( char cadeia [ ] ) 
{ 
int i ; 
for ( i = 0; cadeia [ i ] != '\0'; i++ ) 
; /* bloco nulo */ 
return ( i ) ; 
} 
 
Exercício: Construir a função stringcpy 
 
Solução 1: 
 
void stringcpy ( char [ ], char [ ] ) ; 
 
void stringcpy ( char sl [ ], char s2 [ ] ) 
{ 
int i ; 
for ( i = 0; s2 [ i ] != '\0'; i++ ) 
sl [ i ] = s2 [ i ] ; 
sl [ i ] = '\0' ; 
} 
 
Solução 2: 
 
void stringcpy ( char sl [ ], char s2 [ ] ) 
{ 
int i = 0 ; 
while ( ( sl [ i ] = s2 [ i ] ) != '\0' ) 
i++ ; 
} 
 
 
 
 
 
 
Exercício: Desenvolver um programa para ler um nome e imprimir o número de 
caracteres deste nome (usar strlen). 
 
void main ( ) 
{ 
int tam ; 
 22
char nome [100] ; 
printf ( " Entre seu nome " ) ; 
gets ( nome ) ; 
tam = strlen ( nome ) ; 
printf ( " Seu nome tem %d caracteres \n ", tam ) ; 
} 
 
10. ARRAYS BIDIMENSIONAIS (MATRIZES BIDIMENSIONAIS) 
 
Formato: <tipo> <nome> [ <d1> ] [ <d2> ] ; 
 
onde: d1 ⇒ número de linhas (varia de 0 a d1 - 1) 
 d2 ⇒ número de colunas (varia de 0 a d2 - 1) 
 
Exemplo: 
int b [3] [5] ; ⇒ 3 linhas e 5 colunas 
 
Inicialização na declaração: 
static int b [ ] [3] = 
{ 
1, 1, 1, 
2, 2, 2, 
3, 3, 3 
}; 
static int b [ ] [3] = 
{ 
{l, 1, 1}, 
{2, 2, 2}, 
{3, 3, 3} 
} 
 
Obs.: A inicialização também pode ser feita de forma esparsa. Por exemplo: 
 
static int b [3] [3] = 
{ 
{1, 1}, 
{1} 
}; 
 
A matriz gerada pela inicialização acima será: 
 
 1 1 0 
1 0 0 
0 0 0 
 
Deve-se notar que para este tipo de inicialização é necessário que se 
forneça o número de linhas e de colunas. 
 
Obs.: O armazenamento em C é feito por linhas. 
 
Passagem de um array bidimensional para uma função: ao passar uma matriz 
bidimensional, a única dimensão fixa é o número de colunas (ao contrário do 
FORTRAN que fixa o número de linhas). 
Exemplo: 
Dado: 
void main ( ) 
{ 
 23
int mat [10] [20] ; 
. . . 
f ( mat, 10, 20 ) ; 
. . . 
} 
 
A função f ( ) poderia receber a matriz de duas formas: 
 
void f ( int mat [10] [20], int lin, int col ) ou 
void f ( int mat [ ][20], int lin, int col ) 
{ 
. . . 
} 
 
Obs.: Uma matriz bidimensional passada como parâmetro tem que apresentar, 
obrigatoriamente, o número máximo de colunas. 
Exemplo para zerar uma matriz: 
 
void zera ( float mat [ ] [20], int, int ) ; /* protótipo */ 
 
void main ( ) 
{ 
float mat [10] [20] ; 
zera ( mat, 10, 20 ) ; 
} 
void zera ( float mat [ ] [20], int lin, int col ) 
{ 
register int i, j ; 
for ( i = 0; i < lin; i++ ) 
for ( j = 0; j < col; j++ ) 
mat [ i ] [ j ] = 0.0 ; 
} 
 
Exercício: Desenvolver uma função para retornar o menor elemento da diagonal principal 
de uma matriz 15 x 15 de elementos do tipo double. 
double menordp ( double mat [ ] [15] ) 
{ 
int i, menor = mat [0] [0]; 
for ( i = 1; i < 15; i++ ) 
if ( mat [ i ] [ i ] < menor ) 
menor = mat [ i ] [ i ]; 
return ( menor ); 
} 
11. PRIMEIRA LISTA DE EXERCÍCIOS 
 
1. Desenvolver um programa que informe o número de vezes que cada dígito aparece em um 
arquivo texto, usando redirecionamento. 
 
2. Desenvolver um programa para ler um valor inteiro n, ímpar e maior ou igual a 1 (fazer as 
verificações necessárias), calcular e apresentar o valor de π (pi), utilizando os n primeiros 
termos da série abaixo. 
 24
pi = 4 * ( 1 / 1 - 1 / 3 + 1 / 5 - 1 / 7 + . . . + 1 / n ) 
 
3. Desenvolver um programa para ler um valor inteiro n (1 ≤ n ≤ 100) e os n elementos de um 
vetor v de double, calcular e apresentar: 
a) a média aritmética dos elementos múltiplos de 3 (três) 
b) percentual de elementos negativos 
c) produtório dos elementos pares e positivos 
d) índices dos elementos maiores que a média aritmética do vetor. 
Obs.: 
• Fazer todas as verificações necessárias ( validade de n, divisão por zero etc. ). 
• Usar modularidade 
 
4. Desenvolver um programa para ler um valor em binário com no máximo 16 algarismos e 
apresentá-lo nas bases 10 e 16. 
 
5. Desenvolver um programa para ler um arquivo texto e gerar um arquivo novo, trocando as 
letras maiúsculas por minúsculas, minúsculas por maiúsculas e os dígitos por sua forma em 
extenso. Usar redirecionamento. 
 
6. Desenvolver um programa para ler um arquivo texto (usando redirecionamento) e informar 
o número de vezes que cada letra aparece, distinguindo maiúsculas de minúsculas. 
 
7. Desenvolver um programa para ler um valor inteiro e positivo n, calcular e apresentar o 
valor de s, utilizando a série abaixo. 
s = 1 / n1 + 1 / n2 + 1 / n3 + 1 / n4 + . . . + 1 / nn 
 
8. Desenvolver um programa para ler um valor inteiro e positivo n, calcular e apresentar o 
valor de s, utilizando a série abaixo. 
s = 1 / n + 2 / ( n – 1 ) + 3 / ( n – 2) + 4 / ( n –3 ) + . . . + n / 1 
 
9. Desenvolver um programa para ler um valor inteiro, positivo e par n, calcular e apresentar o 
valor de s, utilizando a série abaixo. 
 _ 
s = √1 / 2 + 2! / 3 + √3 / 4 + 4! / 5 + . . . + n! / ( n + 1 ) 
 
10. Desenvolver um programa para ler uma matriz quadrada (6 x 6) de double, a transforme, 
dividindo cada linha pelo respectivo elemento da diagonal principal e apresentando o 
resultado. 
 
11. Desenvolver um programa para ler um valor inteiro n (1 ≤ n ≤ 50), os n elementos de um 
vetor v de double e um valor double d, determinar e apresentar os índices das ocorrências 
de d em v. A função que determina as ocorrências deve retornar –1 caso d nâo pertença a v. 
12. Desenvolver um programa para ler um valor inteiro n (1 ≤ n ≤ 50), os n elementos de 
dois vetores v1 e v2 de double, calcular e apresentar o produto escalar destes vetores. O 
produto escalar deve ser calculado por função específica que trabalhe com vetores de 
qualquer dimensão. 
 
13. Desenvolver um programa para ler um arquivo texto (usando redirecionamento) e informar: 
a) número de vogais 
b) número de consoantes 
c) número de ocorrência de cada letra maiúscula 
 25
d) total de letras maiúsculas e minúsculas 
e) de todos os caracteres do arquivo, o que mais aparece e o que menos aparece. 
 
14. Desenvolver um programa para apresentar os caracteres no código ASCII com valores 
decimais de 32 a 255. Apresentar também para cada caracter seus códigos decimal e 
hexadecimal. 
 
12. PONTEIROS ( Pointers ) 
 
12.1. Definição 
 
Uma variável ponteiro contem um endereço como valor. 
 
Para declarar uma variável como ponteiro, deve-se fornecer o tipo para o qual apontará e 
o nome da variável. 
 
Embora os endereços de variáveis de um tipo, por exemplo int, sejam semelhantes aos de 
outros tipos como float, char ou double, é importante não misturá-los, pois ao serem feitas 
operações aritméticas com os ponteiros eles irão variar em função do (tamanho do) tipo. 
 
Principais vantagens do uso de ponteiros: 
• superar as restrições de passagem de argumento por valor, para as funções; 
• acessar elementos de arrays de modo alternativo ao uso de índices; 
• permitir a criação de estruturas dinâmicas, como árvores e listas encadeadas bem como 
facilitar a sua manipulação. 
 
Forma de definição: < tipo > *< nome > ; 
 
Exemplos: 
int *p ; ⇒ p é um ponteiro para inteiro 
float *pf ; ⇒ f é um ponteiro para float 
 
int i, *pt ; ⇒ pt é um ponteiro para inteiro 
pt = &i ; ⇒ pt recebe o endereço de ' i ' 
 
i pt 
 
5 1002 
 
1002 2000 
 
*pt ⇒ 5 (conteúdo do endereço dado por pt) 
 
void *pvoid ; ⇒ ponteiro para qualquer tipo 
 
Dado: 
float x, y, *p, *q ; 
 
São operações possíveis: 
p = &x ; ⇒ p aponta para x 
y = *p ; ⇒ y recebe o valor que está no endereço dado por p 
 26
q = p ; ⇒ q e p apontam para o mesmo endereço 
 
Erros comuns: 
int *p ; 
*p = 10 ; ⇒ p não apontou para nenhuma variável 
 
float val ; 
int *p ; 
p = &val ; ⇒ tipos incompatíveis 
 
12.2. Inicialização de ponteiros na declaração 
 
int i = 7, *p = & i ; eqüivale a: int i, *p ; 
i = 7 ; 
p = &i; 
 
12.3. Passagem de parâmetros por referência (endereço) 
 
Para passar um parâmetro por referência, o parâmetro formal deve ser um ponteiro e o 
parâmetro efetivo deve ser o endereço da variável. Desta forma, na chamada da função, o 
ponteiro (formal) passa a apontar para a variável, permitindo, assim, que esta variável 
possa ter seu valor alterado dentro da função. 
 
/* 
** Exemplo: ler dois valores e trocá-los de ordem 
*/ 
 
# include < stdio.h > 
void swap ( int *, int * ) ; /* protótipo de swap */ 
 
void main ( ) 
{ 
int a, b ; 
scanf ( " %d %d ", &a, &b ) ; /* leitura dos dados */ 
swap ( &a, &b ) ; /* chama a função swap */ 
printf ( " %d %d \n ", a, b ) ; /* imprime resultado */ 
} 
 
 
 
void swap ( int *x, int *y ) 
{ 
int tmp ; 
tmp = *x ; 
*x = *y ; 
*y = tmp ; 
} 
 
Exercício: fazer uma função para somar dois valores inteiros e que tenha o seguinte 
protótipo: void soma ( int *r, int a, int b ) ; 
 
 27
# include < stdio.h > 
void soma ( int *r, int a, int b ) ; 
 
void main ( ) 
{ 
int x, y, z ; 
printf ( " \n Entre com dois números: \n " ) ; 
scanf ( " %d %d ", &x, &y ) ; 
soma ( &z, x, y ) ; 
printf (" \n A soma é: %d \n ", z ) ; 
} 
 
void soma ( int *r, int a, int b ) 
{ 
*r = a + b ; 
} 
 
Obs.: Pode-se usar, por exemplo: float * *q ; 
Neste caso, q pode armazenar um endereço de um endereço de float. 
 
13. A RELAÇÃO ENTRE PONTEIROS E ARRAYS 
 
O nome de um array é um ponteiro (fixo) para a primeira componente deste array. 
 
Exemplo: 
int k, a [100], *p ; 
. . . 
p = a; ⇔ p = &a [0]; ⇒ atribui a p o endereço do primeiro elemento do vetor a 
 
Para que p aponte para o elemento seguinte, basta incrementar o endereço de 1: 
p = a + 1; ⇔ p = &a[l]; ⇔ p++; ⇒ p aponta para o próximo elemento 
. . . 
k = *p++ ; ⇒ k recebe o valor do elemento apontado por p e, em seguida, p 
aponta para o próximo elemento 
 
Exemplo para zerar um vetor: 
 
for ( i = 0; i < 100; i++ ) /* com índice */ 
a [i] = 0 ; /* cálculo do índice = endereço (base + i * size) */ 
ou 
 
for ( i = 0, p = a; i < 100; i++ ) /* com ponteiro */ 
p++ = 0; /* melhor, pois utiliza somente operação de soma */ 
 
Exercício: Criar a função strlen usando ponteiro. 
 
Solução 1: 
# include < stdio.h > 
int strlen ( char* str ) ; 
 
void main ( ) 
 28
{ 
static char str[ ] = "Esta string tem 30 caracteres." ; 
printf ( " \n Tamanho da string: %d caracteres. \n ", strlen ( str ) ) ; 
} 
int strlen ( register char *s ) 
{ 
register int n ; 
for ( n = 0; *s != '\0'; s++ ) 
++n ; 
return (n) ; 
} 
 
Solução 2: 
# include < stdio.h > 
int strlen ( char *str ) ; 
 
void main ( ) 
{ 
static char str [ ] = "Esta string tem 30 caracteres." ; 
printf ( " \n Tamanho da string: %d caracteres. \n ", strlen (str) ) ; 
} 
 
int strlen ( register char *s ) 
{ 
register int n = 0 ; 
while ( *s++ ) 
++n ; 
return (n) ; 
} 
 
Solução 3: 
# include < stdio.h > 
int strlen ( char *str ) ; 
void main ( ) 
{ 
static char str[ ] = "Esta string tem 30 caracteres." ; 
printf ( " \n Tamanho da string: %d caracteres. \n ", strlen ( str ) ) ; 
} 
int strlen ( register char *s ) 
{ 
register char *ps ; 
ps = s ; /* ps aponta para o inicio da string */ 
while ( *ps ) 
ps++ ; /* melhor solução: só tem um somatório */ 
return ( ps - s ) ; 
} 
 
Exercício: Criar a função strcpy, que tem como finalidade copiar o conteúdo de uma 
string em outra. 
 
 29
# include < stdio.h > 
char *strcpy ( char *d, char *o ) ; 
 
void main ( ) 
{ 
static char orig [ ] = "teste", dest [20] ; 
strcpy ( dest, orig ) ; 
printf ( "origem = %s ; destino = %s \n", dest, orig ) ; 
} 
 
char *strcpy (char *dest, char *origem) 
{ 
char *d = dest ; 
/* 
** o while a seguir faz a copia e incrementa os ponteiros de cada string, até 
aparecer ** um caracter nulo significando fim de string. Alguns compiladores darão 
um aviso ** (warning), achando que se deseja fazer um teste (= =) ao invés de 
atribuição com ** teste, que é o que realmente se pretende fazer. 
/* 
 
while ( *d++ = *origem++ ) ; /* testa *d != '\0'*/ 
return ( dest ) ; 
} 
 
Exercício: Criar a função strcmp que tem como finalidade comparar o conteúdo de duas 
strings. 
 
A função tem o seguinte formato: int strcmp ( char *s, char *t ) ; 
Esta função retoma um valor inteiro: 
• = 0 se as strings forem iguais 
• < 0 se s < t 
• > 0 se s > t 
Solução: 
# include < stdio.h > 
int strcmp ( char *s, char *d ) ; 
 
 
 
void main ( ) 
{ 
char sl [20], s2 [20] ; 
printf ( "Entre com a primeira string: " ) ; 
gets ( sl ) ; 
printf ( "Entre com a segunda string: " ) ; 
gets ( s2 ) ; 
if ( strcmp ( sl, s2 ) ) 
printf ( " \n Diferentes. \n " ) ; /* <> de zero */ 
else 
printf ( "\n Iguais. \n " ) ; 
} 
 30
int strcmp ( char *sl, char *s2 ) 
{ 
while ( *sl = = *s2 ) 
if ( *sl++ ) 
s2++ ; 
else 
return ( 0 ) ; 
return ( *sl - *s2 ) ; 
} 
 
Obs.: 
• Pode-se também inicializar strings usando o seguinte tipo de declaração: 
 char *mens = "Divisao por zero" ; 
onde a variável mens é declarada como sendo um ponteiro para a string "Divisao por 
zero". 
• char str [100] ; 
str = "casa" ; ⇒ ERRADO 
 
char *str ; 
str = "casa" ; ⇒ CERTO 
 
Exercícios: 
 
1. Fazer um programa que utilize uma função que retorne um ponteiro para o maior 
elemento de um vetor. 
 
2. Fazer uma função que retorne um ponteiro para a primeira ocorrência de um valor em 
um vetor de doubles ou null caso não encontre. 
 
double* rp ( double* pv, int n, double val ) 
{ 
int i = 0; 
for ( ; i < n; i++, pv++ ) 
 if ( *pv = = val ) 
 return ( pv ); 
return ( NULL ); 
} 
 
14. PONTEIROS E ARRAVS BIDIMENSIONAIS 
 
Como para vetores unidimensionais, o nome de um array é um ponteiro fixo. Assim, as 
seguintes identificações são equivalentes e geram o mesmo código: 
 
 b [i] [j] * ( * ( b + i ) + j ) * ( b + i * ncolun + j ) 
 
O esquema abaixo representa o acesso à vetores bidimensionais. 
 
b → . . . . . . 
 
b[l] → . . . . . . 
 . . 
 31
 . . 
 . . 
b[i] ou → . . . . . . 
b + i ↑ . . 
 *(b + i) . . 
 . . 
 . . 
b[n] → . . . . . . 
 ↑ 
 b[n][j] ou 
 *(*(b + n) + j) 
 
Embora pouco utilizado, existem formas de acessar matrizes de uma forma mais 
eficiente. Seja, por exemplo, a seguinte declaração de ponteiro para matriz: 
 
int mat [10][20], (*plin) [20] ; 
plin = mat ; 
 
A variável plin foi declarada acima como um ponteiro para uma linha de um array com 
20 colunas e, em seguida, ele passou a apontar para o primeiro elemento. 
 
Forma de armazenamento na memória: 
 
 
 . . . . . . . . . 
 
20 elementos 20 elementos 20 elementos 
 
Para acessar um elemento da matriz através do ponteiro: 
 (*plin) [j] ou *(*(plin) + j) 
 
Para acessar a próxima linha, pode-se usar: 
 plin++ 
 
Obs.: 
• Como em C uma matriz é armazenada por linhas, as variáveis v e mat abaixo, possuem 
exatamente a mesma forma de armazenamento: 
static int v [9] = 
 { 
 1, 2, 3, 4, 5, 6, 7, 8, 9 
 } ; 
static int mat [3][3] = 
 { 
 {1, 2, 3} 
 {4, 5, 6} 
 {7, 8, 9} 
 } ; 
• (*plin) [20] define um ponteiro para linha de uma matriz de 20 colunas, enquanto que 
*plin [20] define um vetor de 20 elementos do tipo ponteiro. 
 32
• O nome de um array bidimensional quando usado com um único índice é um ponteiro 
para o primeiro elemento de uma determinada linha. 
 Ex.: mat [n] ⇒ ponteiro para mat [n][0] 
 
Exercício: Calcular a média dos elementos de uma coluna de uma matriz de 3 colunas. 
 
# include < stdio.h > 
double test_avg ( int [ ][3], int, int ) ; 
void main ( ) 
{ 
double res ; 
static int board [ ][3] = 
{ 
1, 2, 3, 
4, 5, 6, 
7, 8, 9, 
10, 11, 12 
} 
res = test_avg ( board, 4, 2 ) ; 
printf ( " %lf \n ", res ) ; 
} 
 
double test_avg ( int tab [ ][3], int maxlin, int col ) 
{ 
long sum = 0 ; 
int i, ( *ptr ) [3] = tab ; 
for ( i = 0; i < maxlin; i++ ) 
sum += ( *ptr++ ) [col] ; 
return ( ( double ) sum / maxlin ) ; 
} 
 
Exercício: Calcular a média dos elementos de uma linha de uma matriz de 3 colunas. 
 
# include < stdio.h > 
double test2_avg ( int [ ][3], int ) ; 
 
 
voíd main ( ) 
{ 
static int board [ ][3] = 
{ 
1, 2, 3, 
4, 5, 6, 
7, 8, 9, 
10, 11, 12 
}; 
double res ; 
res = test2_avg ( board, 1 ) ; /* calcular para linha 1 */ 
printf ( " %lf \n ", res ) ; 
} 
 33
double test2_avg ( int tab [ ][3], int linha ) 
{ 
long soma = 0 ; 
int i ; 
int p = &tab [linha][0] ; /* endereço do elemento ( linha, 0 ) de tab */ 
for ( i = 0; i < 3; i++ ) 
soma += *p++ ; 
return ( ( double ) soma / 3 ) ; 
} 
 
Obs.: Arrays tridimensionais: seguem as mesmas regras do bidimensional. 
Exemplo: int v [3][4][5] ; /* array com dimensões 3 x 4 x 5 */ 
 ou int ( *ptrv ) [4][5]; 
 
14.1. Arrays de ponteiros (ragged arrays) 
 
São arrays com linhas de tamanho variável. Um array deste tipo é composto por ponteiros 
para outras variáveis e o seu uso mais comum é para acessar tabelas de strings. 
 
Exemplo: definir um vetor de ponteiros para uma tabela de dias da semana. 
static char *dias [ ] = 
{ 
"segunda", "terça", "quarta", "quinta", "sexta", "sábado", "domingo" 
}; 
Nesta definição, dias é um array de 7 ponteiros para caracteres. 
 
O esquema a seguir apresenta a organização interna da tabela dias, declarada 
anteriormente: 
 dias 
0 → s e g u n d a \0
 
1 → t e r ç a \0 
. 
. 
. 
 
6 → d o m i n g o \0
Os elementos de 'dias' podem ser acessados de duas maneira: 
1. dias [i] 
Ex.: printf ( " %s \n ", dias [2] ) ; /* será impresso quarta */ 
 
2. dias [i][j] 
Ex.: printf ( " %c \n ", dias [1][2] ) ; /* será impresso r de terça */ 
 
Exercício: Fazer uma função para imprimir 'dias'. 
 
# include < stdio.h > 
void printab ( char*[ ], int ) ; 
 
void printab ( char* tab[ ], int n ) 
{ 
 34
int i ; 
for ( i = 0; i <= n; i++ ) 
printf ( " %s \n ", tab [i] ) ; 
} 
 
Obs.: Um elemento de um array de pointers pode ser usado como se fosse um pointer 
para o primeiro elemento de uma linha de um array bidimensional (tab[i]). 
 
15. DEFINIÇÃO DE NOVOS TIPOS 
 
Para definição de novos tipos, utiliza-se a instrução typedef 
 
Formato: typedef < tipo existente > < nome do novo tipo >; 
 
Exemplos: 
• para se definir o tipo contador: 
typedef int contador ; 
contador i, j ; 
• para se definir o tipo string: 
typedef char string [81] ; 
string text ; 
• para se definir o tipo boolean: 
typedef int boolean ; 
# define FALSE 0 
# define TRUE 1 
 . . . 
boolean ok = TRUE ; 
if ( ok ) 
. . . 
• para se definir o tipo ptrchar (pointer para caracter): 
typedef char *ptrchar ; 
ptrchar pchar ; 
 
 
 
 
16. PONTEIRO PARA PERCORRER ARRAY DE PONTEIROS. 
 
Como para qualquer array, o nome de um array de ponteiros é um ponteiro para o 
primeiro elemento deste array. Quando se passa um array de ponteiros como parâmetro, 
o que realmente passa é um ponteiro para o primeiro elemento do array. 
 
Exemplo: Imprimir tabela 'dias'. 
 
typedef char *CHARPTR ; /* define tipo pointer para char */ 
 
void print_tab ( CHARPTR *tab_ptr, int n ) 
{ 
CHARPTR *end_ptrl = tab_ptr + ( n - 1 ); 
while ( tab_ptr <= end_ptr ) 
printf ( " %s \n ", *tab_ptr++ ) ; 
 35
} 
 
Obs.: 
• Uma vantagem de usar o typedefno exemplo acima é a de facilitar a sintaxe, isto é, 
se não usasse, ao invés de CHARPTR *tab_ptr teria que ser char * * tab_ptr. 
• Outra alternativa para o corpo da função print_tab seria: 
{ 
int i ; 
for ( i = 0; i < n; i++ ) 
printf ( " %s \n ", *tab_ptr++ ) ; 
} 
 
Esquema dos ponteiros: 
 
tab_ptr dias 
 → 0 → s e g u n d a \0 
 
 1 → t e r ç a \0 
 
 
end_ptr 
. 
. 
. 
 
 → 6 → d o m i n g o \0 
 
16.1. Argumentos para o main 
 
As vezes, torna-se necessário passar argumentos para a rotina principal (main) de um 
programa C. Estes argumentos são fornecidos na chamada para execução do programa. 
Um exemplo seria: A > listar teste.dat 
 
Neste exemplo, o nome do programa é listar e o parârnetro passado é teste.dat. 
 
 
 
 
 
Para receber parâmetros, o main deve possuir o seguinte formato: 
 void main ( int argc, char *argv [ ] ) 
onde: 
• argc é o número de parâmetros usados (contando, inclusive, com o nome do 
programa) 
• argv é um array de ponteiros para carateres (strings), também chamado de vetor 
de argumentos. 
 
Exemplos: 
a) Um programa chamado 'echo' que recebe como parâmetro uma mensagem e a exibe 
no vídeo. 
Seja a seguinte chamada: echo um dois tres 
Neste caso tem-se: 
argc = 4 
 
 36
argv 
 → 0 → e c h o \0 
 
 1 → u m \0 
 
 2 → d o i s \0 
 
 3 → t r e s \0 
 
O programa echo poderia ser: 
 
/* versão com índice */ 
# include < stdio.h > 
void main ( int argc, char *argv [ ] ) 
{ 
int next ; 
for ( next = 1; next < argc; next++ ) 
printf ( " %s%c ", argv [ next ], ( next < argc – 1 ) ? ‘ ’ : ‘ \n ’ ); 
} 
 
/* versão com pointer */ 
# include < stdio.h > 
void main ( int argc, char *argv [ ] ) 
{ 
/* ponteiro para o último parâmetro */ 
char **last_ptr = argv + argc - 1; 
/* incrementa antes de testar, pois o primeiro elemento é o nome do programa */ 
while ( ++argv <= last_ptr ) 
printf ( " %s%c ", *argv, ( argv < last_ptr ) ? ‘ ’ : ‘ \n ’ ); 
} 
 
 
 
 
 
17. PONTEIRO PARA FUNÇÃO 
 
Permite a passagem de função como argumento. 
 
Formato geral: <tipo> ( *func_ptr ) ( <tipos dos argumentos> ); 
 
Obs.: 
• <tipo> ( *func_ptr ) ( <tipos dos argumentos> ); ⇒ é um ponteiro (func_ptr) para 
uma função que retorna um valor do tipo <tipo>. 
• <tipo> *func_ptr ( <tipos dos argumentos> ); ⇒ é uma função que retorna um 
ponteiro para um valor do tipo <tipo>. 
 
Exemplo: 
int soma ( int, int ); 
void main ( ) 
{ 
 37
int ( *ptr_soma ) ( int, int ); 
int x; 
ptr_soma = soma; /* pointer para função soma */ 
x = ( *ptr_soma ) ( 3, 5 ); /* equivale a x = soma ( 3, 5 ); */ 
} 
 
Exercício: Fazer um programa que leia duas strings de dados numéricos ou alfanuméficos 
e as compare conforme o seu tipo. 
 
# include < stdio.h > 
# include < stdlib.h > 
# include < string.h > 
 
int numcmp ( char *, char * ) ; 
void check ( char *, char *, int ( *cmp ) ( char *, char * ) ) ; 
 
void main ( ) 
{ 
char sl [80], s2 [80] ; 
gets (sl) ; /* obtem a primeira string */ 
gets (s2) ; /* obtem a segunda string */ 
if ( isalpha (*sl) ) /* a função isalpha retorna <> 0 para letra do alfabeto */ 
check ( sl, s2, strcmp ) ; /* a função strcmp compara strings */ 
 else 
check ( sl, s2, numcmp ) ; /* compara como números */ 
} 
 
void check ( char *a, char *b, int ( *cmp ) ( char *, char * ) ) 
{ 
if ( ! ( *cmp ) ( a, b ) ) 
printf ( " iguais \n " ) ; 
else 
printf ( " diferentes \n " ) ; 
} 
int numcmp ( char *a, char *b ) 
{ 
if ( atoi (a) = = atoi (b) ) /* a função atoi transforma string em inteiro */ 
return ( 0 ) ; 
else 
return ( l ) ; 
} 
 
18. PRÉ-PROCESSADOR E DIRETIVAS 
 
O pré-processador é um programa que age antes do compilador da linguagem C, 
aumentando sua capacidade e auxiliando a portabilidade. Ele é comandado por diretivas 
que começam sempre pelo sinal #. As diretivas só valem no arquivo onde estão definidas 
ou em arquivos incluídos abaixo destas diretivas. As principais diretivas são o # define e 
o # include. Além destas existem diretivas para compilação condicional (# if, # else, # 
elif, # endif, # ifdef e # ifndef) e outras utilizadas apenas por determinados 
compiladores como, por exemplo, para montagem de trechos de programa em linguagem 
 38
Assembly (# asm e # endasm). Entretanto, estas últimas não são portáveis, pois não 
são encontradas em todos os compiladores C. 
 
18.1. # define 
 
Serve para definir uma constante simbólica. Por exemplo: 
 
# define TRUE 1 
# define FALSE 0 
# define MENS_ERRO " Erro. Divisão por zero. \n " 
# define MAX_TAM 100 
 
Obs.: Não usar ; ou comentário na linha de um define, pois ele passa a fazer parte dele. 
 
As constantes assim definidas podem ser usadas, por exemplo, para atribuir valores a 
variáveis passadas como parâmetros ou para comparações dentro do programa. 
Ex.: 
int erro ; 
. . . 
erro = FALSE ; 
if ( a = = 0 ) 
erro = TRUE ; 
. . . 
if ( erro ) 
printf ( MENS_ERRO ) ; 
 
Outra finalidade do # define é criar macro funções. Por exemplo: 
 
# define MIN ( a, b ) ( (a) < (b) ? (a) : (b) ) 
o comando: a - MIN ( X, 3 ) ; equivale a => a = ( (x) < (3) ? (x) : (3) ) ; 
# define streq ( x, y ) ( strcmp ( (x), (y) ) = = 0 ) /* iguais */ 
# define strlt ( x, y ) ( strcmp ( (x), (y) ) < 0 ) /* x < y */ 
# define strgt ( x, y ) ( strcmp ( (x), (y) ) > 0 ) /* x > y */ 
Obs.: 
• Deve-se tomar um certo cuidado ao utilizar macros, pois não há verificação de tipo dos 
argumentos como nos protótipos das funções. 
• Macro é mais rápido do que função, porque ela é acrescentada no corpo do programa 
em cada ponto em que precise ser executada, não havendo, portanto, chamada. Por 
outro lado, só se deve desenvolver macros para pequenas tarefas, evitando assim que 
o corpo do programa fique muito grande. 
• Usar parênteses nos argumentos para evitar erros do tipo: 
# define quad (x) x * x 
. . . 
a = quad ( b + c ) ; /* será calculado b + c*b + c e não (b + c)*(b + c) */ 
• Para que se possa reutilizar uma constante devemos torná-la antes indefinida, usando a 
diretiva # undef e a seguir redefiní-la novamente usando outro # define. 
Exemplo: 
# define TAM 100 
# define COMP 100 
char array [ TAM ][ COMP ] ; 
 39
. . . 
# undef COMP 
# define COMP 200 
char arrayb [ TAM ][ COMP ] ; 
 
18.2. # include 
 
Permite incluir o conteúdo de um arquivo no programa. 
Exemplo: 
# include < stdio.h > ⇒ procura stdio.h no diretório de arquivos header 
# include "stdio.h" ⇒ procura stdio.h no diretório corrente 
# include "b:auxiliar.h" ⇒ procura auxiliar.h no drive b (não deve ser usado, pois 
prejudica a portabilidade) 
 
18.3. Compilação Condicional 
 
Os comandos para compilação condicional são: # if, # else, # elif, # ifdef, # ifndef e # 
endif, # if, # else, # elif e # endif 
 
São comandos do tipo if then else que indicam para o compilador os trechos do 
programa que deverão ou não ser compilados. 
 
Exemplos de formatos possíveis: 
 
# if expressão constante 
seqüência de comandos 
# endif 
 
# if expressão constante 
seqüência de comandos 1 
# else 
seqüência de comandos 2 
# endif 
# if expressão constante 1 
seqüência de comandos 1 
# elif expressão constante 2 
seqüência de comandos 2 
# else 
seqüência de comandos 3 
# endif 
 
Exemplosde uso: 
a) # define MAX 100 
. . . 
void func ( ) 
{ 
# if MAX > 99 
printf ( " Compilado para array > 99 \n " ) ; 
# endif 
. . . 
} 
 40
 
b) # if PC = = 1 
# define COMP 1 
# else 
# define COMP 2 
# endif 
 
Obs.: Neste caso, PC deve ser definido na solicitação de compilação. 
Ex.: xlc -o teste -d PC = 1 teste.c 
 
# ifdef e # ifndef - Usados para testar se algum símbolo foi definido como parâmetro. 
Exemplos de formatos possíveis: 
 
# ifdef símbolo 
seqüência de comandos 
# endif 
 
# ifdef símbolo 
seqüência de comandos 1 
# else 
seqüência de comandos 2 
# endif 
 
# ifndef símbolo 
seqüência de comandos 
# endif 
 
# ifndef símbolo 
seqüência de comandos 1 
# else 
seqüência de comandos 2 
# endif 
Exemplos de uso: 
a) # ifdef DEBUG 
printf ( " Valor de x : %d \n ", x ) ; 
# endif 
 
b) # ifndef DEBUG 
printf ( " Sem debug \n " ) ; 
# endif 
 
Obs.: Para evitar redefinição, todo arquivo .h deve iniciar com um # ifndef. 
Ex.: 
# ifndef MEU_H 
# define MEU_H 
. . . 
int soma ( int, int ); 
. . . 
# define TAM 1000 
. . . 
struct xx 
 41
. . . 
# endif 
 
19. TIPOS CONSTRUIDOS 
 
19.1. Tipo enumerado 
 
Uma variável de um tipo enumerado pode assumir seu valor somente dentre o conjunto 
determinado na enumeração. 
 
Forma de definição: enum < nome > { <conjunto de valores separados por vírgulas> } 
; 
 
Exemplo para definir o tipo dia: 
enum dia { segunda, terça, quarta, quinta, sexta, sábado, domingo } ; 
enum dia dial, dia2, todos [ 7 ] ; /* as variáveis aqui declaradas só podem assumir 
*/ /* os valores enumerados entre chaves */ 
dial = segunda ; 
todos [ 6 ] = domingo ; 
 
Obs.: 
• Deve-se notar que os valores entre chaves não são strings. 
• Uma outra forma possível de definição/declaração é a seguinte: 
enum { segunda, terça, quarta, quinta, sexta, sábado, domingo } dial, dia2 ; 
• Cada valor enumerado na definição é associado a um valor inteiro a partir de 0(zero). 
Entretanto, pode-se indicar os valores inteiros que se deseja associar: 
enum frutas { laranja = 7, limão = 6, jaca = 0 } ; 
enum frutas1 { pera = 2, laranja, abacate, limão = 7 } ; 
• Os valores dos tipos enumerados não são inteiros. Para usar o valor inteiro associado, 
deve-se empregar o cast para inteiro: 
i = ( int ) dial ; 
 
19.2. Estruturas 
 
Estruturas (struct) são usadas quando há necessidade de se combinar dados de um mesmo 
tipo ou de tipos diferentes em um único objeto ou registro. 
 
Forma de definição: 
struct < nome > 
{ 
< tipol > < campol > ; 
< tipo2 > < campo2 > ; 
. . . 
< tipon > < campon > ; 
}; 
 
Exemplos: 
struct sl /* Define uma estrutura chamada sl contendo os seguintes campos: */ 
{ 
int a ; /* inteiro - a */ 
char nome [ 81 ] ; /* array de caracteres - nome */ 
 42
char ender [ 100 ] ; /* array de caracteres - endereço */ 
} 
 
Para declarar duas variáveis estruturadas infol e info2, usa-se: 
struct sl infol, info2 ; 
 
Outra forma de definição/declaração poderia ser: 
struct /* neste caso, não está sendo criado um tipo */ 
{ 
int x ; 
int y ; 
} v1, v2 ; 
 
É comum o uso do typedef em conjunto com as estruturas, onde a estrutura passa a ter um 
nome: 
typedef struct sl s_s1 ; 
. . . 
s_sl infol, info2 ; 
 
Para acessar aos membros de uma estrutura, usa-se a seguinte sintaxe: 
< variável >.< campo > ; 
 
Exemplos: 
infol.a = 10 ; 
strcpy ( infol.nome, "teste" ) ; 
 
struct s2 
{ 
int a ; 
char *nome ; 
} ; 
struct s2 info3 ; 
info3.a = 10 ; 
info3.nome = " José da Silva " ; /* nome é um ponteiro */ 
 
A operação de atribuição e as comparações de igualdade e de desigualdade são possíveis 
entre estruturas, mas devem ser usadas com certo cuidado, pois podem causar problemas. 
A partir da definição struct sl vl, v2 ; pode-se executar: 
v1 = v2; /* tomar cuidado com campo do tipo ponteiro */ 
. . . 
if ( vl = = v2 ) /* tomar cuidado com a comparação de lixo */ 
{ 
. . . 
} 
 
É possível testar se duas estruturas são iguais ou diferentes, mas não se uma é maior ou 
menor do que a outra. No entanto, os campos (membros) de uma estrutura podem ser 
comparados com os de outra, normalmente. 
Apesar da versão ANSI permitir passar estruturas por valor para as funções, isto deve ser 
evitado, já que torna a chamada da função mais lenta e aumenta a área ocupada no stack. 
 43
A forma mais indicada para se passar estruturas é utilizando pointers, como será visto 
mais adiante. 
 
19.3. Inicialização de Estruturas 
 
Para alguns compiladores, é necessário que as variáveis locais sejam estáticas. 
struct s_carta 
{ 
int carta ; 
char naipe ; /* Ouros, Copas, Paus e Espadas */ 
} ; 
 
struct s_carta c = 
{ 
12, 
‘ O ’ /* inicializa como dama de ouros */ 
} ; 
 
19.4. Uniões 
 
Permite o armazenamento de valores de tipos diferentes. 
 
Forma de definição: 
union < nome > 
{ 
< tipo1 > <campo1 >; 
< tipo2 > <campo2 >; 
. . . 
< tipon > <campon >; 
}; 
 
Exemplo: 
union mixed 
{ 
char c ; 
float f ; 
int i ; 
} ; 
. . . 
union mixed x ; 
x . c = ‘ A ’ ; 
. . . 
x . f = 10.0 ; 
. . . 
x . i = 1 ; 
 
A área reservada para uma variável deste tipo é calculada pelo tamanho do maior campo. 
No exemplo acima, a área reservada teria o tamanho de um float. 
 
Deve-se notar que uma variável do tipo union guarda apenas um valor de cada vez, isto é, 
ao armazenar um dado de um novo tipo, apaga o do tipo anterior. 
 44
 
A seguir, é apresentado um exemplo no qual se monta um array de estruturas que guarda 
elementos de tipos diferentes. 
 
/* 
** Exemplo: uso combinado de estrutura e união: Cria um array chamado tabela de 
** dimensão TAMANHO. Cada elemento do array contem uma estrutura que consiste de 
** um ponteiro para caracter (nome), um inteiro (tipo) e um membro do tipo union 
** (dados). Cada membro dados do array pode conter um valor do tipo int, float ou char. 
*/ 
# include < stdio.h > 
# define INTEGER 0 
# define FLOATING 1 
# define CHARACTER 2 
# define TAMANHO 6 
 
void main ( ) 
{ 
struct 
{ 
char *nome ; 
int tipo ; 
union 
{ 
int i ; 
float f ; 
char c ; 
} dados ; 
} tabela [TAMENHO] ; 
int j ; 
/* Inicialização dos elementos da estrutura */ 
tabela [0] . nome = "Cesar" ; 
tabela [0] . tipo = CHARACTER ; 
tabela [0] . dados . c = ‘A’ ; 
 
tabela [1] . nome = "do vale" ; 
tabela [1] . tipo = CHARACTER ; 
tabela [1] . dados . c = ‘A’ ; 
 
tabela [2] . nome = "Ferrari" ; 
tabela [2] . tipo = CHARACTER ; 
tabela [2] . dados . c = ‘A’ ; 
 
tabela [3] . nome = "Idade" ; 
tabela [3] . tipo = INTEGER ; 
tabela [3] . dados . i = 42 ; 
 
tabela [4] . nome = "Salário" ; 
tabela [4] . tipo = FLOATING ; 
tabela [4] . dados . f = 7200 . 56‘A’ ; 
 
 45
tabela [5] . nome = "Imposto" ; 
tabela [5] . tipo = FLOATING ; 
tabela [5] . dados . c = 2785 . 89 ; 
 
for ( j = 0; j < TAMANHO; j++ ) 
{ 
printf ( " %s ", tabela [ j ] . nome ) ; 
switch ( tabela [ j ] . tipo ) 
{ 
case INTEGER : 
printf ( " %d \n ", tabela [ j ] . dados . i ) ; 
break ; 
case FLOATING : 
printf ( " %.f \n ", tabela [ j ] . dados . f ) ; 
break ; 
case CHARACTER : 
printf ( " %c \n ", tabela [ j ] . dados . c ) ; 
break ; 
default : 
printf ( " Tipo desconhecido %d - elemento %d . \n ", tabela [ j ] . 
tipo, j ) ; 
break ; 
} 
} 
} 
 
 
 
 
20. PONTEIRO PARA ESTRUTURAS 
 
Uma estrutura deve ser sempre passada como parâmetro através de um ponteiroe não por 
valor, mesmo que nenhum dos valores de seus campos necessite ser alterado. 
 
Exemplo: 
# include < stdio . h > 
struct s_data 
{ 
int dia, mes, ano ; 
} 
 
typedef struct s_data sdata ; 
 
void prtdata ( sdata* ) ; 
 
void main ( ) 
{ 
sdata data ; 
data . dia = 15 ; 
data . mes = 3 ; 
data . ano = 2000 ; 
 46
prtdata ( &data ) ; /* passa o endereço da estrutura */ 
} 
 
void prtdata ( sdata *data ) 
{ 
printf ( " %d / %d / %d \n ", data -> dia, data -> mes, data -> ano ) ; 
/* também poderia usar: (*data ) . dia, (*data ) . mes, (*data ) . ano */ 
} 
 
21. ARQUIVOS 
 
A linguagem C possui alguns arquivos pré-definidos, que são abertos automaticamente 
quando um programa começa a ser executado. São eles: 
 stdin standard input (default = teclado) 
 stdout standard output (default = vídeo) 
 stderr standard error (default = vídeo) 
 stdprn standard printer (aceito por somente alguns compiladores) 
 
Obs.: 
• Os arquivos stdin, stdout e stderr podem ser redirecionados pelo usuário. 
• Nos arquivos pré-definidos, a leitura e a impressão são realizadas como se estivesse 
utilizando um arquivo texto (leitura e gravação sequenciais). 
 
O conceito de arquivos em C é um pouco diferente do de outras linguagens. Na maioria 
das linguagens, o tipo de acesso (direto ou sequencial) está diretamente ligado a forma de 
abertura do arquivo. Em C, o tipo de acesso (direto ou sequencial) é escolhido pelo 
usuário, de acordo com as suas necessidades. 
A linguagem C trabalha com arquivos de uma forma bastante eficiente, utilizando 
diversas funções. 
Em primeiro lugar, deve-se incluir o arquivo stdio.h. Deve-se também declarar uma 
variável do tipo ponteiro para arquivo, utilizando o seguinte formato? 
 FILE *fp ; 
 
21.1. Funções para abertura e fechamento de arquivos 
 
21.1.1. Abertura de arquivos 
 
 FILE fopen ( char*, char* ) ; 
 
Exemplo de chamada: 
 fp = fopen ( nome, modo ) ; 
 
Esta chamada abre um arquivo de nome "nome" no modo "modo". "nome" e "modo" são 
strings de caracteres. 
Os modos de abertura possíveis são apresentados na tabela a seguir: 
"nome" no modo "modo". 
Modo Tipo Read Write Cria Append 
r t s n n n 
r+ t s s n n 
rb b s n n n 
 47
rb+ b s s n n 
w t n s s n 
w+ t s s s n 
wb b n s s n 
wb+ b s s s n 
a t n s s s 
a+ t s s s s 
ab b n s s s 
ab+ b s s s s 
Onde: 
• r ⇒ read 
• w ⇒ write 
• a ⇒ append 
• t ⇒ texto 
• b ⇒ binário 
• s ⇒ sim 
• n ⇒ não 
 
Obs.: 
• r+ ⇒ lê e escreve. 
• w+ ⇒ escreve e lê. 
• rb+ ⇒ atualiza registro. 
• w ⇒ sempre cria arquivo. 
• a ⇒ cria se não existir. Se existir, grava no final. 
• r ⇒ nunca cria. 
 
Em caso de sucesso na abertura, a função fopen retorna um ponteiro para o arquivo, que 
será usado como nome lógico deste arquivo dentro do programa (fp do exemplo de 
chamada). Em caso de erro, retorna NULL. 
É recomendável que o usuário sempre verifique se o arquivo foi aberto ou não. 
 
Exemplo: 
fp = fopen ( " teste.dat ", " r " ) ; 
if ( fp = = NULL ) 
{ 
printf ( " \n Erro: abertura de arquivo para leitura " ) ; 
exit (1) ; 
} 
 
O nome do arquivo pode indicar um nome simples ou um nome com path (caminho). 
Exemplos: " teste.dat " 
" C:\programas\testes\teste.dat ", 
 
21.1.2. Fechamento de arquivos 
 
Protótipo: int fclose ( fp ) ; 
 
Chamada: fclose ( fp ) ; 
 
 48
Em caso de sicesso, a função retorna 0 (zero) e em caso de erro, um valor diferente de 0 
(zero). 
 
Exemplo: (caso usual) 
. . . 
if ( fp = fopen ( " teste.dat ", " r " ) = = NULL ) 
{ 
printf ( " \n Erro de abertura para leitura " ) ; 
exit (1) ; 
} 
. . . 
fclose ( fp ) ; 
 
21.2. Principais funções para leitura e gravação sequenciais 
 
21.2.1. Leitura 
 
fgets 
Protótipo: char *fgets ( char*, int, FILE* ) ; 
Exemplo de chamada: pchar = fgets ( buffer, num, fp ) ; 
Lê caracteres do arquivo dado por fp. A leitura termina quando forem lidos (num - 1) 
caracteres ou quando encontrar o caracter NL (New Line = \n) ou EOF (End Of File = 
\0). Os caracteres lidos são colocados em buffer. Se a leitura foi correta, a função retorna 
um ponteiro para buffer e em caso de erro, retorna NULL. 
Obs.: 
• O buffer deve ser grande o suficiente para armazenar os caracteres \0 e \n. 
• A função fgets é muito usada para arquivos do tipo texto. 
Exemplo: 
. . . 
char *pchar, buffer [100] ; 
FILE *fp ; 
. . . 
pchar = fgets ( buffer, 98, fp ) ; 
. . . 
 
fgetc 
Protótipo: int fgetc ( FILE* ) ; 
 
Exemplo de chamada: v_int = fgetc ( fp ) ; 
 
Lê um caracter do arquivo dado por fp e retorna o seu código ASCII. Se a leitura chegar 
ao final do arquivo ou houver erro de leitura, retorna EOF. 
O uso desta função requer um certo cuidado, pois EOF pode ser um caracter válido em 
arquivos abertos no modo binário. 
Obs.: Esta é uma função pouco usada. 
 
fscanf 
Protótipo: int fscanf ( FILE*, char*, . . . ) ; 
 
Exemplo de chamada: fscanf ( fp, fmt, args ) ; 
 
 49
Funciona de forma semelhante à função scanf. Lê as variáveis em args com o formato fmt 
do arquivo dado por fp. Em caso de sucesso, retorna o número de variáveis que foram 
lidas e caso chegue ao fim do arquivo, retorna EOF. 
Obs.: 
• Esta é uma função muito usada, principalmente para leitura de valores numéricos. 
• Depois da fgets pode usar a fscanf, mas depois da fscanf, pode dar problema usar a 
fgets. 
 
21.2.2. Gravação 
 
fputs 
Protótipo: int fputs ( char*, FILE* ) ; 
 
Exemplo de chamada: fputs ( str, fp ) ; 
 
Grava a string str no arquivo dado por fp. A função retorna 0 (zero) se gravou 
corretamente ou outro valor em caso de erro. 
Obs.: Grava a string e para, não registrando mudança de linha. 
 
fputc 
Protótipo: int fputc ( int, FILE* ) ; 
 
Exemplo de chamada: fputc ( carac, fp ) ; 
 
Grava o caracter carac no arquivo dado por fp. Em caso de erro, retorna EOF. 
Obs.: Deve-se atentar para o fato de que se o arquivo for aberto no modo binário, EOF 
pode ser um caracter válido. 
 
fprintf 
Protótipo: int fprintf ( FILE*, char*, . . . ) ; 
 
Exemplo de chamada: fprintf ( fp, fmt, args ) ; 
 
Funciona de forma semelhante à função pritf. Lê as variáveis de args com o formato fmt 
no arquivo dado por fp. Em caso de sucesso, retorna o número de caracteres que foram 
gravados e em caso de erro, retorna um número negativo. 
Obs.: 
• Esta é a função mais usada. Faz tudo que a fputs faz. 
• As funções fscanf e fprintf realizam, respectivamente, leitura e gravação em strings. 
 
Exercício: Fazer um programa que copie um arquivo em disco para o outro. Os nomes 
dos arquivos são fornecidos com parâmetros na linha de comando, sendo o 
primeiro o nome do arquivo origem e o segundo o nome do arquivo destino. 
 
# include < stdio.h > 
# include < stdlib.h > 
# include < string.h > 
 
void main ( int argc, char *argv [ ] ) 
 { 
 50
 char buffer [512] ; 
 char arqorg [150], arqdst [150] ; 
 FILE *nome1, *nome2 ; 
 if ( argc < 3 ) 
 { 
 printf ( " \n Erro: Número de parâmetros incorreto " ) ; 
 exit (1) ; 
 } 
strcpy ( arqorg, argv [1] ) ; 
strcpy ( arqdst, argv [2] ) ; 
nome1 = fopen ( arqorg, " rb " ) ; 
nome2 = fopen ( arqdst, " wb " ) ; 
if ( nome1 = = NULL ⎥⎟ nome2 = = NULL ) 
{ 
printf ( " Erro na abertura dos arquivos. \n " ) ; 
exit (1) ; 
} 
while ( fgets ( buffer, 512, nome1 ) ) 
fprintf ( nome2, buffer ) ; 
fclose ( nome1 ) ; 
fclose ( nome2 ) ; 
} 
 
 
 
21.3. Principais funções para leitura e gavação direta ( binàrio ) 
 
Normalmente, para acesso direto abre-se o arquivo comobinário, para que a 
correspondência de caracteres seja de 1 para 1. 
 
21.3.1. Funções para posicionamento 
 
rewid 
Protótipo: void rewind ( FILE * ) ; 
 
Exemplo de chamada: rewid ( fp ) ; 
 
Coloca o ponteiro interno de I/O no início do arquivo dado por fp. 
 
fseek 
Protótipo: int fseek ( FILE*, long, int ) ; 
 
Exemplo de chamada: fseek ( fp, offset, origem ) ; 
 
Move o ponteiro interno de I/O do arquivo dado por fp conforme offset e origem. O 
parâmetro offset (do tipo long int) indica o número de bytes, a partir da origem, que o 
ponteiro deve se deslocar. Os valores possíveis para origem são: 
 0 ou SEEK_SET - início do arquivo 
 1 ou SEEK_CUR - posição atual do ponteiro 
 2 ou SEEK_END - final do arquivo 
A função fseek retorna 0 (zero) para sucesso e um valor diferente de 0 (zero) para erro. 
 51
Por exemplo, a instrução fseek (fp, 0l, SEEK_SET) é equivalente a rewind (fp). 
Obs.: Esta é uma função muito usada. 
 
ftell 
Protótipo: long ftell ( FILE *stream ) ; 
 
Exemplo de chamada: pos = ftell ( fp ) ; 
 
Retorna a posição atual (em bytes) do ponteiro do arquivo, em relação a origem. 
 
21.3.2. Leitura 
 
fread 
Protótipo: int fread ( void*, int, int, FILE* ) ; 
 
Exemplo de chamada: fread ( buf, tam, nblocos, fp ) ; 
 
Le para buf, nblocos de tam bytes do arquivo dado por fp. A função fread retorna o 
número de blocos lidos. 
 
 
 
 
 
 
21.3.3. Gravação 
 
fwrite 
Protótipo: int fwrite ( void*, int, int, FILE* ) ; 
 
Exemplo de chamada: int fwrite ( buf, tam, nblocos, fp ) ; 
 
Funciona como a fread, só que para gravação. A função retorna o número de blocos que 
foram gravados. 
 
Exemplo: Gravar e ler a variável double x em um arquivo. 
 . . . 
 double x ; 
 . . . 
 fwrite ( &x, sizeof ( double ), 1, fp ) ; 
 . . . 
 fread ( &x, sizeof ( double ), 1, fp ) ; 
 
Obs.: Na pesquisa, principalmente para gravação, após abrir e gravar, é recomendável 
fechar o arquivo, obrigando o descarregamento do buffer. A função fflush 
(FILE*); é outra alternativa para causar este mesmo efeito. 
 
Exercício: Fazer um oprograma que inicialize um vetor de 100 elementos com os 
números de 0 a 99, o grave em um arquivo e, em seguida, leia o conteúdo do 
arquivo para outro vetor. 
# include < stdio.h > 
# include < stdlib.h > 
 52
viod main ( ) 
 { 
 FILE *fp ; 
 int i ; 
 float v1[100], v2[100], *pv ; 
 for ( pv = v1, i = 0; i < 100; i++ ) 
 *pv++ = ( float ) i ; /* inicializa o vetor */ 
 
 /* abre o arquivo */ 
 if ( ( fp = fopen ( " teste.dat ", " wb " ) ) = = NULL ) 
 { 
 printf ( " \n Erro de abertura: gravação " ) ; 
 exit (1) ; 
 } 
 fwrite ( v1, sizeof ( float ), 100, fp ) ; /* grava vetor */ 
 fclose ( fp ) ; /* fecha o arquivo */ 
 
 /* reabre o arquivo */ 
 if ( ( fp = fopen ( " teste.dat ", " rb " ) ) = = NULL ) 
{ 
printf ( " \n Erro de abertura: leitura " ) ; 
exit (2) ; 
} 
fread ( v2, sizeof ( float ), 100, fp ) ; /* le para novo vet */ 
fclose ( fp ) ; /* fecha o arquivo */ 
 
for ( pv = v2, i = 0; i < 100; i ++ ) 
 printf ( " %.2f \n ", pv++ ) ; /* imprime vetor lido */ 
} 
 
Exercício: Fazer um programa que grave em um atrquivo três vetores de 100 elementos 
cada, sendo o primeiro formado pelos números de 0 a 99, o segundo os 100 primeiros 
numeros pares e o terceiro os os 100 elementos múltiplos de 3. A seguir, fechar o arquivo 
e reabrí-lo lendo e imprimindo cada um dos vetores. 
 
# include < stio.h > 
# include < stdlib.h > 
# define TAM 100 
void main ( ) 
 { 
 FILE *fp ; 
 int i ; 
 float v1( TAM ), *pv ; 
 
 /* Abre o arquivo testando se houver erro. */ 
 if ( ( fp = fopen ( " teste.vet ", " wb " ) ) = = NULL ) 
 { 
 printf ( " \n Erro ao abrir arquivo inicial. \n " ) ; 
 exit (1) ; 
 } 
 53
 for ( pv = v1, i = 0; i < TAM; i++ ) 
 pv++ = ( float ) i ; /* gera números de 0 a 99 */ 
 
 /* Grava apenas um bloco com tamanho v1, o que equivale a gravar ( sizeof ( float 
) * TAM ) bytes. */ 
 if ( fwrite ( v1, sizeof ( v1 ), 1, fp ) != 1 ) 
 { 
 printf ( " \n Erro ao gravar vetor 1. \n " ) ; 
 exit (2) ; 
 } 
 for ( pv = v1, i = 0; i < TAM; i ++ ) 
 *pv++ = ( float ) i * 2 ; /* vet: 0, 2, 4, . . . , 198 */ 
 
 /* Grava TAM blocos com o tamanho de float, o que equivale a gravar ( 
sizeof ( float ) * TAM ) bytes e testa se gravou tudo. */ 
 if ( fwrite ( v1, sizeof ( float ), TAM, fp ) != TAM ) 
 { 
 printf ( " \n Erro ao gravar vetor 2. \n " ) ; 
 exit (3) ; 
 } 
 
 for ( pv = v1, i = 0; i < TAM; i++ ) 
 *pv++ = ( float ) i * 3 ; /* vet: 0, 3, 6, . . . , 297 */ 
 /* Grava TAM blocos com o tamanho de float, o que equivale a gravar ( 
sizeof ( float ) * TAM ) bytes e testa se gravou tudo. */ 
 if ( fwrite ( v1, sizeof ( float ), TAM, fp ) != TAM ) 
{ 
printf ( " \n Erro ao gravar vetor 3. \n " ) ; 
exit (4) ; 
} 
 
fclose ( fp ) ; /* fecha o arquivo */ 
 
/* Reabre arquivo testando se houve erro. */ 
if ( ( fp = fopen ( " teste.vet ", " rb " ) ) = = NULL ) 
{ 
printf ( " \n Erro ao reabrir o arquivo. \n " ) ; 
exit (5) ; 
} 
 
fread ( vl, sizeof ( vl ), 1, fp ) ; /* lê primeiro vetor */ 
for ( pv = vl, i = 0; i < TAM; i++ ) 
printf ( " %f ", *pv++ ) ; /* imprime */ 
 
printf ( " \n \n " ) ; /* pula 2 linhas */ 
rewind ( fp ) ; /* volta ao inicio do arquivo */ 
 
/* A partir do inicio e com deslocamento de vl, aponta para o segundo vetor */ 
 
fseek ( fp, ( long ) sizeof ( vl ), 0 ) ; 
fread ( vl, sizeof ( float ), TAM, fp ) ; /* lê segundo vetor */ 
 54
 
for ( pv = vl, i = 0; i < TAM; i++ ) 
printf ( " %f ", *pv++ ) ; /* imprime */ 
printf ( " \n \n " ) ; /* pula 2 linhas */ 
rewind ( fp ) ; /* volta inicio arquivo */ 
 
/* A partir do início, com deslocamento de (2 * vl), aponta para o terceiro vetor */ 
 
fseek ( fp, ( long ) sizeof ( vl ) * 2, 0 ) ; 
fread ( vl, sizeof ( vl ), 1, fp ) ; /* lê terceiro vetor */ 
 
for ( pv = vl, i = 0; i < TAM; i++ ) 
printf ( " %f ", *pv++ ) ; /* imprime */ 
printf (" \n \n " ) ; /* pula duas linhas */ 
fclose ( fp ) ; /* fecha arquivo */ 
} 
 
 
 
 
 
 
Exemplo do uso de estruturas em arquivos: 
struct s_prop 
{ 
char nome [20] ; 
double mod ; 
double coef ; 
} ; 
 
typedef struct s_prop sprop ; 
sprop prop ; 
sprop variasprops [100] ; 
. . . 
/* grava uma propriedade */ 
fwrite ( &prop, sizeof ( sprop ), 1, fp ) ; 
. . . 
/* grava vetor com várias propriedades */ 
fwrite ( variasprops, sizeof ( sprop ), 100, fp ) ; 
. . . 
 
22. ALOCAÇÃO DINÂMICA DE MEMÓRIA 
 
A linguagem C possui uma série de funções que permitem alocar e liberar áreas de 
memória, fazendo com que se possa usar a memória gasta por