Logo Passei Direto
Buscar

Apostila Ling C Completa

User badge image

Enviado por Laurine Goulart em

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

UNIVERSIDADE DE CAXIAS DO SUL
CENTRO DE CIÊNCIAS EXATAS E TECNOLÓGICAS
DEPARTAMENTO DE INFORMÁTICA
LINGUAGEM C
Professor Maurício Galimberti
Professor André Gustavo Adami
LINGUAGEM C
2/103
Sumário
1. INTRODUÇÃO .............................................................................................................................. 8
1.1 VISÃO GERAL DE UM PROGRAMA C ........................................................................................... 8
2. ESTRUTURA BÁSICA DE UM PROGRAMA EM C ............................................................... 9
2.1 BIBLIOTECAS ............................................................................................................................. 9
3. TIPOS DE DADOS....................................................................................................................... 10
3.1 ENUMERAÇÃO ......................................................................................................................... 11
3.2 MODIFICADORES DE TIPO DE ACESSO ..................................................................................... 12
3.2.1 Const............................................................................................................................... 12
3.2.2 Volatile ........................................................................................................................... 12
3.3 CONSTANTES ........................................................................................................................... 12
3.4 CONSTANTES PRÉ-DEFINIDAS................................................................................................... 13
3.5 AS BIBLIOTECAS DISPONÍVEIS E ALGUMAS FUNÇÕES INTERESSANTES ...................................... 14
4. COMANDO DE ESCRITA ......................................................................................................... 16
4.1 MODIFICADORES DE SAÍDA...................................................................................................... 17
4.2 EXERCÍCIOS ............................................................................................................................. 18
5. VARIÁVEIS.................................................................................................................................. 19
5.1 NOME DE VARIÁVEIS............................................................................................................... 19
5.2 DECLARAÇÃO DE VARIÁVEIS................................................................................................... 19
5.2.1 Tipos de Declarações de Variáveis ................................................................................ 19
5.2.1.1 Variáveis locais ......................................................................................................................... 20
5.2.1.2 Parâmetros Formais................................................................................................................... 20
5.2.1.3 Variáveis Globais ...................................................................................................................... 20
5.3 PALAVRAS RESERVADAS ......................................................................................................... 21
6. OPERADORES ............................................................................................................................ 22
6.1 ATRIBUIÇÃO ............................................................................................................................ 22
6.1.1 Conversão de Tipos em Atribuições ............................................................................... 22
6.2 OPERADORES ARITMÉTICOS .................................................................................................... 23
6.2.1 Incremento e Decremento............................................................................................... 23
6.2.2 Operadores Aritméticos de Atribuição ........................................................................... 24
6.3 OPERADORES RELACIONAIS E LÓGICOS................................................................................... 24
6.4 OPERADORES BIT A BIT........................................................................................................... 24
6.5 OPERADOR ?............................................................................................................................ 25
6.6 OPERADORES DE PONTEIROS & E * ......................................................................................... 25
6.7 OPERADOR VÍRGULA............................................................................................................... 26
6.8 EXPRESSÕES............................................................................................................................ 26
6.8.1 Conversão de Tipos em Expressões................................................................................ 26
6.8.2 Casts ............................................................................................................................... 27
6.8.3 Espacejamento e Parênteses........................................................................................... 28
7. ALGUMAS FUNÇÕES DE E/S .................................................................................................. 29
7.1 FUNÇÃO SCANF() ..................................................................................................................... 29
7.2 FUNÇÃO GETCHE() E GETCH().................................................................................................. 30
7.3 LENDO E ESCREVENDO CARACTERES....................................................................................... 30
LINGUAGEM C
3/103
7.4 EXERCÍCIOS ............................................................................................................................. 31
8. COMANDOS CONDICIONAIS ................................................................................................. 32
8.1 COMANDO IF............................................................................................................................ 32
8.2 COMANDO SWITCH .................................................................................................................. 32
8.3 EXERCÍCIOS ............................................................................................................................. 33
9. COMANDOS REPITITIVOS OU LAÇOS................................................................................ 35
9.1 LAÇO FOR ................................................................................................................................ 35
9.2 LAÇO WHILE ............................................................................................................................ 35
9.3 LAÇO DO-WHILE ...................................................................................................................... 36
9.4 COMANDOS BREAK, CONTINUE E EXIT() .................................................................................. 36
9.5 EXERCÍCIOS ............................................................................................................................. 37
10. FUNÇÕES..................................................................................................................................... 39
10.1 LOCALIZAÇÃO DAS FUNÇÕES ................................................................................................... 40
10.1.1 Corpo da função antes do programa principal (no mesmo arquivo) ............................. 40
10.1.2 Corpo da função depois do programa principal (no mesmo arquivo) ........................... 40
10.1.3 Corpo da função escrito em arquivo separado ..............................................................
41
10.2 ARGUMENTOS PARA FUNÇÃO MAIN()....................................................................................... 42
10.3 PROTÓTIPO DE FUNÇÕES .......................................................................................................... 42
10.4 RETORNO DE PONTEIROS......................................................................................................... 42
10.5 NÚMEROS DE PARÂMETROS VARIÁVEL.................................................................................... 43
10.5.1 Funções para número variável de argumentos .............................................................. 43
10.6 CLASSES DE ARMAZENAMENTO .............................................................................................. 44
10.6.1 Classe de Armazenamento - auto ................................................................................... 44
10.6.2 Classe de Armazenamento - extern................................................................................. 44
10.6.3 Classe de Armazenamento - static .................................................................................. 45
10.6.3.1 Variáveis Locais static ............................................................................................................. 45
10.6.3.2 Variáveis Globais static............................................................................................................ 45
10.6.4 Classe de Armazenamento - register .............................................................................. 45
10.7 DIRETIVA #DEFINE................................................................................................................... 45
10.8 FUNÇÕES RECURSIVAS ............................................................................................................ 46
10.9 EXERCÍCIOS ............................................................................................................................. 46
11. VETORES E MATRIZES ........................................................................................................... 47
11.1 INICIALIZAÇÃO DE VETORES E MATRIZES................................................................................ 47
11.2 MATRIZES E VETORES COMO ARGUMENTO DE FUNÇÕES ......................................................... 48
11.3 LIMITES ................................................................................................................................... 48
11.4 STRINGS .................................................................................................................................. 49
11.4.1 Leitura de Strings ........................................................................................................... 49
11.4.1.1 Função scanf().......................................................................................................................... 49
11.4.1.2 Função gets()............................................................................................................................ 50
11.4.2 Escrita de Strings............................................................................................................ 50
11.4.2.1 Função printf() ......................................................................................................................... 50
11.4.2.2 Função puts() ........................................................................................................................... 50
11.4.3 Funções de Manipulação de Strings .............................................................................. 51
11.4.3.1 Função strlen() ......................................................................................................................... 51
11.4.3.2 Função strcat() ......................................................................................................................... 51
11.4.3.3 Função strcmp() ....................................................................................................................... 52
11.4.3.4 Função strcpy() ........................................................................................................................ 52
11.4.3.5 Função strchr() ......................................................................................................................... 52
11.4.3.6 Função strstr() .......................................................................................................................... 52
11.4.4 Exemplo Geral................................................................................................................ 52
LINGUAGEM C
4/103
11.5 EXERCÍCIOS ............................................................................................................................. 53
12. PONTEIROS................................................................................................................................. 55
12.1 EXPRESSÕES COM PONTEIROS ................................................................................................. 55
12.1.1 Atribuição de Ponteiros.................................................................................................. 55
12.1.2 Aritmética de Ponteiros .................................................................................................. 56
12.2 INICIALIZAÇÃO DE PONTEIROS................................................................................................. 56
12.2.1 Comparação de Ponteiros .............................................................................................. 57
12.3 PONTEIROS E MATRIZES .......................................................................................................... 57
12.3.1 Matrizes de Ponteiros ..................................................................................................... 58
12.3.2 Acessando partes de Matrizes como vetores .................................................................. 59
12.4 INDIREÇÃO MÚLTIPLA ............................................................................................................. 59
12.5 PONTEIROS PARA FUNÇÕES ..................................................................................................... 60
12.6 MAIS SOBRE DECLARAÇÕES DE PONTEIROS ............................................................................ 61
12.7 EXERCÍCIOS ............................................................................................................................. 63
13. ESTRUTURAS E UNIÕES.......................................................................................................... 64
13.1 ESTRUTURAS ........................................................................................................................... 64
13.1.1 Inicializando Estruturas ................................................................................................. 65
13.1.2 Estruturas Aninhadas ..................................................................................................... 65
13.1.3 Estruturas e funções ....................................................................................................... 66
13.1.4 Vetor de Estruturas......................................................................................................... 67
13.1.5 Ponteiros para Estruturas .............................................................................................. 67
13.2 CAMPOS DE BITS ..................................................................................................................... 68
13.3 UNIÕES.................................................................................................................................... 69
13.4 SIZEOF() .................................................................................................................................. 71
13.5 TYPEDEF ................................................................................................................................. 71
13.6 EXERCÍCIOS ............................................................................................................................. 72
14. ALOCAÇÃO DINÂMICA........................................................................................................... 73
14.1 FUNÇÕES DE ALOCAÇÃO DINÂMICA EM C ............................................................................... 73
14.1.1 Função malloc() ............................................................................................................. 73
14.1.2 Função calloc() .............................................................................................................. 74
14.1.3 Função free() .................................................................................................................. 75
14.1.4 Função realloc() ............................................................................................................. 76
14.2 MATRIZES DINAMICAMENTE ALOCADAS................................................................................. 76
14.3 LISTAS ENCADEADAS .............................................................................................................. 78
14.3.1 Listas Singularmente Encadeadas.................................................................................. 78
14.3.2 Listas Duplamente Encadeadas...................................................................................... 79
14.4 ÁRVORES BINÁRIAS ................................................................................................................ 81
14.5 EXERCÍCIOS ............................................................................................................................. 83
15. E/S COM ARQUIVO ................................................................................................................... 84
15.1 E/S ANSI X E/S UNIX ........................................................................................................... 84
15.2 STREAMS ................................................................................................................................. 84
15.3 ARQUIVOS ............................................................................................................................... 84
15.4 SISTEMA DE ARQUIVOS ........................................................................................................... 85
15.5 ESTRUTURA FILE.................................................................................................................... 85
15.6 ABERTURA DE ARQUIVOS........................................................................................................ 85
15.7 FECHAMENTO DE ARQUIVO ..................................................................................................... 86
15.8 LEITURA E GRAVAÇÃO DE CARACTERES.................................................................................. 86
LINGUAGEM C
5/103
15.8.1 Gravação ........................................................................................................................ 87
15.8.2 Leitura ............................................................................................................................ 87
15.9 VERIFICANDO FIM DE ARQUIVO ............................................................................................... 87
15.10 TRABALHANDO COM ARQUIVOS .............................................................................................. 88
15.11 TRABALHANDO COM STRINGS: FPUTS() E FGETS() ................................................................... 89
15.12 FUNÇÕES DE TRATAMENTO DE ARQUIVOS ............................................................................... 89
15.12.1 rewind() ...................................................................................................................... 89
15.12.2 ferror() ........................................................................................................................ 90
15.12.3 remove()...................................................................................................................... 90
15.12.4 fflush()......................................................................................................................... 91
15.12.5 Função ftell() .............................................................................................................. 91
15.13 LENDO E GRAVANDO REGISTROS ............................................................................................. 91
15.13.1 Escrita de um bloco de dados..................................................................................... 91
15.13.2 Leitura de um bloco de dados..................................................................................... 92
15.13.3 Utilizando os comandos de leitura e gravação de registros....................................... 92
15.14 ACESSO ALEATÓRIO................................................................................................................. 93
15.15 COMANDO DE GRAVAÇÃO EM MODO TEXTO FORMATADO ....................................................... 94
15.16 CONDIÇÕES DE ERRO ............................................................................................................... 95
15.17 STREAMS PADRÃO................................................................................................................... 95
15.18 EXERCÍCIO:.............................................................................................................................. 96
15.19 SISTEMA DE ARQUIVO TIPO UNIX........................................................................................... 96
15.19.1 open().......................................................................................................................... 97
15.19.2 creat() ......................................................................................................................... 98
15.19.3 close() ......................................................................................................................... 98
15.19.4 read() e write()............................................................................................................ 98
15.19.5 unlink() ..................................................................................................................... 100
15.19.6 lseek() e tell() ............................................................................................................ 100
15.19.7 Funções para manipulação de buffers ..................................................................... 100
16. BIBLIOGRAFIA ........................................................................................................................ 103
LINGUAGEM C
6/103
Lista de Figuras
FIGURA 1.1 - CODIFICAÇÃO DE UM PROGRAMA C................................................................................... 8
FIGURA 12.1 INDIREÇÃO SIMPLES E MÚLTIPLA ..................................................................................... 60
FIGURA 14.1 EXEMPLO DE UMA ÁRVORE BINÁRIA................................................................................ 82
LINGUAGEM C
7/103
Lista de Tabelas
TABELA 3.1 - TIPOS DE DADOS BÁSICOS.................................................................................................. 10
TABELA 3.2 - UTILIZAÇÃO DOS MODIFICADORES DE TIPOS DE DADOS ..................................................... 10
TABELA 3.3 - EXEMPLO DE CONSTANTES ................................................................................................ 13
TABELA 4.1 - CÓDIGO PARA OS CARACTERES ESPECIAIS .........................................................................
16
TABELA 4.2 - CÓDIGOS PARA FORMATAÇÃO DE IMPRESSÃO.................................................................... 16
TABELA 6.1 - CONVERSÕES DE TIPOS E SEUS EFEITOS ............................................................................. 22
TABELA 6.2 - OPERADORES ARITMÉTICOS .............................................................................................. 23
TABELA 6.3 - OPERADORES ARITMÉTICOS DE ATRIBUIÇÃO ..................................................................... 24
TABELA 6.4 - OPERADORES RELACIONAIS E LÓGICOS.............................................................................. 24
TABELA 6.5 - OPERADORES BIT-A-BIT..................................................................................................... 24
TABELA 6.6 - EXEMPLO DE OPERAÇÕES DE DESLOCAMENTO BIT-A-BIT................................................... 25
TABELA 7.1 - CÓDIGOS DE FORMATAÇÃO DO COMANDO SCANF() ........................................................... 29
TABELA 15.1 - FUNÇÕES MAIS COMUNS DO SISTEMA DE ARQUIVO COM BUFFER ..................................... 85
TABELA 15.2 - OS MODOS DE ABERTURA VÁLIDOS ................................................................................. 86
TABELA 15.3 – MACROS DEFINIDAS EM STDIO.H PARA AS POSIÇÕES PERMITIDAS NA FUNÇÃO FSEEK().93
TABELA 15.3 - FUNÇÕES DE E/S TIPO UNIX SEM BUFFER ...................................................................... 96
TABELA 15.4 MODOS DE ABERTURA DE ARQUIVOS EM BAIXO NÍVEL ...................................................... 97
LINGUAGEM C Introdução
8/103
1. Introdução
A linguagem C foi criada por Dennis M. Ritchie e Ken Thompson no Laboratório Bell em 1972,
baseada na linguagem B de Thompson que era uma evolução da antiga BCPL.
A linguagem C tornou-se muito popular devido a características como:
• O C é uma linguagem de alto nível com uma sintaxe bastante estruturada e flexível tornando sua
programação bastante simplificada.
• Programas em C são compilados, gerando programas executáveis.
• O C compartilha recursos tanto de alto quanto de baixo nível, pois permite acesso e programação
direta do microprocessador. Com isto, rotinas cuja dependência do tempo é crítica, podem ser
facilmente implementadas usando instruções em Assembly. Por esta razão o C é a linguagem
preferida dos programadores de aplicativos.
• O C é uma linguagem estruturalmente simples e de grande portabilidade. O compilador C gera
códigos mais enxutos e velozes do que muitas outras linguagens.
• Embora estruturalmente simples (poucas funções intrínsecas) o C não perde funcionalidade pois
permite a inclusão de uma farta quantidade de rotinas do usuário. Os fabricantes de compiladores
fornecem uma ampla variedade de rotinas pré-compiladas em biblioteca
Como uma ferramenta poderosa a linguagem C é usada na construção de vários aplicativos como
sistemas operacionais, planilhas eletrônicas, processadores de texto, sistemas de transmissão de dados, entre
outros. Um exemplo clássico é o sistema operacional UNIX, o qual foi implementado em C.
No início da década de 80 a linguagem C é padronizada pelo American National Standard Institute:
surge o ANSI C.
Atualmente, a linguagem C vem sendo usada para desenvolver novas linguagens, entre elas a linguagem
C++ e Java.
1.1 Visão geral de um programa C
A geração do programa executável a partir do programa fonte obedece a uma seqüência de operações
ante de tornar-se um executável. Depois de escrever o módulo fonte em um editor de textos, o programador
aciona o compilador. Essa ação desencadeia uma seqüência de etapas, cada qual traduzindo a codificação do
usuário para uma forma de linguagem de nível inferior, que termina com o executável criado pelo linkador.
FIGURA 1.1 - Codificação de um programa C
Editor
(módulo fonte em C)
Pré-processador
(novo fonte expandido)
Compilador
(arquivo objeto)
Linkador
(executável)
LINGUAGEM C Estrutura Básica de um Programa em C
9/103
2. Estrutura Básica de um Programa em C
Um programa C consiste em uma ou várias “funções”. Baseado nesta afirmação, deve existir um
mecanismo que garanta que todos os programas inicializem da mesma forma. Este mecanismo pode ser
entendido como a padronização da função que será chamada primeiramente. Tal função chama-se main().
Sintaxe:
main() primeira função a ser executada
{ inicia o corpo da função
} termina a função
Abaixo relacionam-se algumas regras para a programação em C:
• Toda função C deve ser iniciada e encerrada por um abre chaves e um fecha chaves
respectivamente;
• O nome da função, os parênteses e as chaves são os únicos elementos obrigatórios de uma
função;
• Todas as instruções devem estar dentro das chaves;
• As instruções C são sempre encerradas por um ponto-e-vírgula;
2.1 Bibliotecas
Para não ter que criar programas grandes devido a linkedição de um arquivo objeto a linguagem inclui
as bibliotecas para que no momento da compilação seja gerado um programa com as funções utilizadas.
Para fazer isto, usa-se uma diretiva de compilação a qual é colocada fora de qualquer função do
programa. A diretiva é #include instrui o compilador a ler outro arquivo-fonte.
Sintaxe:
#include nome_arq
onde:
nome_arq tem que estar entre aspas ou símbolos de maior ou menor
Em algumas plataformas existe somente uma das duas formas. No caso da plataforma DOS/Windows
95, aspas significa para o compilador que a biblioteca em questão deve ser buscada primeiramente no diretório
atual e depois no diretório padrão do turbo ou pelas variáveis de ambiente. Também, os caracteres “<>“
significam ao compilador que deve procurar somente no diretório padrão ou nos diretórios definidos pelas
variáveis de ambiente.
Exemplo 2.1
#include <stdio.h>
Não se usa o ponto-e-vírgula após diretivas de compilação. Este tipo de declaração é muito usado nos
programas profissionais. Cada comando pré-definido em C está prototipado em alguma biblioteca (isto é
informado conjuntamente ao comando).
LINGUAGEM C Tipos de Dados
10/103
3. Tipos de Dados
Em C existem 5 tipos de variáveis básicas. Nos computadores da linha IBM-PC (sistema operacional 16
bits) a tabela 3.1 é válida.
Tabela 3.1 - Tipos de dados básicos
TIPO BIT FAIXA MÍNIMA
char 8 -128 a 127
int 16 - 32768 a 32767
float 32 3.4E-38 a 3.4E+38
double 64 1.7E-308 a 1.7E+308
void 0 sem valor
Com exceção de void, os tipos de dados básicos podem estar acompanhados por modificadores na
declaração de variáveis. Os modificadores de tipos da linguagem C são:
• signed;
• unsigned;
 
 
• long;
• short.
Os modificadores signed, short, long e unsigned podem ser aplicados aos tipos básicos
caractere e inteiro. Contudo, long também pode ser aplicado a double. A tabela 3.2 mostra todas a
combinações de tipos de dados
Tabela 3.2 - Utilização dos modificadores de tipos de dados
TIPO BIT FAIXA MÍNIMA
unsigned char 8 0 a 255
signed char 8 -128 a 127
unsigned int 16 0 a 65535
signed int 16 O mesmo que int
short int 16 O mesmo que int
unsigned short int 16 0 a 65535
signed short int 16 O mesmo que short int
long int 32 -2.147.483.647 a 2.147.483.647
signed long int 32 -2.147.483.647 a 2.147.483.647
unsigned long int 32 0 a 4.294.967.925
long double 128 Dez dígitos de precisão
O uso de signed com inteiros é redundante. No entanto, ele é permitido porque a declaração
default de inteiros assume um número com sinal. O uso mais importante de signed é modificar char em
implementações em que esse tipo, por padrão, não tem sinal. Algumas implementações podem permitir
que unsigned seja aplicado aos tipos de ponto flutuante (como em unsigned double). Porém, isso reduz a
portabilidade de seu código e geralmente não é recomendado. O modificador unsigned altera o valor da
faixa mínima do tipo através do uso do bit mais significativo (indicador de sinal).
No caso do sistema operacional ser 32 bits o tipo int será o único a ser alterado, pois ele passa de
2 bytes para 4 bytes de armazenamento. Isto também faz com que o intervalo de valores seja aumentado.
Exemplo 3.1
main()
{
 int qtde;
 char tam;
 float total;
 qtde = 2; tam = ‘G’;
 total = 20.70;
 printf(“Comprei %d camisas de tamanho %c.”, qtde, tam);
LINGUAGEM C Tipos de Dados
11/103
 printf(“\nNo total, paguei R$ %f.”, custo);
}
Execução:
Comprei 2 camisas de tamanho G.
No total, paguei R$ 20.70.
As variáveis podem ser inicializadas no momento em que se faz a declaração das mesmas. Pode-
se ver isto usando o programa anterior, que a execução será a mesma da versão anterior.
main()
{
 int qtde=2;
 char tam=‘G’;
 float total=20.70;
 printf(“Comprei %d camisas de tamanho %c.”, qtde, tam);
 printf(“\nNo total, paguei R$ %f.”, custo);
}
3.1 Enumeração
Enumeração é um conjunto de constantes inteiras que especifica todos os valores legais de uma
variável desse tipo pode ser. A forma geral para enumeração é:
Sintaxe:
enum nome { lista_de_enumeração } lista_de_variáveis;
Aqui, tanto o nome da enumeração quanto a lista de variáveis são opcionais. O nome da
enumeração é usado para declarar variáveis daquele tipo. Com isso pode-se declarar as cores
Exemplo 3.2
enum cores {amarelo, verde, vermelho};
enum cores semaforo;
Dada essa definição e declaração, os tipos de comandos seguintes são perfeitamente válidos:
semaforo = verde;
if (semaforo==verde) printf(“Passagem permitida \n”);
Para melhor compreensão da enumeração entende-se que cada símbolo representa um valor
inteiro. O valor do primeiro símbolo da enumeração é 0. Assim,
printf (“%d %d”, verde, vermelho);
mostra 1 2 na tela.
Como extensão, pode-se inicializar os símbolos de forma alternada para algum problema
específico.
Exemplo 3.3
enum cores { amarelo, verde=10, vermelho };
Agora os valores destes símbolos são
amarelo 0
verde 10
vermelho 11
LINGUAGEM C Tipos de Dados
12/103
3.2 Modificadores de Tipo de Acesso
O padrão ANSI introduziu dois novos modificadores de tipo que controlam a maneira como a
variáveis podem ser acessadas ou modificadas. Esses modificadores são const e volatile. Devem preceder
os modificadores de tipo e os nomes que eles modificam.
3.2.1 Const
Variáveis do tipo const não podem ser modificadas por seu programa (por isso ela recebe um
valor inicial).
Exemplo 3.4
const int a=10;
O exemplo 5.4 cria uma variável inteira chamada a, com um valor inicial 10, que seu programa
não pode modificar.
Um exemplo do uso do const é para verificar se uma variável em particular é modificada pelo
seu programa.
3.2.2 Volatile
O modificador volatile é usado para informar ao compilador que o valor de uma variável pode
ser alterado de maneira não explicitamente especificada pelo programa. Por exemplo, um endereço de
uma variável global pode ser passado para a rotina de relógio do sistema operacional e usado para guardar
o tempo real do sistema. Nessa situação, o conteúdo de uma variável é alterado sem nenhum comando de
atribuição explicito no programa. Isso ajuda o programa no sentido de avisar ao compilador que o
conteúdo de uma variável é mutável, mesmo que sua referência não aparecer no lado esquerdo da
expressão.
É possível usar const e volatile juntos. Por exemplo, se 0x30 é assumido como sendo o valor de
uma porta que é mudado por condições externas. Para evitar efeitos colaterais deve-se declarar da
seguinte forma:
 const volatile unsigned char *port = 0x30;
