Sunteți pe pagina 1din 58

Lógica de Programação e

Introdução a C
APOSTILA DE LÓGICA DE PROGRAMAÇÃO (ALGORITMOS)

ALGORITMO
Definição: Sequência finita de passos que levam à execução uma tarefa.

Ou seja, para que o computador realize alguma atividade deve ser passado a ele um passo-
a-passo para que ele faça o que lhe foi solicitado corretamente. Por exemplo, para se fritar
batatas é necessário, inicialmente, descascar as batatas, em seguida cortá-las para só
então, finalmente, fritá-las. É essa sequência de passos que chamamos de algoritmos e é
dessa forma que os computadores operam.

PROGRAMAS
“Os programas de computadores nada mais são do que algoritmos escritos numa linguagem
de computador (Pascal, C, COBOL, Fortran, Visual Basic entre outras) e que são
interpretados e executados por uma máquina, no caso um computador.” (MORAES, 2000)

As linguagens de programação são como os idiomas que os seres humanos usam para se
comunicar, só que a linguagem de programação é a maneira em que os homens se fazem
ser entendidos pelas máquinas em seus interpretadores. Para cada linguagem existe um
interpretador específico.

“Os algoritmos são descritos em uma linguagem chamada pseudocódigo. Este nome é uma
alusão à posterior implementação em uma linguagem de programação, ou seja, quando
formos programar em uma linguagem, por exemplo Visual Basic, estaremos gerando código
em Visual Basic. Por isso os algoritmos são independentes das linguagens de
programação. Ao contrário de uma linguagem de programação não existe um formalismo
rígido de como deve ser escrito o algoritmo.” (MORAES, 2000)

Os algoritmos devem ser escritos em passo-a-passo de forma simples e objetiva.

EXERCÍCIOS RESOLVIDOS
1) Crie um algoritmo para resolver a expressão numérica:

Críticas e sugestões: consultjnr@gmail.com Página 2


2 x {42/2 - [3 – 5x8 + (7 x 10 -9 +56) + 106]} - 48 = ???

2) Crie um algoritmo para se tomar banho

3) Crie um algoritmo para chupar uma bala

SOLUÇÃO DOS EXERCÍCIOS


1)a) Primeiro resolva o que está entre parênteses:

Multiplique 7 x 10

Subtraia 9

Some 56

b) Depois resolva o que está dentro dos colchetes

Multiplique 5 x 8

Subtraia de 3

Some com o resultado encontrado dentro dos parênteses

Some 106

c) Resolva o que está dentro das chaves

Divida 42 por 2

Subtraia o resultado dos colchetes

d) Por fim resolva o que está fora das chaves

Multiplique por 2 o resultado das chaves

Subtraia 48

2)

Se não estiver vestido e se não estiver calçado:

Críticas e sugestões: consultjnr@gmail.com Página 3


Ir para o banheiro;

Se a porta estiver fechada:

Abrir a porta e entrar e andar em direção ao boxe;

Senão, entrar no banheiro e andar em direção ao boxe;

Se o boxe estiver fechado:

Abrir o boxe e entrar;

Senão, entrar no boxe;

Se o chuveiro não estiver ligado:

Ligar o chuveiro, se molhar, se ensaboar e enxaguar;

Senão, se molhar, se ensaboar e enxaguar;

Desligar chuveiro;

} Senão

Tirar o sapato ou tirar a roupa;

Ir para o banheiro;

Se a porta estiver fechada:

Abrir a porta e entrar e andar em direção ao boxe;

Senão, entrar no banheiro e andar em direção ao boxe;

Se o boxe estiver fechado:

Abrir o boxe e entrar;

Senão, entrar no boxe;

Se o chuveiro não estiver ligado:

Ligar o chuveiro, se molhar, se ensaboar e enxaguar;

Senão, se molhar, se ensaboar e enxaguar;

Desligar chuveiro;

Fim do Algoritmo.

3)

Pegar a bala

Críticas e sugestões: consultjnr@gmail.com Página 4


Retirar o papel

Chupar a bala

Jogar o papel no lixo

EXERCÍCIOS PARA RESOLVER


1. Crie um algoritmo para sair de casa e chegar até a faculdade.

2. Crie um algoritmo para resolver as expressões:

a. 36 + 2 x{25 + [ 18 – (5 – 2) x3]} = ???

b. [(5² - 6.2²).3 + (13 – 7)² : 3] : 5 = ???

3. Crie um algoritmo para fazer um gol.

FLUXOGRAMA
Outra forma de se escrever um algoritmo é através de fluxograma. Fluxograma nada mais é
do que símbolos que representam os passos lógicos de um determinado algoritmo.

O quadro abaixo traz alguns dos símbolos utilizados:

Críticas e sugestões: consultjnr@gmail.com Página 5


Dentro dos símbolos deve existir um texto que indique qual passo ele representa.

Exemplo:

Críticas e sugestões: consultjnr@gmail.com Página 6


TIPOS PRIMITIVOS E VARIÁVEIS
Chamamos de tipos primitivos aquilo que da forma ao dado que será armazenado.

Quando falamos em número e letra, por exemplo, estamos tipando símbolos, declarando
que alguns representam letras e possuem uma determinada maneira que devem ser
utilizados de acordo com o seu significado, o mesmo acontece com os números. O
computador, assim como nós seres humanos, também precisa que se defina o que cada
símbolo representa para poder executar algumas operações. Os tipos primitivos existentes
na linguagem computacional são inteiro, real, caractere e lógico.

INTEIRO: corresponde aos números inteiros positivos e negativos

REAL: corresponde aos números reais positivos e negativos

LÓGICO: Toda e qualquer situação só pode assumir apenas dois estados: Verdadeiro
e Falso

CARACTERE: Toda e qualquer informação comporta por um conjunto de caracteres


