You are on page 1of 69

NOÇÕES DA LINGUAGEM " C "

INTRODUÇÃO

A linguagem "C" vem se tornando bastante difundida a partir de suas qualidades


principais tais como a portabilidade, a flexibilidade e o alcance, pois não só é usada em
computadores com características e sistemas operacionais diferentes, como apresenta um
código fonte escrito de forma bastante livre, adaptando-se a todos os estilos de pro-
gramação.

É capaz ainda de comparar-se às linguagens ditas de "alto nível" pelo poder de


suas instruções e funções padronizadas e às de "baixo nível" pela manipulação de bytes,
bits e seus respectivos endereços.
Sinteticamente, a linguagem C presta-se tanto ao desenvolvimento de grandes sistemas e
aplicativos de uso geral como de sistemas operacionais e compiladores.

Criada nos laboratórios da Bell nos meados da década de 70, tornou-se conhecida
associadamente ao UNIX, surgido no mesmo laboratório. Seu criador é Dennis M. Ritchie.
A "bíblia" sobre o assunto é o livro "The "C" Programming Language" (Englewood Cliffs,
New Jersey, Prentice Hall, 1978), por ele escrito, juntamente com Brian W. Kernigham.

É uma linguagem de aspecto simples, que possibilita a elaboração de programas


estruturados com extrema portabilidade devido ao uso superlativo de funções. É sufi-
cientemente compacta para permitir fontes menores e mais rápidos que a maioria das
outras linguagens.

Apresenta uma restrição que deve ser observada com carinho pelos altos riscos
nela embutidos. O preço da quase absoluta liberdade de programação é, na-
turalmente, a "eterna vigilância". O desconhecimento de tal norma poderá conduzir a
erros não detectados, com conseqüências imprevisíveis.

Noções da Linguagem C pág 1


Estrutura básica de um programa em C

Pascal Linguagem C
program exemplo1; #include <biblioteca1.h>
uses crt; .... (* bibliotecas *) #include <biblioteca2.h>
var (* declaração das variáveis *) main()
x: integer; {
y: real; /* declaração das variaveis */
int x;
float y;
begin
(* comandos *) /* comandos */
x := 0; x = 0;
y := 1.8; y = 1.8;
end. }

Estrutura completa de um programa em C

Pascal Linguagem C
program exemplo1; #include <stdio.h>
uses crt; .... (* bibliotecas *) #include “biblioteca2.h”
var (* declaração das variáveis *) /* declaração das variáveis globais */
x: integer; main()
{
procedure … /* procedimentos */ /* declaração das variaveis locais */
begin int x;

end; /* comandos */
function …. /* funções */ x = 20;
begin ..........
}
end;
função1(.......)
begin {
(* comandos *)
x := 20; }
............
............ função2(.......)
end. {

Noções da Linguagem C pág 2


IDENTIFICADORES
C, como todas as linguagens, consiste em um determinado grupo de símbolos,
geralmente letras e dígitos que, ordenados conforme a sintaxe estabelecida, executam
funções ou identificam variáveis ou expressam valores constantes de forma a cumprirem
as tarefas que o programador imaginou.

A intensa procura de compiladores C fez com que surgissem vários produtos no mercado,
inicialmente de forma independente e não normalizada. Recentemente a ANSI,
organização americana, editou as normas para C de forma que, hoje, a maioria dos
compiladores existentes estão a elas se adaptando. Diferentes entre si, mantem um núcleo
central normalizado, que aumenta ainda mais a portabilidade já referida, desde que sejam
evitados os comandos, instruções e construções específicos, tão somente, do compilador
em uso. Ao longo desta apostila utilizaremos conceitos e critérios correntes e, quando
particularizarmos, usaremos o Turbo C, produto e marca registrada da BORLAND
INTERNATIONAL, INC.

Para a maioria dos compiladores de C somente são considerados os oito primeiros


caracteres do nome de um identificador, mesmo que sua denominação abranja um maior
número de letras.

O "C" reservou algumas palavras que não podem ser usadas, a não ser em seu contexto
correto.

As “palavras reservadas” segundo estabelecido pela ANSI são:

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

Turbo C, por exemplo, acrescenta mais algumas às palavras reservadas acima:

asm _cs _ds _es


_ssc decl far huge
interrupt near pascal

Noções da Linguagem C pág 3


Bibliotecas mais utilizadas

Biblioteca Uso Geral Funções mais usadas


stdio.h standard input output printf(), scanf(), puts()...
entrada e saída padrão
conio.h console input output clrscr(), gotoxy(), clreol(),
entrada e saída padrão getch()....
math.h mathematics exp(), sqrt(), log(), sin() ....
funções matemáticas
string.h strings strcmp(), strcpy(), strchr()...
manuseio de strings

Para incluir uma bliblioteca, deve-se usar a diretiva #include, como por exemplo:
#include <stdio.h>
ou
#include “conio.h”

VARIÁVEIS
As variáveis devem ser declaradas antes da primeira referência a elas no corpo do
programa.

Os nomes das variáveis são formados por letras e dígitos, sendo o primeiro caractere,
obrigatoriamente, uma letra. O símbolo _(sublinhado) é considerado uma letra, podendo
portanto ser também utilizado, inclusive no começo do nome. Contrariamente, $ pode ser
usado, mas não no início.

Como as letras são consideradas pelo valor de seu código numérico, as maiúsculas e
minúsculas não são equivalentes. É comum o uso das letras minúsculas para os nomes
das variáveis, enquanto as maiúsculas são usadas como constantes simbólicas,
resultantes do uso da instrução de pré-compilação #define.

Os nomes externos, tais como os nomes de funções ou de variáveis externas, devem ter
até oito caracteres.

Tipos e tamanhos

char - ocupa 8 bits e armazena o valor numérico correspondente a um caractere na


tabela de caracteres da máquina em uso. Pode conter valores de 0 a 255.

int - ocupa 16 bits, variando seu valor de -32768 a 32767. Normalmente reflete o inteiro
natural da máquina em uso.

float - ocupa 32 bits, representando o valor em ponto flutuante de precisão simples. Seu
valor varia de 3.4E-38 a 3.4E+38 no PC.

double - ocupa 64 bits, representando o número em ponto flutuante de precisão dupla.


Os valores são representados de forma semelhante aos do tipo float, diferenciando-se
somente no número de dígitos que pode explicitar. Seu valor pode variar de 1.7E-307 a
1.7E+308.

void - sem valor. Usada para identificar as funções que não retornam valor algum.

Noções da Linguagem C pág 4


Algumas variáveis podem ter qualificadores, a saber:

signed - com sinal, default para int.

unsigned - ocupa dois bites, representando o inteiro sem o sinal, com o valor variando, no
PC, de 0 a +65535.

short e long - são qualificadores que se referem ao tamanho dos inteiros. No PC o tipo
short é igual ao tipo int. O tipo long, ocupando 32 bits, equivale ao dobro de int, variando o
seu valor de -2E+9 a +2E+9.

As declarações dos qualificadores são feitas da seguinte forma:


unsigned int x;
short int y;
long int z;

A palavra int pode ser suprimida, modificando as declarações para:


unsigned a;
short b;
long x; Todas estas formas são válidas.

Declaração de variáveis

A declaração de variáveis pode ser feita de maneira isolada ou múltipla.


Uma declaração liga o nome de uma ou mais variáveis a um tipo específico de dados:

char c;
int maior, menor, passo;

Outra forma de escrever seria:

int maior;
int menor;
int passo;

Uma variável pode ser inicializada quando de sua declaração:

int i = 0;
float x = 3.14;

Noções da Linguagem C pág 5


Observações:

1) O nome da variável é apenas uma referência e portanto não ocupa espaço na memória.
Desta forma, procure sempre dar nome significativo às variáveis para tornar o programa
mais fácil de ser entendido e modificado no futuro.

ex: em vez de declarar as variáveis x, y e z:

float x, y, z;
y = x - z;

declare as variáveis com nome mais significativo, como por exemplo:

float salario_bruto, salario_liquido, total_descontos;


salario_liquido = salario_bruto - total_descontos;

2) A linguagem C diferencia caracteres MAIUSCULOS e minúsculos. Desta forma procure


utilizar todas as variáveis com letra minúscula.
ex: int Salario, SALARIO, salario, SalariO; /* são 4 variáveis distintas */

3) As variáveis devem ser declaradas antes de qualquer comando:

main() main()
{ int x; { int x;
x = 0; int y; /* correto */
int y; /* ERRADO */ x = 0;
y = 20; y = 20;
} }

Em C++ é possível declarar uma variável em qualquer parte do programa.


Em termos de estruturação e documentação do programa recomenda-se efetuar a
declaração das variáveis sempre no início de cada bloco.

Typedef

Um tipo complexo de variável pode ainda ser renomeado, o que será crescentemente útil.
Para fazê-lo emprega-se a palavra chave typedef. Por exemplo:

#include <stdio.h>
main()
{
typedef unsigned char uchar;
uchar alpha=224, beta=225;
printf("%c %c", alpha, beta);
}

typedef não cria um novo tipo de variável, apenas renomeia, permitindo utilizar nomes
menores (uchar, no exemplo), para identificar as variáveis complexas correspondentes
(alpha e beta, que são na verdade do tipo unsigned char).

Noções da Linguagem C pág 6


Escopo de uma variável

Entende-se como escopo de uma variável os limites dentro dos quais tem seu valor
disponível.

As variáveis podem ser internas, conhecidas e referenciadas dentro de determinado


bloco, ou externas, que podem ser referidas de qualquer lugar dentro de determinado
programa.

As variáveis externas, que são definidas fora de qualquer função, são também
chamadas variáveis globais e têm por duração a do programa que as criou, podendo ser
referidas por quaisquer das funções que o compõe.

Como são accessíveis globalmente, as variáveis externas provêm uma forma alternativa
de passagem de argumentos e retorno de valores para funções. O único incoveniente é
que todas as funções devem referencia-las pelo mesmo nome, tirando a liberdade dos
programadores em definir o nome das variáveis internas às funções.

Regras de limites

As fronteiras dentro das quais uma variável é reconhecida são as partes do programa onde
a variável pode ser referida sem perda de valor.

Deve-se conceitualmente separar a declaração da variável da definição de seu escopo.


