Escolar Documentos
Profissional Documentos
Cultura Documentos
1. INTRODUÇÃO
1.2. Histórico
• BCPL ⇒ B ⇒ C ⇒ C++
• C - Kernighan e Ritchie (K & R) - 1971
• C ANSI (American National Standards Institute) - 1989
definições
função_1
função_2
...
função_n
função_main
Obs.:
• toda instrução termina por ';'
• o protótipo da função printf está em stdio.h (standard IO)
• Protótipo em C
• Protótipo em C ANSI
2. IDENTIFICADORES
A maioria dos compiladores reconhece 31 caracteres tanto para variáveis quanto para
funções.
Ex.:
signed char ⇒ -128 a 127
unsigned int ⇒ 0 a 65535
4. DECLARACÃO DE VARIÁVEIS
Ex.:
int a, b, c;
float soma;
double somatorio, raiz_quadrada;
char sim, nao;
São aquelas declaradas dentro de um bloco e só são reconhecidas dentro deste bloco.
Exemplo 1 Exemplo 2
# include <stdio.h> void f1 ( )
void main ( ) {
{ int x ;
int i, j ; x = 5;
i = 2; ...
j = 2 * i; if ( a > 10 )
printf ( "0 dobro de %d = %d \ n", i, j ); {
} int x, y ;
x = 20 ;
No exemplo acima, 'i' e 'j' são variáveis locais da y = 40 ;
função "main". ...
}
}
Neste exemplo, a variável 'x' tem o
valor 5 quando sair do bloco mais
interno.
4
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 ) ;
}
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 ) ;
Área de Código
Área de Variáveis
Estáticas e Globais
Stack
Heap
5
Ex.:
int x ; <=> auto int x ;
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.
Quando uma função é definida como static, ela só pode ser ativada por uma função que
esteja no mesmo arquivo.
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 ) ;
...
}
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.1. printf
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.
5.2. scanf
Principais formatos:
• %d - inteiro
• %c - caracter
• %f - float
• %ld - long int
• %lf - double
• %s - string
Ex:
scanf ( " %d %d ", &a, &b ) ;
scanf ( " %d , %d ", &a, &b ) ;
6. INSTRUÇÕES DE CONTROLE
6.1. while
Obs.:
• Em C, verdadeiro é tudo o que for diferente de zero.
• Se o bloco possuir mais de uma instrução é necessário { }.
Solução 1 Solução 2:
# include < stdio.h >
void main ( ) # include < stdio.h >
{ void main ( )
int i, soma ; {
i = 1; int i = 1, soma = 0 ;
soma = 0 ; while ( i <= 5 )
while ( i <= 5 ) {
{ soma += i ;
soma = soma + i ; i ++ ;
i = i + 1; }
} printf ( " \n Soma = %d \n ", soma ) ;
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 = 2ea = 2
para b = a ++ ; ⇒ b = 1ea = 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
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);
6.4. if e derivados
Formatos:
10
6.5. if com ?
if ( a = = 10 ) Poderia ser: b = ( a = = 10 ) ? 20 : 30 ;
b = 20; ⇓ ⇓
else V F
11
b = 30;
6.6. switch
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
...
for ( i = 0 ; i < 10 ; i ++ )
{
if ( i = = 0 )
continue ; /* evita divisão por zero */
printf ( " %f \n ", ( float ) 10 / i ) ;
}
...
6.8. break
Ex.: ...
while ( 1 ) /* loop infinito */
{
scanf ( " %d ", &a ) ;
if ( a != 0 )
processa ( ) ;
else
break;
}
...
...
goto Label_1 ;
...
Label_l :
...
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.
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 */
}
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 ;
{
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.
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.
void main ( )
{
long fat, val ;
val = leval ( ) ;
15
long leval ( )
{
long x ;
printf ( " Entre o valor a ser calculado : " ) ;
scanf ( " %ld ", &x ) ;
return ( x ) ;
}
8. OPERADORES
() [ ] ->
! ~ ++ -- (type) * & sizeof
* / %
+ -
<< >>
< <= > >=
== !=
&
∧
|
&&
||
?
= += -= *= /=
,
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:
static float vet [ 10 ] ; ⇒ assume que o vetor vet tem 10 elementos, os quais
serão inicializados com zero (0)
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.
9.1. Strings
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
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" ;
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
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.
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 ) ;
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 ) ;
}
Solução 1:
Solução 2:
void main ( )
{
int tam ;
22
Exemplo:
int b [3] [5] ; ⇒ 3 linhas e 5 colunas
Inicialização na declaração:
static int b [ ] [3] = static int b [ ] [3] =
{ {
1, 1, 1, {l, 1, 1},
2, 2, 2, {2, 2, 2},
3, 3, 3 {3, 3, 3}
}; }
Obs.: A inicialização também pode ser feita de forma esparsa. Por exemplo:
1 1 0
1 0 0
0 0 0
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 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 )
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.
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.
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
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.1. Definição
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.
Exemplos:
int *p ; ⇒ p é um ponteiro para inteiro
float *pf ; ⇒ f é um ponteiro para float
i pt
5 1002
1002 2000
Dado:
float x, y, *p, *q ;
Erros comuns:
int *p ;
*p = 10 ; ⇒ p não apontou para nenhuma variável
float val ;
int *p ;
p = &val ; ⇒ tipos incompatíveis
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
*/
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 */
}
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
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 ) ;
}
Exemplo:
int k, a [100], *p ;
...
p = a; ⇔ p = &a [0]; ⇒ atribui a p o endereço do primeiro elemento do vetor a
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) ) ;
}
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
void main ( )
{
static char orig [ ] = "teste", dest [20] ;
strcpy ( dest, orig ) ;
printf ( "origem = %s ; destino = %s \n", dest, orig ) ;
}
Exercício: Criar a função strcmp que tem como finalidade comparar o conteúdo de duas
strings.
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
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.
b → ... ...
. .
. .
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:
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.
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
Exercício: Calcular a média dos elementos de uma coluna de uma matriz de 3 colunas.
Exercício: Calcular a média dos elementos de uma linha de uma matriz de 3 colunas.
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
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.
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 */
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]).
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 ;
Obs.:
• Uma vantagem de usar o typedef no 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++ ) ;
}
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
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
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
Exercício: Fazer um programa que leia duas strings de dados numéricos ou alfanuméficos
e as compare conforme o seu tipo.
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 ) ;
}
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
# 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 ) ;
...
# undef COMP
# define COMP 200
char arrayb [ TAM ][ COMP ] ;
18.2. # include
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.
# 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
Exemplos de 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
# 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
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> }
;
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
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
É 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 ;
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 */
É 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.
struct s_carta c =
{
12,
‘O’ /* inicializa como dama de ouros */
};
19.4. Uniões
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
/*
** 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’ ;
Uma estrutura deve ser sempre passada como parâmetro através de um ponteiro e 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 ;
}
void main ( )
{
sdata data ;
data . dia = 15 ;
data . mes = 3 ;
data . ano = 2000 ;
46
21. ARQUIVOS
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).
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 ",
Chamada: fclose ( fp ) ;
48
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* ) ;
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*, . . . ) ;
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* ) ;
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* ) ;
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*, . . . ) ;
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.
Normalmente, para acesso direto abre-se o arquivo como binário, para que a
correspondência de caracteres seja de 1 para 1.
rewid
Protótipo: void rewind ( FILE * ) ;
fseek
Protótipo: int fseek ( FILE*, long, int ) ;
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 ) ;
21.3.2. Leitura
fread
Protótipo: int fread ( void*, int, int, FILE* ) ;
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* ) ;
Funciona como a fread, só que para gravação. A função retorna o número de blocos que
foram gravados.
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 */
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.
/* 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 */
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 variáveis, vetores,
matrizes, listas, pilhas etc., de uma forma mais eficiente.
Quando necessário, o usuário aloca uma área para utilização e, quando ela não for mais
necessária, é devolvida ao sistema operacional, de forma a poder ser utilizada em outra
etapa do programa.
O esquema abaixo apresenta o mapa da memória de um programa em C:
55
Área de Código
Área de Variáveis
Estáticas e Globais
Stack
Heap
malloc
Protótipo: void *malloc ( unsigned int ) ;
Aloca uma área de tam bytes. Se alocou corretamente, retorna um pointer para o
primeiro byte da área alocada e em caso de erro retoma NULL.
Exemplo1:
int *ptr ;
...
ptr = ( int ) malloc ( sizeof ( int ) ) ;
if ( ptr = = NULL )
{
printf ( " Erro de alocação " ) ;
exit ( l ) ;
}
*ptr = 10 ;
...
free ( ptr ) ;
Exemplo 2:
int *ptr, *ptraux, i ;
...
ptr = ( int ) malloc ( sizeof ( int ) * 100 ) ;
if ( ptr = = NULL ) /* ou ( ! ptr ) */
{
printf ( " Erro de alocação " ) ;
exit ( l ) ;
}
ptraux = ptr ;
for ( i = 0; i < 100; i++ )
*ptr++ = i ;
ptr = ptraux ;
...
56
Obs.: Neste exemplo foi necessária a criação de um ponteiro auxiliar (ptraux) para
preservar o endereço inicial da área alocada (ptr).
Exemplo 3:
/* Aloca área para um vetor do tipo float. O número de elementos do vetor é pedido pelo
** programa. Após a obtenção da área necessária são pedidos os dados para o seu
** preenchimento. */
void main ( )
{
int num, i ;
float *pvet, *pinic ;
calloc
Protótipo: void *calloc ( unsigned int, unsigned int ) ;
Aloca uma área de (tam * num) bytes. Se alocou corretamente, retorna um pointer para
o primeiro byte da área alocada e em caso de erro retorna NULL. A área alocada é
automaticamente zerada.
Exemplo1:
int *ptr ;
...
ptr = calloc ( l, sizeof ( int ) ) ;
if ( ptr = = NULL )
{
printf ( " Erro de alocação " ) ;
exit ( l ) ;
}
*ptr = 10 ;
...
Exemplo 2:
int *ptr, *ptraux, i ;
...
ptr = calloc ( 100, sizeof ( int ) ) ;
if ( ptr = = NULL )
{
printf ( " Erro de alocação " ) ;
exit ( l ) ;
}
ptraux = ptr ;
for ( i = 0; i < 100; i++ )
*ptr++ = i ;
ptr = ptraux ;
...
free
Protótipo: void free ( void* ) ;
Exemplo:
int *ptr, *ptraux, i ;
...
ptr = malloc ( sizeof ( int ) * 100 ) ;
if ( ptr = = NULL )
{
printf ( " Erro de alocação " ) ;
exit ( l ) ;
}
ptraux = ptr ;
for ( i = 0; i < 100; i++ )
*ptr++ = i ;
ptr = ptraux ;
...
free ( ptr ) ;
...
realloc
Protótipo: void *realloc ( void*, unsigned int ) ;
A função realloc altera o tamanho da área alocada por malloc ou calloc. O novo tamanho
pode ser maior ou menor do que o tamanho original. Quando chamada, retorna um
pointer para a nova área alocada, sendo old um pointer para a área antiga e tam o novo
tamanho em bytes. Em caso de erro, a função retorna NULL e o programa deve ser
interrompido, já que a área original poderá estar comprometida.
Exemplo:
# include < stdio.h >
# include < stdlib.h >
void main ( )
{
int num, i ;
char *p ;
p = ( char* ) malloc ( 23 ) ;
strcpy ( p, " AQUI TEM 22 CARACTERES " ) ;
printf ( " \n %s \n ", p ) ;
p = realloc ( p, 24 ) ;
strcat ( p, " . " ) ;
printf ( " \n %s \n ", p ) ;
free ( p ) ;
}