alfanuméricos: numéricos (0...9), alfabéticos(a...z, A...Z), e especiais (!, @, #, $, %...).

Chamamos de variáveis os dados que apresentam a possibilidade de sofrerem alterações


em algum instante no decorrer do tempo na execução do Algoritmo, podendo serem simples
ou compostas (arrays – vetores e matrizes). Aqueles dados que não sofrem modificações
são chamados de constantes.

Toda variável, ao ser utilizada num algoritmo, deve ser nomeada através de um identificador
e deve ser tipada.

Exemplo: inteiro: nome_da_variável;

COMANDOS DE ENTRADA E SAÍDA DE DADOS


Para que um algoritmo possa receber os dados necessários, utilizamos um comando de
entrada de dados denominado leia.

Exemplo: Leia (nome_da_variável1, nome_da_variavel2);

O comando de saída de dados é o comando escreva e serve para exibir o conteúdo da


variável identificada.

Críticas e sugestões: consultjnr@gmail.com Página 7


Exemplo: Escreva (nome_da_variável ou “expressão_alfanumerica”);

ESTRUTURAS DE CONTROLE
Existem três tipos de estruturas de controle (sequenciação, repetição e seleção). Através da
combinação dessas estruturas podemos criar algoritmos que solucionem nossos problemas.

Seqüencial: Corresponde ao conjunto de ações primitivas executado em sequência


linear de cima para baixo e da esquerda para a direita, sendo cada ação encerradas
por um ponto-e-vírgula(;).

Seleção: A estrutura de seleção permite que um bloco de ações seja selecionado


através de uma condição sendo satisfeita ou não. As condições nada mais são do
que expressões lógicas que, quando inspecionadas podem gerar um resultado falso
ou verdadeiro. As estruturas de seleção podem ser simples ou compostas.

Repetição: A repetição permite que, através de um teste de condição, um bloco de


ações seja executado enquanto for verdade ou até que a condição seja satisfeita

“A construção "enquanto" (também chamada "repetição pré-testada") é a mais difundida


estrutura de repetição, e sua estrutura básica é a seguinte:

Enquanto (condição) Faça


(bloco de código)
Fim Enquanto

Nesta estrutura, a condição é primeiramente verificada, e se for verdadeira o bloco é


executado. No final de execução do bloco a condição é novamente testada.A construção
"repita enquanto" (também chamada "repetição pós-testada") é uma variação da construção
anterior, e difere pois a verificação da condição é feita após uma execução do bloco. Sua
estrutura básica é a seguinte:

Repita
(bloco de código)
Enquanto (condição)

Nesta estrutura, o bloco de código é primeiramente executado, e então a condição é


verificada, e se for verdadeira o bloco é executado novamente.”.
(www.wikipedia.org)

Críticas e sugestões: consultjnr@gmail.com Página 8


Exemplo 1:

Exemplo 1.1

Inicio

Pegar bala;

//Condição (neste caso faz parte da estrutura de seleção)

Se bala = morango{

Vá para o Fim;

// O senão representa a seleção composta

Senão {

Retirar o papel;

Chupar a bala;

Jogar o papel no Lixo;

}Fim

Exemplo 2

Críticas e sugestões: consultjnr@gmail.com Página 9


Suponha a média de aprovação de uma escola maior ou igual a 5. O algoritmo para saber se
o aluno está ou não aprovado seria:

Exemplo 3:

Críticas e sugestões: consultjnr@gmail.com Página 10


O algoritmo calcula a soma dos salários dos funcionários de uma empresa. O programa
termina quando o usuário digitar um salário menor que 0.

PROGRAMA SOMA_SALARIOS;

//Declarando variáveis do tipo Real

VAR

SOMA, SALARIO : REAL;

//Começo do algoritmo

INICIO

Críticas e sugestões: consultjnr@gmail.com Página 11


SOMA:=O;

//ESTRUTURA DE REPETIÇÃO PÓS-TESTADA

REPITA

LER (SALARIO);

SOMA:=SOMA+SALARIO

ATE SALARIO<0;

ESCREVER (SOMA)

FIM.

LINGUAGEM C
LÓGICA NÃO É MÁGICA

A partir daqui será iniciado o estudo de C, uma linguagem de programação resistente ao tempo,
que permite perceber os processos de forma minuciosa, com vários recursos que fazem dela uma
das linguagens mais utilizadas ainda hoje.

Esperamos que você tenha percebido que a lógica não lhe dirá o que usar. Essa será uma
questão de muita leitura das bibliotecas e funções para saber qual se adequa à melhor situação.
O intuito de estudar lógica é o de saber pensar seqüencialmente, elencando as prioridades
corretas na hora de programar, assim, evitando erros, gerando otimizações, códigos com menos
esforço computacional, etc. Não se preocupe: todos cometem falhas em lógica, mas não a culpe
se não há domínio da sintaxe e desleixo quanto ao uso correto de todas as entidades envolvidas
em C.

Boa leitura!

ESTRUTURA BÁSICA DE UM PROGRAMA EM C:


declarações globais;
/*assim se faz um comentário
com mais de uma linha*/
//assim se faz um comentário numa única linha
tipo_devolvido main (array_argumentos, lista_argumentos /*opcionais*/){
declarações locais;
sequência de comandos;
}

Críticas e sugestões: consultjnr@gmail.com Página 12


/*Funções usadas em 'main'*/
tipo_devolvido nome_funcao1 (lista de argumentos) {
declarações locais;
sequência de comandos;
}
tipo_devolvido nome_funcao2 (lista de argumentos) {
declarações locais;
sequência de comandos;
}
O que na prática pode ser exemplificado por:
#include <stdio.h>
int main () {
int x;

x = 23;
printf ("Este e o valor do numero x: %d ", x);

system ("PAUSE");

return 0;
}

Explicando brevemente:

#include <stdio.h>: é um comando que adiciona ao seu arquivo o conteúdo daquilo


referenciado entre < e >.

int main () { : esta é a declaração da função principal (main). Todo programa deve ter uma
para funcionar.

int x; : declaração de x como variável local do tipo inteiro.

x = 23; : atribuição do valor 23, a x. Costuma-se ler: x recebe 23, ou x deve ser igual a 23.

printf ("Este e o valor do numero x: %d ", x); : essa é uma função (explicaremos
a seguir) que imprime na tela o valor atual da variável x (no console será exibido: Este e o valor
do numero x: 23).

system ("PAUSE"); : esta função força uma pausa no sistema esperando por uma tecla.
Alguns compiladores pausam o console (prompt) após a execução do programa, mas outros não
o fazem e sem essa instrução o programa não seria visto.

return 0; : é a instrução final da função main, ou seja, do programa. Ela diz que o retorno da
função ao sistema operacional é 0.

todos os comandos em C precisam ser encerrados, inicialmente, com ponto-e-vírgula ( ; ). Alguns


iniciarão um bloco de comando (seqüência com mais de um comando) e é necessário que esse
bloco esteja entre „{ „e „} „exemplo:
int main {
return 0;
}

Resumindo: Como o programa é executado?

Críticas e sugestões: consultjnr@gmail.com Página 13


Para que programas sejam executados é necessário um processo de tradução das informações
em código binário. Esse processo compõe-se de outros dois: Compilação e Linkedição.

Todo o código precisa ser gerado a partir de um editor de texto padrão (Notepad, Notepad++,
gEdit, etc.). Outros editores (Microsoft Word, Wordpad, por exemplo) podem incluir dados e
formatos que o compilador desconheça. O programador gera um código-fonte (source code), que
é o programa em si, contendo os comandos da linguagem. Ao solicitar a compilação, ocorre a
validação de erros de sintaxe (declaração dos comandos, uso correto da linguagem), caso não
seja encontrado nenhum erro é gerado um código-objeto, que é a tradução do código fonte para
uma forma em que o computador pode executar diretamente. Esta tradução é feita pelo
Compilador, que após a tradução pode executar o código. A Linkedição trata da adição de dados
ao código-objeto referenciados no código fonte. Esses dados são fornecidos por bibliotecas (A
biblioteca C padrão com funções que realizam as tarefas mais comuns) dinâmicas ou estáticas.
Por exemplo, quando se chama uma função que não faz parte do programa, o compilador guarda
o seu nome. Após, o linkeditor combina o código escrito com o código objeto já encontrado na
biblioteca.

CASE-SENSITIVE
O C é sensível a palavras maiúsculas e minusculas. Dessa forma ele encara 'voce', 'Voce' e
'VOCE' como palavras diferentes. Isso vale para todos os identificadores em C (variáveis,
funções, etc. Que serão explicados mais adiante). Não são aceitas palavras acentuadas. Muita
atenção: o seu compilador geralmente informará erros dessa natureza, mas evitar cometê-los é o
melhor caminho.

PALAVRAS RESERVADAS
São palavras usadas pelo programador para acessar funcionalidades da linguagem. Veja uma
lista de palavras reservadas usadas em C:

auto double int struct


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

Durante o desenvolvimento de qualquer programa em C, atente-se ao uso de letras maiúsculas


em palavras reservadas! Elas não serão usadas para nenhum outro propósito que não seja o
definido para elas. Para entender essa questão, perceba que "If" não equivale a "if"; "INT" não é
igual a "int", etc. Algumas delas serão usadas e explicadas nas seções seguintes.

TIPOS BÁSICOS
Os tipos básicos em C são: Inteiro (int), Caractere (char), Real (Ponto Flutuante, float), Real
Longo (Ponto Flutuante de precisão dupla, double) e void (Genérico/sem definição).

Críticas e sugestões: consultjnr@gmail.com Página 14


LEMBRETE: Podem ocorrer mudanças nos tipos básicos (ou primitivos) em C. Variando de PC a
PC, arquitetura a arquitetura, etc. O Padrão C ANSI não definiu até o momento um tamanho
específico e sim uma faixa mínima (da informação). O que tem a ver, também, com a quantidade
de bits usados para representá-la e assim se deduz seu respectivo tamanho mínimo em bytes.

Veja a tabela abaixo, adaptada de "C Completo e Total":

Tipo Faixa Mínima bits Bytes


char -127 a 127 8 1
int -32.767 a 32.767 16 2
float 3.4e-38 a 3.4e38 32 4
double 1.7E-308 a 1.7E308 64 8

MODIFICADORES DE TIPOS BÁSICOS

A depender da implementação, serão necessárias mudanças, modificações na forma como se


manipulam os tipos básicos, uma forma é usando modificadores que alteram, literalmente, a faixa
e até mesmo o tamanho especificado para um tipo básico determinado. Pode-se tratar a
modificação como um novo tipo, mas recomenda-se não perder a ligação com o tipo original.
Atenção com o tipo 'void': por ser um tipo genérico, não pode ser alterado.

Veja a lista abaixo adaptada de Julio Battisti:

* Long: É empregado aos tipos de dados 'int' e 'double' para indicar um tipo de dado maior.
Também pode ser declarado em conjunto com o 'float', mas isto seria a mesma coisa de usar o
tipo double.

* Short: Muito utilizado para indicar valores inteiros curtos (short int). Normalmente possui o
mesmo tamanho do tipo int, mas, geralmente são armazenados em um numero menor de bytes.

* Signed: A declaração padrão dos tipos já permite o uso de sinais (- unário: -1,-2,-3, não
confundir com o operador) em números int e caracteres, então seu uso seria redundante.

* Unsigned: Não permite o uso de sinais. Este modificador não é indicado para o tipo int, pois
podem ocasionar algumas complicações caso seja atribuído em algum lugar do código um valor
negativo.

Veja a tabela abaixo, adaptada de "C Completo e Total":

Tipo Faixa Mínima bits Bytes


unsigned char 0 a 255 8 1
signed char -127 a 127 8 1
unsigned int 0 a 65.535 16 2
signed int o mesmo que int 16 2
short int o mesmo que int 16 2
unsigned short int 0 a 65.535 16 2
signed short int o mesmo que short int 16 2
long int -2.147.483.647 a 2.147.483.647 32 4
unsigned long int 0 a 4.294.967.265 32 8
signed long int o mesmo que long int 32 8

Críticas e sugestões: consultjnr@gmail.com Página 15


long double* dez dígitos de precisão 80 10

* para os tipos de dados em ponto flutuante (float, double) é usada, geralmente a notação em
"dígitos de precisão". Analogamente, poderíamos dizer que são a quantidade de zeros após a
vírgula: 0,000000, por exemplo.

O TIPO 'void'
Este tipo genérico é usado quando se desconhece o tamanho daquilo armazenado ou lido na
memória. Muito cuidado com seu uso pois pode trazer problemas à suas aplicações. No entanto,
seu uso correto permitirá generalizar muitos dados em seu código, mas dependerá bastante de
casting (métodos de conversão de tipo, coerção), o que será tratado a seguir.

O TIPO 'char'
Este tipo possui uma característica um tanto explorada no ínicio da vida de programação: é
necessária uma tabela para organizar os caracteres, por conseqüência, associando-os a valores
inteiros, de modo que sejam acessíveis em código binário assim como qualquer outro dado. O C
ANSI usa a tabela ASCII (American Standard Code for Information Interchange), que usa até um
byte (8 bits) para fazer a representação dos caracteres, um a um. Lembre-se que um texto inteiro
é interpretado e impresso usando essa tabela como intermédio. Visite:
http://www.tabelaascii.com/ para visualizá-la.

OPERADORES
Existem, basicamente, três classes de operadores em C: Aritméticos, Lógicos e Binários. Além
disso

LEMBRETE: A atribuição, em C é feita com um único sinal de igual ( = ). Portanto não confunda
com a igualdade lógica, que usa um sinal duplo ( == ). Veja:
if ( a == b ) { /*se a é igual a b*/
...
}

ARITMÉTICOS
Os operadores aritméticos são:

+ Adição
- Subtração
* Multiplicação
/ Divisão
% Módulo (resto da divisão de inteiros)*

O operador módulo não funciona com número em ponto flutuante, pois não considera a parte
fracionária do número (neste caso o resto é conservado inteiro).

DICA: Todos os operadores aritméticos podem ser combinados com a atribuição. Pode causar
confusão no início, mas evita a repetição do nome da variável (por exemplo) de destino.
Exemplos:

x+=y; equivale a x = x + y;

Críticas e sugestões: consultjnr@gmail.com Página 16


a*=b; equivale a a = a * b;

x/=y * (a+b); equivale a x = x / (y * (a+b));

Existem os operadores de incremento/decremento, que permitem o aumento/diminuição em 1 de


uma variável qualquer. Observe:

x++; equivale a x = x+1; ou x+=1;

++x; equivale a x = x+1; ou x+=1;

y = x++ equivale a y = x; x = x+1; /* aqui ocorre o pós-incremento. A variável x é


incrementada após ter seu valor copiado à y */

y = ++x equivale a x = x+1; y = x; /* aqui ocorre o pré-incremento. A variável x é


incrementada antes de ter seu valor copiado à y */

PARA O CÓDIGO ABAIXO TEREMOS DETERMINADO RESULTADO:

Críticas e sugestões: consultjnr@gmail.com Página 17


LÓGICOS, OU RELACIONAIS
Usados na relação de entidades, ocorre a validação da expressão booleana ou operação e
retorna-se 0 em caso de falso e algum valor diferente de zero em caso verdadeiro.

LEMBRETE: Não existem true e false (palavras reservadas), nem um tipo boolean no C ANSI,
use essas características do retorno e dos valores reconhecidos como verdadeiro e falso para
verificar a veracidade de uma expressão relacional/lógica.

Relacionais:

x < y menor que


x <= y menor ou igual
x > y maior que
x >= y maior ou igual
x == y comparar igualdade
x != y comparar diferença

Lógicos:

&& and (e)


|| or (ou)
! not (negação)

LEMBRETE: O que você compara são os valores das entidades. Algumas vezes você pode
comparar o retorno de funções, endereços de memória e muitas outras coisas. Lembre-se de
usar parêntesis para alternar a ordem de verificação. Exemplo:
(a && b) || c == !((a || b) && c);

Críticas e sugestões: consultjnr@gmail.com Página 18


O OPERADOR TERNÁRIO ? (Interrogação)

É usado quando se deseja fazer uma ação ou atribuir um valor à expressão verificada se verdade
ou falso. Seu uso ainda traz muitas dificuldades para iniciantes, pois sua leitura não é clara.
Porém, com prática, alguns comandos de seleção (if-then-else) serão facilmente substituídos por
ele. Observe:

Eis o protótipo extraído de “C Completo e Total”: Exp1 ? Exp2: Exp3;

Assim ocorre a validação da primeira expressão (Exp1). Sendo verdade, valida-se a segunda
expressão e ela se torna o valor da primeira. Se não, a terceira expressão é validada e se torna o
valor da primeira. Um exemplo retirado do mesmo livro exemplifica bem isso:
x = 10;
y = x > 9 ? 100 : 200;

Ou seja, se x for maior que nove, 100 é retornado à expressão e é atribuído à y. Se não, ocorre a
atribuição de 200 em y. Outro exemplo:
char r = „R‟;
char s = „A‟;
r > s? ++r : s = --r; /*Geralmente lê-se: r é maior que s? Se sim, r mais, mais. Se
não, s recebe menos, menos r.*/

OPERADORES * e &
Operadores de ponteiro são importantes em C. Permitem a passagem de vetores, strings e
structs (estruturas) para funções, além de permitir que as mesmas alterem o conteúdo dos
argumentos passados. Os dois operadores de ponteiros são & e * (Diferente do operador
aritmético, este operador é unário a um ponteiro ou expressão)

O operador & retorna o endereço da variável a que precede.

O operador * leva o conteúdo da variável à que precede e usa este valor como endereço da
informação na memória.

PRECEDÊNCIA DE OPERADORES
Para se construir e ler corretamente expressões em C, é necessário um conhecer a precedência
entre eles.

A tabela seguinte lista as precedências por ordem decrescente bem como o sentido de avaliação
(associatividade).

Operadores Associatividade
() [] -> . da esquerda para a direita
! ^ ++ -- + - * & (tipo) sizeof da direita para a esquerda
* / % da esquerda para a direita
+ - da esquerda para a direita
<< >> da esquerda para a direita
< <= > >= da esquerda para a direita
== != da esquerda para a direita
& da esquerda para a direita
^ da esquerda para a direita

Críticas e sugestões: consultjnr@gmail.com Página 19


| da esquerda para a direita
&& da esquerda para a direita
|| da esquerda para a direita
?: da direita para a esquerda
= += -= *= /= %= &= ^= |= <<= >>= da direita para a esquerda
, da esquerda para a direita

COERÇÃO DE TIPOS
Com um pouco mais de experiência, você perceberá que algumas funções mais antigas que
lidam com caracteres, recebem/retornam um int no lugar de um char. Como isso acontece? há,
de certa forma, uma similaridade entre os dois tipos: mesmo que o tamanho de um char seja
menor que o de um int (8 bits e 16, respectivamente), é possível acessar um caractere
informando seu correspondente inteiro. Como uma sequência binária pode ser qualquer coisa, o
que muda de caso a caso é o contexto. Perceba que, 'A' tem como correspondente binário "0100
0001" e ao fazer a conversão dessa seqüência a um número inteiro obtemos 65. Se ele for usado
para referenciar o char 'A', está correto (Pela tabela ASCII, informada anteriormente). A
sequência equivalente a este char ficaria representada numa posição de memória do tipo int
desse modo: "0000 0000 0100 0001", portanto, podemos afirmar que todo char pode ser
representado num int, mas o contrário não acontece. Para tentar resolver esses problemas são
usadas técnicas de coerção.

Basicamente, em C existem formas de se converter um tipo em outro: na exibição dos dados ao


usuário, na atribuição de variáveis de tipos diferentes compatíveis, ou não (cast). Ao exibir ou
converter entre variáveis “compatíveis”, o casting ocorreria internamente no computador, sem
alteração do conteúdo real daquilo exibido ou copiado (retorno de função ou variável, por
exemplo). Observe:

Ocorre conversão automática em relações/atribuições como no seguinte exemplo:

char para int ;

short int para int ;

float para double ;

---------------------
#include <stdio.h>

int main (void){


short int si = 200;
int i;
float f = 3.1415;
double d;
char c = 'A';
int j;
int k;

i = si;
d = f;

Críticas e sugestões: consultjnr@gmail.com Página 20


j = c;
k = f;
printf ("int i recebendo o short int: %d\n"
"double d recebendo float: %lf\n"
"int j recebendo char: %d (número) e %c (letra) \n",
i, d, j, j );
system ("PAUSE");
return 0;
}
Lembra-se do tamanho dos tipos? Você consegue ver qual a relação entre eles? Veja que esses
castings ocorrem de um tipo de tamanho menor ou igual ao tipo de destino. No caso de um int
receber um float, ocorre que o int contará somente com a parte inteira do float. Lembre-se disso.
Outro exemplo:
...
int i = 2;

printf(“numero i como real = %f”, i); /*coerção interna do computador, não se altera o
tipo da variável.*/
/*Para visualizar i como número real, informamos à função printf que ela deve exibir o
conteúdo da variável como float (%f). Trataremos dessa função a seguir*/
...

Como se sabe, a atribuição copia o valor daquilo referenciado na direita para aquilo referenciado
à esquerda do „=‟. Portanto há a mudança no valor, ou atualização. Para converter, nesse caso, a
coerção se apresenta com a inscrição, entre parêntesis, do novo tipo, segundo o protótipo:
variável1 = (novo_tipo) variável2;

Ou
variável0 = variável1 + (novo tipo) variável2;

Onde as variáveis devem ser de tipos diferentes e o novo tipo deve ser igual ao da variável que
recebe a atribuição. Se não, seria ilógica a conversão. Segue o exemplo:
int i;
float f;

f = (int) i;

IDENTIFICADORES, VARIÁVEIS E COMANDOS EM C


São todos os nomes que resultam em acesso ou chamada à entidades no seu programa, sejam
eles: funções, variáveis, rótulos, etc. Devem sempre iniciar por letra minúscula ou underline ( _ ),
após esse caractere inicial, não podem haver pontuações ou caracteres acentuados. Somente
números, caracteres e underlines. Eis alguns exemplos de uso:

Correto Incorreto
count Count
count1 1count
_abc abc!
abc abc.

Críticas e sugestões: consultjnr@gmail.com Página 21


VARIÁVEIS
São posições de memória que recebem um valor alterável durante a execução do programa. Veja
o exemplo de declaração:
int x;

Assim declara-se x como uma variável do tipo inteiro. (lembra-se do tamanho mínimo em bytes e
a faixa de valores?)

Exercício:

Como seria a declaração de inteiro sem sinal? Informe seu tamanho em bits. E faça assim com
os outros tipos.

E responda: O número 120968 cabe em qual, ou quais tipos? Por quê?

Todas as variáveis precisam ser declaradas antes de serem usadas. Existem algumas formas de
fazer essa declaração: Fora de todas as funções (variável global), dentro de uma função (variável
local) ou como parâmetro (valores de entrada de uma função). Declararíamos várias variáveis do
mesmo tipo usando vírgula:
int x, y, z;

ESCOPO
É comum se ouvir referência ao “Escopo” de uma variável. Este seria a porção de código onde a
variável foi declarada. Isso determina seu tempo de vida e possibilidades de acesso e são
classificadas, em C, em variáveis locais, parâmetros formais, e globais.

VARIÁVEIS LOCAIS
Comumente declaradas em funções, são aquelas que somente são acessíveis dentro do bloco
onde ocorreu a declaração. Simplesmente, a variável é criada ao iniciar o bloco e destruída,
assim inacessível, quando o bloco termina. Por exemplo:

float f1 () {
float x;
float y;

x = 36,63;
y = x * 8 /6,5 ;
return ( x * y );
}

float f2 () {
float x;
float y;

x = 15.1;
y = 16.9;
return ( x * y );
}

Aqui temos duas funções onde as variáveis são locais e todas do tipo float. Perceba que o nome
dado à variável é uma marcação e não tem nada a ver com o seu tipo ou com outros homônimos

Críticas e sugestões: consultjnr@gmail.com Página 22


em outras funções. Portanto, seria incorreto declarar duas variáveis com o mesmo nome, pois
poderia causar confusão na leitura do código. Duas variáveis com o mesmo rótulo não são
permitidas numa mesma função.

Uma forma de declaração pouco abordada em leituras iniciais sobre C, por causar confusão, é a
de declarar variáveis dentro de um bloco de código específico. Isso é importante em ocasiões de
grande esforço computacional ou de memória baixa, criando variáveis condicionalmente. A
confusão dessa forma de declaração, geralmente, está no pensamento de que a variável estaria
visível a partir dali quando ela é interna a um bloco. Usando a conceituação citada acima, a
variável tem seu fim junto com o bloco de código. Portanto, veja o exemplo:
float f1 () {
float x;
float y;
x = 36;
y = x * 8 /6;
int a = 36;

/*a declaração está dentro do bloco da função e permite que se utilize a partir daqui,
mas antes, impossível. Vale-se dos mesmos pretextos alicerçados antes*/

if (x > y) {/*se x é maior que y*/


float z = x-y; /*é declarada a variável z que será acessível somente dentro dessas
chaves*/
y = z;
}
/* a partir daqui, z não está mais acessível pelo programa. Qualquer menção à z gera
erros de compilação ou de execução em seu programa. Observe que seu valor fora copiado
a y antes de seu término neste bloco*/
return 0;
}

PARÂMETROS
Em algum momento, funções precisarão tratar de variáveis que já sofreram alterações no
programa (trataremos as funções logo mais). Usam-se argumentos - parâmetros para o
funcionamento da função - que são, essencialmente, variáveis locais, para se utilizar esses
valores de entrada na função que os recebe.

Exemplo:
int sub (int x, int y /*parâmetros são listados aqui e separados por vírgula*/ ){
if(a > b) return (a-b);
else return (b-a);
}

É necessário informar o tipo do parâmetro que será recebido na abertura da função, como visto
acima. Logicamente, os parâmetros que serão passados devem ser do mesmo tipo daqueles que
serão recebidos, ou então podem acontecer erros inesperados. Mais adiante trataremos dos
mecanismos de passagem de parâmetros.

VARIÁVEIS GLOBAIS
Raramente haverá a necessidade de usá-las, pois a parametrização das funções garante seu
reaproveitamento. Ou seja, o uso de variáveis locais permite uma maior generalização de seus
programas e funções. Uma variável global é declarada fora de todas as funções – recomenda-se

Críticas e sugestões: consultjnr@gmail.com Página 23


antes do main – e está disponível, acessível, por qualquer trecho de código de qualquer função
do programa. Veja o exemplo:
#include <stdio.h>
int x; /*neste caso, x é global*/
int main () {
x = 23; /*referenciamos x como global*/
int x = 13;
/*ao declararmos uma variável com o mesmo rótulo de uma global a sua função utilizará
a sua variável local.*/

printf ("Este e o valor do numero x: %d ", x);


system ("PAUSE");
return 0;
}

Observe que não mudamos o caráter da variável x, pois são duas variáveis distintas. Acontece
que x, local à main, é mais específica que x, global ao programa sendo usado o último. As
variáveis globais são alocadas em uma região de memória fixa, ocupando espaço adicional do
seu programa, podendo ser alteradas acidentalmente. Entenda que uma variável global é visível
dentro de um bloco de código maior, que é o programa em si. Lembra das variáveis locais? Elas
não são destruídas ao fim do bloco onde foram declaradas? Então uma variável global é
destruída com o término do programa.

DICA: Inicialização de variáveis: As variáveis podem ser inicializadas logo após sua declaração
sendo desnecessária mais uma instrução para colocar o valor inicial à variável. Quando
colocamos “int x = 3;” em um código, isso significa que a variável x foi alocada com valor inicial 3.
Se você não inicializasse x e quisesse obter seu valor para alguma coisa em um programa,
ocorreriam erros imprevisíveis (seria lido um trecho de memória com restos de programas
anteriores). Inicializar as variáveis promove segurança à utilização do seu programa.
Recomendamos deixar de inicializar uma variável somente quando for estritamente necessário e
que, se possível, marque-a ou recorde-se dela quando for fazer alguma alteração.

COMANDOS
São as palavras reservadas responsáveis por fazer seu código executar ações pré-estabelecidas
pela linguagem. Basicamente, existem comandos de seleção, laço e desvio, em C. Existem
outras nomenclaturas e outros comandos, mas nosso intuito aqui é mostrar os principais e de
forma rápida .

SELEÇÃO

IF - ELSE
O se – então – senão (if-else) é usado caso queiramos dar um tratamento a depender do valor
booleano da expressão avaliada. Protótipo:
if (expressão) comando;
else
comando;
Ou

Críticas e sugestões: consultjnr@gmail.com Página 24


if (expressão) {
comando;
comando;
comando;
comando;
...
}
else {
comando;
comando;
...
}

A expressão booleana, não necessariamente precisa ter operadores relacionais para ser validada
dentro do if. Se não houver, assume-se que se questiona se o valor da expressão é verdadeiro. O
else é opcional e somente é executado se for obtido o valor booleano contrário ao esperado na
expressão dentro do if. Podemos usar um comando único ou um bloco – não se esquecer das
chaves –. Exemplos:
if (a>b) b-=a;
else b*=a;

/*pergunta-se se a é maior que b. Sim: ocorre a subtração de a em b. Não: (se a é menor


ou igual a b) ocorre a multiplicação com atribuição a b*/
If (s){
printf(“s tem valor verdadeiro (diferente de zero)\n”);
s--;
}
else {
printf (“s tem valor igual a zero\n”);
s++;
}
/*Neste caso, verifica-se o valor de s.*/

DICA: Muitas vezes você quererá executar um comando num if em caso de algo retornar zero.
Naturalmente, como o valor da expressão seria zero, não se “entraria” no if. Para isso, use o
operador de negação ( ! ), que inverterá o valor obtido na expressão. Então toda vez que uma
expressão for verdadeira, o if ,com a inversão, a tratará como falsa e vice-versa. Exemplo:
If (!(a-b)) printf (“ os números são iguais!\n”); /* se a-b == 0, quer dizer que os
números são iguais: a=8 e b=8*/

IF’s CASCATEADOS E ANINHADOS


Quando se tem uma variedade de validações a fazer é comum que se aninhem ou cascateiem
alguns ifs e elses. Veja os exemplos:
/*Adaptado de C Completo e Total*/
if (i) {
if(j) ... ;
if (k) {
...
}
else ... ; /* este else se refere ao if (k)*/
}
else … ; /* já este se refere ao primeiro if. Perceba os blocos*/

Críticas e sugestões: consultjnr@gmail.com Página 25


Neste exemplo o programa questiona i. Se verdade, ele questiona j. Sendo j um valor verdadeiro
ou não, logo após a sua validação, o programa questionará k, que tem um else próprio e é feito
seu tratamento. Finalmente, se i foi falso o seu else correspondente trataria da situação.
Lembrando que o uso de else é opcional ou se for necessário seu tratamento. Observe o mesmo
tratamento com o cascacateamento e perceba a diferença:
if (i) …;
else if(j) ... ;
else if (k) { ...}
else ... ;
/* ao ser executado qualquer um dos ifs, o programa seguiria a partir daqui*/
...

Perceba que somente verificar-se-á j se i apresentar um valor falso (entenda zero, por falso), e k
se i e j forem falsos e assim sucessivamente. O mais importante: ao ser cumprida uma validação
do cascateamento, o programa não permanece mais preso a ele e prossegue a partir do “bloco if-
else-if” .

SWITCH
Esta é uma solução mais organizada que os ifs aninhados. É usado quando se tem muitas
possibilidades no valor de uma expressão. Trata-se no switch também o caso de não ser
cumprida nenhuma das possibilidades esperadas. São usados, o que são chamados de
operadores de rótulo (pois atuam sobre uma marca. Neste caso um valor), dois outros comandos:
o break e o default. Veja o protótipo.
switch (expressão) {
case valor_constante1 :
comando;
comando;
...
break;

case valor_constante2 :
comando;
comando;
...
break;
...
default : comando;
comando;
...
}

A expressão é avaliada e é verificado se seu valor retornado está enquadrado em algum dos
casos (case). Dessa forma, são executados os comandos referentes aquele caso linha por linha.
E, se o retorno da expressão não se enquadra em nenhum caso, recomendamos a utilização do
default para tratar isso. Para evitar que o compilador execute comandos referentes a outro caso
(fall-through), utiliza-se o comando break, que desvia a execução do programa à linha seguinte ao
fim do bloco (prioritariamente, laços).

É possível perceber que em cada caso pode haver um comando ou vários, porém como usamos
o break não há a necessidade de chaves para o bloco de comandos e o uso delas não influi no
comportamento do compilador. Veja um exemplo de switch:

Críticas e sugestões: consultjnr@gmail.com Página 26


switch (opcao) {
case 1:
// usa-se system("cls"); para limpar a tela
system("cls");
printf("\n\nOpcao escolhida: 1");
break;
case 2:
system("cls");
printf("\n\nOpcao escolhida: 2");
break;

case 3:
system("cls");
printf("\n\nOpcao escolhida: 3");
break;
case 4:
system("cls");
printf("\n\nOpcao escolhida: 4");
break;
// opção padrão
default: system("cls");
} /*fim do bloco do switch*/

LAÇO
Comandos usados quando é necessário repetir uma operação várias vezes em tempo de
execução.

FOR
Quando se quer fazer um laço com controle claro de final, geralmente, se usa um for para isso,
pois sua leitura exige pouco já que agrega informações cruciais para a sua execução. Veja o
protótipo:
for (comando1; comando2; comando4) comando3;
/*para entender, siga o número dos comandos*/

A posição do comando no for é importante para a execução correta do mesmo. Cada um tem
uma função específica e é opcional o seu preenchimento. Porém não usar corretamente os
comandos de laço pode gerar o que chamamos de “loop infinito”, que é a execução ininterrupta
de uma mesma porção de código.

O primeiro comando no for é a inicialização. Aqui provavelmente se indicará qual o início de uma
faixa de valores ou condições. No segundo comando existe a expressão condicional, que
determina até quando será executado o comando ou o bloco de comandos interno ao for
(comando3). Já no terceiro (comando4) ocorre o incremento/decremento, que possibilitará atingir
a condição informada no comando dois. Ou seja o for executa a operação interna de uma
condição inicial até uma condição terminal. Veja exemplos de for:
int i;
for (i = 1; i <=100; i++) /*como não há um bloco se dispensam as chaves*/
if (i % 3 == 0) printf(“%d, “, i);

Neste trecho se imprime os múltiplos de 3 até 99.


for (i = 100; i >1; i--) /*como não há um bloco se dispensam as chaves*/
if (i % 3 == 0) printf(“%d, “, i);

Críticas e sugestões: consultjnr@gmail.com Página 27


Aqui se faria o inverso. Nos comandos do for, podemos inicializar mais de uma variável, fazer
mais de um incremento. Para isso usamos vírgula. Também se pode condicionar com uma
expressão mais complexa.
int i, j, n;
for ( i = 1, n = 4; i <= n; i++){
for ( j = 1; j <= i; j++){
printf (“%d “, i + j);
}
printf("\n");
}

WHILE E DO-WHILE
Os “enquantos”, geralmente são usados quando não se necessitam muitos condicionantes numa
operação. O while, em particular é a mais usada estrutura de repetição. Protótipos:
while (expressão) comando;

Ou
while (expressão) {
bloco_de_comandos;
}

/*Protótipo geral do “faça-enquanto”*/


do {
comando;
}
while (condição);

o while (enquanto) é formalmente chamado de laço de verificação inicial. É possível encontrar o


apelido de “verificação de cabeça” (na cabeça, topo do bloco). Consiste em se validar uma
condição inicial, sendo verdade, se repetir uma operação até que a condição não seja mais
satisfeita. Logicamente, se a condição não é satisfeita de início, o bloco não é executado.
Exemplo:
int a = 100;
while (a>0) a--;
/*Como só há um comando intero ao enquanto, não precisamos das chaves. Certo?*/

PERGUNTA: Qual será o valor de a depois que sair do while?


char alfa = 'a';
while (alfa != 'z') {
printf("%c , ", alfa);
alfa++;
}

Já o do-while é uma variação do while comumente utilizado. É empregado quando precisamos


fazer uma operação antes dela ser validada e assim continuar testando sua condição até que
essa operação não satisfaça mais a condição.

Pode-se representar os for à mesma estrutura de um while:

Críticas e sugestões: consultjnr@gmail.com Página 28


int i= 1;
while ( i <=100) {
if (i % 3 == 0) printf("%d\n", i);
i++;
}

LEMBRETE: Qual dos dois usar é opção do programador. É importante salientar que qualquer
estrutura de repetição precisa de condições claras de parada. Havendo o engano com uma
condição o programa executará infinitamente o mesmo bloco (loop infinito). Cuidado!

DESVIO

BREAK
Este é usado, como já comentado antes, na parada brusca de um laço ou switch:
int i = 1;
while ( i <=100) {
if (i > 15) break;
if (i % 3 == 0) printf("%d\n", i);
i++;
}

Neste caso, não ocorreria a impressão dos múltiplos de 3 após o número 15.

CONTINUE
Pouco explorado, o continue força a execução de mais uma repetição no laço (quando usado no
fim do laço, próximo às chaves):
for (i= 100; i >1; i--) {
if (i % 3 == 0) printf(“%d, “, i);
else continue;
}
Geralmente é usado para poupar ao programa outras verificações antes da repetição.

INTRODUÇÃO AOS TIPOS ESTRUTURADOS DE


DADOS
Agora falaremos rapidamente sobre os tipos estruturados de dados mais usados: vetores,
matrizes e strings

VETORES
São um conjunto contíguo de memória de dimensão única que agrega elementos de um mesmo
tipo. Declaração:
tipo nome_do_vetor[TAMANHO];

Exemplo:
float mensalidades[50];

Críticas e sugestões: consultjnr@gmail.com Página 29


Acima está um vetor com 50 elementos. Podemos criar TEDs com valores iniciais:
float mensalidades[50] = {1,2,3};

As três primeiras posições deste vetor foram inicializadas. Ainda, pode-se criar um TED sem
tamanho definido. Porém é exigida uma inicialização na declaração.
float mensalidades[] = {1,2,3};

Pode-se dizer que o vetor tem tamanho 3, mas isso é hipotético.

LEMBRETE: Os limites de cada TED devem ser controlados pelo programador. Se o programa
ultrapassar um limite durante sua execução, pode haver erros graves. A linguagem C não lida
com validações de limites de estruturas. Então, tenha cuidado. Somente crie TEDs sem tamanho
conhecido se realmente necessário e fique de olho nas validações que você deverá fazer para
que não ocorram erros.

ANTES, A INDEXAÇÃO
Como acessar elementos dentro de um TED? Uma forma eficiente e clara é a indexação.
Consiste em usar o número das posições (iniciando em zero) para acessar o elemento. Para isso
usamos os colchetes com valores constantes ou variáveis (da mesma forma que denominamos o
tamanho). Exemplo

float mensalidades[3] = {1,2,3};

Temos que:

float[0] == 1; /*a primeira posição de um vetor é indexada em 0*/

float[1] == 2;

float[2] ==3;

LEMBRETE: Perceba que o último índice é 2. Então todo e qualquer tamanho definido a um TED
é acessível de 0 ao tamanho-1. Isso é muito importante.

Veja um exemplo de acesso a vetor com um for:


Int vetor[100];
int i = 1;
for (i = 0; i < 100; i++) vetor[i] = i + 1; /*vetor [0] = 0 + 1... vetor[5] =
5+1...*/

Aqui, preenchemos o vetor com o valor dos índices somado a 1, de modo que:
vetor[0] == 1;
vetor[1] == 2;
vetor[2] == 3;
...
vetor[99] == 100;

LEMBRETE: Mesmo que tenha declarado um tamanho, um algoritmo que ultrapasse esse
tamanho, em C, não será barrado. O que pode fazer o erro perceptível é a parada abrupta do
programa por tentar acessar uma área de outro.

Críticas e sugestões: consultjnr@gmail.com Página 30


MATRIZES
São a agregação contígua de elementos do mesmo tipo (vetor), com várias dimensões. Para
ajudar, pense abstratamente nas dimensões de uma piscina: profundidade, largura, comprimento.
Declaração:
tipo nome_da_matriz[tamanho_1][tamanho_2]...[tamanho_n];

Alguns compiladores permitem um número limitado de dimensões. A utilidade de se ter várias


dimensões é exemplificada pela representação computacional de um mapa hipotético.
Inicialmente imaginamos os dados em duas dimensões: linha (tamanho1) e coluna (tamanho2). A
relação dos índices nos dará a posição do corpo no mapa.
char mapa[180][360]; /*graus no mapa mundi*/
/*latitude (linha), longitude (coluna), */

onde: mapa [0][0], mapa[1][2], mapa [9][9], são posições na matriz. Ela deve ser acessada com os
índices relacionados.

Mas, podemos ser mais precisos nessa representação. Podemos incluir os segundos como
dimensão. Temos, então:
char mapa [181][361][60];

Aqui podemos referenciar, hipoteticamente, o objeto: mapa[50][110][26]. O índice relacionado tem


o conteúdo de uma variável do tipo do TED. Ou seja, Podemos colocar no nosso mapa apenas
um caractere por posição.

Para inicializar uma matriz de duas dimensões (mais utilizada), uma forma é percorrê-la é a
seguinte:
int i,j matriz [5][5];
for (i=0; i<5; i++)
for(j=0; j<5; j++)
matriz[i][j] = i * 5 + (i + 1);
/*não precisamos de chaves pois cada comando executa um comando. Mas não faz mal usá-
las.*/

STRINGS
São vetores do tipo char com dimensão única, finalizados com um caractere nulo („\0‟). Não há
uma implementação natural das strings em C, pois em outras linguagens existe um tipo string.
Como já manipulamos um vetor, como acima. Basta perceber que DEVE haver um caractere nulo
no fim se o programador quiser usar o vetor de char com funções destinadas à manipulação de
strings.

Essas funções estão informadas no cabeçalho string.h, que deve ser incluído ao seu código fonte
(#include <string.h>). Em http://www.cplusplus.com/reference/string/ há uma referência sobre
essas funções. Acostume-se com o inglês. Visite.

PONTEIROS
Sabemos que as variáveis locais, por exemplo, são destruídas após o termino da função aonde
ela foi declarada. E que não são acessíveis em outras funções. Ou seja, como sabemos, são

Críticas e sugestões: consultjnr@gmail.com Página 31


restritas ao seu escopo. Com os ponteiros podemos acessar locais na memória onde não se tem
acesso naturalmente.

Os ponteiros são variáveis que armazenam somente o endereço de memória do objeto


“apontado”.

A declaração é feita com relação ao tipo da variável a ser apontada. O ponteiro deve referenciar
sempre um objeto do mesmo tipo (com exceção do void).

Exemplo de declaração de ponteiro


char *c;
char ch = 'a';
c = &ch;

Ao se declarar um ponteiro para char se utiliza o operador * após o tipo. Alguns compiladores
permitem que este operador seja colocado logo após o nome, como char*.

RELEMBRANDO OS OPERADORES * e &


É usado o operador * quando:

se declara um ponteiro

se deseja acessar o conteúdo daquilo que é apontado pelo ponteiro.

Segundo o exemplo acima podemos acessar o valor 'a' através do ponteiro se usarmos: „*c‟.

assim podemos alterar o valor da variável ch sem usá-la, isso chama-se desreferenciamento do
ponteiro:
char *c;
char ch = 'a';
c = &ch; /*a atribuição do endereço de ch, a c*/
*c = 'b'; /* o valor de ch foi alterado a partir dessa linha */

O operador & é usado quando se deseja obter o valor do endereço na memória de uma variável
(por exemplo):
char *c;
char ch = 'a';
c = &ch;

Deve-se lembrar que esse operador é geral e seu propósito não se limita aos ponteiros. Você
pode obter o endereço de memória de qualquer variável.

O foco principal dado aos ponteiros é o de poder acessar locais de memória em tempo de
execução, dinamizando o seu programa. Para alguns dos dados que podem ser acessados, são
usadas as funções de alocação dinâmica.

MÚLTIPLA INDIREÇÃO
É criar ponteiros para ponteiros (e assim sucessivamente) de um determinado tipo.

A notação para múltipla indireção é:


Tipo de dado **nome_ponteiro /* ** para ponteiro-de-ponteiro, *** para ponteiro-de-
ponteiro-deponteiro... e assim sucessivamente. */

Críticas e sugestões: consultjnr@gmail.com Página 32


Exemplo:
char** ch;

Diferentemente dos ponteiros, guardarão um endereço de memória de um outro ponteiro para


aquele tipo. É pouco utilizado, porém essencial para tratar de posições de memória que precisam
ser destruídas e criadas sobre estruturas já alocadas.

Ou seja, os apontadores para apontadores guardam endereços de memória de um ponteiro de


seu mesmo tipo.

Por Exemplo:
*ptrVetor[valor] // vetor de ponteiros

É considerado múltipla indireção. Uma vez que o nome do vetor puro (rótulo, ptrVetor) é um
ponteiro para a primeira posição do vetor (que também é um ponteiro).

Código Exemplo:

ARITMÉTICA DE PONTEIROS
Expliquemos rapidamente: O C reconhece comandos que comparam e operam ponteiros. Usado
geralmente na manipulação de vetores de maneira prática e concisa.

Quando se usam os operadores aritméticos, ocorre a relação com o tipo. Move-se a quantidade
de bytes de cada tipo quantas vezes for informado pelo programador através da operação de
soma ou subtração (*(p+7) = acesso em “7 vezes bytes do tipo” ao longo de um tipo estruturado).
Uso recomendado em vetores.

Ao usar operadores relacionais, o que se compara são as posições de memória. Ao questionar se


um ponteiro é igual a outro (ptr1 == ptr2), por exemplo, só seria verdade se os dois apontassem
para o mesmo objeto (posição de memória) ou se estivessem nulos.

Críticas e sugestões: consultjnr@gmail.com Página 33


FUNÇÕES
São blocos de código com instruções e variáveis próprias. O main é uma função, por exemplo.
Eis o protótipo:
tipo nome_da_função (tipo nome_parâmetro1, tipo nome_parâmetro2, ... tipo
nome_parâmetro_n)
{
declarações locais;
sequência_de_comandos;
}

Por questões de organização mantenha sempre um protótipo de sua função declarando-a antes
do main ou num arquivo de cabeçalho. Segue o protótipo geral:
tipo nome_da_função (tipo_parâmetro1, tipo_parâmetro2, ... tipo_parâmetro_n);

Onde:

Não se escreve o nome do parâmetro de entrada na prototipação;

Funções sem parâmetros podem usar parêntesis vazios ou a palavra void entre eles.

Muitas vezes, ao realizar um processamento, uma parte de um programa precisa de um dado


feito ou tratado em outra parte. Necessitamos então entender bem algumas coisas concernentes
às funções.

RETORNO DE UMA FUNÇÃO


Com o uso da palavra reservada return se diz à função que ela chegou ao seu fim. A função deve
retornar um elemento ou resultado de expressão do tipo informado na abertura. Pode ocorrer
coerção automática nesse caso, mas sempre se recomenda clareza e objetividade. Pense num
compromisso: sua função promete entregar algo de um tipo no início e acaba por entregar outro.

As funções void, como não retornam valor (pelo nome void, vazio) não é necessário usar a
palavra return.

RELEMBRANDO PARÂMETROS
Lembra-se deles? Variáveis com comportamento local? Pois bem. Essa será a entrada dos dados
que a sua função precisa tratar. Por exemplo, uma função de somar dois inteiros:

#include <stdio.h>
#include <stdlib.h>
int soma (int, int);
int main (void) {
int a, b;

printf(“digite um número a: ”); /* impressão em tela*/


scanf(“%d”, &a); /*função de atribuição solicitada ao usuário*/
fflush(stdin);
printf(“digite um número b: ”);
scanf(“%d”, &b);
fflush(stdin);
printf(“a + b = %d\n”, soma (a, b)); /*Aqui, se utilizará o valor de retorno da
função*/

Críticas e sugestões: consultjnr@gmail.com Página 34


system (“PAUSE”);
return 0;
}

int soma (int x, int y) {/*não haveria problema se os nomes dos parâmetros fossem
iguais aos do main*/
return (x + y); /*se retorna o valor inteiro resultado da expressão*/
}

A função soma recebe dois parâmetros inteiros (x e y) e retorna a sua soma à posição requerida
no main. Cabe ao programador realizar todas as validações possíveis em seu programa. O valor
da soma poderia ser armazenado em outra variável, mas isso gasta tempo e memória com a
alocação de uma nova variável e sua atribuição.

PASSAGEM DE PARÂMETROS
É o ato de entrar valores para tratamento numa função. São feitas passagens de duas formas:
cópia (valor), onde não ocorre alteração do valor original do dado na função chamadora e por
referência (ponteiros) onde isso ocorre. Veja o programa de soma:
#include <stdio.h>
#include <stdlib.h>
int soma (int, int);
int main (void) {
int a, b;

printf(“digite um número a: ”); /* impressão em tela*/


scanf(“%d”, &a); /*função de atribuição solicitada ao usuário*/
fflush(stdin);
printf(“digite um número b: ”);
scanf(“%d”, &b);
fflush(stdin);
printf(“a + b = %d\n”, soma (a, b)); /*Aqui, se utilizará o valor de retorno da
função*/
system (“PAUSE”);
return 0;
}

int soma (int x, int y) {/*não haveria problema se os nomes dos parâmetros fossem
iguais às do main*/
return (x + y); /*se retorna o valor inteiro resultado da expressão*/
}
Neste caso acima, o valor de a e b foi “copiado” para x e y respectivamente na função soma. Mas
se quisermos alterar o conteúdo de uma variável fora do âmbito da função? Coisa impossível?
Ponteiros são a solução do problema.

LEMBRETE: A passagem de parâmetros pode ser considerada uma atribuição entre as variáveis
passadas e os parâmetros de entrada. No caso acima, x = a; e y = b; caracterizariam esse
processo. Perceba que assim como uma atribuição podem ser passados resultados de
expressões, retornos de funções ou até uma coerção para um tipo esperado do outro lado.

PASSAGEM POR CÓPIA


Acabamos de definir a “passagem por valor” ou “por cópia”, quando informalmente se percebeu
que o programa cria uma estrutura igual a passada à função para que a mesma possa usar os
dados. Prova:
Críticas e sugestões: consultjnr@gmail.com Página 35
#include <stdio.h>
#include <stdlib.h>
int soma (int, int);
int main (void) {
int a, b;

printf(“digite um número a: ”); /* impressão em tela*/


scanf(“%d”, &a); /*função de atribuição solicitada ao usuário*/
fflush(stdin);
printf(“digite um número b: ”);
scanf(“%d”, &b);
fflush(stdin);
printf(“a + b = %d\n”, soma (a, b)); /*Aqui, se utilizará o valor de retorno da
função*/
printf(“valor de a = %d | valor de b = %d\n”, a, b);
system (“PAUSE”);
return 0;
}

int soma (int x, int y) {/*não haveria problema se os nomes dos parâmetros fossem
iguais aos do main*/
x += y +1;
y *= x;
printf(“valor de x = %d | valor de y = %d\n”, x, y);
return (x + y); /*se retorna o valor inteiro resultado da expressão*/
}
Pronto. Se perceberá que o valor de a e b não sofreu alteração alguma.

PASSAGEM POR REFERÊNCIA


É usar como parâmetros de entrada, endereços para entidades fora da função. Os ponteiros são
os únicos que podem direcionar posições de memória através de endereços. Portanto é declarar
ponteiros como parâmetros e desreferenciá-los na alteração de uma variável. Vamos alterar o
valor de a e b:
#include <stdio.h>
#include <stdlib.h>
int soma (int *, int *); /*mudamos o protótipo para receber ponteiros para inteiros*/
int main (void) {
int a, b, *ptra; /*declaramos somente um ponteiro para mostrar as duas formas de
referenciar as variáveis*/

printf(“digite um número a: ”); /* impressão em tela*/


scanf(“%d”, &a); /*função de atribuição solicitada ao usuário*/
fflush(stdin); /*limpa o buffer do teclado*/
ptra = &a;
printf(“digite um número b: ”);
scanf(“%d”, &b);
fflush(stdin);
printf(“a + b = %d\n”, soma (ptra, &b)); /*passamos um ponteiro, que passará seu
valor e o endereço de b*/

printf(“valor de a = %d | valor de b = %d\n”, a, b);


system (“PAUSE”);
return 0;
}

int soma (int *x, int *y) {/*x e y são ponteiros agora*/

Críticas e sugestões: consultjnr@gmail.com Página 36


(*x) += y +1;
(*y) *= x;
printf(“valor de x = %d | valor de y = %d\n”,* x, *y);
return (x + y); /*se retorna o valor inteiro resultado da expressão*/
}

DICA: numa função que recebe ponteiros, você pode fazê-lo por cópia. Simples: o valor do
ponteiro é copiado. Qual o valor esperado de um ponteiro? Um endereço, uma vez copiado,
desreferenciações simples funcionam muito bem.

Pronto. Alteramos ou não o valor das variáveis inteiras? As duas formas usadas foram: transferir
o valor do ponteiro por cópia ou passar diretamente a referência da entidade.

PONTEIROS E TED
Podemos usar ponteiros para acessar e manipular (sem indexação) vetores e strings. Assim
como podemos declarar TED de ponteiros como faríamos a qualquer outro tipo. Para isso
usamos a aritmética de ponteiros.

LEMBRETE: É interessante saber que todo o rótulo de vetor (e string também) é um ponteiro
constante (sem possibilidade de ser alterado) para o início da área do mesmo. Ou seja:

Ao atribuir um vetor a um ponteiro há duas formas:


char *uneb;
char si[500];
uneb = si;
Como são dois ponteiros, o compilador não exprime alerta de sintaxe
uneb = &si[0];

Ao usar aritmética de ponteiros para percorrer o vetor si usando uneb há também duas formas:
/*preservando a referência ao primeiro elemento*/
*(uneb + 1);

Como uneb aponta para o primeiro elemento de si, dessa forma eu acesso o segundo elemento.
Veja: se uneb aponta a si[0], uneb +1 aponta a si[0+1] ou si[1]. Para o quarto elemento usaríamos
*(uneb + 3) e assim sucessivamente.
/*Atualizando o início da referência*/
uneb++;

Críticas e sugestões: consultjnr@gmail.com Página 37


Antes apontando para o primeiro elemento, uneb foi atualizado ao segundo. Na proposição
anterior não ocorreria mudança do valor de uneb, pois se tratava de uma expressão entendida
como comparação. Perceba que uneb a partir de agora será diferente de si[0] e seu tamanho é
menor.

LEMBRETE: Não esquecer do desconhecimento de limites do C com relação a esses tipos. Não
é feita pela linguagem validação quanto ao espaço percorrido através da estrutura. isso é papel
do programador e é crucial para construção de um bom programa.

ALOCAÇÃO DINÂMICA
A alocação dinâmica permite a criação de dados na área destinada a isso (heap) na memória do
computador. Usa-se a função malloc, que retorna um ponteiro do tipo void que pode ser
assignado a qualquer tipo de ponteiro se a alocação foi bem sucedida e NULL (nulo), se não. E
free que libera a área alocada previamente. Eis os exemplos de uso:
char* p = malloc(1000);
/* o parâmetro de entrada de malloc é a quantidade de bytes que será alocada. Essa
função devolve endereço do primeiro desses bytes.*/
int *in = malloc(8);

A mesma coisa acontece aqui, mas como em alguns computadores um inteiro ocupa 4 bytes
estão alocados dois. O que fazer para não corrigir seu código em cada PC que usar? faça uso de
uma função chamada sizeof que recebe um tipo ou número e retorna o tamanho em bytes usado
no PC. Assim o seu programa fica portabilizado, usável em qualquer PC. Seguem exemplos:
char* p = malloc(1000 * sizeof(char)); /*alocando espaço para 1000 caracteres*/

char* p = malloc(sizeof(char)); /*agora para somente um*/

int *in = malloc(2 * sizeof(int)); /*alocando espaço para dois inteiros*/

Ressalva-se a importância do mantimento dessas referências. Como são áreas de memória, se


você atualizar os ponteiros, perderá o acesso á essa área. Cuidado!

Para liberar o espaço que foi alocado anteriormente usa-se free que recebe um ponteiro void para
a área alocada. Dessa forma:
free (p); ou free (in); /*marca-se, na memória principal como sem uso e, portanto
poderá ser sobreescrito*/
Exercícios

Com vistas ao comentado anteriormente em vetores sobre o desconhecimento de C acerca de


limites dos TED, é seguro utilizar a função gets? Existe alternativa?

Construa um trecho de código que transforme uma string minúscula para maiúscula. Sem o uso
de funções. Dica: lembre-se da tabela ASCII e o do poder de se referir a um caractere como um
int, e dos mecanismos de coerção. (se não conseguir, dê uma olhada na seção exercícios
resolvidos).

ESTRUTURAS
São a agregação de tipos permitindo criar o modelo de um novo tipo de elementos. É usado
quando se intenciona ter vários tipos de informação numa mesma entidade. Entenda que seu uso

Críticas e sugestões: consultjnr@gmail.com Página 38


pode ser feito em várias outras formas, mas a sua natureza é a mesma: Reunir vários tipos a fim
de usar, num só ponto, todos eles. Veja o protótipo básico de uma Estrutura:
struct nome_da_estrutura {
tipo_de_dado nome_da_variável;
tipo_de_dado nome_da_variável;
tipo_de_dado nome_da_variável;
tipo_de_dado nome_da_variável;
...
} variáveis_criadas_pos_declaração;

Declarando uma estrutura de exemplo:


struct pessoa {
char nome[80];
char cpf[11];
};
int main () {
...
return 0;
}

Note que não há declaração ou inicialização de nenhuma variável. Mas a criação de uma
estrutura é um comando como qualquer outro. Portanto o ponto-e-vírgula no final.

É possível criar uma variável do tipo struct pessoa logo após a declaração, antes do ponto e
vírgula final:
struct pessoa {
char nome[80];
char cpf[11];
}Joao, Elias, Jose;
/* Separamos por „virgula‟ elementos da estrutura. Para que não haja confusão com
funções ou variáveis que você criar no seu programa, use pelo ou menos a Primeira Letra
em Maiúsculo.*/

Lembrando que, como toda estrutura é declarada fora das funções ela é acessível em todo o
programa, e qualquer elemento dessa estrutura declarado dessa forma será global (assim como
as variáveis). Podemos criar elementos de uma estrutura em qualquer parte do programa.
Assuma como a declaração de qualquer variável. Observe:

int main () {
...
struct pessoa Novo;
return 0;
}
O acesso aos “campos” de uma estrutura local é feito a partir do operador „.‟. Então podemos
inicializar a estrutura da seguinte maneira:
strcpy (Novo.nome, “ConsultJR”); /*ambas funções para manipulação de strings*/

strcpy (Novo.cpf,”12345678909”);

Críticas e sugestões: consultjnr@gmail.com Página 39


Os ponteiros podem ser usados para apontar para uma estrutura assim como é feito com
qualquer tipo, mas para o acesso aos campos é usado um operador diferente, é o operador seta ,
„->‟. Veja o exemplo:
int main () {
struct pessoa *p;
if ((p = (pessoa*)malloc(sizeof(pessoa))) == NULL){ /*feito o cast do ponteiro
void para ponteiro pessoa*/
printf("SEM MEMORIA\n");
system("PAUSE"); /*pausa o sistema*/
exit(1); /*sai do programa*/
}

strcpy(p->nome, “ConsultJR”);
strcpy(p->cpf,”12345678909”);
...
return 0;
}

DICA: Podem ser usadas estruturas como qualquer outro tipo. Podemos declarar vetores,
matrizes e estruturas de estruturas. Pratique!

O TYPEDEF
É usado quando, seja para a clareza ou estética do código, se necessita renomear um tipo de
dado. Lembremos que as structs podem ser consideradas tipos e, por conseqüência, sofrem ação
do typedef.
struct aluno {
char nome[80];
int cod_turma;
...
int cod_curso;
char nome_curso[80];
};/*não esquecer do ponto-e-vírgula*/
/*Agora usamos o typedef para deixarmos de usar o „struct aluno‟ toda vez que fizer
referência à estrutura*/

typedef struct aluno Aluno;


/*quando fizer referência à estrutura chamamos Aluno, a partir de agora*/
Ou, com relação aos tipos:
typedef float Real;
typedef unsigned char UnsChar;
typedef int* PtrInt;
typedef float Vetor[4]; /*declara-se aqui um vetor com 4 posições do tipo Vetor:
Vetor[0], Vetor[1] ...*/

Então podemos usar os apelidos dados para economizar em declarações, mas ressalva-se a
importância de um código claro, que não seja necessário verificar a declaração toda a vez que se
queira entender o real teor daquilo.

Podemos combinar o uso do typedef com a declaração da struct. Dessa forma:


typedef struct aluno {
char nome[80];
...
}Aluno; /* aqui ocorre a associação do nome Aluno à estrutura*/

Críticas e sugestões: consultjnr@gmail.com Página 40


O typedef deve ser declarado junto à estrutura, ou seja, antes do main do seu código, ou num
arquivo de cabeçalho, com fins de que em todo o código que se analise se encontrem as
informações com uma arrumação padronizada.

ARQUIVOS
Antes de trabalharmos com arquivos, é importante saber diferenciar as streams dos arquivos.

Uma stream é um dispositivo lógico que representa um arquivo ou dispositivo. A stream é


independente do arquivo ou dispositivo. Devido a isso, a função que manipula uma stream pode
escrever tanto em um arquivo no disco quanto em algum outro dispositivo, como o monitor.

Existem dois tipos de streams: texto e o binária.

STREAM TEXTO
Uma stream de texto é uma sequência de caracteres que pode ou não conter um caractere de
quebra de linha. O padrão C ANSI não exige que as linhas sejam organizadas com quebra de
linha. Outro fato importante a observar é que não é necessário na última linha um caractere de
final de linha.

Nem sempre a tradução entre a representação do caractere no fluxo de texto e no sistema de


arquivos do computador hospedeiro é um para um, ou seja um caractere poder ter tradução
diferente em sistemas diferentes.Por exemplo entre UNIX e DOS há uma diferença na
representação de final de linha que causa problemas na impressão de arquivos.

STREAM BINÁRIA
Uma stream binária é composta por uma seqüência de bytes lidos, sem tradução, diretamente do
dispositivo externo. Não ocorre nenhuma tradução e existe uma correspondência um para um
entre os dados do dispositivo e os que estão no arquivo.

MANIPULANDO ARQUIVOS
Para utilizar um arquivo você deve associá-lo a uma stream e, então, manipulá-la. Os arquivos
são associados a uma stream através de uma operação de abertura (fopen) e desassociados por
uma operação de fechamento (fclose) que serão vistos posteriormente.
Função Descrição
fopen( ) Abrir um arquivo
fclose( ) Fechar um arquivo
putc( ) Escrever um caracter em um arquivo
fputc( ) O mesmo que putc( )
getc( ) Ler um caracter de um arquivo
fgetc( ) O mesmo que getc( )
fseek( ) Posicionar o ponteiro de arquivo num byte específico
fprintf( ) É para o arquivo o que printf é para o console
fscanf( ) É para o arquivo o que scanf é para o console
feof ( ) Devolve verdadeiro se o fim do arquivo foi atingido
ferror( ) Devolve verdadeiro se ocorreu um erro
rewind( ) Posicionar o ponteiro de arquivo no início deste
remove( ) Apagar um arquivo
fflush( ) Descarregar um arquivo

Críticas e sugestões: consultjnr@gmail.com Página 41


O PONTEIRO DE ARQUIVO
Basicamente um ponteiro de arquivo identifica um arquivo específico e é usado pela stream para
direcionar as operações das funções de E/S. Um ponteiro de arquivo é uma variável ponteiro do
tipo FILE. Esta variável é um tipo pré-definido pela linguagem C na biblioteca <stdio.h>. Para ler
ou escrever em arquivos seu programa precisa usar os ponteiros de arquivo. Para declarar uma
variável como ponteiro de arquivo use a seguinte sintaxe:
FILE *arq;

“arq” será o ponteiro para um arquivo

ABRINDO UM ARQUIVO
Para abrir um arquivo usa-se a função fopen, essa função faz a associação do arquivo a uma
stream retornando um ponteiro de arquivos associado que será aberto. Um arquivo pode ser
aberto de diversas maneiras: leitura, escrita, leitura/escrita, adição de texto, etc.

A função fopen tem o seguinte protótipo:


FILE *fopen (char* nome_do_arquivo, char *modo);

O primeiro parâmetro “nome_do_arquivo” é um ponteiro que aponta para uma cadeia de


caractere que forma o nome do arquivo ou local onde o arquivo se encontra ou se for criado, onde
o arquivo deve ser armazenado.

O segundo parâmetro “*modo” especifica como o arquivo deve ser aberto.

Obs:Se não for informado o caminho absoluto do arquivo (c:\diretório) será criado no diretório
onde o programa esta sendo executado.

Abaixo uma tabela mostrando os modos possíveis:


Modo Descrição
r Abre um arquivo texto para leitura
w Cria um arquivo texto para escrita
a Adiciona texto ao fim de um arquivo texto
rb Abre um arquivo binário para leitura
wb Abre um arquivo binário para escrita
ab Anexa a um arquivo binário
r+ Abre um arquivo texto para leitura e escrita
w+ Cria um arquivo texto para leitura e escrita
a+ Cria ou anexa a um arquivo texto para leitura e escrita
r+b Abre um arquivo binário para leitura e escrita
w+b Cria um arquivo binário para leitura e escrita.
a+b Anexa a um arquivo binário para leitura e escrita

Exemplo: Para abrir um arquivo com o nome de tutorial.txt para que permita escrita pode digitar:
FILE *arq = fopen(“tutorial.txt”,w);

Obs: Caso o arquivo tutorial.txt ainda não exista ele será criado pronto para escrita, caso ele já
exista ele será sobrescrito. Se você deseja escrever a partir do final do arquivo existente você
deve abrir no modo de adicionar texto (a) no final do arquivo.

Críticas e sugestões: consultjnr@gmail.com Página 42


FECHANDO UM ARQUIVO
Um arquivo deve ser fechado com a função fclose() cujo protótipo é:
int fclose (FILE *arq);

onde arq é um ponteiro de arquivo para o arquivo que deve ser fechado. Um valor zero de retorno
significa que a operação foi executada com êxito, qualquer outro valor implica em erro.

LENDO E ESCREVENDO EM UM ARQUIVO

ESCREVENDO UM CARACTERE:
Para escrever um caractere em um arquivo previamente aberto utiliza-se a função putc() ou
fputc().

Protótipo:
int putc (char c,FILE *arq);

Onde c é o caractere a ser escrito no arquivo apontado por arq. O mesmo protótipo serve para
fputc()

LENDO UM CARACTERE DO ARQUIVO


Para ler um caractere de um arquivo utiliza-se a função getc() ou fgetc()

Protótipo:
int getc(FILE *arq);

A função lê o caracter como um unsigned char mas retorna o valor como um inteiro, onde o byte
mais significativo vale zero. A função devolve o código EOF ao chegar ao final do arquivo. O valor
EOF também é um inteiro válido e, portanto ao usar arquivos binários é necessário que a função
feof() seja utilizada para verificar o final do arquivo.

feof()
Às vezes é necessário verificar se um arquivo chegou ao fim, para isto podemos usar a função
feof().

Esta função retorna diferente zero se o arquivo chegou ao EOF, caso contrário retorna zero.

EOF (“End of file”) – indica o fim de um arquivo. Protótipo:

int feof (FILE *arq);

Onde arq é o ponteiro que aponta para o arquivo a ser lido

LENDO E ESCREVENDO UMA CADEIA DE CARACTERES


Para escrever uma cadeia de caracteres em um arquivo utiliza-se a função fputs()cujo protótipo é:
int fputs(char *str, FILE *arq);

Para escrever a cadeia de caracteres apontada por str no arquivo apontado por arq.

Críticas e sugestões: consultjnr@gmail.com Página 43


Para ler uma cadeia de caracteres de um arquivo utiliza-se a função fgets() cujo protótipo é:
char fgets(char *str, int comp, FILE *arq);

Essa função lê uma cadeia de caracteres do arquivo apontado por “arq” até que um caracter de
nova linha seja encontrado ou comp-1(comprimento) caracteres sejam lidos. Se bem sucedida a
função retorna um ponteiro para “str” caso contrário retorna NULL.

fprintf() e fscanf()
As funções fprintf() e fscanf() são equivalentes às funções printf() e scanf(), sendo a única
diferença o fato de que elas trabalham com fluxos de dados (arquivos).O protótipo das duas
funções são:
int fprintf(FILE *arq, const char *formatacao, ...);

Onde “FILE *arq” – o arquivo a ser escrito e “const char *formatacao” - o que será escrito.
int fscanf(FILE *arq, const char *formatacao, ...);

Onde “FILE *arq” – o arquivo a ser lido e “const char *formatacao” – qual a variável que receberá
os dados do arquivo.

Exemplo:
Char x[50], c;

FILE *arq = fopen(“Text.txt”,w);


printf(“digite o nome do arquivo”);
gets(x); //Lê o nome do arquivo e atribui a x
fprintf(*arq,%s,x); //Imprime o conteúdo de x no arquivo

FILE *arq = fopen(“Text.txt”,r);


while (!feof(arq)){ //Enquanto não chega ao final do arquivo
fscanf(arq,"%c",&c); //atribui o caractere lido do arquivo a variável c
printf("%c",c); //imprime o valor da variável c na tela
}
fclose(arq);

LENDO E ESCREVENDO EM ARQUIVOS BINÁRIOS


Para efetuar o processo de leitura e escrita em arquivos binários, utilizam-se as funções fread() e
fwrite() respectivamente. Os protótipos dessas funções são:

size_t fread (void *buffer, int numero_de_bytes, int count, FILE *arq);

- “void *buffer” – variável na qual serão armazenados os dados lidos.

- “int numero_de_bytes” – o número de bytes a ser lido.

- “int count” – indica quantas unidades devem ser lidas cada uma com comprimento
numero_de_bytes.

- “FILE *arq” – arquivo a ser lido.

Críticas e sugestões: consultjnr@gmail.com Página 44


size_t fwrite (void *buffer, int numero_de_bytes, int count, FILE *arq);

- “void *buffer” – variável na qual serão transmitidos os dados ao arquivo.

- “int numero_de_bytes” – o número de bytes a ser escrito.

- “int count” – indica quantas unidades devem ser escritas cada uma com comprimento
numero_de_bytes.

- “FILE *arq” – arquivo a ser escrito.

Obs:O tipo size_t está definido na biblioteca stdio.h e é equivalente a unsigned.

A função fread devolve o número de itens lidos, esse valor poderá ser menor que count se o final
do arquivo for atingido ou ocorrer algum erro.A função fwrite devolve o número de itens escritos,
esse valor será igual a count a menos que ocorra algum erro.

fseek()
Operações de escrita e leitura aleatória(randômica)pode ser realizadas com a ajuda do
fseek().Esta função move a posição corrente de leitura ou escrita no arquivo de um valor
especificado, a partir de um ponto especificado. Protótipo:
int fseek (FILE *arq, long num_bytes, int origem);

- FILE *fp” – arquivo a ser manipulado.

- “long numbytes” – indica quantos bytes o cursor de posição do arquivo será movimentado a
partir da sua posição atual.

- “int origem” – indica a partir de onde os num_bytes serão contados.

Abaixo uma tabela com os possíveis valores:


Nome Valor Significado
SEEK_SET Início do arquivo
SEEK_CUR Ponto corrente no arquivo
SEEK_END Fim do arquivo

Para mover num_byte do inicio do arquivo então origem deve ser SEEK_SET

Para mover num_byte a partir da posição atual do arquivo então origem deve ser SEEK_CUR

Para mover num_byte a partir do final do arquivo então origem deve ser SEEK_END

rewind
Retorna o indicador de posição do arquivo para o início dele. Protótipo:
void rewind (FILE *arq);

“FILE *arq” - arquivo a ser manipulado.

Críticas e sugestões: consultjnr@gmail.com Página 45


ferror()
Esta função determina de algum erro foi produzido na operação com arquivo.Ela retorna
verdadeiro se ocorreu algum erro caso contrario retorna falso. Protótipo:
int ferror(FILE *arq);

remove()
Esta função apaga o arquivo especificado, ela devolve zero se a operação foi bem sucedida ou
um valor diferente de zero caso contrário. Protótipo:
int remove(char*nome_do_arquivo);

fflush()
A função fflush escreve o que existe no buffer para o arquivo. O fluxo permanece aberto após a
execução da função. Retorna zero no caso de sucesso e EOF se ocorreu um erro. Protótipo:
int fflush (FILE *arq)

EXERCÍCIOS

QUESTÕES DE LÓGICA DE PROGRAMAÇÃO

Questão 1.

Uma empresa tem para um determinado funcionário uma ficha contendo o nome, número de
horas trabalhadas e o n0 de dependentes de um funcionário.

Considerando que:

a) A empresa paga 12 reais por hora e 40 reais por dependentes.

Sobre o salário são feito descontos de 8,5% para o INSS e 5% para IR.

Faça um algoritmo para ler o Nome, número de horas trabalhadas e número de dependentes de
um funcionário. Após a leitura, escreva qual o Nome, salário bruto, os valores descontados para
cada tipo de imposto e finalmente qual o salário líquido do funcionário.

Em Algoritmo:
Algoritmo salario
variaveis
nome : cadeia
dependente : inteiro
horas, salarioLiq, salarioBruto, DescontoINSS, DescontoIR : real;
Inicio
Leia(nome,horas, dependente);
salarioBruto <- (12 * horas) + (40 * dependente);
DescontoINSS <- (salarioBruto*0,085);
DescontoIR <-(salarioBruto*0,05) ;

Críticas e sugestões: consultjnr@gmail.com Página 46


salarioLiq <-(salarioBruto – ( DescontoINSS + DescontoIR));
Escreva(nome,salarioBruto, DescontoINSS, DescontoIR, salarioLiq);
Fim

Em C:
int main () {
char nome[50];
int dependente
float horas, salarioLiq, salarioBase, DescontoINSS, DescontoIR;

printf(“Escreva o nome”);
gets(nome);
printf(“Escreva a quantidade de horas”);
scanf("%f",&horas);
printf(“Escreva a quantidade de dependentes”);
scanf("%i",&dependente);
salarioBruto = (12 * horas) + (40 * dependente);
descontoINSS = (salarioBruto * 0,085);
descontoIR = (salarioBruto * 0,05);
salarioLiq = (salarioBase – (DescontoINSS + DescontoIR));
printf(“%s ,%f , %f, %f , %f”,nome,salarioBruto,DescontoINSS,DescontoIR,
salarioLiq,);

system("pause"); //Utilize este comando manter a janela do console aberta

return 0;
}

Questão 2.

Faça um algoritmo para ler a base e a altura de um triângulo. Em seguida, escreva a área do
mesmo.
Área = (Base * Altura ) / 2

Em Algoritmo:
Variaveis:
base, altura, area : real;
Inicio
Leia(Base)
Leia(Altura)
Area<- (Base * Altura)/2
Escreva (Area)
Fim

Em C
int main(){
float base, altura, area;
printf(“informe a base ”);
scanf(“%f”, &base);
printf(“informe a altura ”);
scanf(“%f”, &altura);
área = (base * altura)/2;

Críticas e sugestões: consultjnr@gmail.com Página 47


printf(“%f”, area);
system("pause");
return 0;
}

QUESTÕES EM C

1) Ler um número inteiro e imprimir seu sucessor e seu antecessor.

2) Informar tres numeros inteiros e imprimir a média

3) Ler um número inteiro e imprimir seu quadrado.

