mirror of
https://gitlab.com/blau_araujo/cblc.git
synced 2025-05-10 02:26:36 -03:00
Compare commits
5 commits
95ea0a5bd5
...
efe8315b14
Author | SHA1 | Date | |
---|---|---|---|
efe8315b14 | |||
2bbee7c9ff | |||
be7c0286c0 | |||
96c9082189 | |||
699cf4bd1c |
3 changed files with 555 additions and 1 deletions
|
@ -32,7 +32,7 @@ qualquer distribuição.
|
|||
- [[./aulas/08-processos/README.org][Aula 8: Processos e layout de memória]] ([[https://youtu.be/60bXYVCFoTI][vídeo]]) (sem exercícios)
|
||||
- [[./aulas/09-args/README.org][Aula 9: Argumentos e ambiente]] ([[https://youtu.be/uufnW60rg2Q][vídeo]]) ([[./exercicios/09/README.org][exercícios]])
|
||||
- [[./aulas/10-dataio][Aula 10: Entrada e saída de dados]] ([[https://youtu.be/b6cbnZlY328][vídeo]]) (sem exercícios)
|
||||
- [[./aulas/#][Aula 11: Leitura da entrada padrão com a função 'scanf']] ([[https://youtu.be/MZiI95b2gdY][vídeo]]) ([[./exercicios/11/README.org][exercícios]])
|
||||
- [[./aulas/11-scanf][Aula 11: Leitura da entrada padrão com a função 'scanf']] ([[https://youtu.be/MZiI95b2gdY][vídeo]]) ([[./exercicios/11/README.org][exercícios]])
|
||||
- [[./aulas/#][Aula 12: Leitura da entrada padrão com a função 'fgets']] ([[https://youtu.be/ZZr9HBPo0Oc][vídeo]]) ([[./exercicios/12/README.org][exercícios]])
|
||||
- [[./aulas/#][Aula 13: Leitura da entrada padrão com chamadas de sistema]] ([[https://youtu.be/bW3Xox6LP_U][vídeo]]) ([[./exercicios/13/README.org][exercícios]])
|
||||
- [[./aulas/#][Aula 14: Abertura de arquivos para leitura]] ([[https://youtu.be/uh3UdYyzXRM][vídeo]]) ([[./exercicios/14/README.org][exercícios]])
|
||||
|
|
483
aulas/11-scanf/README.org
Normal file
483
aulas/11-scanf/README.org
Normal file
|
@ -0,0 +1,483 @@
|
|||
#+title: Curso Básico da Linguagem C
|
||||
#+subtitle: Aula 11: Leitura da entrada padrão com 'scanf'
|
||||
#+author: Blau Araujo
|
||||
#+startup: show2levels
|
||||
#+options: toc:3
|
||||
|
||||
* Aula 11: Leitura da entrada padrão com 'scanf'
|
||||
|
||||
[[https://youtu.be/MZiI95b2gdY][Vídeo desta aula]]
|
||||
|
||||
A entrada padrão (/stream/ =stdin= ou descritor de arquivos =0=) pode estar ligada...
|
||||
|
||||
- A um terminal para leitura interativa (digitação);
|
||||
- A um arquivo via redirecionamento de leitura;
|
||||
- À saída de um processo via pipe ou arquivo FIFO;
|
||||
- A um arquivo de forma programática (ex: funções =freopen=, =dup2=).
|
||||
|
||||
Para verificar se o fluxo =stdin= está ligado a um terminal, nós podemos
|
||||
utilizar a função =isatty= (=unistd.h=):
|
||||
|
||||
#+begin_src c
|
||||
int isatty(int fd);
|
||||
#+end_src
|
||||
|
||||
Onde =int fd= é o descritor de arquivos testado. O retorno será o inteiro =1=,
|
||||
se =fd= estiver ligado a um terminal, ou =0=, se estiver ligado a um pipe ou
|
||||
redirecionado para um arquivo.
|
||||
|
||||
Exemplo (=test-tty.c=):
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(void) {
|
||||
if (isatty(0)) {
|
||||
puts("STDIN ligada ao terminal.");
|
||||
} else {
|
||||
puts("STDIN ligada a um arquivo ou pipe.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Compilação e testes:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -Wall test-tty.c
|
||||
:~$ ./a.out
|
||||
STDIN ligada ao terminal.
|
||||
:~$ ./a.out < /dev/null
|
||||
STDIN ligada a um arquivo ou pipe.
|
||||
:~$ : | ./a.out
|
||||
STDIN ligada a um arquivo ou pipe.
|
||||
#+end_example
|
||||
|
||||
#+begin_quote
|
||||
O comando interno do shell =:= é o comando nulo, que não faz nada e sempre termina
|
||||
com sucesso.
|
||||
#+end_quote
|
||||
|
||||
Para associar programaticamente a entrada padrão a um arquivo (de nome recebido
|
||||
como argumento, por exemplo), nós podemos utilizar a função =freopen= para abrir
|
||||
um arquivo para leitura através de um fluxo (/stream/) previamente aberto: no caso
|
||||
o stream =stdin=...
|
||||
|
||||
#+begin_src c
|
||||
FILE *freopen(const char *caminho, const char *modo, FILE *stream);
|
||||
#+end_src
|
||||
|
||||
Onde:
|
||||
|
||||
- =caminho=: string do nome do arquivo;
|
||||
- =modo=: string de modo de abertura (="r"=, ="w"=, ="rw"=);
|
||||
- =stream=: um fluxo de dados previamente aberto.
|
||||
|
||||
Retorna um ponteiro para o tipo =FILE= ou =NULL=, em caso de erro.
|
||||
|
||||
#+begin_quote
|
||||
O propósito original desta função é alterar o arquivo associado a um dos
|
||||
fluxos padrão (=stdin=, =stdout= ou =stderr=).
|
||||
#+end_quote
|
||||
|
||||
Exemplo (=redir-stdin.c=):
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
// Diretório 'fd' do processo de 'a.out'...
|
||||
puts("Antes do redirecionamento...");
|
||||
system("ls -l /proc/$(pidof a.out)/fd");
|
||||
|
||||
// Nome do arquivo...
|
||||
char *filename;
|
||||
if (argc > 1) {
|
||||
// Primeiro argumento...
|
||||
filename = argv[1];
|
||||
} else {
|
||||
// Dispositivo nulo...
|
||||
filename = "/dev/null";
|
||||
}
|
||||
|
||||
// Redirecionamento...
|
||||
FILE* stream = freopen(filename, "r", stdin);
|
||||
if (!stream) {
|
||||
perror("freopen");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Diretório 'fd' do processo de 'a.out'...
|
||||
puts("Depois do redirecionamento...");
|
||||
system("ls -l /proc/$(pidof a.out)/fd");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Compilação...
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -Wall redir-stdin.c
|
||||
#+end_example
|
||||
|
||||
Executando sem argumentos (redireciona para =/dev/null=):
|
||||
|
||||
#+begin_example
|
||||
:~$ ./a.out
|
||||
Antes do redirecionamento...
|
||||
total 0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 0 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 1 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 2 -> /dev/pts/0
|
||||
Depois do redirecionamento...
|
||||
total 0
|
||||
lr-x------ 1 blau blau 64 abr 24 09:16 0 -> /dev/null
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 1 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 2 -> /dev/pts/0
|
||||
#+end_example
|
||||
|
||||
Executando com argumentos (nome do arquivo no primeiro argumento):
|
||||
|
||||
#+begin_example
|
||||
:~$ ./a.out /etc/shells
|
||||
Antes do redirecionamento...
|
||||
total 0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 0 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 1 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 2 -> /dev/pts/0
|
||||
Depois do redirecionamento...
|
||||
total 0
|
||||
lr-x------ 1 blau blau 64 abr 24 09:16 0 -> /etc/shells
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 1 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:16 2 -> /dev/pts/0
|
||||
#+end_example
|
||||
|
||||
No caso de um erro de abertura (ex: arquivo "banana" não existe):
|
||||
|
||||
#+begin_example
|
||||
:~$ ./a.out banana
|
||||
Antes do redirecionamento...
|
||||
total 0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:21 0 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:21 1 -> /dev/pts/0
|
||||
lrwx------ 1 blau blau 64 abr 24 09:21 2 -> /dev/pts/0
|
||||
freopen: No such file or directory
|
||||
#+end_example
|
||||
|
||||
** Leitura interativa da entrada padrão
|
||||
|
||||
Seja o conteúdo de um arquivo ou uma entrada digitada no terminal, a leitura
|
||||
da entrada padrão pode ser feita com várias formas e com diversas funções da
|
||||
biblioteca padrão:
|
||||
|
||||
- Leitura de caracteres: =getchar=, =getc(stdin)= e =fgetc(stdin)=;
|
||||
- Leitura de linhas (até =\n=): =fgets(buf, size, stdin)=, =getline(&buf, &size, stdin)=;
|
||||
- Leitura formatada de linhas: =scanf=, =fscanf(stdin, ...)=;
|
||||
|
||||
Mas, como a leitura das linhas de um arquivo é mais controlada (o conteúdo
|
||||
é previamente conhecido), nós vamos concentrar nossa atenção na leitura de
|
||||
linhas digitadas no terminal por um usuário (leitura interativa). Para isso,
|
||||
nós estudaremos três funções (nesta e nas próximas aulas):
|
||||
|
||||
- Leitura formatada de linhas: =scanf=;
|
||||
- Leitura de linhas: =fgets=;
|
||||
- Leitura de caracteres: chamada de sistema =read=.
|
||||
|
||||
** Leitura formatada de linhas com 'scanf'
|
||||
|
||||
A função =scanf= lê os bytes de uma linha enquanto encontra casamentos com os
|
||||
especificadores definidos em uma string de formato (como no =printf=). A parte
|
||||
da linha que encontrar casamento é passada para um ou mais endereços de
|
||||
variáveis (ou endereços em ponteiros) e tudo que vier depois permanece no
|
||||
buffer de entrada do processo.
|
||||
|
||||
Outros pontos importantes:
|
||||
|
||||
- Cada especificador de formato é casado com apenas uma palavra da linha
|
||||
lida (palavras são cadeias de caracteres delimitadas por espaços,
|
||||
tabulações e quebras de linha).
|
||||
- Retorna o número de casamentos com as especificações de formatos que
|
||||
foram encontrados e atribuídos (o que pode ser menor do que a quantidade
|
||||
de especificadores fornecidos).
|
||||
- Em caso de erro, retorna zero ou =EOF= (com vários significados possíveis).
|
||||
- Altera a variável =errno= (=errno.h=), o que possibilita obter descrições de
|
||||
erros, por exemplo, com as funções =perror= (=stdio.h=) ou =strerror= (=string.h=).
|
||||
- No caso da formatação como string, o caractere nulo (='\0'=) é inserido
|
||||
após o último byte do casamento encontrado.
|
||||
- Tudo que delimita a quantidade de bytes lidos é a forma como a string
|
||||
de formato é construída.
|
||||
- Uma string de formato mal construída pode levar a vulnerabilidades de
|
||||
memória.
|
||||
|
||||
A função =scanf= é considerada insegura por muitos programadores, mas a
|
||||
verdade é que ela é reconhecidamente difícil de ser utilizada corretamente.
|
||||
|
||||
De =man 3 scanf=:
|
||||
|
||||
#+begin_quote
|
||||
It is very difficult to use these functions correctly, and it is preferable
|
||||
to read entire lines with fgets(3) or getline(3) and parse them later with
|
||||
sscanf(3) or more specialized functions such as strtol(3).
|
||||
#+end_quote
|
||||
|
||||
Ou seja, o próprio manual diz que é muito difícil utilizar corretamente
|
||||
as funções da família do =scanf= e pode ser mais fácil ler linhas inteiras
|
||||
com funções como =fgets= e =getline= e fazer os processamentos necessários
|
||||
com a função =sscanf= ou outras funções mais especializadas em conversões
|
||||
de cadeias de caracteres em valores numéricos.
|
||||
|
||||
*** Para reduzir as chances de erro
|
||||
|
||||
A maior parte dos problemas com =scanf= decorre da conversão de strings.
|
||||
Por isso, é sempre bom atentar para alguns detalhes, como:
|
||||
|
||||
- Sempre validar o retorno de =scanf= e fazer o tratamento dos erros.
|
||||
- Na leitura de uma linha inteira como string (por exemplo, uma linha
|
||||
que contenha apenas uma palavra), sempre limitar a quantidade de
|
||||
caracteres lidos no especificador =%s= (ex: =%9s= para ler até 9 bytes).
|
||||
- Na leitura de caracteres únicos, o especificador =%c= deve ser precedido
|
||||
de um espaço.
|
||||
|
||||
|
||||
*** Exemplos
|
||||
|
||||
**** Leitura de duas strings
|
||||
|
||||
#+begin_src c
|
||||
char str1[10];
|
||||
char str2[10];
|
||||
|
||||
// "%Ns" não deve incluir '\0'!
|
||||
scanf("%9s", str1);
|
||||
scanf("%9s", str2);
|
||||
#+end_src
|
||||
|
||||
**** Separando a entrada digitada em campos
|
||||
|
||||
#+begin_src c
|
||||
char str1[10];
|
||||
char str2[10];
|
||||
|
||||
scanf("%9s %9s", str1, str2);
|
||||
|
||||
printf("str1: %s\n", str1);
|
||||
printf("str2: %s\n", str2);
|
||||
#+end_src
|
||||
|
||||
**** Lendo caracteres
|
||||
|
||||
Neste caso, apenas o primeiro caractere será casado, seja ele qual for.
|
||||
|
||||
#+begin_src c
|
||||
printf("Confirma (s/n)? ");
|
||||
char reply;
|
||||
scanf("%c", &reply);
|
||||
#+end_src
|
||||
|
||||
Mas, se o usuário digitar espaços antes dos caracteres esperados,
|
||||
o primeiro espaço é que será consumido. Para evitar isso...
|
||||
|
||||
#+begin_src c
|
||||
scanf(" %c", &reply);
|
||||
#+end_src
|
||||
|
||||
**** Lendo vários caracteres
|
||||
|
||||
#+begin_src c
|
||||
char a, b, c;
|
||||
scanf(" %c %c %c", &a, &b, &c);
|
||||
printf("A:%c B:%c C:%c\n", a, b, c);
|
||||
#+end_src
|
||||
|
||||
**** Lendo e formatando números
|
||||
|
||||
Inteiros:
|
||||
|
||||
#+begin_src c
|
||||
printf("Digite um inteiro: ");
|
||||
int num;
|
||||
scanf("%d", &num);
|
||||
#+end_src
|
||||
|
||||
Ponto flutuante:
|
||||
|
||||
#+begin_src c
|
||||
printf("Digite um float: ");
|
||||
float f;
|
||||
int c = scanf("%f", &f);
|
||||
#+end_src
|
||||
|
||||
*** Descarregando o buffer de entrada
|
||||
|
||||
A função =scanf= não consome nada que não case com o formato especificado,
|
||||
o que inclui a quebra de linha. Todo o restante da linha permanece no buffer
|
||||
de entrada e, na ausência de uma função da biblioteca padrão para descarregá-lo,
|
||||
nós podemos criar uma função como esta:
|
||||
|
||||
#+begin_src c
|
||||
void stdin_flush(void) {
|
||||
char ch;
|
||||
while ((ch = getchar()) != '\n' && ch != EOF);
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Ela consumiria todos os caracteres restantes no buffer de entrada enquanto
|
||||
o caractere atribuído a =ch= fosse diferente da quebra de linha (=\n=) ou de =-1=,
|
||||
que é o valor retornado por =getchar= quando tenta ler caracteres após o fim
|
||||
de um arquivo (constante simbólica =EOF=).
|
||||
|
||||
Isso é particularmente necessário quando houver leituras consecutivas da
|
||||
entrada padrão com =scanf=...
|
||||
|
||||
#+begin_src c
|
||||
int a;
|
||||
scanf("%d", &b);
|
||||
stdin_flush();
|
||||
|
||||
int b;
|
||||
scanf("%d", &b);
|
||||
stdin_flush();
|
||||
|
||||
/* ... */
|
||||
#+end_src
|
||||
|
||||
*** Inicialização de valores numéricos
|
||||
|
||||
Nos exemplos anteriores, nós temos um problema que precisa ser considerado:
|
||||
a leitura pode falhar e, nesse caso, nada será escrito nos endereços das
|
||||
variáveis passados para =scanf=. Como as variáveis não foram inicializadas,
|
||||
seu conteúdo será /lixo de memória/. Portanto, é importante que essas variáveis
|
||||
sejam devidamente inicializadas com algum valor, por exemplo:
|
||||
|
||||
#+begin_src c
|
||||
int a = 0;
|
||||
scanf("%d", &b);
|
||||
stdin_flush();
|
||||
|
||||
int b = 0;
|
||||
scanf("%d", &b);
|
||||
stdin_flush();
|
||||
|
||||
/* ... */
|
||||
#+end_src
|
||||
|
||||
Este problema é relevante com variáveis que recebem valores numéricos (=char=,
|
||||
=int=, =float=, =double=, etc), porque, em geral, elas são utilizadas em cálculos
|
||||
e até o lixo de memória pode ser tratado como número válido, levando a
|
||||
resultados enganosos.
|
||||
|
||||
*** Buffer overflow
|
||||
|
||||
A leitura de strings com =scanf= é bastante suscetível a causar vulnerabilidades
|
||||
graves de memória, como o /buffer overflow/, que é quando a quantidade de dados
|
||||
escritos a partir do endereço de um buffer excedem seu limite máximo e continuam
|
||||
sendo escritos em endereços válidos subsequentes.
|
||||
|
||||
#+begin_quote
|
||||
Quando o programa tentar escrever os bytes excedentes em uma região inválida
|
||||
da memória, nós temos uma /falha de segmentação/.
|
||||
#+end_quote
|
||||
|
||||
Observe este exemplo:
|
||||
|
||||
#+begin_src c
|
||||
char ref[10] = "123456789"; // Uma string qualquer.
|
||||
char str[10]; // Buffer para a stringa digitada no terminal.
|
||||
|
||||
scanf("%s", str); // Especificador %s sem limite.
|
||||
|
||||
printf("str: %s\n", str);
|
||||
printf("ref: %s\n", ref);
|
||||
#+end_src
|
||||
|
||||
Ao ser compilado e executado, os 10 bytes de =ref= serão escritos na pilha
|
||||
e, acima deles (em um endereço 10 bytes mais baixo), serão reservados os
|
||||
10 bytes para o buffer =str=.
|
||||
|
||||
Se o usuário digitar =abc=, esta parte da pilha ficará assim:
|
||||
|
||||
#+begin_example
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
ref | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | 0x00 |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13
|
||||
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
str | 0x61 | 0x62 | 0x63 | 0x00 | lixo | lixo | lixo | lixo | lixo | lixo |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
|
||||
#+end_example
|
||||
|
||||
A impressão no terminal será:
|
||||
|
||||
#+begin_example
|
||||
str: abc
|
||||
ref: 123456789
|
||||
#+end_example
|
||||
|
||||
Mas, se digitar =abcdefghjij=, este será o resultado:
|
||||
|
||||
#+begin_example
|
||||
buffer overflow
|
||||
↓
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
ref | 0x00 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | 0x00 |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13
|
||||
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
str | 0x61 | 0x62 | 0x63 | 0x64 | 0x65 | 0x66 | 0x67 | 0x68 | 0x69 | 0x6A |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
|
||||
#+end_example
|
||||
|
||||
E isso vai imprimir:
|
||||
|
||||
#+begin_example
|
||||
str: abcdefghij
|
||||
ref:
|
||||
#+end_example
|
||||
|
||||
Porque o caractere nulo (=0x00=) foi escrito no décimo primeiro endereço
|
||||
após o endereço de =str= (endereço =0x0A=), que é o primeiro byte de =ref=.
|
||||
|
||||
Esse problema poderia ser evitado especificando o limite do tamanho da
|
||||
string:
|
||||
|
||||
#+begin_src c
|
||||
char ref[10] = "123456789"; // Uma string qualquer.
|
||||
char str[10]; // Buffer para a stringa digitada no terminal.
|
||||
|
||||
scanf("%9s", str); // Limite de 9 bytes para a string.
|
||||
|
||||
printf("str: %s\n", str);
|
||||
printf("ref: %s\n", ref);
|
||||
#+end_src
|
||||
|
||||
Executando e digitando =abcdefghij=, os dados na pilha seriam...
|
||||
|
||||
#+begin_example
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
ref | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | 0x00 |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13
|
||||
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
str | 0x61 | 0x62 | 0x63 | 0x64 | 0x65 | 0x66 | 0x67 | 0x68 | 0x69 | 0x00 |
|
||||
+------+------+------+------+------+------+------+------+------+------+
|
||||
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
|
||||
#+end_example
|
||||
|
||||
E nós veríamos isso no terminal:
|
||||
|
||||
#+begin_example
|
||||
str: abcdefghi <- O 'j' não foi consumido...
|
||||
ref: 123456789
|
||||
#+end_example
|
||||
|
||||
|
71
exercicios/11/README.org
Normal file
71
exercicios/11/README.org
Normal file
|
@ -0,0 +1,71 @@
|
|||
#+title: Curso Básico da Linguagem C
|
||||
#+subtitle: Exercícios
|
||||
#+author: Blau Araujo
|
||||
#+startup: show2levels
|
||||
#+options: toc:3
|
||||
|
||||
* Exercícios da aula 11: Leitura da entrada padrão com 'scanf'
|
||||
|
||||
- [[../../aulas/11-scanf/README.org][Anotações da aula]]
|
||||
- [[https://youtu.be/MZiI95b2gdY][Vídeo]]
|
||||
|
||||
** 1. Desafio: Conversão Milhas->Km e Km->Milhas
|
||||
|
||||
Utilizando a função =scanf= para receber os valores digitados no formato:
|
||||
|
||||
#+begin_example
|
||||
<valor>[m, k ou km]
|
||||
#+end_example
|
||||
|
||||
Crie um programa que converta o valor de uma unidade para outra:
|
||||
|
||||
- Se a unidade for =k= ou =km=, converter para milhas;
|
||||
- Se a unidade for =m=, converter para quilômetros;
|
||||
- Se não houver unidade, presume-se o valor em quilômetros;
|
||||
- Espaços entre o valor e a unidade devem ser ignorados;
|
||||
- As unidades podem ser escritas em maiúsculas ou minúsculas;
|
||||
- Tudo após a unidade deve ser ignorado;
|
||||
- Valores devem poder ser passados com representações de ponto flutuante;
|
||||
- Resultados sempre serão impressos em ponto flutuante e duas casas de precisão.
|
||||
|
||||
** 2. Desafio: Separação de linhas em campos
|
||||
|
||||
Considerando o arquivo =ender.csv=, abaixo:
|
||||
|
||||
#+begin_example
|
||||
Maria das Couves, Rua da Horta, 23, Hortolândia, PR
|
||||
João da Silva, Av. da Prata, 42, Silverado, RS
|
||||
Antônio Pereira, Av. dos Pomares, 65, Pomorânia, SP
|
||||
#+end_example
|
||||
|
||||
Crie um programa que, usando =scanf=, liste os dados em cada linha desta forma:
|
||||
|
||||
#+begin_example
|
||||
Nome : ...
|
||||
Endereço: ...
|
||||
Cidade : ...
|
||||
Estado : ...
|
||||
#+end_example
|
||||
|
||||
O programa deve ser executado com:
|
||||
|
||||
#+begin_example
|
||||
./a.out < ender.csv
|
||||
#+end_example
|
||||
|
||||
** 3. Desafio: Implementando um 'cat' com 'scanf'
|
||||
|
||||
Crie um programa que, ao ser executado em um pipe ou com um redirecionamento
|
||||
de leitura, imprima todas as linhas de um arquivo. Se executado sem operadores,
|
||||
o programa deve imprimir todas as linhas digitadas no terminal antes do
|
||||
atalho =Ctrl+D=.
|
||||
|
||||
Exemplos:
|
||||
|
||||
#+begin_example
|
||||
./a.out < /etc/shells # Imprime todas as linhas de /etc/shells
|
||||
cat /etc/shells | ./a.out # Imprime todas as linhas enviadas pelo cat
|
||||
./a.out # Espera Ctrl+D para imprimir todas as linhas digitadas
|
||||
#+end_example
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue