Universo Online
Web Sites Pessoais

Básico da Linguagem C

1. Introdução 10. Funções 19. Blocos de Comandos
2. História 11. Operadores 20. Decisões
3. Estrutura de Programa 12. Expressões 21. Loops
4. Include 13. Entrada de Dados 22. Funções Matemáticas
5. Define 14. Saída de Dados 23. Tratamento de Telas
6. Tipos de Dados 15. Pointers 24. Definições de Tipos
7. Identificadores 16. Aritmética de Pointers 25. Tipos Enumerados
8. Declaração de Variáveis 17. Arrays 26. Estruturas
9. Protótipos 18. Strings 27. Arquivos
TÓPICOS
1. Introdução Este texto tem a finalidade de proporcionar uma orientação básica sobre a linguagem de programação C aos alunos já familiarizados com PASCAL. Em alguns momentos serão usadas comparações entre C e PASCAL.

Serão abordados tópicos fundamentais para a elaboração de programas em C, desde programas simples até rotinas mais sofisticadas usando estruturas de dados. Veremos também algumas técnicas para tratamento de arquivos em disco nos sistemas de micros da linha PC, disponíveis nos laboratórios.
 

Nesta linha de raciocínio, serão abordados tópicos exclusivos do compilador TURBO C da Borland, uma vez que este será o compilador usado nos testes de laboratório, por sua flexibilidade como ferramenta para desenvolvimento de programas.

Topo da página
2. História A linguagem de programação C foi projetada para permitir grande economia de expressão nos programas, isto é, produzir programas fonte mais compactos. Foi primeiramente usada para escrever cerca de 90% do código do sistema operacional UNIX, e com a popularização deste sistema operacional em equipamentos de médio porte, e até micros, a linguagem C também ganhou popularidade entre os programadores profissionais.

Em 1969, os laboratórios Bell procuravam uma alternativa para o sistema operacional Multics para o computador PDP-7. Uma versão básica do sistema operacional UNIX foi então escrita em Assembly. Neste mesmo período, uma linguagem experimental estava sendo desenvolvida por Keneth Thompson. Esta linguagem se chamava B, e a partir dela foi projetada a linguagem C, em 1972.
 

Logo depois, em 1973, o sistema operacional UNIX foi melhorado e cerca de 90% de seu código foi escrito em C. Por causa desta libertação do Assembly, UNIX ( e conseqüentemente C) adquiriu grande portabilidade, foi rapidamente adaptado a uma série de computadores e seu uso não parou de crescer. No final da década de 70 e inicio da década de 80, a proliferação de UNIX e C foi muito grande e chegou até os micros. Além do que, C ficou independente do sistema operacional UNIX e uma série de compiladores C surgiram para muitos equipamentos.
 

Hoje, C é a linguagem preferida dos programadores profissionais por várias razões, e chega a substituir Assembly em boa parte do software recentemente desenvolvido.
 

Basta dar uma olhada nas revistas e livros que trazem algoritmos e dicas para programadores, vê-se que as implementações que, há alguns anos, vinham em PASCAL ou Assembly, estão hoje quase totalmente escritas em C.
 

Com o advento da Programação Orientada para Objetos (OOP - Object Oriented Programming), a linguagem que se tornou mais usada para esta técnica de programação é uma extensão da linguagem C, chamada C++.
 

 Topo da página
   3. Estrutura de programa Um programa C é na verdade uma coleção de funções, criadas pelo programador ou funções de biblioteca.

A linguagem C é basicamente muito simples, mas a maioria dos compiladores vêm com uma grande quantidade de funções já criadas e compiladas em bibliotecas que são juntadas às definidas pelo programador durante o processo de compilação.
 

A estrutura básica de um programa C é a seguinte:
 
comentários
diretivas de compilação
definições globais
protótipos de funções
definição das funções

Comentários podem, e devem, estar em qualquer ponto do programa, mas é aconselhável colocar logo no inicio do programa algumas informações como: nome do programa, programador, período de elaboração, para que serve, se é parte de algum sistema maior, restrições de acesso por motivo de segurança, etc. Os comentários são escritos entre os delimitadores /* e */, e desta forma não são considerados pelo compilador.
 

Diretivas de compilação não são instruções da linguagem C, são mensagens ao compilador para que execute alguma tarefa no momento da compilação. Deve haver uma linha inteira para cada diretiva , que são iniciadas pelo caracter #. As diretivas mais comuns são #include e #define, respectivamente usadas para, especificar bibliotecas de funções a serem incorporadas na compilação, e macro substituições, como veremos mais adiante.
 

Definições globais normalmente são especificações de constantes, tipos e variáveis que serão válidas em todas as funções que formam o programa.
 

Embora sejam de relativa utilidade, não é boa prática de programação definir muitas variáveis globais, pois um descuido em algum ponto do programa, alterando seus valores, pode provocar problemas em muitos outros pontos, uma vez que tais variáveis são visíveis em todas as partes do programa.
 

Protótipos de funções são semelhantes aos cabeçalhos das funções e não são obrigatórios. Os protótipos são usados pelo compilador para fazer verificações durante a compilação, para ver se as partes do programa que acionam tais funções o fazem de modo correto, com nome certo, número e tipo de parâmetros corretos, etc. Esta é a melhor maneira de programar em C.
 

Como foi dito, um programa C é basicamente uma coleção de funções que fazem o papel dos subprogramas em outras linguagens, como por exemplo PASCAL. Em C temos apenas funções e não as procedures do PASCAL, mas temos grande liberdade para trabalhar com estas funções, podendo inclusive desprezar seu valor de retorno, acionando-as como as procedures em PASCAL.
 

Uma das funções deve ter o nome "main", e esta será a função por onde começa a execução do programa. Equivale à rotina principal dos programas PASCAL.
 

Não há ordem obrigatória para codificar as funções. No entanto procuraremos sempre começar pela função main, para facilitar a tarefa de manutenção do programa. Todas as outras funções serão codificadas em seguida, com a liberdade de que uma função pode acionar uma outra mesmo que esta não tenha sido ainda definida, liberdade esta, proprocionada pelos protótipos.
 

A seguir vem um exemplo de programa C, apenas como ilustração.
 

/* Exemplo.C

Programa para calcular e exibir a soma de dois inteiros digitados.

Por sua simplicidade, este programas não tem

variáveis globais. */
 
 

#include <stdio.h> /* diretiva */
 

int sum(int a, int b); /* protótipo */
 

void main() /* função principal*/

{

int a, b, c;
 

printf("Digite dois numeros inteiros: ");

scanf("%d %d", &a, &b);

c = sum(a,b);

printf("Resultado da soma: %d \n", c);

}
 

int sum(int a, int b) /* função soma */

{

return a+b ;

}
 

Finalmente, como se vê no exemplo acima, não é obrigatório, mas aconselhável, colocarmos uma instrução em cada linha.
 

 Topo da página

  4. Include Esta diretiva de compilação serve para especificar arquivos com definições de tipo, macro substituições e protótipos de funções que serão usadas no programa. Como C tem uma grande variedade destas funções e definições, é comum que elas estejam agrupadas em arquivos diferentes de acordo com a natureza das tarefas que executam.

A forma geral para esta diretiva é a seguinte:
 

#include <nome do arquivo>

ou
 

#include "nome do arquivo"
 