4) Ler um numero e se for maior que 20 imprimir a metade desse numero.

5) Ler um numero e imprimir: maior que 20, igual a 20 ou menor que 20.

6) Ler um ano de nascimento e ano atual. Imprimir a idade da pessoa.

7) Apresentar os quadrados dos números inteiros de 15 a 200

8) Crie um programa que receba uma palavra letras em minúsculas e escreva essa palavra em
letras maiúculas.

9) Apresentar todos os números divisíveis por 4 que sejam menores que 200.

10) Solicitar um nome e escrevê-lo de trás pra frente.

11) Preencher um vetor com números inteiros(8unidades); solicitar um número do teclado.

Pesquisar se esse número existe no vetor. Se existir,imprimir em qual posição do vetor. Se não

existir,imprimir MSG que não existe.

12)Preencher um vetor de 8 elementos inteiros. Mostrar o vetor e informar quantos números são

maior que 30, Somar estes números. Somar todos os números.

13)Preencher um vetor com 3 nomes com 20 letras no máximo cada. Imprimir os Nomes.

14)Função preencher Vetor, imprimir o Vetor, imprimir o quadrado, imprimir o primeiro e o

ultimo numeros

15)Criar uma função que receba um caractere como parâmetro e retorne 1 (um) caso seja uma