3.3 Constantes
Uma constante tem valor fixo e inalterável durante a execução do programa. Isto pode ser
exemplificado pelos exemplos 3.1 e 3.2 da função printf().
Em uma constante caractere é escrita entre aspas simples (‘’), uma constante cadeia de caracteres
entre aspas duplas (“”) e constantes numéricas como o número propriamente dito.
Exemplo 3.5
‘C’
“programa”
8
465.67
LINGUAGEM C Tipos de Dados
13/103
Constantes em C podem ser de qualquer um dos cinco tipos de dados básicos. A maneira como
cada constante é representada depende do seu tipo. Pode-se especificar precisamente o tipo da constante
numérica através da utilização de um sufixo. Para tipos em ponto flutuante coloca-se um F após o número,
ele será tratado como float. Se for colocado um L, ele tornar-se-á um long double. Para tipos inteiros, o
sufixo U representa unsigned e o L representa long. A tabela 5.3 mostra alguns exemplos de constantes.
Tabela 3.3 - Exemplo de constantes
Tipo de Dado Exemplo de Constantes
int 1 123 21000 -234
long int 35000L -34L
short int 10 -12 90
unsigned int 10000U 987U 40000“
float 123.23F 2.34e-3F
Double 123.23 12312333 -0.9876324
long double 1001.2L
Além deste tem-se as constantes Hexadecimais e Octais. Usam-se tais sistemas numéricos para
facilitar a programação. Uma constante hexadecimal deve consistir em um 0x seguido por uma constante
na forma hexadecimal. Uma constante octal começa com 0.
Exemplo 3.6
int hex = 0x80; /* 128 em decimal */
int oct = 012; /* 10 em decimal */
3.4 Constantes pré-definidas
Em alguns compiladores C, algumas constantes simbólicas já estão pré-definidas. Estas
constantes em geral definam alguns valores matemáticos (pi, pi/2, e, etc.), limites de tipos etc. A seguir
segue uma tabela contendo algumas (existem muitas outras) constantes simbólicas pré-definidas no
compilador Turbo C++ da Borland.
Biblioteca Constante Valor Significado
math.h M_PI 3.14159... pi
math.h M_PI_2 1.57079... pi/2
math.h M_PI_4 0,78539... pi/4
math.h M_1_PI 0,31830... 1/pi
math.h M_SQRT2 1,41421... √2
conio.h BLACK 0 valor da cor (preto)
conio.h BLUE 1 valor da cor (azul)
conio.h GREEN 2 valor da cor (verde)
conio.h CYAN 3 valor da cor (cyan)
conio.h RED 4 valor da cor (vermelho)
conio.h MAGENTA 5 valor da cor (magenta)
limits.h INT_MAX 32767 limite superior do tipo int
limits.h INT_MIN -32768 limite inferior do tipo int
LINGUAGEM C Tipos de Dados
14/103
3.5 As bibliotecas disponíveis e algumas funções interessantes
A seguir segue uma lista de algumas as bibliotecas disponíveis no compilador Turbo C++
Borland: Ao longo do texto veremos o uso de muitas funções cobrindo uma boa parte destas bibliotecas,
porém o leitor que desejar tornar-se "fluente" na linguagem C pode (e deve) estudá-las com profundidade.
alloc.h assert.h bcd.h bios.h complex.h
conio.h ctype.h dir.h dirent.h dos.h
errno.h fcntl.h float.h fstream.h generic.h
graphics.h io.h iomanip.h iostream.h limits.h
locale.h malloc.h math.h mem.h process.h
setjmp.h share.h signal.h stdarg.h stddef.h
stdio.h stdiostr.h stdlib.h stream.h string.h
strstrea.h sys\stat.h sys\timeb.h sys\types.h time.h
values.h
Vejamos algumas funções disponíveis nas bibliotecas C.
Biblioteca math.h
int abs(int i);
double fabs(double d);
Calcula o valor absoluto do inteiro i e do real d, respectivamente.
double sin(double arco);
double cos(double arco);
double tan(double arco);
double asin(double arco);
double acos(double arco);
double atan(double arco);
Funções trigonométricas do ângulo arco, em radianos.
double ceil(double num);
double floor(double num);
Funções de arredondamento para inteiro.
ceil() arredonda para cima. Ex. ceil(3.2) == 4.0;
floor() arredonda para baixo. Ex. floor(3.2) == 3.0;
LINGUAGEM C Tipos de Dados
15/103
double log(double num);
double log10(double num);
Funções logarítmicas: log() é logaritmo natural (base e), log10() é
logaritmo decimal (base 10).
double pow(double base, double exp);
Potenciação: pow(3.2,5.6) = 3.25.6.
double sqrt(double num);
Raiz quadrada: sqrt(9.0) = 3.0.
Biblioteca stdlib.h
int random(int num);
Gera um número inteiro aleatório entre 0 e num - 1.
LINGUAGEM C Comando de Escrita
16/103
4. Comando de Escrita
A função printf() é uma das funções de E/S (entrada e saída) que mostra na tela os parâmetros
que são passados como argumento. Seu protótipo está na biblioteca stdio.h.
Sintaxe:
printf(“<expressao_de_controle>”, lista_de_argumentos)
onde:
expressão_de_controle = contém caracteres que serão exibidos na tela e códigos de formatação
que indicam o formato em que os argumentos (da
lista_de_argumentos) devem ser impressos.
lista_de_argumentos = lista de argumentos que serão apresentadas na expressão_de_controle. Tal
lista não tem tamanho máximo e cada argumento deve ser separado
por uma vírgula.
A tabela 4.1 mostra os códigos na linguagem para caracteres que não podem ser inseridos
diretamente do teclado.
Tabela 4.1 - Código para os caracteres especiais
CÓDIGOS ESPECIAIS SIGNIFICADO
\n Nova Linha (LF)
\t Tab (HT)
\b Retrocesso (BS)
\” ”
\\ Barra invertida
\f Salta Página de Formulário (FF)
\r Retorno de Carro (CR)
\’ ’
\v Tabulação Vertical
\a Alerta (beep)
\N Constante octal (onde N é uma constante octal)
\xN Constante Hexadecimal (onde N é uma constante
hexadecimal)
\0 Nulo
Além destes, existem os códigos para formatação de impressão dos argumentos passados para a
função, os quais estã relacionados na tabela 4.2.
Tabela 4.2 - Códigos para formatação de impressão
CÓDIGO printf() FORMATO
%c Caractere Simples
%d ou %i Decimal
%e ou %E (minúsculo ou maiúsculo) Notação Científica
%f Ponto Flutuante
%g ou %G (minúsculo ou maiúsculo) %e ou %f (o mais curto)
%o Octal
%s Cadeia de Caracteres
%u Decimal sem Sinal
%x ou %X (minúsculo ou maiúsculo) Hexadecimal
%% Escreve o símbolo %
LINGUAGEM C Comando de Escrita
17/103
Exemplo 4.1
main()
{
 printf(“A %s C foi criada em %d \nOk?”, “Linguagem”, 1972);
}
Execução::
A Linguagem C foi criada em 1972
Ok?
No exemplo 3.1 percebe-se o uso de dois tipos de códigos: primeiro de formatação com %s (para
strings) representando a constante string “Linguagem” e %d (decimais) representando a constante ano
1972; segundo um código especial para representar caracter de nova linha \n.
Exemplo 4.2
main()
{
 printf(“A %s %c foi ”, “Linguagem”, ‘C’);
 printf(“ criada em %d ”, 1972);
}
Execução:
A Linguagem C foi criada em 1972
No exemplo 3.2, nota-se que ‘C’ é delimitado por aspas simples enquanto que “Linguagem” é
delimitado por aspas duplas. Isto indica ao compilador como diferenciar um caractere de uma cadeia de
caracteres.
4.1 Modificadores de Saída
Para complementar os códigos de formatação a linguagem C oferece alguns códigos especiais
para melhorar a apresentação dos dados. Para isto relacionam-se:
• ‘-’ : alinhamento pela esquerda.
• ‘+’: coloca o sinal de menos ou mais se o valor for numérico.
• n (número) : indica o tamanho mínimo do campo. O preenchimento é feito com espaços em
branco. Se o tamanho iniciar com 0 (ex. 05), o preenchimento passa a ser feito com zeros.
• .m : tamanho máximo do campo, menos para os reais onde é usado para indicar o número de
casas decimais.
• * : substitui um dos n, mas o seu valor é o próximo parâmetro.
• ‘l’ : indica que segue um dado longo.
Exemplo 4.3
COMANDO TELA
printf(“%s %d”, “Numero”,10); Numero 10
printf(“%8s %4d”, “Numero”,10); ##Numero ##10
printf(“%-8s %4d”, “Numero”,10); Numero## ##10
printf(“%-3.5s %2.2f”,“Numero”,10.99); Numer 10.99
printf(“Long %ld”, 57485784); Long 57485784
onde:
# - representa 1 espaço em branco.
LINGUAGEM C Comando de Escrita
18/103
4.2 Exercícios
1. Escreva os comandos de saída formatada que gerem as seguintes saídas:
 01234567890123456789012
 a) Camisa estampada 40,00
 Camisa lisa 35,00
 
 b) Camisa estampada 40,00
 Camisa lisa 35,00
 
 c) Camisa estampada 00040,00
 Camisa lisa 00035,00
 
 d) Camisa e 40
 Camisa l 35
LINGUAGEM C Variáveis
19/103
5. Variáveis
Variável em C é um espaço de memória reservado para armazenar um certo tipo de dado e tendo
um nome para referenciar o seu conteúdo. O conteúdo da mesma pode variar segundo os comandos de
alteração do programa.
Exemplo 5.1
main()
{
 int ano;
 ano = 1972;
 printf(“A Linguagem C foi criada em %d ”, ano);
}
A primeira instrução,
int ano;
é um exemplo de declaração de variável, isto é, apresenta um tipo, int, e um nome, ano.
A segunda instrução,
ano = 1972;
atribui um valor à variável e este valor será acessado através de seu nome. Para realizar isto foi usado o
operador de atribuição (=).
5.1 Nome de Variáveis
Regras:
• Uma variável não pode ter o mesmo nome de uma palavra-chave de C.
• Em C, letras maiúsculas diferem das letras minúsculas.
• O número de caracteres válidos é definido de ambiente para ambiente.
 
5.2 Declaração de Variáveis
A declaração de variáveis serve para reservar uma quantidade de memória apropriada para
armazenar o tipo especificado. Tal declaração consiste no nome de um tipo, seguido do nome da variável.
Em C todas as variáveis devem ser declaradas.
Se existir mais de uma variável do mesmo tipo, pode-se declará-la de uma vez separando seus
nomes por vírgulas.
int ano, mes, dia;
5.2.1 Tipos de Declarações de Variáveis
O tipo de declaração de variável é definido pela localização onde a mesma é declarada. Existem
três lugares básicos: dentro de funções, na definição dos parâmetros das funções e fora de todas as
funções. Estas são variáveis locais, parâmetros formais e variáveis globais, respectivamente.
LINGUAGEM C Variáveis
20/103
5.2.1.1 Variáveis locais
Variáveis que são declaradas dentro de uma função. Tais variáveis só podem ser referenciadas
por comandos que estão dentro do bloco no qual as variáveis foram declaradas, isto é, não são
reconhecidas fora de seu próprio bloco de código.
Exemplo 5.2
void main()
{
 int num;
 num = 10;
}
5.2.1.2 Parâmetros Formais
Variáveis que são declaradas para passagem de parâmetros em funções.
Exemplo 5.3
int soma(int x, int y)
{
 return (x+y);
}
5.2.1.3 Variáveis Globais
Variáveis que são declaradas fora de qualquer função. Tais variáveis são reconhecidas pelo
programa inteiro e podem ser usadas por qualquer pedaço de código.
Exemplo 5.4
int num=10;
void main()
{
 printf(“%d”, num * num);
}
Exemplo 5.5
int num=2;
int media(int y, int x)
{
 return ((x+z)/num);
}
void main()
{
 int x1 = 15;
 int x2 = 4;
 printf(“%d %d ”, num * num, media(x1,x2));
}
LINGUAGEM C Variáveis
21/103
5.3 Palavras reservadas
Existem certos nomes que não podem ser usados como identificadores. São chamadas as
palavras reservadas e são de uso restrito da linguagem C (comandos, estruturas, declarações, etc.). O
conjunto de palavras reservadas usadas em C é o seguinte:
 asm auto break case cdecl char
 class const continue _cs default delete
 do double _ds else enum _es
 extern _export far _fastcall float for
 friend goto huge if inline int
 interrupt _loadds long near new operator
 pascal private protected public register return
 _saveregs _seg short signed sizeof _ss
 static struct switch template this typedef
 union unsigned virtual void volatile while
LINGUAGEM C Operadores
22/103
6. Operadores
A linguagem C
é muito rica em operadores internos. C define quatro classes de operadores:
aritméticos, relacionais, lógicos e bit a bit. Além disso, C tem alguns operadores especiais para tarefas
particulares.
6.1 Atribuição
Para o operador de atribuição é utilizado o símbolo =. Além disto, a linguagem oferece um tipo
de atribuição múltipla, isto é, em um comando só pode-se atribuir o mesmo valor a muitas variáveis.
Exemplo 6.1
x = 10;
a = b = c = 10;
6.1.1 Conversão de Tipos em Atribuições
A linguagem C permite a conversão automática de tipos, o que não acontece em Pascal, por
exemplo. Conversão de tipos refere-se à situação em que variáveis de um tipo são misturadas com
varáveis de outro tipo. Em um comando de atribuição, a regra de conversão de tipos é muito simples: o
valor do lado direito (lado da expressão) de uma atribuição é convertido no tipo do lado esquerdo
(variável destino), como no exemplo 6.2:
Exemplo 6.2
void main()
{
int x;
char ch;
float f;
ch = x; // Linha 1
x = f; // Linha 2
f = ch; // Linha 3
f = x; // Linha 4
}
Na linha 1, os bits mais significativos da variável inteira x são ignorados, deixando ch com os 8
bits menos significativos. Se x está entre 255 e 0, então ch e x têm valores idênticos. De outra forma, o
valor de ch reflete apenas os bits menos significativos de x. Na linha 2, x recebe a parte inteira de f. Na
linha 3, f converte o valor inteiro de 8 bits armazenado em ch no mesmo valor em formato de ponto
flutuante. Isso também acontece na linha 4, exceto por f converter um valor inteiro de 16 bits no formato
de ponto flutuante.
Na tabela 6.1 mostra-se algumas conversões de tipos e seus efeitos. Para essas conversões foi
assumido uma palavra de 16 bits.
Tabela 6.1 - Conversões de tipos e seus efeitos
Tipo Destino Tipo da Expressão Possível Informação Perdida
signed char char Se valor > 127, o destino é negativo
char short int Os 8 bits mais significativos
char int Os 8 bits mais significativos
char long int Os 24 bits mais significativos
int long int Os 16 bits mais significativos
int float A parte fracionária e possivelmente mais
float double Precisão, o resultado é arredondado
double long double Precisão, o resultado é arredondado
LINGUAGEM C Operadores
23/103
Para utilizar a tabela acima a fim de fazer uma conversão não mostrada, simplesmente converta
um tipo por vez até acabar. por exemplo, para converter double em int, primeiro converta double em
float e, então, float em int.
6.2 Operadores Aritméticos
A tabela 6.2 mostra os operadores aritméticos suportados pela linguagem.
Tabela 6.2 - Operadores aritméticos
Operador Ação
- Subtração, também menos unário
+ Adição
* Multiplicação
/ Divisão
% Módulo da Divisão (resto)
-- Decremento
++ Incremento
O menos unário multiplica seu único operando por -1. Isso é, qualquer número precedido por um
sinal
Quando / é aplicado a um inteiro ou caractere, qualquer resto é truncado.
O operador módulo % também, trabalha em C da mesma forma que em outras linguagens,
devolvendo o resto de uma divisão inteira. Contudo, % não pode ser usado nos tipos em ponto flutuante.
6.2.1 Incremento e Decremento
A linguagem C inclui dois operadores que geralmente não encontramos em outras linguagens.
São os operadores de incremento (++) e decremento (--), os quais somam 1 ao seu operando, e subtraem 1
de seu operando, respectivamente.
x++; ou ++x; ou x = x + 1;
x--; ou --x; ou x = x - 1;
Quando usados em uma expressão tem seus efeitos alterados pela posição do sinal de decremento
e incremento. Se o operador de incremento ou decremento preceder seu operando, C executa a operação
de incremento ou decremento antes de usar o valor do operando. Se o operador estiver após seu operando,
C usa o valor do operando antes de incrementá-lo ou decrementá-lo.
Exemplo 6.3
x = 10;
y = ++x; /*y recebe 11*/
x = 10;
y = x++; /* y recebe 10 */
A precedência dos operadores aritméticos é a seguinte:
++ -- Mais alta
-
* / %
+ - Mais baixa
Os operadores do mesmo nível de precedência são avaliados pelo compilador da esquerda para a
direita.
LINGUAGEM C Operadores
24/103
6.2.2 Operadores Aritméticos de Atribuição
A tabela 6.3 mostra os operadores aritméticos de atribuição suportados pela linguagem.
Tabela 6.3 - Operadores aritméticos de atribuição
Operador Ação
x -= y x = x - y
x += y x = x + y
x *= y x = x * y
x /= y x = x / y
x %= y x = x % y
As expressões com este operadores são mais compactas e normalmente produzem um código de
máquina mais eficiente.
6.3 Operadores Relacionais e Lógicos
A tabela 6.4 mostra os operadores relacionais e lógicos suportados pela linguagem C.
Tabela 6.4 - Operadores relacionais e lógicos
Operador Ação Operador Ação
> Maior que && AND
>= Maior ou igual que || OR
< Menor que ! NOT
<= Menor ou igual que
== Igual
!= Diferente
Em C, verdadeiro é qualquer valor diferente de zero, e falso é zero. As expressões que usam
operadores relacionais ou lógicos devolvem zero para falso e 1 para verdadeiro. Lembre-se de que toda
expressão relacional e lógica produz como resiltado 0 ou 1.
A precedência dos operadores relacionais e lógicos é a seguinte:
! Mais alta
> >= < <=
== !=
&&
|| Mais baixa
6.4 Operadores Bit a Bit
A tabela 6.5 mostra os operadores bit a bit suportados pela linguagem.
Tabela 6.5 - Operadores bit-a-bit
Operador Ação
& AND
| OR
^ OR exclusivo (XOR)
~ Complemento de um
>> Deslocamento à direita
<< Deslocamento à esquerda
LINGUAGEM C Operadores
25/103
A tabela 6.6 mostra o resultado de operações com deslocamento.
Tabela 6.6 - Exemplo de operações de deslocamento bit-a-bit
char x; x a cada execução da sentença Valor de x
x =7 00000111 7
x << 1 00001110 14
x << 3 01110000 112
x << 2 11000000 192
x >> 1 01100000 96
x >> 2 00011000 24
Os operadores bit a bit só podem ser utilizados sobre um byte ou uma palavra, isto é, aos tipos de
dados char e int e variantes do padrão C. Operações bit não podem ser usadas em float, double, long
double, void ou outros tipos mais complexos.
6.5 Operador ?
O operador ? substitui sentenças da forma Se-então-senão.
Sintaxe:
Exp1 ? Exp2 : Exp3;
Onde Exp1, Exp2 e Exp3 são expressões. Onde Exp1 é avaliada e se a mesma for verdadeira,
então Exp2 é avaliada e se torna o valor da expressão. Se Exp1 é falso, então Exp3 é avaliada e se torna o
valor da expressão.
Exemplo 6.4
x = 10;
y = x > 9 ? 100 : 200;
No exemplo 6.4, y recebe o valor 100, porque x (valor é 10) é maior que 9. Uma expressão
equivalente seria
x = 10;
if (x > 9) y = 100;
else y = 200;
6.6 Operadores de Ponteiros & e *
Um ponteiro é um endereço na memória de uma variável. Uma variável de ponteiro é uma
variável especialmente declarada para guardar um ponteiro para seu tipo especificado.
O primeiro operador de ponteiro é &. Ele é um operador unário que devolve o endereço na
memória de seu operando. Por exemplo,
m = &cont;
atribui o endereço de memória da variável cont em m.
Este tipo de operando não pode ser utilizado em três casos:
1. &(cont + 4) - sempre associa-se a uma variável e não expressão;
2. &3 - constantes não são válidas;
LINGUAGEM C Operadores
26/103
3. variáveis declaradas com classe de armazenamento register (não existe endereço para
registrador).
O segundo operador é *. Ele é um operador unário que devolve o valor da variável localizada no
endereço que o segue. Por exemplo, se m contém o endereço da variável cont,
q = *m;
coloca o valor de cont em q.
Os seguintes operadores * e & colocam o valor 10 na variável chamada target. O resultado (o
valor 10) deste programa é mostrado na tela.
Exemplo 6.5
#include “stdio.h”
void main()
{
 int target, source;
 int *m;
 source = 10;
 m = &source;
 target = *m;
 printf(“%d”,target);
}
6.7 Operador Vírgula
O operador vírgula
é usado para encadear diversas expressões. O lado esquerdo de um operador
vírgula é sempre avaliado como void. Isso significa que a expressão do lado direito se torna o valor de
toda a expressão separada por vírgulas.
Exemplo 6.6
x = (y = 3, y + 1);
No exemplo 6.5, primeiro y recebe 3 e, em seguida, atribui o valor 4 a x.
6.8 Expressões
Operadores, constantes e variáveis são os elementos que constituem as expressões. Uma
expressão é qualquer combinação válida desses elementos.
6.8.1 Conversão de Tipos em Expressões
Quando constantes e variáveis de tipos diferentes são misturadas em uma expressão, elas são
convertidas a um mesmo tipo. O compilador C converte todos os operandos no tipo do maior operando, o
que é denominado promoção de tipo. Isso é feito operação por operação, como descrito nas regras de
conversão de tipos abaixo.
SE um operando é long double
ENTÃO o segundo é convertido para long double.
SENÃO, SE um operando é double
ENTÃO o segundo é convertido para double.
SENÃO, SE um operando é float
LINGUAGEM C Operadores
27/103
ENTÃO o segundo é convertido para float.
SENÃO, SE um operando é unsigned long
ENTÃO o segundo é convertido para unsigned long.
SENÃO, SE um operando é long
ENTÃO o segundo é convertido para long.
SENÃO, SE um operando é unsigned
ENTÃO o segundo é convertido para unsigned.
Há ainda um caso adicional especial: se um operando é long e o outro é unsigned, e se o valor
do unsigned não pode ser representado por um long, os dois operandos são convertidos para unsigned
long.
Considere as conversões de tipo que ocorrem no exemplo 6.6. Primeiro, o caractere ch é
convertido para um inteiro e float f é convertido para double. Em seguida, o resultado de ch/i é
convertido para double porque f*d é double. O resultado final é double porque, nesse momento, os dois
operandos são double.
Exemplo 6.7
char ch;
int i;
float f;
double d;
result= ( ch / i ) + ( f * d ) - ( f + i );
 int double float
 double
 double