A diferença entre estas duas formas está no local onde o compilador vai procurar o arquivo no momento da compilação. No primeiro formato, o arquivo é procurado no diretório já definido pelo TURBO C como sendo aquele que contem os "header files", isto é, os arquivos com extensão .h que contém definições de tipos, dados e funções já prontas que vem com o TURBO C. O segundo formato é usado quando queremos que o TURBO busque o arquivo especificado no diretório atual do disco. Esta forma é usada normalmente quando queremos incorporar arquivos por nós criados e salvos no diretório atual.
 

Aqui estão alguns arquivos já prontos e usados com maior frequência:
 
 
stdio.h rotinas padrão de entrada e saída definidas pelos criadores da linguagem C.
alloc.h funções para gerenciamento de memória
float.h funções para tratar números de ponto flutuante
math.h funções matémáticas
stddef.h vários tipos de dados e macro substituições
stdlib.h várias rotinas muito usadas, conversão, sort, etc..
string.h rotinas p/ manipular strings e memória

 

O leitor deve procurar o manual do compilador para maiores informações sobre estes arquivos a partir do momento que se fizerem necessários às suas aspirações.
 

 Topo da página
   5. Define Esta outra diretiva serve para macro substituições, isto é, para especificar que certas palavras escritas no programa fonte devem ser substituídas por determinado valor ou trecho de código antes da compilação propriamente dita.

No momento, veremos como substituir palavras por valores. A sintaxe seria algo assim:
 

#define palavra valor
 

Exemplo:
 

#define imposto 0.25

#define pi 3.1415

#define e 2.718281828
 

 Topo da página
   6. Tipos de dados A linguagem C trabalha basicamente com números que estão classificados em tipos. Estes tipos têm seus respectivos nomes.
 

Basicamente, temos os seguintes tipos:
 
 
int inteiros entre -32768 e 32767
char inteiros entre -128 e 127
float reais entre 3.4e-38 e 3.4e+38 (positivos e negativos)
double reais entre 1.7e-3.8 e 1.7e+3.8 (positivos e negativos)

 

Há ainda o modificador long, que se aplica ao tipo int, e o modificador unsigned, que se aplica aos tipos int e char , além de poder ser usado com long. Estes modificadores indicam outra forma de representação interna e assim produzem novas faixas de valores representáveis. Em C, unsigned int pode ser abreviado para apenas unsigned, e long int para apenas long.
 
 
unsigned int (ou unsigned)  0..65535
unsigned char  0..255
long int (ou long)  2147483648..2147483647
unsigned long  0..4294967295
Estes limites são dados com base em manuais do compilador TURBO C versão 1.5, mas podem ser diferentes em outros compiladores e versões.
 

 Topo da página
   7. Identificadores Identificadores são nomes criados pelo programador para constantes, variáveis, tipos, estruturas, funções, tudo o que necessitar de um nome e não for padrão.
 

Para criar tais "nomes" em C, há algumas regras:

· usar somente letras, dígitos e sublinha,

· começar sempre por uma letra ou sublinha,

· somente um certo numero de caracteres é considerado, dependendo do compilador (normalmente 32),

· letras maiúsculas são DIFERENTES de minúsculas

 
Topo da página
 8. Declaração de variáveis
Como sabemos, variáveis são entes formados por um nome associado a um local na memória que pode armazenar valores. Tais valores, o conteúdo das variáveis, podem ser alterados durante a execução do programa e daí vem o nome "variável".

Para declarar variáveis nos programas devemos especificar seus tipos e nomes da seguinte forma:
 

tipo nome, nome, ...;
 

Exemplo:
 

int a, b, c;

float x, y;

char ch;
 

As variáveis podem ser basicamente, globais ou locais. As globais são aquelas definidas fora de qualquer função do programa e são válidas (visíveis) por qualquer parte do programa a partir do ponto em que está sua definição. As locais são definidas dentro de alguma função e são válidas somente nesta função.
 

No momento da declaração, as variáveis podem ser inicializadas com algum valor. Para isto basta colocar à frente do nome da variável, o símbolo = e o valor desejado.
 

Exemplo:
 

int a = 0, b = 1;
 

 Topo da página
   9. Protótipos A maneira moderna de programar em C usa protótipos de funções para que o compilador faça verificações nos pontos em que as funções são acionadas e nas próprias definições das funções.

O protótipo de uma função tem o mesmo formato que o cabeçalho da função.
 

tipo nome da função ( parâmetros ) ;
 

O ponto e vírgula no final é obrigatório, o tipo é referente ao valor que a função retorna, o nome da função é um identificador definido pelo programador, e os parâmetros são especificados um a um com tipo e nome, ou somente tipo.
 

Exemplo:
 

int sum(int a, int b);

float raiz(float);

void tela(void);
 

O um tipo chamado "void" serve para indicar que a função não retorna nenhum valor. Este tipo é indicado nos casos em que a função será acionada como se fosse uma procedure do PASCAL, e não dentro de uma expressão. Protótipos em que especificamos o tipo void entre parênteses indicam que a função não tem parâmetros.
 

 Topo da página
   10. Funções As funções são os módulos dos programas C, fazem o papel das procedures e functions em PASCAL ou as subroutines e functions do FORTRAN.

Sua forma geral é a seguinte:
 

tipo nome da função( parâmetros )

{

corpo da função

}
 

O cabeçalho tem a forma dos protótipos, e o corpo da função vem escrito entre chaves. Estas chaves fazem o papel de BEGIN e END em PASCAL.
 

O cabeçalho tem os parâmetros definidos completamente, isto é, com tipo e nome de cada um, ou com os parênteses vazios, no caso de não haver parâmetros.
 

No corpo da função são feitas as definições locais e, usando os comandos da linguagem C , é implementado o algoritmo da tarefa que a função executa.
 

Uma coisa muito importante a entender é que C nos dá uma grande liberdade para trabalhar com funções, ao ponto de podermos simplesmente desprezar seu valor de retorno acionando-as como se fossem procedures em PASCAL. No entanto, é preciso estar atento a certas particularidades da linguagem C, principalmente no que se refere aos parâmetros.
 

Em PASCAL, é possível passar parâmetros de dois tipos às subrotinas, tipo valor ou tipo variável. Usamos o tipo variável (escrevendo a palavra VAR antes do nome do parâmetro) quando desejamos receber de volta um novo valor para este parâmetro.
 

Em C, só podemos passar parâmetros tipo valor, e aparece um problema. O que fazer quando desejamos receber de volta novos valores para os parâmetros que passamos à subrotina ?
 

A resposta é simples. Neste caso, basta passar como parâmetro o endereço da variável que desejamos alterar. Deste modo podemos usar tal endereço na subrotina e alterar o conteúdo daquela posição de memória, e assim, ao voltarmos à rotina que acionou a subrotina, teremos na memória um novo conteúdo para aquele dado.
 

Finalmente, para especificar o valor de retorno de uma function em PASCAL, atribuímos tal valor ao nome da função. Em C, as coisas são diferentes. Para especificar o valor de retorno de uma função usa-se o comando return seguido do valor de retorno. Tal valor pode ser uma constante, conteúdo de uma variável ou resultado de uma expressão.
 

 Topo da página
   11. Operadores C tem uma quantidade grande de operadores. Muitos elementos que em outras linguagens estão presentes, mas não nos fazem nem mesmo lembrar de operações, em C, são considerados operadores. Veremos alguns operadores da linguagem C divididos em alguns grupos:
 