vogal e zero caso não seja.

16)Criar um algoritmo que leia os elementos de uma matriz inteira de 4 x 4 e imprimir os

elementos da diagonal principal.

17)Criar um algoritmo que leia os elementos de uma matriz inteira de 3 x 3 e imprimir todos os

elementos, exceto os elementos da diagonal principal.

Críticas e sugestões: consultjnr@gmail.com Página 48


18)Crie um programa para pedir o nome, sexo, altura e cor dos olhos de 5 pessoas.Se a pessoa
for do sexo masculino, informar a altura da pessoa, se for do sexo feminino, informar a cor dos
olhos.

RESOLUÇÕES DOS EXERCÍCIOS

1)
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<math.h>
#include<string.h>
main()
{
int x, n1, n2;
printf("\n\n Digite um numero: ");
scanf("%d",&x);
n1=x+1;
n2=x-1;
printf("\n\nSeu sucessor e : %d",n1);
printf("\n\nSeu antecessor e : %d",n2);
printf("\n\n");
system("pause");
return (0);
}

2)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
int main()
{
int a,b,c;
printf("Informe um numero inteiro: ");
scanf("%d",&a);
printf("Informe um numero inteiro: ");
scanf("%d",&b);
printf("Informe um numero inteiro: ");
scanf("%d",&c);
printf("A media dos tres numeros informados e: %4.2f\n\n",float((a+b+c))/3);
system("PAUSE");
return 0;
}

