Escolar Documentos
Profissional Documentos
Cultura Documentos
DE IPATINGA
1
SUMÁRIO
01
1.1 DEFINIÇÃO DE OPERADOR .................................................................................... 7
1.2 CLASSES E TIPOS DE OPERADORES ........................................................................ 8
1.3 DEFINIÇÃO E RESOLUÇÃO DE EXPRESSÕES ........................................................ 16
1.4 TIPOS DE EXPRESSÕES........................................................................................... 20
FIXANDO O CONTEÚDO ...................................................................................... 24
02
2.1 ESTRUTURAS ........................................................................................................... 29
2.2 ENUMERAÇÕES ..................................................................................................... 33
2.3 PONTEIROS ............................................................................................................ 37
FIXANDO O CONTEÚDO ...................................................................................... 44
03
3.1 SELEÇÃO SIMPLES ................................................................................................. 50
3.2 SELEÇÃO COMPOSTA .......................................................................................... 51
3.3 SELEÇÃO MÚLTIPLA............................................................................................... 56
FIXANDO O CONTEÚDO ...................................................................................... 59
04
4.1 REPETIÇÃO COM TESTE NO INÍCIO...................................................................... 65
4.2 REPETIÇÃO COM TESTE NO FINAL ....................................................................... 67
4.3 REPETIÇÃO COM VARIÁVEL DE CONTROLE........................................................ 69
FIXANDO O CONTEÚDO ...................................................................................... 73
05
5.1 MODULARIZAÇÃO DE CÓDIGO .......................................................................... 78
5.2 FUNÇÕES............................................................................................................... 79
5.3 TIPOS DE PASSAGEM DE PARÂMETRO................................................................. 84
FIXANDO O CONTEÚDO ...................................................................................... 88
06
6.1 ARMANEZAMENTO DE DADOS ............................................................................ 94
6.2 ABERTURA E FECHAMENTO DE ARQUIVOS.......................................................... 95
6.3 LEITURA E ESCRITA EM ARQUIVOS ....................................................................... 99
FIXANDO O CONTEÚDO .................................................................................... 104
5
CONFIRA NO LIVRO
6
OPERADORES E EXPRESSÕES UNIDADE
int main()
{
int a, b, c;
a = 10;
b = 20;
c = a + b;
}
Fonte: Elaborado pelo Autor (2021)
7
geral, possui alguma seção dedicada a apresentar e detalhar os operadores
disponíveis naquela linguagem.
É interessante dizer que os operadores das linguagens de programação são
muito semelhantes aos operadores usados na matemática. Sendo assim, uma
operação sempre envolve um operador e um conjunto de operandos, como
ilustrado na Figura 2.
Operador
Operando Operando
a+ b
Operação
Fonte: Elaborado pelo Autor (2021)
8
Como o número de operandos pode variar, os operadores podem ser
classificados de acordo com essa quantidade de operandos necessária para
executar a sua operação. A Tabela 1 lista as classes de operadores de acordo com
a quantidade de operandos requeridos.
● Relacionais: são usados para comparar elementos que podem ser desde
números inteiros e reais, até caracteres ou tipos de dados especiais. As
opções de comparação incluem igualdade, diferença, maior, maior ou
igual, menor e menor ou igual.
9
● Atribuição: constituem o operador simples de atribuição de valor à variável e
algumas variações interessantes como as atribuições seguidas de alguma
operação aritmética.
10
Para se ter uma ideia mais prática da quantidade, tipos e classes de
operadores que são suportados por uma linguagem de programação real, o
Quadro 2 lista todos os 36 operadores disponíveis na linguagem C.
11
Avaliando essa lista de operadores, pode-se identificar algumas questões
interessantes. Em primeiro lugar, a linguagem C oferece operadores de todas as 3
classes, isto é, operadores unários e binários, e também um operador ternário que é
usado para escrever uma estrutura de seleção simples. É preciso ficar atento à
classe do operador para que se possa empregá-lo da forma correta.
Além disso, não é difícil notar que muitos operadores têm a mera finalidade
de simplificar a escrita do código ao oferecer uma espécie de atalho para se
executar uma determinada tarefa. Esse é o caso do operador de atribuição com
adição (“+=”) que atualiza uma variável com a soma entre seu valor atual e o
operando utilizado na operação, como exemplificado no código ilustrado na Figura
3.
int main()
{
int a, b;
a = 10;
b = 10;
a = a + 20;
b += 20;
}
Fonte: Elaborado pelo Autor (2021)
12
versões abreviadas dos operadores a fim de tornar o código mais limpo, legível e
elegante.
13
símbolo. O símbolo “*” (asterisco) é utilizado para representar, ao mesmo tempo, a
operação aritmética de multiplicação e a operação especial de manipulação de
ponteiros. A diferença entre uma situação e outra é que o operador aritmético é
binário e o especial, unário.
Se essa situação pode ser observada dentro de uma mesma linguagem de
programação, não é de se espantar que linguagens diferentes possam usar
símbolos completamente distintos para representar uma mesma operação. De fato,
o Quadro 2 apresenta os símbolos usados para representar algumas operações em
8 linguagens diferentes, evidenciando o quão diferente pode ser a simbologia
utilizada pelas linguagens.
Por exemplo, o operador de atribuição simples é representado pelo símbolo
“=” nas linguagens C, Python, Java, Ruby e Lua, enquanto que a linguagem R
adota um símbolo completamente diferente das demais linguagens para
representar essa operação (“<-”). Nesse mesmo sentido, a linguagem Lua utiliza o
símbolo “~=” para representar a operação de diferença que, como pode ser visto
no Quadro 2, não é o mesmo símbolo usado para representar essa operação em
nenhuma das linguagens listadas.
Outra questão que pode ser observada no Quadro 2 é que nem todas as
operações suportadas por uma linguagem estão disponíveis em uma outra. Das
linguagens listadas no quadro, apenas Python, Ruby, Lua e R possuem um operador
aritmético para cálculo da potência. Sendo assim, para calcular a potência nas
outras linguagens, o programador precisaria recorrer a alguma função provida por
uma biblioteca específica, como pode ser visto no exemplo de código em C
ilustrado na Figura 4.
14
Figura 4: Cálculo de potência em C
#include <math.h>
int main()
{
int numero;
numero = pow(2,4);
printf(“%d\n”, numero);
}
Fonte: Elaborado pelo Autor (2021)
15
1.3 DEFINIÇÃO E RESOLUÇÃO DE EXPRESSÕES
16
Figura 6: Operações contidas em uma expressão
Operação Operação
1 2
1+2-1
Fonte: Elaborado pelo Autor (2021)
17
Figura 8: Resolução de expressões observando a ordem de precedência dos operadores
( ) 1 + 5 + 3 ∗ 2 = 1 + 5 + 6 = 1 + 11 = 12
( )8/4 + 1 − 2 = 2 + 1−2 = 3 − 2 = 1
( ) 5 ∗ 3 + 4 / 2 = 15 + 4 / 2 = 15 + 2 = 17
Fonte: Elaborado pelo Autor (2021)
1 Parêntesis
2 Exponenciação
Sendo assim, as operações que estão delimitadas entre parêntesis são as que
possuem a maior prioridade de resolução em uma expressão, seguidas pelas
operações de exponenciação. Já as operações de multiplicação e divisão estão
no mesmo patamar de prioridade e, por isso, não possuem prioridade uma sobre a
outra. Esse é o mesmo caso das operações de soma e subtração, sendo que essas
duas ocupam a última posição na ordem de prioridade.
18
A fim de tornar essa explicação menos abstrata, a Figura 9 ilustra como a
ordem de precedência é utilizada para resolver uma expressão composta por
variados tipos de operação e também por operações delimitadas entre parêntesis.
((4 ∗ 5) − (4 + 1) + 3) / 9 ⇒
(20 − (4 + 1) + 3) / 9 ⇒
(20 − 5 + 3) / 9 ⇒
(15 + 3) / 9 ⇒
18 / 9 =
Fonte: Elaborado pelo Autor (2021)
19
de precedência definida na matemática quando se trata das operações
aritméticas elementares e do uso dos parêntesis. Contudo, como discutido na
próxima seção, cada linguagem pode apresentar especificidades em relação à
ordem de precedência quando se trata de expressões que não contenham
somente operações aritméticas básicas.
#include <stdio.h>
int main()
{
int nota, assiduidade;
if ((nota >= 7) && (assiduidade >= 75))
printf(“Aluno aprovado\n”);
else
printf(“Aluno reprovado\n”);
}
Fonte: Elaborado pelo Autor (2021)
20
Como pode ser visto na Figura 10, o programa determina se o aluno foi ou
não aprovado ao avaliar o resultado da expressão ((nota >= 7) && (assiduidade >=
75)). Essa expressão combina operadores relacionais, lógicos e também o uso de
parêntesis para determinar a ordem em que as operações devem ser avaliadas.
Portanto, primeiro deve-se apurar se a nota é maior ou igual a 7. Após, verifica-se se
a assiduidade do aluno foi maior ou igual a 75 (representando 75%). Por fim, os
resultados das duas operações relacionais se tornam os operandos da operação
lógica de conjunção (E lógico) que, em C, é representada pelos símbolos “&&”.
Ao avaliar cuidadosamente a expressão usada para determinar se o aluno
foi ou não aprovado, é possível notar que os parênteses foram utilizados para
garantir que as condições de média e assiduidade fossem avaliadas
individualmente de tal modo que o aluno pudesse ser considerado aprovado
somente se ambas as condições fossem satisfeitas. A expressão, portanto, modela a
regra que é usada para resolver o referido problema de aprovação do aluno.
Mas, nesse caso em particular, cabe uma ressalva quanto à obrigatoriedade
do uso dos parênteses para segregar as operações. Nessa situação específica, não
haveria necessidade de se utilizar os parênteses para determinar a ordem de
precedência naquela expressão. Isso acontece, pois as linguagens de
programação possuem uma ordem de precedência própria para todos os seus
operadores, e não somente para os aritméticos. Em C, a ordem de precedência
preconiza que as operações envolvendo o operador “>=” têm prioridade sobre
aquelas que estão utilizando o operador “&&”.
Sendo assim, o código descrito na Figura 11, que não utiliza parênteses para
separar as operações relacionais, teria a mesma funcionalidade do código original,
já que as operações relacionais seriam resolvidas antes de se considerar a
operação lógica.
21
Figura 11: Código para determinar a aprovação de um aluno com expressão sem
parênteses
#include <stdio.h>
int main()
{
int nota, assiduidade;
if (nota >= 7 && assiduidade >= 75)
printf(“Aluno aprovado\n”);
else
printf(“Aluno reprovado\n”);
}
Fonte: Elaborado pelo Autor (2021)
22
23
FIXANDO O CONTEÚDO
program exponenciacao
implicit none
! Declarando variaveis
integer :: a, b, c
! Atribuindo valores
a = 5
b = 3
! Calculando a exponenciacao
c = a ** b
a) unário.
b) binário.
c) ternário.
d) lógico.
e) relacional.
Operador Operação
+ Adição entre dois elementos
- Subtração entre dois elementos
* Multiplicação entre dois elementos
/ Divisão entre dois elementos
mod Obtém o resto de uma divisão inteira
24
- Sinaliza que um número é negativo
+ Sinaliza que um número é positivo
ou Conectivo lógico de conjunção
e Conectivo lógico de disjunção
não Conectivo lógico de negação
<- Atribuição de valores
== Igualdade
!= Diferença
> Maior
< Menor
>= Maior ou igual
<= Menor ou igual
int main()
{
int a, b;
a = 15;
b = 8;
b++;
a = a + 5;
a *= 5;
}
25
b) o operador de incremento, representado pelo símbolo “++”, é categorizado
como um operador aritmético.
c) o único operador de atribuição utilizado no código é o de atribuição simples,
representado pelo símbolo “=”.
d) o símbolo “;”, utilizado no final de cada linha, é considerado como um dos
operadores especiais da linguagem.
e) as chaves utilizadas no código são classificadas como operadores relacionais
ternários.
26
a) 1.
b) 2.
c) 3.
d) 4.
e) 5.
a) 18.
b) 72.
c) 23.
d) 30.
e) 51.
1 () Delimitação de operações
3 * / Multiplicação e divisão
4 + - Adição e subtração
27
a) 0.
b) 1.
c) Verdadeiro.
d) 3.
e) 4.
a) (( < ) || ( == )) || (( > ) || ( == ))
b) (( < ) && ( == )) && (( > ) || ( == ))
c) (( < ) || ( == )) || (( > ) && ( == ))
d) (( < ) || ( == )) && (( > ) || ( == ))
e) ( < ) || ( > )
28
TIPOS DE DADOS ESPECIAIS UNIDADE
2.1 ESTRUTURAS
02
As linguagens de programação fornecem tipos pré-definidos de dados que
permitem armazenar e manipular valores lógicos, números, caracteres, entre outros
tipos de valores. Esses tipos de dados pré-definidos são conhecidos como tipos
primitivos ou básicos, e são parte integrante da especificação da própria
linguagem.
Os tipos primitivos podem variar bastante de uma linguagem para outra. Em
C, por exemplo, há tipos primitivos para tratar números inteiros, números reais e
caracteres, mas não há um tipo básico para manipular valores lógicos e cadeias de
caracteres. Já em Pascal, por exemplo, há sim tipos primitivos para manipular
valores lógicos (booleanos) e cadeias de caracteres. As linguagens apresentam
diferentes tipos primitivos, pois essa questão está intimamente ligada à essência e às
características predominantes de cada linguagem.
Embora o uso dos tipos primitivos seja suficiente e adequado para uma
ampla gama de situações, há casos em que pode ser mais interessante agrupar
diferentes tipos primitivos em um único novo tipo de dado a fim de facilitar a
manipulação da informação. Uma das formas de se fazer isso é por meio da
definição de uma estrutura ou registro.
Uma estrutura é composta por um grupo de membros ou campos, onde
cada um desses elementos pode ser do mesmo tipo primitivo ou de tipos
completamente diferentes. A Figura 13 ilustra um exemplo de definição de uma
estrutura em C que é usada para representar um produto.
typedef struct
{
int codigo;
int quantidade;
float valor;
}produto;
Fonte: Elaborado pelo Autor (2021)
29
A estrutura produto é composta pelos membros codigo, quantidade e valor,
e poderia ser usada, por exemplo, para manipular produtos em um programa de
controle de estoque. Os membros codigo e quantidade são do mesmo tipo primitivo
(int), enquanto que o elemento valor é do tipo float que é usado em C para
representar números reais. Portanto, essa estrutura agrupa três elementos que,
semanticamente, estão conectados entre si de acordo com um determinado
contexto.
Após a definição da estrutura, pode-se utilizar esse novo tipo para declarar
novas variáveis, exatamente da mesma forma que se faz com os tipos primitivos. No
exemplo ilustrado na Figura 14 foram criadas duas variáveis - ventilador e
geladeira - que receberam diferentes valores para cada um dos membros ou
campos da estrutura.
typedef struct
{
int codigo;
int quantidade;
float valor;
}produto;
int main()
{
produto ventilador, geladeira;
ventilador.codigo = 1;
ventilador.quantidade = 10;
ventilador.valor = 10.59;
geladeira.codigo = 2;
geladeira.quantidade = 7;
geladeira.valor = 602.45;
}
Fonte: Elaborado pelo Autor (2021)
30
Além disso, é possível criar uma nova estrutura em que os seus membros
sejam do novo tipo criado a partir de outra estrutura. Em outras palavras, pode-se
fazer um encadeamento de estruturas de forma a tornar o código ainda mais
legível, organizado e elegante. Para exemplificar essa possibilidade, a Figura 15
apresenta uma alteração na definição da estrutura produto.
Figura 15: Estrutura composta por membro definido a partir de outra estrutura
typedef struct
{
int largura;
int comprimento;
int altura;
}medidas;
typedef struct
{
int codigo;
int quantidade;
float valor;
medidas dimensoes;
}produto;
int main()
{
produto ventilador;
ventilador.codigo = 1;
ventilador.quantidade = 10;
ventilador.valor = 1.59;
ventilador.dimensoes.largura = 35;
ventilador.dimensoes.altura = 25;
ventilador.dimensoes.comprimento = 15;
}
Fonte: Elaborado pelo Autor (2021)
31
Além de contar com os membros originais, a estrutura produto agora possui
um novo membro chamado dimensoes. Mas, em vez de utilizar um tipo primitivo
para declarar esse novo campo, foi utilizado o tipo medidas, definido a partir de
uma estrutura. A estrutura medidas, por sua vez, é composta pelos membros altura,
comprimento e largura. Dessa forma, o tipo produto passa a ter dois níveis de
informação, onde o segundo nível diz respeito às medidas ou dimensões do
produto. O acesso a esse novo nível também é feito por meio do símbolo ponto
(“.”).
Outra possibilidade interessante é combinar um tipo definido a partir de uma
estrutura com vetores unidimensionais. Dessa forma, em vez de criar uma variável
para cada item que se deseja manipular, pode-se criar vetor de elementos em que
cada posição do vetor armazene um desses itens. A Figura 16 exemplifica esse
cenário.
Figura 16: Combinação entre estrutura e vetor
typedef struct
{
int largura;
int comprimento;
int altura;
}medidas;
typedef struct
{
int codigo;
int quantidade;
float valor;
medidas dimensoes;
}produto;
int main()
{
produto estoque[100];
estoque[0].codigo = 1;
estoque[0].quantidade = 10;
estoque[0].valor = 1.59;
estoque[0].dimensoes.largura = 35;
estoque[0].dimensoes.altura = 25;
estoque[0].dimensoes.comprimento = 15;
}
Fonte: Elaborado pelo Autor (2021)
Como pode ser visto no código da Figura 15, foi criado um vetor de 100
elementos chamado estoque. Por estar sendo declarado com o tipo produto, então
32
cada um dos 100 elementos deste vetor pode ser usado para armazenar um item
que, por sua vez, possui todos os campos da estrutura produto. De fato, o exemplo
mostra que os dados de um produto foram atribuídos ao primeiro elemento do vetor
estoque que, na linguagem C, é referenciado pelo índice 0.
Utilizando essa combinação de vetor e tipos definidos a partir de estruturas,
não há necessidade de criar uma variável diferente para cada um dos itens que se
deseja manipular. Dessa forma, o uso de estruturas confere muito mais organização
e clareza ao código, facilitando tanto o armazenamento quanto a recuperação da
informação.
2.2 ENUMERAÇÕES
33
Tomando como exemplo o código apresentado na Figura 16, poderia-se
expandir um pouco mais o tipo produto para incluir um novo membro a fim de
indicar a categoria ou classe do produto. Isso pode ser feito a partir de constantes,
como mostra o código ilustrado na Figura 17.
#define ELETRODOMESTICO 10
#define ALIMENTO 20
#define ROUPA 30
#define INFORMATICA 40
typedef struct
{
int largura;
int comprimento;
int altura;
}medidas;
typedef struct
{
int codigo;
int quantidade;
int categoria;
float valor;
medidas dimensoes;
}produto;
int main()
{
produto estoque[100];
estoque[0].codigo = 1;
estoque[0].quantidade = 10;
estoque[0].categoria = ELETRODOMESTICO
estoque[0].valor = 1.59;
estoque[0].dimensoes.largura = 35;
estoque[0].dimensoes.altura = 25;
estoque[0].dimensoes.comprimento = 15;
}
Fonte: Elaborado pelo Autor (2021)
34
como foi feito na atribuição da informação ELETRODOMESTICO ao campo categoria
do primeiro produto do vetor estoque. Sendo assim, a constante ELETRODOMESTICO
passou a ser utilizada como um rótulo para denotar que um dado produto é um
eletrodoméstico.
Essa identificação da categoria do produto poderia ser feita de outras
formas. Uma opção seria utilizar diretamente o número inteiro que foi atribuído no
momento de criação das constantes. Dessa forma, em vez de atribuir a constante
ELETRODOMESTICO ao membro categoria, poderia-se atribuir diretamente o seu valor,
isto é, o número 10. Contudo, essa solução certamente dificultaria o entendimento
do código, já que seria preciso lembrar que o número 10 estaria associado à
categoria de eletrodoméstico. Para não precisar lembrar dessa associação, haveria
a possibilidade de manter a informação sobre essa relação em um arquivo
separado ou dentro do próprio código, mas em formato de comentários. De
qualquer forma, essa solução está bem longe de ser eficiente, visto que a leitura do
código precisaria ser interrompida a todo momento para que essa informação fosse
decifrada.
Outra opção seria utilizar um texto para representar essa informação, algo
como atribuir o valor “eletrodomestico” ao membro categoria da estrutura produto.
Mesmo parecendo uma boa solução, essa alternativa traz alguns problemas. Em
primeiro lugar, há linguagens que fazem diferenciação entre letras maiúsculas e
minúsculas (também conhecido pelo termo em Inglês case sensitive). Dessa forma,
mesmo que as palavras sejam ortograficamente idênticas, o valor
“eletrodomestico”, para essas linguagens, não é igual à “Eletrodomestico”, visto que
a segunda palavra está escrita com um “E” maiúsculo. Além disso, ao adotar essa
abordagem, o programador também pode cometer o erro de escrever a palavra
com alguma pequena variação, como utilizar o plural, por exemplo. Assim, mesmo
que a linguagem de programação não seja case sensitive, os valores
“eletrodomestico” e “eletrodomesticos” são inegavelmente diferentes.
Por conta de todos esses aspectos, a utilização de constantes é uma boa
prática quando se precisa representar informações por meio de rótulos. Mas, em
vez de definir individualmente essas constantes, pode-se fazer uso de um recurso
chamado enumeração em que as constantes são definidas em conjunto e ficam
associadas a um novo tipo de dado.
O código da Figura 18 mostra como uma enumeração pode ser usada para
35
substituir as definições individuais de constantes para representação de rótulos. Em
C, uma enumeração é criada por meio da palavra reservada enum.
typedef enum
{
alimento,
roupa,
eletrodomestico,
informatica
}departamentos;
typedef struct
{
int largura;
int comprimento;
int altura;
}medidas;
typedef struct
{
int codigo;
int quantidade;
departamentos categoria;
float valor;
medidas dimensoes;
}produto;
int main()
{
produto estoque[100];
estoque[0].codigo = 1;
estoque[0].quantidade = 10;
estoque[0].categoria = eletrodomestico
estoque[0].valor = 1.59;
estoque[0].dimensoes.largura = 35;
estoque[0].dimensoes.altura = 25;
estoque[0].dimensoes.comprimento = 15;
}
Fonte: Elaborado pelo Autor (2021)
36
o membro categoria, da estrutura produto, não é mais do tipo int, mas sim do tipo
departamentos. Diferentemente da versão anterior deste código, a variável
categoria, do primeiro elemento do vetor estoque, recebeu a constante
eletrodomestico que é um dos itens definidos na enumeração departamentos.
Uma das grandes vantagens de se empregar enumerações diz respeito à
característica de restrição dos valores que podem ser atribuídos a uma variável que
seja declarada com um tipo criado a partir de uma enumeração. Por exemplo,
uma variável declarada com o tipo departamentos apenas aceitará os itens que
estejam definidos nesta enumeração. Sendo assim, caso o programador decida
atribuir o valor livros a variável categoria, o código não será compilado, já que
esse item, essa constante, não é parte integrante da enumeração departamentos.
Essa propriedade evita que ocorra o problema de se atribuir valores que não
estejam incluídos na enumeração, tornando o código mais confiável e menos
suscetível a erros.
2.3 PONTEIROS
37
e manipular corretamente os dados dos programas.
A Figura 19 apresenta uma ilustração sobre a divisão da memória principal
em células. Esse esquema é meramente didático, já que há muito mais detalhes
técnicos que envolvem a divisão, alocação e acesso aos dados em uma memória
real. Mas, para o contexto dessa disciplina, basta entender que a memória é
dividida em células que armazenam o conteúdo das variáveis e que cada uma
dessas células é referenciada por meio de um endereço de memória. Sendo assim,
de acordo com este exemplo, a memória estaria dividida em 8 células e, na célula
endereçada pelo número 5, estaria armazenado o conteúdo “Maicon Melo”.
Conteúdo
Endereço
1 252 Célula
2 15
3 Verdadeiro
4 8.25
5 Maicon Melo
6 1589
7 369
8 Produto
Memória principal
38
programadores podem escrever códigos muito mais simples, visto que não precisam
se preocupar com questões tão relacionadas ao hardware do computador.
#include <stdio.h>
int main()
{
int numero;
numero = 10;
printf("%p\n", &numero);
}
Fonte: Elaborado pelo Autor
39
valores a cada execução, pois cada uma dessas execuções resultará em
diferentes solicitações de alocação de memória realizadas ao sistema operacional.
Embora as linguagens de programação e o sistema operacional ofereçam
essa abstração em que identificadores são usados para referenciar endereços de
memória, há uma série de tarefas em que o programador precisa lidar diretamente
com esses endereços. É por conta dessa necessidade que grande parte das
linguagens de programação oferecem um tipo de dado chamado ponteiro. Uma
variável do tipo ponteiro é utilizada para armazenar o endereço de memória que
está sendo referenciado por uma outra variável. O código da Figura 21 mostra
como essa operação pode ser feita em C.
Figura 21: Utilização de variável do tipo ponteiro
#include <stdio.h>
int main()
{
int numero = 10;
int *ptro;
ptro = №
printf("%p\n", ptro);
}
Fonte: Elaborado pelo Autor
#include <stdio.h>
40
int main()
{
int numero = 10;
int *ptro;
ptro = №
printf("%d\n", numero);
*ptro = 20;
printf("%d\n", numero);
}
Fonte: Elaborado pelo Autor
Ao ser executado, este código deve imprimir os valores 10 e 20, nessa ordem.
A variável numero recebeu o valor 10 durante sua criação e, em seguida, o
endereço de memória referenciado por numero foi atribuído à variável ptro que é
um ponteiro para variáveis do tipo int.
Nesse ponto, como ilustrado na Figura 23, tanto a variável numero quanto
ptro estão apontando para o mesmo endereço de memória. A execução da
instrução “*ptro = 20;” atribui o valor 20 ao endereço de memória apontado pela
variável ptro o que, na prática, acaba resultando também na alteração do valor
da variável numero.
numero
10 Célula
0x7fffd9871784
*ptro Endereço
Como pode ser visto no código da Figura 22, o operador “*” foi utilizado em
duas ocasiões diferentes. Na primeira, o operador foi utilizado para declarar a
variável do tipo ponteiro chamada ptro. Já na outra ocasião, o operador foi
empregado para acessar o endereço de memória contido na variável ptro. Trata-
se, portanto, de um acesso indireto feito a um endereço de memória a partir de um
41
ponteiro.
Algo importante a ser compreendido é que o próprio ponteiro também é
uma variável e, mesmo sendo de um tipo especial que armazena endereços,
também está alocado em uma determinada posição de memória. Sendo assim, o
próprio ponteiro também possui o seu endereço de memória e esse endereço não
tem qualquer relação com o endereço que está armazenado no conteúdo
ponteiro.
Para ficar mais claro, a Figura 24 apresenta um esquema onde é
evidenciada a diferença entre o conteúdo de um ponteiro e o endereço da
posição de memória que essa variável está alocada. Nesse exemplo, a variável
ponteiro ptro está armazenada no endereço 0x7ffc3aeb2c70, enquanto que o seu
conteúdo é, na verdade, o endereço 0x7fffd9871784, isto é, o endereço da
variável numero.
Figura 24: Diferença entre o conteúdo de um ponteiro e a sua posição alocada na memória
numero
*ptro
0x7fffd987178 10
Endereços
Conteúdo
0x7fffd9871
0x7ffc3aeb2c7
ptro
Fonte: Elaborado pelo Autor (2021)
42
43
FIXANDO O CONTEÚDO
typedef struct
{
int x;
}estruturaA;
typedef struct
{
int x;
estruturaA a;
}estruturaB;
int main()
{
estruturaB b;
}
a) x
b) b.x
c) a.x.b
d) b.a.x
e) b.b.x
int main()
{
int x, *y, *z;
x = 10;
y = &x;
z = y;
}
44
a) O número 10.
b) Não possuirá nenhum valor.
c) O endereço de memória da variável x.
d) O valor da variável x.
e) O endereço de memória da variável y.
typedef struct
{
montadoras marca;
}carro;
Mesmo sem ter mais detalhes sobre o tipo montadoras, é correto afirmar que:
45
int main()
{
int x, y, z;
x = 10;
y = 20;
z = ((*&x) + (*&y));
}
a) um valor nulo.
b) o endereço de memória da variável x.
c) o resultado entre a soma dos valores contidos nos endereços de memória
apontados pelas variáveis x e y.
d) o resultado entre a soma dos endereços de memória da variáveis x e y.
e) o endereço de memória da variável x somado ao valor 20.
typedef enum
{
campos,
Campos,
caMpos,
}cidades;
I. essa não é uma enumeração válida, pois todos os seus elementos são iguais.
II. embora válida, essa enumeração não pode ser utilizada para definir um membro
em uma estrutura.
III. embora os nomes dos elementos sejam ortograficamente iguais, cada um dos
elementos é uma constante completamente diferente.
a) apenas I.
46
b) apenas II.
c) apenas III.
d) I e II.
e) I, II e III.
int main()
{
int x, *y, z;
x = 10;
y = &x;
x = x + 20;
z = *y;
}
a) 0.
b) 10.
c) 20.
d) 30.
e) 40.
7. Uma variável do tipo ponteiro é, antes de mais nada, uma variável. Por essa
razão, uma variável ponteiro também ocupa uma posição de memória,
identificada por um endereço único, em que uma informação é armazenada. No
caso dos ponteiros, essa informação é o endereço de memória ocupado por uma
outra variável. Em C, os endereços de memória são iniciados com o prefixo “0x” e
são exibidos no formato hexadecimal.
47
a) o conteúdo armazenado na posição de memória referenciada pelo endereço
0x7fffd9871784 é um outro endereço cuja posição em memória armazena um
número real.
b) o conteúdo armazenado na posição de memória referenciada pelo endereço
0x7fffd9871784 é o número inteiro 658.
c) o conteúdo armazenado na posição de memória referenciada pelo endereço
0x7fffd98711281 é o endereço 0x7adfd98711348 que, por sua vez, faz referência
a uma posição de memória em que um terceiro endereço está armazenado.
d) o conteúdo armazenado na posição de memória referenciada pelo endereço
0x7adfd98711348 é o endereço 0x7ffc3aeb2c70 que, por sua vez, faz referência
a uma posição de memória em que um número inteiro está armazenado.
e) o conteúdo armazenado na posição de memória referenciada pelo endereço
0x7fffd9871784 é um texto.
typedef enum
{
usado,
seminovo,
novo
}tipos_situacao;
typedef struct
{
tipos_situacao situacao;
48
}detalhes;
typedef struct
{
int codigo;
float valor;
detalhes informacoes;
}carro;
49
ESTRUTURAS DE SELEÇÃO UNIDADE
50
múltipla.
A estrutura de seleção simples é composta por uma condição e por um
único conjunto ou bloco de instruções que será executado caso a condição seja
verdadeira. O código em C ilustrado na Figura 26 mostra o uso dessa estrutura de
seleção simples para verificar se um número é negativo.
#include <stdio.h>
int main()
{
int numero;
if (numero < 0)
{
printf("Número é negativo!");
}
}
Fonte: Elaborado pelo Autor (2021)
51
Figura 27: Exemplo de avaliação condicional
Então, nesse exemplo, a pessoa irá ao cinema caso não o dia não esteja
com sol. Em linguagem natural, costuma-se utilizar a palavra “senão” que tem o
mesmo significado de “do contrário”. Portanto, em suma, se a condição for
verdadeira, então uma determinada ação será realizada, senão uma outra ação
alternativa deverá ser executada.
Essa estrutura em que é utilizada uma alternativa a ser executada caso a
condição seja falsa é denominada como estrutura de seleção composta. Para
ilustrar a aplicação de uma estrutura de seleção composta, o código apresentado
na Figura 28 mostra como determinar se um número é par ou ímpar.
#include <stdio.h>
int main()
{
int numero = 10;
int resto;
resto = numero % 2;
if (resto == 0)
{
printf("Número é par");
}
else
{
printf("Número é ímpar");
}
52
}
Fonte: Elaborado pelo Autor (2021)
53
Figura 29: Encadeamento de avaliações condicionais
#include <stdio.h>
int main()
{
int nota;
int frequencia;
if (nota < 7)
{
printf("Aluno reprovado por média!");
54
}
else
{
if (frequencia < 75)
printf("Aluno reprovado por falta!");
else
printf("Aluno aprovado!");
}
}
Fonte: Elaborado pelo Autor (2021)
55
3.3 SELEÇÃO MÚLTIPLA
#include <stdio.h>
int main()
{
int dia_da_semana;
switch (dia_da_semana)
{
case 1: printf("domingo");
break;
case 2: printf("segunda");
break;
case 3: printf("terça");
break;
56
case 4: printf("quarta");
break;
case 5: printf("quinta");
break;
case 6: printf("sexta");
break;
case 7: printf("sábado");
break;
default: printf("número incorreto");
break;
}
}
Fonte: Elaborado pelo Autor (2021)
57
58
FIXANDO O CONTEÚDO
if (x > 0)
{
if (z == 10)
a = 20;
else
b = 30;
}
else
{
a = 0;
}
if (x > 0)
{
if (x < 10)
{
y = 0;
}
else
{
if (x > 11)
{
y = -1;
}
else
{
y = 1;
59
}
}
}
printf(“%d”, y);
a) 0.
b) 1.
c) -1.
d) Nulo.
e) 20.
if (x == 1)
{
printf(“verão”);
}
else
{
if (x == 2)
{
printf(“outono”);
}
else
{
if (x == 3)
{
printf(“inverno”);
}
else
{
printf(“primavera”);
}
}
60
d) apenas 1 estrutura de seleção encadeada contendo apenas 1 avaliação
condicional.
e) apenas 1 estrutura de seleção encadeada sem conter nenhuma avaliação
condicional.
II. Essa estrutura de seleção múltipla poderia ser reescrita utilizando-se apenas 1
estrutura de seleção simples.
char letra;
switch (letra)
{
case ‘a’: printf("vogal");
break;
case ‘e’: printf("vogal");
break;
case ‘i’: printf("vogal");
break;
case ‘o’: printf("vogal");
break;
case ‘u’: printf("vogal");
break;
default: printf("consoante");
break;
}
61
b) apenas II.
c) apenas III.
d) I e II.
e) I e III.
if (x == 10)
{
z = 0;
}
if (y == 10)
{
z = 0;
}
6. Avaliando o código a seguir, indique qual seria o valor que estaria atribuído à
variável z no final da execução desse programa:
int main()
{
62
int x, z;
x = 0;
if (x > 0)
z = 0;
else
z = 1;
if (x < 0)
z = 1;
else
z = 0;
}
a) 1.
b) -1.
c) 0.
d) -2.
e) 2.
Código A:
if (x > 0)
z = 0;
else
z = 1;
Código B:
if (x < 0)
z = 1;
else
z = 0;
63
d) ambos os códigos utilizam uma estrutura de seleção composta e são
equivalentes em relação a lógica de programação.
e) ambos os códigos utilizam uma estrutura de seleção composta, mas não são
equivalentes em relação a lógica de programação.
int main()
{
int x, z;
64
ESTRUTURAS DE REPETIÇÃO UNIDADE
#include <stdio.h>
int main()
{
printf("Contagem regressiva: 5\n");
printf("Contagem regressiva: 4\n");
printf("Contagem regressiva: 3\n");
printf("Contagem regressiva: 2\n");
printf("Contagem regressiva: 1\n");
}
Fonte: Elaborado pelo Autor (2021)
65
estruturas de repetição também fazem parte do conjunto de estruturas de controle
que estão presentes em grande parte das linguagens de programação. Enquanto
as estruturas de seleção são usadas para alterar o fluxo de execução de um
programa, as estruturas de repetição são empregadas para executar um conjunto
de instruções enquanto uma dada condição for verdadeira.
As estruturas de repetição podem ser divididas entre estruturas com teste no
início, teste no final e com variável de controle. Da mesma forma que acontece
com as estruturas de seleção, cada tipo de estrutura de repetição é mais
adequada para resolver um determinado tipo de problema.
Enquanto
<condição> faça
Bloco de
Fonte: Elaborado pelo Autor (2021)
66
para implementar aquele mesmo exemplo de contagem regressiva.
#include <stdio.h>
int main()
{
int x = 10;
while (x != 0)
{
printf("Contagem regressiva: %d\n", x);
x--;
}
}
Fonte: Elaborado pelo Autor (2021)
Entre parênteses, logo após a palavra reservada while, vem a condição que
está sendo testada no início da estrutura de repetição. Portanto, enquanto o valor
da variável x for diferente de 0, as instruções contidas dentro do bloco de código
serão executadas repetidamente. Como a variável x é iniciada com o valor 10 e, a
cada execução da repetição, está sendo decrementada de 1 (operador “--”),
então, após 10 execuções, a variável possuirá o valor 0. Quando isso ocorrer, a
condição que está sendo testada pela estrutura de repetição não será mais
verdadeira, ocasionando o término da repetição.
Vale notar que a estrutura de repetição com teste no início é mais adequada
para as situações em que, caso a condição avaliada seja falsa, o bloco de
instruções não deve ser executado nenhuma vez.
67
natural, essa estrutura poderia ser interpretada como “faça as instruções contidas
neste bloco, enquanto a condição for verdadeira”, indicando que a lógica
adotada nessa estrutura vai de encontro àquela empregada na estrutura de
repetição com teste no início.
Faça
Bloco de instruções
Enquanto <condição>
#include <stdio.h>
int main()
{
int operacoes = 5;
int operando = 2;
int cont = 1;
do
{
printf("Tabuada: %d x %d = %d\n", operando, cont, operando *
cont);
cont++;
}while (cont < operacoes);
}
Fonte: Elaborado pelo Autor (2021)
68
com 1 ou, até mesmo com 0, o programa iria imprimir ao menos a primeira
operação da tabuada, ou seja, a operação “2 x 1” caso o operando fosse o
número 2. Isso acontece, pois o bloco de instruções seria executado no mínimo uma
vez, mesmo que a condição do laço já fosse falsa desde sua primeira iteração.
Talvez possa parecer que essa estrutura não tenha muita utilidade prática,
principalmente quando comparada com a estrutura de repetição com teste no
início. Ledo engano. Em certos casos, uma estrutura de repetição com teste no final
se encaixa perfeitamente na maneira como o programador implementou a sua
lógica de programação, evitando, assim, que ele faça alterações desnecessárias
no código para garantir que o bloco de instruções seja executado pelo menos uma
vez.
69
Figura 37.
Essa estrutura difere bastante das estruturas vistas nas últimas duas seções. Em
linguagem natural, essa estrutura poderia ser lida como “Para o valor inicial de uma
variável de controle até um valor final, repita as instruções contidas neste bloco,
observando um determinado passo de iteração”. Essa estrutura é composta,
portanto, por 4 elementos, descritos a seguir:
#include <stdio.h>
int main()
{
70
int operacoes = 5;
int operando = 2;
int cont;
for (cont=1; cont<=operacoes; cont++)
{
printf("Tabuada: %d x %d = %d\n", operando, cont, operando *
cont);
}
}
Fonte: Elaborado pelo Autor (2021)
71
o caso em que o programa precisa percorrer um vetor de certo tamanho, por
exemplo. Essa finalidade difere, dessa forma, das estruturas de repetição com testes
no início e no final, em que o objetivo é continuar repetindo as instruções.
72
FIXANDO O CONTEÚDO
x = 1;
while (x != 0)
{
x--;
}
a) nenhuma vez.
b) três vezes.
c) apenas 1 vez.
d) duas vezes.
e) nunca.
x = 1;
while (x != 0)
{
x--;
}
a) nenhuma vez.
b) três vezes.
c) apenas 1 vez.
d) duas vezes.
e) nunca.
73
3. Na linguagem C, a estrutura de repetição com teste no final é implementada
utilizando-se as palavras reservadas do e while. Nesta estrutura, o bloco de
instruções dentro do laço é executado enquanto a condição for verdadeira.
Portanto, mesmo que a condição seja falsa ainda na primeira iteração, o bloco de
instruções é sempre executado, no mínimo, uma vez.
x = 1;
{
x--;
} while (x != 0)
a) nenhuma vez.
b) três vezes.
c) apenas 1 vez
d) duas vezes.
e) nunca.
x = 1;
{
x--;
} while (x != 0)
a) nenhuma vez.
b) três vezes.
c) apenas 1 vez.
d) duas vezes.
e) nunca.
74
5. Em C, a estrutura de repetição com variável de controle é implementada
utilizando-se a palavra reservada for. Nesta estrutura, o bloco de instruções dentro
do laço é executado até que uma determinada condição seja satisfeita. Quando
a condição é alcançada, o laço é interrompido.
cont = 0
while (cont < 5):
print(“teste”)
cont = 0
75
a) esse laço de repetição será executado uma única vez.
b) a mensagem “teste” será impressa apenas 4 vezes.
c) a mensagem “teste” será impressa apenas 5 vezes.
d) esse laço de repetição nem será executado, visto que a condição já é falsa
desde a primeira iteração.
e) esse laço de repetição será executado continuamente, já que a condição de
parada nunca se tornará falsa.
cont = 0;
while(cont < 5)
{
for(x=cont; x<5; x++)
{
printf(“teste”);
}
cont += 2;
}
a) nenhuma vez.
b) mais de 5 vezes.
c) apenas 1 vez.
d) mais de 20 vezes.
e) até 3 vezes.
8. A linguagem Object Pascal suporta uma estrutura de repetição com teste no final
conhecida como “Repita até que”. Essa estrutura repete uma sequência de
comandos até que uma dada condição seja verdadeira. Portanto, o laço segue
repetindo um bloco de instruções enquanto a condição for falsa. Esta estrutura é
implementada por meio das palavras reservadas repeat e until que significam,
respectivamente, “repita” e “até que” na língua portuguesa.
76
O trecho de código abaixo, escrito em Object Pascal, utiliza um laço “Repita até
que” para imprimir a palavra “teste” na tela.
cont := 2;
repeat
write('Teste');
cont := cont - 1;
until cont = 0;
a) nenhuma vez.
b) uma única vez.
c) menos do que 2 vezes.
d) mais do que 2 vezes.
e) exatamente 2 vezes.
77
MODULARIZAÇÃO UNIDADE
78
Figura 39: Códigos monolítico e modular
5.2 FUNÇÕES
79
A seção anterior mostrou algumas das vantagens de se aplicar o conceito de
modularização em um código-fonte. Todos aqueles benefícios são praticamente os
mesmos, a despeito da linguagem de programação utilizada para criar o
programa. Por outro lado, a maneira como a modularização é feita vai depender
do tipo e também das características da linguagem de programação.
Em linguagens orientadas a objeto, por exemplo, uma forma de modularizar
o código é por meio da definição de objetos que são constituídos por métodos e
propriedades. Dessa forma, os objetos podem ser usados para representar
diferentes módulos responsáveis por executar determinadas tarefas ou prover certas
funcionalidades. Já em outras linguagens, como em NesC - linguagem usada para
programar Redes de Sensores sem Fio (RSSF), a modularização pode ser feita por
meio de componentes.
80
A Figura 40 apresenta um exemplo bem simples de função em que o seu
processamento consiste em calcular o dobro de um número inteiro. Portanto, ao
receber o número 15 como entrada, a função retorna o dobro desse valor, isto é, o
número 30. Os dados de entrada recebidos por uma função costumam ser
referenciados como parâmetros de entrada, enquanto que o valor de saída é mais
frequentemente conhecido pelos termos retorno ou resultado da função.
81
● Tipo do valor de retorno (B). Na definição da função, é preciso indicar o tipo
do valor de retorno. No exemplo, a função dobro irá retornar um valor que é
do tipo primitivo int.
● Parâmetros de entrada (C). Entre parênteses, logo após o nome da função,
deve-se indicar os parâmetros de entrada que serão recebidos pela função
quando esta for executada.
● Corpo da função (D). Bloco de código que será executado quando a função
for chamada ou evocada.
● Retorno da função (E). O retorno da função é realizado pela palavra
reservada return.
Nesse exemplo, a função dobro recebe apenas um número inteiro como
parâmetro de entrada, identificado como numero. Após calcular o dobro desse
número, o resultado desse processamento é armazenado na variável valor. Por fim,
a palavra reservada return é usada para indicar o valor que será retornado pela
função.
C
A
B
int dobro(int numero)
{
int valor;
valor = numero * 2; D
return valor;
}
82
chamada da função, então a variável resultado será preenchida com o dobro
desse valor, isto é, com o número 16.
int main()
{
int resultado;
int parametro = 8;
resultado = dobro(parametro);
}
Fonte: Elaborado pelo Autor (2021)
É interessante lembrar que essa função pode ser evocada quantas vezes
forem necessárias. Afinal, o reuso é justamente um dos principais motivos para se
criar uma função. Além disso, caso fosse preciso realizar algum ajuste no cálculo
dessa função, bastaria alterar o seu bloco de código para que essa modificação
fosse aplicada a todas as partes do programa que evocam essa função.
83
5.3 TIPOS DE PASSAGEM DE PARÂMETRO
int teste(int x)
{
x = x + 1;
return x;
}
84
int main()
{
int parametro = 5;
int resultado;
resultado = teste(parametro);
printf("variável parametro: %d\n", parametro);
printf("variável resultado: %d\n", resultado);
}
Fonte: Elaborado pelo Autor (2021)
85
Figura 44: Exemplo de passagem de parâmetro por referência
int main()
{
int parametro = 5;
int resultado;
resultado = teste(¶metro);
printf("variável parametro: %d\n", parametro);
printf("variável resultado: %d\n", resultado);
}
86
87
FIXANDO O CONTEÚDO
int main()
{
int a = 5;
int b = 2;
int resultado;
resultado = soma(soma(a,b),soma(a,b));
}
a) 7.
b) 10.
c) 8.
d) 5.
88
e) 14.
int main()
{
int a = 3;
int b = 5;
int c = 0;
imprimir(b,c,a);
}
a) 0 3 5.
b) 5 0 3.
c) 0 5 3.
d) 3 5 0.
e) 5 3 0.
int decremento(int a)
{
return a - 1;
}
89
sintaxe própria para ser definida, pois, na terminologia dessa linguagem, esse
módulo é um procedimento.
b) a função decremento, se fosse implementada em Pascal, precisaria utilizar uma
sintaxe própria para ser definida, pois, na terminologia dessa linguagem, esse
módulo é um procedimento.
c) a função decremento, caso fosse implementada em Python, poderia usar a
sintaxe para definição de procedimento, desde que permaneça recebendo
apenas um parâmetro de entrada.
d) a função decremento, caso fosse implementada em Pascal, poderia usar tanto a
sintaxe para função quanto para procedimento.
e) a função decremento, caso fosse implementada em Pascal, não poderia usar a
sintaxe para definição de procedimento, pois há retorno de valor.
int main()
{
int x = 10;
int y = 15;
int z;
z = decremento(x,&y);
}
a) 15 e 15.
b) 10 e 10.
c) 9 e 9.
d) 9 e 15.
e) 10 e 9.
90
conjunto de dados de entrada a fim de produzir um determinado valor de saída.
Na prática, porém, as linguagens de programação permitem definir funções que
não recebem nenhum parâmetro de entrada e tampouco fornecem um valor de
retorno. Em C, por exemplo, a definição dessa função poderia ser feita como
mostrado abaixo. O tipo de dado void é utilizado em C para denotar que uma
variável ou função é vazia, ou seja, não armazena (ou retorna - no caso das
funções) nenhuma informação.
void funcao()
{
/*bloco de código*/
}
a) uma função desse tipo, embora não retorne nenhum valor, pode ser usada para
modularizar trechos de código extensos e frequentemente executados a fim de
tornar o código-fonte mais legível e organizado.
b) uma função desse tipo não tem nenhuma aplicabilidade prática, nem sequer
para organizar ou estruturar melhor o código-fonte.
c) não há nenhuma linguagem de programação que permita criar uma função
que não retorne nenhum valor e que não receba nenhum parâmetro de entrada.
d) há linguagens de programação que até permitem criar uma função que não
retorne nenhum valor, desde que recebam ao menos um parâmetro de entrada.
e) no contexto de linguagens de programação, uma função que não retorna
nenhum valor e que não recebe nenhum parâmetro de entrada, não é, de fato,
uma função.
7. Por meio da passagem de parâmetro por referência, uma função torna-se capaz
de retornar valores além do próprio retorno padrão da função. O trecho de código
abaixo implementa a função soma_ao_dobro que retorna em sua saída padrão a
soma entre dois números inteiros. Mas, adicionalmente, a função também utiliza a
passagem de parâmetro por referência para retornar o dobro dessa soma.
91
int dobro = soma * 2;
*c = dobro;
return a + b;
}
int main()
{
int x = 10;
int y = 15;
int w;
int z;
z = soma_ao_dobro(x,y,&w);
}
Dito isso e considerando o código acima, o que deve ser preenchido na lacuna dos
parâmetros de entrada da função soma_ao_dobro para que a variável w, no
programa principal, receba o dobro da soma entre x e y:
a) int w
b) int *w
c) int c
d) int *c
e) int *b
92
93
MANIPULAÇÃO DE ARQUIVOS UNIDADE
94
sejam corrompidos, o sistema operacional libera o acesso para edição a apenas
um programa por vez. Do contrário, caso não houvesse esse controle, as
informações gravadas por um programa seriam sobrescritas por um outro,
causando perda de dados.
Em resumo, os arquivos são parte fundamental dos sistemas computacionais
e, além de serem usados para gravar as informações geradas pelos programas, são
também usados para armazenar suas informações de configuração e controle,
dados que são necessários para o correto funcionamento do software. Sendo assim,
é imprescindível que um programador saiba como escrever programas capazes de
manipular dados em arquivos.
95
possui uma forma muito particular e específica para realizar esse processo. Na
linguagem C, a abertura de um arquivo pode ser feita como ilustrado na Figura 45.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
}
Fonte: Elaborado pelo Autor (2021)
96
Quadro 4: Exemplo de código com uma enumeração
97
Após a abertura bem-sucedida do arquivo, o programa já pode ler e
escrever dados em seu conteúdo. Mas, para que o sistema operacional possa
executar adequadamente seu trabalho de gerenciamento, o programa deve
realizar uma operação para indicar que não está mais acessando aquele arquivo.
Trata-se do processo de fechamento de arquivo. Enquanto a abertura é o primeiro
passo a ser realizado na manipulação de arquivos, o fechamento é o último.
O código ilustrado na figura abaixo mostra como realizar esse processo na
linguagem C. Sem dúvida, fechar um arquivo é bem mais simples do que efetuar a
sua abertura.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
98
arquivo = fopen("teste.txt", "r");
fclose(arquivo);
}
Fonte: Elaborado pelo Autor (2021)
#include <stdio.h>
#include <stdlib.h>
int main()
99
{
FILE *arquivo;
char caractere;
if (arquivo == NULL)
exit(1);
caractere = fgetc(arquivo);
fclose(arquivo);
}
Fonte: Elaborado pelo Autor (2021)
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
char caractere;
if (arquivo == NULL)
exit(1);
while ((caractere=fgetc(arquivo))!=EOF)
putchar(caractere);
fclose(arquivo);
100
}
Fonte: Elaborado pelo Autor (2021)
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
char string[30];
if (arquivo == NULL)
exit(1);
fclose(arquivo);
}
Fonte: Elaborado pelo Autor (2021)
101
caracteres que irá receber os dados lidos, a quantidade de caracteres que serão
lidos por vez e o descritor do arquivo na qual a operação será realizada.
Diferentemente de fgetc, a função retorna NULL quando não há mais nenhum
dado a ser lido no arquivo. A função realiza a leitura do arquivo obtendo a
quantidade de caracteres indicada no seu segundo parâmetro e armazenando
esses dados no vetor indicado no primeiro parâmetro de entrada.
Há um porém. Caso a função encontre um indicador de final de linha, os
caracteres dessa linha serão imediatamente retornados, a despeito da quantidade
de caracteres que foi solicitada à função. Por exemplo, se a função está realizando
a leitura de 10 caracteres por vez e, na primeira linha do arquivo há apenas 2
caracteres, então a função fgets vai retornar somente esses 2 caracteres. Por outro
lado, se uma linha possui 40 caracteres, então a função, nesse exemplo, iria retornar
somente os 10 primeiros caracteres dessa linha.
No exemplo descrito na Figura 49, a função fgets está sendo usada
para obter 30 caracteres por vez. Como dito anteriormente, essa função retorna
NULL quando não há mais nenhum dado a ser lido. Por essa razão, o laço de
repetição continua sua iteração enquanto o valor retornado pela função for
diferente de NULL. A cada iteração o programa utiliza a função puts para imprimir
na tela os caracteres lidos do arquivo.
A escrita de 1 caractere por vez segue a mesma lógica da leitura. Para
escrever 1 caractere por vez, pode-se utilizar a função fputc que é a contraparte
de fgetc. O funcionamento é praticamente o mesmo, com exceção do retorno da
função. Quando a operação de escrita é realizada com sucesso, a função fputc
retorna o próprio caractere que foi escrito. Mas, em caso de falha, essa função
retorna a constante EOF.
Já o processo de escrita de uma cadeia de caracteres muda um pouco em
relação à leitura. A Figura 50 ilustra como utilizar a função fputs para escrever um
vetor de caracteres em um arquivo texto.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
102
char string[30] = “escrevendo algo no arquivo”;
if (arquivo == NULL)
exit(1);
fputs(string, arquivo);
fclose(arquivo);
}
Fonte: Elaborado pelo Autor (2021)
103
FIXANDO O CONTEÚDO
a) “a”
b) “x”
c) “w”
d) “w+”
e) “r+”
a) “r”
b) “x”
c) “w”
d) “z+”
e) “r+”
104
Considerando as informações acima e tomando como exemplo o seguinte arquivo
texto contendo 3 linhas, é correto afirmar que a função fgets, ao ser chamada com
o parâmetro de 20 caracteres e logo após a abertura do arquivo, retornaria:
4. Em C, a função fgetc é utilizada para ler apenas 1 caractere por vez. Quando
não há mais nenhum dado a ser lido, a função retorna a constante EOF. Supondo
que seja necessário ler um arquivo contendo 100 caracteres, é correto afirmar que:
a) não há nenhuma forma de utilizar a função fgetc para realizar essa leitura.
b) a função fgetc pode até ser utilizada nesse tipo de operação, mas desde que o
arquivo não contenha mais do que 200 caracteres.
c) a função fgetc pode até ser utilizada nesse tipo de operação, mas desde que o
arquivo não contenha mais do que 250 caracteres.
d) a função fgetc pode ser utilizada para realizar essa leitura ao ser inserida em um
laço de repetição que executa continuamente a leitura de caracteres até que essa
função retorne a constante NULL.
e) a função fgetc pode ser utilizada para realizar essa leitura ao ser inserida em um
laço de repetição que executa continuamente a leitura de caracteres até que essa
função retorne a constante EOF.
105
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *arquivo;
char string[30] = “escrevendo algo no arquivo”;
if (arquivo == NULL)
exit(1);
fputs(string, arquivo);
fclose(arquivo);
}
a) fputc e “a+”
b) fputs e “a+”
c) fputs e “r”
d) fputc e “a”
e) fputc e “r+”
106
7. A função fgets obtém, de uma única vez, um determinado número de caracteres
contidos dentro de um arquivo texto. No entanto, caso a função encontre um
indicador de final de linha, todos os caracteres dessa linha são imediatamente
retornados, mesmo que essa quantidade de caracteres seja menor do que a
solicitada inicialmente. Lembrando que, no contexto de linguagens de
programação, os espaços entre as letras também são considerados como sendo
caracteres.
#include <stdio.h>
#include <stdlib.h>
107
int main()
{
FILE *arquivo;
char caractere;
if (arquivo == NULL)
exit(1);
fgetc(caractere, arquivo);
fclose(arquivo);
}
108
RESPOSTAS DO FIXANDO O CONTEÚDO
UNIDADE 01 UNIDADE 02
QUESTÃO 1 B QUESTÃO 1 D
QUESTÃO 2 D QUESTÃO 2 C
QUESTÃO 3 B QUESTÃO 3 E
QUESTÃO 4 C QUESTÃO 4 C
QUESTÃO 5 D QUESTÃO 5 C
QUESTÃO 6 C QUESTÃO 6 D
QUESTÃO 7 D QUESTÃO 7 D
QUESTÃO 8 A QUESTÃO 8 B
UNIDADE 03 UNIDADE 04
QUESTÃO 1 D QUESTÃO 1 C
QUESTÃO 2 B QUESTÃO 2 D
QUESTÃO 3 A QUESTÃO 3 C
QUESTÃO 4 A QUESTÃO 4 C
QUESTÃO 5 E QUESTÃO 5 A
QUESTÃO 6 C QUESTÃO 6 E
QUESTÃO 7 D QUESTÃO 7 B
QUESTÃO 8 A QUESTÃO 8 E
UNIDADE 05 UNIDADE 06
QUESTÃO 1 D QUESTÃO 1 A
QUESTÃO 2 E QUESTÃO 2 C
QUESTÃO 3 B QUESTÃO 3 B
QUESTÃO 4 E QUESTÃO 4 E
QUESTÃO 5 C QUESTÃO 5 E
QUESTÃO 6 A QUESTÃO 6 B
QUESTÃO 7 D QUESTÃO 7 D
QUESTÃO 8 B QUESTÃO 8 C
109