Em C, a atribuição de valores à variáveis é considerada uma operação.
 
 
Operador Operação Comentários
= Atribuição Usado para atribuir valores à variáveis. 

Ex: 

a = b + c * 1.2;

C permite agrupar várias operações deste tipo. 

Ex: 

x = y = z = 0;


Como em outras linguagens, há vários operadores aritméticos.
 
 
Operador Operação Comentários
+ Adição  
- Subtração  
* Multiplicação  
/ Divisão  
% Módulo (Resto) Ex:

11 % 4 resulta 2.

++ Incremento Usado para adicionar 1 (um) a algum valor. 

Ex: 

a++ ou ++a 

Estas duas formas diferem quando o operador aparece numa expressão, ++a soma 1 ao valor de a e depois o novo valor de a será usado na expressão, a++ fará com que o valor atual de a seja usado na expressão e depois some-se 1 ao valor de a. 

Ex:

c = ++a + b ou c = b + a++

-- Decremento Análogo ao incremeento.
<<  Shift para esquerda Este operador desloca os bits um dado número de posições para esquerda, e equivale a multiplicar um valor por uma potência de base 2.

Ex: 

x = y << 2;

>> Shift para direita Análogo ao anterior, mas equivale a dividir por uma potência de base 2.
& AND  Operação AND com os bits de dois valores.

Ex: 

a = b & c

| OR Operação OR com os bits.
XOR Operação XOR (OU exclusivo) com bits.
~ NOT Negação com os bits. 

Ex:

b = ~a


Com algumas das operações acima, é possível fazer abreviações usando, na verdade, alguns operadores da linguagem C. Qualquer expressão da forma:
 

variável = variável operador expressão
 

pode ser substituída por
 

variável operador= expressão
 

Exemplo:
 

a = a + 5 a += 5

b = b - 3 b -= 3

c = c * 2 c *= 2

a = a / 3 a /= 3

x = x % y x %= y
 

analogamente para <<, >>, &, |, e ^.
 

Veremos agora os operadores relacionais, usados para comparar dois valores.
 
 
Operador Descrição
> maior que
>= maior ou igual a
< menor que
<= menor ou igual a
== igual a
!= diferente de

O uso destes operadores é semelhante a seus correspondentes em PASCAL, porém há uma diferença fundamental no que se refere ao resultado das comparações.
 

Como sabemos, o resultado de uma comparação deve ser "verdadeiro" ou "falso". Em PASCAL temos as constantes false e true que definem o tipo de dado chamado BOOLEAN, mas em C isto não existe. C trabalha basicamente com números e não há constantes lógicas. O que ocorre é o seguinte: se o resultado de uma comparação for falso, o resultado da operação será 0 (zero); e se a comparação for verdadeira, o resultado da operação será 1 (um).
 

Uma convenção semelhante se aplica aos chamados operadores lógicos em C, vistos aqui:
 
 
Operador Operação
&& AND
|| OR
! NOT

Devemos tomar cuidado para não confundir estes operadores com aqueles que operam sobre os bits ( &, !, ~ ). Os operadores lógicos produzem como resultado valores lógicos, isto é, zero ou um, enquanto que os outros operadores agem diretamente sobre os bits de valores inteiros.
 

Outro ponto importante e diferente do PASCAL é que os operadores && e || são otimizados, isto é, se tivermos em uma expressão algo como:
 

exp1 && exp2
 

exp1 é analisada primeiro e se for falsa, ex2 nem será considerada.
 

 Topo da página
   12. Expressões Um dos pontos mais característicos da linguagem C é a grande liberdade que ela dá aos programadores no momento de escrever as expressões. Isto é responsável pela compactação do código escrito em C, mas também faz com que as expressões escritas por programadores mais experientes sejam realmente complicadas.
· Atribuição Qualquer atribuição escrita entre parênteses é uma expressão que tem o mesmo valor que está sendo atribuído.
 

Por exemplo, a expressão (sum = 5+3) tem o valor 8, e a expressão relacional ((sum = 5+3) <= 10) será verdadeira, resultando 1.
 

Veja isto,

if ((ch=getch()) == 'q')

puts("Desistindo, hein ?\n") ;

else

puts("Continuou, hein ?\n");
 

Neste exemplo, a função getch() tem o mesmo funcionamento de READKEY em TURBO PASCAL.
 

· Vírgula Você pode usar a vírgula para separar várias expressões dentro de um conjunto de parênteses. As expressões são avaliadas da esquerda para a direita, e toda a expressão assume o valor da ultima expressão avaliada.
 

Por exemplo, (oldch = ch, ch = getch())
 

· Aritmética Um fator importante a ser analisado, é o que ocorre em relação aos tipos dos operandos e do resultado quando uma operação é executada.
 

Aqui estão as etapas que o TURBO C segue no momento de fazer uma operação aritmética:
 

1. Qualquer valor não inteiro ou não double é convertido de acordo com a tabela a seguir: char int

unsigned char int

float double
 

Assim, quaisquer dois valores associados a um operador serão ou int (incluindo long e unsigned) ou double.
 

2. Se um dos operandos é double, o outro é convertido para double.
 

3. Se não, se um dos operandos é do tipo unsigned long, o outro é convertido para unsigned long.
 

4. Se não, se um dos operandos é do tipo long, o outro é convertido para long.
   

5. Se não, se um dos operandos é do tipo unsigned, o outro é convertido para unsigned.
 

6. Se não, ambos os operandos são do tipo int.
 

7. A operação é executada e o resultado tem o mesmo tipo dos operandos.
 

· Conversão de tipos
 

TURBO C nos permite converter um valor de um certo tipo para um valor correspondente de outro tipo. Isto se chama typecast.
 

Para converter um valor de um tipo para outro basta escrever entre parênteses o nome do novo tipo antes do valor original.
 

Exemplo: f = (float) 4 / 3
 

No entanto, C já faz automaticamente uma série de conversões para facilitar a vida dos programadores. Por exemplo, em atribuições ou em passagem de parâmetros para funções, os tipos numéricos são automaticamente convertidos.
 

 Topo da página
   13. Entrada de dados C tem uma série de funções para entrada de dados, dados vindos de arquivos, ou do teclado.

Para entrada de dados vindos do teclado veremos as funções usadas com mais frequência: scanf, gets, getch e getchar.

· scanf A sintaxe para chamada desta função é a seguinte:

scanf ( string de formato , endereço, endereço, ... );
 

Uma string é escrita em C entre aspas, e não entre apóstrofos como em PASCAL. A string de formato é uma série de especificadores de formato para os dados que serão digitados. Cada especificador é formado pelo caracter % seguido de uma letra que indica a natureza (tipo) do dado.
 

%u unsigned int

%d int

%c char

%f double or float em notação não científica

%e double ou float em notação científica

%s string
 

Uma coisa importante a lembrar na função scanf é que são especificados os endereços de memória das variáveis que receberão os valores digitados, e não os nomes destas variáveis como no READLN do PASCAL. Isto poderia ser um problema, mas felizmente C possui um operador unário que devolve o endereço da variável a que ele se aplica. Trata-se do operador &.
 

Exemplo: &a (devolve o endereço da variável a)
 

Em uma mesma chamada de scanf podemos especificar vários dados a serem digitados, mas para separá-los na digitação devemos usar o mesmo símbolo usado na string de formato para separar os especificadores.
 

Por exemplo, para a chamada
 

scanf("%d,%f",&x, &y);
 

poderíamos digitar a seguinte linha
 