3)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
int main()
{
float a;
printf("Informe um numero inteiro: ");
scanf("%f",&a);

Críticas e sugestões: consultjnr@gmail.com Página 49


printf("O quadrado do numero informado e: %3.0f\n\n",pow(a,2));
// para usar a potencia, usa-se pow(numero, potencia)
system("PAUSE");
return 0;
}

4)
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main()
{
float numero;
printf("Informe um numero: ");
scanf("%f",&numero);
if (numero > 20)
printf("A metade desse numero e %3.2f", numero/2);
system("PAUSE");
return 0;
}

5)
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main()
{
float numero;
printf("Informe um numero: ");
scanf("%f",&numero);
if (numero > 20)
printf("\nNumero informado e maior a 20.\n");
else
if (numero = 20)
printf("\nNumero informado e igual a 20.\n");
else
printf("\nNumero informado e menor que 20.\n\n");
system("PAUSE");
return 0;
}

6)
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main()
{
char nome[30], sexo;
int idade;
printf("Informe seu nome: ");
gets(nome);
printf("Informe seu sexo: ");
scanf("%c",&sexo);
printf("Informe sua idade: ");
scanf("%d",&idade);
if (sexo == 'f' || sexo == 'F' && idade < 25)

Críticas e sugestões: consultjnr@gmail.com Página 50


printf("\n%s. ACEITA.\n\n", nome);
else
printf("\nNAO ACEITA.\n\n");
system("PAUSE");
return 0;
}