Numa se informa as propriedades da variável (tipo, tamanho, etc.), enquanto na definição
faz-se a reserva de memória para seu armazenamento, bem como se estabelece o seu
modelo de duração.

Variáveis automáticas

As variáveis automáticas, são declaradas no início de uma função e sua duração


restringe-se ao da própria função. Neste caso, só existem dentro desta e enquanto a
função estiver ativa.

Variáveis estáticas (static)

Existem enquanto durar a função, e retêm o seu valor para futura referência, quando a
função for reativada.

Noções da Linguagem C pág 7


CONSTANTES
Uma constante é um valor que não se modifica ao longo de todo o programa.
Seu aspecto, no entanto, pode ser o de uma variável. Isto pode ser obtido pelo uso da
palavra-chave const.

O uso de const permite, em beneficio da clareza de entendimento, usar, por exemplo:

const int qtd_alunos = 35;


const float cm_por_pol = 2.54;
const float pi = 3.1416;

no próprio corpo do programa, permitindo dai em diante o uso do nome em vez do valor.

Qualquer tentativa de modificar o conteudo será impedida pelo compilador.

- Constantes hexadecimais:

São escritas colocando-se 0x ou 0X antes da constante. Por exemplo:

0x123
0Xfad4
0XDAB2

As constantes do tipo hexadecimal pode ser seguida pelas letras l ou L para identificá-las
como do tipo long.

- Constantes de caractere:

Constante de caractere é um caractere dentro de aspas simples. Por exemplo:


'a' , 'F' .

- Constante "string"

É uma seqüência de zero ou mais caracteres entre aspas duplas.

" Linguagem "C""

"" - string nula

As aspas não fazem parte da "string", servem apenas para delimitá-la.

Noções da Linguagem C pág 8


COMENTÁRIOS
O uso de comentários é importante para documentar trechos ou rotinas dentro do
programa. Eles tem significado apenas para nós programadores, sendo ignorados pelo
computador no momento da compilação.

Os comentários em C podem ser inseridos em qualquer parte do programa.

Um comentário é iniciado por /* (barra asterisco) e finalizado por */ (asterisco barra)

Ex: main()
{ /* Este programa tem por objetivo mostrar o uso dos comentários */
int x,y; /* variáveis inteiras */
float w; /* variável real */
x = x + 1; /* incrementa x */
}

Procure utilizar os comentários com equilíbrio. Ao comentar todas as linhas do programa,


ele se torna muito poluído visualmente.
Procure evitar comentários óbvios da linguagem, afinal de contas um programador que
está lendo um programa em C, deve posuir os conhecimentos básicos relativos à
linguagem.

OPERADORES
Operadores são aqueles elementos da linguagem que, colocados junto aos respectivos
operandos, forçam o compilador a executar determinadas operações matemáticas, lógicas,
de atribuição ou especiais.

Operador de atribuição

Na linguagen C, pode-se usar o operador de atribuição dentro de qualquer expressão, o


que não acontece na maioria das outras linguagens tais como: Pascal, Basic, etc...
A forma geral do operador de atribuição é:

nome_da_variável = expressão;

Exemplos:

x = 10;
y = x + 3;
z = y * 2;

Noções da Linguagem C pág 9


- conversão de tipos em atribuições:

A regra básica para a convesão de tipos em uma atrubuição é simples: o valor do lado
direito (o lado da expressão) é convertido para o tipo da variável do lado esquerdo (a
variável de destino).
Deve ser tomado o devido cuidado para a possível perda de valores quando efetuada a
conversão.

Por exemplo: Na conversão de float (real) para int (inteiro), além de perder as casas
decimais, os valores podem ser truncados para os limites de -32768 a 32767.

A tabela abaixo ilustra algumas situações:

Tipo do destino Tipo da expressão Possível informação perdida


(Lado esquerdo) (Lado direito)
signed char char Se valor > 127, destino negativo
char short int Os 8 bits mais significativos
char int Os 8 bits mais significativos
char long int Os 24 bits mais significativos
int long int Os 16 bits mais significativos
int float A parte fracionária e e os valores
limitados entre -32768 a 32767.
float double Precisão, o resultado é
arredondado

Observação:

Deve-se ter especial atenção ao escolher o tipo de variável a ser utilizada.


A escolha incorreta do tipo da variável poderá acarretar erros de lógica difíceis de serem
identificados.
Por exemplo, utilizar variáveis do tipo int (inteiro) para contadores que podem armazenar
valores acima de 32767, poderá provocar um "looping" no programa, como mostrado
abaixo:

em C: Algoritmo:

for (x =1; x < 35000; x++) para x = 1 ate 35000 passo 1


printf("%d", x); escreva (x)

Em C, quando x ultrapassar o valor de 32767, o seu valor será -32768, o que acarretará
um "looping" (execução sem fim) do comando for (para).

explicação:
a variável do tipo int ocupa 2 bytes, ou seja 16 bits. Um bit para armazenar o sinal (0
positivo e 1 negativo) e 15bits que possibilitam armazenar valores inteiros entre 0 a 32767

Atribuições múltiplas:

Noções da Linguagem C pág 10


C permite atribuir o mesmo valor à várias variáveis, utilizando atribuições múltiplas em
um único comando.

x = y = z = w = 0;

Atribui zero às variáveis x, y, z e w.

Isto é possível em C porque o comando de atribuição, além de atribuir também retorna o


respectivo valor atribuído.

Ex: Em C, são válidos os seguintes comandos:

b = 10; /* atribui 10 à variável b */


a = b; /* atribui 10 (valor de b) à variável a*/
x = b + ( c = 2 ); /* atribui 2 à variável c e 12 à variável x */
a = b = c = 9; /* atribui 9 às variáveis a,b e c */
soma = ( y = 5 ) + ( z = 12 ); /* atribui 12 à z, 5 à y e 17 à soma */

Observação:

A linguagem C, diferentemente das outras, permite que uma variável no lado direito de
uma expressão tenha o seu valor alterado, com no exemplo abaixo:

x = (y = 3) + (z = 7);

o comando acima atribui o valor 3 à variável y, o valor 7 à z e o valor 10 à x.

Operadores aritméticos

Os operadores aritméticos são:

+ soma
- subtração ou, como prefixo, invertendo o sinal do operando.
* multiplicação
/ divisão
% módulo (resto da divisão)
-- decremento
++ incremento

A precedência entre os diversos operadores aritméticos é expressa pela relação abaixo:

++ e - - Mais alta
- (menos unário, como prefixo)
* / %
+ - Mais baixa

Noções da Linguagem C pág 11


Vale observar que os operadores de decremento e incremento podem vir antes ou
depois da variável que afetam.

Ex:
++x; (pre-incremento)
x++; (pos-incremento)
--y; (pre-decremento)
y--; (pos-decremento)

Têm então comportamentos diferentes. Além de serem os operadores de maior


prioridade, se antecederem a variável, esta será incrementada ou decrementada
antes de ser usado o valor do operando (pre-incremento/decremento).

Se utilizados após a variável, será "retornado" o valor da variável para depois


incrementar ou decrementar o seu respectivo valor (pos-incremento/decremento).

Exemplo de pre-incremento:

x=10;
y = ++x; (incrementa e retorna) após a execução x e y terão o valor 11,
pois o valor de será incrementado antes de ser atribuído à y.

Exemplo de pos-incremento:

x=10;
y = x++; (retorna e incrementa) após a execução y valerá 10, enquanto
que x terá o valor 11, pois o valor de x será primeiro atribuído a
y e depois incrementado de uma unidade.

A maioria dos compiladores C produz código-objeto para as operações de incremento e


decremento muito rápidas e eficientes, melhor que o código gerado pelo uso da sentença
equivalente de atribuição.
Portanto, procurar utilizar x++; em vez de x = x + 1

exemplo:
void main(void)
{ int x,y,z;
x = 10;
y = ++x;
z = x ++;
prinft(" x = %d y = %d e z = %d",x,y,z);
return(0);
}

O programa acima exibirá: x = 12 y = 11 e z = 11

Noções da Linguagem C pág 12


Observação sobre prioridades de execução:

Em:
x= 2+y*3; primeiro se efetuada a multiplicação de Y por 3 para depois
somar 2 ao resultado.

x = 2 + (Y * 3); a utilização dos parenteses, neste caso, não afetará a ordem


de execução, porém auxiliam a visualização da sequência em
que as operações serão efetuadas.

x=y/2-34*z; as duas expressões atribuirão o mesmo valor a x. Porém, a


x = (y /2) - (34 * z); utilização dos parênteses esclarece a ordem de avaliação

Desta forma, recomenda-se a utilização de parênteses, mesmo que redundantes, em


expressões complexas, para facilitar a visualização e o melhor entendimento da sequência
em que as operações serão realizadas.

Os parênteses também podem ser utilizados para alterar a ordem natural de avaliação e
execução de uma expressão aritmética ou lógica.

Operadores relacionais

> maior que


< menor que
>= maior ou igual a
<= menor ou igual a
== igual a
!= não igual a (diferente)

Operadores lógicos

&& and (E)


|| or (OU)
! not (NÃO)

Tabela Verdade:

p q p && q p || q !p
0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0

Noções da Linguagem C pág 13


Precedência entre operadores lógicos e relacionais

A precedência entre os diversos operadores relacionais e lógicos é expressa pela relação


abaixo, na ordem decrescente:

! mais alta
> >= < <=
== !=
&&
|| mais baixa

Como nas expressões aritméticas, os parênteses alteram a precedência natural dos


operadores.

Operadores de Atribuição composta

Existe uma variante do comando de atribuição, às vezes chamada de C reduzido, que


simplifica a codificação de um certo tipo de operações de atribuição.
A forma geral de uma atribuição composta em C é:

var operador = expressão

Por exemplo:

x = x + 10; pode ser escrito x += 10;

Operador Exemplo Forma equivalente


= a = 0; a = 0;
+= a += 3; a = a + 3;
-= a -= 5; a = a - 5;
*= a *= 2; a = a * 2;
/= a /= 4; a = a / 4;
%= a %= 2; a = a % 2;
++ a++ ou ++a a = a + 1;
-- a-- ou --a a = a - 1;

Observação:

x *= y + 1; corresponde a x = x * ( y + 1 );

e não a x = x * y + 1;

Noções da Linguagem C pág 14