12,3.14
 

o valor 12 ficaria na variável x e o valor 3.14 ficaria na variável y.
 

Uma limitação importante que se deve destacar, é que para ler strings, scanf lê uma seqüência de caracteres até encontrar um espaço em branco ou ENTER, o que é inviável quando desejamos digitar, por exemplo, o nome completo de uma pessoa.
 

· gets Esta função resolve o problema citado acima, pois lê uma seqüência de caracteres até que um ENTER seja encontrado. O ENTER não é armazenado , mas um caracter null (código 0 da tabela ASCII) é armazenado no final da string.
 

A sintaxe para chamar esta função é:
 

gets( variável );
 

Exemplo:
 

gets(profissao);
 

Note que aqui é especificado o nome da variável e não seu endereço. Note também que a chamada se faz como que uma procedure em PASCAL, uma vez que o valor de retorno que interessa vem no parâmetro.
 

Quanto ao parâmetro, veremos mais adiante como definir variáveis para armazenar strings, uma vez que isto é bem diferente em C do que e em PASCAL.
 

Um problema ocorre quando usamos um gets após um scanf. O ENTER usado para finalizar a entrada de dados via scanf permanece no buffer de teclado e será captado pelo gets. Desta forma o programa "passa direto" pelo gets, sem parar para digitação. A solução para o problema é usar uma função de biblioteca que descarrega o buffer de teclado. Pode-se usar a função flushall.
 

Exemplo:
 

....

scanf("%f", &salario);

....

flushall();

gets(profissao);

....
 

· getch Esta é uma função que não tem parâmetros e cujo valor de retorno é um único caracter. Este caracter não é exibido na tela, e a chamada de tal função se faz através de uma expressão em que seu valor de retorno ( o tal caracter) esteja envolvido.
 

Exemplo:
 

char ch;

ch = getch();
 

Caso seja necessário o aparecimento do caracter digitado na tela, pode-se usar a função getchar, equivalente a getch. No entanto, enquanto getch não mostra o caracter digitado na tela e não espera por um ENTER para confirmar a digitação, getchar, além de exibir o caracter digitado, espera por um ENTER.
 

 Topo da página
   14. Saída de dados Veremos agora como exibir dados no monitor de vídeo, visto que este é sem duvida o dispositivo padrão em ambientes de microcomputadores.

Ha três funções mais usadas para exibir dados na tela: printf, puts e putchar.

· printf Esta é a mais genérica de todas e tem a seguinte sintaxe para sua chamada:

printf( string de formato, item, item, ...);
 

A string de formato é uma seqüência de caracteres que também são exibidos na tela, e deve conter especificadores de formato para cada item especificado, se existirem.
 

Os especificadores de formato são os mesmos que usamos na função scanf, mas é possível, e muito útil, colocar indicadores de tamanho de campo para os dados de saída. Trata-se do número de posições a serem ocupadas pelo dado de saída. Este número é indicado por um inteiro colocado entre o símbolo % e a letra que designa o tipo do dado. Para strings e inteiros usa-se um único número, mas para dados tipo float, pode-se usar um número que indica o total de posições, um ponto, e um número que indica quantas das posições são reservadas para decimais. Um sinal negativo antes do indicador de tamanho provocara o alinhamento pela esquerda, ao contrário do alinhamento padrão pela direita.
 

Além dos especificadores de formato, é possível colocar também algumas "scape sequences", que são representações de caracteres especiais inseridos na string de formato. Aqui estão algumas destas scape sequences:
 

\n new line

\t tab

\b backspace

\f formfeed ou clear screen
 

Caso seja necessário que a "barra invertida" faça parte da string, como por exemplo em nomes de diretórios, deve-se escrevê-las duas vezes seguidas.
 

Exemplo: "C:\\TC\\BGI"
 

Os itens após a string de formato podem ser constantes, conteúdo de variáveis ou resultado de expressões.
 

Podemos ver aqui alguns exemplos:
 

printf("Digite alguma coisa: ")

printf("Resultado = %f", result)

printf("Area do triangulo: &f \n", base*altura/2)
 

· puts Esta função é usada para exibir uma string, e a sintaxe para sua chamada é:
 

puts( string );
 

Exemplo:
 

puts("Esta e uma string");
 

Uma observação importante é que puts automaticamente envia um caracter new line após a string.
 

· putchar Esta função exibe um único caracter na tela, e a sintaxe para sua chamada é a seguinte:
 

putchar( caracter );
 

exemplo:
 

putchar('a');
 

Ao contrário de strings, escritas entre aspas, constantes tipo caracter são escritas entre apóstrofos em C.
 

Aí você pergunta, porque usar puts e putchar se temos printf ?
 

A resposta é que a função printf é muito maior que as outras, e se você está preocupado com o tamanho do programa EXE que será gerado na compilação, esta é uma boa razão.
 

 Topo da página
   15. Pointers Em PASCAL, é possível trabalhar muito sem ter que usar apontadores (pointers), mas isto não ocorre em C.

Do mesmo modo que em PASCAL, um pointer é um dado que armazena endereços de memória. Nestes locais da memória podemos colocar valores e em seguida aproveitá-los.
 

O tipo dos valores colocados na memória é importante na definição do pointer, pois é este tipo que diz ao compilador qual é a quantidade de memória necessária para armazenar os valores. Isto é imortante, como veremos mais adiante, em aritmética de pointers.
 

Para definir uma variável pointer a sintaxe é:
 

tipo *nome da variável,...;
 

Exemplo:
 

int *apint;

float *apfloat;
 

Deve-se tomar cuidado, pois nestes exemplos acima, apint e apfloat são variáveis que armazenam endereços de memória e não valores tipo int ou float.
 

Quando queremos nos referir ao valor na memória e não ao endereço, usamos o asterisco antes do nome da variável.
 

Exemplo:
 

*apfloat = 3.2;
 

Este asterisco é um operador. Na verdade, o operador contrário a &, que serve para nos das o endereço de uma variável.
 

TURBO C trabalha com alguns tipos de pointers. Entre estes tipos veremos dois deles, pointers tipo near e pointers tipo far.
 

Para entender a diferença entre estes tipos de pointers, é preciso antes entender como o DOS trabalha com a memória do computador.
 

Desde sua primeira versão, mais por causa das limitações dos microprocessadores disponíveis na época, o DOS trabalha com a memória dividida em blocos de 64Kb, chamados "segmentos". Um segmento é representado pelo endereço de seu primeiro byte, que pode ser qualquer um, ou quase qualquer um, em um universo de 1Mb. No entanto, para um endereço de até 1Mb são necessários 20 bits, enquanto que o microprocessador, ao menos os mais antigos, têm registradores de apenas 16 bits (registradores são dispositivos do microprocessador para armazenar dados processados ou a serem processados). Por este motivo, o endereço de um segmento deve ser tal, que os primeiros 4 bits sejam zeros, assim podemos armazenar somente os 16 bits restante, e assim cabem em um registrador. Por causa destes quatro bits zerados implícitos, o endereço de um segmento pode ser qualquer um dentro de 1Mb, mas que seja divisível por 16 (decimal).
 

Além do segmento em que se encontra, para que um byte tenha sua localização completamente definida, é preciso saber qual dos 64Kb do segmento é aquele que nos interessa. Para tanto, mais uma informação é necessária, trata-se do offset dentro do segmento. O primeiro byte do segmento possui offset 0, o segundo offset 1, o terceiro offset 2, e assim por diante.
 