7)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
main()
{
int x,soma=0;
for(x=15; x<=200;x++)
printf("O quadrado do numero %d eh: %d\n",x, x*x);

/* Pode ser feito assim também – Usando While


x=15;
while (x <=200)
{
printf("O quadrado do numero %d eh: %d\n",x, x*x);
x=x+1;
}*/

system("pause");
return 0;
}

8)int main(){

char cadeia[10] ;
int i;
printf("escreva a palavra ");
gets(cadeia);

for(i = 0;cadeia[i] != '\0'; i++){


if(cadeia[i] >= 'a' && cadeia[i] <= 'z'){ //Verifica se conteúdo é um
caractere
cadeia[i] = cadeia[i] - 'a' + 'A'; // aqui fazemos os cálculos
utilizando
} // os valores
dos caracteres correspondente
printf("%c",cadeia[i]); // na tabela ASCII
}

printf("\n");
system("pause");

9)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
main()

Críticas e sugestões: consultjnr@gmail.com Página 51


{
int x;
for(x=1; x<200;x++)
if (x % 4 == 0)
printf("%d\n",x);
/* Pode ser feito assim tambem
x=1;
while (x <200)
{
if (x % 4 == 0)
printf("%d\n",x);
x=x+1;
}
*/
system("pause");
return 0;
}

