Escolar Documentos
Profissional Documentos
Cultura Documentos
Introdução à
Programação:
Conceitos e Práticas.
Revisão Técnica:
Dez-2019
Este capítulo resume os principais eventos históricos que culminaram com o estágio
atual da tecnologia digital. Os desenvolvimentos ocorreram nas mais variadas áreas
da Ciência e o processo de evolução tecnológica foi gerado em grande parte pelo
cruzamento destes conhecimentos. Destaca-se como fonte de consulta para
aprofundamento dos temas aqui citados a publicação [ ] . Além disso, o
acervo do e do site , principalmente as
fotos, também serviram de fonte de consulta.
1
http://pt.wikipedia.org/wiki/Máquina_de_Anticítera
Figura 4: Réplica do
Figura 3: O mecanismo de mecanismo de Figura 5: Esquema da máquina
Anticítera. Anticítera2. de Anticítera.
2
Disponível no Museu Arqueológico Nacional de Atenas (feita por Robert J. Deroski, com base em Derek J. de
Solla Price. http://pt.wikipedia.org/wiki/Máquina_de_Anticítera
Este processo evolutivo culminou com o surgimento das primeiras máquinas capazes
de realizar cálculos mais complexos do que as operações básicas. A partir de então,
várias gerações de computadores foram surgindo até os dias atuais.
Surgimento do ;
Utilização de relés como dispositivos de chaveamento;
Utilização de válvulas a vácuo;
Hardware caro, grande e de difícil manutenção;
Programação em linguagem de máquina;
permitiu o uso de software.
Cada programa que necessitava de processamento tinha que seguir este fluxo. A
necessidade de melhores mecanismos de interação entre usuário e o computador,
facilidades para administrar o processamento dos programas e formas de gerenciar o
computador e seus periféricos levou ao desenvolvimento de um conjunto de programas
que foi denominado de .
invenção do transistor;
1º computador a transistor ( ) pela ;
uso de transistores e diodos;
uso de memória de núcleo;
uso de linguagem Assembly até , e de e a partir daí;
surgimento dos Sistemas Operacionais para Processamento em (Lote)
de programas.
Houve importante melhoria nos compiladores, que passaram a produzir códigos tão
eficientes quanto um bom programador de linguagem assembly.
Outro recurso típico da terceira geração permitiu que programas fossem carregados
diretamente dos cartões para discos. Assim, logo que um determinado programa
tivesse sua execução encerrada, o sistema operacional iria carregar um dos
programas do disco para a partição de memória destinada aos serviços em execução.
Este recurso recebeu o nome de (
).
Sistemas Operacionais
Compiladores Inteligentes
Software Linguagem de Máquina Linguagem Assembly Linguagem de Alto Nível Circuitos Integrados VLSI
Hardware Válvulas e Relés Diodo e Transistor Circuitos Integrados Microprocessadores
Essa ideia de agrupar marcas foi utilizada nos sistemas mais antigos de numeração.
Os egípcios da antiguidade criaram um sistema muito interessante para escrever
números, baseado em agrupamentos.
era representado por uma marca que se parecia com um bastão |
por duas marcas ||
E assim por diante.
calcanhar
rolo de corda
flor de lótus
dedo apontando
peixe
homem
Este sistema é bastante limitado para aplicações da matemática que vão além da
representação de números, onde mesmo as operações básicas são difíceis de
serem efetuadas.
● ● ● ●
●● ●● ●● ●●
𝟐 ●
●
𝟗 ●●●●
Este sistema permite a execução das operações básicas com a mesma facilidade
que fazemos no nosso sistema decimal. Vejamos o exemplo que segue:
Os sistemas numéricos egípcio, romano e maia apresentados são apenas parte dos
muitos sistemas adotados por diferentes civilizações da antiguidade, tais como os
babilônios, gregos, chineses, hindus entre outros. Cabe destacar, que com exceção
dos maias, que habitavam a América, as civilizações da Europa, Oriente e Oriente
Na notação posicional os dígitos assumem valores diferentes para cada posição que
ocupa na representação do número.
Tomando o ponto como separador para a parte inteira e a parte fracionária, temos
que a cada posição que se desloca o dígito a esquerda do ponto o seu valor é
multiplicado cumulativamente pela base. Da mesma forma, a cada posição que se
desloca o dígito a direita do ponto o seu valor é dividido cumulativamente pela base.
{ } → Base → Símbolos.
i.
ii.
iii.
{ } → Base → Símbolos.
Valor
Símbolo
Absoluto
i.
ii.
iii.
{ } → Base → Símbolos.
= conjunto de (exemplo: );
( ) = conjunto de , ou mais bytes, que determina a
capacidade de processamento do computador (exemplo: palavra =
bits, palavra = , Arquitetura Pentium = );
= conjunto de (KBytes);
= conjunto de (MBytes);
= conjunto de (GBytes);
Potência de 10 Potência de 2
Potência Valor
Nome Símbolo Nome Símbolo
quilo k kibi Ki 1024
mega M mebi Mi 1 048 576
giga G gibi Gi 1 073 741 824
tera T tebi Ti 1 099 511 627 776
peta P pebi Pi 1 125 899 906 842 624
exa E exbi Ei 1 152 921 504 606 846 976
zetta Z zebi Zi 1 180 591 620 717 411 303 424
yotta Y yobi Yi 1 208 925 819 614 629 174 706 176
i.
ii.
iii.
sendo a parte inteira com dígitos e a parte fracionária com dígitos, sendo que
. Expandindo na forma de polinômio, tem-se:
Desta forma, temos todos os elementos para formar um sistema em qualquer base:
os dígitos a base e as posições em . Por exemplo, para a , teria:
{ } → Base → Símbolos/Dígitos.
i.
ii.
iii.
Exemplos:
A conversão da base para uma base qualquer é feita de forma distinta para a
Parte inteira e para a Parte fracionária:
i. Parte inteira:
47 2 33 2
1 23 2 1 16 2
1 11 2 0 8 2
1 5 2 0 4 2
1 2 2 0 2 2
0 1 2 0 1 2
1 0 1 0
235 8 2030 16
3 29 8 14 126 16
5 3 8 14 7 16
3 0 7 0
Esta primeira divisão de inteiros produziu como resto o dígito . O quociente obtido
foi , que é um polinômio que representa um número
composto pelos demais dígitos, deslocados uma posição à direita
. De fato, a divisão por faz com que todos os dígitos sejam
deslocados uma posição à direita e elimina o dígito mais a direita. Assim, a lógica de
fundo é uma sequência de deslocamentos à direita e a obtenção de um dígito
através do resto. O término deste processo ocorre quando o quociente é zerado.
Neste caso a próxima divisão seria:
Observe a dízima
3.6. Exercícios
i.
ii.
iii.
iv.
v.
vi.
i.
ii.
iii.
iv.
A conversão de uma base com potência inteira de ( ) para a base pode ser
feita através do uso de uma tabela.
Ao converter um número na base para a base , basta substituir cada dígito pela
respectiva combinação de bits.
Exemplo:
Exemplo:
Exemplo:
3.7. Exercícios
i.
ii.
iii.
iv.
v.
vi.
vii.
viii.
ix.
x.
xi.
xii.
xiii.
i.
ii.
iii.
iv.
v.
i.
ii.
4.1.1. Soma
Exemplos:
11 1 1 11111111 111111 1 11
111010101 469 11111111 255 10111100 188 10011001 153
4.1.2. Subtração
–
–
–
–
2
-1 2 -1-1
0 0
- 1 - 1
--- ---
1 0
Exemplos:
222222 2 2
11111111 *******2 **2**2
111010101 469 11111111 255 10111100 188 10110100 180
- 10010100 148 - 1 1 - 1111111 127 - 10011001 153
---------- --- ---------- --- ---------- --- ---------- ---
101000001 321 11111110 254 00111101 61 00011011 27
4.1.3. Multiplicação
O produto de dois números binários também fica simplificado uma vez que a tabela
de multiplicação dos dígitos se reduz a quatro combinações. A composição das
parcelas é feita através da soma. As combinações possíveis são:
Exemplos:
4.1.4. Divisão
A divisão entre binários, apesar de não ser tão direta quanto à multiplicação,
também é simplificada, uma vez que o quociente final é montado a partir de
quocientes parciais que vão agregando e . Além disso, cada dígito do
quociente inicia uma subtração para completar o cálculo. Desta forma, a divisão é
executada através de uma série de subtrações. Observe o seguinte exemplo:
-1+2
1 0 1 1 0 1 1 22 3
- 1 1 1 1 1 - 21 7
1 0 1 quociente 1 quociente
- 1 1 resto
0 1 0 0
- 1 1
0 0 0 1
resto
O quadro da esquerda apresenta o processo de divisão binária e o quadro da direita
mostra os equivalentes decimais.
10110 11
11 1
10
101101 101
101 1001
000101
101
0
O resultado pode ser certificado pelo equivalente decimal, onde neste caso é
dividido por , resultando em quociente e resto .
4.1.5. Exercícios
i.
ii.
iii.
iv.
v.
vi.
vii.
i. –
ii. –
iii. –
i.
ii.
iii.
i.
ii.
iii.
Circuito Equivalente
𝑋 𝐴
Representação da Operação
Tabela Verdade
NOT 0 1
1 0
Circuito Equivalente
𝑋
𝑋
Representação da Operação
Característica da Operação
AND
A B X
0 0 0
0 1 0
1 0 0
1 1 1
ou
Circuito Equivalente
𝑋 𝐴 𝐵
𝑋 ̅̅̅̅̅̅̅̅
𝐴 𝐵
𝐴 𝐵
Tabela Verdade
OR 0 1
0 0 1
1 1 1
Característica da Operação
ou
Representação da Operação
Tabela Verdade
XOR 0 1
0 0 1
1 1 0
Característica da Operação
̅ , onde ̅ significa a negação de
ou
A operação pode ser utilizada para realizar uma criptografia básica. Efetuando
, e em seguida calculando , produz-se novamente o valor de
.
4.2.5. Exemplos
0 0 0 0 0
0 0 1 0 1
0 1 0 0 0
0 1 1 0 1
1 0 0 0 0
1 0 1 0 1
1 1 0 1 1
1 1 1 1 1
0 0 1 1 0 0 0
0 1 1 0 0 1 1
1 0 0 1 1 0 1
1 1 0 0 0 0 0
4.2.6. Exercícios
Montar a tabela verdade e o circuito lógico para cada uma das expressões abaixo:
i.
ii.
iii.
iv.
v.
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0
0 1 0 1 1 0 0 0 0 0
0 1 1 1 0 0 1 0 1 1
1 0 0 0 0 0 0 0 0 0
1 0 1 1 1 1 0 1 1 1
1 1 0 1 1 1 0 1 1 1
1 1 1 1 0 1 1 0 1 1
Este circuito é capaz de realizar a soma de bit. Para somar um inteiro com mais
bits, basta sequenciar vários somadores completos. Por exemplo, se o projeto da
prevê a soma de dois inteiros de , então o circuito poderia ser similar á:
A abordagem feita para o somador completo pode ser aplicada para o subtrator
completo. Para isto, temos que montar uma Tabela Verdade completa para a
subtração de por com eventual pagamento do ,
produzindo como resultado e um novo indicador de .
0 0 0 0 0
0 0 1 1 1
0 1 0 1 0
0 1 1 0 0
1 0 0 1 1
1 0 1 0 1
1 1 0 0 0
1 1 1 1 1
2 2
-1 0 -1-1
0 0
- 1 - 1
--- ---
1 0
De modo similar à soma, é possível observar que a coluna pode ser obtida por
. Já pode ser obtido por para as quatro primeiras
linhas e para as quatro últimas. Combinando as duas equações para
, tem-se . Também é possível obter para
as quatro primeiras linhas e para as quatro últimas. Combinando as
duas equações para , tem-se ( ). Esta última expressão
pode ser trabalhada para reduzir o número de portas lógicas do circuito final e
aproveitar uma operação do cálculo de e chega-se a
( ).
A figura abaixo ilustra a separação entre as informações que circulam no mundo real
e seus equivalentes no mundo digital. Estão exemplificadas informações clássicas,
tais como números e textos. Para entrar no mundo digital é fundamental que o dado
exterior seja convertido para e através de algum método ou convenção.
Representação
de Dados
Números Textos
Inteiros Reais
Exemplo Representação
00001010
01011101
01111111
00000000
11111111
100000000 Não foi possível representar em 8 bits.
A representação de inteiros com sinal demanda algum artifício para codificar o sinal
no padrão digital. Na sequência são apresentados os quatro métodos mais
conhecidos.
Exemplo Representação
00001010
10001010
01111111
00000000
10000000
010000000 Não foi possível representar em 8 bits.
110000000 Não foi possível representar em 8 bits.
11111111
01100100
5.1.2.2. Complemento de 1
5.1.2.3. Complemento de 2
Exemplo Representação
00001010
: 00001010
C-1: 11110101
+ 1
--------
C-2: 11110110 Ok
01111111
00000000
: 00000000
C-1: 11111111
Exemplo Representação
: 10001010 Ok
: 01110110 Ok
: 11111111 Ok
: 10000000 Ok
: 10000000 Ok
: 100000000 Não foi
possível representar em 8 bits.
: 00000000 Ok
5.1.2.5. Visualização
Binário Puro
0 1 ... 127
00..00 00.01 ... 01..11
Módulo-Sinal Complemento de 1
Complemento de 2 Excesso
Quando somamos dois inteiros sem sinal e ocorre o no último bit então
estamos diante de um , ou estouro de capacidade.
Quando somamos dois inteiros com sinal deve ser observada a coerência dos
sinais: Soma de positivos deve gerar resultado positivo e soma de negativos
deve gerar resultado negativo.
11 1 C-2 11001001
11001001 201 -55 - 1
+ 11101110 + 238 + -18 --------
-------- ----- ------ C-1 11001000
10110111 183 -73 |X| 00110111
X = -55
(overflow) (ok) C-2 11101110
- 1
--------
C-1 11101101
|X| 00010010
X = -18
C-2 10110111
- 1
--------
C-1 10110110
|X| 01001001
X = -73
5.1.4. Exercícios
BP MS C–1 C–2
0000 1111
1000 1101
1111 0000
0111 1111
1000 0000
1111 1111
Exemplo:
, onde:
iii. Enquadrar
S Expoente ( ) Mantissa ( )
Exemplo:
0.004600000 x 16 = 0.073600000
0.073600000 x 16 = 1.177600000
0.177600000 x 16 = 2.841600000
0.841600000 x 16 = 13.465600000
0.465600000 x 16 = 7.449600000
0.449600000 x 16 = 7.193600000
ii. Normalizar
iii. Enquadrar
BP: 0000111
C-1: 1111000
+ 1
--------
C-2: 1111001 Ok
Ou simplesmente:
Nem todos os pontos das retas dos reais são representados. Neste caso interessa
conhecer os maiores e os menores números representáveis. Estes limites são
calculados a partir da equação normalizada:
Como:
Logo:
Dígitos
Total Expoente Bits
Tipo Sinal Expoente Mantissa Significativos
bits bias Precisão
Decimal
Half 1 5 10 16 15 11 ~3.3
Single 1 8 23 32 127 24 ~7.2
Double 1 11 52 64 1023 53 ~15.9
Double extended
1 15 64 80 16383 64 ~19.2
(80-bit)
Quad 1 15 112 128 16383 113 ~34.0
S Expoente ( ) Mantissa ( )
Exemplo:
ii. Normalizar
Observe que o primeiro bit da mantissa reposicionado na parte inteira ficará oculto
na representação.
iii. Enquadrar
Ou simplesmente:
5.2.2. Exercícios
–
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0 NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
1 1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
2 2 ! " # $ % & ' ( ) * + , - . /
3 3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 4 @ A B C D E F G H I J K L M N O
5 5 P Q R S T U V W X Y Z [ \ ] ^ _
6 6 ` a b c d e f g h i j k l m n o
7 7 p q r s t u v w x y z { | } ~ •
8 8 Ç ü é â ä à å ç ê ë è ï î ì Ä Å
9 9 É æ Æ ô ö ò û ù ÿ Ö Ü ø £ Ø × ƒ
10 A á í ó ú ñ Ñ ª º ¿ ® ¬ ½ ¼ ¡ « »
11 B ░ ▒ ▓ │ ┤ Á Â À © ╣ ║ ╗ ╝ ¢ ¥ ┐
12 C └ ┴ ┬ ├ ─ ┼ ã Ã ╚ ╔ ╩ ╦ ╠ ═ ╬ ¤
13 D ð Ð Ê Ë È ı Í Î Ï ┘ ┌ █ ▄ ¦ Ì ▀
14 E Ó ß Ô Ò õ Õ µ þ Þ Ú Û Ù ý Ý ¯ ´
15 F ± ‗ ¾ ¶ § ÷ ¸ ° ¨ · ¹ ³ ² ■
Caracter . Caracter .
J o a o d a S i l v a
74 111 97 111 32 100 97 32 83 105 108 118 97
A v B r a s I l , 3 2 2 . A p t º 1 0 6 C
65 118 32 66 114 97 115 73 108 44 32 51 50 50 46 32 65 112 116 186 32 49 48 54 67
DADOS
C: 1101
X: 1100
Y: 1011
Z: 1010
T: 1001
mov eax, A 1000
add eax, B 0111
INSTRUÇÕES mov C, eax 0110
(código) mov ebx, eax 0101
mov eax, X 0100
sub eax, Y 0011
mov Z, eax 0010
add ebx, eax 0001
mov T, eax 0000
A pode ser vista como uma fábrica que processa matéria prima gerando
produtos. Neste cenário a é a responsável pelo gerenciamento
da fábrica, incluindo a entrada da matéria prima, a fabricação do produto e a entrega
deste produto. Cada instrução recebida pela se assemelha a
chegada de uma ordem de serviço para fabricação de um produto. Inicialmente o
gerente da fábrica busca compreender a instrução de fabricação do produto,
partindo em seguida para o preparo da linha de produção para a fabricação, e uma
vez pronto, o produto é encaminhado para o depósito, de onde tomará seu destino
final.
Os sinais de controle que são ativados, bem como a sequência com que são
ativados, dependem de cada instrução em particular.
Os registradores de uso geral normalmente são usados para armazenar dados que
serão processados pela , bem como resultados produzidos pela . O
registrador de estado (status register) associado à é um registrador de uso
específico, e contém informações sobre o resultado produzido pela . Este
3
registrador possui bits sinalizadores que são ativados ou desativados de acordo
com o tipo de resultado produzido pela ( ...).
Um outro exemplo de registrador de uso específico é o contador de programa
( ), cuja função é guardar o endereço da locação de memória onde
se encontra a próxima instrução a ser executada pelo processador.
6.1.3. Barramentos
3
Considera-se aqui que um bit é ativado quando ele recebe o valor lógico 1, sendo desativado ao receber o
valor lógico 0.
[ ]
Esta sequência culmina com uma ordem ao controlador da memória em pegar estas
informações dos barramentos e efetuar a operação de gravar na memória.
Barramento de Endereço
Tamanho dos Barramento
Processador Tamanho Quantidade de Bytes
Registradores de Dados
de Memória
8086 20 16 16
80286 24 16 16
80386 32 32 32
80486 32 32 32
Pentium 32/36/644 32/64 32/64
clock
4
Primeiras implementações utilizam parte dos 64 bits. O uso integral permitiria 16 EBytes,
http://en.wikipedia.org/wiki/X86-64
6.1.5.1. Pipeline
Uma destas técnicas foi inspirada no conceito da linha de produção. Foi visto que o
processamento de uma instrução na passa pelas etapas de: , ,
e ; ou Busca, Decodificação, Execução e Resultado. Assim, uma
instrução quando trazida da memória para a ocupava a até o término da
sua execução. Então, os projetistas trabalharam no redesenho da de tal forma
que os circuitos fossem reagrupados em partes independentes, uma para cada
etapa do ciclo de execução. Esta técnica, inspirada na linha de produção, foi
denominada de .
6.1.5.2. Cache
Logo os projetistas concluíram que era possível desenvolver uma memória com
tempo de acesso menor que o tempo de acesso da , e em menor quantidade, o
suficiente para acomodar o trecho da que está sendo referenciado num
determinado momento.
Atualmente não restam mais dúvidas sobre o sucesso da tecnologia digital, pois em
curto espaço de tempo alcançou um grau de disseminação que transcende
fronteiras, culturas e classes sociais. Certamente o nível de desenvolvimento
tecnológico que presenciamos hoje representa apenas uma amostra do que há por
vir.
FORTRAN (54)
COBOL (59)
1960 ALGOL 60
Simula 67 ALGOL 68
1970
C (72) Pascal (71)
1990
5
http://pt.wikipedia.org/wiki/Fortran
LISP (59)
Smalltalk (69)
PROLOG (72)
OPS5 (77)
L01 instrução
L02 instrução
L03 jump L06
L04 instrução
L05 jump L16
L06 instrução
L07 jump L10
L08 instrução
L09 jump L04
L10 instrução
L11 jump L15
L12 instrução
L13 jump L02
L14 instrução
L15 jump L01
L16 instrução
L17 jump L10
L18 instrução
Figura 44: Estilo de Programação Spaghetti.
Nas décadas seguintes foram surgindo novos estilos de programação com o objetivo
de melhorar a engenharia dos programas, e consequentemente elevar o grau de
qualidade e confiabilidade destes produtos. Estes estilos visavam a melhor
organização das entidades que compõem um programa, principalmente dados e
6
http://pt.wikipedia.org/wiki/ALGOL
A linguagem também era complexa, e acabou dando origem a uma versão mais
simples, denominada (Basic ). Novas adaptações geraram a linguagem ,
para se consolidar em uma proposta de linguagem que foi denominada de .
#include <stdio.h>
int main () {
/* área destinada a dados e estruturas locais */
Uma das vantagens das linguagens de alto nível em relação às linguagens de baixo
nível foi a possibilidade de trabalhar com nomes em vez de endereços físicos de
memória. Assim, na sequência são apresentadas as regras para a criação de nomes
ou identificadores.
11. Identificadores
7
Notação semelhante à BNF:
O símbolo “::=” significa “é definido como”.
O símbolo “*” significa “repetir 0 ou mais vezes”.
Como citado anteriormente, uma constante inteiro pode ter um sufixo, caracter
adicionado a direita, para designar como (U) e (L). Este sufixo pode
ser maiúsculo ou minúsculo e pode estar em qualquer ordem. Por exemplo: ,
e .
x = 3.1415;
x = -1234.4567;
x = +.4567;
x = +1234.;
Ou
Faremos uma analogia da variável com uma cadeira. Observe o seguinde esquema:
int v;
int main () {
v = +5;
int main () {
int x, y, z; A expressão irá fazer com o valor
x = y = z = 0;
seja atribuído a variável , em seguida a e a .
8
https://en.cppreference.com/w/c/language/operator_precedence, acessado em 05-mar-2019 as 12:03
9
https://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm, acessado em 05-mar-2019 as
12:15
a)
b)
c)
d)
e)
f)
g)
h)
i)
j)
k)
l)
m)
n)
o)
p)
q)
r)
s)
t)
u)
v)
w)
1)
Exemplo:
#include <stdio.h>
#include <math.h>
ret = acos(-1);
printf("O valor de PI = %lf\n", ret);
return 0;
}
O valor de PI = 3.141593
2)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
ret = asin(1.0);
printf("O valor de PI = %lf\n", 2*ret);
return 0;
}
O valor de PI = 3.141593
3)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
O valor de PI = 3.141593
4)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
O valor de PI = 3.141593
5)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, pi;
pi = acos (-1.0);
ret = cos(pi/3);
printf("O valor de cos(PI/3) = %lf\n", ret);
return 0;
6)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
7)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, pi;
pi = acos (-1.0);
ret = sin (pi/6);
printf("O valor de sin(PI/6) = %lf\n", ret);
return 0;
}
8)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
9)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
10)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, euler;
return 0;
}
11)
, onde
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double mantissa, x = 1.0/16; int expoente;
12)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = 0.5, ret;
int n = 4;
13)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret;
return 0;
}
14)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double ret, x = 0.0001;
return 0;
}
15)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = 123.456, parte_frac, parte_int;
16)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("Raiz quadrada de 2 = %lf\n", pow(2, 1.0/2));
printf("Raiz cubica de 8 = %lf\n", pow(8, 1.0/3));
return 0;
}
17)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("Raiz quadrada de 2 = %lf\n", sqrt(2));
printf("Raiz quadrada de 64 = %lf\n", sqrt(64));
return 0;
}
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("ceil (-1.4) = %lf\n", ceil (-1.4));
printf("ceil (-1.5) = %lf\n", ceil (-1.5));
printf("ceil (-1.6) = %lf\n", ceil (-1.6));
printf("ceil (1.4) = %lf\n", ceil (1.4));
printf("ceil (1.5) = %lf\n", ceil (1.5));
printf("ceil (1.6) = %lf\n", ceil (1.6));
return 0;
}
19)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x = -11.11, y = 22.22;
int z = -123;
return 0;
}
20)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
printf("floor (-1.4) = %lf\n", floor (-1.4));
printf("floor (-1.5) = %lf\n", floor (-1.5));
printf("floor (-1.6) = %lf\n", floor (-1.6));
printf("floor (1.4) = %lf\n", floor (1.4));
printf("floor (1.5) = %lf\n", floor (1.5));
printf("floor (1.6) = %lf\n", floor (1.6));
return 0;
}
21)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x;
x = 7.3; printf("round (%5.1lf) = %5.1lf\n", x, round(x));
x = 7.5; printf("round (%5.1lf) = %5.1lf\n", x, round(x));
x = 10.5; printf("round (%5.1lf) = %5.1lf\n", x, round(x));
x = -10.5; printf("round (%5.1lf) = %5.1lf\n", x, round(x));
return 0;
}
22)
Exemplo:
#include <stdio.h>
#include <math.h>
int main () {
double x;
x = 7.3; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
x = 7.5; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
x = 10.5; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
x = -10.5; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
x = -7.5; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
x = -7.3; printf("trunc (%5.1lf) = %5.1lf\n", x, trunc(x));
return 0;
}
23)
Exemplo:
int main () {
double x, y;
x = 7.5; y = 2.3;
printf("resto de %lf/%lf = %lf\n", x, y, fmod(x, y));
x = acos(-1.0); y = 1.0;
printf("resto de %lf/%lf = %lf\n", x, y, fmod(x, y));
return 0;
}
Equação Programa
#include <stdio.h>
#include <math.h>
int main () {
double x = 3, y = 3, z;
return 0;
}
z = 1.000000
int main () {
double x = M_PI/2, z;
ii. z = (1 + sin(x))/(1 + cos(x));
printf("z = %lf\n", z);
return 0;
}
z = 2.000000
int main () {
double x = 2, z;
iii.
z = 1 + 1/x + 1/(x*x) + 1/(x*x*x) +
1/(x*x*x*x);
printf("z = %lf\n", z);
return 0;
}
y = 1/x;
z = 1 + y*(1 + y*(1 + y*(1 + y)));
( ( ( ))) printf("z = %lf\n", z);
return 0;
}
z = 1.937500
int main () {
double x = 2, y = 1, z, w;
iv.
w = x/y;
( ) z = w - (x + pow(w,2))/(y - pow(w,2));
printf("z = %lf\n", z);
( )
return 0;
}
z = 4.000000
int main () {
double x = 25, z;
z = sqrt(M_PI +
v.
sqrt(exp(3) +
sqrt(4 +
sqrt(x)
√ √ √ √ )));
printf("z = %lf\n", z);
return 0;
}
z = 2.818924
Vamos reforçar alguns conceitos sobre string em a fim de fazer uso das funções
de entrada e saída. É necessário no momento compreender o uso de uma string
literal ou constante string, já mencionado anteriormente. É bastante simples: é uma
sequência de caracteres delimitados no início e no fim pelo símbolo aspas. É
Logo após a string de formato vem uma lista com tantos endereços de memória
quantos forem os dados indicados na string de formato. Neste exemplo, usamos
uma string de formato indicando que serão fornecidos dois números inteiros em
decimal. Logo, deverão ser fornecidos dois endereços de memória separados por
vírgula.
35 56
Formato Descrição
Inteiro decimal com sinal
Inteiro decimal sem sinal
Inteiro – decimal, octal ou hexa
Inteiro octal
ou Inteiro hexa
Float
Double
Float com notação de engenharia. Ex. ou
Um caractere
Uma string, sequência de até encontrar um separador
#include <stdio.h>
#include <math.h>
int main () {
int x, y, z, n;
n = scanf ("%d %d",&x,&y);
z = x + y;
printf ("foram fornecidos %d dados\n", n);
printf ("O valor de %d + %d = %d\n", x, y, z);
return 0;
}
35 56
foram fornecidos 2 dados
O valor de 35 + 56 = 91
^Z
foram fornecidos -1 dados
O valor de 0 + 4202064 = 4202064
A primeira linha corresponde ao texto digitado pelo usuário, que neste caso não
forneceu nenhum número e teclou ( seguido de ). Assim, o
retorna , indicando que a digitação foi encerrada pelo usuário sem o
fornecimento de dado algum. Observe que as variáveis e ficaram com resquícios
da memória, comumente chamado de .
35^Z
foram fornecidos 1 dados
O valor de 35 + 4202064 = 4202099
#include <stdio.h>
#include <math.h>
int main () {
int x, y, z;
scanf ("%d %d",&x,&y);
z = x + y;
printf ("O valor de %d + %d = %d\n",x, y, z);
return 0;
}
35 56
O valor de 35 + 56 = 91
O diagrama que segue apresenta os efeitos de cada uma das três linhas de códigos.
Na primeira linha, a função aguarda a digitação de um texto composto por
dois números inteiros em decimal. O texto fornecido é interpretado pela função
, de acordo com a orientação dada pela string de formato, e dois números
inteiros são extraídos, sendo que o primeiro é armazenado no endereço associado
a variável e o segundo no endereço associado a variável .
#include <stdio.h>
int main () {
return 0;
}
ola mundo
10
O programa que segue explora os tamanhos dos tipos básicos da linguagem para o
compilado em uso. Os valores podem alterar conforme a plataforma utilizada.
#include <stdio.h>
#include <math.h>
int main () {
return 0;
}
sizeof (char) = 1
sizeof (int) = 4
sizeof (float) = 4
sizeof (double) = 8
sizeof (void) = 1
sizeof (char *) = 4
sizeof (long int) = 4
sizeof (long long int) = 8
sizeof (long double) = 12
#include <stdio.h>
#include <float.h>
#include <math.h>
#include <limits.h>
int main(void)
{
printf("INT_MIN = %+d\n", INT_MIN);
printf("INT_MAX = %+d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("\n");
return 0;
}
INT_MIN = -2147483648
INT_MAX = +2147483647
UINT_MAX = 4294967295
LONG_MIN = -2147483648
LONG_MAX = +2147483647
ULONG_MAX = 4294967295
FLT_MIN = 1.175494e-038
FLT_MAX = 3.402823e+038
FLT_EPSILON = 1.192093e-007
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308
DBL_EPSILON = 2.220446e-016
#include <iostream>
#include <limits>
int main(void)
{
printf("INT_MIN = %+d\n", INT_MIN);
printf("INT_MAX = %+d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("\n");
return 0;
}
INT_MIN = -2147483648
INT_MAX = +2147483647
UINT_MAX = 4294967295
LONG_MIN = -2147483648
LONG_MAX = +2147483647
ULONG_MAX = 4294967295
FLT_MIN = 1.175494e-038
FLT_MAX = 3.402823e+038
FLT_EPSILON = 1.192093e-007
DBL_MIN = 2.225074e-308
DBL_MAX = 1.797693e+308
DBL_EPSILON = 2.220446e-016
17.3. Exemplos
i. Elaborar um programa que leia dados como: Peso, Altura e Sexo; e calcule o Índice
de Massa Corpórea.
#include <stdio.h>
#include <math.h>
int main () {
double peso, altura, imc;
char sexo;
return 0;
}
O programa inicia com a declaração das variáveis. São utilizadas três variáveis do
tipo para representar as grandezas peso, altura e o imc. Uma variável do tipo
é reservada para representar o sexo.
0 2 5 . 8 3
#include <stdio.h>
#include <math.h>
return IMC;
};
int main () {
double peso, altura, imc;
return 0;
}
1)
2)
3)
4)
5)
1)
2) {
Início do .
3)
4)
5)
6) }
Por fim vem o símbolo que indica o fim do bloco de código que corresponde
ao .
18.2. Exercícios
i. Elaborar um programa que calcule a distância entre dois pontos dados pelas suas
coordenadas
#include <stdio.h>
#include <math.h>
double getDIST (double x1, double y1, double x2, double y2) {
};
int main () {
double D, Ax, Ay, Bx, By;
return 0;
}
ii. Escrever uma function que receba dois números ( e ) e calcule o valor de .
Seria o equivalente ao papel da função .
#include <stdio.h>
#include <math.h>
int main () {
double x = 2, y = 0.5;
printf ("pow (%lf, %lf) = %.15lf\n", x, y, pow (x, y));
printf ("power (%lf, %lf) = %.15lf", x, y, power (x, y));
return 0;
}
( )
iii. Escrever uma function que calcule a média aritmética entre dois números:
#include <stdio.h>
#include <math.h>
return 0;
}
iv. Escrever uma function que calcule a média aritmética entre quatro números:
#include <stdio.h>
#include <math.h>
int main () {
double x, y, u, v, z;
printf ("entre com quatro numeros\n");
scanf ("%lf %lf %lf %lf", &x, &y, &u, &v);
z = media4 (x, y, u, v);
printf ("media (%lf, %lf, %lf, %lf) = %lf", x, y, u, v, z);
return 0;
}
v. Escrever uma function que calcule o seno de um ângulo utilizando a seguinte série:
Vamos limitar a série aos cinco primeiros termos, pois não temos recursos
adequados para ir muito além de forma estruturada.
#include <stdio.h>
#include <math.h>
return rad
- pow(rad, 3)/(3*2)
+ pow(rad, 5)/(5*4*3*2)
- pow(rad, 7)/(7*6*5*4*3*2)
+ pow(rad, 9)/(9*8*7*6*5*4*3*2);
};
int main () {
double ang_g, ang_r, y1, y2;
return 0;
( )
#include <stdio.h>
#include <math.h>
int main () {
double ang_g, ang_r, y1, y2;
return 0;
}
int main( )
Avalia se o valor do primeiro argumento é {
ao valor do segundo argumento. float x = 3.14;
Aplica-se tanto a valores numéricos e int a = 100;
ponteiros. char c = 'b';
ii. int z;
Nas três linhas seguintes temos testes sobre o valor da variável inteira , que foi
inicializada com o valor : .
Operadores Tabelas-Verdade
i. (AND)
O resultado é ( ) quando
todos os operandos forem 0 1 A B X
( ). Nas demais situações o 0 0 0 0 0 0
resultado é ( . 1 0 1 0 1 0
1 0 0
ii. 1 1 1
iii. (OR)
O resultado é ( quando 0 1 A B X
todos os operandos forem 0 0 1
. Nas demais situações 0 0 0
1 1 1 0 1 1
o resultado é ( .
1 0 1
1 1 1
iv. (XOR)
O resultado é ( quando
um e apenas um dos operandos 0 1 A B X
for Nas demais 0 0 1 0 0 0
situações o resultado é 1 1 0 0 1 1
( .
1 0 1
1 1 0
v. (NOT)
! 0 1
O resultado é a negação do 1 0 A X
valor do operando. 0 1
1 0
Está claro que codificar expressões lógicas operando somente sobre constantes
ou não permitirá construir programas práticos. Assim, normalmente utilizamos os
19.3. Exemplos
#include <stdio.h>
#include <ctype.h>
int main( )
{
return 0;
}
Ao executar este programa, a seguinte tela de saída será gerada decorrente dos
:
1
1
0
0
0
i.
Retorna a equivalente maiúscula de um caractere.
#include <stdio.h>
#include <ctype.h>
int main( )
{
printf ("%d\n", isconsoante('a'));
printf ("%d\n", isconsoante('O'));
printf ("%d\n", isconsoante('0'));
printf ("%d\n", isconsoante('T'));
printf ("%d\n", isconsoante('*'));
return 0;
}
0
0
0
1
0
i.
Verifica se um cararactere é uma letra ou dígito.
ii.
Verifica se um cararactere é uma letra maiúscula ou minúscula.
iii.
Verifica se um cararactere é um dos caracteres de controle (código de
a ). Podem existir outros, dependendo da arquitetura.
iv.
Verifica se um cararactere é um dos dígitos númericos.
v.
Verifica se um cararactere possui representação gráfica.
vi.
Verifica se um cararactere é um dos caracteres de a '.
vii.
Verifica se um cararactere é passível de ser impresso.
viii.
Verifica se um cararactere é um caractere gráfico ( ) e não é um
caractere alfanumérico ( ).
ix.
x.
Verifica se um cararactere é um dos caracteres de a '.
xi.
Verifica se um cararactere é um dos símbolos utilizados na base hexadecimal
( a , a , e ).
iii. Elaborar um programa que informa o maior entre dois números inteiros.
#include <stdio.h>
return x > y ? x : y;
int main( )
{
int a, b, c;
scanf ("%d %d", &a, &b);
printf ("%d\n", maior (a, b));
return 0;
}
30 40
40
Em função das inúmeras alterações que o calendário foi submetido ao longo dos
tempos, acabou sendo consolidado o padrão solar. O ano solar, ou seja, o tempo
que a leva para completar um ciclo em torno do te a duração de
ou dias. Para sincronizar o calendário e o tempo solar
foi introduzido, pelos imperadores e , o ano bissexto para
compensar a fração de dia não considerada no total de dias do ano. Esta mudança
fez com que os anos que fossem múltiplos de 4 passassem a ter dias, e os
demais dias. Mesmo com esta alteração, a diferença entre a duração média do
novo calendário com dias e o valor real de conduzia a defasagem
significativa no longo prazo. Deste modo, o , em
, suprimiu dias do calendário e determinou que o dia
fosse seguido pelo dia . As regras para
foram ajustadas e ficou estabelecido que10:
10
http://pt.wikipedia.org/wiki/Ano_bissexto
Leap Years: the rule. U.S. Naval Observatory (14 September 2007).
#include <stdio.h>
t31 = m == 1 || m == 3 || m == 5 || m == 7 ||
m == 8 || m == 10 || m == 12;
t30 = m == 4 || m == 6 || m == 9 || m == 11;
t02 = m == 2;
return t31 ? 31 :
t30 ? 30 :
t02 ? 28 + leap (a) : 0;
int main( )
{
printf ("%d\n", days ( 2, 2000));
printf ("%d\n", days ( 1, 1996));
printf ("%d\n", days ( 2, 1992));
printf ("%d\n", days ( 2, 1994));
printf ("%d\n", days ( 2, 1996));
printf ("%d\n", days (12, 1600));
printf ("%d\n", days ( 2, 2013));
printf ("%d\n", days (15, 2000));
return 0;
}
29
31
29
28
29
31
28
0
O tipo ponteiro faz parte do conjunto de tipos básicos da linguagem . Este tipo é
destinado a criação de variáveis com capacidade de armazenar um endereço de
memória. O conceito é bastante simples e ao mesmo tempo com grande poder de
construção de estruturas complexas.
Vamos retornar ao nosso modelo que associa uma variável a uma cadeira.
Recordemos que a cadeira possui um nome que provê acesso direto ao valor
assentado sobre ela. Sob o assento tem uma outra identificação da cadeira que é o
número que representa o endereço de memória. Considerando as variáveis:
teríamos a seguinte representação visual:
Vamos atribuir um valor à esta variável. Pela declaração ela pode receber o
endereço de qualquer cadeira do tipo . O operador já foi apresentado e
bastante utilizado junto com função , para infomar os endereços de memória
Agora vamos lançar mão deste operador para obter o endereço da variável e
armazenar este valor na variável :
#include <stdio.h>
#include <math.h>
ptr = &x;
ptr = NULL;
printf ("ptr = %p\n", ptr);
return 0;
}
&x = 6422188
&x = 0061FEAC
&y = 0061FEA8
&z = 0061FEA4
&ptr = 0061FEA0
x = 35
y = 56
z = 91
ptr = 0061FEAC
*ptr = 35
ptr = 00000000
#include <stdio.h>
#include <math.h>
int main () {
int x = 7, y = 3, z;
return 0;
}
x = 7
y = 3
z = 14
i. Início da função :
#include <stdio.h>
#include <math.h>
int main () {
int x = 7, y = 3, z;
return 0;
}
Observe as alterações feitas nas definições dos parâmetros. Nesta versão, a função
aguarda como parâmetros os endereços das variáveis que ela terá possibilidade
de alterar. Ao executar este programa, a seguinte tela de saída será gerada
decorrente dos :
x = 8
y = 6
z = 14
A diferença em relação ao programa anterior está nos valores finais das variáveis
e , que passoaram a conter os valores e respectivamente. Isto foi consequência
do forma de passagem de parâmetro. Observar na sequência a visualização do
comportamento do programa, nos pontos e , perante esta modificação.
i. Vamos utilizar o problema do cálculo das raízes de uma equação do segundo grau
para ilustrar bem o caso de um módulo que deve produzir mais de um resultado. É
esperado que uma lógica deste tipo produzisse pelo menos as duas raízes. Assim,
segue uma solução para o problema de obter as raízes de uma equação do segundo
grau a partir dos coeficientes , e .
#include <stdio.h>
#include <math.h>
int main () {
double x1, x2;
return 0;
}
A solução adotada utiliza uma função para cálculo das raízes com retorno .
Desta forma todo o trabalho da função será capturado por meio de acesso indireto
via parâmetros. Os parâmetros , e são passados por cópia de valor e os
parâmetros e são cópia de endereços de memória (ponteiros). Estes últimos
parâmetros servirão como mecanismo para transferir o resultado para a função
chamadora, que neste caso é a função . O diagrama mais adiante ilustra o
contexto da .
i. Início da função :
raiz 1 = 7.00000
raiz 2 = 5.00000
void tohms (int seg, int *h, int *m, int *s) {
*h = seg / 3600;
seg = seg % 3600;
*m = seg / 60;
*s = seg % 60;
}
int main () {
int segundos, hh, mm, ss;
scanf ("%d", &segundos);
tohms (segundos, &hh, &mm, &ss);
printf ("%d = %02d:%02d:%02d", segundos, hh, mm, ss);
return 0;
}
void tohms (int seg, int *h, int *m, int *s) {
*h = seg / 3600;
seg = seg % 3600;
*m = seg / 60;
*s = seg % 60;
}
37850
37850 = 10:30:50
Estas linhas de código, com os recursos vistos, podem ser do tipo instrução de
atribuição ou chamadas de funções. O lado direito da instrução pode incluir
expressões com funções. Assim, quando uma linha de código invoca uma ,
tem-se um fluxo semelhante ao da figura que segue:
Ainda nesta situação, tem-se o seguinte fluxo único para a execução do programa.
;
;e
.
A sintaxe da instrução é:
22.1.2. Exemplos
Diagrama Programa
#include <stdio.h>
int main () {
double X, Y;
int A, B;
if (X > Y)
A = 1;
else
A = 2;
i.
// Se a expressão for então será
// executada a instrução caso contrário
// será executada a instrução .
if (X > 10) {
A = 1;
B = 2;
} else A = B + 1;
if (X >= 10) {
A = -1;
B = -2;
} else {
iv.
A = 3;
B = 4;
}
// Observe a ausência do antes do
Quando o else é precedido de um bloco, como { } , este bloco não pode ser encerrado com
. Observe nos exemplos e que o não vem precedido deste símbolo. Outra
if (X > Y) if (X > Y)
if (X != 0) if (X != 0)
A = 1; A = 1;
else A = 2; else A = 2;
Observe que os dois códigos são idênticos, pois possuem exatamente a mesma sequência
de palavras ( ). O deslocamento ( ) do texto mais para esquerda ou para a
direita não afeta em nada a forma como a linguagem interpreta a sentença. Assim, uma das
duas construções não representa o diagrama correspondente. Está claro no desenho que são
lógicas distintas. Este caso ficou conhecido no Algol como . Esta ambiguidade é
resolvida vinculando o ao mais interno. Havendo a necessidade em vincular
o ao mais externo, um bloco { } deverá ser utilizado, como indicado no código abaixo:
if (X > Y) { if (X > Y)
if (X != 0) if (X != 0)
A = 1; A = 1;
} else A = 2;
else A = 2;
A solução adotada anteriormente para o cálculo das raízes poderá em fim ser
completada com o tratamento adequado das condições que poderiam levar a um
erro na execução da lógica.
Assim, devemos construir uma nova lógica que desvia o fluxo de execução do
módulo para que não ocorram nem nem .
#include <stdio.h>
#include <math.h>
int main () {
double a1, b1, c1, x1, x2;
int code;
return 0;
}
vi. Uma família de problemas muito comum em situações reais é a relacionada com a
manipulação de datas. Assim, este exemplo pede uma solução para o cálculo da
quantidade de dias de um determinado mês e ano.
#include <stdio.h>
int main () {
printf ("%d\n", days ( 2, 2000));
printf ("%d\n", days ( 1, 1996));
printf ("%d\n", days ( 2, 1992));
printf ("%d\n", days ( 2, 1994));
printf ("%d\n", days ( 2, 1996));
printf ("%d\n", days (12, 1600));
printf ("%d\n", days ( 2, 2013));
printf ("%d\n", days (15, 2000));
return 0;
}
29
31
29
28
29
31
28
0
vii. Implementar uma solução que incrementa em um dia uma data válida (dia, mês e
ano).
#include <stdio.h>
#include <math.h>
int main () {
int dia, mes, ano;
printf ("entre com uma data: dd mm aaaa\n");
scanf ("%d %d %d", &dia, &mes, &ano);
incdate (&dia, &mes, &ano);
printf ("o dia seguinte eh: %02d/%02d/%d\n", dia, mes, ano);
return 0;
}
22.2.2. Exemplos
#include <stdio.h>
int main () {
int x;
printf ("entre com um inteiro\n");
scanf ("%d", &x);
switch (x) {
case 0 : printf ("o valor eh zero\n");
case 1 : printf ("o valor eh um\n");
case 2 : printf ("o valor eh dois\n");
default :
printf ("o valor eh maior que dois\n");
}
return 0;
}
Neste caso, o usuário forneceu o valor para . O teste para falha. Então o
processamento segue para o teste do , que é bem sucedido e o fluxo é
desviado para executar as instruções associadas a esta condição. A mensagem
correspondente a este case é impressa, e o fluxo segue processando as instruções
dos demais . Certamente não era o comportamento pensado pelo programador
para este problema.
#include <stdio.h>
#include <math.h>
int main () {
int x;
printf ("entre com um inteiro\n");
scanf ("%d", &x);
switch (x) {
case 0 : printf ("o valor eh zero\n"); break;
case 1 : printf ("o valor eh um\n"); break;
case 2 : printf ("o valor eh dois\n"); break;
default :
printf ("o valor eh maior que dois\n");
}
return 0;
}
#include <stdio.h>
#include <ctype.h>
int main () {
printf ("%d\n", isconsoante('a'));
printf ("%d\n", isconsoante('B'));
printf ("%d\n", isconsoante('x'));
printf ("%d\n", isconsoante('3'));
return 0;
}
0
1
1
0
#include <stdio.h>
int main () {
printf ("%d\n", days ( 2, 2000));
printf ("%d\n", days ( 1, 1996));
printf ("%d\n", days ( 2, 1992));
printf ("%d\n", days ( 2, 1996));
printf ("%d\n", days (12, 1600));
printf ("%d\n", days ( 2, 2013));
printf ("%d\n", days (15, 2000));
return 0;
}
A expressão teste do é avaliada e seu valor comparado com zero. Caso seja
!= o fluxo do processamento segue para as instruções controladas. Após executar
todas as instruções controladas, retorna novamente ao para um novo teste do
valor da expressão inteira. O é encerrado quando o valor da expressão teste for
. Observe que utilizaremos o termo expressão teste, expressão inteira ou
expressão lógica como equivalentes.
#include <stdio.h>
void lerdados () {
int t;
float maior, x, s, m;
s = 0;
t = 0;
printf ("Entre com os dados\n");
scanf ("%f", &x);
maior = x;
while (x != -1) {
t++;
if (x > maior)
maior = x;
s = s + x;
scanf ("%f", &x);
};
m = s/t;
printf ("soma = %7.2f\nmedia = %7.2f\nmaior = %7.2f", s, m, maior);
};
int main () {
lerdados();
return 0;
}
ii. Elaborar um programa que calcule a raiz quadrada ( √ ) de um número pelo método
de .
( )
A repetição deve ser encerrada quando a diferença entre o valor atual e o anterior for
inferior a um determinado . Normalmente o erro estabelecido é o menor número
possível de ser representado na arquitetura em que o programa está rodando. Este
erro pode ser obtido por uma função que executa divisões sucessivas a partir de
até alcançar o , sendo que o valor anterior ao é o menor número
representável naquela arquitetura. A solução proposta é dada por:
#include <stdio.h>
#include <math.h>
double GetEPS () {
double EPS, x;
x = 1;
while (x != 0) {
EPS = x;
x = x/2;
};
return EPS;
};
if (x >= 0) {
R = 0; r = 1;
EPS = GetEPS();
while (fabs(r - R) >= EPS) {
R = r;
r = (R + x / R) / 2;
return r;
};
int main () {
R = 0; r = 1;
EPS = GetEPS();
O principal determina que seja calculada uma nova raiz a cada ciclo
a diferença entre o valor do ciclo atual e o do ciclo anterior seja
superior à precisão estabelecida. A é muito
semelhante à expressão codificada.
1.4142135623730951
1.4142135623730951
#include <stdio.h>
#include <math.h>
double GetEPS () {
double EPS, x;
x = 1;
while (x != 0) {
EPS = x;
x = x/2;
};
return EPS;
};
p = i = 1;
while (i <= t) {
p *= x;
i++;
}
return p;
};
if (x >= 0) {
R = 0; r = 1;
EPS = GetEPS();
while (fabs(r - R) >= EPS) {
R = r;
r = ((n-1)*R + x / powint(R, n-1)) / n;
};
} else r = 0;
return r;
};
int main () {
double y
y = raizn (25, 3);
printf ("%.15f\n%.15f", y, powint(y, 3));
return 0;
}
A função pode ser implementada de outra forma, tal como a que segue,
(I)
baseada no exemplo do , publicado pelo prof. Niklaus Wirth.
w = x; z = 1; i = t;
while (i) {
if (i % 2) z *= w;
i /= 2;
w *= w;
};
return z;
};
A expressão equivale a
A expressão equivale a
A expressão equivale a
A série possui termos, onde pode ser qualquer valor maior que um. Os termos
da série possuem a seguinte forma geral:
Assim, é necessário agora formular uma solução para gerar cada um dos termos, e
somá-los a fim de atender a especificação do problema. Segue solução
implementada:
#include <stdio.h>
return s;
}
int main () {
printf ("%8.6lf\n", soma (2));
printf ("%8.6lf\n", soma (5));
printf ("%8.6lf\n", soma (100));
return 0;
}
Instrução S
1
2
3
4
5
1.500000
2.283333
5.187378
Esta série difere da anterior pela alternância dos sinais dos termos. Uma solução
possível é utilizar variável contadora de repetições para definir o sinal do termo a ser
adiconado.
#include <stdio.h>
i = i + 1;
}
return s;
}
int main () {
printf ("%8.6lf\n", soma (2));
printf ("%8.6lf\n", soma (5));
printf ("%8.6lf\n", soma (100));
return 0;
}
A lógica adotada aproveita o valor de para definir o sinal da parcela. Assim, quando
for par o termo deve ser subtraído (negativo) de e quando for impar deve ser
somado (positivo). O teste se é par é feito avaliando o resto da divisão de por .
Uma alternativa a lógica apresentada para a função soma e usar uma variável
adicional para fornecer o sinal correto a cada ciclo. Segue esta versão:
Além disso, observe que a instrução foi substituída por ++;, onde o
operador ++ soma à variável indicada. De modo semelhante, para acumular
valores na variável foi utilizado o operador resultando na expressão
.
Esta série poderia ser reescrita para ficar explícita a ordem de cada termo, como
segue:
#include <stdio.h>
#include <math.h>
return s;
}
int main () {
printf ("%8.6lf\n", soma (2, 2));
printf ("%8.6lf\n", soma (2, 5));
printf ("%8.6lf\n", soma (1, 100));
return 0;
}
return s;
}
Uma nova variante para a implementação desta série é dada pela seguinte
transformação, para alguns termos:
( ( ( )))
return s;
}
#include <stdio.h>
#include <math.h>
return s;
}
int main () {
double Graus, Rad, Y1, Y2;
int i = 0;
while (i <= 24) {
Graus = i*15;
Rad = torad (Graus);
Y1 = sin (Rad);
Y2 = seno (Rad);
printf ("%4d %4.0lf %18.15lf %18.15lf %18.15lf\n",
A função inclui uma repetição para o cálculo de termos, onde o termo geral
tem a seguinte forma:
A menos da obtenção do sinal, a expressão que codifica cada termo tem exatamente
este formato. O sinal foi gerado a partir da variável que alterna entre e a
cada ciclo. Isto evita utilizar apenas para obter e .
O termo atual é o termo anterior multiplicado por e dividido pelo produto de dois
números, que também podem ser generalizados em função da posição do termo na
série. Assim, temos a seguinte relação de recorrência:
Onde:
return s;
}
viii. Refazer o programa que calcula o seno de um ângulo a fim de que não tenha
diferença com a função interna do :
( )
( ) ( )( )
( )√ ( )( )
#include <stdio.h>
#include <math.h>
return s;
}
return s;
};
int main () {
double Graus, Rad, Y1, Y2;
printf (" I X SIN(X) SENO(X)
ERRO\n");
int i = 0;
while (i <= 24) {
Graus = i*15;
Rad = torad (Graus);
Y1 = sin (Rad);
Y2 = seno (Rad);
printf ("%4d %4.0Lf %18.15Lf %18.15Lf %18.15Lf\n",
i, Graus, Y1, Y2, Y1-Y2);
i++;
}
return 0;
}
A lógica do módulo principal, que calcula o valor da série, foi alterada para utilizar o
com o critério de parada definido quando dois cálculos sucessivos não
acrescentam precisão alguma ao resultado.
i = 0;
while (i <= n) {
...
i++;
}
Onde,
#include <stdio.h>
return f;
};
int main () {
int k;
for (k = 0; k <= 30; k++)
printf ("%3d %d\n", k, fat(k));
return 0;
}
Porém há um problema crítico, que reside no fato que o fatorial de um número tem
um rápido crescimento dos valores, uma vez que há um acúmulo de multiplicação. A
função retorna um resultado do tipo , que tem de comprimento e
admite uma faixa de a . Este tamanho em bits para o
resultado limita ao valor , a partir da qual há um estouro de capacidade. A
solução é utilizar um tipo com faixa maior. Assim, a função foi testada para os tipos
e . As seguintes saídas foram obtidas para cada um destes
tipos:
#include <stdio.h>
return f;
};
int main () {
int k;
for (k = 0; k <= 30; k++)
printf ("%3d %.10e\n", k, fat(k));
return 0;
}
A listagem para o tipo também chama atenção para o fato de que a partir de
certo valor perde-se a precisão, pois a quantidade de dígitos significativos é bastante
limitada, mesmo que a faixa de representação admita números de alta magnitude.
Para o tipo escolhido neste exemplo a capacidade é de dígitos
significativos, e fica evidente na listagem o complemento feito com zeros a partir de
igual a .
ii. Elaborar um programa que leia vários números fornecidos via teclado e apresente ao
final a somatória destes valores.
#include <stdio.h>
int main () {
lerdados (10);
return 0;
}
#include <stdio.h>
int main () {
lerdados (10);
return 0;
}
O cálculo da média é feito dividindo a soma dos dados pela quantidade de dados
fornecidos. A primeira impressão poderia levar a uma solução utilizando o valor do ,
variável de controle do loop, acreditando que ela reterá a quantidade de termos.
Porém esta não é uma boa prática de programação, pois não há garantias para o
uso da variável de controle fora do . Ao encerrar a repetição, esta variável não
contém um valor confiável. Assim, a solução demanda que dentro do loop
resolvamos esta questão.
#include <stdio.h>
int main () {
lerdados (10);
return 0;
}
Deve ser agregada ao programa anterior a lógica para obter o maior entre os
números fornecidos. A estratégia consiste em inicializar uma variável com um valor
pequeno, e dentro do loop atualizar esta variável toda vez que um valor fornecido ( )
for superior ao atual maior. Segue a lógica proposta:
#include <stdio.h>
int main () {
lerdados (10);
return 0;
}
Para cada valor fornecido, verificar se o mesmo é maior que o maior atual, e
em caso positivo faça a substituição:
Esta lógica tem um problema quando os valores fornecidos forem todos negativos.
Neste cenário, o programa informará que o maior número é . Uma melhoria na
estratégia seria inicializar a variável com o primeiro número informado, pois
assim fica assegurada a consistência da lógica.
#include <stdio.h>
int main () {
lerdados (10);
return 0;
}
i. Elaborar um programa que leia um inteiro e apresente um dígito por linha, iniciando
pelo dígito menos significativo. Segue a lógica proposta:
#include <stdio.h>
int main () {
int x;
return 0;
}
12345
5
4
3
2
1
Entrada Saída
1 4321
2
3
4
-1
int main () {
int digit, x;
digit = x = 0;
do {
x = 10*x + digit;
scanf ("%d", &digit);
} while (digit != -1);
return 0;
}
6
5
4
3
2
1
-1
654321
Os tipos de dados vistos até o momento são de natureza escalar, ou seja, permitem
definir variáveis com capacidade para representar apenas um elemento. Uma
variável do tipo tem capacidade para armazenar apenas um número em
determinado instante. O conteúdo pode ser alterado, porém sempre será apenas um
valor por vez. Esta característica é inerente aos tipos escalares: , , ,
e .
Os exemplos citados poderiam ser submetidos a lógicas para obter: média de cada
aluno em uma disciplina ordenados de várias formas, livro de chamada, relação de
professores por curso, disciplina que mais reprova, regressão linear de grandezas
medidas, semana com a menor temperatura média, folha de pagamento, e etc.
X: V:
[ ]
int main () {
char s[100];
int vi[50];
float vf[200];
double vd[150];
long long int vl[1000];
unsigned int vu[300];
s[0] = 'a';
vi[3] = -1;
vf[0] = 1.25;
vd[10] = -3.18e300;
vl[2] = 0x0102030405060708;
vu[4] = -2;
}
int main () {
char s[100];
int vi[50];
int i;
vi[i - 'a'] = 0;
s[i % 2] = '\n';
s[vi[0]] = 'x';
int main () {
int v[10], i;
i. O usuário deverá informar uma sequência de inteiros. Estes valores devem ser
armazenados em um vetor. O programa apresentará os valores informados em
ordem invertida. O conteúdo do vetor não é alterado. O primeiro valor informado
indica a quantidade de elementos do vetor.
Entrada Saída
4
75
12
42
28
28
42
12
75
#include <stdio.h>
int main () {
int v[100], n;
return 0;
}
[ ]
[]
Esta função é responsável pela leitura dos dados de entrada, sendo fornecido
primeiramente a quantidade de elementos, e em seguida os valores do vetor. Assim,
deve alterar variáveis que estão declaradas em outra função. No tópico
sobre passagem de parâmetros foi explicado que ao defrontar com esta situação, a
forma de passagem de parâmetros deve ser por endereço ou referência. A função
que irá alterar os dados deve receber os endereços das variáveis a serem alteradas.
Observe que o segundo parâmetro foi declarado como sendo uma cadeira chamada
preparada para receber um valor que significa ou endereço de um inteiro.
O corpo da função é:
[]
Na linha seguinte a lógica codifica uma repetição onde a variável inicia com o
índice da primeira cadeira e cresce de em ( ) até alcançar o índice do último
[]
[]
É utilizada uma repetição onde a variável inicia com o índice do último elemento do
vetor e decresce de em ( ) até alcançar o índice , que
corresponde a primeira cadeira. Para cada valor de é apresentado na tela o valor
que está na cadeira [ ]: []
4
12
28
42
75
75
42
28
12
ii. Reescrever o programa que lê vários números fornecidos via teclado e apresente ao
final algumas estatísticas, tais como: somatória destes valores, média, maior valor, e
etc.
#include <stdio.h>
return s;
}
m = vx[0];
for (i = 1; i < nx; i++)
if (vx[i] > m)
m = vx[i];
return m;
}
int main () {
double v[100];
int n;
double soma, media, maior;
return 0;
}
[]
double somavet (double vx[], int nx) O primeiro parâmetro é um vetor e o segundo
{ parâmetro é um inteiro que indica a quantidade de
elementos do vetor.
int i; double s; Variáveis para o e para acumular a soma.
Zerar a soma e o contador de repetições
for (s = i = 0; i < nx; i++) Repetir enquanto
s += vx[i]; Acumula em o valor de [ ]
Incrementar o contador de repetições
double getmaior (double vx[], int nx) O primeiro parâmetro é um vetor e o segundo
{ parâmetro é um inteiro que indica a quantidade
de elementos do vetor.
int i; double m; Variáveis para o e para armazenar o maior
#include <stdio.h>
return s;
};
int main () {
double v[100];
int n;
double x, y;
return 0;
( ( ( )))
Problemas de busca são corriqueiros em grande parte dos problemas. Isto faz com
que se estude muito sobre métodos eficientes para este propósito. Neste exercício
faremos implementações básicas, ficando fora do escopo métodos mais elaborados.
Segue uma solução para a busca por um elemento em um vetor não ordenado:
#include <stdio.h>
int main () {
double v[100], x; int n, p;
return 0;
}
Serão apresentadas várias versões de uma busca linear, onde os elementos do vetor
são percorridos a fim de encontrar a primeira ocorrência do elemento . Caso este
elemento não seja encontrado a função retorna .
return -1;
}
return -1;
}
vx[nx] = x;
for (i = 0; vx[i] != x; i++)
;
return -1;
}
Esta função visa simplificar a expressão que controla o , fazendo com que reste
apenas um teste. Para que isto seja feito com segurança, observe que antes de
iniciar a repetição o elemento é inserido no final do vetor. Assim, há a certeza de
que pelo menos um elemento exista no vetor. Caso a busca indique que o
elemento encontrado está na posição , então significa que no vetor original o não
foi encontrado. Destaca-se também que a instrução controlada pelo é
simplemente o incremento de , cujo significado é simplesmente avançar a busca
para o próximo elemento. É importante certificar que a posição do vetor é área
válida de memória para o programa.
return NULL;
}
return 0;
}
#include <stdio.h>
m = p;
for (i = p+1; i <= q; i++)
if (vx[i] < vx[m])
m = i;
return m;
};
};
int main () {
double v[100]; int n;
return 0;
}
};
A lógica da função é:
A cada execução há uma permutação entre elementos de tal forma que o menor
valor encontrado em um domínio do vetor seja posicionado no início deste domínio.
Ao final desta sequência o vetor estará em ordem crescente. A forma geral desta
sequência é:
A abstração está novamente presente nesta rotina, pois trazer para a posição do
vetor o menor elemento entre e , foi codificado em duas linhas, sendo uma que
chama que retorna a posição do menor elemento de entre as posições
e . A segunda linha executa o módulo que os conteúdos das células e .
m = p;
for (i = p+1; i <= q; i++)
if (vx[i] < vx[m])
m = i;
return m;
Neste módulo a lógica utilizada assume que o primeiro elemento do intervalo dado é
a posição do menor elemento, e em seguida o vetor é percorrido do segundo
elemento do intervalo até o seu final, perguntando se o elemento é menor que o
menor atual. Em caso positivo, a posição do novo elemento assume como a posição
do menor. Ao final, este valor é retornado como resultado da função.
};
O primeiro passo é deslocar o maior valor de todo o vetor (da primeira a última
célula) para a última posição. O segundo passo é deslocar o maior elemento entre a
primeira e penúltima célula, para o final deste intervalo. Repetir este processo e
reduzindo o intervalo tratado até que restar apenas dois elementos. Ao final, o vetor
estará completamente ordenado. Formalizando estes passos, tem-se:
#include <stdio.h>
int main () {
char str[100];
char texto[100];
char dias[] = "DSTQQSS";
char vogais[] = {'a', 'e', 'i', 'o', 'u', '\0'};
char hexdigits[17] = "0123456789ABCDEF";
Caso o programador deseja atribuir toda a linha digitada para uma única string,
poderá então lançar mão da função . Assim, neste caso os dois podem
ser substituídos por e , como ilustra o seguinte resultado:
Quando o recurso foi apresentado, foi destacado que é usual manter uma
variável inteira cujo valor indica a quantidade de células do que deve ser
considerada naquele contexto da lógica. A em é um que dispensa a
presença desta variável, pois o flag é adotado para indicar a última célula válida
no presente contexto.
#include <stdio.h>
int main () {
char str[100];
gets (str);
printf ("tamanho = %d", length(str));
}
ola mundo
tamanho = 9
ii. Escrever um programa que copia todos os caracteres de uma string de origem para
uma string de destino.
#include <stdio.h>
dst[i] = '\0';
}
int main () {
char str[100], bak[100];
gets (str);
cpystr (bak, str);
printf ("backup = [%s]", bak);
#include <stdio.h>
return c;
int main () {
char str[100];
gets (str);
maiusculo (str);
printf ("%s", str);
return c;
}
iv. Escrever um programa que conta a quantidade de vogais contidas em uma string.
#include <stdio.h>
int main () {
char str[100];
gets (str);
printf ("%d", contavogal (str));
Vamos explorar uma segunda lógica a partir de uma função que recebe uma string e
um caractere, e retorna o índice da primeira posição do array que coincide com o
return -1;
}
aAxxeEixxoxU
7
#include <stdio.h>
return 1;
}
int main () {
char str[100];
return 1;
}
int main () {
char str[100];
arara
>> sim
aba
>> sim
abcdcba
>> sim
abcdcab
>> nao
fim
vi. Elaborar um programa que obtém a primeira palavra de uma . Considerar que
o texto dado contenha várias palavras separadas por um ou mais espaços em
branco. Incluir também a lógica que retorna uma cópia da sem a primeira
palavra.
#include <stdio.h>
dst[j] = '\0';
int main () {
char str[100], car[100], cdr[100];
gets (str);
strcar (car, str);
strcdr (cdr, str);
printf ("[%s]\n", car);
printf ("[%s]\n", cdr);
dst[j] = '\0';
}
O primeiro salta todos os caracteres branco ( ) no início da string e posiciona a
variável no primeiro caractere não branco. Esta repetição também é controlada para
que não extrapole o final da string: [] [] . A segunda repetição
salta todos os caracteres não branco e posiciona a variável no primeiro caractere
branco. Isto equivale a saltar a primeira palavra. A terceira repetição volta a saltar
brancos para posicionar a variável no início da segunda palavra. O último copia
toda a sequência de caracteres a partir da segunda palavra para a string .
#include <stdio.h>
int main () {
char str[100];
int x;
5
>> 101
255
>> 11111111
32
>> 100000
4000
>> 111110100000
19
>> 10011
-1
#include <stdio.h>
return i;
}
int main () {
char str[100];
int x;
viii. Elaborar um programa que converta um número inteiro decimal para uma
contendo o equivalente em uma base indicada.
#include <stdio.h>
return i;
}
int main () {
char str[100];
int x, b;
return i;
}
A variável serve como índice de acesso a cada posição da string a ser preenchida
com o símbolo correspondente ao dígito da base. O valor inicial de indica o índice
da primeira posição da string a ser ocupada pelo primeiro resto. A repetição é
controlada pelo valor de , quando este número alcançar o loop é encerrado.
A cada ciclo o valor de é substituído por . A expressão
fornece o valor numérico do dígito na indicada e ao mesmo tempo é o índice
para a string onde se encontra o símbolo correspondente. Uma segunda
versão poderia dispensar a variável , ficando apenas com .
0
s[0] '1' 1 1
s[1] '1' 1 3
s[2] '0' 0 6
s[3] '1' 1 13
s[4] '\0'
#include <stdio.h>
return -1;
}
return x;
}
int main () {
char str[100];
int x, b;
return x;
}
return -1;
}
return -1;
}
#include <stdio.h>
dst[++j] = 0;
}
int main () {
char str[100], txt[100];
dst[++j] = 0;
}
xi. Escrever um programa que conta a quantidade de vezes que cada letra do alfabeto
se repete em uma .
#include <stdio.h>
#include <ctype.h>
int main () {
char str[100];
int n = 'z' - 'a' + 1, v[n];
O primeiro serve para zerar cada uma das células que irão conter as
quantidades de cada letra contidas na .
int v[10];
int *ptr;
ptr = &v[0];
A primeira célula de possui agora duas formas de acesso ao seu conteúdo, uma
por meio de acesso indexado [ ] e outra por meio de acesso indireto . A
expressão é uma forma alternativa de se obter o valor do endereço da primeira
célula, dispensando a expressão [ ] . De forma semelhante, a expressão
também produz o mesmo resultado, ou seja o endereço da primeira célula de .
#include <stdio.h>
int main () {
int v[] = {30, 40, 50, 60, 70, 80, 90, 100, 110, 120};
int *ptr;
ptr = &v[0];
0061FE88
0061FE88
0061FE88
0061FE88
0061FE84
30
30
30
O exemplo também apresenta três vezes o valor , sendo uma como resultado de
[ ] uma segunda vez como resultado de e a terceira a partir de . Isto
confirma a possibilidade de se obter o conteúdo de uma célula de um vetor tanto
pela forma indexada quanto de forma indireta por meio de ponteiro.
Expressão Significado
atribuir um endereço
––
––
No caso dos ponteiros apontarem para outros tipos de dados, o significado das
operações serão adequadas, considerando .
#include <stdio.h>
int main () {
double v[10];
0061FE60
0061FE70
0061FE90
0061FE78
0061FE78
4
0061FE78
0061FE88
0061FE70
0061FE90
0061FE5C
0061FE58
00000000
0 1 2 3 4 5 6 7
0061FE60 v[0]
0061FE68 v[1]
0061FE5C p 0061FE70 0061FE70 v[2]
0061FE78 v[3]
0061FE80 v[4]
0061FE88 v[5]
0061FE58 q 0061FE90 0061FE90 v[6]
0061FE98 v[7]
0061FEA0 v[8]
0061FEA8 v[9]
#include <stdio.h>
scanf("%d", p);
return s;
int main () {
int va[1000], na;
return 0;
}
5
12
23
32
34
10
soma = 111
return s; return s;
} }
Uma segunda versão destas funções faz com que o parâmetro seja atualizado a
cada repetição a fim de referenciar a próxima célula. No início da função, o
parâmetro contém o endereço da primeira célula do vetor originalmente passado
como argumento da chamada da função. A expressão faz com que o valor do
parâmetro seja alterado para o endereço da próxima célula de inteiro.
#include <stdio.h>
return s;
int main () {
int va[1000], na;
return 0;
}
#include <stdio.h>
#include <ctype.h>
return p - str;
}
return p;
}
return p;
int main () {
char sa[100], sb[100];
gets (sa);
return p - str;
}
return p - str;
}
Em seguida é implementada a função que copia uma string para uma outra string,
aqui chamada de . Esta função recebe como parâmetros o endereço do
primeiro caractere da string de destino e o endereço do primeiro caractere da string
de origem. Após efetivar a cópia, a função retorna o endereço do primeiro
caractere da string de destino.
return p;
}
Seguem distintas formas de implementação da função que copia uma string para um
destino, iniciando com uma versão indexada e movendo para a versão apenas com
ponteiros.
void cpystr (char dst[], char src[]) { void cpystr (char dst[], char src[]) {
int i; int i;
for(i = 0; src[i]; i++) for(i = 0; dst[i] = src[i]; i++)
dst[i] = src[i]; ;
}
dst[i] = '\0';
}
char *strcpy (char *dst, char *src) {
char *p = dst;
return p;
}
Valor
Significado
Retornado
Em relação ao primeiro caractere que causou a
diferença: o de str1 tem um valor menor do que o de
str2:
Entrada Saída
abcde
ola mundo belo #a = 1
#b = 1
#c = 0
#d = 1
#e = 1
abcdabcdabc #a = 3
#b = 3
#c = 3
#d = 2
#e = 0
fim
#include <stdio.h>
#include <string.h>
int main () {
char ref[100];
char str[100];
int vc[100], nc;
Entrada Saída
dbcayx [abcdxy]
ola mundo belo [ abdellmnooou]
rua 19, casa 32. [ ,.1239aaacrsu]
1032437698AaBbCcZzWw [0123346789ABCWZabcwz]
fim
#include <stdio.h>
#include <string.h>
return s;
}
int main () {
char str[100];
return s;
}
s 1000 x
1001 y
1002 d
1003 a
1004 c
1005 \0
'x' > 'y' 'y' > 'd' 'y' > 'a' 'y' > 'c'
s 1000 x x x x
1001 y y|d d d
1002 d d|y y|a a
1003 a a a|y y|c
1004 c c c c|y
1005 \0 \0 \0 \0
O tipo utilizado para definir vetores também pode ser utilizado para definir
matrizes. Muitos problemas do mundo real podem ser modelados sob a forma de
matrizes tanto pela conveniência da representação quanto pela necessidade de
fazer uso dos recursos da álgebra matricial. A maioria das linguagens imperativas
clássicas não trazem operadores embutidos para processar matrizes, ficando ao
programador a incumbência de codificar as lógicas usando os recursos escalares da
linguagem e suas estruturas de controle. Algumas linguagens do paradigma
funcional, tal como , disponibilizam uma grande quantidade de operadores
matriciais. A seguinte figura ilustra os tipos , e :
m[i][j]
[99]
int m[100][50];
int main() {
int m[MAXL][MAXC];
//programa principal
}
#define EVER ;;
int main() {
for (EVER) {
// loop forever
}
//programa principal
}
23.5.1. Exemplos
i. Escrever um programa que preenche uma matriz bidimensional com dados lidos via
teclado e apresente estes dados na tela.
#include <stdio.h>
int main () {
int mat[MAXL][MAXC];
int nl, nc;
Uma matriz bidimensional é um array de array. Neste caso, a matriz foi definida
com linhas por colunas, resultando na alocação de células do tipo .
Esta é a capacidade máxima de armazenamento para esta matriz. No entanto, os
problemas podem utilizar uma região desta matriz, em um determinado contexto do
programa.
Esta figura ilustra o uso de uma porção de uma matriz definida como
. No caso de vetor, era comum vincular uma variável inteira indicando a
quantidade efetiva de células utilizada naquele contexto de execução. Para a matriz
de duas dimensões, serão necessárias duas variáveis inteiras a fim de manter o
controle sobre a área da matriz efetivamente em uso em um determinado momento.
Uma variável indica a quantidade de linhas e a outra indica a quantidade de colunas,
normalmente tendo como referência a linha e a coluna zero. Neste exercício, as
variáveis e desempenharão este papel.
Assim, para percorrer cada uma das células da matriz, quer seja para armazenar um
dado ou obter o dado armazenado, será demandado do programa, no caso de uma
lógica simples, a utilização de duas repetições aninhadas, cada uma controlando um
índice. A repetição mais interna processa mais rapidamente que a repetição
envolvente. Observe a função :
A chamada da
função a partir de toma a seguinte forma:
. O primeiro argumento é a referência a matriz a ser preenchida,
enquanto que os dois outros argumentos são os endereços das variáveis e ,
onde serão armazenados a dimensão da área da matriz efetivamente em uso.
M[i][j]
Após imprimir todos os elementos de uma linha da matriz, é iniciada uma nova linha
na tela, e o loop retorna para processar o próximo , apresentando uma linha da
matriz após a outra.
3 2
#[0][0] = 10
#[0][1] = 18
#[1][0] = 6
#[1][1] = 12
#[2][0] = 43
#[2][1] = 17
10 18
6 12
43 17
a) b)
| | | |
| | | |
c) d)
| | | |
| | | |
| |
| |
Os casos apresentados possuem lógicas que define o valor de uma célula de acordo
com os índices associados a ela. É importante buscar uma associação entre os
índices e o valor pretendido para a posição. Por exemplo, no
onde se pretende preencher uma matriz de acordo com o padrão dado pela matriz
identidade, a lógica que orienta a forma de preenchimento é:
Esta lógica deve ser aplicada a cada célula da matriz. Assim, a lógica para percorrer
cada elemento da célula requer duas repetição do tipo , sendo a mais externa
produzindo os índices para as linhas { } e a interna os índices para as
colunas { }. As combinações de e geradas serão semelhantes às
indicadas no exemplo anterior.
#include <stdio.h>
int main () {
int mat[MAXL][MAXC];
int nl, nc;
nl = 5; nc = 5;
fill_a (mat, nl, nc); printf ("\nMatriz a)\n"); show (mat, nl, nc);
fill_b (mat, nl, nc); printf ("\nMatriz b)\n"); show (mat, nl, nc);
fill_c (mat, nl, nc); printf ("\nMatriz c)\n"); show (mat, nl, nc);
fill_d (mat, nl, nc); printf ("\nMatriz d)\n"); show (mat, nl, nc);
}
M[i][i] = 1;
}
Matriz a)
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
Matriz b)
0 0 0 0 0
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
Matriz c)
0 0 0 0 1
0 0 0 1 0
0 0 1 0 0
0 1 0 0 0
1 0 0 0 0
Matriz d)
1 1 1 1 1
0 1 1 1 1
0 0 1 1 1
0 0 0 1 1
0 0 0 0 1
A soma dos elementos de cada linha deverá produzir um valor por linha, que
resultará em um vetor com o comprimento igual a quantidade de linhas da matriz.
| |
| |
[ ]
[ ]
| | | |
[ ]
| |
| [ ]|
[ ]
Onde significa a somatória de todos os elementos da linha . De forma geral
[] ∑ .
| |
| |
[ ] [ ] [ ] [ ] [ ]
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void posmaior (int M[][MAXC], int L, int C, int *LIN, int *COL) {
int i, j, lin = 0, col = 0;
for (i = 0; i < L; i++)
for (j = 0; j < C; j++)
if (M[i][j] > M[lin][col]) {
lin = i;
col = j;
}
*LIN = lin;
*COL = col;
}
return s;
}
void somalin (int M[][MAXC], int L, int C, int V[], int *N) {
int i;
*N = L;
for (i = 0; i < L; i++)
V[i] = somavet (M[i], C);
}
void somacol (int M[][MAXC], int L, int C, int V[], int *N) {
int i, j, s;
*N = C;
for (j = 0; j < C; j++) {
for (s = i = 0; i < L; i++)
s += M[i][j];
V[j] = s;
}
}
int main () {
int mat[MAXL][MAXC], vx[MAXN];
int nl, nc, nx, lin, col;
srand (time(NULL));
fill_random (mat, nl = 4, nc = 5);
printf ("Matriz randomica\n"); show (mat, nl, nc);
void somalin (int M[][MAXC], int L, int C, int V[], int *N) {
int i;
*N = L;
for (i = 0; i < L; i++)
V[i] = somavet (M[i], C);
}
return s;
}
void somacol (int M[][MAXC], int L, int C, int V[], int *N) {
int i, j, s;
*N = C;
for (j = 0; j < C; j++) {
for (s = i = 0; i < L; i++)
s += M[i][j];
V[j] = s;
}
}
void posmaior (int M[][MAXC], int L, int C, int *LIN, int *COL) {
int i, j, lin = 0, col = 0;
for (i = 0; i < L; i++)
for (j = 0; j < C; j++)
if (M[i][j] > M[lin][col]) {
lin = i;
col = j;
}
*LIN = lin;
*COL = col;
}
Matriz randomica
8 5 18 15 0
10 4 12 18 5
2 13 2 13 16
5 19 19 16 3
maior[3][1] = 19
| | | | | |
| | | | | |
Ou seja:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
return 0;
}
int main () {
int ma[MAXL][MAXC], la, ca;
int mb[MAXL][MAXC], lb, cb;
int mc[MAXL][MAXC], lc, cc;
srand (time(NULL));
fill_random (ma, la = 3, ca = 4);
printf ("Matriz A\n"); show (ma, la, ca);
matprod (mc, &lc, &cc, ma, la, ca, mb, lb, cb);
printf ("\nMatriz C\n"); show (mc, lc, cc);
return 0;
}
Matriz A
2 2 2 2
1 4 3 3
1 0 1 1
Matriz B
1 4
4 2
4 3
1 3
Matriz C
20 24
32 30
6 10
, onde:
[ ] [ ] [ ]
[ ]
[ ]
[ ]
[ ]
| |
( )
( )
#include <stdio.h>
#include <stdlib.h>
printf("\n");
}
}
int combina (float M[][MAXC], int L, int C, int Lx, int Ly) {
int j; float k;
k = -M[Lx][Lx] / M[Ly][Lx];
int backsubstitution (float M[][MAXC], int L, int C, float *V, int *N) {
int i,j;
*N = L;
V[i] /= M[i][i];
}
}
int combina (float M[][MAXC], int L, int C, int Lx, int Ly) {
int j; float k;
k = -M[Lx][Lx] / M[Ly][Lx];
*N = L;
V[i] /= M[i][i];
}
}
4 5
1 3 2 -4 -3
2 1 -3 1 -1
1 2 -5 3 2
-2 2 2 -1 4
#4 x 5
1.000 3.000 2.000 -4.000 -3.000
0.000 2.500 3.500 -4.500 -2.500
0.000 0.000 -14.000 13.000 10.000
0.000 0.000 0.000 -1.538 -6.154
#4
1.000
2.000
3.000
4.000
10 11
-4.70 -2.80 -3.00 -2.70 3.10 1.70 -3.20 -4.10 0.60 3.60 3.50
-2.00 3.40 -1.70 0.10 1.20 2.50 1.40 1.10 -1.20 3.30 -4.50
4.30 -2.10 -4.10 -4.70 0.60 -0.30 2.40 -1.20 -1.10 0.00 -3.10
-0.10 -1.20 -1.90 -3.50 1.60 0.20 1.90 -0.70 4.50 0.40 -3.40
-3.40 4.20 3.50 2.70 -3.30 3.50 -3.00 -4.20 1.00 0.70 3.30
2.90 0.40 -2.80 -3.90 3.80 5.00 -0.20 -2.50 -4.40 4.20 -3.60
-0.60 -1.00 -4.90 0.00 -3.10 -4.40 -4.10 4.70 0.20 4.00 -1.10
1.00 3.90 2.20 -4.00 3.20 -3.10 1.00 3.00 4.40 -2.80 -2.70
-4.50 5.00 0.00 4.80 3.10 -0.30 4.30 -4.60 1.00 1.70 0.70
-2.50 -4.90 3.20 1.00 -5.00 -2.70 -2.80 1.70 -1.70 -1.00 0.30
#10 x 11
-4.700 -2.800 -3.000 -2.700 3.100 1.700 -3.200 -4.100 0.600 3.600 3.500
0.000 -10.790 0.995 -2.935 0.280 -4.175 -6.490 -6.685 3.420 -4.155 14.075
0.000 -0.000 16.838 13.661 -7.673 -7.081 -5.269 4.775 4.695 -11.778 13.839
-0.000 -0.000 -0.000 -13.506 5.375 -1.832 17.750 5.588 40.479 -5.165 -29.199
-0.000 0.000 -0.000 -0.000 21.631 -17.780 33.625 49.585 35.364 -5.591 -53.287
-0.000 0.000 -0.000 -0.000 0.000 -52.240 67.240 74.358 87.105 -35.442 -79.702
-0.000 -0.000 0.000 0.000 0.000 -0.000 31.448 -17.035 -17.950 -24.857 10.516
0.000 -0.000 -0.000 0.000 -0.000 0.000 0.000 -36.629 -62.725 -21.276 58.877
0.000 -0.000 -0.000 0.000 -0.000 -0.000 0.000 -0.000 -69.450 -18.982 36.809
-0.000 0.000 -0.000 -0.000 0.000 0.000 0.000 0.000 0.000 -17.961 27.447
#10
-0.224
0.019
-0.833
0.767
0.477
0.050
-1.223
-0.527
-0.112
-1.528
#include <stdio.h>
#include <string.h>
}
void trocar (char *a, char *b) {
char t[MAXC];
strcpy (t, a);
strcpy (a, b);
strcpy (b, t);
}
A função verifica se uma string tem ordem superior a próxima string, que
em caso positivo a troca de conteúdo é efetuada. Executando esta lógica em uma
sequência de strings, a que tiver maior ordem léxica vai sendo deslocada para o final
do vetor. A comparação entre duas strings é feita utilizando a função .
Josue Ribeiro
Carlos Galhardo
Andrea Conti Santos
Maria dos Santos
Justino Roman
Mario Valdivino
Roberto Diniz
Adair Pereira
Leona Mattos
Adalto Cardoso
Antonio Barbara
Pedro Antunes
Afonso Vargas
Alexandre Teixeira
Romana Peres
^Z
Ordenados:
Adair Pereira
Adalto Cardoso
Afonso Vargas
Alexandre Teixeira
Andrea Conti Santos
Antonio Barbara
Carlos Galhardo
Josue Ribeiro
Justino Roman
Leona Mattos
Maria dos Santos
Mario Valdivino
Pedro Antunes
Roberto Diniz
Romana Peres
#include <stdio.h>
struct DADOS {
int a;
float x;
char c;
};
int main () {
struct DADOS r;
return 0;
}
struct DADOS {
int a;
float x;
char c;
};
A função faz uso deste modelo para criar e manipular uma variável:
int main () {
struct DADOS r;
return 0;
}
#include <stdio.h>
struct DADOS {
int a;
float x;
char c;
};
int main () {
TDADOS r;
return 0;
}
typedef struct {
int a;
float x;
char c;
} TDADOS;
Após a criação da variável , seguem instruções que carregam cada um dos seus
campos a partir de valores fornecidos via teclado, e apresenta estas informações na
tela. A instrução passa os endereços de cada
campo para as devidas manipulações.
x 32 -3.14
[x] [32] [-3.140000]
#include <stdio.h>
#define MAX 100
#define MAXC 200
typedef struct {
int n;
float v[MAX];
} TVETOR;
int main () {
TDADOS r;
return 0;
}
[]
[]
* -1 3.14
ola mundo belo
3
1.5
3.2
4.7
[*] [-1] [3.140000]
ola mundo belo
#0 = 1.500000
#1 = 3.200000
#2 = 4.700000
O programa anterior será alterado para que toda a saída de dados seja feita em uma
única função. A variável será passada como parâmetro para a função ,
que fará as impressões na tela. Seguem a função modificada e a função
:
int main () {
TDADOS r;
Uma nova versão de pode ser feita transferindo todo o código que carrega
valores para os campos de para uma função específica. Neste caso, é desejado
que uma outra função tenha permissão para alterar o conteúdo da . Para obter
esta permissão, é necessário que a função chamadora passe o endereço da variável
para a função chamada.
int main () {
TDADOS r;
getrec (&r);
showrec (r);
return 0;
}
[]
[]
[]
[]
Até o momento, vimos que uma função pode retornar um tipo escalar simples
como resultado, e não pode retornar uma cópia de todo um . No caso de
, quando necessário a função retorna o endereço do primeiro elemento como
resultado. Porém, é permitido que uma função retorne como resultado uma cópia de
uma com todos os seus campos. O programa exemplo foi modificado para
demonstrar este recurso:
TDADOS getrec () {
TDADOS temp;
scanf ("%c %d %f%*c", &temp.c, &temp.a, &temp.x);
scanf ("%[^\n]", temp.s);
lervet (temp.vet.v, &temp.vet.n);
return temp;
}
int main () {
TDADOS r;
r = getrec ();
showrec (r);
return 0;
}
24.5. Exercícios
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
#define MAXC 150
typedef struct {
char nome[MAXC];
char sexo;
float peso, altura, imc;
int main() {
TPESSOA vp[MAX];
int np;
return 0;
}
Nesta versão, usando a notação ponteiro, o parâmetro vai sendo atualizado para
apontar para o próximo agregado do vetor. A variável contadora de repetição é
utilizada para obter de forma simples a quantidade de dados fornecidos.
Agora, a associação entre a variável e o recurso arquivo passa a ser declarado pelo
programador. As funções de leitura do teclado e escrita na tela de vídeo são
substituídas por funções semelhantes que operam os fluxos com arquivos.
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
É suposto que o programador não deva fazer uso direto dos elementos desta
estrutura, ficando isto a cargo das funções de entrada/saída da linguagem.
Além da facilidade nos testes, problemas reais quase sempre lidam com situações
que determinam a necessidade de armazenar dados em meios permanentes. Neste
contexto, os dados que servem de entrada para algum programa poderiam ter sido
informados apenas uma vez e guardados em algum meio persistente, ou seja, com
capacidade de reter estas informações. O recurso de proporciona esta
facilidade, permitindo que dados sejam lidos e gravados em meios de
armazenamento permanentes.
"notas.txt"
4
Adair Pereira; 8.4 3.3 3.4 4.2
Adalto Cardoso; 9.9 4.2 6.8 9.1
Afonso Vargas; 5.1 5.6 6.5 6.2
Alexandre Teixeira; 8.8 8.9 6 7.2
Andrea Conti Santos; 4.1 2.5 8.9 2.3
Antonio Barbara; 4.4 9.4 7.4 7.6
Carlos Galhardo; 2.9 9 9.8 2.8
Josue Ribeiro; 4.8 9.9 6.7 4.9
Justino Roman; 3.6 9.8 6.3 2
Leona Mattos; 4.7 5.4 3.8 2.4
Maria dos Santos; 7.7 4.8 5.5 9.1
Mario Valdivino; 3.4 3.4 9.5 7.2
Pedro Antunes; 7.3 10 9.4 9.5
Roberto Diniz; 6.4 4.2 5.2 9.6
Romana Peres; 7.9 2.1 2.1 8.4
#include <stdio.h>
#include <string.h>
*L = i;
fclose (fp);
}
fclose (fp);
}
int main () {
char nomes[MAXL][MAXC]; int lin;
float notas[MAXL][MAXB];
float medias_al[MAXL];
float medias_bim[MAXB]; int col;
A seguinte figura ilustra as tabelas definidas para este problema, bem como as duas
variáveis de controle para as quantidades de linhas ( ) e quantidades de colunas
( ) efetivamente em uso no contexto da execução.
...
...
...
...
...
...
...
...
... ...
... ...
... ...
... ...
col
float medias_bim[MAXB]
...
col
*L = i;
fclose (fp);
}
ii. Chamada da função que liga a variável a um arquivo designado pelo seu
nome completo, seguido da descrição da operação desejada:
A função tem a seguinte definição:
iii. O primeiro dado a ser lido do arquivo é o número inteiro que indica a quantidade de
notas atribuídas a cada aluno. A sintaxe é bastante semelhante a um para
leitura do teclado. É necessário indicar que a leitura se dará a partir de um arquivo,
informando o ponteiro retornado pelo que no caso em questão foi
armazenado na variável :
vi. A próxima instrução, ainda embutida no realiza a leitura, nota a nota, do aluno ,
armazenando estes valores nas células do vetor [ ] . A função é
chamada para realizar estas leituras:
b) Após realizar os cálculos das médias dos alunos e das médias de cada bimestre,
todas as estruturas de dados estão devidamente preenchidas. O próximo passo é
preparar um relatório com estas informações. A função produz um relatório
em arquivo texto:
void show (char *fn, char NOMES[][MAXC], float NOTAS[][MAXB],
float MEDIAS_AL[], float MEDIAS_BIM[], int L, int C) {
int i;
FILE *fp;
fclose (fp);
}
ALUNO N1 N2 N3 N4 Med
123456789012345678901234512345123451234512345
ALUNO N1 N2 N3 N4 Med
123456789012345678901234512345123451234512345
ALUNO N1 N2 N3 N4 Med
iv. Na sequência da função tem-se o , que a cada repetição produz uma linha
do relatório com os dados do aluno. Cada linha contém o nome do aluno, seguido
das notas e na última coluna a média do aluno. A função é responsável por
imprimir o nome do aluno e suas notas, conforme a seguinte lógica:
123456789012345678901234512345123451234512345
ALUNO N1 N2 N3 N4 Med
123456789012345678901234512345123451234512345
ALUNO N1 N2 N3 N4 Med
vi. Após a impressão dos dados de todos os alunos, a função grava uma última
linha com as médias do bimestre. A função é também utilizada para este
propósito, porém com os argumentos alterados para produzir o texto
seguido dos valores do vetor .
ALUNO N1 N2 N3 N4 Med
#include <stdio.h>
fclose (fsrc);
fclose (fdst);
return 0;
}
int main () {
char fnsrc[255], fndst[255];
int code;
printf ("Entre com o nome do arquivo de entrada:\n");
gets (fnsrc);
printf ("Entre com o nome do arquivo de saída:\n");
gets (fndst);
if (code = ascii (fnsrc, fndst,8))
printf ("Erro: %d\n", code);
Lower: abcd..z
Upper: ABCD..Z
Digits: 012..9
TAB < >
<<FIM>>
L 4C | o 6F | w 77 | e 65 | r 72 | : 3A | 20 | a 61 |
b 62 | c 63 | d 64 | . 2E | . 2E | z 7A | * 0A | U 55 |
p 70 | p 70 | e 65 | r 72 | : 3A | 20 | A 41 | B 42 |
C 43 | D 44 | . 2E | . 2E | Z 5A | * 0A | D 44 | i 69 |
g 67 | i 69 | t 74 | s 73 | : 3A | 20 | 0 30 | 1 31 |
2 32 | . 2E | . 2E | 9 39 | * 0A | T 54 | A 41 | B 42 |
20 | < 3C | * 09 | > 3E | * 0A | < 3C | < 3C | F 46 |
I 49 | M 4D | > 3E | > 3E |
Para cada caractere lido do arquivo de entrada foi gerada uma informação no
arquivo de saída com o seu símbolo e valor numérico. Os caracteres de controle,
cujos valores estão entre e foram representados pelo símbolo . As linhas
contém informações de oito caracteres, conforme o valor passado para o argumento
da função . A interpretação do arquivo de saída permite observamos
alguns detalhes da organização de um arquivo texto. Podemos constatar que os
caracteres são armazenados como bytes, cujo valor numérico é o convencionado
pela tabela . Por exemplo, o caractere é armazenado como byte de valor
em hexa, que é em decimal. Os caracteres representados pelo símbolo são os
caracteres de controle.
18
FC
FF
FF
#include <stdio.h>
*N = i;
}
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
int main () {
int v[100], n;
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
ptr
size
blocks
...
#include <stdio.h>
#define MAX 100
fclose (fp);
return 0;
}
int main () {
int v[MAX], n;
fclose (fp);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX 100
typedef struct {
int t;
float a;
char c;
} TDADO;
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
DADOS ORIGINAIS
0 1.000 a
1 2.718 b
2 7.389 c
3 20.086 d
4 54.598 e
#include <stdio.h>
#include <sys/stat.h>
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fclose (fp);
return size;
}
int main () {
char fn[255];
printf ("Entre com o nome do arquivo\n");
gets (fn);
printf ("filesize(\"%s\") = %d\n", fn, filesize (fn));
printf ("filesize(\"%s\") = %d\n", fn, sizeoffile (fn));
}
#include <stdio.h>
#include <string.h>
return -1;
}
int main () {
TDIA d; char s[10];
d = QUA;
printf ("%s\n", diatostr(s, d));
d++;
printf ("%s\n", diatostr(s, d));
d-=2;
printf ("%s\n", diatostr(s, d));
d = strtodia (strcpy(s, "QAU"));
printf ("#%s = %d\n", s, d);
}
b. Criar a variável que serve de base para a conversão de uma variável do tipo
para um texto :
return -1;
}
int main () {
TDIA d; char s[10];
d = QUA;
printf ("%s\n", diatostr(s, d));
d++;
printf ("%s\n", diatostr(s, d));
d-=2;
printf ("%s\n", diatostr(s, d));
d = strtodia (strcpy(s, "QAU"));
printf ("#%s = %d\n", s, d);
}
QUA
QUI
TER
#QAU = -1
Caso o programador queira vincular valores inteiros distintos dos valores padrões
para os indentificadores do enumerado, isto pode ser feito da seguinte
forma:
Observar que os símbolos dos operadores binários diferem dos símbolos dos
operadores lógicos. Abaixo das tabelas estão indicadas as formas de uso dos
operadores , , e , bem como os símbolos utilizados O seguinte
quadro ilustra uma expressão com o operador e o efeito nos de um inteiro de
8 bits:
Uma análise na tabela verdade destaca que o operador é adequado para zerar
( ) determinados bits de uma palavra. O operador é aplicado nos casos em
que se deseja ligar ( ) bits de uma palavra. O operador é mais indicado
quando é necessário inverter ( ) determinados , enquanto que o operador
é útil para inverter todos os bits de uma representação.
27.3. Exemplos
̅̅̅
11
http://bits.stephan-brumme.com/
#include <stdio.h>
#include <string.h>
int main () {
char str[100];
int x = 0;
printf ("%s\n", tobin (str, x, 32));
return i;
}
void tobin (char s[], int num) { char *tobin (char *dst, int x, int n) {
int i, n; unsigned y = x;
dst[n] = '\0';
n = numdigit (num, 2); while (n) {
s[n] = '\0'; dst[--n] = '0' + (y & 1);
y >>= 1;
for (i = n-1; num; i--, num /= 2) }
s[i] = '0' + num%2; return dst;
} }
A primeira diferença que se observa é que esta versão não utiliza os operadores e
para obter os dígitos binários. A lógica empregada isola o menos
significativo através da expressão , onde é uma versão sem sinal de . A
constante deve ser vista na forma representada em binário com tamanho estendido
O quadro que segue ilustra o passo a passo das principais transformações feitas
com os bits bem como as combinações necessárias para produzir o resultado
00000000000000000000000000000000 x = 0;
10000000000000000000000000101000 set bits 3, 5 e 31
10000000000000000000000000001000 clear bit 5
10000000000000000000000001101000 change bits 5 e 6 para 1
00000000000000000000000001101000 flip bit 31
10000000000000000000000001101000 flip bit 31
00000000000000000000110100010000 rotate to the left 5 bits
10000000000000000000000001101000 rotate to the right 5 bits
int main () {
char *str; (1)
str = NULL; (2)
Neste caso, foi feita a leitura de uma string a partir do teclado e apresentado o seu
valor na tela. A partir do momento em que a área de memória não é mais
necessária, o programador pode explicitamente solicitar ao a liberação do bloco
alocado: . Segue a sintaxe desta função:
28.3. Exemplos
int main () {
char m[1000][100]; int n;
load ("dados.txt", m, &n);
...
}
Mesmo a variável sendo local a , o seu tempo vida coincide com o tempo de
vida do programa, uma vez que a função principal permanece na memória do início
ao fim do programa.
m[997]
m[998]
m[999]
n 3
HEAP
m[0] a \0
m[1]
m[2] b \0
m[3]
m[4] c \0
m[997]
m[998]
m[999]
n 3
int main () {
char *m[1000]; int n;
load ("dados.txt", m, &n);
...
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 1000
#define MAXC 100
fclose (fp);
return 0;
}
int main () {
char *m[MAXL];
int n;
return 0;
}
m m[0] a \0
m[1]
n 3 m[2] b \0
c \0
int main () {
char **m; int n;
load ("dados.txt", m, &n);
…
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 1000
#define MAXC 100
fclose (fp);
return 0;
}
int main () {
char **m;
int n;
return 0;
}
dados.txt
as palavras se repetem e repetem com muita frequencia
e quando se 12repetem &*¨* com muita frequencia, estas palavras
devem ter a 9987 frequencia contada. Assim, as -=33\* palavras devem
apenas conter caracteres de a a z ou de A a Z.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXC 50
#define MAXL 1000
#define MAXW 100
typedef struct {
char *s;
int c;
} TDADO;
*N = i;
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
fclose (fp);
return 0;
}
};
};
*N = j;
}
free (vs);
free (vw);
return 0;
*N = i;
fclose (fp);
return 0;
}
Após a abertura do arquivo, é feita uma leitura para descartar eventual sequência de
caracteres não alfabéticos contida no início do arquivo, e posicionar o indicador de
leitura na primeira palavra: [ – – ] Em seguida há uma
repetição que obtém uma sequência de caracteres alfabéticos e descarta uma
sequência de caracteres não alfabéticos:
[ – – ] [ – – ] A string obtida é armazenada de forma
provisória na variável local . No interior do , para cada palavra, é feita uma
alocação de memória com o tamanho exato da string lida e o endereço da string é
guardado no vetor indicado: [ ] A palavra lida é
copiada para a área alocada: [] A repetição é encerrada ao ser
alcançado o final de arquivo.
};
typedef struct {
char *s;
int c;
} TDADO;
*N = j;
}
};
Os três primeiros parâmetros são triviais. O quarto parâmetro requer que seja
codificada em separado uma função de comparação, cujo cabeçalho deverá ter o
seguinte formato:
};
};
};
#include <stdio.h>
#include <stdlib.h>
struct NODO {
float dado;
TNODO *next;
};
int main () {
TNODO *Head;
Head = NULL;
}
PASSO 0
O próximo passo é inserir o primeiro nodo na lista. Será utilizada uma variável
ponteiro , do tipo para auxiliar no processo de montagem da lista:
Para fixar o conceito, seguem os passos para inserir o segundo nodo na lista:
fclose (fp);
return H;
}
Para cada nodo que se deseja inserir na lista, é feita a alocação de memória para
uma , o campo recebe o valor de , o campo passa a
apontar para o primeiro nodo e por fim o endereço do nodo recém criado é retornado
para a função chamadora, que o fará como o novo cabeça da lista.
Para percorrer uma lista encadeada e utilizar o dado armazenado no nodo também é
utilizada uma variável ponteiro auxiliar Inicialmente aponta para onde aponta o
cabeça da lista ( ). Em seguida é iniciada uma repetição que a cada ciclo
faz com que avance e aponte para o seu próximo elemento ( ). A
repetição é executada enquanto o valor for diferente de , indicando assim
que alcançou o final da lista. A seguinte figura ilustra uma lista encadeada e
destaca a variável se deslocando pelos nodos a cada ciclo do .
return c;
}
Segue o programa proposto com várias operações com uma lista encadeada.
#include <stdio.h>
#include <stdlib.h>
struct NODO {
float dado;
TNODO *next;
};
fclose (fp);
return H;
}
return c;
}
if (T == NULL) return H;
if (P == NULL) return delfirst (H);
P->next = T->next;
free (T);
return H;
}
int main () {
TNODO *Head;
A função já foi explicada e serve para criar a lista encadeada invertida a partir
dos dados contidos em um arquivo texto. A função apresenta os dados
contidos nos nodos, permitindo que seja verificada a correção da lógica de
montagem. O módulo retira da lista e libera a memória alocada do nodo cujo
dado contenha um valor procurado. A função desaloca todos os nodos da lista.
if (T == NULL) return H;
if (P == NULL) return delfirst (H);
P->next = T->next;
free (T);
return H;
}
O ponteiro apontará para o nodo que se deseja eliminar da lista, enquanto que o
ponteiro apontará para o nodo anterior ao nodo . A seguinte figura ilustra a
configuração desejada para e após a busca pelo nodo a ser eliminado:
P
3.14 76.4 98.23 35.45
H
if (T == NULL) return H;
Neste caso, o nodo a ser eliminado é retirado da lista, fazendo com que o próximo
do nodo anterior aponte para o próximo do nodo a ser eliminado. Em seguida, o
nodo pode ser liberado, conforme ilustrado na figura a seguir:
P->next = T->next;
free (T);
T
76.4
P
3.14 98.23 35.45
H
P->next = T->next
tamanho = 6
====
3.14
76.40
98.23
41.42
17.80
35.45
====
-45.50
3.14
76.40
98.23
41.42
17.80
35.45
====
3.14
76.40
98.23
41.42
tamanho = 0
fclose (fp);
*pH = H;
return H;
}
$ pwd
/home/arquivos
$ ls –l *.txt
drwxr--r-- 1 arquivos editors 4096 dados.txt
-rw-r--r-- 1 arquivos editors 30405 pessoas.txt
-r-xr-xr-x 1 arquivos fred 8460 notas.txt
[]
#include <stdio.h>
return 0;
}
29.1. Exemplos
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
fclose (fp);
return 1;
}
fclose (fp);
free (buffer);
return 0;
int getparam (int argc, char *argv[], char *fn, int *sizep) {
if (fn[0])
if (existfile (fn))
split (fn, size);
else printf ("arquivo [%s] não encontrado\n\
forma de uso: split -f FNAME -s SIZE\n", fn);
else printf ("forma de uso: split -f FNAME -s SIZE\n");
} else printf ("forma de uso: split -f FNAME -s SIZE\n");
return 0;
}
C:\...\split>dir
Pasta de C:\...\split
Este programa é um bom caso de estudo, pois utiliza vários recursos da linguagem
, tais como: enumerado, arquivo binário, parâmetros da linha de comando, bem
como algumas técnicas de programação. Fica a cargo de leitor aprofundar a
compreensão da lógica.
fclose (fp);
free (buffer);
return 0;
ii. Elaborar um programa que agrupa vários arquivos binários em um único arquivo,
seguindo as definições do exemplo anterior.
fclose (fp);
return 0;
}
if (argc > 1)
if (savefile (argv[1]))
join (argv[1]);
else printf ("erro rename = [%s]\n", argv[1]);
else printf ("falta argumento com filename\n");
return 0;
}
C:\...\split>join info.dat
C:\...\split>dir
Pasta de C:\...\split
O usuário deve informar apenas o nome do arquivo a ser montado. O programa irá
buscar por todos os arquivos que tenham o mesmo nome acrescido de um
sequencial. A partir do sequencial os arquivos serão copiados um a um para formar
um único arquivo. Caso haja um arquivo com o mesmo nome do arquivo que se
deseja montar, o programa salva um cópia com a extensão . Havendo um
feito, então cria um novo arquivo com mais uma extensão .
30.1. Conceito
{ }
#include <stdio.h>
typedef long long int64;
return 0;
}
Fatorial (5)
fat (0) = 1
fat (1) = 1
fat (2) = 2
fat (3) = 6
fat (4) = 24
fat (5) = 120
fat (6) = 720
fat (7) = 5040
fat (8) = 40320
fat (9) = 362880
...
fat (19) = 121645100408832000
fat (21) = -4249290049419214848
fat (22) = -1250660718674968576
fat (23) = 8128291617894825984
fat (24) = -7835185981329244160
fat (25) = 7034535277573963776
A busca recursiva por um em um vetor não ordenado pode ser definido como:
{ [] }
[]
{ [ ] }
[ ]
#include <stdio.h>
x = 25;
printf ("#%d = %d\n", x, buscadir(v, 0, n-1, x));
x = 55;
printf ("#%d = %d\n", x, buscadir(v, 0, n-1, x));
#25 = 2
#55 = -1
#56 = 5
O valor foi encontrado na posição , o valor não foi encontrado e valor foi
encontrado na posição .
{ }
Program ExFIBO;
#include <stdio.h>
#include <stdio.h>
return 0;
}
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
...
fib(28) = 317811
fib(29) = 514229
fib(30) = 832040
A soma dos elementos de um vetor pode ser descrita de forma recursiva. O passo
primitivo é para o caso do vetor não possuir elemento algum, resultando em soma
igual a zero. O passo geral estabelece que a soma dos elementos do vetor é igual
à adição do primeiro elemento com a soma dos demais elementos.
{ [ ] }
[ ]
#include <stdio.h>
return 0;
}
soma = 428
#38 = 3 vezes
Acessado em 03-02-2014.
VII. Intel Corporation. Intel® 64 and IA-32 Architectures Software Developer’s
Manual. Volume 1: Basic Architecture. Order Number: 253665-043US. May
2012.
VIII. Lancharro, Eduardo Alcalde. Informatica Basica. MAKRON BOOKS ISBN:
0074605100.
IX. Meirelles, Fernando S. Informática: Novas aplicações com
microcomputadores. Makron Books.
X. Barroso, L. C. et al., Cálculo Numérico com Aplicações, 2 ed. Ed.
HARBRA, São Paulo, 1987. ISBN:8529400895.
XI. Wirth, Niklaus. Algoritmos e estruturas de dados. Rio de Janeiro: LTC -
Livros Técnicos e Científicos, 1989. 255p. ISBN 8521611900.
XII. Wirth, Niklaus. Programação Sistemática. Editora Campus. 1978.
XIII. Ralston, Anthony; Neill, Hugh. Algorithms (Teach Yourself). Editora
McGraw-Hill. 1997. 194p.
A B
ábaco, 10, 12, 28
base
ábaco chinês, 10
binário, 32
Ada, 14
conceito, 30
Álgebra de Boole, 14, 15, 47
conversão, 33
Al-Kharazmi, 28
decimal, 30
Alocacação Dinâmica
dígitos, 33
free, 352
hexadecimal, 31
Heap, 351
qualquer, 33
Lista Encadeada, 368
símbolos, 33
malloc, 351
Blaise Pascal, 12
realloc, 353
Anticítera, 10, 11
aritmética binária, 14 Ch
Arquivos
Arquivo Texto, 318 Charles Babbage, 14
Arquivos Binários, 316
fclose, 316 C
feof, 323
fgetc, 328 circuito integrado, 20
fgets, 356 Colossus, 15
FILE *, 316
fopen, 316, 322
fputs, 356
D
fread, 334 decimal, 70, 245, 248
fscanf, 316, 324 Decimal Codificado em Binário, 15
fscanf, 323
fseek, 336
ftell, 337
E
fwrite, 332 EDVAC, 16
modo de acesso, 322 ENIAC, 16
stat, 337 Enigma, 15
stdin, 314 Entrada de Dados
stdout, 314 scanf, 122
struct FILE, 317 Enumerado
ASCII conceito, 338
\\, 98 identificador, 338
\0, 98 Expressões Numéricas, 104
\n, 98
\r, 98
\t, 98 F
Assembly, 19, 20 Fibonacci, 28
atoi, 253