TURBO C permite que o usuário escolha um entre alguns modelos de utilização de memória. Por exemplo, modelo Tiny, modelo Small, modelo Large, etc. Um programa compilado e linkado com um destes modelos sofre as limitações que tal modelo impõe. Por exemplo: no modelo Tiny, o código em linguagem de máquina, os dados e outros itens prezentes na execução do programa, devem ocupar um único segmento da memória; no modelo Small, o código ocupa um segmento, os dados ocupam outro segmento, e assim por diante; com modelos maiores podemos ter programas maiores que 64K e estruturas de dados também maiores que 64K.
 

Os modelos de memória tem importância porque determinam o tipo de pointer que podemos, ou não, utilizar em nosso programas. Dependendo do modelo com o TURBO C estiver configurado, ao definirmos um pointer, é assumido um determinado tipo para este pointer. Por exemplo, no modelo Small, apenas pointers tipo near são utilizados, mas no modelo Large podemos utilizar pointers tipo far.
 

A diferença entre estes tipos é a seguinte: pointer tipo near ocupa apenas dois bytes, 16 bits na memória, isto é, pode guardar apenas valores (endereços) dentro da faixa de 0 a 65535, ou seja, dá para guardar apenas o offset dentro de um determinado segmento ; no entanto, pointer tipo far ocupa 32 bits, e neste caso armazena endereços compostos por segmento e offset. O segmento é representado pelo endereço de seu primeiro byte, e o offset indica qual dos bytes do segmento é aquele que nos interessa. Costuma-se escrever tais endereços na forma SSSS:OOOO, onde SSSS são quatro dígitos hexadecimais que indicam o segmento, e OOOO são quatro dígitos hexadecimais que indicam o offset.
 

Para trabalhar com pointers é preciso muito cuidado. Ao ser definido, um pointer tem como conteúdo não um endereço, mas algo indefinido. Se tentamos usar o pointer assim mesmo, corremos o risco de que o conteúdo do pointer seja interpretado como o endereço de algum local da memória vital para o programa ou mesmo para o funcionamento da máquina. Neste caso podemos provocar danos no programa, nos dados, ou mesmo travar a máquina.
 

Seja um pointer tipo near ou far, devemos tomar cuidado ao inicializar tais variáveis. O valor a elas atribuído deve ser realmente um endereço disponível na memória.
 

Por exemplo, podemos colocar em um pointer o endereço de uma variável já definida:
 

int ivar, *iptr;

iptr = &ivar;

ivar = 421;
 

Equivalente a:
 

int ivar, *iptr;

iptr = &ivar;

*iptr = 421;
 

Outro recurso usado é acionar uma função de biblioteca que procura um local livre na memória, reserva este local e devolve o endereço do byte inicial. A sintaxe para chamada é:
 

malloc( número );
 

Onde número é a quantidade de bytes a ser reservada.
 

Esta quantidade deve ser suficiente para o tipo de valor a ser colocado na memória, aquele tipo com que foi definido o pointer. Para facilitar a vida do programador, há um operador que devolve o número de bytes necessários para representar valores de um dado tipo. A sintaxe para tanto é:
 

sizeof( tipo );
 

Portanto, podemos combinar malloc e sizeof, mas há ainda outro ponto a destacar. O endereço devolvido por malloc é na verdade do tipo *void, compatível com qualquer pointer. No entanto, isto pode não ser verdade para alguns compiladores mais antigos, ou mesmo para tipos definidos pelo programador. Nestes casos, um simples typecast resolve o problema.
 

Exemplo:
 

registro *ptr;

prt = (registro *) malloc(sizeof(registro));

ptr->codigo = 421;
 

Os pointers são usados para implementar uma técnica que proporciona uma utilização mais racional da memória chamada "alocação dinâmica". Trata-se da alocação de espaço de memória para armazenar dados "durante" a execução do programa, somente quando tal armazenamento se fizer realmente necessário. Pode-se ainda "liberar" memória quando os dados não forem mais necessários. Este conceito envolve estruturas de dados que não são nosso objetivo no momento. Porém, a liberação da memória que está reservada e cujo endereço se encontra em um pointer, pode ser facilmente implementada com o uso da função de biblioteca free.
 

Exemplo:
 

int a = calloc(10, sizeof(int));

.....

free(a);
 

Ainda relativo a pointers, a função de saída printf possui um especificador de formato bastante interessante e que envolve conceitos importantes em programação em abiente DOS, trata-se do especificador %p, que pode também aparecer na forma %Fp. Este especificador se aplica quando desejamos exibir o conteúdo de um pointer, tipo near no primeiro formato e tipo far no segundo formato. No caso de pointer tipo near, o formato com que o endereço será exibido é formado por quatro dígitos hexadecimais, suficientes para representar endereços dentro de um único segmento. Porém, no caso de pointers tipo far, o formato com que os dados serão exibidos é dividido em duas partes, segmento e offset, ambos com quatro dígitos haxadecimais e separados por "dois pontos"(:).

Exemplo:
 

Para o trecho de codigo abaixo,
 

int i;

int *pt = &i;

...

printf("%Fp", pt);

...
 

A saída poderia ser algo assim:
 

00FA:0000
 

 Topo da página
   16. Aritmética de Pointers Uma outra maneira de alocar memória para um apontador e usar a função calloc ao invés de malloc. Qual a diferença ?
 

calloc recebe dois parâmetros. A sintaxe seria algo assim:
 

calloc( inteiro1, inteiro2);
 

Exemplo:
 

apont = (int *) calloc(3, sizeof(int));
 

Para entender o que significam os parâmetros observemos o exemplo acima: sizeof(int) é o número de bytes necessários para armazenar um dado tipo int. Sabe-se que neste caso são necessários dois bytes. O primeiro parâmetro é o número três, e indica que devem ser alocados três vezes sizeof(int) bytes, cujo endereço do byte inicial será atribuído ao pointer apont.
 

Desta forma temos espaço em memória para armazenar três números inteiros. Para ver como colocar tais números na memória observe o seguinte exemplo:
 

*apont = 32;

*(apont + 1) = -123;

*(apont + 2) = 1987;
 

Parece estranho somar números a pointers e deve-se tomar muito cuidado.
 

Suponha que apont tenha recebido o endereço 64001 (em notação decimal) após a chamada da função calloc. Foram reservados seis bytes de memória, portanto uma área que vai desde o byte 64001 até o byte 64006.
 

Quando escrevemos apont + 1, isto não equivale a 64001 + 1. De fato isto équivale a 64001 + 1 * sizeof(int), pois apont é um pointer para o tipo int. De modo análogo, apont + 2 equivale a 64001 + 2 * sizeof(int). Esta é a aritmética de pointers, e vale para adição e subtração.
 

Observe que no exemplo acima não há problemas porque a quantidade de memória alocada foi suficiente. No entanto, pode ser desastroso fazer tal coisa quando a quantidade de bytes alocados não é suficiente. Pode-se danificar conteúdo de memória importante e até provocar o travamento da máquina.
 

 Topo da página
   17. Arrays Em C, é possível também criar arrays, isto é, conjuntos de dados de um mesmo tipo, com um mesmo nome, mas identificados através de um índice.

A sintaxe para definir um array é:
 

tipo nome do array[número de elementos];
 

Exemplo:
 

int vetor[3];
 

Os elementos de um array são identificados através de um índice inteiro que começa sempre em 0.
 