10)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
main()
{
char nome[30]; int x,t;
printf("\n\n DIGITE UM NOME: ");
gets(nome);
t=strlen(nome);
for(x=t+1;x>=0;x++)
printf("%c",nome[x]);
printf("\n\n");
system("pause");
return(0);

11)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
main()
{
int x, vet[8], num, achei=0;
for(int x=0;x<8;x++)
{
printf("\n[%d] Digite um numero: ",x);
scanf("%d",&vet[x]);
}
printf("\n\n");
printf("Digite um valor a ser pesquisado: ");
scanf("%d",&num);
for(int x=0;x<8;x++)

if(vet[x]==num)
{
printf("\n O numero %d esta na posicao %d: ",num,x);
achei=1;
}
if(achei!=1)

Críticas e sugestões: consultjnr@gmail.com Página 52


printf("\n Este numero nao existe");

printf("\n\n");
system("pause");
return(0);

12)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
main()
{
int vet1[8], x, cont=0, soma=0, soma2=0;
for(x=0;x<=7;x++)
{
printf("\nDigite um valor: ");
scanf("%d",&vet1[x]);
if(vet1[x]>30)
{
cont++;
soma=soma+vet1[x];
}
}
for(x=0;x<=7;x++)
printf("\t%d",vet1[x]);
printf("\n\n %d Numeros sao maiores que 30",cont);
printf("\n\n A Soma dos numeros maiores que 30 e = %d",soma);
for(x=0;x<=7;x++)
soma2=soma2+vet1[x];
printf("\n\n A Soma dos numeros digitados e = %d",soma2);
printf("\n\n");
system("pause");
return(0);

13)

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include <string.h>
main()
{
char nome[3][20],;
int x;
for(x=0;x<=2;x++)
{
printf("\n[%d] Digite o nome : %d ",x,(x+1));
gets(nome[x]);
}
for(x=0;x<=2;x++)
{
printf("\n %s",nome[x],x);
printf("\tO NOME %s tem %d letras",nome[x],strlen(nome[x]));
}
printf("\n\n");

Críticas e sugestões: consultjnr@gmail.com Página 53


system("pause");
return(0);

14)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
void preenche(int vetp[])
{
int x;
for (x=0; x<6; x++)
{
// [%d] e o x vao mostrar a posição do número digitado
printf ("\t[%d] Digite um numero: ",x);
scanf("%d",&vetp[x]);
printf ("\n");
}
}
void imprimevet(int vetp[])
{
int x;
for (x=0; x<6; x++)
printf (" [%d] %d\t",x,vetp[x]);
}
void quadrado(int vetp[])
{
int x;
for(x=0; x<6; x++)
printf("%d\t",vetp[x]*vetp[x]);
}
void primultimo(int vetp[])
{
printf ("%d\t %d\t",vetp[0], vetp[5]);
}

