mirror of
https://gitlab.com/blau_araujo/cblc.git
synced 2025-05-10 18:46: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/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/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/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 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 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]])
|
- [[./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