6.8.2 Casts
A linguagem permite que uma expressão pode ser forçada a ser de um tipo especificado usando
uma construção chamada cast. A forma geral de um cast é
(tipo) expressão
onde um tipo é um tipo de dado padrão de C. Por exemplo, para ter certeza de que a expressão x /2 será
do tipo float, escreve-se
(float) x/2;
Neste caso se a variável x fosse um inteiro ímpar sem o cast seu valor reria um inteiro (o que não
seria verdadeiro). Entretanto, com o uso do Cast oa variável x é definida como um float o que tornará o
seu resultado um float.
Como um operador, um cast é unário e tem a mesma precedência de qualquer outro operador
unário.
LINGUAGEM C Operadores
28/103
6.8.3 Espacejamento e Parênteses
A linguagem C não limita o espaçamento ou tabulações em uma expressão. Ajudam a aumentar a
legibilidade do programa.
O excesso de parênteses não causa erros, isto é, colocar parênteses onde não necessita, não
provoca erros, mas dificulta a leitura do programa.
LINGUAGEM C Algumas funções de E/S
29/103
7. Algumas funções de E/S
Neste capítulo será visto algumas funções como scanf(), getche(), getch(), getchar() e putchar().
Tais funções encontram-se no arquivo stdio.h, exceto getch() e getche() que encontram-se seus protótipos
em conio.h.
7.1 Função scanf()
Esta função serve para ler dados formatados da entrada padrão (teclado). Sua sintaxe é similar à
de printf(), isto é, uma expressão de controles seguida por uma lista de argumentos separados por
vírgulas.
A principal diferença é que os argumentos de scanf() devem ser endereços de variáveis. Para
enviar o endereço de cada argumento utiliza-se o operador &.
Sintaxe:
scanf(“expressão_de_controle”,lista_de_argumentos)
A expressão de controle pode conter códigos de formatação, precedidos por um sinal %. Além
disso, o caractere * colocado após o % que avisa à função que deve ser lido um valor do tipo indicado
pela especificação, mas não deve ser atribuído a nenhuma variável (não deve ter parâmetros na lista de
argumentos para estas especificações).
Tabela 7.1 - Códigos de formatação do comando scanf()
CÓDIGO printf() FORMATO
%c Lê um único caractere simples
%d ou %i Lê um inteiro decimal
%e Lê um número em notação científica
%f Lê um número em ponto flutuante
%g Lê um número em ponto flutuante
%o Lê um número em octal
%s Lê um número em cadeia de Caracteres
%u Lê um decimal sem sinal
%x Lê um hexadecimal
%l Lê um inteiro longo
%% Busca por um conjunto de caracteres
Exemplo 7.1
main()
{
 char a;
 printf(“Digite um caractere e veja-o em decimal, ”);
 printf (“ octal e hexadecimal. \n”);
 scanf(“%c”,&a);
 printf(“\n%c=%d dec., %o oct. e %x hex. \n”,a,a,a,a);
}
Execução (faça você mesmo):
LINGUAGEM C Algumas funções de E/S
30/103
7.2 Função getche() e getch()
A função getche() lê um caractere do teclado sem pressionar <ENTER> e mostra o que foi
digitado. Esta função não aceita argumentos e devolve o caracter lido para a função que a chamou.
Exemplo 7.2
main()
{
 char ch;
 printf(“Digite algum caractere:”);
 ch=getche();
 printf(“\n A tecla pressionada eh %c.”, ch);
}
Execução:
Digite algum caractere: a
A tecla pressionada eh a.
A função getch() lê um caractere do teclado sem pressionar <ENTER> e não mostra o que foi
digitado. Esta função não aceita argumentos e devolve o caracter lido para a função que a chamou.
Exemplo 7.3
main()
{
 char ch;
 printf(“Digite algum caractere:”);
 ch=getch();
 printf(“\n A tecla pressionada eh %c.”, ch);
}
Execução:
Digite algum caractere:
A tecla pressionada eh a.
7.3 Lendo e escrevendo caracteres
A função getchar() lê um caractere do teclado (este comando necessita o pressionamento da
tecla <ENTER> após o caractere), e putchar() escreve um caracter na tela. A função getchar() espera
até que uma tecla seja pressionada (a qual é mostrada na tela) e devolve o seu valor. A função putchar()
escreve seu argumento caractere na tela a partir da posição atual do cursor. Os protótipos para getchar() e
putchar() são mostrados aqui:
int getchar(void);
int putchar(int c);
A função getchar() devolve um inteiro, mas o byte de baixa ordem contém o caractere. Além
disso, pode-se chamar putchar() com um argumento caractere. A função putchar() devolve o caractere
escrito, ou EOF (definida em stdio.h e geralmente é igual a -1), se ocorreu algum erro. A função
putchar() não acrescenta um \n a saída.
Exemplo 7.4
main()
{
 char ch;
 printf(“Digite algum caractere:”); ch=getchar();
 printf(“\n A tecla pressionada eh ”);
 putchar(ch);
LINGUAGEM C Algumas funções de E/S
31/103
}
7.4 Exercícios
1. Escreva um programa que leia 3 números e mostre a sua média.
2. Escreva um programa que leia 2 números e mostre qual é o maior.
3. Faça um programa que leia dois números e em seguida mostre ,o produto a soma e a subtração entre
eles.
4. Faça um programa que leia 3 variáveis a, b e c, coeficientes de uma equação do 2º grau e escreva as
duas raízes da equação.
5. Escreva um programa onde o usuário entra com um número qualquer e o programa responda se o
número e par ou impar . Se for par emite a mensagem “ O número é par ” ou caso contrário “O número
é impar ”.
 
acb,
a
b
x 4 onde 
2
2
−=∆∆±−=
LINGUAGEM C Comandos Condicionais
32/103
8. Comandos Condicionais
Neste capítulo serão vistos os comandos condicionais da linguagem C.
8.1 Comando if
Sintaxe:
if (<expressão>)
 <comando>;
else
 <comando>;
Onde comando pode ser um único comando, um bloco de comandos ou nada (no caso de
comandos vazios). A cláusula else é opcional.
Exemplo 8.1
if (x != 0)
 x = 0;
Obs.: Caso haja um else ele pertence ao if mais próximo.
Exemplo 8.2
if (a > 5)
 {
 if (a < 10)
 b = 3;
 }
else
 b = 7;
No exemplo 8.2, se o comando if mais interno não fosse separado por chaves, o else pertenceria
ao mesmo e não ao mais externo.
Exemplo 8.3
/* Programa para adivinhar um numero */
#include “stdio.h”
#include “stdlib.h”
void main()
{
int num, tentativa;
num = rand(); /*gera numero aleatorio entre 0 e 32767*/
scanf(“%d”, &tentativa);
if (tentativa == num)
 printf(“* * * CERTO * * *”);
}
Lembre-se que para montar um bloco de comando é somente necessário usar o abre e fecha
chaves para marcar início e fim de bloco de comandos respectivamente.
8.2 Comando switch
C tem um comando interno de seleção múltipla, switch, que testa sucessivamente o valor de uma
expressão contra uma lista de constantes inteiras ou de caractere.
Sintaxe:
switch (<expressão>) {
LINGUAGEM C Comandos Condicionais
33/103
case <valor1> : <seqüência de comandos>
break;
case <valor2> : <seqüência de comandos>
break;
…
default:
 <seqüência de comandos>
}
O valor da expressão é testado, na ordem, contra os valores das constantes especificadas nos
comandos case. Quando uma coincidência for encontrada, a seqüência de comandos associada àquele
case será executada até que o comando break ou o fim do comando switch seja alcançado. O comando
default (opcional) é executado no momento em que não coincidir nenhum valor.
Exemplo 8.4
#include <stdio.h>
void main()
{
char opcao;
printf(“1. Opcao 1 \n”);
printf(“2. Opcao 2 \n”);
printf(“3. Opcao 3 \n”);
printf(“Opcao:”);
opcao=getchar();
switch(opcao) {
case ‘1’: printf (“\nVocê escolheu a opcao 1”);
break;
case ‘2’: printf (“\n Você escolheu a opcao 2”);
break;
case ‘3’: printf (“\n Você escolheu a opcao 3”);
break;
default: printf (“\n Nenhuma opcao selecionada”);
break;
}
}
8.3 Exercícios
1. Escrever um programa que leia 3 pares de cordenadas (x,y), que definam 3 pontos e:
• Verificar se eles formam um triângulo : não podem estar alinhados e não podem haver pontos
sobrepostos.
• calcular os lados do triângulo.
• classificar o tipo do triângulo:
• eqüilátero, isósceles ou escaleno.
• acutângulo, obtusângulo ou retângulo
2. Escreva um programa que leia 3 números e os escreve em ordem crescente.
3. Faça um programa que leia um número inteiro, verifique se é positivo ou negativo e escreva uma
mensagem apropriada.
4. Faça um programa contendo um menu com as seguintes opções :
S - soma
P - produto
U - subtração
D - divisão
Q - sair
LINGUAGEM C Comandos Condicionais
34/103
O programa deve conter uma função para executar cada tarefa pedida: soma , subtração etc .
Quando o usuário teclar ESC o programa deve terminar.
LINGUAGEM C Comandos Repititivos ou Laços
35/103
9. Comandos Repititivos ou Laços
9.1 Laço for
Comando de repetição condicional com inicialização e incremento.
Sintaxe:
for(inicialização; condição;incremento) comando;
Onde comando é um comando vazio, um comando simples, ou um bloco de comandos. Primeiro
é executado a inicialização que consiste em atribuições iniciais. Depois é testada a condição (expressão
relacional) de controle do laço. O incremento é executado após a execução do comando ou bloco de
comandos. O fim do laço é determinado pelo valor falso que a condição se apresenta.
Exemplo 9.1
#include “stdio.h”
main()
{
 int x;
 for(x=1; x<=100; x++)
 printf(“%d”,x);
}
No programa acima x é inicializado com 1. Uma vez que x é menor que 100, printf() é
executado e x é incrementado em 1 e testado para ver se ainda é menor ou igual a 100. Esse processo se
repete até que x fique maior que 100; nesse ponto, o laço termina. Então este programa imprime na tela os
números de 1 a 100.
9.2 Laço while
Comando de repetição condicional.
Sintaxe:
while(condição) comando;
Onde comando é um comando vazio, uma comando simples, ou um bloco de comandos. Primeiro
é testada a condição (expressão relacional) de controle do laço. E a cada execução de comando é repetido
o teste de condição. O fim do laço é determinado pelo valor falso que a condição se apresenta.
Exemplo 9.1
#include “stdio.h”
main()
{
 int cont=0;
 char ch;
 while((c=getchar()) != ‘0’)
 cont++;
 printf(“Foram lidos %d caracteres”,cont);
}
LINGUAGEM C Comandos Repititivos ou Laços
36/103
9.3 Laço do-while
Ao contrário dos comandos for e while, que testam a condição do laço no começo, o laço do-
while verifica a condição ao final do laço.
Sintaxe:
do
<comando>
while(condição);
Exemplo 9.2
#include <stdio.h>
void main()
{
char opcao;
printf(“1. Opcao 1 \n”);
printf(“2. Opcao 2 \n”);
printf(“3. Opcao 3 \n”);
printf(“Opcao:”);
do {
 opcao=getchar();
 switch(opcao) {
case ‘1’: printf (“\nVocê escolheu a opcao 1”);
 break;
case ‘2’: printf (“\n Você escolheu a opcao 2”);
 break;
case ‘3’: printf (“\n Você escolheu a opcao 3”);
 break;
default: printf (“\n Nenhuma opcao selecionada”);
 break;
 }} while (opcao != ‘1’ && opcao != ‘2’ && opcao != ‘3’);
}
9.4 Comandos break, continue e exit()
O comando break pode ser usado no corpo de qualquer estrutura de laço C. Causa a saída
imediata do laço e o controle passa para o próximo comando do programa.
Sintaxe:
break;
O comando continue força a próxima iteração do laço e pula o código que estivar abaixo. Nos
laços while e do-while um comando continue faz com que o controle do programa vá diretamente para o
teste condicional e depois continue o processo do laço.
Sintaxe:
continue;
Da mesma forma que pode-se sair de um laço, pode-se sair de um programa usando a função
exit() da biblioteca padrão. Essa função provoca uma terminação imediata do programa inteiro, forçando
um retorno ao sistema operacional.
Sintaxe:
void exit(int código_de_retorno);
LINGUAGEM C Comandos Repititivos ou Laços
37/103
O valor de código_de_retorno é retornado ao processo chamador (sistema operacional). O zero é
geralmente usado como um código de retorno que indica uma terminação normal do programa.
Exemplo 9.3
#include <stdio.h>
void main()
{
char opcao;
printf(“1. Opcao 1 \n”);
printf(“2. Opcao 2 \n”);
printf(“3. Opcao 3 \n”);
printf(“4. Abandonar \n”);
printf(“Opcao:”);
do {
 opcao=getchar();
 switch(opcao) {
case ‘1’: printf (“\nVocê escolheu a opcao 1”);
 break;
case ‘2’: printf (“\n Você escolheu a opcao 2”);
 break;
case ‘3’: printf (“\n Você escolheu a opcao 3”);
 break;
case ‘4’: exit(0); /* retorna ao SO */
 }} while (opcao != ‘1’ && opcao != ‘2’ && opcao != ‘3’);
}
9.5 Exercícios
1. Escreva um programa para calcular o fatorial de um número lido.
2. Escreva um programa para ler um caracter de comparação e vários caracteres de entrada finalizados
por ‘0’ e contar o número de vezes que o caracter de comparação apareceu.
3. Escrever um programa que mostre os números primos entre 1 e 100.
4. Escreva um programa que leia um número não determinado de valores positivos, e mostre a soma e
média dos respectivos números ao ser lido um valor negativo.
5. Faça um programa que leia um número (inteiro) e escreva todos os seus divisores.
6. Faça um programa que leia um número de no máximo 3 dígitos e escreva-o na tela.
7. Faça um programa que receba como entrada uma quantia em dinheiro e mostre na tela a quantidade de
notas de 5, 10, 50 e 100 são necessárias para representar o valor. O programa deve contabilizar a partir
das notas de 100.
8. Suponha um número N qualquer: se N é par então N agora é N / 2; se N é ímpar N agora é 3*N + 1.
Assim para N = 3 calculamos a seguinte tabela :
3 � 10 4 � 2
10 � 5 2 � 1
5 � 16 1 � 4
16 � 8 4 � 2
8 � 4 2 � 1
Observe que a partir de sete iterações a seqüência 4 2 1 começa a se repetir . Faça um programa
que calcule para um dado N o número de iterações até se chegar ao
primeiro 1 .
LINGUAGEM C Comandos Repititivos ou Laços
38/103
9. Faça um programa que imprima os elementos de uma PA e o somatório da mesma dados : primeiro
termo , numero de termos e razão
10. Faça um programa que imprima um elemento da seqüência de Fibonacci , dado o numero do elemento.
11. Faça um programa onde o usuário entra com um número decimal e o mesmo calcula e imprime o
número no sistema binário .
12. Faça um programa onde o usuário entra com dois números A e B o programa devolve como resultado
A elevado a B .
13. Escreva um programa que solicite ao usuário três números inteiros a,b,c onde a é maior que 1 . Seu
programa deve somar todos os inteiros entre b e c divisíveis por a .
LINGUAGEM C Funções
39/103
10. Funções
A forma geral de uma função é:
Sintaxe:
tipo_função nome_função (lista_de_parâmetros)
declaração_parâmetros
{
 corpo_função;
}
Exemplo 10.1
 int soma(int x, int y)
 {
 ...
 }
ou
int soma(x, y)
int x,y;
{
...
 }
As funções retornam um valor (do tipo indicado em tipo_função). O valor retornado pela função
é dado pelo comando return (o valor retornado pode ou não ser utilizado).
Existem dois tipos de passagem de argumentos: por valor e por referência. A segunda é realizada
através de apontadores.
Exemplo 10.2
int pot(x,n) /* x elevado na n potência */
int x,n;
{
int p;
for(p=1;n>0;n--)
 p *= x;
return p;
}
No exemplo 11.2, os argumentos foram passados por valor e a função retorna um valor do tipo
inteiro. A chamada seria:
a = pot(10,2);
No exemplo 11.3, nenhum valor é retornado (por isso usa-se o tipo void) mas é realizado uma
troca dos valores das variáveis, necessitando de uma passagem de parâmetros por referência.
Exemplo 10.3
void troca(a,b) /* troca os valores das variáveis */
int *a, *b; /* usa-se ponteiros para referenciar */
{ /* o conteúdo do endereço da variável */
int aux;
aux = *a;
*a = *b;
*b = aux;
}
LINGUAGEM C Funções
40/103
A chamada para esta função seria:
int x=1,y=2;
troca(&x,&y);
Na passagem de parâmetros por referência é passado explicitamente o endereço da variável com
o uso do operador &.
Quando o argumento for uma matriz automaticamente será passado o endereço da matriz para a
função.
A linguagem C aceita chamadas recursivas de funções.
10.1 Localização das funções
Existem basicamente duas posições possíveis para escrevermos o corpo de uma função: ou antes
ou depois do programa principal. Podemos ainda escrever uma função no mesmo arquivo do programa
principal ou em arquivo separado.
10.1.1 Corpo da função antes do programa principal (no mesmo arquivo)
Quando escrevemos a definição de uma função antes do programa principal e no mesmo arquivo
deste, nenhuma outra instrução é necessária.
Exemplo 10.4
float media2(float a, float b){ // função
 float med;
 med = (a + b) / 2.0;
 return(med);
}
void main(){ // programa principal
 float num_1, num_2, med;
 puts(”Digite dois números:”);
 scanf(”%f %f”, &num_1, &num_2);
 med = media2(num_1, num_2); // chamada da função
 printf(”\nA media destes números e´ %f”, med);
}
10.1.2 Corpo da função depois do programa principal (no mesmo arquivo)
Quando escrevemos a definição de uma função depois do programa principal e no mesmo
arquivo deste, devemos incluir um protótipo da função chamada. Um protótipo é uma instrução que
define o nome da função, seu tipo de retorno e a quantidade e o tipo dos argumentos da função. O
protótipo de uma função indica ao compilador quais são as funções usadas no programa principal os tipo.
A sintaxe geral para isto é a seguinte:
Sintaxe:
void main(){ // programa principal
 tipo nomef(...); // protótipo da função
 ...
 var = nomef(...) // chamada a função
 ...
}
LINGUAGEM C Funções
41/103
tipo nomef(...){ // definição da função
 [corpo de função]
}
Exemplo 10.5
void main(){ // programa principal
 float media2(float,float); // protótipo de media2()
 float num_1, num_2, med;
 puts(”Digite dois números:”);
 scanf(”%f %f”, &num_1, &num_2);
 med = media2(num_1, num_2); // chamada a função
 printf(”\nA media destes números e´ %f”, med);
}
float media2(float a, float b){ // função media2()
 float med;
 med = (a + b) / 2.0;
 return(med);
}
Observe que o protótipo de uma função nada mais é que a declaração da função sem o seu
corpo. Observe ainda que na lista de argumentos do protótipo podem ser escritos apenas os tipos dos
argumentos.
10.1.3 Corpo da função escrito em arquivo separado
Em C, como em muitas outras linguagens, é permitido que o usuário crie uma função em um
arquivo e um programa que a chame em outro arquivo distinto. Esta facilidade permite a criação de
bibliotecas de usuário: um conjunto de arquivos contendo funções escritas pelo usuário. Esta
possibilidade é uma grande vantagem utilizada em larga escala por programadores profissionais.
Quando escrevemos a definição de uma função em arquivo separado do programa principal
devemos incluir este arquivo no conjunto de arquivos de compilação do programa principal. Esta
inclusão é feita com a diretiva #include. Esta diretiva, vista nas seções 2.4.2 e 3.7.1, instrui o
compilador para incluir na compilação do programa outros arquivos que contem a definição das funções
de usuário e de biblioteca.
Sintaxe:
#include ”path” // inclusão da função
void main(){ // programa principal
...
 var = nomef(...) // chamada a função
...
}
Na diretiva #include, indicamos entre aspas duplas o caminho de localização do arquivo onde
está definida a função chamada.
Exemplo 10.6
#include ”c:\tc\userbib\stat.h” // inclusão da função
void main(){ // programa principal
 float num_1, num_2, med;
 puts(”Digite dois números:”);
 scanf(”%f %f”, &num_1, &num_2);
 med = media2(num_1, num_2); // chamada a função
LINGUAGEM C Funções
42/103
 printf(”\nA media destes números e´ %f”, med);
}
10.2 Argumentos para função main()
A função main() aceita argumentos para a passagem de parâmetros realizada através da chamada
do programa. Os dois argumentos são:
argc: contador de argumentos;
argv: vetor de argumentos (vetor de apontadores para strings).
Sintaxe:
main(int argc, char *argv[])
É importante lembrar que
Exemplo 10.7
#include “stdio.h”
void main(int argc, char *argv[])
{
int cont;
printf(“Foram encontrados %d argumentos \n”,argc -1);
for (cont=1;cont < argc;cont++)
 printf(“Argumento %d: %s \n”, cont, argv[cont]);
}
O primeiro argumento (argv[0]) é o nome do programa.
10.3 Protótipo de funções
O padrão ANSI C expandiu a declaração tradicional de função, permitindo que a quantidade e os
tipos dos argumentos das funções sejam declarados. A definição expandida é chamada protótipo de
função. Protótipos de funções não faziam parte da linguagem C original. Protótipos permitem que C
forneça uma verificação mais forte dos tipos.
Protótipos de funções ajudam a detectar erros antes que eles ocorram. É verificado número de
parâmetros, compatibilidade de tipos, entre outras.
Existem três tipos de declaração de protótipos:
Sintaxe Exemplo
tipo_função nome_função(); int pot();
tipo_função nome_função(lista_tipo_argumentos); int pot(int,int);
tipo_função nome_função(lista_tipo_nome_argumentos); int pot(int x, int y);
10.4 Retorno de Ponteiros
Ponteiros para variáveis não são variáveis e tampouco inteiros sem sinal. São endereço na
memória de um certo tipo de dado. A forma geral é:
Sintaxe:
tipo_função *nome_função(lista_de_argumentos);
LINGUAGEM C Funções
43/103
10.5 Números de parâmetros variável
Em C, pode-se especificar uma função que
possui a quantidade e os tipos de parâmetros
variáveis. O exemplo mais comum é printf(). Para informar ao compilador que um número desconhecido
de parâmetros será passado para uma função, deve-se terminar a declaração dos seus parâmetros usando
três pontos. Por exemplo, essa declaração especifica que func() terá ao menos dois parâmetros inteiros e
um número desconhecido (incluindo 0) de parâmetros após eles.
func(int a, int b, ...);
Essa forma de declaração também é usada por um protótipo de função.
Qualquer função que usa um número variável de argumentos deve ter pelo menos um argumento
verdadeiro. Por exemplo, isso está incorreto:
func(...);
Para poder utilizar esta característica tem-se que utilizar as funções descritas na próxima seção.
10.5.1 Funções para número variável de argumentos
Para este tipo de aplicação é necessário o uso de três macros definidas em STDARG.H:
va_arq(), va_start() e va_end().
Sintaxe:
void va_arg(va_list argptr, type);
type va_start(va_list argptr, last_parm);
void va_end(va_list argptr);
O tipo va_list é definido em STDARG.H. O procedimento geral para criar uma função que pode
receber um número variável de argumentos é o seguinte: a função deve ter pelo menos um parâmetro
conhecido, podendo, porém, ter mais anterior à lista variável de parâmetros. O parâmetro conhecido, mais
à direita, é last_parm. Antes que qualquer dos parâmetros de comprimento variável possa ser acessado, o
argumento ponteiro argptr deve ser inicializado através de uma chamada à va_start(). Em seguida,
parâmetros são devolvidos via chamadas à va_arg(), com type sendo o tipo do próximo parâmetro.
Finalmente, após todos os parâmetros terem sido lidos e antes de retornar a função, deve ser feita uma
chamada à va_end() para garantir que a pilha seja corretamente restaurada. Se va_end() não for chamada,
é muito provável que ocorra um crash do programa.
No exemplo abaixo, utiliza-se sum_series() para devolver a soma de uma série de números. O
primeiro argumento contém o número de argumentos que se sucedem.
#include “stdio.h”
#include “stdarg.h”
double sum_series(int num, ...);
void main()
{
 double d, sum_series();
 d = sum_series(5, 0.5, 0.25, 0.125, 0.0625, 0.03125);
 printf(“soma da serie: %f \n”,d);
}
double sum_series(int num, ...)
LINGUAGEM C Funções
44/103
{
 double sum=0.0, t;
 va_list argptr;
va_start(argptr,num); /* inicializa argptr */
for (;num;num--) /* soma a serie */
 {
 t = va_arg(argptr,double);
 sum += t;
 }
 va_end(argptr); /* finaliza a lista de argumentos
*/
 return sum;
}
10.6 Classes de Armazenamento
São quatro as classes de armazenamento de variáveis C:
• auto (automáticas)
• extern (externas)
• static (estáticas)
• register (em registradores)
 