int vetp[5];
main()
{
int x, resp;
resp=1;

while(resp!=0)
{
printf("\n 1 - Preenche o vetor: ");
printf("\n");
printf("\n 2 - Imprime o vetor: ");
printf("\n");
printf("\n 3 - Imprime o quadrado do vetor original: ");
printf("\n");
printf("\n 4 - Imprime o primeiro e ultimo numero: ");
printf("\n");
printf("\n 0 - Sair do programa: ");
scanf("%d",&resp);
printf("\n");
if(resp==0)break;
if(resp==1)

Críticas e sugestões: consultjnr@gmail.com Página 54


preenche(vetp);
if(resp==2)
imprimevet(vetp);
if(resp==3)
quadrado(vetp);
if(resp==4)
primultimo(vetp);
printf("\n");
system("pause");
system("cls");
}
return(0);
}

15)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int verificavogal (char M)
{
if(M=='a' || M=='A' || M=='e' || M=='E' || M=='i' || M=='I' || M=='o' || M=='O' ||
M=='u' || M=='U')
return(1);
else
return(0);
}

main()
{
char x;
printf("Digite uma letra: ");
scanf("%c",&x);
if(verificavogal(x)==1)
printf("\nA letra [ %c ] eh uma vogal: ",x);
else
printf("\nA letra [ %c ] eh uma constante: ",x);
printf("\n\n");
system("pause");
return(0);
}

16)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <string.h>
main()
{
int lin,col, tab;
int mat[4][4];
for (lin=0; lin<=3; lin++)
{
for (col=0; col<=3;col++)
{
printf("Digite ELEMENTO da linha %d, coluna %d da matriz: ",lin+1,col+1);
// aqui no scanf preenchemos a matriz
scanf("%d", &mat[lin][col]);

Críticas e sugestões: consultjnr@gmail.com Página 55


}
}
//Imprimindo a matriz
printf("Matriz\n");
for (lin=0;lin<=3;lin++)
{
for (col=0;col<=3;col++)
printf("%d\t",mat[lin][col]);
printf("\n\n");
}
// Imprimindo a diagonal principal
printf("\n\nDiagonal principal\n\n");
for (lin=0; lin<=3;lin++)
{
printf("%d\n",mat[lin][lin]);
for (tab=1;tab<=lin+1;tab++)
printf("\t");
}
printf("\n\n");
system("pause");
return 0;
}

17)
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <string.h>
main()
{
int lin,col, tab;
int mat[3][3];
for (lin=0; lin<3; lin++)
{
for (col=0; col<3;col++)
{
printf("Digite ELEMENTO da linha %d, coluna %d da matriz: ",lin+1,col+1);
// aqui no scanf preenchemos a matriz
scanf("%d", &mat[lin][col]);
}
}
//Imprimindo a matriz
printf("Matriz\n");
for (lin=0;lin<=2;lin++)
{
for (col=0;col<3;col++)
printf("%d\t",mat[lin][col]);
printf("\n\n");
}
// Imprimindo a matriz menos diagonal principal
printf("\n\nMatriz menos a diagonal principal\n\n");
for (lin=0; lin<3;lin++)
{
for (col=0;col<3;col++)
{
if (lin != col)
printf("%d",mat[lin][col]);
printf("\t");

Críticas e sugestões: consultjnr@gmail.com Página 56


}
printf("\n");
}

printf("\n\n");
system("pause");
return 0;
}

18)

int main(){

char nome[50] , sexo, cor[10];


float altura;
int cont = 0;

while(cont < 5){

printf(“Escreva o nome ”);


gets(nome);
printf(“Escreva o sexo ”);
scanf(“%c”,&sexo);
fflush(stdin);
printf(“Escreva a cor dos olhos”);
gets(cor);
printf(“Escreva a altura ”);
scanf(“%f”, &altura);
fflush(stdin);

if(sexo == „m‟){
printf(“A altura da pessoa é: %f” ,altura);
}
else{
printf(“A cor dos olhos é: %s” ,cor);
}
cont++;
}
system("pause");
return 0;
}

Críticas e sugestões: consultjnr@gmail.com Página 57


REFERÊNCIAS:
http://www.siban.com.br/destaque/21_carta.pdf

ftp://ftp.unicamp.br/pub/apoio/treinamentos/logica/logica.pdf

http://minerva.ufpel.edu.br/~guntzel/prog1/prog1_aula4.pdf

http://www.juliobattisti.com.br/tutoriais/katiaduarte/cbasico002.asp

http://www.carlosfelgueiras.hpg.com.br/Cursos/LinguagemC/Cap_7.html

http://www.portaldaprogramacao.com/artigos2.asp?n=897

http://www.dca.ufrn.br/~lmarcos/courses/DCA800/pdf/Algoritmos01.pdf

FORBELLONE, A. L. e EBRSPACHER, H. F. - Lógica de Programação – Construção de


Algoritmos e Estrutura de Dados. 2a.Ed. Makron Books, 1993

SCHILDT, Herbert. C Completo e Total. 3 Ed. São Paulo: Makron Books, 1996.

JAMSA, Kris; KANDLER, Lars. Programando em C/C++ - A Bíblia. 1 ed. São Paulo: Makron
Books, 1999.

Críticas e sugestões: consultjnr@gmail.com Página 58

S-ar putea să vă placă și