Portanto, no exemplo acima temos vetor[0], vetor[1] e vetor[2].
 

Na verdade, em C o nome de um vetor pode ser usado como um pointer que guarda o endereço do primeiro byte do local na memória que guarda os elementos do array. Considerando o exemplo acima, especificar apenas o nome vetor, é o mesmo que um pointer para tipo int, uma vez que se trata de um vetor de inteiros. Seria um pointer para o primeiro elemento do array.
 

Na verdade, para acessar os elementos de um array podemos usar, tanto os índices, quanto a aritmética de pointers vista antes.
 

Outro ponto a destacar é que um array pode ser inicializado no momento de sua definição, como já vimos para outros dados. Nestes caso, a sintaxe é um pouco diferente, mas o exemplo a seguir ilustar o processo. Os valores iniciais para cada elemento do array são escritos entre chaves e separados por vírgula.
 

Exemplo:
 

float vet[5] = { 1.001, 2.123, 4.555, -2.345, 123.3 };
 

Arrays multidimensionais, equivalentes a matrizes, podem ser definidos de modo análogo a arrays unidimensionais, exceto pelo fato de que são especificadas mais de uma dimensão. O exemplo a seguir mostra a definição e inicializacao de uma matriz 3x2 de elementos tipo float.
 

Exemplo:
 

float mat[3][2] = { {1,2}, {3,4}, {5,6} };
 

 Topo da página
   18. Strings Foram abordados os temas pointers e arrays porque são muito usados em C e estão de certo modo relacionados entre si e relacionados às strings.

Em TURBO PASCAL, há um tipo string, mas em C as coisas são diferentes. Uma string é na verdade um array de caracteres, como de fato ocorre no PASCAL padrão. No entanto, como o nome de um array é na verdade um pointer, há outra maneira de definir uma variável string, ela pode ser um pointer para o tipo char.
 

Exemplos:
 

char nome[20];

char *profissao;
 

No primeiro exemplo, a variável nome guarda o endereço do primeiro byte de um local na memória que pode armazenar strings de até 19 caracteres. O ultimo caracter de uma string é sempre null (código zero ASCII). Na verdade, null não pertence de fato à string, serve apenas para indicar seu final.
 

Uma importante diferença entre definir uma variável string como array de char e como pointer para char está na memória. Quando a definimos como array, a memória é automaticamente reservada, como para qualquer array, mas quando o fazemos como pointer, não há memória reservada previamente. Pode ser necessário faze-lo através da função malloc.
 

Outro aspecto importante está em como atribuir valores a estas variáveis.
 

Quando escrevemos uma string entre aspas, TURBO C cria tal string seguida por null em algum lugar no meio do programa objeto. O endereço de tal lugar é que esta disponível para ser então usado no programa. Desta forma, atribuição direta como no exemplo a seguir é perfeitamente válida.
 

Exemplo:
 

char *nome;

nome = "Geraldo Ribeiro";

puts(nome);
 

Esta forma se aplica bem à variáveis string definidas como pointers para char, mas para arrays e outras operações mais complicadas, há uma série de funções de bliblioteca prontas à disposição dos programadores em todos os compiladores.
 

Há uma função de biblioteca que copia os caracteres de uma string para outra. Tais strings podem ser especificadas através de pointers ou mesmo constantes.
 

Sintaxe:
 

strcpy( destino, origem );
 

Exemplo:
 

char nome[20];

strcpy(nome, "Geraldo Ribeiro");

puts(nome);
 

Ao acionarmos a função puts passamos como parâmetro o endereço do primeiro byte da string, isto é, tanto faz passar um pointer ou o nome de um array. De modo análogo especifica-se o parâmetro da função de entrada gets.
 

 Topo da página
   19. Blocos de comandos Como em PASCAL temos BEGIN e END para delimitar os blocos de comandos, em C também há delimitadores, mas são as chaves { e }.

O corpo de uma função é escrito entre chaves, e dentro da função, quando quisermos agrupar várias instruções, por exemplo, para estarem todas subordinadas a uma condição, devemos colocá-las entre chaves.
 

Em C, toda instrução deve terminar por ponto-e-vírgula, exceto aquelas que terminam com uma chave.
 

 Topo da página
   20. Decisões Em C, para em determinado trecho do programa, escolhermos entre dois caminhos a seguir em função de uma determinada condição, como em outras linguagens, há um comando if.

Sintaxe:
 

if (expressão)

instrução1;

else

instrução2;
 

Se o valor da expressão é diferente de zero, a instrução1 é executada, caso contrário a instrução2 é executada.
 

A parte do else é opcional, e se desejarmos, ao invés de uma única, executar mais de uma instrução subordinada ao if, devemos usar um bloco de comandos, no lugar da instrução1 e/ou da instrução2.
 

Há também um comando equivalente ao CASE do PASCAL, o que permite a escolha entre vários procedimentos distintos em função do resultado de uma expressão. O comando é switch.
 

Sintaxe:
 

switch (expressão) {

case item : instruções;

case item : instruções;

...

case item : instruções;

default : instruções;

}
 
 

Onde o resultado da expressão, seja int ou char, é comparado aos itens à frente das palavras case. Diferentemente do PASCAL, à frente de cada palavra case deve haver um único item, e as instruções, zero ou mais, terminam por ponto-e-virgula.
 

Outro ponto importante, e diferente do PASCAL, é que se o resultado da expressão for igual a um dos itens à frente de alguma palavra case e as instruções correspondentes forem executadas, a execução não salta imediatamente para o comando após o switch, mas sim a comparação continua com os próximos itens à frente das palavras case que ainda restarem. Caso se deseje que, após executar os comandos associados a algum item, o comando switch se encerre, basta finalizar a seqüência de comandos com um comando break, como veremos no exemplo a seguir.
 

switch (getchar()) {

case 'C': compile();

break;

case 'R': compile();

roda_programa();

break;

case 'S': salve_arquivo();

break;

case 'E': edita_arquivo();

break;

case 'Q': alve_arquivo();

break;

default: mensagem("Opção inválida...");

}
 

 Topo da página
   21. Loops Há três formas de provocar a repetição de um comando ou bloco de comandos em C: while, do...while e for.
 
· while Neste caso a sintaxe é:
 

while (expressão)

instrução;
 

Onde expressão resulta em zero ou não zero, e instrução é simples ou composta (bloco). Enquanto a expressão for verdadeira (diferente de zero), a instrução será executada.

 

Exemplo:
 

char *msg;

int cont;

mas = "Teste";

cont = 1;

while (cont <= 3) {

printf("%d vez: %s\n", cont, msg);

cont++;

};
 

· do...while Sintaxe:
 

do

instrução;

while (expressão);
 
 

Aqui há uma diferença fundamental em relação ao comando visto anteriormente, enquanto que no comando while pode ocorrer que a instrução não seja executada nem mesmo uma única vez (teste no inicio), no comando do...while a instrução é executada certamente ao menos uma vez (teste no final).
 

Do mesmo modo que antes, instrução pode ser simples ou composta (bloco).
 

· for Este comando pode ser difícil de entender para programadores PASCAL, pois é um pouco diferente do comando de mesmo nome do PASCAL.

Neste caso a sintaxe é:
 

for (exp1; exp2; exp3)

instrução;
 

Do mesmo modo que em PASCAL, o comando for pode ser usado para repetir uma instrução enquanto uma variável contador é incrementada ou decrementada a cada repetição. No entanto, em C o comando é muito mais flexível e poderoso.
 