FUNÇÕES DE ENTRADA E SAÍDA

PRINTF

A função printf produz uma saída formatada.

printf(cadeia-de-controle, expressão1, expressão2,...);

A cadeia de controle determina de que forma a saída será formatada, conforme a tabela
abaixo:

código formato
%d decimal inteiro
%c caractere
%s cadeia (string) de caracteres
%f numérico, ponto flutuante, expresso em decimal
%e numérico, ponto flutuante, expresso em exponencial
%g como %f ou %e, o que for mais expressivo
%u inteiro decimal, sem sinal
%p ponteiro
%x inteiro hexadecimal, sem sinal

Exemplo:

main()
{
int x , y;
float z;
x = 1; y = 2; z = 1.75;
printf(Variável X = %d Variável Y = %d e Variável Z = %f", x,y,z);
}

Se executado o código fonte acima, resultará no seguinte texto:

Variável X = 1 Variável Y = 2 e Variável Z = 1.750000

Para evitar que variáveis reais sejam exibidas com um número excessivo de casas
decimais, podemos ainda utilizar algumas especificações de formato:

Noções da Linguagem C pág 15


Especificações de formato

As especificações de formato na função printf têm, a seguinte formação:

% [flag] [tamanho] [.precisão]

- seqüência de flags [flags]


- especificador da largura mínima [tamanho]
- especificador de precisão [precisão]
- modificador do tamanho de entrada [F|N|h|l]
- conversor do tipo do dado [tipo de dado]

flags - contém justificação, sinal, pontuação decimal, brancos à direita ou à esquerda,


prefixos octais e hexadecimais.

Os caracteres de flag são:

- (menos) que alinha o resultado à esquerda, preenchendo com brancos à direita.

+ (mais) que obriga a explicitação do sinal seja + ou - sempre precedendo o valor.

b (branco) default em relação ao sinal. Só é explicitado o sinal negativo.

# (tralha) garante que haverá um ponto decimal, mesmo que não haja digitos decimais.

tamanho - especifica o número de caracteres que serão impressos, preenchimento com


zeros a esquerda ou não, etc.

precisão - número máximo ou mínimo de caracteres a serem impressos, conforme o


caso.

Exemplos:

x = 1.75;

printf("%f ", x); Produzirá a saída: 1.750000

printf("%5.2f ", x); Produzirá a saída: 1.75

printf("%05.2f ", x); Produzirá a saída: 01.75

printf("%5.3f ", x); Produzirá a saída: 1.750

Noções da Linguagem C pág 16


SCANF

É uma função de entrada (leitura).

A função scanf funciona na leitura de forma semelhante à printf na saída.

As principais diferenças são:

- Os argumentos que se seguem à cadeia de controle são sempre endereços. Podem


ser "operadores endereços" como em &num, ou nomes de "arrays" (vetores), ou seja,
"pointers" para o primeiro elemento da matriz.

- Não há a opção %g.

- %e e %f funcionam da mesma forma, aceitando qualquer dos dois formatos.

- %h é o especificador para leitura de inteiros do tipo short.

Exemplo:
int matr;
float média;
char nome[20];
printf("Informe a matricula e o nome de um aluno \n").
scanf ("%d %s", &matr, &nome);
.....

onde &matr significa o endereço da variável matr e &nome o endereço do primeiro


elemento do vetor de vinte elementos (cadeias de caracteres), chamada nome.

No caso de variáveis do tipo "array" (vetores) pode-se omitir o caractere &.

Neste caso pode-se utilizar: &nome[0], &nome ou simplesmente nome.

Observação:
Para ler uma "string" , a função scanf() lê os caracteres até que seja encontrado um
caractere de espaço em branco. Desta forma para ler "strings" com mais de uma nome,
devemos utilizar a função gets(), que lê os caracteres até que seja pressionado "enter"
(retorno e carro) como no exemplo abaixo:

Supondo que seja informado o nome de "Ana Maria".

Exemplo com scanf() Exemplo com gets()

printf ("Informe o nome de um aluno \n"); printf ("Informe o nome de um aluno \n");

scanf ("%s",&nome); gets(nome);

printf ("Nome do aluno = %s", nome); printf ("Nome do aluno = %s", nome);

resultado: Nome do aluno = Ana resultado: Nome do aluno = Ana Maria

Noções da Linguagem C pág 17


Comandos de Repetição: WHILE, FOR e DO WHILE
Comando WHILE

O comando while tem a seguinte sintaxe:

while ( expressão )
comando;

Se o resultado da expressão for diferente de zero, o comando é executado e a expressão


é desenvolvida novamente. O ciclo continua até que a expressão produza zero, quando
então a execução do programa salta para a instrução que estiver após o comando.

main()
{ int x;
x=1;
while (x < 11 )
{ printf(%d \n, x);
x = x + 1;
)
}

O programa acima exibe os numeros de 1 a 10

Comando FOR

O comando for tem a seguinte sintaxe:

for (exp1 ; exp2 ; exp3)


comando;

exp1: inicialização - é execurtada apenas uma vez no inicio do comando for


exp2: condição - é verificada todas as vezes no início do comando for
exp3: incremento - é executada todas as vezes no final do comando for

main() main()
{ int x; { int x;
for (x=1; x < 11; x=x+1 ) for (x=1; x < 11; x++)
printf(%d \n, x); printf(%d \n, x);
} }

Os programas acima também exibem os numeros de 1 a 10

Noções da Linguagem C pág 18


Equivalência do comandos FOR e WHILE

main() main()
{ {
for (exp1; exp2; exp3 ) exp1;
comando; while (exp2)
} comando;
exp3;
}

Um uso comum de for é o de montagem de retardadores. Destinados a, por exemplo,


permitir a leitura de mensagens na tela, são simples consumidores de tempo:

for (t=0; t<10000; t++);

A expressão só conta, consumindo tempo, sem alterar nada no contexto programa.

do while

O comando do while possui a seguinte sintaxe:

do
comando;
while (expressão);

O comando é executado e, posteriormente, a expressão é desenvolvida. Se a mesma


produzir zero, o loop é terminado; caso produza valores positivos, o comando é executado
novamente. Significa dizer que o loop do-while será executado, ao menos uma vez.

O programa abaixo também exibe os numeros de 1 a 10

main()
{ int x;
x = 1;
do
{ printf(%d \n, x);
x = x + 1;
} while (x < 11);
}

BREAK

Noções da Linguagem C pág 19


O comando break permite sair de um loop controlado por um comando while, for, do-while
ou switch, incondicionalmente.

CONTINUE

O comando continue faz com que em um comando while, for ou do, o restante do
módulo não seja executado e força uma iteracão.

O programa a seguir utiliza esta instrução, manipulando os dados, de forma a montar


uma tabela de números pares.

main() /* imprime números pares */


{
int x;
for (x=0; x<100; x++)
{
if (x % 2 != 0)
continue;
printf("%d ", x);
}
}

Toda vez que (x % 2) for verdadeiro, ou seja, houver resto, continue força o retorno ao for
sem passar pelo printf.

No retorno ao for este executa o incremento antes do teste condicional, o que não se
daria se o comando fosse while ou do-while quando o retorno seria diretamente ao teste.

Noções da Linguagem C pág 20


CONTROLE DE FLUXO

Os comandos de fluxo em uma linguagem são aqueles que modificam a ordem natural de
execução de um programa.

Comandos de bloco
Para entendermos o que se segue é bom fixar o conceito de bloco.

Uma expressão como x = 0 ou i++ torna-se um comando quando é seguida por um


símbolo ; (ponto e vírgula), que em "C" é considerado um dos finalizadores de comando.

Os símbolos { (abre chave) e } ( fecha chave ) são usados para agrupar declarações de
dados ou comandos compostos em blocos, de tal maneira que estes sejam equivalentes a
um só comando.

if - else
O comando if - else é usado para desvios condicionais. Sua sintaxe é :

if ( expressão )
comando-1;
else
comando-2;

O else é opcional, podendo ou não ser usado. Sua execução se inicia pela resolução da
expressão. Se a mesma for verdadeira, (o seu resultado for diferente de zero), o
comando-1 é executado; caso contrário, o comando-2 é desenvolvido.

Por definição o else refere-se sempre ao if mais interno:

No bloco
if ( n < 0)
if ( a > b )
z=a;
else
z= b;

o else é uma alternativa ao segundo if, aquele que testa se a > b.

Nota: Para melhor visualização, procure sempre "alinhar" os comandos if - else;

Noções da Linguagem C pág 21


Caso desejemos ligar o else ao if mais externo, poderemos construir um bloco:

if ( n < 0 )
{
if ( a > b )
z=a;
}
else
z=b;

A construção
if (expressão)
comando1;
else
if (expressão)
comando2;
else
if (expressão)
comando3;
else
comando4;

é válida e representa um conjunto de decisões relacionadas a um conjunto de expressões.


O último else corresponde a "nenhuma das opções anteriores".

Atenção :

if (a == b)
x = 10; /* atribuirá 10 a x se a e b forem iguais */

if (a = b)
x = 10; /* atribuirá 10 a x se b for maior que zero */

Noções da Linguagem C pág 22


Expressões condicionais - ternário
Em "C" existe o operando ternário, ( ? )que substitui a seguinte operação:

if (expressão-1)
expressão-2;
else
expressão-3;

É utilizado da seguinte forma:

e1 ? e2 : e3

O operador e1 é desenvolvido inicialmente. Se o seu resultado for diferente de zero,


(zero = falso ), então é desenvolvida a expressão e2. Caso contrário é desenvolvida a
expressão e3.
A expressão

if ( a > b )
z = 10;
else
z = 20;

pode ser escrita da seguinte forma:

a > b ? z = 10 : z = 20;

EXEMPLOS:

Enumeramos a seguir três diferentes formas de calcular um valor para y, na dependência


do valor de x. Cada uma delas tem diferentes requisitos de memoria e características de
velocidade, que tornam seu uso melhor em condições determinadas.

A primeira é clássica:

y = 0;
if (x == 5)
y = 1;

A segunda usa o operador ternário demonstrado no presente item:

y = (x == 5) ? 1 : 0;

E, finalmente, a terceira utiliza o conhecimento de prioridade dos diversos operadores em


C, já que o operador == (teste de igualdade entre dois valores) é prioritário em relação ao =
(operador de atribuição).

y = x == 5;

Noções da Linguagem C pág 23


SWITCH

Switch é uma forma especial de construção if - else, onde o teste ao invés de ocorrer
sobre expressões, é feito sobre um conjunto de valores constantes.

switch ( expressão )
{
case constante-1:
comando;
case constante-2:
comando;
.
.
default:
comando;
}

A condição default é opcional na estrutura do comando.

O case pode ser vazio. Tal recurso é utilizado quando o comando para um grupo de
constantes é o mesmo. Não utilizando o break, que será visto a seguir, obtêm-se o
resultado desejado explicitando todas as constantes e condições e escrevendo a ação a
tomar uma só vez.

A resolução de switch se dá da seguinte forma: a expressão é resolvida e seu resultado é


comparado com todas as opções case no bloco. Se nenhum dos comandos contidos nos
cases for executado, o comando que se segue à opção default o será.

Caso uma opção case seja aceita, após a execução do comando associado, os cases
posteriores continuam a ser testados. Para que isto não ocorra um comando break
deve ser colocado ao final da expressão contida em cada case.

Noções da Linguagem C pág 24


Exemplo:

main()
{ int x;
clrscr();
for (x=0;t<7;x++)
switch (x)
{ case 1: printf(" Este ");
break;
case 2: printf(" exemplo ");
case 3:
case 4: printf("\n mostra ");
printf(" como o switch ");
break;
case 5:
case 6:
case 7: printf( "\n funciona ");
break;
}
}

O programa acima produzirá como resultado:

Este exemplo (case 1 e case 2)


mostra como o switch (case 2)
mostra como o switch (case 3)
mostra como o switch (case 4)
funciona (case 5)
funciona (case 6)

Noções da Linguagem C pág 25


Vetores (Matrizes)
As matrizes são formadas por um conjunto de elementos de um mesmo tipo de dado.
Além das informações fornecidas ao declarar uma variável, na declaração de uma matriz
deve também ser fornecido o número de elementos que a compõe.

/* algumas declarações de matrizes */


int meses [12]; /* vetor de 12 inteiros */

main()
{
float notas[50]; /*50 elementos float */
char code[12]; /* 12 elementos char */
...............

Como as variáveis escalares, as matrizes também podem ser inicializadas. Para que tal
possa ser feito a matriz deve ser externa ou estática. Quando da definição de uma matriz
especificada como externa ou estática, seus elementos são inicializados em 0 (zero), por
default. Para outros valores, a inicialização pode ser:

int dias[12]={31,28,31,30,31,30,31,31,30,31,30,31};

A declaração da matriz dias[12], está feita simultaneamente à inicialização dos seus 12


elementos com o número de dias contidos em cada um dos meses do ano.

Caso o número de valores seja menor que o de elementos, os restantes permanecerão


preenchidos com zeros.
Exemplo: /* dias em cada mês */

main()
{ int x, dias[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
for ( índice = 0; x < 12; x++ )
printf (" Mês %d tem %d dias. \n", x+1, dias[x]);
}

A saída será:

Mês 1 tem 31 dias.


Mês 2 tem 28 dias.
Mês 3 tem 30 dias.
..................
Mês 12 tem 31 dias.

Nota:
É bom lembrar que o "índice+1" contido em printf é necessário. De outra forma, o primeiro
mês será 0 e não 1, pois o primeiro elemento de um vetor é o de número 0 e não 1.

Noções da Linguagem C pág 26


Geralmente utiliza-se o comando for para inicializar os elementos do vetor.

main()
{
int x, pares[50];
for (x = 0; x < 50; x++)
pares [x] = 2 * x;
...........
}

As matrizes de dimensão única são essencialmente listas de dados do mesmo tipo e seu
uso mais comum é na criação de "strings" de caracteres.

Toda "string" de caracteres tem como seu último elemento \0, elemento terminal, o
que obriga a declaração, sempre, de mais um elemento do que os que serão ali
colocados.

"C" não controla o fim das matrizes, sendo esta tarefa estritamente do programador que,
para isto, deverá montar suas próprias rotinas, principalmente na atribuição. Ao
ultrapassar os limites de uma matriz, você estará gravando sobre os campos próximos,
talvez sobre o próprio programa, o que poderá ocasionar erros imprevisíveis.

As matrizes multidimensionais são declaradas como se segue:

int mat [2] [5];

Trata-se de uma matriz de inteiros, com duas dimensões, uma delas contendo 2
elementos e outra 5 ( 2 linhas e 5 colunas).

A inicialização de uma matriz multidimensional se dá na ordem do aparecimento das


dimensões:

int quads [6] [2] =


{ 1, 1,
2, 4,
3, 9,
4, 16,
5, 25,
6, 36}

Certamente não há necessidade de montar uma visão espacial como a do exemplo. Ela aí
está para facilitar o entendimento. Fica claro que o elemento quads [5] [2] tem o valor de
25.

Esta inicialização pode ser feita sem especificar o tamanho. Em "C", se não for
especificado o tamanho numa operação de inicialização, este será criado grande o
suficiente para conter os valores apresentados:

int quads [ ] [2] = { 1,1,2,4,3,9,4,16,5,25,6,36 };

Será alocado na memória o mesmo espaço que para a declaração anterior.

Noções da Linguagem C pág 27


FUNÇÕES e a ESTRUTURA DOS PROGRAMAS

As funções têm como principal objetivo modulizar os programas, ou seja, dividir um


programa grande em pequenas tarefas, mais fáceis de entender e, portanto, de construir
corretamente e de forma independente.

Sua origem não é recente e vem do desenvolvimento da própria técnica de


programação, primeiro com a modularização e depois com a estruturação.

O uso das técnicas citadas permitiu dividir entre vários profissionais a tarefa de programar
um problema complexo, cada um realizando uma parte perfeitamente delimitada.
Um programa em "C" é , na verdade, um conjunto de definições de funções isoladas. A
comunicação entre as diferentes funções se dá, normalmente, através da passagem de
parâmetros entre a função chamada e a função que a ativou.

Podem ser distribuídas em qualquer ordem ao longo do programa fonte e, mesmo este,
pode ser dividido em vários programas fonte.

Exemplo de declaração de função:

tipo Nome_da_função(tipo parametro1, tipo parametro2, .......)


{ ..........
comandos da função
return (variável);
}

Observação:
1) Não se pode omitir o tipo na declaração dos parâmetros, nem se forem do mesmo tipo.

2) Pode-se omitir o comando return ao término de uma função. Caso isto ocorra, quando
for encontrado o símbolo } , que encerra a sua execução, nenhum valor será retornado à
função ou ao programa chamador.

