mirror of
https://gitlab.com/blau_araujo/cblc.git
synced 2025-05-11 10:56:37 -03:00
Compare commits
No commits in common. "efe8315b1432494b8be5a32b9748096e0b51bb63" and "95ea0a5bd56b730db612db2703892b2e42e862f9" have entirely different histories.
efe8315b14
...
95ea0a5bd5
3 changed files with 1 additions and 555 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/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 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]])
|
||||||
|
|
|
@ -1,483 +0,0 @@
|
||||||
#+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
|
|
||||||
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
#+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