10.6.1 Classe de Armazenamento - auto
As variáveis declaradas nos exemplos anteriores só podem ser acessadas somente às funções
onde estão declaradas. Tais variáveis são chamadas locais ou automáticas, são criadas quando a função é
chamada e destruídas quando a função ou o bloco de código termina a sua execução.
As variáveis declaradas dentro de uma função são automáticas por padrão. A classe de variáveis
automáticas pode ser explicitada usando-se a palavra auto. O código
 main()
 {
 auto int x;
 ...
 }
 
 é equivalente a
 
 main()
 {
 int x;
 ...
 }
 
10.6.2 Classe de Armazenamento - extern
Toda variável declarada fora de qualquer função têm a classe de armazenamento extern.
Como pode-se somente uma única vez declarar uma variável global, ao usar diversos arquivos
para um mesmo programa (por ser grande, por exemplo) deve-se utilizar a declaração extern nos outros
arquivos onde a variável é utilizada. Se não proceder assim, o compilador acusará um erro de duplicidade
de variável.
 Exemplo 10.8
 Arquivo 1
 int x,y;
 char ch;
 main()
 {
 ...
 }
 func1()
 {
 x = 123;
 }
LINGUAGEM C Funções
45/103
 
 Arquivo 2
 extern int x,y;
 extern char ch;
 func22()
 {
 x = y / 10;
 }
 func23()
 {
 y = 10;
 }
 
No arquivo 2, a lista de variáveis globais foi copiada do arquivo 1 e o especificador extern foi
adicionado às declarações. O especificador extern diz ao compilador que os tipos de variável e nomes que
o seguem foram declarados em outro lugar. Isto é, o compilador não reserva um espaço de memória para
essas variáveis declaradas com o especificador extern na certeza de estarem declaradas em outro módulo.
 
10.6.3 Classe de Armazenamento - static
Dentro de sua própria função ou arquivo, variáveis static são variáveis permanentes. Ao contrário
das variáveis globais, elas não são reconhecidas fora de sua função ou arquivo, mas mantêm seus valores
entre chamadas. O especificador static tem efeitos diferentes em variáveis locais e em variáveis globais.
 
10.6.3.1 Variáveis Locais static
Quando o modificador static é aplicado a uma variável local, o compilador cria armazenamento
permanente para ela quase da mesma forma como cria armazenamento para uma variável global. Em
termos simples, uma variável local static é uma variável local que retém seu valor entre chamadas. Mas
ela só é reconhecida apenas no bloco em que está declarada.
 
10.6.3.2 Variáveis Globais static
Quando o modificador static é aplicado a uma variável global, o compilador cria uma variável
que é reconhecida apenas no arquivo na qual a mesma foi declarada.
 
10.6.4 Classe de Armazenamento - register
A classe de armazenamento register indica que a variável associada deve ser guardada
fisicamente numa memória de acesso muito mais rápido, chamada registrador. No caso do IBM-PC pode
ser colocado o tipo int e char associado a register pois o registrador tem apenas 16 bits.
Basicamente, variáveis register são usadas para aumentar a velocidade de processamento. Por
exemplo, variáveis de uso freqüente como variáveis de laços e argumentos de funções.
 
10.7 Diretiva #define
A diretiva #define pode ser usada para definir constantes simbólicas com nomes apropriados.
Por exemplo, a constante PI pode ser definida com o valor 3.14159.
 #define PI 3.14159
Só pode ser escrito um comando destes por linha, e não há ponto-e-vírgula após qualquer diretiva
do pré-processador.
Esta diretiva é usada para definir macros com argumentos.
LINGUAGEM C Funções
46/103
 #define AREA(x) (4*PI*x*x)
A declaração acima define a função AREA() a qual calcula a área de uma esfera. A vantagem
desta declaração é não tipagem do argumento x. Não deve haver espaços entre o nome da macro e seus
identificadores.
10.8 Funções Recursivas
Uma função é dita recursiva quando se é definida dentro dela mesma. Isto é, uma função e´
recursiva quando dentro dela está presente uma instrução de chamada a ela própria.
Exemplo 10.9
// imprime uma frase invertida . Usa recursao
#include <stdio.h>
#include <conio.h>
void inverte( )
void main( )
{
 clrscr( );
 inverte( );
 getch();
}
void inverte ( )
{
 char ch ;
 if ((ch=getche( )) != ‘\r’ ) inverte( );
 scanf(“%c”,ch)
}
 
10.9 Exercícios
1. Escreva um programa que receba como parâmetro um índice (float). Após, ler uma sequência de
números (a qual termina por 0) e exibir o seu valor multiplicado pelo índice. A função que transforma
uma string em um float é atof(char *x).
2. Escreva uma função que receba um caracter como argumento e que retorne a letra maiúscula se a
mesma for minúscula. funções: islower(int ch), toupper(int ch).
3. Escreva uma função que calcule o fatorial de forma recursiva.
4. Implementar as seguintes funções:
� strlen()
� strcat()
� strcpy()
 
LINGUAGEM C Vetores e Matrizes
47/103
11. Vetores e Matrizes
Vetores e matrizes são estruturas de dados usada para representar uma certa quantidade de
variáveis de valores homogêneos. Em C, estas estruturas precisam ser declaradas,
como quaisquer outras
variáveis, para que o compilador conheça o tipo do vetor ou da matriz e reserve espaço de memória
suficiente para armazená-lo.
O que diferencia a declaração de uma matriz ou vetor da declaração de qualquer variável é a
parte que segue o nome, isto é, os pares de colchetes ([ e ]) que envolvam um número inteiro, que indica
ao compilador o tamanho da matriz.
int notas[5];
A declaração acima aloca um intervalo de memória com nome notas para armazenar 5 elementos
do tipo int. Por definição um vetor ou matriz é composto por elementos de um único tipo.
Para a declaração de mais de uma dimensão em C é necessário o número de par de colchetes ser
igual ao número de dimensões. Isto é, se a matriz for de duas dimensões teremos uma declaração assim:
int notas[5][5];
que declara uma matriz bi-dimensional; para três dimensões assim
int notas[5][5][5];
que declara uma matriz tridimensional e assim por diante. O limite de índices é definido pelo compilador
ou pela memória.
Os elementos do vetor ou da matriz são sempre enumerados por índices iniciados por 0 (zero).
Exemplo 11.1
for (i=0;i<5;i++)
{
 printf(“Digite a nota do aluno %d:”,i);
 scanf(“%d”, &notas[i]);
}
Obs.: A linguagem C não realiza verificação de limites em matrizes!!!
11.1 Inicialização de Vetores e Matrizes
Como o C permite a inicialização de variáveis básicas, também é permitido a inicialização de
vetores e matrizes.
Exemplo 11.2
int TAB[5]={1,5,10,15,20}
int MAT[5][6]={ { 1, 0, 1, 0, 1, 0 },
 { 0, 1, 0, 1, 0, 1 },
 { 1, 0, 1, 0, 1, 0 },
 { 0, 1, 0, 1, 0, 1 },
 { 1, 0, 1, 0, 1, 0 } };
Obs.: Quando houver uma inicialização de um vetor ou uma matriz na declaração, pode-se suprimir
sempre o valor do primeiro colchete, isto é, pelo número de argumentos ele assume o número do primeiro
colchete.
LINGUAGEM C Vetores e Matrizes
48/103
Exemplo 11.3
int TAB[]={1,5,10,15,20}
int MAT[][6]={ { 1, 0, 1, 0, 1, 0 },
 { 0, 1, 0, 1, 0, 1 },
 { 1, 0, 1, 0, 1, 0 },
 { 0, 1, 0, 1, 0, 1 },
 { 1, 0, 1, 0, 1, 0 } };
11.2 Matrizes e Vetores como argumento de Funções
O C permite que passe matrizes ou vetores como argumento de uma função. Mas há uma
peculiaridade na passagem de parâmetros: a forma como ela é passada.
O nome de uma matriz ou vetor desacompanhado de colchetes é equivalente ao endereço da
matriz ou do vetor, isto é, passagem por referência.
Quando o nome estiver acompanhado de um indexador é passado como argumento o conteúdo
daquela posição da matriz ou do vetor, isto é, passagem por valor.
11.3 Limites
Na linguagem C, devemos ter cuidado com os limites de um vetor. Embora na sua declaração,
tenhamos definido o tamanho de um vetor, o C não faz nenhum teste de verificação de acesso a um
elemento dentro do vetor ou não.
Por exemplo se declaramos um vetor como int valor[5], teoricamente só tem sentido usarmos os
elementos valor[0], ..., valor[4]. Porém, o C não acusa erro se usarmos valor[12] em algum lugar do
programa. Estes testes de limite devem ser feitos logicamente dentro do programa.
Este fato se deve a maneira como o C trata vetores. A memória do microcomputador é um espaço
(físico) particionado em porções de 1 byte. Se declaramos um vetor como int vet[3], estamos reservando
6 bytes (3 segmentos de 2 bytes) de memória para armazenar os seus elementos. O primeiro segmento será
reservado para vet[0], o segundo segmento para vet[1] e o terceiro segmento para vet[2]. O segmento
inicial é chamado de segmento base, de modo que vet[0] será localizado no segmento base. Quando
acessamos o elemento vet[i], o processador acessa o segmento localizado em base+i. Se i for igual a 2,
estamos acessando o segmento base+2 ou vet[2](o ultimo segmento reservado para o vetor). Porém, se i
for igual a 7, estamos a acessando segmento base+7 que não foi reservado para os elementos do vetor e
que provavelmente está sendo usado por uma outra variável ou contém informação espúria (lixo).
Observe que acessar um segmento fora do espaço destinado a um vetor pode destruir
informações reservadas de outras variáveis. Estes erros são difíceis de detectar pois o compilador não
gera nenhuma mensagem de erro... A solução mais adequada é sempre avaliar os limites de um vetor antes
de manipulá-lo.
A princípio este fato poderia parecer um defeito da linguagem, mas na verdade trata-se de um
recurso muito poderoso do C. Poder manipular sem restrições todos os segmentos de memória é uma
flexibilidade apreciada pelos programadores.
LINGUAGEM C Vetores e Matrizes
49/103
11.4 Strings
Em C não existe um tipo de dados string como na linguagem "Pascal". Ao contrário, as string
são implementadas como cadeia de caracteres, terminados pelo caracter null (‘\0’). O caracter null serve
como uma marca de fim de string para as funções que lidam com strings.
Como em qualquer vetor, os caracteres da string podem ser individualmente acessados.
A declaração:
char nome[15];
reserva um espaço de memória para uma string de tamanho 14, pois o décimo-quinto byte é para o
caracter null. A manipulação de strings pode ser feita através de funções ou elemento a elemento como se
fosse um vetor.
Exemplo 11.4
char str[2];
str[0] = ‘U’;
str[1] = ‘/0’; /* Caracter NULL para finalizar string */
Como todo vetor as strings podem ser inicializadas, como mostram o exemplo 10.5:
Exemplo 11.5
char nome[]={‘C’,’a’,’r’,’l’,’o’,’s’,’\0’};
char nome[]=“Carlos”;
char nomes[][8]={“Eduardo”,
 “Andre”,
 “Adriana”,
 “Carla”,
 “Jose”};
11.4.1 Leitura de Strings
Para ler uma string pode-se utilizar a função scanf() ou a função gets() da biblioteca C para
receber a string.
11.4.1.1 Função scanf()
A função scanf() é bastante limitada para a leitura de strings. Pois não pode haver espaços em
branco na string lida. O que vier após o espaço em branco é eliminado.
Exemplo 11.6
#include <stdio.h>
main()
{
 char nome[15];
 printf(“Digite seu nome:”);
 scanf(“%s”,&nome);
 printf(“Saudacoes %s”,nome);
}
Execução
Digite seu nome: Carlos Alberto
LINGUAGEM C Vetores e Matrizes
50/103
Saudacoes, Carlos
11.4.1.2 Função gets()
A função gets() é bastante conveniente para a leitura de strings. O seu propósito é unicamente ler
uma string da sua entrada padrão que por default é o teclado.
Visto que uma string não tem um tamanho pré-determinado, gets() lê caracteres até encontrar o
de nova linha (‘\n’) que é gerado ao pressionar a tecla [Enter]. Todos os caracteres anteriores ao ‘\n’ são
armazenados na string e é então incluído o caractere ‘\0’. Caracteres brancos como espaços e tabulações
são aceitáveis como parte da string.
Exemplo 11.7
#include <stdio.h>
main()
{
 char nome[15];
 printf(“Digite seu nome:”);
 gets(nome);
 printf(“Saudacoes %s”,nome);
}
Execução
Digite seu nome: Carlos Alberto
Saudacoes, Carlos Alberto
11.4.2 Escrita de Strings
Para escrever uma string pode-se utilizar a função printf() ou a função puts() da biblioteca C.
11.4.2.1 Função printf()
A função printf() ao imprimir a string não realiza uma nova linha, como é definido na sua
implementação. Em compensação é uma função que pode ser usada para impressão de mais que uma
string por linha.
11.4.2.2 Função puts()
A função puts() é o complementa de gets(). O seu propósito é unicamente imprimir a string
apontada pelo seu argumento. O endereço desta string deve ser mandado para puts() como argumento.
O próximo exemplo ilustra algumas das muitas possibilidades de seu uso.
/* mostra o uso de putc() */
#include <stdio.h>
main()
{
 char nome[81];
 puts(“Digite seu nome:”);
 gets(nome);
 puts(“Saudacoes, ”);
 puts(nome);
 puts(“puts() pula de linha sozinha”);
LINGUAGEM C Vetores e Matrizes
51/103
 puts(&nome[4]);
}
Execução
Digite seu nome:
Carlos Alberto
Saudacoes,
Carlos Alberto
puts() pula de
linha sozinha
los Alberto
A função puts() reconhece ‘/0’como fim de string. Cada impressão de puts(), ele acaba com um
caracter de nova linha.
Utilizando a função printf() o mesmo efeito seria:
printf(“%s\n”,nome); puts(nome);
11.4.3 Funções de Manipulação de Strings
Como strings são implementadas como vetores, os operadores usuais não podem ser aplicados
sobre este tipo de dado. Por exemplo: comparação atribuição não são suportados por este tipo de dado.
As funções descritas nas próximas seções utilizam o cabeçalho padrão STRING.H.
11.4.3.1 Função strlen()
Devolve o comprimento da string terminada por um nulo apontada por str. O nulo não é contado.
Sintaxe:
size_t strlen(char *str);
Exemplo 11.8
printf(“%d”,strlen(“ola”)); /* O resultado é 3 */
11.4.3.2 Função strcat()
Concatena uma cópia de str2 em str1 e termina str1 com um nulo. Como não existe verificação
de limites, é de inteira responsabilidade do usuário que o tamanho de str1 seja suficientemente grande
para armazenar seu conteúdo original e o de str2.
Sintaxe:
char *strcat(char *str1, const char *str2);
Exemplo 11.9
 printf(“%s”,strcat(“ola”, “ mundo”)); /* O resultado é ola mundo */
