Aula 1
- C
- hello, world
- Compiladores
- String
- Blocos do Scratch em C
- Tipos, formatos e operadores
- Mais exemplos
- Memória, imprecisão e estouro
C
-
Hoje vamos aprender uma nova linguagem, C: uma linguagem de programação que tem todos os recursos do Scratch e muito mais, mas talvez um pouco menos amigável pois é totalmente em texto:
#include <stdio.h> int main(void) { printf("hello, world\n"); }
- Apesar de as palavras serem novas, as ideias são as mesmas dos blocos "Quando a bandeira verde é clicada" e "diga (hello, world)" do Scratch:
- Apesar de as palavras serem novas, as ideias são as mesmas dos blocos "Quando a bandeira verde é clicada" e "diga (hello, world)" do Scratch:
-
Apesar de parecer complicado, não se esqueça que 2/3 dos alunos do CS50 nunca fizeram ciência da computação antes, então não se assuste! E embora no início, para pegar emprestada uma frase do MIT, tentar absorver todos esses novos conceitos possa parecer como beber de uma mangueira de incêndio, tenha certeza de que até o final do semestre estaremos capacitados e experientes na aprendizagem e aplicação desses conceitos.
- Podemos comparar vários dos construtos em C com blocos que já vimos e usamos no Scratch. A sintaxe é muito menos importante que os princípios, que já nos foram apresentados.
olá, mundo
-
O bloco “quando a bandeira verde for clicada” no Scratch inicia o programa principal; clicar na bandeira verde faz com que o conjunto direito de blocos abaixo comece. Em C, a primeira linha para o mesmo é
int main(void)
, sobre a qual aprenderemos mais nas próximas semanas, seguida por uma chave aberta{
, e uma chave fechada}
, envolvendo tudo o que deve estar em nosso programa.int main(void) { }
-
O bloco “dizer (olá, mundo)” é uma função e mapeia para
printf("olá, mundo");
. Em C, a função para imprimir algo na tela éprintf
, ondef
significa "format", o que significa que podemos formatar a string impressa de diferentes maneiras. Então, usamos parênteses para passar o que queremos imprimir. Temos que usar aspas duplas para circundar nosso texto para que seja entendido como texto, e finalmente, adicionamos um ponto-e-vírgula;
para finalizar essa linha de código. - Para que nosso programa funcione, também precisamos de outra linha no topo, uma linha de cabeçalho
#include <stdio.h>
que define a funçãoprintf
que queremos usar. Em algum lugar há um arquivo em nosso computador,stdio.h
, que inclui o código que nos permite acessar a funçãoprintf
, e a linha#include
diz ao computador para incluir esse arquivo com nosso programa. - Para escrever nosso primeiro programa em Scratch, abrimos o site do Scratch. Da mesma forma, usaremos o CS50 Sandbox para começar a escrever e executar o código da mesma maneira. O CS50 Sandbox é um ambiente virtual baseado em nuvem com as bibliotecas e ferramentas já instaladas para escrever programas em várias linguagens. No topo, há um editor de código simples, onde podemos digitar texto. Abaixo, temos uma janela de terminal, na qual podemos digitar comandos:
- Digitaremos nosso código do início na parte superior, depois de usar o sinal
+
para criar um novo arquivo chamadohello.c
:
- Finalizamos o arquivo do nosso programa com
.c
por convenção, para indicar que ele foi concebido como um programa C. Observe que nosso código é colorido, para que certas coisas fiquem mais visíveis.
Compiladores
- Uma vez que salvamos o código que escrevemos, que é chamado de código-fonte, precisamos convertê-lo em código de máquina, instruções binárias que o computador entende diretamente.
- Usamos um programa chamado compilador para compilar nosso código-fonte em código de máquina.
- Para fazer isso, usamos o painel Terminal, que tem um prompt de comando. O
$
à esquerda é um prompt, após o qual podemos digitar comandos. - Digitamos
clang hello.c
(ondeclang
significa "linguagens C", um compilador escrito por um grupo de pessoas). Mas antes de pressionarmos Enter, clicamos no ícone da pasta no canto superior esquerdo do CS50 Sandbox. Vemos nosso arquivo,hello.c
. Então, pressionamos Enter na janela do terminal e vemos que agora temos outro arquivo chamadoa.out
(abreviação de "saída de montagem"). Dentro desse arquivo está o código do nosso programa, em binário. Agora, podemos digitar./a.out
no prompt do terminal para executar o programaa.out
em nossa pasta atual. Acabamos de escrever, compilar e executar nosso primeiro programa!
String
-
Mas depois de executar nosso programa, vemos
hello, world$
, com o novo prompt na mesma linha que nossa saída. Acontece que precisamos especificar precisamente que precisamos de uma nova linha após o nosso programa, por isso podemos atualizar nosso código para incluir um caractere de nova linha especial,\n
:#include <stdio.h> int main(void) { printf("hello, world\n"); }
- Agora precisamos nos lembrar de recompilar nosso programa com
clang hello.c
antes de executar esta nova versão.
- Agora precisamos nos lembrar de recompilar nosso programa com
-
A linha 2 de nosso programa está intencionalmente em branco, pois queremos iniciar uma nova seção de código, assim como iniciar novos parágrafos em ensaios. Não é estritamente necessário para que nosso programa execute corretamente, mas ajuda os humanos a ler programas mais longos com mais facilidade.
- Podemos alterar o nome de nosso programa de
a.out
para outra coisa também. Podemos passar argumentos de linha de comando ou opções adicionais para programas no terminal, dependendo do que o programa foi escrito para entender. Por exemplo, podemos digitarclang -o hello hello.c
, e-o hello
está dizendo ao programaclang
para salvar a saída compilada como apenashello
. Então, possiamo executar apenas./hello
. -
Em nosso prompt de comando, podemos executar outros comandos, como
ls
(lista), que mostra os arquivos em nossa pasta atual:$ ls a.out* hello* hello.c
- O asterisco,
*
, indica que esses arquivos são executáveis ou que podem ser executados por nosso computador.
- O asterisco,
-
Podemos usar o comando
rm
(remover) para excluir um arquivo:$ rm a.out rm: remove regular file 'a.out'?
- Podemos digitar
y
ouyes
para confirmar e usarls
novamente para ver que ele realmente se foi para sempre.
- Podemos digitar
-
Agora, vamos tentar obter a entrada do usuário, como fizemos no Scratch quando queríamos dizer "ola, David":
string answer = get_string("What's your name?\n"); printf("hello, %s\n", answer);
- Primeiro, precisamos de uma string, ou parte do texto (especificamente, zero ou mais caracteres em uma sequência entre aspas duplas, como
""
,"ba"
, ou “bananas”), que podemos pedir ao usuário, com a funçãoget_string
. Passamos o prompt, ou o que queremos perguntar ao usuário, para a função com"What is your name?\n"
entre parênteses. À esquerda, queremos criar uma variável,answer
, cujo valor será o que o usuário insere. (O sinal de igual=
está definindo o valor da direita para a esquerda.) Finalmente, o tipo de variável que queremos éstring
, então especificamos isso à esquerda deanswer
. - Em seguida, dentro da função
printf
, queremos o valor deanswer
no que imprimimos de volta. Usamos um espaço reservado para nossa variável de string,%s
, dentro da frase que queremos imprimir, como"hello, %s\n"
, e então damosprintf
outro argumento, ou opção, para dizer que queremos que a variávelanswer
seja substituída.
- Primeiro, precisamos de uma string, ou parte do texto (especificamente, zero ou mais caracteres em uma sequência entre aspas duplas, como
-
Se cometermos um erro, como escrever
printf("hello, world"\n);
com o\n
fora das aspas duplas de nossa string, veremos erros de nosso compilador:$ clang -o hello hello.c hello.c:5:26: error: expected ')' printf("hello, world"\n); ^ hello.c:5:11: note: to match this '(' printf("hello, world"\n); ^ 1 error generated.
- A primeira linha do erro nos diz para olhar para
hello.c
, linha 5, coluna 26, onde o compilador esperava um parêntese de fechamento, em vez de uma barra invertida.
- A primeira linha do erro nos diz para olhar para
-
Para simplificar as coisas (pelo menos no começo), incluiremos uma biblioteca, ou conjunto de códigos, do CS50. A biblioteca nos fornece o tipo de variável
string
, a funçãoget_string
e muito mais. Só temos que escrever uma linha no topo paraincluir
o arquivocs50.h
:#include <cs50.h> #include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, name\n"); }
-
Então vamos criar um novo arquivo,
string.c
, com este código:#include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, %s\n", name); }
-
Agora, se tentarmos compilar esse código, obteremos muitas linhas de erro. Às vezes, um erro significa que o compilador começa a interpretar o código correto incorretamente, gerando mais erros do que realmente existem. Então, começamos com nosso primeiro erro:
$ clang -o string string.c string.c:5:5: error: use of undeclared identifier 'string'; did you mean 'stdin'? string name = get_string("What's your name?\n"); ^~~~~~ stdin /usr/include/stdio.h:135:25: note: 'stdin' declared here extern struct _IO_FILE *stdin; /* Standard input stream. */
- Não queríamos dizer
stdin
(“entrada padrão”) em vez destring
, então essa mensagem de erro não foi útil. Na verdade, precisamos importar outro arquivo que define o tipostring
(na verdade, uma roda de treinamento do CS50, como descobriremos nas próximas semanas).
- Não queríamos dizer
-
Então podemos incluir outro arquivo,
cs50.h
, que também inclui a funçãoget_string
, entre outras.#include <cs50.h> #include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, %s\n", name); }
-
Agora, quando tentarmos compilar nosso programa, teremos apenas um erro:
$ clang -o string string.c /tmp/string-aca94d.o: In function `main': string.c:(.text+0x19): undefined reference to `get_string' clang-7: error: linker command failed with exit code 1 (use -v to see invocation)
- Acontece que também temos que dizer ao nosso compilador para adicionar nosso arquivo de biblioteca CS50 especial, com
clang -o string string.c -lcs50
, com-l
para “link”.
- Acontece que também temos que dizer ao nosso compilador para adicionar nosso arquivo de biblioteca CS50 especial, com
-
Podemos até mesmo abstrair isso e digitar
make string
. Vemos que, por padrão no CS50 Sandbox,make
usaclang
para compilar nosso código destring.c
parastring
, com todos os argumentos necessários ou sinalizadores passados.
Blocos do Scratch em C
-
O bloco “definir [contador] para (0)” está criando uma variável e, em C, escreveríamos
int contador = 0;
, ondeint
especifica que o tipo da nossa variável é um número inteiro. -
“alterar [contador] por (1)” é
contador = contador + 1;
em C. (Em C,=
não é como um sinal de igual em uma equação, onde estamos dizendo quecontador
é igual acontador + 1
. Em vez disso,=
é um operador de atribuição que significa “copiar o valor à direita no valor à esquerda”.) E observe que não precisamos mais dizerint
, pois presume-se que já especificamos anteriormente quecontador
é umint
, com algum valor existente. Também podemos dizercontador += 1;
oucontador++;
, ambos os quais são “sintaxe simplificada” ou atalhos que têm o mesmo efeito com menos caracteres para digitar. -
Uma condição seria mapeada para:
c
if (x < y)
{
printf("x é menor que y\n");
}
-
Observe que, em C, usamos
{
e}
(bem como indentação) para indicar como as linhas de código devem ser aninhadas. -
Também podemos ter condições se-senão:
c
if (x < y)
{
printf("x é menor que y\n");
}
else
{
printf("x não é menor que y\n");
}
-
Observe que as linhas de código que não são, elas mesmas, uma ação (
if...
e as chaves) não terminam em ponto e vírgula. -
E até mesmo
senão se
:
c
if (x < y)
{
printf("x é menor que y\n");
}
else if (x > y)
{
printf("x é maior que y\n");
}
else if (x == y)
{
printf("x é igual a y\n");
}
- Observe que, para comparar dois valores em C, usamos
==
, dois sinais de igual. -
E, logicamente, não precisamos de
if (x == y)
na condição final, pois esse é o único caso restante, e podemos simplesmente dizerelse
. -
Os loops podem ser escritos como o seguinte:
c
while (true)
{
printf("olá, mundo\n");
}
-
A palavra-chave
while
também requer uma condição, então usamostrue
como a expressão booleana para garantir que nosso loop seja executado para sempre. Nosso programa verificará se a expressão é avaliada comotrue
(o que sempre será o caso) e, em seguida, executará as linhas dentro das chaves. Em seguida, repetirá isso até que a expressão não seja mais verdadeira (o que não mudará neste caso). -
Poderíamos fazer algo um certo número de vezes com
while
:
c
int i = 0;
while (i < 50)
{
printf("olá, mundo\n");
i++;
}
- Criamos uma variável,
i
, e a definimos como 0. Então, enquantoi < 50
, executamos algumas linhas de código e adicionamos 1 ai
após cada execução. -
As chaves em torno das duas linhas dentro do loop
while
indicam que essas linhas serão repetidas e podemos adicionar linhas adicionais ao nosso programa depois, se quisermos. -
Para fazer a mesma repetição, mais comumente podemos usar a palavra-chave
for
:
c
for (int i = 0; i < 50; i++)
{
printf("olá, mundo\n");
}
- Novamente, primeiro criamos uma variável chamada
i
e a definimos como 0. Então, verificamos sei < 50
toda vez que alcançamos o início do loop, antes de executar qualquer parte do código dentro dele. Se essa expressão for verdadeira, então executamos o código interno. Finalmente, depois de executar o código interno, usamosi++
para adicionar um ai
e o loop se repete.
Tipos, formatos, operadores
- Há outros tipos que podemos usar para as nossas variáveis
bool
, uma expressão booleana detrue
oufalse
char
, um único caractere comoa
ou2
double
, um valor de ponto flutuante com ainda mais dígitosfloat
, um valor de ponto flutuante, ou número real com um valor decimalint
, inteiros até um determinado tamanho, ou número de bitslong
, inteiros com mais bits, para que possam contar mais altostring
, uma cadeia de caracteres
- E a biblioteca CS50 tem funções correspondentes para obter entrada de vários tipos:
get_char
get_double
get_float
get_int
get_long
get_string
- Para
printf
, também, há diferentes espaços reservados para cada tipo:%c
para caracteres%f
para flutuantes, duplos%i
para ints%li
para longos%s
para strings
- E existem alguns operadores matemáticos que podemos usar:
+
para adição-
para subtração*
para multiplicação/
para divisão%
para resto
Mais exemplos
- Para cada um desses exemplos, você pode clicar nos links do sandbox para executar e editar suas próprias cópias deles.
-
Em
int.c
, obtemos e imprimimos um inteiro:#include <cs50.h> #include <stdio.h> int main(void) { int idade = get_int("Qual é a sua idade?\n"); int dias = idade * 365; printf("Você tem pelo menos %i dias de idade.\n", dias); }
- Observe que usamos
%i
para imprimir um inteiro. - Agora podemos executar
make int
e executar nosso programa com./int
. -
Podemos combinar linhas e remover a variável
dias
com:int idade = get_int("Qual é a sua idade?\n"); printf("Você tem pelo menos %i dias de idade.\n", idade * 365);
-
Ou mesmo combinar tudo em uma linha:
printf("Você tem pelo menos %i dias de idade.\n", get_int("Qual é a sua idade?\n") * 365);
-
No entanto, quando uma linha fica muito longa ou complicada, pode ser melhor manter duas ou até três linhas para facilitar a leitura.
- Observe que usamos
-
Em
float.c
, podemos obter números decimais (chamados de valores de ponto flutuante em computadores, porque o ponto decimal pode "flutuar" entre os dígitos, dependendo do número):#include <cs50.h> #include <stdio.h> int main(void) { float preço = get_float("Qual é o preço?\n"); printf("Seu total é %f.\n", preço * 1.0625); }
- Agora, se compilarmos e executarmos nosso programa, veremos um preço impresso com impostos.
- Podemos especificar o número de dígitos impressos após a vírgula decimal com um espaço reservado como
%.2f
para dois dígitos após a vírgula decimal.
-
Com
parity.c
, podemos verificar se um número é par ou ímpar:#include <cs50.h> #include <stdio.h> int main(void) { int n = get_int("n: "); if (n % 2 == 0) { printf("par\n"); } else { printf("ímpar\n"); } }
- Com o operador
%
(módulo), podemos obter o restante den
após sua divisão por 2. Se o restante for 0, sabemos quen
é par. Caso contrário, sabemos quen
é ímpar. - Funções como
get_int
da biblioteca CS50 fazem a verificação de erros, onde apenas as entradas do usuário que correspondem ao tipo desejado são aceitas.
- Com o operador
-
Em
conditions.c
, transformamos os trechos de condição anteriores em um programa:// Condições e operadores relacionais #include <cs50.h> #include <stdio.h> int main(void) { // Solicita ao usuário x int x = get_int("x: "); // Solicita ao usuário y int y = get_int("y: "); // Compara x e y if (x < y) { printf("x é menor que y\n"); } else if (x > y) { printf("x é maior que y\n"); } else { printf("x é igual a y\n"); } }
- As linhas que começam com
//
são comentários, ou nota para humanos que o compilador irá ignorar. - Para David compilar e executar este programa em seu sandbox, ele primeiro precisava executar
cd src1
no terminal. Isso altera o diretório ou pasta para aquele no qual ele salvou todos os arquivos de origem da aula. Então, ele poderia executarmake conditions
e./conditions
. Compwd
, ele pode ver que está em uma pastasrc1
(dentro de outras pastas). Ecd
por si só, sem argumentos, nos levará de volta para nossa pasta padrão no sandbox.
- As linhas que começam com
-
Em
agree.c
, podemos pedir ao usuário para confirmar ou negar algo:// Operadores lógicos #include <cs50.h> #include <stdio.h> int main(void) { // Solicita ao usuário que concorde char c = get_char("Você concorda?\n"); // Verifica se concordou if (c == 'S' || c == 's') { printf("Concordo.\n"); } else if (c == 'N' || c == 'n') { printf("Não concordo.\n"); } }
- Usamos duas barras verticais,
||
, para indicar um "ou" lógico, seja qual for a expressão que pode ser verdadeira para que a condição seja seguida. - E se nenhuma das expressões for verdadeira, nada acontecerá, pois nosso programa não tem um loop.
- Usamos duas barras verticais,
-
Vamos implementar o programa de tosse da semana 0:
#include <stdio.h> int main(void) { printf("tosse\n"); printf("tosse\n"); printf("tosse\n"); }
-
Podemos usar um loop
for
:#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("tosse\n"); } }
- Por convenção, os programadores tendem a começar a contar em 0 e, portanto,
i
terá os valores de0
,1
e2
antes de parar, para um total de três iterações. Também poderíamos escreverfor (int i = 1; i <= 3; i++)
para o mesmo efeito final.
- Por convenção, os programadores tendem a começar a contar em 0 e, portanto,
-
Podemos mover a linha
printf
para sua própria função:#include <stdio.h> void tosse(void); int main(void) { for (int i = 0; i < 3; i++) { tosse(); } } void tosse(void) { printf("tosse\n"); }
- Declaramos uma nova função com
void tosse(void);
antes que nossa funçãomain
a chame. O compilador C lê nosso código de cima para baixo, então precisamos dizer a ele que a funçãotosse
existe, antes de usá-la. Então, após nossa funçãomain
, podemos implementar a funçãotosse
. Dessa forma, o compilador sabe que a função existe e podemos manter nossa funçãomain
perto do topo. - E nossa função
tosse
não recebe nenhuma entrada, então temostosse(void)
.
- Declaramos uma nova função com
-
Podemos abstrair a
tosse
ainda mais:#include <stdio.h> void tosse(int n); int main(void) { tosse(3); } void tosse(int n) { for (int i = 0; i < n; i++) { printf("tosse\n"); } }
- Agora, quando queremos imprimir "tosse" qualquer número de vezes, podemos simplesmente chamar a mesma função. Observe que, com
void tosse(int n)
, indicamos que a funçãotosse
recebe como entrada umint
, que chamamos den
. E dentro detosse
, usamosn
em nosso loopfor
para imprimir "tosse" o número correto de vezes.
- Agora, quando queremos imprimir "tosse" qualquer número de vezes, podemos simplesmente chamar a mesma função. Observe que, com
-
Vamos dar uma olhada em
positivo.c
:#include <cs50.h> #include <stdio.h> int obter_ inteiro_positivo(void); int main(void) { int i = obter_inteiro_positivo(); printf("%i\n", i); } // Solicita ao usuário um inteiro positivo int obter_ inteiro_positivo(void) { int n; faça { n = get_int("%s", "Inteiro positivo: "); } Enquanto (n < 1); Retorno n; }
- A biblioteca CS50 não tem uma função
obter_inteiro_positivo
, mas podemos escrever uma nós mesmos. Nossa funçãoint obter_inteiro_positivo(void)
solicitará ao usuário umint
e retornará esseint
, que nossa funçãomain
armazena comoi
. Emobter_inteiro_positivo
, inicializamos uma variável,int n
, sem atribuir um valor a ela ainda. Então, temos uma nova construção,faça ... enquanto
, que faz algo primeiro, então verifica uma condição e repete até que a condição deixe de ser verdadeira. - Assim que o loop terminar porque temos um
n
que não é "< 1", podemos retorná-lo com a palavra-chavereturn
. E de volta à nossa funçãomain
, podemos definirint i
para esse valor.
- A biblioteca CS50 não tem uma função
Telas
-
Podemos desejar um programa que imprima parte de uma tela de um videogame, como Super Mario Bros. Em
mario0.c
, temos:// Imprime uma linha de 4 pontos de interrogação #include <stdio.h> int main(void) { printf("????\n"); }
-
Podemos pedir para o usuário um número de pontos de interrogação e, em seguida, imprimi-los, com
mario2.c
:#include <cs50.h> #include <stdio.h> int main(void) { int n; do { n = get_int("Largura: "); } while (n < 1); for (int i = 0; i < n; i++) { printf("?"); } printf("\n"); }
-
E podemos imprimir um conjunto bidimensional de blocos com
mario8.c
:// Imprime uma grade nxn de tijolos com um loop #include <cs50.h> #include <stdio.h> int main(void) { int n; do { n = get_int("Tamanho: "); } while (n < 1); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("#"); } printf("\n"); } }
- Observe que temos dois loops aninhados, onde o loop externo usa
i
para fazer tudo dentro das vezesn
e o loop interno usaj
, uma variável diferente, para fazer algon
vezes para cada uma daquelas vezes. Em outras palavras, o loop externo imprimen
“linhas” ou linhas, e o loop interno imprimen
“colunas” ou caracteres “#”, em cada linha.
- Observe que temos dois loops aninhados, onde o loop externo usa
-
Outros exemplos não abordados em aula estão disponíveis em “Código-fonte” para a Semana 1.
# Memória imprecisão e overflow
- Nosso computador possui memória em chips de hardware chamados RAM, memória de acesso aleatório. Nossos programas utilizam essa RAM para armazenar dados enquanto são executados, porém essa memória é finita. Portanto, com um número finito de bits não podemos representar todos os números possiveis (dos quais existe um número infinito). Então nosso computador tem um determinado número de bits para cada
float
eint
e tem de arredondar para o valor decimal mais próximo em determinado ponto. -
Com
floats.c
podemos ver o que acontece quando usamos floats:#include <cs50.h> #include <stdio.h> int main(void) { // Solicita o x ao usuário float x = get_float("x: "); // Solicita o y ao usuário float y = get_float("y: "); // Executa a divisão printf("x / y = %.50f\n", x / y); }
- Com
%50f
, podemos especificar o número de casas decimais exibidos. -
Hmm, agora obtemos...
x: 1 y: 10 x / y = 0.10000000149011611938476562500000000000000000000000
-
Acontece que isso se chama imprecisão de ponto flutuante, onde não temos bits suficientes para armazenar todos os valores possíveis então o computador tem de armazenar o valor mais próximo possível de 1 dividido por 10.
- Com
-
Podemos ver um problema similar em
overflow.c
:#include <stdio.h> #include <unistd.h> int main(void) { for (int i = 1; ; i *= 2) { printf("%i\n", i); sleep(1); } }
- No nosso laço
for
, definimosi
como1
e o dobramos com*= 2
. (E continuaremos fazendo isso para sempre, então não há condição para verificação.) - Também usamos a função
sleep
dounistd.h
para permitir que nosso programa pause sempre. -
Agora, quando executamos esse programa veremos o número crescer cada vez mais até que:
1073741824 overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int' -2147483648 0 0 ...
-
Acontece que nosso programa reconheceu que um inteiro assinado (um inteiro com um sinal positivo ou negativo) não poderia armazenar esse próximo valor e exibiu um erro. Então, uma vez que ele tentou dobrá-lo de qualquer forma,
i
se tornou um número negativo e então 0. - Esse problema é chamado de overflow de inteiro, onde um inteiro pode ser somente tão grande antes que acabem os bits e ele "reinicie". Podemos imaginar adicionar um ao número 999 decimal. O último digito se torna 0, pegamos o 1 emprestado para que o próximo digito se torne 0 e obtemos 1000. Mas se tivéssemos apenas três digitos, acabaríamos com 000 pois não há lugar para o 1 final!
- No nosso laço
-
O problema da virada do milênio surgiu porque muitos programas armazenavam o ano-calendário com apenas dois digitos, como 98 para 1998 e 99 para 1999. Porém quando o ano 2000 se aproximou, os programas teriam armazenado 00, levando a confusão entre os anos 1900 e 2000.
- Um avião Boeing 787 também teve um bug onde um contador no gerador tinha overflow após um certo número de dias de operação contínua, visto que o número de segundos que havia rodado já não cabia mais naquele contador.
- Portanto, vimos alguns problemas que podem acontecer, mas agora entendemos por que e como prevení-los.
- Com o problema definido nesta semana, usaremos o CS50 Lab, construído no CS50 Sandbox, para escrever alguns programas com orientações para nos guiar.