3) int é o tipo default para o Nome_da_função. Por definição, toda função é,


implicitamente, do tipo inteiro.

4) Forma antiga de declarar as funções:


tipo Nome_da_função(parametro1, parametro2, prametro3.......);
{ tipo parametro1;
tipo parametro2;
tipo parametro3;
comandos da função
return (variável);
}

Noções da Linguagem C pág 28


Como exemplo, dobro retorna um valor inteiro;

#include <stdio.h>
main ()
{ int x;
x = 5;
printf("\n Dobro de %d = %d",x,dobro(x));
}

int dobro(int z);


{ z = z * 2;
return(z);
}

Argumentos para funções (Passagem de parâmetros)

Passagem de parâmetro por valor: o valor do argumento é copiado para uma variável
definida na função. Neste caso, a função não pode alterar o valor do argumento no módulo
chamador.

Passagem de parâmetro por referência: o enderereço do argumento é passado para a


variável (ponteiro) definida dentro da função. Neste cao, a função pode alterar o valor do
argumento no módulo chamador.

OBS: Quando um nome de um vetor (matriz) aparece como argumento de uma função,
o endereço do início do vetor é passada e os elementos não são copiados. Assim sendo,
a função pode alterar diretamente os valores do vetor.

Exemplo de função com passagem de parâmetros por referência:

#include <stdio.h>
main()
{ int x=10, y=30;
printf("Antes: x = %d y = %d\n", x, y);
troca(&x, &y);
printf("Depois: x = %d y = %d\n", x, y);
}
troca(int *a, int *b)
{ int temp;
temp = *a;
*a = *b;
*b = temp;
}

/*** Resposta: antes: x = 10 y = 30 depois: x = 30 y = 10 ****/


Ponteiros
São variáveis que contêm endereços de memória, para os quais estão apontando.

Noções da Linguagem C pág 29


main()
{ int *pi;
char *pc;
float *pf, *pg;

A especificação de tipo, espeficica a natureza do dado contido na variável para a qual o


ponteiro está apontado. O * (asterisco) identifica a declaração de um ponteiro.

São operadores que se relacionam com ponteiros:

& - quando antecede o nome de uma variável fornece o endereço daquela variável.

* - quando antecede um ponteiro, retorna o valor armazenado no endereço apontado


pelo ponteiro.

por exemplo:

main()
{ int x, y , *p;
x = 10; /* atribui 10 a x */
p = &x; /* atribui o endereço da variável x ao ponteiro p */
printf (" x = %d *p = %d",x,*p);
}

No exemplo acima o ponteiro p recebeu o endereço de x.


Logo, p contêm o endereço de x, ou seja, p aponta para a variável x.
*p refere-se ao conteúdo de x (no caso, 10).

*p, lê-se: o conteúdo apontado por p.

No exemplo acima,

*p = *p + 1; equivale a x = x + 1;

(conteudo apontado por p + 1 unidade)

Noções da Linguagem C pág 30


Atenção:

*p + 1 é diferente de *p++

*p + 1 : soma 1 ao conteúdo apontado por p (no exemplo anterior x = x + 1)

*p++: incrementa 1 "endereço" ao ponteiro, ou seja p = p + 1 (no exemplo anteiror, o


ponteiro p vai apontar para y).

*p++ equivale a *(p = p + 1), ou seja, incrementa p de 1 endereço inteiro (2 bytes).

p + 1 é equivalente a (*p)++ ou ++(*p)

Exemplo1:
main()
{ int *p, x=1, y =5;

p = &x; /* p recebe o endereço de x */


printf("\n valor x = %d *p = %d ",x,*p);
*p = *p + 1; /* o mesmo que (*p)++ */
printf("\n valor x = %d *p = %d ",x,*p);
p = &y; /* p recebe o endereço de y */
printf("\n valor y = %d *p = %d ",y,*p);
*p = *p - 1; /* o mesmo que (*p)- - */
printf("\n valor y = %d *p = %d ",y,*p);
}

No exemplo acima será exibido:


valor de x = 1 *p = 1
valor de x = 2 *p = 2
valor de y = 5 *p = 5
valor de y = 4 *p = 4

Exemplo2:
main()
{
int *p, x[3]={10,20.30};

p = &x; /* p recebe o endereço de x */


printf("\n valor x[0] = %d *p = %d ",x[0],*p);
p++; /* p = p + 1, p aponta p/ x[1] */
printf("\n valor x[1] = %d *p = %d ",x[1],*p);
}

No exemplo acima será exibido:


valor de x[0] = 10 *p = 10
valor de x[1] = 20 *p = 20

Noções da Linguagem C pág 31


Exemplo3:
main() main()
{ {
int i,*p,x[6]={2,5,7,9,1,3}; int i,*p,x[6]={2,5,7,9,1,3};

clrscr(); clrscr();
p = x; p = x;
for(i=0; i<6; i++) for(i=0; i<6; i++)
printf("\n posicao %d = %d”,i,*(p + i)); printf("\n posicao %d = %d”,i,x[i]));
} }