LINGUAGEM C Vetores e Matrizes
52/103
11.4.3.3 Função strcmp()
Compara lexicograficamente duas strings e devolve um inteiro baseado no resultado como
mostrado aqui:
Valor Significado
Menor que zero str1 é menor que str2
Zero str1 é igual a str2
Maior que zero str1 é maior que str2
Sintaxe:
int strcmp(const char *str1, const char *str2);
11.4.3.4 Função strcpy()
Copia o conteúdo de str2 em str1. str2 deve ser um ponteiro para uma string terminada com um
nulo. A função strcpy() devolve um ponteiro para str1.
Sintaxe:
char *strcpy(char *str1, const char *str2);
Exemplo 11.10
char str[80];
strcpy(str, “mundo”); /* copia mundo na string str */
11.4.3.5 Função strchr()
Devolve um ponteiro à primeira ocorrência do byte menos significativo de ch na string apontada
por str. Se não for encontrada nenhuma coincidência, será devolvido um ponteiro nulo.
Sintaxe:
char *strchr(const char *str, int ch);
11.4.3.6 Função strstr()
Devolve um ponteiro à primeira ocorrência da string apontada por str2 na string apontada por
str1. Se não for encontrada nenhuma coincidência, será devolvido um ponteiro nulo.
Sintaxe:
char *strstr(const char *str1, const char *str2);
11.4.4 Exemplo Geral
O programa abaixo exemplifica o uso das funções descritas anteriormente.
Exemplo 11.11
#include “stdio.h”
#include “string.h”
void main()
LINGUAGEM C Vetores e Matrizes
53/103
{
 char s1[80], s2[80];
 gets(s1);
 gets(s2);
 printf(“comprimento: %d %d \n”, strlen(s1), strlen(s2));
 if (!strcmp(s1,s2))
 printf(“As strings sao iguais\n”);
 strcat(s1,s2);
 printf(“%s\n”,s1);
 strcpy(s1, “Isso e um teste \n”);
 printf(s1);
 if (strchr(“alo”,’o’))
 printf(“o esta em alo \n”);
 if (strstr(“ola aqui”, “ola”))
 printf(“ola encontrado”);
}
Ao rodar este programa e entrar com as strings “ola” e “ola”, a saída será
comprimentos: 3 3
As strings são iguais
aloalo
Isso e um teste
o esta em alo
ola encontrado
11.5 Exercícios
1. Faça um programa que leia 10 valores inteiros e escreva-os na ordem inversa a que foram lidos.
2. Faça um programa que leia 10 valores inteiros, calcule sua média e escreva todos os valores que estão
abaixo da média.
3. Calcular os números primos entre 0 e 100 (inclusive) utilizando o crivo de Eritóstenes:
� Usar um vetor de 101 inteiros;
� zerar o vetor;
� repetir de 2 a 100
� para cada número: somar 1 a todos os contadores dos seus múltiplos.
� ao final os números que possuem 0 no vetor são primos.
4. Escreva um programa que leia um vetor de 100 posições e mostre-o em ordem crescente e
decrescente.
5. Faça um programa que leia uma matriz M[10][10] e some todos os elementos abaixo da diagonal
principal.
6. Faça um programa que leia 3 nomes (até 30 letras) e os escreva em ordem alfabética.
7. Escreva um programa que leia um número indeterminado de caracteres e conta o número de vezes
que cada letra (A-Z) aparece:
� Desconsiderar se a letra é maiúscula ou minúscula;
� Termina com o caracter ´0´.
8. Faça um programa que leia uma matriz M[4][4] e mostra a sua transposta.
9. Escreva um programa cuja execução se segue abaixo :
digite uma frase :
LINGUAGEM C Vetores e Matrizes
54/103
carlos <enter>
digite uma letra dessa frase :
r <enter>
rlos
10. Escreva uma função que realize a multiplicação de matrizes.
11. Escrever a função atoi() (recebe uma string como parâmetro e transforma em um inteiro).
12. Faça um programa que use a função (definida por você) :
char *concat( char *s1 , char * s2) . A função retorna um ponteiro que é a concatenação de s2 com
s1 . Exemplo de chamada :
char mat1[80]=”casa ” ;
char mat2[80]=”grande”;
char *p;
p = concat(mat1,mat2);
printf( “%s”,p);
13. Escreva uma função que receba um vetor e o tamanho do mesmo e troque os elementos (1º com o
último, o 2º com o penúltimo e assim por diante).
LINGUAGEM C Ponteiros
55/103
12. Ponteiros
Para uma boa utilização dos ponteiros deve-se compreender corretamente o seu uso. Existem três
razões para isso: primeiro, ponteiros fornecem os meios pelos quais as funções podem modificar seus
argumentos; segundo, eles são usados para suportar as rotinas de alocação dinâmica de C, e terceiro, o uso
de ponteiros para aumentar a eficiência de certas rotinas.
Por ser um dos aspectos mais poderosos da linguagem também são os mais perigosos. Por erros
no uso de ponteiros (como a não inicialização de ponteiros - ponteiros selvagens) podem provocar quebra
do sistema.
Por definição, um ponteiro é uma variável que contém um endereço de memória. Esse endereço
é normalmente uma posição de uma outra variável na memória.
Uma declaração de ponteiros consiste no tipo base, um "*" e o nome da variável. A forma geral
é:
tipo *nome;
onde tipo é qualquer tipo válido em C e nome é o nome da variável ponteiro.
O tipo base do ponteiro define que tipo de variáveis o ponteiro pode apontar. Basicamente,
qualquer tipo ponteiro pode apontar para qualquer lugar, na memória. Mas, para a aritmética de ponteiros
é feita através do tipo base.
Os operadores utilizados são * e &, como já foi explicado na seção 6.6.
12.1 Expressões com Ponteiros
Nesta seção serão analisados alguns aspectos especiais de expressões com ponteiros.
12.1.1 Atribuição de Ponteiros
Como é o caso com qualquer variável, um ponteiro pode ser usado no lado direito de um
comando de atribuição para passar seu valor para um outro ponteiro.
Exemplo 12.1
#include “stdio.h”
void main()
{
int x;
int *p1,*p2; /* declaração do ponteiro p1 e p2 com o
tipo base int. */
p1 = &x;
p2 = p1;
printf(“%p”,p2); /* escreve o endereço de x, não seu
valor */
}
Agora, tanto p1 quanto p2 apontam para x. O endereço de x é mostrado usando o modificador de
formato de printf() %p, que faz com que printf() apresente um endereço no formato usado pelo
computador hospedeiro.
LINGUAGEM C Ponteiros
56/103
12.1.2 Aritmética de Ponteiros
Existem apenas duas operações aritméticas que podem ser usadas com ponteiros: adição e
subtração. Os operadores permitidos no caso seriam: +, -, ++, --.
O incremento é sempre realizado do tamanho básico de armazenamento do tipo base. Isto é, se o
tipo base for um inteiro e incrementarmos em uma unidade, o apontador apontará para o próximo inteiro
(no caso do inteiro ocupar 2 bytes o incremento será de dois bytes), no caso de um caracter (char) será de
um byte.
Exemplo 12.1
int *ptri=3000;
char *ptrc=4000;
float *ptrf=4000;
ptri++; /* ptri apontará para o endereço 3002 */
ptrc++; /* ptrc apontará para o endereço 4001 */
ptrf++; /* ptrf apontará para o endereço 5004 */
ptri = ptri - 10; /* ptri
apontará para o endereço 2982 */
ptrc = ptrc - 10; /* ptrc apontará para o endereço 3991 */
ptrf = ptrf - 10; /* ptrf apontará para o endereço 4964 */
Além de adição e subtração entre um ponteiro e um inteiro, nenhuma outra operação aritmética
pode ser efetuada com ponteiros. Isto é, não pode multiplicar ou dividir ponteiros; não pode aplicar os
operadores de deslocamento e de mascaramento bit a bit com ponteiros e não pode adicionar ou subtrair o
tipo float ou o tipo double a ponteiros.
Lembre-se! Não altera-se o valor de um ponteiro constante (ponteiro para um tipo básico - int,
float double, entre outros), somente de um ponteiro variável (ponteiro de estruturas complexas - vetores,
matrizes, strings, entre outros).
12.2 Inicialização de Ponteiros
Após um ponteiro ser declarado, mas antes que lhe seja atribuído um valor, ele contém um valor
desconhecido. Ao usar este ponteiro antes de inicializar, provavelmente provocará uma falha do programa
ou até do sistema operacional.
Como um ponteiro nulo é assumido como sendo não usado, pode-se utilizar o ponteiro nulo para
fazer rotinas fáceis de codificar e mais eficientes. Por exemplo, pode-se utilizar um ponteiro nulo para
marcar o fim de uma matriz de ponteiros. Uma rotina que acessa essa matriz sabe que chegará ao final ao
encontrar o valor nulo. A função search(), mostrada no exemplo 13.11, ilustra esse tipo de abordagem.
Exemplo 12.1
/* procura um nome */
search(char *p[], char *name)
{
 register int t;
 for (t=0;p[t];++t)
 if(!strcmp(p[t],name)) return t;
 retrun -1; /* não encontrado */
}
O laço for dentro de search() é executado até que seja encontrada uma coincidência ou um
ponteiro nulo. Como o final da matriz é marcado com um ponteiro nulo, a condição de controle do laço
falha quando ele é atingido.
Uma outra utilização de inicialização de ponteiros é a inicialização de strings. Isto pode ser
levado como uma variação no tema de inicialização usado na variável argv.
LINGUAGEM C Ponteiros
57/103
Exemplo 12.2
char *p= “alo mundo \n”;
O ponteiro p (exemplo 13.12) não é uma matriz, mas como o compilador C cria uma tabela de
strings, a constante string é colocada em tal tabela sendo que a mesma pode ser utilizada em todo o
programa como se fosse uma string comum (exemplo 13.13). Por isso, inicializar uma matriz de strings
usando ponteiros aloca menos memória que a inicialização através de matriz.
Exemplo 12.3
#include “stdio.h”
#include “string.h”
char *p=“alo mundo”;
void main()
{
 register int t;
 printf(p);
 for (t=strlen(p) - 1; t > -1; t--) printf(“%c”,p[t]);
}
12.2.1 Comparação de Ponteiros
É possível comparar dois ponteiros em uma expressão relacional.
Exemplo 12.2
if (p<q) printf(“p aponta para uma memória mais baixa que q /n”);
Geralmente, a utilização de comparação entre ponteiros é quando os mesmos apontam para um
objeto comum. Exemplo disto é a pilha, onde é verificado se o ponteiro de início e fim da pilha estão
apontando para a mesma posição de memória, significando pilha vazia.
12.3 Ponteiros e Matrizes
Existe uma estreita relação entre matrizes e ponteiros. Pois C fornece dois métodos para acessar
elementos de matrizes: aritmética de ponteiros e indexação de matrizes. Aritmética de ponteiros pode ser
mais rápida que indexação de matrizes. Normalmente utiliza-se ponteiros para acessar elementos de
matrizes devido a velocidade de acesso.
Exemplo 12.3
char str[80], *p1;
p1 = str;
Para acessar a string str pode-se utilizar estes dois mecanismos
str[4] /* indexação de matrizes */
ou
*(p1 + 4) /* aritmética de ponteiros */
Os dois comandos devolvem o quinto elemento.
*(matriz + índice) é o mesmo que matriz[índice].
LINGUAGEM C Ponteiros
58/103
Para uma melhor compreensão ou facilidade de programação as funções de indexação trabalham
com ponteiros (como mostra o exemplo 13.5 a implementação da função puts()).
Exemplo 12.4
/* Indexa s como uma matriz */
void put(char *s)
{
 register int t;
 for (t=0;s[t]; ++t) putchar(s[t]);
}
/* Acessa s como um ponteiro */
void put(char *s)
{
 while (*s) putchar(*s++);
}
No caso da passagem de parâmetros é possível tratar uma matriz como se fosse um ponteiro.
Exemplo 12.5
#include “stdlib.h”
#include “stdio.h”
#include “string.h”
void le_tab(int *p)
{
 register int i;
 for(i=0; i<20; i++)
 scanf(“%d”,(p+i));
}
void mostra_tab(int *p)
{
 register int i;
 for(i=0; i<20; i++)
 printf(“%d”,*(p+i));
}
void main(void)
{
 int mat[20];
 le_tab(mat);
 mostra_tab(mat);
}
12.3.1 Matrizes de Ponteiros
Ponteiros podem ser organizados em matrizes como qualquer outro tipo de dado. A declaração
de uma matriz de ponteiros int, de tamanho 10, é
int *x[10];
Para atribuir o endereço de uma variável inteira, chamada var, ao terceiro elemento da matriz de
ponteiros, deve-se escrever
x[2] = &var;
para encontrar o valor de var, escreve-se
*x[2];
LINGUAGEM C Ponteiros
59/103
Se for necessário passar uma matriz de ponteiros para uma função, pode ser usado o mesmo
método que é utilizado para passar outras matrizes - simplesmente chama-se a função com o nome da
matriz sem qualquer índice (como mostra o exemplo 13.7).
Exemplo 12.6
void display_array(int *q[])
{
 register int t;
 for (t=0; t<10; t++)
 printf(“%d”,*q[t]);
}
Lembre-se de que q não é um ponteiro para inteiros; q é um ponteiro para uma matriz de
ponteiros para inteiros. Portanto, é necessário declarar o parâmetro q como uma matriz de ponteiros para
inteiros, como é mostrado no exemplo 13.7. Isto é, não é uma passagem de parâmetros por referência por
dois motivos: primeiro, matriz como argumento de função é automaticamente passada por referência por
questão da implementação da linguagem, e segundo, é uma matriz de ponteiros e consequentemente sua
declaração é caracterizada pelo asterisco na frente do nome da variável.
Matrizes de ponteiros são usadas normalmente como ponteiros de strings como, por exemplo, o
argumento da linha de comandos argv.
12.3.2 Acessando partes de Matrizes como vetores
A linguagem C trata partes de matrizes como matrizes. Mais especificamente, cada linha de uma
matriz de duas dimensões pode ser considerada como uma matriz de uma dimensão. Isto pode ser muito
útil no tratamento de matrizes. O exemplo 13.8 mostra a atribuição de uma linha da matriz nome para um
ponteiro.
Exemplo 12.7
main()
{
 static char nome[10][10];
 char *p[10];
 for (i = 0;i<10;i++)
 p[i] = nome[i];
 ordena(p);
}
12.4 Indireção Múltipla
Indireção múltipla é uma situação onde o ponteiro aponta para um outro ponteiro e que o mesmo
aponta para um valor final. A figura 12.1 mostra o conceito de indireção múltipla.
Ponteiro Variável
endereço valor
Indireção Simples
 Ponteiro Ponteiro Variável