exp1 é normalmente usada para inicializar o contador

exp2 é o teste para continuação do loop

exp3 é normalmente alguma modificação do contador

instrução pode ser simples ou composta (bloco)
 

Exemplo:
 

int i;

for (i = 0; i <= 10; i++)

printf("%d x 3 = %d\n",i,i*3);
 

Para entender melhor como funciona este comando, lembre-se sempre que o for é equivalente ao seguinte:
 

exp1;

while (exp2) {

instrução;

exp3;

}
 
 

Lembrando que as expressões podem ser formadas por outras expressões (com o uso da vírgula) e conter atribuições, chamadas de funções, etc, vê-se como é versátil o comando for em C. Na verdade, um programa inteiro em C pode estar em um único for, mas isto é coisa para programadores mais experientes.
 

 Topo da página
   22. Funções Matemáticas Para nossos interesses acadêmicos, certamente é importante sabermos usar as funções matémáticas disponíveis no TURBO C. Há uma série delas, cujos protótipos estão distribuídos entre as bibliotecas MATH.H, FLOAT.H e STDLIB.H. Quando for necessário, os programadores devem procurar detalhes nos manuais do TURBO C.
 
 Topo da página
   23. Tratamento de Tela Quando trabalhamos com microcomputadores, o principal dispositivo de saída de dados é o monitor de vídeo, sendo portanto muioto natural a preocupação do programador com a plástica na apresentação dos dados.

Para tratar telas, TURBO C nos fornece uma série de funções especificas cujos protótipos se encontram no arquivo CONIO.H. Há funções para limpar a tela, limpar uma linha, posicionar o cursor, tratar cores, etc. O programador deve buscar maiores informações nos manuais da sua versão de TURBO C.
 

 Topo da página
   24. Definições de tipos Veremos agora como definir nossos próprios tipos em um programa na linguagem C. Trata-se de algo equivalente a seção type de um programa PASCAL e na verdade o que fazemos é escolher novos nomes para os tipos. Isto é muito útil quando começamos a tratar de estruturas mais complexas.

Em C, usamos a diretiva typedef, cuja sintaxe é:
 

typedef tipo nome;
 

Exemplo:
 

typedef unsigned char uchar;

typedef int inteiro;
 

 Topo da página
   25. Tipos enumerados Em muitas situações os dados se adaptam a sequências ordenadas de itens onde um sucede o outro, como por exemplo os dias da semana. Seria ótimo poder escrever, em certas situações, um comando como este a seguir:

for (dia = segunda; dia <= sexta; dia++) { ...
 

Com o que vimos até agora seria perfeitamente possível fazer tal coisa se tivéssemos colocado as seguintes linhas em algum ponto anterior do programa:
 

#define segunda 0

#define terca 1

#define quarta 2

...

#define quinta 3

#define sexta 4
 
 

No entanto, C nos dá uma outra alternativa. Podemos fazer o mesmo que as linhas acima, com uma boa economia de código, usando um tipo enumerado.
 

enum dia_util {segunda, terca, quarta, quinta, sexta};

int dia;
 

for (dia = segunda; dia <= sexta; dia++) {....

}
 

Na verdade, o que ocorre quando definimos um tipo enumerado é o automático relacionamento do primeiro item da seqüência a 0, do segundo item a 1, e assim por diante.
 

Tipos enumerados são conjuntos de identificadores criados pelo programados associados aos inteiros a partir de 0 (primeiro item), 1(segundo item), 2, 3, etc.
 

A sintaxe é:

enum nome do tipo { seqüência de identificadores};
 

O nome do tipo é dispensável e somente deve ser colocado se for intenção do programador definir variáveis deste tipo. Neste caso, a palavra enum deverá preceder o nome do tipo na definição de variáveis.
 

Exemplo:
 

enum vogal { a, e, i, o, u }; /* tipo enumerado vogal */

enum vogal v; /* variável v */
 

Há ainda a possibilidade de usar a diretiva typedef, como no exemplo a seguir:
 

typedef enum { masculino, feminino, indefinido} sexo;
 

 Topo da página
   26. Estruturas Veremos aqui qual é o equivalente em C aos records do PASCAL, isto é, estruturas com itens que tem seus próprios tipos e nomes.

Usando a diretiva typedef para relacionar um nome à estrutura, a sintaxe seria:

 

typedef struct {

tipo campos;

tipo campos;

...

} nome do tipo;
 

Exemplo:
 

typedef struct {

char nome[20], endereço[40];

float salario;

} registro;
 

registro reg;
 

Podemos definir a estrutura diretamente no momento de definir as variáveis do seu tipo.
 

Exemplo:
 

struct registro {

char nome[20], endereço[40];

float salario;

} reg;
 

Neste caso, a palavra registro é dispensável e somente deve estar presente se for intenção do programador definir variáveis de tal tipo em outro ponto do programa.
 

Para nos referirmos a um determinado campo de uma variável tipo estrutura, usamos uma notação semelhante ao ponto no PASCAL, o nome da variável, um ponto, e o nome do campo.
 

No entanto, se tivermos apenas o endereço de estrutura em um pointer, a sintaxe para acessar um campo é o nome do pointer, seguido pelo símbolo -> e o nome do campo.
 

Exemplo:
 

struct estrutura {

char nome[20];

char fone[15];

float salario;

};
 
 

struct estrutura reg;

struct estrutura *apont;

reg.salario = 0;

apont->salario = 0;
 

Neste exemplo, observe também que a definição de uma estrutura como um tipo pode ser feita sem typedef, sem especificar variáveis imediatamente. Neste caso, ao definirmos variáveis mais adiante no programa, devemos usar a palavra struct antes do nome da estrutura.
 

 Topo da página
   27. Arquivos Em aplicações mais elaboradas é comum a necessidade de armazenar dados de modo permanente, isto é, de forma que não se percam ao desligarmos o equipamento ou simplesmente terminarmos o a execução do programa. Normalmente este armazenamento se faz em meio magnético. No nosso caso, em discos magnéticos, floppy disks ou hard disks.
 

Veremos agora alguns recursos que a linguagem C oferece para trabalharmos com arquivos em disco. Teremos apenas um idéia básica. Quem quiser mais detalhes deve procurar estudar os manuais do compilador que estiver usando.

 

O arquivo header stdio.h possui uma série de definições relativas ao tratamento de arquivos em disco. Há um tipo pré definido chamado FILE para o qual definimos um pointer, e este pointer representará o arquivo nas instruções que virão mais adiante em nosso programa.
 

Todo arquivo deve ser aberto para ser processado e depois fechado. Para abrir um arquivo em C usamos a função fopen, quando então especificamos o nome externo do arquivo, isto é, o nome com o qual ele está, ou estará, gravado no disco, e o modo com que estamos abrindo este arquivo, isto é, se estamos criando o arquivo, abrindo-o somente para leitura, para acrescentar dados, etc. Outra coisa indicada no momento da abertura é o tipo do arquivo. C trabalha basicamente com dois tipos de arquivos: arquivos texto e arquivos binários. Após o processamento, fechamos o arquivo com a função fclose.
 

Enquanto o arquivo está aberto, podemos usar uma série de rotinas pré definidas para processa-lo. Veremos agora algumas destas rotinas e para que servem, além daquelas já citadas acima.
 

· fopen Sintaxe:
 

apontador = fopen( nome externo do arquivo, modo de abertura);
 

Tanto o nome externo do arquivo quanto o modo são strings. O nome externo do arquivo pode estar acompanhado por indicação de drive, diretório, etc. Os modos de abertura são indicados por caracteres que podem estar combinados.
 
 
w Serve para indicar a criação de um novo arquivo, sobrepondo-se a um eventual já existente.
r Serve para especificar abertura apenas para leitura, neste caso, o arquivo deve já existir.
a Serve para acrescentar dados ao final de um arquivo, ou cria-lo caso não exista.
+ Caracter que pode ser acrescentado aos anteriores para indicar que é permitida leitura e gravação.
t Caracter que serve para indicar um arquivo texto (normalmente default), também acrescentado aos outros.
b Especifica arquivo tipo binário, também acrescentável aos outros.

 

fopen retorna o endereço que deve ser colocado no pointer para tipo FILE. Caso a função retorne o valor NULL, houve algum erro na abertura do arquivo. Logo, a abertura de um arquivo normalmente faz parte de um teste para já tomar alguma providência no caso de qualquer erro de abertura.
 

· fclose Sintaxe:
 

fclose(apontador );
 

Esta função bastante simples fecha o arquivo.
 

· fprintf Sintaxe:
 

fprintf(apontador do arquivo, string de formato, valores);
 

Esta função é muito semelhante àquela usada para saída no monitor de vídeo, apenas recebendo a mais como parâmetro o apontador para tipo FILE que indica um arquivo já aberto para receber gravação. Caso a função tenha sucesso, retorna o número de bytes gravados, mas em caso de erro retorna um valor especial que pode ser identificado pela palavra pré definida EOF.
 

· fputc Sintaxe:
 

fputc(caracter, apontador para o arquivo);
 

Esta função grava um único caracter no arquivo. Retorna o próprio caracter ou, no caso de erro, EOF.

· fscanf Sintaxe:
 

fscanf(apontador do arquivo, string de formato, endereços);
 

Também esta função é muito semelhante àquela já vista para entrada via teclado. Possui as mesmas limitações para ler strings, uma vez que um espaço em branco é encarado como separador de campos na leitura. Esta função retorna o número de campos lidos com sucesso e, no caso de encontrar o fim do arquivo em uma leitura, retorna EOF.
 

· fgets Sintaxe:
 

fgets(string, numero de caracteres, apontador do arquivo);
 

Esta função resolve o problema de ler strings com espaços no meio, do mesmo modo que sua correspondente para entrada via teclado. A leitura pára quando for encontrado um caracter new line ou quando forem lidos o numero de caracteres especificados menos um. O new line não é acrescentado à string lida, e no seu final é colocado um caracter nulo (para indicar o fim da string). Retorna um apontador para a string lida ou NULL no caso de erro ou fim de arquivo.
 

· fgetc Sintaxe:
 

caracter = fgetc(apontador para arquivo);
 

Lê um único caracter do arquivo, retornando-o como resultado. No caso de erro ou fim de arquivo retorna EOF.
 

· fread Sintaxe:
 

fread(apontador, tamanho em bytes, numero de itens, apontador para o arquivo);
 

Esta função lê um certo número de itens, cada um deles do tamanho especificado, e coloca estes dados na região de memória com endereço no apontador especificado. Esta função se adapta bem quando desejamos, por exemplo, gravar estruturas em um arquivo. Retorna o número de itens lidos e, em caso de erro, retorna um valor menor que o número de itens especificado.
 

· fwrite Sintaxe:
 

fwrite(apontador, tamanho, numero de itens, apontador do arquivo);
 

Análoga à anterior, esta função grava no arquivo um ou mais itens de tamanho especificado e que estavam na memória na região indicada pelo apontador especificado. Retorna o número de itens gravados. Em caso de erro, retorna um valor menor que o número de itens especificado.
 

· fseek Sintaxe:
 

fseek(apontador do arquivo, offset, ponto de referência);
 

Esta função posiciona um apontador interno que o arquivo possui desde o momento em que é aberto. Tal apontador indica onde os dados são gravados no arquivo e de onde são lidos. Sua posição é atualizada automaticamente após cada comando para leitura ou gravação usando uma das funções já comentadas. fseek posiciona o apontador offset bytes à partir do ponto de referência especificado. O ponto de referência pode ser: o inicio do arquivo, representado pelo valor 0 ou o identificador SEEK_SET; a posição atual do apontador interno, representada pelo valor 1 ou pelo identificador SEEK_CUR; ou o fim do arquivo, representado pelo valor 2 ou por SEEK_END.
 

· ftell Sintaxe:
 

posição = fteel(apontador do arquivo);
 

Esta função devolve a posição atual do apontador interno.
 

O próximo exemplo ilustra os aspectos acima discutidos...
 

/* Teste com arquivos em C */
 

#include <stdio.h>

#include <conio.h>
 

typedef struct {

int codigo;

char descricao[21];

float preco;

} registro;
 

FILE *arq;

registro reg;
 

char menu();

void inclusao();

void alteracao();

void listagem();
 

void main() {

char op;

if ((arq=fopen("estoque.txt", "r+"))==NULL)

arq=fopen("estoque.txt", "w+");

do

switch (op=menu()) {

case '1' : inclusao(); break;

case '2' : alteracao(); break;

case '3' : listagem(); break;

case '4' : break;

default : printf("\n\nOpcao invalida...\nPress Enter..."); getch();

}

while (op!='4');

fclose(arq);

}
 
 

char menu() {

clrscr();

printf("1=Inclusao\n2=Alteracao\n3=Listagem\n4=Fim\nSua opcao: ");

return getch();

}

void inclusao() {

clrscr();

printf("Codigo: ");

scanf("%d", &reg.codigo);

flushall();

printf("Descricao: ");

gets(reg.descricao);

printf("Preco: ");

scanf("%f", &reg.preco);

fseek(arq, 0, SEEK_END);

fwrite(&reg, sizeof(registro), 1, arq);

}
 

void alteracao() {

int wcod, pos, achou;

clrscr();

printf("Codigo: ");

scanf("%d", &wcod);

fseek(arq, 0, SEEK_SET);

while ((pos=ftell(arq),fread(&reg, sizeof(registro), 1, arq)==1) && !(achou=reg.codigo==wcod));

if (achou)

{

printf("Descricao: %s\n", reg.descricao);

printf("Preco: %12.2f\n", reg.preco);

printf("\nNovos dados...\n\n");

printf("Codigo: ");

scanf("%d", &reg.codigo);

flushall();

printf("Descricao: ");

gets(reg.descricao);

printf("Preco: ");

scanf("%f", &reg.preco);

fseek(arq, pos, SEEK_SET);

fwrite(&reg, sizeof(registro), 1, arq);

}

else

{

printf("\n\nRegistro nao encontrado...\nPressione Enter...");

getch();

}

}
 

void listagem() {

clrscr();

fseek(arq, 0, SEEK_SET);

while (fread(&reg, sizeof(registro), 1, arq)==1)

printf("%5d %-20s %12.2f\n", reg.codigo, reg.descricao, reg.preco);

printf("\n\nPressione Enter...");

getch();

}
 

 Topo da página
Para Saber Mais...

www.cprogramming.com

Voltar

Atualizado em 31-Out-2000.

Dúvidas, critícas ou qualquer problema no site, favor entrar em contato com o WebMaster.

Informática