Ambos os programas do exemplo 3 exibirão:


posicao 0 = 2
posicao 1 = 5
posicao 2 = 7
posicao 3 = 9
posicao 4 = 1
posicao 5 = 3

Observação: No caso de vetores ou matrizes:

1) podemos atribuir o endereço do vetor das seguintes formas:

p = x; p recebe o endereço de x
p = &x; p recebe o endereço de x.
p = &x[0]; p recebe o endereço de x.

2) quando p contém o endereço inicial de x, podemos obter o valor da célula n, a partir de


*(p+n).
.
x[i] pode ser obtido através de *(p + i)

x[4] equivale a *(p+4)


x[53] equivale a *(p+53)

3) Os dois trechos de programas abaixo são equivalentes, exibindo os valores do vetor X:

int i,*p,x[6]={2,5,7,9,1,3}; int i,*p,x[6]={2,5,7,9,1,3};

p = &x; p = &x;
for(i=0; i<6; i++) for(i=0; i<6; i++)
printf("\n %d",x[i]); printf("\n %d",*(p + i));

Exemplo4:

Noções da Linguagem C pág 32


main()
{
int i,*p,x[6]={2,1,7,9,1,3};

p = &x;
printf("\n %d", *(p + 3));
printf("\n %d", *p + 3);
}

Observações:
1) p aponta para o primeiro elemento do vetor (célula de número 0)
2) o primeiro printf exibirá 9, ou seja, o valor da célula de número 3.
3) o segundo printf exibirá 5, ou seja, o valore da célula de número 0 mais 3.

Exemplo4:

main()
{
int i,*p,x[5];

clrscr();
for(i=0;i<5;i++)
x[i] = i * 3;
p = &x;
printf("\n %d ",*(p+4));
printf("\n %d ",*p+4);
}

Observações:
1) p aponta para o primeiro elemento do vetor (célula de número 0)
2) o primeiro printf exibirá 12, ou seja, o valor da célula de número 4.
3) o segundo printf exibirá 4, ou seja, o valore da célula de número 0 mais 4.

Atenção:
Ao trabalhar com ponteiros devemos observar atentamente o endereçamento dos
mesmos para não invadir endereços de memória não alocados, ou até mesmo
comprometer o conteúdo de outras variáveis, provocando erros inesperados.

Noções da Linguagem C pág 33


Noções da Linguagem C pág 34
Recursividade
Funções recursivas são funções que chamam a si próprias.
As funções em "C" podem ser recursivas, como por exemplo:
Por exemplo:

Utilizando função recursiva Método tradicional


main () main ()
{ {
int x; int x;
x=5; x=5;
printf(“%d”,fatorial(x)); printf(“%d”,fatorial(x));
} }

fatorial (int num) fatorial (int num)


{if (num <= 1) {int i,fat = 1;
return (1); for (i=num; i>1; i --)
else fat = fat * i
return (num * fatorial(num – 1)); return(fat);
} }

Quando uma função chama a si própria (recursividade) ela cria um novo conjunto de
variáveis automáticas que são independentes das do conjunto anterior, que fez a
chamada. Este processo, se repetido muitas vezes, tende a consumir rapidamente a
memória disponível na pilha (stack área).
Portando, deve-se tomar muito cuidado para não estourar essa área.

A grande vantagem do uso de codificação com recursividade, é o fato de ser compacto o


seu código. No entanto, deve ser feito com cuidado, antecedido de grande prática e
profundo conhecimento da linguagem.

O uso de rotinas recursivas é aconselhavel quando:

- O algorítmo exige que haja um "loop" na função.


- Variáveis no "loop" usam valores intermediários que são determinados no passo
anterior do "loop".
- Não há possibilidade de determinar estes valores a priori.

Noções da Linguagem C pág 35


Exemplo:
Programa para exibir os termos da série de Fibonacci: 1, 1, 2, 3, 5, 8, 13,.....

Utilizando função recursiva Método tradicional


main () main ()
{ {
int termo;
int termo;
for (termo=1; termo < 6; termo++)
printf (“ %d “, fibonacci(termo)); clrscr();
} for (termo=1; termo < 6; termo++)
printf (" %d ", fibonacci(termo));
getch();
fibonacci (int x) }
{ if (x <= 2)
return (1); fibonacci (int termo)
else {int termoAtual= 1, termoAnt1=1, termoAnt2=0;
return (fibonacci(x - 1) + fibonacci(x - 2)); int i;
} for (i=3; i<=termo; i++ )
{termoAnt2 = termoAnt1;
termoAnt1 = termoAtual;
termoAtual = termoAnt1 + termoAnt2;
}
return (termoAtual);
}

Noções da Linguagem C pág 36


ESTRUTURAS (Registros)

Estrutura (ou registro) é um conjunto de variáveis, de tipos distintos, logicamente


relacionadas.

Geralmente é usada para agregar neste único nome algumas variáveis que se relacionam
em termos lógicos, tais como uma tupla de uma tabela em banco de dados.

Na linguagem C, um registro é definido através do comando STRUCT.

Opção 1) Declarar um variável do tipo struct


struct
{ int matricula;
int cpf;
char nome[40];
} aluno;

Neste caso, aluno é uma variável do tipo registro.

Opção 2) Declarar uma estrutura


struct tipoAluno
{ int matricula;
int cpf;
char nome[40];
};

struct tipoAluno aluno; /* declara uma variável aluno */

Opção 3) Declarar um novo tipo

typedef struct
{ int matricula;
int cpf;
char nome[40];
} tipoAluno;

tipoAluno x; /* declara uma variável x, do tipo tipoAluno */

Obs: tipoAluno não ocupa espaço na memória. Já a variável X ocupa 44 bytes.

É fácil atribuir valores aos campos de um registro:

a) atribuição de valores na declaração da variável


tipoAluno x = {123, 456, “Jose”};
b)
x.matricula = 123;
x.cpf = 456;
strcpy(x.nome,”Jose”);

Noções da Linguagem C pág 37


Registros e Ponteiros
É muito comum usar ponteiros que apontam para registros, ou seja, guardam o endereço
de um registro, como por exemplo,

tipoAluno *p; /* define um ponteiro p para registros do tipoAluno */


tipoAluno x;
p = &x; /* agora, p aponta para x */
(*p).matricula = 123; /* o mesmo x.matricula = 123 */
(*p).cpf = 456;

ATENÇÂO! A expressão *p.cpf, que equivale a *(p.cpf), tem significado muito diferente
de (*p).cpf .
A expressão p->cpf é uma abreviatura muito útil para a expressão (*p).cpf :

p->cpf = 123456; /* mesmo efeito que (*p).cpf = 123456 */