endereço endereço valor
LINGUAGEM C Ponteiros
60/103
Indireção Múltipla
Figura 12.1 Indireção simples e múltipla
A indireção múltipla pode ser levada a qualquer dimensão desejada, mas raramente é necessário
mais de um ponteiro para um ponteiro.
Obs.: Não confunda indireção múltipla com listas encadeadas.
A declaração deste tipo de variável é feita colocando-se um * adicional em frente ao nome da
variável, como mostra o exemplo 13.9. Tal exemplo mostra a declaração da variável ptrptrint como um
ponteiro para um ponteiro do tipo int.
Exemplo 12.1
int **ptrptrint;
Para acessar o valor final apontado indiretamente por um ponteiro a um ponteiro, você deve
utilizar o operador asterisco duas vezes, como no exemplo 13.10:
Exemplo 12.2
#include “stdio.h”
void main()
{
 int x, *p, **q;
 x = 10;
 p = &x;
 q = &p;
 printf(“%d”, **q); /* imprime o valor de x */
}
12.5 Ponteiros para Funções
A linguagem C permite apontadores para funções. Isto é permitido pois toda função
tem uma
posição física na memória que pode ser atribuída a um ponteiro. Portanto, um ponteiro de função pode ser
usado para chamar uma função.
O endereço de uma função é obtido usando o nome da função sem parênteses ou argumentos.
Mas para declarar este tipo de apontador tem que se seguir uma sintaxe especial como mostra o exemplo
13.14.
Exemplo 12.1
#include “stdio.h”
#include “string.h”
void check(char *a, char *b, int (*cmp)());
void main()
{
 char s1[80], s2[80];
 int (*p)();
 p = strcmp;
 gets(s1);
 gets(s2);
 check(s1,s2,p);
}
void check(char *a, char *b, int (*cmp)());
{
 if (!(*cmp) (a, b)) printf(“igual”);
 else printf(“diferente”);
}
LINGUAGEM C Ponteiros
61/103
Quando a função check() é chamada, dois ponteiros para caracter e um ponteiro para uma função
são passados como parâmetros. Dentro da função check(), note como o ponteiro para função é declarado,
pois esta é a forma correta de se declarar este tipo de ponteiro. Os parênteses ao redor de *cmp são
necessários para que o compilador interprete o comando corretamente.
Uma outra forma de fazer a chamada é mostrada no exemplo 13.14 o qual dispensa o uso de um
ponteiro adicional.
check(s1, s2, strcmp);
Uma das grandes utilidades é o uso de drivers de dispositivos (placas de som, placas de vídeo,
modems, entre outros) que fornecem rotinas de tratamento para aquele hardware específico. Onde o
programador lê o arquivo do driver para a memória e o executa de acordo com as especificações do
fabricante.
Outra utilidade é o programador poder enviar a função que se apropria para a comparação por
exemplo. Isto é, no caso de strings pode-se pensar em um comparador de strings genérico onde como
terceiro parâmetro é enviado a função que vai realizar a comparação. Antes da chamada da função
genérica pode verificar se a string é composta por caracteres alfanuméricos (através da função isalpha()) e
enviar a função strcmp(), caso contrário uma função que realize uma comparação de números inteiros
(nesta função conterá a conversão das strings em um inteiro (função atoi()).
12.6 Mais Sobre declarações de Ponteiros
As declarações de ponteiros podem ser complicadas e é necessário algum cuidado na sua
interpretação. principalmente em declarações que envolvem funções e matrizes. Assim, a declaração
int *p(int a);
indica uma função que aceita um argumento inteiro e retorna um ponteiro para um inteiro. Por outro lado,
a declaração
int (*p)(int a);
indica um ponteiro para uma função que aceita um argumento inteiro e retorna um inteiro. Nessa última
declaração, o primeiro par de parênteses é usado para o aninhamento e o segundo par, para indicar uma
função.
A interpretação de declarações mais complexas pode ser extremamente mais trabalhosa. Por
exemplo, considere a declaração
int *(*p)(int (*a)[]);
Nessa declaração, (*p)(..) indica um ponteiro para uma função. Por isso, int *(*p)(...) indica um
ponteiro para uma função que retorna um ponteiro para um inteiro. Dentro do último par de parênteses (a
especificação dos argumentos da função), (*a)[] indica um ponteiro para um vetor. Assim int (*a)[]
representa um ponteiro para um vetor de inteiros. Juntando todas as peças, (*p)(int (*a)[]) representa um
ponteiro para uma função cujo argumento é um ponteiro para um vetor de inteiros. E, finalmente, a
declaração original representa um ponteiro para uma função que aceita um ponteiro para um vetor de
inteiros como argumento e devolve um ponteiro para um inteiro.
Lembre-se que se logo após um identificador existir um “abre parênteses” indica que o
identificador representa uma função e os colchetes representam uma matriz. Os parênteses e colchetes
têm maior precedência do que qualquer operador.
A seguir será mostrado várias declarações envolvendo ponteiros e seu significado.
LINGUAGEM C Ponteiros
62/103
int *p; p é um ponteiro para um valor inteiro
int *p[10]; p é uma matriz de ponteiros com 10 elementos para
valores inteiros
int (*p)[10]; p é um ponteiro para uma matriz de inteiros com 10
elementos
int *p(void); p é uma função que retorna um ponteiro para um valor
inteiro
int *p(char *a); p é uma função que aceita um argumento que é um
ponteiro para um caracter e retorna um ponteiro para um
valor inteiro
int (*p)(char *a); p é m ponteiro para uma função que aceita um
argumento que é um ponteiro para um caracter e retorna
um valor inteiro
int (*p(char *a))[10]; p é uma função que aceita um argumento que é um
ponteiro para um caracter e retorna um ponteiro para
uma matriz inteira de 10 elementos
int p(char (*a)[]); p é uma função que aceita um argumento que é um
ponteiro para uma matriz de caracter e retorna um valor
inteiro
int p(char *a[]); p é uma função que aceita um argumento que é uma
matriz de ponteiros para caracter e retorna um valor
inteiro
int *p(char a[]); p é uma função que aceita um argumento que é uma
matriz de caracter e retorna um ponteiro para um valor
inteiro
int *p(char (*a)[]); p é uma função que aceita um argumento que é um
ponteiro para uma matriz de caracter e retorna um
ponteiro para um valor inteiro
int *p(char *a[]); p é uma função que aceita um argumento que é uma
matriz de ponteiros para caracteres e retorna um
ponteiro para um valor inteiro
int (*p)(char (*a)[]); p é um ponteiro para uma função que aceita um
argumento que é um ponteiro para uma matriz de
caracter e retorna um valor inteiro
int *(*p)(char (*a)[]); p é um ponteiro para uma função que aceita um
argumento que é um ponteiro para uma matriz de
caracter e retorna um ponteiro para um valor inteiro
int *(*p)(char *a[]); p é um ponteiro para uma função que aceita um
argumento que é uma matriz de ponteiros para
caracteres e retorna um ponteiro para um valor inteiro
int (*p[10])(char a); p é uma matriz de ponteiros com 10 elementos para
funções; cada função aceita um argumento que é um
LINGUAGEM C Ponteiros
63/103
caracter e retorna um valor inteiro
int *(*p[10])(char a); p é uma matriz de ponteiros com 10 elementos para
funções; cada função aceita um argumento que é um
caracter e retorna um ponteiro para um valor inteiro
int *(*p[10])(char *a); p é uma matriz de ponteiros com 10 elementos para
funções; cada função aceita um argumento que é um
ponteiro para um caracter e retorna um ponteiro para um
valor inteiro
12.7 Exercícios
1. Como referenciar mat[x][y] em notação de ponteiros.
2. Qual será a saída deste programa?
 main()
 {
 int i=5;
 int *p;
 p = &i;
 printf(“%u %d %d %d %d \n”, p, *p+2,**&p,3**p,**&p+4);
 }
3. Escreva uma função que inverta a ordem dos caracteres de uma string.
4. Crie uma função que receba como parâmetro uma matriz de ponteiros para strings e devolve a matriz
ordenada.
5. Faça uma função que receba um ponteiro para uma matriz e que realize a ordenação da mesma.
6. Faça a declaração de uma função (nome: teste) que receba um ponteiro para uma função que possui
dois argumentos (int e char) e retorne um ponteiro para um float.
7. Faça a declaração e inicialização de uma matriz de ponteiros para os dias da semana.
8. Faça uma função que receba uma matriz de ponteiros para caracteres e realize a ordenação alfabética
da mesma.
LINGUAGEM C Estruturas e Uniões
64/103
13. Estruturas e Uniões
A linguagem C permite criar tipos de dados definíveis pelo usuário de cinco formas diferentes. A
primeira é estrutura, que é um agrupamento de variáveis sobre um nome e, algumas vezes, é chamada de
tipo de dado conglomerado. O segundo tipo definido pelo usuário é o campo de bit, que é uma variação
da estrutura que permite o fácil acesso aos bits dentro de uma palavra. O terceiro é a união, a qual
permite que a mesma porção da memória seja definida por dois ou mais tipos diferentes de variáveis. Um
quarto tipo de dado é a enumeração, que é uma lista de símbolos, como foi visto na seção 1.5. O último
tipo
definido pelo usuário é criado através do uso de typedef e define um novo nome para um tipo
existente.
13.1 Estruturas
O tipo estruturado struct possibilita a criação de estruturas de dados complexas, isto é, pode-se
obter estruturas que contenham mais de um tipo de dado. Tal estrutura é conhecida em outras linguagens
como registros.
Cada elemento que compõe a estrutura (chamado campo) pode ser acessado individualmente,
assim como a estrutura pode ser acessada como um todo. Em C, a declaração de uma estrutura é feita da
seguinte forma:
struct [nome_struct] {
 tipo var1;
 tipo var2;
 …
 tipo varN;} [nome_var];
Deve-se encerrar com um ponto-e-vírgula a declaração porque a definição de estrutura é na
realidade uma instrução C.
A declaração de estruturas pode se apresentar de diversas formas. Tais como:
struct {
char nome[30];
int idade;
int codigo;
float saldo; } conta1, conta2;
Na declaração acima, o nome_struct não é utilizado pois esta estrutura será utilizada pelas
variáveis de estrutura conta1 e conta2. Para utilizar esta estrutura na definição de outras variáveis tem-se
que declará-las juntas com a definição da estrutura. No caso de um programa que utilize esta estrutura
para passar parâmetros, declarar variáveis locais, entre outros, a linguagem permite a criação de rótulos de
estruturas (nome_struct).
struct cad_conta {
char nome[30];
int idade;
int codigo;
float saldo; } conta1, conta2;
Como mostra o exemplo acima, foram declaradas as variáveis conta1 e conta2 como sendo uma
estrutura do tipo cad_conta. Quando rotula-se a estrutura pode-se omitir a declaração das variáveis, como
é mostrado no exemplo abaixo:
struct cad_conta {
char nome[30];
int idade;
LINGUAGEM C Estruturas e Uniões
65/103
int codigo;
float saldo; };
Para usar esta estrutura em outras declarações deve-se especificar desta forma:
struct cad_conta conta1, conta2;
As estruturas seguem o padrão do escopo de variáveis, isto é, se a declaração estiver contida
numa função, a estrutura tem escopo local para aquela função; se a declaração estiver fora de todas as
funções, ela terá um escopo global.
Para acessar um campo específico de uma struct utiliza-se o operador . (ponto).
Exemplo 13.1
conta1.saldo = 0;
conta1.codigo = 0;
strcpy(conta1.nome,”Joao”);
conta1.idade = 21;
É permitida a atribuição entre struct. Neste caso todos os campos são copiados.
Exemplo 13.2
conta2 = conta1;
13.1.1 Inicializando Estruturas
Uma estrutura só pode ser inicializada se pertencer às classes static ou extern. Observe que a
classe de uma estrutura é dada pelo ponto em que as variáveis foram declaradas e não pelo ponto onde a
estrutura foi definida.
Da mesma forma que os vetores, as estruturas são inicializadas com uma lista de valores (cada
um correspondente a um campo de estrutura) entre chaves e separados por vírgulas.
struct cad_conta {
char nome[30];
int idade;
int codigo;
float saldo; };
main()
{
 static struct cad_conta
 conta1 = {“Andre”, 23, 9507, 1567.89},
 conta2 = {“Carlos”, 33, 9678, 1000.59};
 …
}
13.1.2 Estruturas Aninhadas
Como os campos da estrutura podem ser de qualquer tipo, também é permitido o uso de
estruturas na declaração.
struct data {
 int dia;
 char mes[10];
 int ano;
 };
struct func {
 char nome[20];
LINGUAGEM C Estruturas e Uniões
66/103
 int codigo;
 float salario;
 struct data nascimento;
 };
main()
{
 static struct func
 funcionario = {“Marcio”, 1234, 3743.44, {10, “Janeiro”, 1967}},
 gerente = {“Jose”, 456, 5634.28, {18, “Marco”, 1950}};
}
Observe a inicialização das variáveis. A estrutura é inicializada também com uma lista de valores
entre chaves e separados por vírgulas. O acesso a um campo de uma estrutura aninhada é feito na forma:
funcionário.nascimento.dia = 10;
strcpy(gerente.nascimento.mes,”Abril”);
13.1.3 Estruturas e funções
Em versões mais antigas de compiladores C, as estruturas não podiam ser usadas em passagem
de parâmetros por valor para funções. Isto se devia a razões de eficiência, uma vez que uma estrutura
pode ser muito grande e a cópia de todos os seus campos para a pilha poderia consumir um tempo
exagerado. Desta forma, as estruturas eram obrigatoriamente passadas por referência, usando-se o
operador de endereço (&).
No Turbo C e outros compiladores mais recentes, a responsabilidade da decisão fica a cargo do
programador. Assim, uma função pode passar ou retornar uma estrutura.
struct cad_conta {
char nome[30];
int idade;
int codigo;
float saldo; };
main()
{
static struct cad_conta conta1;
conta1 = ins_conta();
lista(conta1);
…
}
struct cad_conta ins_conta()
{
 struct cad_conta aux;
 gets(aux.nome);
 scanf(“%d”, &aux.idade);
 scanf(“%d”, &aux.codigo);
 scanf(“%f”, &aux.saldo);
 return(aux);
}
void lista(aux)
struct cad_conta aux;
{
 printf(“Nome : %s\n”,aux.nome);
 printf(“Idade : %d\n”, aux.idade);
 printf(“Codigo : %d\n”, aux.codigo);
 printf(“Saldo : %.2f\n”, aux.saldo);
}
LINGUAGEM C Estruturas e Uniões
67/103
13.1.4 Vetor de Estruturas
A criação de tabela de estruturas mantém a sintaxe normal de definição de matrizes, como é
mostrada abaixo:
struct cad_conta {
char nome[30];
int idade;
int codigo;
float saldo; };
main()
{
int i
static struct cad_conta conta[10]=
{ {“Andre”, 23, 9507, 1567.89},
 {“Carlos”, 33, 9678, 1000.59},
...
};
for (i=0;i<10;i++)
 {
 printf(“Nome : %s\n”,conta[i].nome);
 printf(“Idade : %d\n”, conta[i].idade);
 printf(“Codigo : %d\n”, conta[i].codigo);
 printf(“Saldo : %.2f\n”, conta[i].saldo);
 }
…
}
13.1.5 Ponteiros para Estruturas
C permite ponteiros para estruturas exatamente como permite ponteiros para outros tipos de
variáveis. No entanto, há alguns aspectos especiais de ponteiros de estruturas.
Como outros ponteiros, declara-se colocando um * na frente do nome da estrutura. No exemplo
12.3 declara-se ptr_cta como um apontador da estrutura previamente definida cad_conta.
Exemplo 13.3
struct cad_conta *ptr_cta;
Há dois usos primários para ponteiros de estrutura: gerar uma chamada por referência para uma
função e criar estruturas de dados dinâmicas (listas, pilhas, filas, entre outras) utilizando-se do sistema de
alocação de C.
Na forma de acessar os elementos ou campos de uma estrutura usando um ponteiro para a
estrutura, deve-se utilizar o operador -> (seta). A seta é usada sempre no caso de apontador de estruturas.
No exemplo abaixo é mostrada a declaração, atribuição e utilização de ponteiros de estruturas.
Exemplo 13.4
struct cad_conta {
char nome[30];
int idade;
int codigo;
float saldo; } conta;
main()
{
struct cad_conta *ptr;
LINGUAGEM C Estruturas e Uniões
68/103
ptr = &conta; /* o ponteiro recebe o endereco da
estrutura a ser apontada */
ptr->idade = 23;
ptr->codigo = 1000;
}
13.2 Campos de Bits
Ao contrário das linguagens de computador, C tem um método intrínseco para acessar um único
bit dentro de um byte. Isso pode ser útil por um certo número de razões:
• Se o armazenamento é limitado, você pode armazenar diversas variáveis Booleanas
(verdadeiro/falso) em um byte.
• Certos dispositivos transmitem informações codificadas nos bits dentro de um byte.
• Certas rotinas de criptografia precisam acessar os bits dentro de um byte.
 Para acessar os bits, C usa um método baseado na estrutura. Um campo de bits é, na verdade,
apenas um tipo de elemento de estrutura que define o comprimento, em bits, do campo. A forma geral de
uma definição de campo de bit é:
struct nome {
 tipo var1 : comprimento;
 tipo var2 : comprimento;
 …
 tipo varN : comprimento;} [lista_de_variaveis];
 Um campo de bit deve ser declarado como int, unsigned ou signed.
Campos de bit de
comprimento 1 devem ser declarados como unsigned, porque um único bit não pode ter sinal. (Alguns
compiladores só permitem campos do tipo unsigned).
 Um exemplo de campos de bits é a comunicação via serial que devolve um byte de estado
organizado desta forma:
Bit Significado quando ligado
0 alteração na linha clear-to-send
1 alteração em data-set-ready
2 borda de subida da portadora detectada
3 alteração na linha de recepção
4 clear-to-send
5 data-set-ready
6 chamada do telefone
7 sinal recebido
Pode-se representar a informação em um byte de estado utilizando o seguinte campo de bits:
struct status_type {
unsigned delta_cts : 1;
unsigned delta_dsr : 1;
unsigned tr_edge : 1;
unsigned delta_rec : 1;
unsigned cts : 1;
unsigned dsr : 1;
unsigned ring : 1;
unsigned rec_line : 1;
} status;
LINGUAGEM C Estruturas e Uniões
69/103
Para atribuir um valor a um campo de bit, simplesmente utiliza-se a forma para atribuição de
outro tipo de elemento de estrutura.
status.ring = 0;
Não é necessário dar um nome a todo campo de bit. Isto torna fácil alcançar o bit que se deseja
acessar, contornando os não usados. Por exemplo, se apenas cts e dtr importam, pode-se declarar a
estrutura status_type desta forma:
struct status_type {
unsigned : 4;
unsigned cts : 1;
unsigned dsr : 1;
} status;
Além disso, nota-se que os bits após dsr não precisam ser especificados se não são usados.
Variáveis de campo de bit têm certas restrições:
• Não pode obter o endereço de uma variável de campo de bit.
• Variáveis de campo de bit não podem ser organizadas em matrizes.
• Não pode ultrapassar os limites de um inteiro.
• Não pode saber, de máquina para máquina, se os campos estarão dispostos da esquerda para a
direita ou da direita para a esquerda.
• Em outras palavras, qualquer código que use campos de bits pode ter algumas dependências
da máquina.
Finalmente, é válido misturar elementos normais de estrutura com elementos de campos de bit.
Por exemplo,
struct emp {
struct addr address;
float pay;
unsigned lau_off : 1; /* ocioso ou ativo */
unsigned hourly : 1; /* pagamento por horas */
unsigned deduction : 3; /* deduções de imposto */
};
define um registro de um empregado que usa apenas um byte para conter três informações: o estado do
empregado, se o empregado é assalariado e o número de deduções. Sem o campo de bits, essa variável
ocuparia três bytes.
13.3 Uniões
Uma união é um tipo de dado que pode ser usado de muitas maneiras diferentes. Por exemplo,
uma união pode ser interpretada como sendo um inteiro numa operação e um float ou double em outra.
Embora, as uniões possam tomar a aparência de uma estrutura, elas são muito diferentes.
Uma união pode conter um grupo de muitos tipos de dados, todos eles compartilhando a mesma
localização na memória. No entanto, uma união só pode conter informações de um tipo de dados de cada
vez. Para criar uma união utiliza-se a seguinte sintaxe:
union [nome_union] {
 tipo var1;
 tipo var2;
 …
 tipo varN;} [nome_var];
LINGUAGEM C Estruturas e Uniões
70/103
Deve-se encerrar com um ponto-e-vírgula a declaração porque a definição de união é na
realidade uma instrução C. A declaração de uniões pode se apresentar de diversas formas. Tais como:
union {
char c;
int i;
double d;
float f;
} data;
Na declaração acima, o nome_union não é utilizado pois esta união será utilizada pela variável
data. Para utilizar esta união na definição de outras variáveis tem-se que declará-las juntas com a
definição da união. No caso de um programa que utilize esta união em várias partes do programa a
linguagem C permite a criação de rótulos de estruturas (nome_union).
union tipos {
 char c;
 int i;
 double d;
 float f; } data;
Como mostra o exemplo acima, foi declarada a variável data como sendo uma união do tipo
tipos. Quando rotula-se a união pode-se omitir a declaração das variáveis, como é mostrado no exemplo
abaixo:
union tipos {
 char c;
 int i;
 double d;
 float f; };
Para usar esta união em outras declarações deve-se especificar desta forma:
union tipos data1, data2;
As estruturas seguem o padrão do escopo de variáveis, isto é, se a declaração estiver contida
numa função, a estrutura tem escopo local para aquela função; se a declaração estiver fora de todas as
funções, ela terá um escopo global.
Para acessar um campo específico de uma union utiliza-se o operador . (ponto). Pode-se declarar
estruturas dentro de uniões.
struct so_int {
int i1,i2;
 };
struct so_float {
float f1,f2;
 };
union {
struct so_int i;
struct so_float f;
} teste;
main()
{
teste.i.i1 = 2;
teste.i.i2 = 3;
printf(“i1 = %-3d i2 = %-3d\n”,teste.i.i1,teste.i.i2);
teste.f.f1 = 2.5;
teste.f.f2 = 3.5;
printf(“f1 = %.1f f2 = %.1f\n”,teste.f.f1,teste.f.f2);
LINGUAGEM C Estruturas e Uniões
71/103
13.4 Sizeof()
Com uso de estruturas, uniões e enumerações pode-se utilizá-las para a criação de variáveis de
diferentes tamanhos e que o tamanho real dessas variáveis pode mudar de máquina para máquina. O
operador unário sizeof() calcula o tamanho de qualquer variável ou tipo e pode ajudar a eliminar códigos
dependentes da máquina de seus programas.
Exemplo 13.5
union tipos {
 char c;
 int i;
 double d;
 float f; } data;
O sizeof(data) é 8. No tempo de execução, não importa o que a união data está realmente
guardando. Tudo o que importa é o tamanho da maior variável que pode ser armazenada porque a união
tem de ser do tamanho do seu maior elemento.
13.5 Typedef
C permite que defina-se explicitamente novos nomes aos tipos de dados, utilizando a palavra-
chave typedef. Não há criação de uma nova variável, mas sim, definindo-se um novo nome para um tipo
já existente. Serve para uma boa documentação ou até tornar os programas dependentes de máquina um
pouco mais portáteis. A forma geral de um comando typedef é
typedef tipo nome;
Por exemplo, poderia ser criado um novo nome para char utilizando
typedef char boolean;
Esse comando diz ao compilador para reconhecer boolean como outro nome para char. Assim,
para se criar uma variável char, usando boolean
boolean ok;
Também é válida a redefinição, isto é, utilizar um novo nome para um nome atribuído a um dado
previamente estabelecido.
#include <stdio.h>
typedef char boolean;
typedef boolean bool;
void main()
{
 boolean a;
 bool b;
 a = 1;
 b = 2;
 printf("%d %d",a,b);
}
A declaração typedef é usado também para definir tipos estruturados (struct e union) para
facilitar a nomenclatura dos tipos na declaração de variáveis (exemplo 12.6)
LINGUAGEM C Estruturas e Uniões
72/103
Exemplo 13.6
typedef struct conta {
char nome[30];
int idade;
int codigo;
float saldo; } cad_conta;
main()
{
cad_conta *ptr;
ptr = &conta;
ptr->idade = 23;
ptr->codigo = 1000;
}
ou
struct conta {
char nome[30];
int idade;
int codigo;
float saldo; };
typedef struct conta cad_conta;
main()
{
cad_conta *ptr;
ptr = &conta;
ptr->idade = 23;
ptr->codigo = 1000;
}
13.6 Exercícios
1. Faça um programa que leia os dados de 10 clientes de um banco e após leia 100 conjuntos de 3
valores:
• código de operação - 0 depósito, 1 - retirada,
• valor da operação
• código do cliente.
Realize as movimentações nas contas correspondentes e ao final escreva o nome e saldo de cada
cliente.
2. Faça um programa de cadastro de clientes que contenham as seguintes opções: incluir, alteração,
excluir e consultar por código ou por nome. O cadastro deve ser da seguinte forma:
• nome (30 caracteres);
• código (0 a 255);
• idade(char);
LINGUAGEM C Alocação Dinâmica
73/103
14. Alocação Dinâmica
Programas consistem em duas coisas:
algoritmos e estruturas de dados. Um bom programa é uma
combinação de ambos. A escolha e a implementação de uma estrutura de dados são tão importantes
quanto as rotinas que manipulam os dados.
Para a manipulação de dados é utilizado mecanismos que auxiliam tanto na forma de como é
armazenado ou recuperado. Existem vários mecanismos que realizam este tipo de processamento. Abaixo
estão listados alguns mecanismos básicos:
• Listas
• Pilhas
• Filas
• Árvores
Cada um destes mecanismos pode ter variações de acordo com a política de processamento
(armazenamento/recuperação). Neste capítulo será abordado com mais ênfase as listas encadeadas, porque
serão como base para a construção dos demais.
14.1 Funções de Alocação dinâmica em C
A memória alocada pelas funções de alocação dinâmica de C é obtida do heap - a região de
memória livre que está o programa, a área permanente e a pilha. As funções são utilizadas para alocar e
desalocar esta memória, o que estiver livre.
O padrão C ANSI define apenas quatro funções para o sistema de alocação dinâmica: calloc(),
malloc(), free(), realloc(). No entanto, serão estudadas, além das funções descritas, algumas funções que
estão sendo largamente utilizadas.
O padrão C ANSI especifica que os protótipos para as funções de alocação dinâmica definidas
pelo padrão estão em STDLIB.H. Entretanto, tais funções estão especificadas na biblioteca ALLOC.H,
onde encontram-se mais funções de alocação dinâmica.
O padrão C ANSI especifica que o sistema de alocação dinâmica devolve ponteiros void, que são
ponteiros genéricos, podendo apontar para qualquer objeto. Porém, alguns compiladores mais antigos
devolvem ponteiros para char. Nesse caso, deve-se usar um cast quando atribuir a ponteiros de tipos
diferentes.
14.1.1 Função malloc()
Esta função devolve um ponteiro para o primeiro byte de uma região de memória de tamanho
size que foi alocada do heap. No caso em que não houver memória suficiente, a função devolve um
ponteiro nulo.
Cuidado! Ao usar um ponteiro nulo, pode ocorrer uma quebra do sistema.
Sintaxe.:
void *malloc(size_t size);
Onde size_t pode ser considerado um inteiro sem sinal e size é o número de bytes de memória
que se quer alocar. Essa função devolve um ponteiro void, como mostra a sintaxe, portanto pode-se
atribuir a qualquer tipo de ponteiro.
LINGUAGEM C Alocação Dinâmica
74/103
Para assegurar a portabilidade de um programa que utilize a alocação dinâmica, faz-se necessário
a utilização da função sizeof().
Exemplo 14.1
Esta função aloca memória suficiente para conter uma estrutura do tipo addr:
struct addr {
char nome[40];
char rua[40];
char cidade[40];
char estado[3];
char cep[10];};
struct addr *get_struct(void)
{
 struct addr *p;
 if ((p=(struct addr*)malloc(sizeof(addr)))==NULL)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 return p;
}
O fragmento do código mostra a alocação de 1000 bytes de memória.
char *p;
p = (char*)malloc(1000);
No fragmento abaixo é alocado memória suficiente para 50 inteiros.
int *p;
p = (int*)malloc(50 * sizeof(int));
O compilador deve conhecer duas informações sobre qualquer ponteiro: o endereço da variável
apontada e seu tipo. Por isso, precisa-se fazer uma conversão de tipo (cast) do valor retornado por
malloc(), já que o mesmo retorna um void. Portanto, no exemplo 13.15 deve-se indicar ao compilador que
o valor retornado por malloc() é do tipo ponteiro para struct addr.
p=(struct addr*) malloc(sizeof(addr))
Este tipo de conversão deve ser realizado em todas as funções de alocação como calloc(),
realloc() e malloc().
14.1.2 Função calloc()
Esta função devolve um ponteiro para o primeiro byte de uma região de memória de tamanho
size * num que foi alocada do heap. No caso em que não houver memória suficiente, a função devolve um
ponteiro nulo.
Sintaxe.:
void *calloc(size_t num, size_t size);
Onde size_t pode ser considerado um inteiro sem sinal e size é o número de bytes de memória
que se quer alocar. Essa função devolve um ponteiro void, como mostra a sintaxe, portanto pode-se
atribuir a qualquer tipo de ponteiro.
LINGUAGEM C Alocação Dinâmica
75/103
Para assegurar a portabilidade de um programa que utilize a alocação dinâmica, faz-se necessário
a utilização da função sizeof().
A diferença entre calloc() e malloc() é que a primeira aloca a memória e inicializa-a com zeros.
Exemplo 14.2
Esta função aloca memória suficiente para conter umvetor de 100 elementos:
#include “stdlib.h”
#include “stdio.h”
float *get_mem(void)
{
 float *p;
 p=(float*)calloc(100, sizeof(float));
 if (!p)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 return p;
}
No fragmento abaixo é alocado memória suficiente para 50 inteiros.
int *p;
p = (int*)calloc(50,sizeof(int));
14.1.3 Função free()
Esta função devolve ao heap a memória apontada por ptr, tornando a memória disponível para
alocação futura.
free() deve ser chamada somente com um ponteiro que foi previamente alocado com uma das
funções do sistema de alocação dinâmica. A utilização de um ponteiro inválido na chamada
provavelmente destruirá o mecanismo de gerenciamento de memória e provocará uma quebra do sistema.
Exemplo 14.3
#include <string.h>
#include <stdio.h>
#include <alloc.h>
int main(void)
{
 char *str;
 /* aloca memoria para uma string */
 str = (char*)malloc(10);
 /* copia "Hello" para a string */
 strcpy(str, "Hello");
 /* mostra a string */
 printf("String: %s\n", str);
 /* libera a memoria */
 free(str);
 return 0;
}
LINGUAGEM C Alocação Dinâmica
76/103
14.1.4 Função realloc()
Esta função modifica o tamanho da memória previamente alocada apontada por ptr para aquele
especificado por size. O valor de size pode ser maior ou menor que o original. Um ponteiro para o bloco
de memória é devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se
isso ocorre, o conteúdo do bloco antigo é copiado no novo bloco; nenhuma informação é perdida.
Sintaxe.:
void *realloc(void *ptr, size_t size);
Se ptr é um nulo, realloc() simplesmente aloca size bytes de memória e devolve um ponteiro
para a memória alocada. Se size é zero, a memória apontada por ptr é liberada.
Se não há memória livre suficiente no heap para alocar size bytes, é devolvido um ponteiro nulo
e o bloco original é deixado inalterado.
Exemplo 14.4
Esta programa primeiro aloca 23 caracteres, copia a string “isso são 22 caracteres” neles e, em
seguida, usa realloc() para aumentar o tamanho para 24 e, assim, pôr um ponto no final.
#include “stdlib.h”
#include “stdio.h”
#include “string.h”
void main(void)
{
 char *p;
 p=(char*)malloc(23);
 if (!p)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 strcpy(p,”isso são 22 caracteres”);
 p = (char*)realloc(p,24);
 if (!p)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 strcat(p,”.”);
 printf(p);
 free(p);
}
14.2 Matrizes Dinamicamente Alocadas
Qualquer ponteiro pode ser indexado como se fosse uma matriz unidimensional, portanto não
haverá nenhum problema para utilizar (exemplo 13.19).
Exemplo 14.6
/* Aloca memória para uma string dinamicamente, solicita */
/* a entrada do usuário e, em seguida, imprime a string */
/* de trás para frente. */
#include “stdlib.h”
#include “stdio.h”
LINGUAGEM C Alocação Dinâmica
77/103
#include “string.h”
void main(void)
{
 char *s;
 register int t;
 s=(char*)malloc(80);
 if (!s)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 gets(s);
 for (t=strlen(s)-1; t>=0; t--) putchar(s[t]);
 free(s);
}
Para acessar uma matriz unidimensional é simples, mas para mais de uma dimensão levam alguns
problemas com a indexação.
Para conseguir uma matriz alocada dinamicamente, deve-se utilizar um truque: passar um
ponteiro como um parâmetro a uma função. Dessa forma, a função pode definir as dimensões do
parâmetro que recebe o ponteiro, permitindo, assim, a indexação normal da matriz. Isto é mostrado no
exemplo 13.20.
Exemplo 14.7
#include “stdlib.h”
#include “stdio.h”
#include “string.h”
void le_tab(int mat[20][5])
{
 register int i,j;
 for(i=0; i<20; i++)
 for(j=0; j<5; j++)
 scanf(“%d”,&mat[i][j]);
}
void mostra_tab(int mat[20][5])
{
 register int i,j;
 for(i=0; i<20; i++)
 for(j=0; j<5; j++)
 printf(“%d”,mat[i][j]);
}
void main(void)
{
 char *p;
 register int t;
 s=(int*)calloc(100, sizeof(int));
 if (!p)
 {
 printf(“ erro de alocação - abortando”);
 exit(1);
 }
 le_tab(p);
 mostra_tab(p);
}
LINGUAGEM C Alocação Dinâmica
78/103
14.3 Listas Encadeadas
Listas encadeadas são usadas para dois propósitos fundamentais. O primeiro é criar matrizes de
tamanho desconhecido na memória. Listas encadeadas também são usadas em armazenamento de banco
de dados em arquivos em disco.
Listas encadeadas podem ser singularmente (simplesmente) - um elo para o próximo item - ou
duplamente - elos para o anterior e próximo elemento da lista - encadeadas.
14.3.1 Listas Singularmente Encadeadas
Uma lista singularmente encadeada requer que cada item de informação contenha um elo como o
próximo da lista. Cada item de dado geralmente consiste em uma estrutura que inclui campos de
informação e ponteiro de enlace (ou de ligação).
Antes, precisa-se definir a estrutura de dados que contenha a informação e os elos. Considere um
exemplo de armazenamento de coordenadas cartesianas (x,y,z) para representação de uma figura
geométrica. A estrutura de dados para cada elemento é definido aqui:
struct ponto {
int x,y,z;
struct ponto *prox;
} figura1;
typedef struct ponto figura;
Na estrutura acima, é declarado um apontador para a próxima estrutura, por isso declara-se um
apontador para a própria estrutura (auto-referência). Após a declaração, foi definido que figura é um tipo,
que representa uma struct ponto, o qual pode ser utilizado em todo o programa.
A função inclui() constrói uma lista singularmente encadeada colocando cada novo item no final
da lista. Deve ser passado um ponteiro para uma estrutura do tipo ponto, ponteiro para o primeiro
elemento e ponteiro para o último elemento.
void inclui(figura *i, figura **inicio, figura **fim)
{
if (!*fim) /* Primeiro elemento da lista */
 {
 *fim = i;
 *inicio = i;
 }
else
 {
 (*fim)->prox = i;
 *fim = i;
 }
}
Os parâmetros início e fim da função têm dois asteriscos porque representam uma indireção
múltipla. Isto é, são apontadores para apontadores da estrutura figura. Isto é necessário para poder
implementar a passagem de parâmetros por referência.
Apagar um item de uma lista singularmente encadeada pode ocorrer em três situações: apagar o
primeiro item, apagar um item intermediário e apagar o último item. A função a seguir excluirá um item
de uma lista de estruturas do tipo ponto:
void exclui(
figura *p, /* item anterior */
figura *i, /* item a apagar */
LINGUAGEM C Alocação Dinâmica
79/103
figura **inicio, /* início da lista */
figura **ultimo) /* final da lista */
{
 if (p)
 p->next = i->next;
 else
 *start = i->next;
 if (i==*last && p)
 *last = p;
}
Listas singularmente encadeadas têm uma desvantagem é que a lista não pode ser lida em ordem
inversa.
14.3.2 Listas Duplamente Encadeadas
Consistem em dados e elos para o próximo item e para o item precedente. Um novo elemento
pode ser inserido em uma lista duplamente encadeada de três maneiras: inserir um novo primeiro
elemento, inserir um elemento intermediário ou inserir um novo último elemento.
A construção de uma lista duplamente encadeada é semelhante à de uma lista singularmente
encadeada, exceto pelo fato de que dois elos devem ser mantidos. Utilizando a estrutura ponto, será
mostrado a declaração de um nodo de lista duplamente encadeada.
struct ponto {
 int x,y,;
 struct ponto *prox;
 struct ponto *ant;
 };
typedef struct ponto figura;
Usando figura como o item de dado básico, a função seguinte constrói uma lista duplamente
encadeada. Esta função inclui um novo dado no fim da lista:
void incfim(figura *i, figura **fim)
{
 if (!*fim)
 *fim = i; /* é o primeiro item da lista */
 else
 (*fim)->prox = i;
 i->prox = NULL;
 i->ant = *fim;
 *fim = i;
}
Para armazenagem de um dados em uma posição específica a função abaixo realiza a inclusão
em ordem crescente pelo eixo x de uma lista duplamente encadeada.
void incord(figura *i, /* novo elemento */
 figura **inicio, /* primeiro elemento da lista */
 figura **fim) /* ultimo elemento da lista */
{
 if (!*fim) /* é o primeiro item da lista */
 {
 i->prox = NULL;
 i->ant = NULL;
 *inicio = i;
 *fim = i;
 }
 else
 {
LINGUAGEM C Alocação Dinâmica
80/103
 figura *old, *p;
 p = *inicio;
 old = NULL;
 while (p && (p->x < i->x))
 {
 old = p;
 p = p->prox;
 }
 if (!old)
 {
 i->prox = p; /* inserir no inicio da lista */
 i->ant = NULL;
 p->ant = i;
 *inicio = i;
 }
 else
 {
 if (p->ant)
 { /* inserir em uma posição */
 p->ant->prox = i; /* intermediária da lista */
 i->prox = p;
 i->ant = p->ant;
 p->ant = i;
 }
 else
 {
 old->prox = i; /* inserir no fim da lista */
 i->prox = NULL;
 i->ant = old;
 *fim = i;
 }
 }
 }
Como o ponteiro de início e fim de lista podem ser alterados, a função incord() atualiza
automaticamente estes ponteiros através das variáveis inicio e fim.
Há três casos a considerar ao excluir um elemento de uma lista duplamente encadeada: excluir o
primeiro item, excluir um item intermediário ou excluir o último item.
void delord(figura *i, /* item a apagar */
 figura **inicio, /* primeiro elemento da lista */
 figura **fim) /* ultimo elemento da lista */
{
 figura *old, *p;
 p = *inicio;
 old = NULL;
 while (p && (p->x != i->x) &&(p->y != i->y))
 {
 old = p;
 p = p->prox;
 }
 if ((p->x = i->x) &&(p->y = i->y))
 {
 if (!old) /* exclusao unico elemento da lista */
 *inicio=*fim=NULL;
 else
 {
 if (!p->prox) * exclusao do ultimo elemento da lista */
 {
 old->prox = NULL;
 *fim = old;
 }
 else
 { /* excluir item de uma posição */
 old->prox = p->prox; /* intermediária da lista */
 p->prox->ant = old;
LINGUAGEM C Alocação Dinâmica
81/103
 }
 }
 free(p);
 }
 }
14.4 Árvores Binárias
A estrutura utilizada para a cosntrução de árvores binárias é semelhante a listas duplamente
encadeadas. A diferença está na política de organização das mesmas.
A função abaixo constrói uma árvore binária ordenada recursivamente:
struct tree {
char info;
struct tree *esq;
struct tree *dir;
};
typedef struct tree arvore;
arvore *stree (
arvore *raiz;
arvore *r;
char info);
{
 if (!r)
 {
 r = (arvore *) malloc(sizeof(arvore));
 if (!r)
 {
 printf(“Sem memória \n”);
 exit(0);
 }
 r->esq = NULL;
 r->dir = NULL;
 r->info = info;
 if (!raiz)
 return r; /* primeira entrada */
 if (info < raiz->info)
 raiz->esq = r;
 else
 raiz->dir = r;
 return r;
 }
if (info < r->info)
 stree(r,r->esq, info);
else
 stree(r,r->dir, info);
A chamada desta função é realizada desta forma:
if (!rt)
 rt = stree(rt, rt, info);
else
 stree (rt, rt, info);
Dessa forma, tanto o primeiro quanto
os elementos subsequentes podem ser inseridos
corretamente. A função stree() é um algoritmo recursivo.
Existem três formas de acessar os dados de uma árvore: ordenada, preordenada e pós-ordenada.
Onde a ordenada, é visitado a subárvore da esquerda, a raiz e em seguida a subárvore da direita. Na
LINGUAGEM C Alocação Dinâmica
82/103
preordenada, visita-se a raiz, subárvore da esquerda e, em seguida, a subárvore da direita. Na pós-
ordenada, visita-se a subárvore da esquerda, subárvore da direita e, depois, a raiz.
d
b f
a c e g
Figura 14.1 Exemplo de uma árvore binária
Utilizando a figura 14.1, a ordem de acesso á árvore usando cada método é
ordenada a b c d e f g
preordenada d b a c f e g
pós-ordenada a c b e g f d
Para o acesso de forma ordenada, pelas formas descritas anteriormente, pode-se utilizar as
funções descritas abaixo:
void inorder(arvore *raiz)
{
 if(!raiz)
 return;
 inorder(raiz->esq);
 printf(“%c”, raiz->info);
 inorder(raiz->dir);
}
void preorder(arvore *raiz)
{
 if(!raiz)
 return;
 printf(“%c”, raiz->info);
 preorder(raiz->esq);
 preorder(raiz->dir);
}
void postorder(arvore *raiz)
{
 if(!raiz)
 return;
 postorder(raiz->esq);
 postorder(raiz->dir);
 printf(“%c”, raiz->info);
}
Para exclusão de um nó de uma árvore tem que ser verificado se o nó é a raiz, um nodo esquerdo
ou direito e que os mesmos podem ter subárvores ligadas a ele. Na função a seguir é realizada uma
exclusão recursiva observando as restrições delineadas anteriormente.
arvore *dtree(arvore *raiz, char key)
{
arvore *p, *p2;
if (raiz->info==key) /* apagar a raiz */
 {
 if (raiz->esq== raiz->dir) /*não tem filhos */
 {
LINGUAGEM C Alocação Dinâmica
83/103
 free(raiz);
 return NULL;
 }
 else
 if (raiz->esq == NULL)
 {
 p = raiz->dir;
 free(raiz);
 return p;
 }
 else
 if (raiz->dir == NULL)
 {
 p = raiz->esq;
 free(raiz);
 return p;
 }
 else
 {
 p2 = raiz->dir;
 p = raiz->dir;
 while (p->esq)
 p = p->esq;
 p->esq = raiz->esq;
 free(raiz);
 return p2;
 }
 }
if (raiz->info < key)
 raiz->dir = dtree(raiz->dir, key);
else
 raiz->esq = dtree(raiz->esq, key);
return raiz;
}
Árvores binárias oferecem grande poder, flexibilidade e eficiência quando usadas em programas
de gerenciamento de banco de dados. Principalmente pela sua política de organização e a não limitação do
tamanho (exceto aquela imposta pela memória).
14.5 Exercícios
1. Escreva um programa que leia vários nomes e endereços, rearranje os nomes em ordem alfabética e,
depois, imprima a lista em ordem alfabética. Utilize várias estruturas.
2. Escreva um programa que gerencie uma pilha. O mesmo deve conter a função de empilhar e
desempilhar para o usuário os quatro tipos de dados básicos da linguagem C (char, float, int, double).
3. Escreva um programa que gerencie uma fila circular do tipo FIFO (Primeiro que entra é o primeiro
que sai).
9. Faça um programa que leia o número de alunos; construa uma matriz dinamicamente alocada de
tamanho N X 4, onde N é o número de alunos e 4 as respectivas notas de cada aluno. Calcule a média
e mostre na tela conforme descrição a seguir:
 ALUNO N1 N2 N3 N4 MEDIA
 1 8.5 7.0 9.5 7.0 8.0
 2 7.5 7.0 6.5 7.0 7.0
LINGUAGEM C E/S com Arquivo
84/103
15. E/S com Arquivo
São grupos de dados armazenados em meio não volátil (disco, fita, entre outros). São utilizados
para armazenar dados de forma permanente.
A linguagem C não contém nenhum comando de E/S. Ao contrário, todas as operações de E/S
ocorrem através de chamadas a funções da biblioteca C padrão. Essa abordagem faz o sistema de arquivos
de C extremamente poderoso e flexível. O sistema de E/S de C é único, porque dados podem ser
transferidos na sua representação binária interna ou em um formato de texto.
15.1 E/S ANSI x E/S UNIX
O padrão ANSI define um conjunto completo de funções de E/S que pode ser utilizado para ler e
escrever qualquer tipo de dado. Em contraste, o antigo padrão C UNIX contém dois sistemas distintos de
rotinas que realizam operações de E/S. O primeiro método é denominado de sistema de arquivo com
buffer (algumas vezes os termos formatado ou alto nível são utilizados para referenciá-lo). O segundo é o
sistema de arquivo tipo UNIX (algumas vezes chamado de não formatado ou sem buffer) definido apenas
sob o antigo padrão UNIX.
15.2 Streams
A linguagem C oferece uma abstração da interface para controle de E/S, independente do
dispositivo real (terminais, acionadores de disco, acionadores de fita, entre outros) que é acessado. Sendo
que o dispositivo real é chamado de arquivo.
Existem dois tipos de streams: texto e binária. A primeira é uma seqüência de caracteres
organizados em linhas e terminadas por um caractere de nova linha (depende da implementação). A
segunda é uma seqüência de bytes com uma correspondência de um para um com aqueles encontrados no
dispositivo externo - isto é, não ocorre nenhuma tradução de caracteres.
15.3 Arquivos
Um arquivo pode ser qualquer coisa, desde um arquivo em disco até um terminal ou uma
impressora. Associa-se uma stream com um arquivo específico realizando uma operação de abertura.
Todos as streams são iguais, mas não todos os arquivos. Isto é, um arquivo disco pode suportar acesso
aleatório enquanto um teclado não pode.
Cada stream associada a um arquivo tem uma estrutura de controle de arquivo do tipo FILE.
Essa estrutura é definida no cabeçalho STDIO.H.
Todos os arquivos são fechados automaticamente quando o programa termina, normalmente com
main() retornando ao sistema operacional ou uma chamada à exit(). Os arquivos não são fechados quando
um programa quebra (crash).
LINGUAGEM C E/S com Arquivo
85/103
15.4 Sistema de Arquivos
O sistema de arquivos ANSI é composto de diversas funções inter-relacionadas. As mais comuns
são mostradas na tabela 15.1. Essas funções exigem que o cabeçalho STDIO.H seja incluído em qualquer
programa em que são utilizadas.
Tabela 15.1 - Funções mais comuns do sistema de arquivo com buffer
Nome Função
fopen() Abre um arquivo
fclose() Fecha um arquivo
putc() Escreve um caractere em um arquivo
fputc() O mesmo que putc()
getc() Lê um caractere de um arquivo
fgetc() O mesmo que getc()
fseek() Posiciona o arquivo em um byte específico
fprintf() É para um arquivo o que printf() é para o console
fscanf() É para um arquivo o que scanf() é para o console
feof() Devolve verdadeiro se o fim de arquivo for atingido
ferror() Devolve verdadeiro se ocorreu um erro
rewind() Repõe o indicador de posição de arquivo no início do arquivo
remove() Apaga um arquivo
fflush() Descarrega um arquivo
O arquivo cabeçalho STDIO.H fornece os protótipos para as funções de E/S e define estes três
tipos: size_t, fpos_t e FILE. O tipo size_t é essencialmente o mesmo que um unsigned, assim como o
fpos_t. O tipo FILE é discutido na próxima seção.
STDIO.H define várias macros como: EOF, SEEK_SET, SEEK_CUR e SEEK_END. A
macro EOF é geralmente definida como -1 e é o valor quando uma função de entrada tenta ler além do
final do arquivo. As outras macros são usadas com fseek(), que é uma função que executa acesso aleatório
em um arquivo.
15.5 Estrutura FILE
Para a manipulação de arquivos é utilizado a declaração de ponteiro (ponteiro de arquivo). Isto
é, um ponteiro para informações que definem vários dados sobre o arquivo, como o seu nome, status, e a
posição atual do arquivo. Um ponteiro de arquivo é uma variável ponteiro do tipo FILE . Todas as
funções são realizadas utilizando o ponteiro. Para a declaração de um ponteiro de arquivo utiliza-se a
seguinte sintaxe:
Sintaxe:
FILE *<var>
Exemplo 15.1
FILE *fp;
15.6 Abertura de Arquivos
A função fopen() abre uma stream para uso e associa um arquivo a ela. Retorna o ponteiro de
arquivo associado a esse arquivo.
Sintaxe:
LINGUAGEM C E/S com Arquivo
86/103
FILE *fopen(const char * <nome_ arquivo>, const char *<modo_abertura>);
O modo de abertura define a forma como é feito o acesso aos dados (somente leitura, leitura e
escrita, etc). As forma principais são apresentadas na tabela 15.2.
Tabela 15.2 - Os modos de abertura válidos
Modo Significado
r Abre um arquivo texto para leitura
w Cria um arquivo texto para escrita
a Anexa a um arquivo texto
rb Abre um arquivo binário para leitura
wb Cria um arquivo binário para escrita
ab Anexa a um arquivo binário
r+ Abre um arquivo texto para leitura/escrita
w+ Cria um arquivo texto para leitura/escrita
a+ Anexa ou cria um arquivo texto para leitura/escrita
r+b ou rb+ Abre um arquivo binário para leitura/escrita
w+b ou wb+ Cria um arquivo binário para leitura/escrita
a+b ou ab+ Anexa a um arquivo binário para leitura/escrita
Exemplo 15.2
FILE *arq; /* ponteiro de arquivo */
arq = fopen(“dados.dat”,”wb”);
Se ao abrir um arquivo para leitura o mesmo não existir a função fopen retorna um ponteiro nulo
(NULL).
arq = fopen(“dados.dat”,”rb”);
if (arq= =NULL)
 arq=fopen(“dados.dat”,”wb”);
15.7 Fechamento de Arquivo
A função fclose() fecha uma stream que foi aberta através de uma chamada à fopen(). Esta
função tem a seguinte sintaxe:
Sintaxe:
int fclose(FILE *fp);
onde fp é o ponteiro de arquivo devolvido pela chamada à fopen(). O valor de retorno 0 significa uma
operação de fechamento bem-sucedida. Qualquer outro valor indica erro, que pode ser diagnosticada pela
função ferror() (discutida mais adiante).
15.8 Leitura e Gravação de caracteres
Para as operações de leitura e gravação são utilizadas duas funções padrões: getc() e putc()
(consideradas tecnicamente macros) . Para cada uma destas funções existem duas equivalentes: fgetc() e
fputc(). Nas seções a seguir serão estudadas as funções declaradas padrão ANSI (as duas primeiras). As
outras funções têm a mesma sintaxe que suas equivalentes.
LINGUAGEM C E/S com Arquivo
87/103
15.8.1 Gravação
Para escrita de caracteres em um arquivo utilizam-se as funções putc() e fputc(), as quais são
equivalentes (putc() é uma macro). O protótipo para essa função é
Sintaxe:
int putc(int ch, FILE *fp);
onde fp é um ponteiro de arquivo devolvido por fopen() e ch é o caractere a ser escrito.
Se a operação putc() foi bem-sucedida, ela devolve o caractere escrito. Caso contrário, ela
devolve EOF.
15.8.2 Leitura
Para leitura de caracteres em um arquivo utilizam-se as funções getc() e fgetc(), as quais são
equivalentes (getc() é uma macro). O protótipo para essa função é
Sintaxe:
int getc(FILE *fp);
onde fp é um ponteiro de arquivo devolvido por fopen().
Se a operação getc() foi bem-sucedida, ela devolve o caractere lido. Caso contrário, ela devolve
EOF. O exemplo 15.3 mostra um laço que realiza uma leitura de um arquivo texto até que a marca de
final de arquivo seja lida.
Exemplo 15.3
do {
 ch = getc(fp);
} while (ch!=EOF);
15.9 Verificando fim de arquivo
Além de realizar um teste para fim de arquivo como no exemplo 15.3 pode-se utilizar uma
função como feof() que devolve verdadeiro quando for encontrado o fim de arquivo. O protótipo desta
função está declarado abaixo:
Sintaxe:
int feof(FILE *fp);
Esta função pode ser aplicada tanto para arquivo texto como para arquivos binários. A rotina do
exemplo 15.4 lê um arquivo binário até que o final do arquivo seja encontrado.
Exemplo 15.4
while (!feof(fp)) ch = getc(fp);
LINGUAGEM C E/S com Arquivo
88/103
15.10 Trabalhando com arquivos
As funções fopen(), getc(), putc() e fclose() constituem o conjunto mínimo de rotinas de
arquivos. O programa a seguir lê caracteres do teclado e os escreve em um arquivo em disco até que o
usuário digite um cifrão ($). O nome do arquivo é passado pela linha de comando.
#include “stdio.h”
#include “stdlib.h”
void main(int argc, char *argv[])
{
 FILE *fp;
 char ch;
 if(argc !=2)
 {
 printf(“Voce esqueceu de entrar o nome do arquivo \n”);
 exit(1);
 }
 if((fp=fopen(argv[1],”w”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 do {
 ch = getchar();
 putc(ch,fp);
 } while(ch!=‘$’);
 fclose(fp);
}
O programa complementar descrito a seguir lê qualquer arquivo ASCII e mostra o conteúdo na
tela.
#include “stdio.h”
#include “stdlib.h”
void main(int argc, char *argv[])
{
 FILE *fp;
 char ch;
 if(argc !=2)
 {
 printf(“Voce esqueceu de entrar o nome do arquivo \n”);
 exit(1);
 }
 if((fp=fopen(argv[1],”r”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 ch = getc(fp);
 while (ch!=EOF)
 {
 putchar(ch);
 ch = getc(fp);
 }
 fclose(fp);
}
LINGUAGEM C E/S com Arquivo
89/103
15.11 Trabalhando com Strings: fputs() e fgets()
Para a gravação e leitura de strings de caractere para e de um arquivo em disco são utilizadas as
funções fgets() e fputs(), respectivamente. São os seguintes os seus protótipos:
Sintaxe:
int fputs(const char *str, FILE *fp);
char *fgets(char *str, int length, FILE *fp);
A função fputs() opera como puts(), mas escreve a string na stream especificada. EOF é
devolvido se ocorre um erro.
A função fgets() lê uma string da stream especificada até que um caractere de nova linha seja
lido ou que length-1 caracteres tenham sido lidos. Se uma nova linha é lida, ela será parte da string
(diferente de gets()). A string resultante será terminada por um nulo. A função devolve um ponteiro para
str se bem-sucedida ou um ponteiro nulo se ocorre um erro.
O programa a seguir lê strings do teclado e escreve-as no arquivo chamado frase.dat. A
condição de saída é uma linha em branco. Como gets() não armazena o caractere de nova linha, é
adicionado um antes que a string seja escrita no arquivo para que o arquivo possa ser lido mais facilmente.
#include “stdio.h”
#include “stdlib.h”
#include “string.h”
void main(void)
{
 char str[80];
 FILE *fp;
 if((fp=fopen(“frase.dat”,”w”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 do {
 printf(“entre uma string (CR para sair): \n”);
 gets(str);
 strcat(str,”\n”);
 fputs(str,fp);
 } while (*str != ‘\n”);
 fclose(fp);
}
15.12 Funções de tratamento de arquivos
Nas próximas seções serão vistas algumas funções utilizadas em operações com arquivos com
buffer.
15.12.1 rewind()
Esta função reposiciona o indicador de posição de arquivo no início do arquivo especificado
como seu argumento. Seu protótipo é:
Sintaxe:
void rewind(FILE *fp);
LINGUAGEM C E/S com Arquivo
90/103
O exemplo 15.5 rebobina o arquivo do programa anterior e mostrar o conteúdo do mesmo.
Exemplo 15.5
#include “stdio.h”
#include “stdlib.h”
#include “string.h”
void main(void)
{
 char str[80];
 FILE *fp;
 if((fp=fopen(“frase.dat”,”w”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 do {
 printf(“entre uma string (CR para sair): \n”);
 gets(str);
 strcat(str,”\n”);
 fputs(str,fp);
 } while (*str != ‘\n”);
 rewind(fp); /* reinicializa o file pointer */
 while(!feof(fp))
 {
 fgets(str, 79, fp);
 printf(str);
 }
 fclose(fp);
}
15.12.2 ferror()
A função ferror() determina se uma operação com arquivo produziu um erro. A função ferror()
tem esse protótipo:
Sintaxe:
int ferror(FILE *fp);
Ela retorna verdadeiro se ocorreu um erro durante a última operação no arquivo; caso contrário,
retorna falso.
15.12.3 remove()
A função remove() apaga
o arquivo especificado. Seu protótipo é:
Sintaxe:
int remove(char *nome_arq)
Ela devolve zero caso seja bem-sucedido e um valor diferente de zero caso contrário. O exemplo
15.6 apresenta um trecho de programa que apaga o arquivo dados.dat.
Exemplo 15.6
LINGUAGEM C E/S com Arquivo
91/103
if (remove(“dados.dat”))
 {
 printf(“Arquivo nao pode ser apagado\n”);
 exit(1);
 }
15.12.4 fflush()
Para se esvaziar o conteúdo de uma stream de saída, deve-se utilizar a função fflush(), cujo
protótipo é mostrado a seguir:
Sintaxe:
int fflush(FILE *fp);
Essa função escreve o conteúdo de qualquer dado existente no buffer arquivo associado a fp. Se
fflush() devolve 0 para indicar sucesso; caso contrário, devolve EOF.
15.12.5 Função ftell()
Esta função retorna a posição do ponteiro de um arquivo binário em relação ao seu começo. Esta
função aceita um único argumento, que é o ponteiro para a estrutura FILE do arquivio. Seu protótipo é
mostrado aqui:
Sintaxe:
long ftell (FILE *fp);
Retorna um valor do tipo long, que representa o número de bytes do começo do arquivo até a
posição atual.
A função ftell() pode não retornar o número exato de bytes se for usada com arquivos em modo
texto, devido à combinação CR/LF que é representada por um único caractere em C.
15.13 Lendo e gravando registros
As funções fread() e fwrite() possibilitam uma maneira de transferir blocos de dados do disco
para a memória do computador e vice-versa. A grande vantagem destes comandos é poder ler e gravar
dados maiores que um byte e que formem estruturas complexas (vetor, matriz, ou um registro, ou até um
vetor de registros).
15.13.1 Escrita de um bloco de dados
Para se gravar um bloco de dados maiores que um byte o sistema de arquivo ANSI C fornece a
função fwrite(). Seu protótipo está definido a seguir:
Sintaxe:
size_t fwrite(void *buffer, size_t num_bytes, size_t count, FILE *fp);
LINGUAGEM C E/S com Arquivo
92/103
onde buffer é um ponteiro para uma região de memória que contém as informações que serão escritas no
arquivo. O número de bytes para gravar é especificado por num_bytes. O argumento count determina
quantos itens serão gravados. E, finalmente, fp é um ponteiro de arquivo para uma stream aberta
anteriormente.
Esta função devolve o número de itens escritos. O número retornado pode ser menor que count
quando o final de arquivo for atingido ou ocorrer um erro de gravação.
Exemplo 15.7
int var_int;
FILE *arq;
arq = fopen(“dados.dat”,”wb”);
var_int = 5;
fwrite(&var_int,sizeof(var_int),10,arq);
15.13.2 Leitura de um bloco de dados
Para se ler um bloco de dados maiores que um byte o sistema de arquivo ANSI C fornece a
função fread(). Seu protótipo está definido a seguir:
Sintaxe:
size_t fread(void *buffer, size_t num_bytes, size_t count, FILE *fp);
onde buffer é um ponteiro para uma região de memória que receberá os dados do arquivo. O número de
bytes para ler é especificado por num_bytes. O argumento count determina quantos itens serão lidos. E,
finalmente, fp é um ponteiro de arquivo para uma stream aberta anteriormente.
Esta função devolve o número de itens lidos. O número retornado pode ser menor que count
quando o final de arquivo for atingido ou ocorrer um erro de leitura.
Exemplo 15.8
int var_int;
FILE *arq;
arq = fopen(“dados.dat”,”rb”);
fread(&var_int,sizeof(var_int),1,arq);
15.13.3 Utilizando os comandos de leitura e gravação de registros
Uma das mais úteis aplicações de fread() e fwrite() envolve ler e escrever tipos de dados
definidos pelo usuário, especialmente estruturas.
#include “stdio.h”
struct pto {
int x,y;
};
typedef struct pto ponto;
void main()
{
 FILE *fp;
 int i;
 ponto coord[10];
 for (i=0; i < 10; i++)
LINGUAGEM C E/S com Arquivo
93/103
 {
 printf(“Coordenada x:”);
 scanf(“%d \n”,&coord[i].x);
 printf(“Coordenada y:”);
 scanf(“%d \n”,&coord[i].y);
 }
 if((fp=fopen(“figura.dat”,”w”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 for (i=0; i < 10; i++)
 fwrite(&coord[i], sizeof(ponto),1, fp);
 rewind(fp);
 i = 0;
 while(!feof(fp))
 {
 fread(&coord[i], sizeof(ponto), 1, fp);
 printf(“Coordenadas (x,y) = (%d,%d),coord[i].x,coord[i].y);
 }
 fclose(fp);
}
15.14 Acesso aleatório
Operações de leitura e escrita aleatórias podem ser executadas utilizando o sistema de E/S
bufferizado com a ajuda de fseek(), que modifica o indicador de posição de arquivo. Seu protótipo é
mostrado aqui:
Sintaxe:
int fseek (FILE *fp, long numbytes, int origem);
Onde, numbytes, um inteiro longo, é o número de bytes a partir de oriem, que se tornará a nova
posição corrente, e origem é uma das seguintes macros definidas em STDIO.H.
Tabela 15.3 – Macros definidas em STDIO.H para as posições permitidas na função fseek().
Origem Nome da Macro
Início do arquivo SEEK_SET
Posição atual SEEK_CUR
Final do arquivo SEEK_END
A função fseek() devolve 0 se a operação for bem-sucedida e um valor diferente de zero se
ocorre um erro.
O exemplo 15.9 mostra a utilização da função fseek(). Este exemplo recebe pela linha de
comando o deslocamento a ser realizado sempre a partir do início do arquivo (SEEK_SET).
Exemplo 15.9
#include “stdio.h”
#include “stdlib.h”
LINGUAGEM C E/S com Arquivo
94/103
void main(int argc, char *argv[])
{
 FILE *fp;
 char ch;
 if(argc !=3)
 {
 printf(“Uso: SEEK nomearq byte \n”);
 exit(1);
 }
 if((fp=fopen(argv[1],”r”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 if(fseek(fp, atol(argv[2]), SEEK_SET))
 {
 printf(“erro na busca\n”);
 exit(1);
 }
 printf(“O byte em %ld é %c\n”, atol(argv[2]), getc(fp));
 fclose(fp);
}
No exemplo 15.9 verifica-se a utilizção da função atol(), que tem por função converter uma
string em um inteiro longo. No printf() utiliza-se o modo de formatação %ld que é para apresentação de
um decimal longo ou um inteiro longo.
15.15 Comando de gravação em modo texto formatado
Como extensão das funções básicas de E/S já discutidas, o sistema de E/S com buffer inclui
fprintf() e fscanf(). Essas funções se comportam exatamente como printf() e scanf() exceto por
operarem em arquivos. Os protótipos de fprintf() e fscanf() são
Sintaxe:
int fprintf(FILE *fp, const char *control_string, ...);
int fscanf(FILE *fp, const char *control_string, ...);
onde fp é um ponteiro de arquivo devolvido por uma chamada à fopen(). fprintf() e fscanf() direcionam
suas operações de E/S para o arquivo apontado por fp.
A fscanf() devolve o número de elementos lidos, convertidos e armazenados. No caso de fim de
arquivo o fscanf() devolve o valor EOF.
O exemplo 15.10 mostra um programa que lê uma string e um inteiro do teclado, escreve-os em
um arquivo em disco e em seguida lê e mostra a informação na tela.
Exemplo 15.10
#include “stdio.h”
#include “stdlib.h”
#include “io.h”
void main(void)
{
 FILE *fp;
 char s[80];
 int t;
 if((fp=fopen(“teste”,”w”))==NULL)
LINGUAGEM C E/S com Arquivo
95/103
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 printf(“entre com uma string e um número: “ );
 fscanf(stdin, “%s%d”, s, &t); /* le do teclado */
 fprintf(fp, “%s %d”, s, t); /* escreve no arquivo */
 fclose(fp);
 if((fp=fopen(“teste”,”w”))==NULL)
 {
 printf(“Arquivo nao pode ser aberto\n”);
 exit(1);
 }
 fscanf(fp, “%s%d”, s, &t); /* le do arquivo */
 fprintf(stdout, “%s %d”, s, t); /* imprime na tela */
}
A formatação realizada para leitura e escrita de dados pode comprometer a velocidade de
processamento do programa.
15.16 Condições de erro
Para determinar se um erro ocorreu utiliza-se a função ferror(), mas esta informação não basta
para a solução por parte do usuário. Para isso
utiliza-se a função perror() em conjunto com a função
ferror(). O argumento de perror() é uma string fornecida pelo programa que normalmente é uma
mensagem de erro que indica em que parte do programa ocorreu erro.
Sintaxe:
void perror (const char *str);
Se for detectado um erro de disco, ferror() retornará um valor verdadeiro (não zero) e perror()
imprimirá a seguinte mensagem
Erro de Busca: Bad data
A primeira parte da mensagem é fornecida pelo programa, e a segunda parte, pelo sistema
operacional.
15.17 Streams Padrão
Sempre que um programa é iniciado três streams padrões são abertas automaticamente:
• stdin (entrada padrão - teclado);
• stdout (saída padrão - tela);
• stderr (saída de erro padrão - tela);
• stdaux (saída auxiliar padrão - porta serial);
• stdprn (impressora padrão - impressora paralela).
Essas streams podem ser utilizadas normalmente para executar operações de E/S bufferizada,
como mostra o exemplo 15.10 na leitura de variáveis pelo teclado.
Para redirecionar as streams padrão utiliza-se a função freopen().
Sintaxe:
LINGUAGEM C E/S com Arquivo
96/103
FILE *freopen(const char *nomearq, const char *modo, FILE *stream);
onde nomearq é um ponteiro para o nome do arquivo que se deseja associar à stream apontada por
stream. O programa do exemplo 15.2 redireciona a stdout para o arquivo chamado OUTPUT.
Exemplo 15.11
#include “stdio.h”
void main(void)
{
 char str[80];
 freopen(“OUTPUT”, “w”, stdout);
 printf(“entre com uma string: ”);
 gets(str);
 printf(str);
}
15.18 Exercício:
1. Faça um programa que escreva os números de 0 a 10 em um arquivo.
2. Faça um programa que leia 11 números de um arquivo.
3. Faça um programa que verifique se o número de abre-chaves corresponde ao número de fecha-
chaves de um arquivo de programa fonte C, o qual é lido pelo teclado.
4. Faça um programa que gere um novo arquivo com registros ordenados (crescente) pelo código de
um arquivo, cujo nome é fornecido pelo usuário. A estrutura do arquivo é:
 struct reg {
 int cod;
 char nome[30];
 char idade;
 };
15.19 Sistema de Arquivo tipo UNIX
Como C foi originalmente desenvolvida sobre o sistema operacional UNIX, ela inclui um
segundo sistema de E/S com arquivos em disco que reflete as operações em disco de baixo nível do
UNIX.
A diferença está no fato que tanto a leitura como a gravação são realizadas sem buffer. Isto é, o
programador fica com o trabalho de criar e manter o buffer de dados.
Para as operações em disco utilizam-se algumas funções especiais, as quais estão descritas na
tabela 15.3
Tabela 15.3 - Funções de E/S tipo UNIX sem buffer
Nome Função
open() Abre um arquivo em disco
close() Fecha um arquivo em disco
read() Lê um buffer de dados
write() Escreve um buffer de dados
lseek() Move ao byte especificado em um arquivo
tell() Devolve o valor atual do indicador de posição de arquivo
unlink() Apaga um arquivo do diretório
LINGUAGEM C E/S com Arquivo
97/103
O arquivo-cabeçalho usado pelo sistema de arquivo tipo UNIX é chamado IO.H em muitas
implementações. Em algumas funções é necessário a inclusão do cabeçalho FNCTL.H (definição de
algumas macros).
15.19.1 open()
Ao contrário do sistema de arquivos com buffer, o sistema de baixo nível (sem buffer) não utiliza
ponteiros de arquivo do tipo FILE, mas descritores de arquivo do tipo int. O protótipo para open() é:
Sintaxe:
int open(const char *nomearq, int modo);
onde nomearq é qualquer nome de arquivo válido e modo é uma das seguintes macros que são definidas
no arquivo-cabeçalho FNCTL.H.
Tabela 15.4 Modos de abertura de arquivos em baixo nível
Modo Efeito
O_RDONLY Abre um arquivo somente para leitura
O_WRONLY Abre um arquivo somente para escrita
O_RDWR Abre um arquivo para leitura/gravação
O_APPEND coloca o ponteiro de arquivo no fim dele
O_CREAT cria um novo arquivo para gravação
(não faz efeito se o arquivo já existe)
O_TRUNC Abre e trunca umarquivo existente para
tamanho 0
O_BINARY Abre um arquivo em modo binário
O_TEXT Abre um arquivo em modo texto
Os modos de abertura de arquivo em baixo nível são mostrados na tabela 16.5. A existência
destas macros dependem do compilador, isto é, nem todas podem existir. Algumas das macros listadas são
mutuamente exclusivas, por exemplo, não pode abrir o arquivo para somente leitura e somente gravação
ao mesmo tempo.
Quando dois ou mais macros são utilizadas ao mesmo tempo, devem ser combinadas usando-se o
operador bit-a-bit OR (|).
Exemplo 15.12
#include <io.h>
#include <fnctl.h>
main()
{
 int i; /* identificador do arquivo */
 char buff[512]; /* definição do buffer */
 if((i=open(“teste.txt”,O_RDONLY | O_BINARY)) < 0)
 perror(“Não posso abrir arquivo:”);
LINGUAGEM C E/S com Arquivo
98/103
...
}
No exemplo 16.12, é mostrado a abertura de um arquivo somente para leitura (O_RDONLY) e
binário (O_BINARY). O comando open() devolve um inteiro para i como descritor do arquivo para
futuras operações no mesmo.
15.19.2 creat()
Se o compilador não permitir criar um arquivo novo com a função open() ou se quer garantir a
portabilidade, deve-se utilizar a função creat() para criar um arquivo novo para operações de escrita. A
sintaxe para creat() é:
Sintaxe:
int creat(const char *nomearq, int modo);
Os argumentos desta função seguem a mesma sintaxe da função open().
15.19.3 close()
A função close() libera o identificador ou descritor do arquivo para que ele possa ser reutilizado
por outro arquivo.
Sintaxe:
int close(int descritor_de_arquivo);
O descritor_de_arquivos deve ser um descritor de arquivos válido, previamente obtido através
de uma chamada open() ou creat(). A função close() devolve -1 se for incapaz de fechar o arquivo, e 0
caso contrário.
15.19.4 read() e write()
Uma vez o arquivo aberto pela função open(), para escrever alguma informação utiliza-se a
função write(). O protótipo para a função write() é
Sintaxe:
int write (int fd, const void *buf, unsigned size);
Ao chamar a função write() são escritos size caracteres no arquivo em disco especificado por fd
do buffer apontado por buf. O buffer pode ser uma região alocada na memória ou uma variável. A função
write() devolve o número de bytes escritos no caso de uma operação bem-sucedida. No caso de erro a
função devolve EOF.
Para a leitura de dados tem-se a função read(). Seu protótipo é:
Sintaxe:
LINGUAGEM C E/S com Arquivo
99/103
int read (int fd, const void *buf, unsigned size);
onde fd, buf e size são os mesmos parâmetrosde write(), exceto pro read() colocar os dados do arquivo
em buf. No caso da operação ser bem-sucedido é devolvido o número de bytes lidos. Caso contrário, ela
devolve 0 se o final do arquivo for ultrapassado e -1 se ocorrerem erros.
O exemplo 15.13 mostra um programa que utiliza alguns aspectos da E/S sem buffer. Este
programa lê linhas de texto, escreve-as em um arquivo em disco e as lê de volta.
Exemplo 15.13
#include “stdio.h”
#include “io.h”
#include “stlib.h”
#include “string.h”
#include “fnctl.h”
#define BUF_SIZE 128
void input(char *buf, int fd1);
void display(char *buf, int fd2);
void main(void)
{
 char buf[BUF_SIZE];
 int fd1,fd2;
 if((fd1=open(“test”,O_WRONLY))==-1)
 {
 printf(“Arquivo não pode ser aberto\n”);
 exit(1);
 }
 input(buf,fd1);
 close(fd1);
 if((fd1=open(“test”,O_RDONLY))==-1)
 {
 printf(“Arquivo não pode ser aberto\n”);
 exit(1);
 }
 display(buf,fd2);
 close(fd2);
}
/* Insere texto */
void input(char *buf, int fd1)
{
 register int t;
 do {
 for(t=0; t<BUF_SIZE; t++)
 buf[t] = ‘\0’;
 gets(buf);
 if (write(fd1, buf, BUF_SIZE) != BUF_SIZE)
 {
 printf(“Erro de escrita \n”);
 exit(1);
 }
 } while (strcmp(buf, “sair”);
}
/* Mostra o arquivo */
void display(char *buf,
int fd2);
{
 for(;;)
 {
 if (read(fd2, buf, BUF_SIZE) == 0)
 return;
LINGUAGEM C E/S com Arquivo
100/103
 printf(“%s\n”, buf);
 }
}
15.19.5 unlink()
Esta função é utilizada para excluir um arquivo. Seu protótipo é
Sintaxe:
int unlink(const char *nomearq);
onde nomearq é um ponteiro de caracteres para algum nome válido de arquivo. A função unlink()
devolve -1 caso seja incapaz de excluir o arquivo.
15.19.6 lseek() e tell()
Para o acesso aleatório as funções lseek() e tell() são as equivalentes em baixo-nível das funções
fseek() e ftell() discutidas em alto-nível. Os protótipos das duas funções são
Sintaxe:
long lseek(int fd, long offset, int origem);
long tell(int fd);
onde os parâmetros são idênticos às esquivalentes em alto-nível, exceto o primeiro argumento que é um
identificador de arquivo e não um ponteiro FILE.
15.19.7 Funções para manipulação de buffers
Para trabalhar com buffers utilizam-se algumas funções especializadas que são independentes de
tipo.
A função memchr() procura, no vetor apontado por buffer, pela primeira ocorrência de ch nos
primeiros count caracteres. Devolve um ponteiro para a primeira ocorrência de ch em buffer ou um
ponteiro nulo se ch não for encontrado. O protótipo da função é
Sintaxe:
void *memchr(const void*buffer, int ch, size_t count);
Exemplo 15.14
#include “stdio.h”
#include “string.h”
void main(void)
{
 char *p;
 p = memchr(“isto e um teste”. ´ ´,14);
 printf(p);
}
LINGUAGEM C E/S com Arquivo
101/103
A função memcmp() compara os primeiros count coracteres das matrizes apontadas por buf1 e
buf2. O valor devolvido segue os valores da função strcmp(). O protótipo da função é
Sintaxe:
int memcmp(const void*buf1, const void*buf2, size_t count);
A função memcpy() copia os primeiros count coracteres do vetor origem para o vetor apontado
por destino. Ela devolve um ponteiro para destino. O protótipo da função é
Sintaxe:
void *memcpy(void*destino, const void*origem, size_t count);
Exemplo 15.15
#include “stdio.h”
#include “string.h”
#define SIZE 80
void main(void)
{
 char buf1[SIZE], buf2[SIZE];
 strcpy(buf1, “Quando, no curso do ...”);
 memcpy(buf2, buf1, SIZE);
 printf(buf2);
}
A função memmove() copia count caracteres do vetor apontado por origem para o vetor
apontado por destino. Se as matrizes se sobrepõem, a cópia ocorrerá corretamente, colocando o conteúdo
correto em destino, porém origem será modificado. Ela devolve um ponteiro para destino. O protótipo da
função é
Sintaxe:
void *memmove(void*destino, const void*origem, size_t count);
Exemplo 15.16
#include “stdio.h”
#include “string.h”
#define SIZE 80
void main(void)
{
 char buf1[SIZE], buf2[SIZE];
 strcpy(buf1, “Quando, no curso do ...”);
 memmove(buf2, buf1, SIZE);
 printf(buf2);
}
A função memset() copia o byte menos significativo de ch nos primeiros count caracteres do
vetor apontado por buf. Ela devolve buf. é muito utilizado na inicialização de uma região de memória com
algum valor conhecido. O protótipo da função é
Sintaxe:
void *memset(void*buf, int ch, size_t count);
Exemplo 15.17
/* Inicaliza com nulo os 100 primeiros bytes */
memset(buf, ’\0’, 100); /*do vetor apontado por buf */
LINGUAGEM C E/S com Arquivo
102/103
/* Inicializa com X os 10 primeiros bytes */
memset(buf, ’X’, 10);
printf(buf);
LINGUAGEM C Bibliografia
103/103
16. Bibliografia
BERRY, J. Programando em C++. São Paulo: Makron Books, 1991.
ECKEL, B. C++. São Paulo: McGraw-Hill, 1991.
ELLIS, M. A. et alli C++: Manual de referência completo. Rio de Janeiro: Campus, 1993.
IBPI. Dominando a Linguagem C. Rio de Janeiro: IBPI, 1993.
MIZRAHI, V. V. Treinamento em Linguagem C. São Paulo: McGraw-Hill, 1990.
PAPPAS, C. H.; MURRAY, W. Turbo C++ completo e total. São Paulo: McGraw-Hill, 1991.
SCHILDT, H. C Completo e total. São Paulo: McGraw-Hill, 1991.

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Mais conteúdos dessa disciplina

Mais conteúdos dessa disciplina