Saltar para o conteúdo
Topos Uranos

Resumo

Esta aula encerra a segunda unidade do curso, dedicada aos dados e seus tipos. Reconstrói a cadeia de conceitos que vai da variável à constante que o compilador vigia, passando pela representação binária dos inteiros e seus dois estouros, os reais aproximados e a tolerância, os caracteres como códigos e as conversões que perdem informação; separa o que disso vale em qualquer linguagem do que é próprio de C++; reúne o comportamento indefinido da unidade, com o que o compilador detecta, o que o detector detecta e o que ninguém detecta; repete na sua avaliação de saída as destrezas da avaliação de entrada; e termina com seis desafios que combinam a unidade inteira.

Objetivos de aprendizagem

  1. Escolher e justificar o tipo de cada dado de um problema segundo os valores que pode tomar, a exatidão que exige e as operações que se farão com ele.
  2. Prever os efeitos dos limites de representação num programa dado: o estouro dos inteiros, o arredondamento do ponto flutuante e a perda de informação de uma conversão.
  3. Usar constantes com nome e conversões explícitas, e explicar o que o compilador verifica em cada caso.
  4. Reconhecer o comportamento indefinido da unidade e dizer, para cada caso, o que o detecta e como se previne.
  5. Separar, no que foi aprendido na unidade, o que vale em qualquer linguagem do que é próprio de C++.

O que a unidade alcançou

A unidade se propôs ensinar que todo dado vive na memória com um tipo que fixa que valores pode tomar, quanto ocupa e que operações admite, e que esses limites têm consequências observáveis. O propósito foi cumprido apoiando-se apenas no que a introdução declarou como sabido: o ciclo de compilar e executar com as opções do curso, a saída com formato, a leitura com std::cin e a leitura das mensagens do compilador. O programa das quatro contas com que a introdução apresentou o problema pode agora ser explicado linha por linha, e corrigido.

Variáveis e memória

A aula Variáveis e memória estabeleceu que uma variável é uma posição de memória com um nome, um tipo fixo e um valor que muda, e que o tipo fixa seu tamanho, que sizeof mede. Distinguiu a inicialização da atribuição, mostrou que as chaves rejeitam uma conversão com perda e ensinou que a atribuição copia e não vincula, e que o valor substituído se perde, de onde se segue a variável auxiliar da troca. Definiu o estado de um programa como o conjunto dos valores de suas variáveis e ensinou a segui-lo com uma tabela de rastreamento. Fixou as convenções dos nomes e fechou com o primeiro comportamento indefinido da unidade: a leitura de uma variável que nunca recebeu valor.

Os inteiros e sua representação

A aula Os inteiros e sua representação ensinou a notação posicional nas bases 10, 2 e 16. Do fato de um tipo ter nn bits deduziu seu intervalo: 2n2^n configurações, que sem sinal representam de 00 a 2n−12^n - 1 e com sinal, em complemento de dois, de −2n−1-2^{n-1} a 2n−1−12^{n-1} - 1, com um negativo a mais que positivos. Distinguiu os dois estouros: o dos inteiros sem sinal, que é aritmética módulo 2n2^n e está definido (0u - 1 vale 4 294 967 2954\,294\,967\,295), e o dos inteiros com sinal, que é comportamento indefinido e que o detector interrompe. Mostrou que comparar um inteiro com sinal com um sem sinal converte o primeiro, de modo que −1<0u-1 < 0u é falso, e terminou com um critério para escolher o tipo inteiro segundo o intervalo previsível do dado.

Os números de ponto flutuante

A aula Os números de ponto flutuante mostrou que um real é guardado como sinal, expoente e mantissa binária, de modo que os valores representáveis são um conjunto finito de racionais com denominador potência de 2. Disso se seguiu que 0.10.1 não existe na máquina: em seu lugar guarda-se o double mais próximo, e 0.1 + 0.2 é escrito 0.30000000000000004. A aula limitou o erro de cada arredondamento, mostrou que os erros se acumulam e que a soma deixa de ser associativa; de onde deduziu que dois reais calculados não se comparam com ==, mas pela sua distância, com uma tolerância justificada. Fixou double como tipo real por omissão, e apresentou o infinito e o valor não numérico, NaN.

Caracteres, booleanos, conversões e constantes

A aula Caracteres, booleanos, conversões e constantes mostrou que um tipo é uma regra para ler os bits: um char guarda um byte cujo valor é o código ASCII do caractere, e como os dígitos e as letras têm códigos consecutivos, '7' - '0' vale 7; um bool é verdadeiro ou falso, e qualquer número diferente de zero se converte em verdadeiro. Estudou as conversões implícitas e estabeleceu que a conversão ocorre dentro da operação, segundo o tipo dos operandos, e não ao guardar o resultado: double r{7 / 2} vale 3. Ensinou a escrever a conversão desejada com static_cast, que não evita a perda, mas a declara; precisou que as chaves rejeitam o estreitamento, e mostrou o terceiro comportamento indefinido da unidade, a conversão para inteiro de um real que não cabe. Fechou com as constantes (const, constexpr e static_assert), o tipo dos literais e auto.