Exemplo:
main()
{
typedef struct /* definindo um novo tipo em C */
{ char nome[30];
int matricula;
long int cpf;
} tipoAluno;

tipoAluno aluno={"Ana Maria",123,729933456};


tipoAluno *p;

p = &aluno; /* p recebe o endereço de aluno, logo p aponta para aluno */

printf("nome: %s matr: %d cpf: %lu \n", aluno.nome, aluno.matricula, aluno.cpf);


printf("nome: %s matr: %d cpf: %lu \n", p->nome, p->matricula, p->cpf);

getch();

No caso acima, as duas instruções “printf” exibirão o mesmo resultado:


nome: Ana Maria matr: 123 cpf: 729933456

Noções da Linguagem C pág 38


/* *************************************************************************
Exemplo 1 – Declaração e utilizacao de Struct
************************************************************************ */
#include <stdio.h>
main()
{ typedef struct
{ char nome[30];
long int telef;
long int cpf;
} tipoAgenda;

tipoAgenda agenda;

strcpy(agenda.nome,"Ana Maria");
agenda.telef = 2223040;
agenda.cpf = 123456;

printf("\n nome: %s ",agenda.nome);


printf(" tel: %lu “,agenda.telef);
printf(" cpf: %lu ",agenda.cpf);

/* *************************************************************************
Exemplo 1.a – Idem ao anterior com a utilização de ponteiro
************************************************************************ */

#include <stdio.h>
main()
{ typedef struct
{ char nome[30];
long int telef;
long int cpf;
} tipoAgenda;

tipoAgenda agenda, *p;


p = &agenda;

strcpy(p->nome,"Ana Maria");
p->telef = 2223040;
p->cpf = 123456;

printf("\n nome: %s ", (*p).nome);


printf(" tel: %lu “, p->telef);
printf(" cpf: %lu ", (*p).cpf);

Noções da Linguagem C pág 39


/* *************************************************************************
Exemplo 2- Declaração e utilização de Struct
************************************************************************ */

#include <stdio.h>

main()
{

typedef struct
{ char nome[30];
long int telef;
long int cpf;
} tipoAgenda;

tipoAgenda agenda;

clrscr();
puts("\n Informe o nome: ");
gets(agenda.nome);
puts("\n Informe o telefone: ");
scanf("%lu",&agenda.telef);
puts("\n Informe o cpf: ");
scanf("%lu",&agenda.cpf);
clrscr();

clrscr();

printf("nome: %s tel: %lu cpf: %lu", agenda.nome, agenda.telef, agenda.cpf);


getch();

Noções da Linguagem C pág 40


Alocação Dinâmica de Memória

Código Executável
(Assembler)

(memória baixa- 00xx)

Área das
Variáveis Globais
•Stack (pilha) – Área onde são alocados os
parâmetros e as variáveis locais das
Stack funções.
(pilha)
Quando chamamos uma função, é alocada
memória para os seus parâmetros e
variáveis locais no topo da pilha.
Ao término da função estes são
Área Livre desempilhados (espaço é liberado).

•Heap – Área onde é feita a alocação


dinâmica de memória.
Heap
•O sentido de crescimento da stack é inverso
(memória alta- FFxx) ao do heap para que se possa aproveitar ao
máximo o espaço da área livre.

Noções da Linguagem C pág 41


Função malloc()
A função malloc() aloca uma área de memória no heap e devolve o endereço inicial
dessa área. Caso não haja memória suficiente disponível, devolve um ponteiro nulo
(NULL).

Exemplo:

#include <stdio.h>
main()
{ typedef struct
{ int matricula;
char nome[30];
int cpf; } tipoAluno;
tipoAluno *p;

p = malloc(sizeof(tipoAluno)); /* aloca 34 bytes no heap */


if (p == NULL)
{ printf(“Erro de alocação de memória. Encerrando o programa”);
exit(1);
}
}

Observação: Procure sempre utilizar o operador unário sizeof, em vez de especificar a


quantidade de bytes a serem alocados pelo comando malloc();
No exemplo acima, é melhor utilizar p = malloc(sizeof(tipoAluno)) do que
p = malloc(34);
Isto garante a portabilidade do programa para diferentes sistemas operacionais, ou seja,
garante que ele será corretamente executado nos ambientes windows, Unix, linux, dos,
etc...
Outro problema que pode ser evitado com a utilização do sizeof() é que o compilador pode
trabalhar com o alinhamento de palavras ou parágrafos (Um parágrafo corresponde a 16
bytes). Desta forma, a estrutura tipoAluno acima pode ser maior que 34 bytes,
dependendo do compilador e do sistema operacional.

Função free()

A função free() libera a memória alocada no heap.

No exemplo acima, para liberar a memória alocada no heap, apontada pelo ponteiro p,
basta utilizar o seguinte comando:

free(p);

Noções da Linguagem C pág 42


#include <stdio.h>

main()
{

typedef struct
{ int dia;
int mes;
int ano;
} tipoData;

typedef struct
{ int matr;
int cpf;
tipoData dtnasc;
} tipoAluno;

tipoAluno x, *p;

clrscr();

if ((p = malloc(sizeof(tipoAluno))) == NULL)


{ puts("Erro de alocacao de memoria");
exit();
}
p->matr = 123;
p->cpf = 456;
p->dtnasc.dia = 01;
p->dtnasc.mes = 11;
p->dtnasc.ano = 1980;

printf("matricula: %d cpf: %d data nacimento: %d/%d/%d",p->matr,p->cpf,p-


>dtnasc.dia,p->dtnasc.mes,p->dtnasc.ano);

if (p != NULL)
{ printf("\n Ponteiro p aponta para uma estrutura de %d bytes",sizeof(tipoAluno));
free(p);
}

if (p == NULL)
printf("\n Espaco de memoria apontado por p foi liberado....");

getch();

O programa acima exibirá na tela:

Matricula: 123 cpf: 456 data nascimento 01/11/1980


Ponteiro p aponta para uma estrutura de 10 bytes
Espaço de memória apontado por p foi liberado

Noções da Linguagem C pág 43


#include “stdio.h"

main()
{

typedef struct
{ int matr;
int cpf;
float cr;
} tipoAluno;

tipoAluno *p[10]; /* vetor com 10 ponteiros */


int i;

for (i=0;i<10;i++) /* aloca espaço no heap para 10 alunos */


{
if ((p[i] = malloc(sizeof(tipoAluno))) == NULL)
{ puts("Erro de alocacao de memoria");
exit();
}
}

p[0]->matr = 123;
p[0]->cpf = 456;
p[0]->cr = 8.5;

clrscr();
printf("matricula: %d cpf: %d cr: %5.2f \n", p[0]->matr, p[0]->cpf, p[0]->cr);

for (i=0;i<10;i++)
free(p[i]);

if (p[0] == NULL)
printf("\n Espaco liberado....");
getch();

Noções da Linguagem C pág 44


Estruturas de dados clássicas
Vetores ou arrays

Vetores, ou arrays, são estruturas de dados lineares e estáticas, isto é, são compostas por
um número fixo (finito) de elementos de um determinado tipo de dados.
O tempo de acesso aos elementos de um vetor é muito rápido, sendo considerado
constante: os elementos são acessados pelo seu índice no vetor.
Porém, a remoção de elementos pode ser custosa se não for desejável que haja espaços
"vazios" no meio do vetor, pois nesse caso é necessário "arrastar" de uma posição todos
os elementos depois do elemento removido.

Essa é uma estrutura muito recomendada para casos em que os dados armazenados não
mudarão, ou pouco mudarão, através do tempo.

Lista
A Lista é uma estrutura de dados linear, composta por uma sequência de nós ou nodos.
Existem duas formas de representar as listas:
- Alocação sequencial: os nós ocupam posições sequenciais contíguas
- Alocação dinâmica (encadeada): os nós (celulas) apontam para o próximo
elemento da lista.

Para "ter" uma lista dinâmica (ligada), devemos guardar seu primeiro elemento,

Lista simplesmente encadeada (LSE): cada nó da lista ponta para o próximo nó.
A LSE só pode ser percorrida em um único sentido

Lista duplamente encadeada (LDE): cada nó da lista aponta para o nó anterior e


posterior.
A LDE pode ser percorrida em ambos os sentidos

Lista Circular: O primeiro nó aponta para o último e o último aponta para o primeiro.

Pilha
.
É uma lista linear em que todas as inserções, as remoções e os acessos são feitos numa
só extremidade, chamada topo.

São baseadas no princípio LIFO (last in, first out) ou UEPS (Último a entrar, primeiro a sair)
onde os dados que foram inseridos por último na pilha serão os primeiros a serem
removidos.

Existem duas funções que se aplicam a todas as pilhas: PUSH, que insere um dado no
topo da pilha, e POP, que remove o item no topo da pilha.
Exemplos de pilhas são as pilhas de pratos, pilhas de livros, etc.

Noções da Linguagem C pág 45


Considere a pilha abaixo:

13
19
14
10

Qual será seu o estado final, após executar as seguintes instruções:

Pop x
Pop y
Pop z
Push y 14
Push x 13
Push z 19
10
Resposta:

Qual será o estado final das pilhas abaixo, após executar as seguintes instruções:

4
3
2
1
A B C

Push(B,pop(A))
Push(C,pop(A))
Push(B,pop(A))
Push(C,pop(A))
Push(A,pop(B))
Push(C,pop(B))
Push(C,pop(A))
2
4
1
3
A B C

Resposta:

Noções da Linguagem C pág 46


Fila
As filas são estruturas baseadas no princípio FIFO (first in, first out) ou PEPS (Primeiro a
entrar, primeiro a Sair) em que os elementos que foram inseridos no início são os primeiros
a serem removidos.
Uma fila possui duas funções básicas: ENQUEUE (INC), que adiciona um elemento ao
final da fila, e DEQUEUE (DEL), que remove o elemento no início da fila.

Ex: fila de bancos, porta copos vertical, porta cigarros vertical,

A política de inserção e remoção em filas é conhecida como:

A) FILO
B) LIFO
C) SIFO
D) LILO
E) FIFO
Resposta: E

A política de inserção e remoção em pilhas é conhecida como:

A) LIFO
B) FILO
C) MIFO
D) LILO
E) FIFO
Resposta: A

Qual será o estado final da Fila abaixo após executar as instruções:

inicio

Y F C

INC “X”
INC “T”
DEL
DEL
INC “R”
INC “J” Resposta:
DEL C–X–T–R-J

Deque de entrada restrita: A remoção poder ser feita por qualquer extremidade, porém as
inclusões podem ser feitas apenas por uma.

Deque de saída restrita: A inclusão poder ser feita por qualquer extremidade, porém as
remoções podem ser feitas apenas por uma.

Noções da Linguagem C pág 47


Estruturas de Dados Dinâmicas - Implementação Física

Listas Simplesmente Encadeadas (LSE)

A grande diferença da lista para as outras estruturas de dados, é que as listas não
possuem critério de inclusão e remoção de dados.

Uma lista encadeada tem necessariamente uma variável ponteiro apontando para o seu
primeiro elemento. Essa variável será utilizada sempre, mesmo que a lista esteja vazia, e
deverá apontar sempre para o início da lista (primeiro elemento). Caso esta primeira
variável não seja atualizada corretamente (no caso da inclusão de um elemento na
primeira posição), a lista poderá se perder na memória e não ser mais acessível.

Um elemento da lista é composto de duas partes: a informação propriamente dita e uma


conexão com o próximo elemento.
São chamadas de simplesmente encadeadas porque possuem somente o endereço do
seu próximo (próximo elemento).

typedef struct celula


{ int dados; /* campo para os dados */
struct celula *proximaCelula; /* ponteiro para a proxima celula */
}tipoCelula; /* nome do novo tipo de estrutura */

Uma lista encadeada é toda alocada dinamicamente.


Sabemos que a memória alocada dinamicamente não possui um nome como acontece nas
variáveis comuns, portanto, todas as listas devem possuir uma variável padrão que vai
apontar para o seu inicio.

Uma vez que o primeiro elemento será uma célula, e cada célula é uma estrutura, então a
variável que apontará para o início da lista será um ponteiro de estrutura.

A variável início mostrada na figura acima será então definida como:

TipoCelula * inicio; /* ponteiro para uma estrutura tipoCelula */

Uma vez que a lista não possui elementos ainda, então:

inicio = NULL;

Para uma lista vazia (sem células), a variável inicio possui valor NULL:

Noções da Linguagem C pág 48


Incluindo o primeiro elemento (primeira célula):

TipoCelula aux; /* variavel auxiliar: ponteiro para uma estrutura tipoCelula

// reserva de memória para nova célula com endereço de memória alocada armazenado
em aux:
aux = malloc (sizeof (tipoCelula));

Temos então:

e precisamos atualizar os ponteiros para que a lista receba seu primeiro elemento. Sendo
assim:
1) aux->proximaCelula = NULL; // atribui NULL ao campo proximacelula da célula
apontada por aux
2) inicio = aux; // copia o endereco de aux em inicio

Para inserir dados nesta primeira célula, podemos utilizar o ponteiro aux, que permanece
apontando para a célula.

aux->dados = 10; /*atribui o valor 10 ao campo dados da célula pontada por aux

Obs: Cada célula de uma lista também é chamada de nó ou nodo

O programa a seguir cria uma lista com tres nós, conforme exibida a abaixo

inicio

matricula proximo

11 2 3

Noções da Linguagem C pág 49


#include <stdio.h>
#include <stdlib.h>

main( )
{ typedef struct no
{int matricula;
stuct no *proximo;
} tipoNo;

tipoNo *inicio, *novo, *atual;

/* cria o primeiro registro (nodo 1) */


novo = malloc(sizeof (tipoNo)); /* aloca o nó na memória */
novo->matricula = 1; /* atribui 1 para amatrícula */
novo->proximo = NULL; /* NULL = Fim */
inicio = novo; /* inicio aponta para o primeiro nó */
atual = novo;
/* cria o segundo registro (nodo 2) */
novo = malloc(sizeof (tipoNo)); /* instancia o segundo nó */
novo->matricula = 2;
novo->Proximo = NULL; /* NULL = Fim */
atual->proximo = novo; /* nó anterior aponta para o segundo lista */
atual = novo;

/* cria o terceiro registro (nó 3) */


novo = malloc(sizeof (tipoNo)); /* instancia o terceiro nó */
novo->matricula = 3;
novo->Proximo = NULL; /* NULL = Fim */
atual->proximo = novo; /* nó anterior aponta para o segundo lista */
atual = novo;

/* exibe os nós da lista */


atual = inicio;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getchar();
}

Noções da Linguagem C pág 50


O programa abaixo cria uma lista dinâmica (FILA) com 5 nós, contendo as matrículas:
1, 2, 3, 4, e 5, respectivamente.

#include "stdio.h"

main()
{ typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *inicio, *novo, *atual;


int i;
clrscr();

inicio = atual = NULL;


for (i=1;i<6;i++)
{ novo = malloc(sizeof(tipoNo));
novo->matricula = i;
novo->proximo=NULL;
if (inicio == NULL)
inicio = novo;
else
atual->proximo = novo;
atual = novo;

}
/* exibe a lista */
atual = inicio;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getch();

Inicio

matricula prox

11 2 3 4 5

Noções da Linguagem C pág 51


Supondo que serão informados os números 10, 15, 19 e 23, o que será
exibido no trecho de programa abaixo ?
#include "stdio.h"
main()
{ typedef struct no
{ int valor;
struct no *proximo;
}tipoNo;

tipoNo *inicio, *novo, *atual;


int i;

inicio = atual = NULL;


for (i=1;i<5;i++)
{ clrscr();
novo = malloc(sizeof(tipoNo));
printf("Informe o valor do elemento %d: ",i);
scanf("%d",&novo->valor);
novo->proximo=NULL;
if (inicio == NULL)
inicio = novo;
else
atual->proximo = novo;
atual = novo;

clrscr();
atual = inicio;
while (atual != NULL)
{ printf("\n %d ",atual->valor);
atual=atual->proximo;
}
getch();

Noções da Linguagem C pág 52


O programa abaixo cria uma lista dinâmica (tipo Pilha) com 3 nós, contendo as matrículas:
1, 2, e 3.

topo

matricula prox
#include "stdio.h" a

main()
3 2 1
{

typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *topo, *novo, *atual;

clrscr();
/* inclui primeiro registro na pilha */
novo = malloc(sizeof(tipoNo));
novo->matricula = 1;
novo->proximo = NULL;
topo = novo;

/* inclui segundo registro na pilha */


novo = malloc(sizeof(tipoNo));
novo->matricula = 2;
novo->proximo = topo;
topo = novo;

/* inclui terceiro registro na pilha */


novo = malloc(sizeof(tipoNo));
novo->matricula = 3;
novo->proximo = topo;
topo = novo;

/* exibe os registros da pilha */


atual = topo;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getch();
}

Será exibido: matricula = 3


matricula = 2
matricula = 1

Noções da Linguagem C pág 53


O programa abaixo cria uma lista encadeada do tipo Pilha, com as matrículas, 1, 2,3,4 e 5.

#include "stdio.h"
main()
{
typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *topo, *novo, *atual;


int i;

clrscr();

topo = atual = NULL;


for (i=1;i<6;i++)
{ novo = malloc(sizeof(tipoNo));
novo->matricula = i;
if (topo == NULL)
novo->proximo = NULL;
else
novo->proximo = topo; /* aponta para o nó anterior */
topo = novo;
}
/* exibe os registros da pilha */
atual = topo;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getch();

Será exibido: matricula = 5


matricula = 4
matricula = 3
matricula = 2
matricula = 1

topo

matricula proximo

15 4 3 2 1

Noções da Linguagem C pág 54


Supondo que serão informados os números 10, 15, 19 e 23, o que será
exibido no trecho de programa abaixo ?
#include "stdio.h"
main()
{
typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *topo, *novo, *atual;


int i;

topo = atual = NULL;


for (i=1;i<5;i++)
{ clrscr();
novo = malloc(sizeof(tipoNo));
puts("Informe o numero da matricula: ");
scanf("%d", &novo->matricula);
if (topo == NULL)
novo->proximo = NULL;
else
novo->proximo = topo;
topo = novo;
}
/* exibe os registros da pilha */
atual = topo;
while (atual != NULL)
{ printf("\n %d ",atual->matricula);
atual=atual->proximo;
}
getch();

Noções da Linguagem C pág 55


Percorrendo a lista

Para percorrer uma lista (fila ou pilha) temos que utilizar uma variável auxiliar (ponteiro
auxiliar).
Inicialmente, aux deverá para o início da fila (ou para o topo, no caso das pilhas).
Depois, aux receberá o endereço contido no campo proximo, ou seja, receberá o
endereço da próxima célula.
Enquanto aux for diferente de NULL, repete-se o processo acima.
Observe o trecho de programa a seguir que exibirá todos os nós da lista.

aux = inicio;
while (aux != NULL)
{ printf("\n matricula = %d ",aux->matricula);
aux = aux->proximo;
}
getchar();
}

Inicio

matricula proximo

11 2 3 4 5

Noções da Linguagem C pág 56


Incluindo um nó no final da lista (Fila)

O último nó da lista não apontava para nenhum outro, agora, este deverá apontar para o
novo nó.

Início 10 27
valor Próximo valor Proximo

- se a lista não é vazia, percorrer a lista até a última célula (aux apontará para o último nó)
- alocar memória para o novo nó (malloc)
- atribuir dados aos campos de dados
- atribuir NULL ao campo ponteiro da nova célula incluída
- atribuir ao campo ponteiro da última célula o endereço da nova célula

TipoNo *aux, *inicio, *novo;

if (inicio != NULL)
{ aux = inicio;
while(aux->proximo != NULL)
aux = aux->proximo;

novo = malloc(sizeof(tipoNo))
novo->valor = 55;
novo->proximo = NULL;
aux->proximo = novo;
}

Desta forma, a lista ficará assim:

Início

10 27 55
valor Proximo valor Proximo Valor Proximo

Noções da Linguagem C pág 57


Excluindo o primeiro no da lista

Início 2 7 3
Dado Prox Dado Prox Dado Prox

apontar aux para o inicio da fila (aux = inicio)


início recebe o endereço do segundo nó (inicio = aux-prox ou inicio = inicio->prox)
retirar o primeiro nó da memória ( free(aux) );

tipoNo *aux, *inicio;


aux = inicio; /* aux aponta para o início da fila */
inicio = aux->prox; /* inicio aponta para o segundo nó */
free(aux); /* libera da memória do primeiro nó */

A lista ficará da seguinte forma:


inicio

Dado prox

17 3

Noções da Linguagem C pág 58


Excluindo um nó qualquer da lista

Observe a lista:

Início 2 7 3
Dado Prox Dado Prox Dado Prox

Desejamos fazer a exclusão do nó com valor 7 (segundo nó)


- percorrer a lista até o nó anterior ao de valor 7 (primeiro nó)
- atribuir o endereço do nó de valor 7 a um ponteiro auxiliar (para poder liberar a memória)
- alterar o endereço apontado pelo nó 1, para que ela aponte para onde o nó de valor 7 aponta
atualmente (nó 3).
- liberar a memória do nó de valor 7 ( free(aux) )

A lista ficará da seguinte forma:

Início 2 7 3
Dado Prox Dado Prox Dado Prox

tipoNo aux, apaga;


aux = inicio;
while(aux->prox->valor != 7)
aux = aux->prox;
apaga = aux->prox; /* apaga recebe o endereço do nó 2 */
aux->prox = apaga->prox; /* nó 1 aponta para o nó 3 */
free(apaga); /* libera área do nó 2 */

Noções da Linguagem C pág 59


Pilha

/* ********************
Exemplo de Pilhas
********************** */

#include "stdio.h"

main()
{
typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *topo, *novo, *atual;

clrscr();

novo = malloc(sizeof(tipoNo));
novo->matricula = 1;
novo->proximo = NULL;
topo = novo;

/* inclui segundo registro na pilha */


novo = malloc(sizeof(tipoNo));
novo->matricula = 2;
novo->proximo = topo;
topo = novo;

/* inclui terceiro registro na pilha */


novo = malloc(sizeof(tipoNo));
novo->matricula = 3;
novo->proximo = topo;
topo = novo;

atual = topo;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getch();

Noções da Linguagem C pág 60


/* ********************
Exemplo de Listas -
********************** */

#include "stdio.h"
#include <stdlib.h>

main()
{
typedef struct no
{int matricula;
struct no *proximo;
}tipoNo;

tipoNo *topo, *novo, *atual;


int i;

clrscr();

topo = atual = NULL;


for (i=1;i<6;i++)
{ novo = malloc(sizeof(tipoNo));
novo->matricula = i;
if (topo == NULL)
novo->proximo = NULL;
else
novo->proximo = topo;
topo = novo;
}

atual = topo;
while (atual != NULL)
{ printf("\n matricula = %d ",atual->matricula);
atual=atual->proximo;
}
getch();

Noções da Linguagem C pág 61


Listas Duplamente Encadeadas (LDE)

As listas duplamente encadeadas são aquelas em que cada nó possui não só o endereço
do nó anterior mas também o endereço do próximo. Observe o gráfico e a declaração
abaixo:

typedef struct no
{
struct no *anterior; // ponteiro da celula anterior
int indice; // campo de dado
char nome[15]; // campo de dado
struct no *proxima; // ponteiro da proxima celula
} tipoNo;

tipoNo *cabeca, *cauda;

Exemplo de manipulação de uma lista duplamente encadeada:

#include <stdio.h>

#define FALSO 0
#define VERDADEIRO 1

#define OK 1
#define ERRO 0

typedef struct no
{ int dado;
struct no *proximo, *anterior;
} tipoNo;

void inicializa_ld (tipoNo **LD)


{
*LD = NULL;
}

void posiciona_inicio_ld (LD)


Nodo_LD **LD;
{
if (*LD != NULL)
{
while ((*LD) -> Anterior != NULL)
*LD = (*LD) -> Anterior;
}
}

int insere_antes_ld (LD, Dado)


Nodo_LD **LD;
Tipo_Dado Dado;

Noções da Linguagem C pág 62


{
Nodo_LD *novo, *aux;

novo=(Nodo_LD *)calloc(1, sizeof(Nodo_LD));


if (novo == NULL)
return (ERRO);

novo -> Dado = Dado;

if (*LD == NULL)
{
*LD = novo;
novo -> Anterior = NULL;
novo -> Proximo = NULL;
}
else
{
aux=(*LD)->Anterior;
(*LD)->Anterior=novo;
novo->Proximo=(*LD);
novo->Anterior=aux;
if (aux != NULL)
aux->Proximo=novo;
}
return(OK);
}

int insere_inicio_ld (LD, Dado)


Nodo_LD **LD;
Tipo_Dado Dado;
{
posiciona_inicio_ld(LD);
return(insere_antes_ld(LD, Dado));
}

/*****

Programa Principal

*****/

main()
{
Nodo_LD *ListaDupla;

inicializa_ld(&ListaDupla);
insere_inicio_ld(&ListaDupla,1);
insere_inicio_ld(&ListaDupla,2);
insere_inicio_ld(&ListaDupla,3);

posiciona_inicio_ld(&ListaDupla);

printf("Nodo 1 - Dado = %d\n",ListaDupla->Dado);


printf("Nodo 2 - Dado = %d\n",ListaDupla->Proximo->Dado);
printf("Nodo 3 - Dado = %d\n",ListaDupla->Proximo->Proximo->Dado);

getch();
}

Noções da Linguagem C pág 63


Fila

As filas são listas que possuem um critério especial. As inserções somente podem ser realizadas no final e as
remoções somente no início da estrutura. O critério da fila é conhecido como FIFO (First In First Out) ou
PEPS (primeiro a entrar primeiro a sair). Basta lembrar de uma fila de banco (não pode furar a fila!) onde os
novos clientes sempre se posicionam no final da fila e o caixa atende (retira da fila) os primeiro cliente e
assim sucessivamente.

O ponto de inserção pode ser definido no inicio ou no final, sabendo que as remoções serão sempre na
extremidade oposta.

Para inserir dados no início:

- variável ponteiro X (auxiliar para a operação de inserção) recebe uma alocação de memória (nova célula)
- cópia dos dados na nova célula
- ponteiro da nova célula recebe o valor do início (apontará para o primeiro elemento)
- início aponta para nova célula (recebe o endereço armazenado em X)

Para excluir no final:

Para exclusão no final:

- variável ponteiro X (auxiliar para a operação de exclusão) recebe o valor de início e passa a apontar para o
início da lista
- verificar se a fila não está vazia!
- verificar se o endereço da próxima célula é diferente de 0 (nulo). Essa informação está disponível no
ponteiro de conexão da célula apontada atualmente por X.
- variável X recebe o endereço da próxima célula da fila (este endereço está armazenado na célula
atualmente apontada por X). Repetir isso até que o passo anterior não seja satisfeito (X deverá estar
apontando para o penúltimo elemento da lista)
- variável ponteiro AUX (outra variável auxiliar para exclusão) recebe o endereço da última célula da fila. Este
endereço está armazenado na célula atualmente apontada por X.
- liberar a memória apontada por AUX
- ponteiro de próxima célula do elemento apontado por X recebe 0 (zero).

Pilha

Nas pilhas, a extremidade escolhida é sempre a que será utilizada para inserir novos elementos e remover os
existentes. O critério da pilha é LIFO (Last In First Out) ou UEPS (último a entrar, primeiro a sair).

O TOPO da pilha pode ser no início ou no final da lista, sendo que as operações deverão
acontecer sempre na extremidade (TOPO) escolhida.
Usaremos os comandos PUSH e POP para inserir e retirar dados na pilha, respectivamente.

Observe os exemplos:

Considere a pilha abaixo:

13
19
14
10

Qual será seu o estado final, após executar as seguintes instruções:

Pop x
Pop y
Pop z
Push y 14
Push x

Noções da Linguagem C pág 64


Push z 13
19
10
Resposta:

Para a operação de inserção, com TOPO no início da lista, são necessários os seguintes passos:
- variável ponteiro X (auxiliar para a operação de inserção) recebe uma alocação de memória (nova célula)
- cópia dos dados na nova célula
- ponteiro da nova célula recebe o valor de TOPO (apontará para o primeiro elemento)
- TOPO aponta para nova célula (recebe o endereço armazenado em X)

Para exclusão:

- variável ponteiro X (auxiliar para a operação de exclusão) recebe o valor de TOPO e passa a apontar para o
início da lista
- verificar se a pilha não está vazia!
- TOPO recebe o endereço da próxima célula da pilha (este endereço está armazenado na célula atualmente
apontada por X).
- liberar a memória apontada por X.

Exercícios

1) Qual será o estado final das pilhas abaixo, após executar as seguintes instruções:

4
3
2
1
A B C

Push(B,pop(A))
Push(C,pop(A))
Push(B,pop(A))
Push(C,pop(A))
Push(A,pop(B))
Push(C,pop(B))
Push(C,pop(A))
2
4
1
3
A B C
Resposta:

Noções da Linguagem C pág 65


3) Sobre o trecho de programa abaixo, pode-se afirmar que:

typedef struct no
{ int matricula;
struct no *proximo;
} tipoNo;
Pode ser a estrutura de uma fila estática
Pode ser a estrutura de um registro de vetor
Pode ser a estrutura de uma lista duplamente encadeada
Pode ser a estrutura de uma lista encadeada
Pode ser a estrutura de uma pilha estática
Resposta: D
4) Sobre o trecho de programa abaixo, pode-se afirmar que:

Typedef struct no
struct no *anterior ;
int matricula;
struct no *proximo;
} tipoNo;
Pode ser a estrutura de uma fila estática
Pode ser a estrutura de um registro de vetor
Pode ser a estrutura de uma lista duplamente encadeada
Pode ser a estrutura de uma lista encadeada
Pode ser a estrutura de uma pilha estática
Resposta: C

5) A política de inserção e remoção em filas é conhecida como:

FILO
LIFO
SIFO
LILO
FIFO
Resposta: E

Noções da Linguagem C pág 66


6) A política de inserção e remoção em pilhas é conhecida como:

LIFO
FILO
MIFO
LILO
FIFO
Resposta: A
7) Sobre o deque de saída restrita podemos afirmar que:

Podemos excluir em ambas as extremidades


Podemos incluir em apenas uma extremidade
Podemos excluir em apenas uma extremidade
Não é permitido excluir elementos
Não é permitido incluir elementos
Resposta: C

Noções da Linguagem C pág 67


8) Sobre o deque de entrada restrita podemos afirmar que:

A) Podemos excluir em ambas as extremidades


Podemos incluir em apenas uma extremidade
Podemos excluir em apenas uma extremidade
Não é permitido excluir elementos
Não é permitido incluir elementos
Resposta: B

9) Qual será o estado final da fila, após executar as instruções abaixo:

INI FIM

A G F

INC C
INC D
INC H
DEL
DEL
INC B
Resposta: F – C – D – H - B

9) Cite duas características da LSE Resposta:


Cada nó aponta para o nó posterior
Só pode ser percorrida em um único sentido

10) Cite duas características da LDE Resposta:


Cada nó aponta para o nó posterior e apar o nó
anterior
Só pode ser percorrida nos dois sentidos s

11) Considerando a lista abaixo que implementa uma PILHA qual será o último valor a ser excluído desta
lista?

End 00 01 02 03 04 05 06
Valor K W I A H Z B
Link 03 Null 06 05 02 04 01
a) W
b) K
c) A
d) B
e) H

Noções da Linguagem C pág 68


12- Suponha que a tabela abaixo represente uma lista encadeada de nomes, organizada sobre os 5
elementos de um vetor. Qual o primeiro elemento desta lista?

Elemento Nome Próximo


1 Maria 0
2 Antonio 4
3 João 5
4 Carlos 3
5 Josefina 1

a) Antonio b) Carlos c) Josefina d) João e) Maria

13) Suponha que a tabela deva representar uma lista duplamente encadeada de cores, organizada sobre os
5 elementos de um vetor:

Elemento Cor Anterior Próximo


1 ? 4 2
2 ? 1 3
3 ? 2 0
4 ? 5 1
5 ? 0 4

Sabendo-se que a ordem das cores na lista é BEGE – VERDE – AZUL – VERMELHO –
AMARELO, a coluna intitulada cor, na tabela acima, deveria apresentar, de cima para
baixo, o seguinte preenchimento:
a) BEGE- VERMELHO-AMARELO-AZUL-VERDE
b) AZUL-BEGE-VERDE-VERMELHO-AMARELO
c) AMARELO-AZUL-BEGE-VERMELHO-VERDE
d) AZUL-VERMELHO-AMARELO-VERDE-BEGE

Noções da Linguagem C pág 69

You might also like