C# Inteiros
Números inteiros estão em todo lugar: a quantidade de cadernos da lojinha, a idade de um cliente, os pontos de um jogo, o número de habitantes de um país. O C# tem uma família de tipos inteiros, de tamanhos diferentes, com e sem sinal. Nesta aula você vai conhecer cada um, ver até onde cada caixa vai e descobrir o que acontece quando uma conta passa do limite, e como fazer o C# avisar quando isso acontece.
- A família:
byte,short,int,longe as versões sem sinal (sbyte,ushort,uint,ulong). - Os limites de cada tipo, com
MinValueeMaxValue. - Como escrever inteiros:
1_000_000,0xFF,0b1010e os sufixosL,UeUL. - Os erros de valor que não cabe:
CS0031,CS0266eCS1021. - O estouro (overflow): por que
int.MaxValue + 1dá um número negativo. checkedeunchecked, e a exceçãoOverflowException.
Todos os programas desta aula foram rodados com o .NET 10 (C# 14) na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs.
A ideia: o hodômetro do carro
O hodômetro de um carro antigo tem 6 rodinhas: mostra de 000000 a 999999 quilômetros. Ele não consegue mostrar um milhão. Se o carro rodar mais 1 km depois de 999999, as rodinhas voltam para 000000.
Os inteiros do computador são iguais: cada tipo tem um número fixo de "rodinhas" (bits) e, por isso, um maior valor e um menor valor. Para números maiores, você precisa de uma caixa com mais rodinhas. E quando uma conta passa do limite, o C# deixa você escolher: dar a volta, como o hodômetro, ou parar o programa com um erro (seção 5).
1. A família dos inteiros
byte idade = 29; short ano = 2026; int habitantes = 213_000_000; long populacaoMundial = 8_100_000_000; Console.WriteLine(idade); Console.WriteLine(ano); Console.WriteLine(habitantes); Console.WriteLine(populacaoMundial);
$ dotnet run familia.cs 29 2026 213000000 8100000000
- byte idade = 29;
byte: 1 byte, de 0 a 255. Pequeno e sem negativos. Aparece muito ao ler arquivos e imagens, byte a byte.- short ano = 2026;
short("curto"): 2 bytes, de -32 768 a 32 767. Quase não se usa no dia a dia.- int habitantes = 213_000_000;
int: 4 bytes, até uns 2,1 bilhões. É o padrão: na dúvida, useint. Os_só ajudam a ler o número (seção 3).- long populacaoMundial = 8_100_000_000;
long("longo"): 8 bytes, até uns 9,2 quintilhões. Para números que passam de 2 bilhões: população, bytes de um disco, milissegundos.
Na prática, você vai usar int quase sempre e long quando o número pode passar de 2 bilhões. Os outros existem para economizar memória ou para conversar com arquivos e outros sistemas.
Com sinal e sem sinal
Cada tamanho tem duas versões: uma com sinal (guarda negativos) e uma sem sinal (só zero e positivos, mas vai até o dobro). A letra u na frente quer dizer unsigned, "sem sinal"; o s do sbyte quer dizer signed, "com sinal":
| Bytes | Com sinal | Sem sinal |
|---|---|---|
| 1 | sbyte | byte |
| 2 | short | ushort |
| 4 | int | uint |
| 8 | long | ulong |
Repare que o byte é a exceção: o "normal" dele é sem sinal, e o com sinal ganhou o nome sbyte.
2. Os limites: MinValue e MaxValue
Todo tipo inteiro sabe o seu menor valor (MinValue, "valor mínimo") e o seu maior valor (MaxValue, "valor máximo"):
Console.WriteLine($"sbyte: {sbyte.MinValue} a {sbyte.MaxValue}");
Console.WriteLine($"byte: {byte.MinValue} a {byte.MaxValue}");
Console.WriteLine($"short: {short.MinValue} a {short.MaxValue}");
Console.WriteLine($"ushort: {ushort.MinValue} a {ushort.MaxValue}");
Console.WriteLine($"int: {int.MinValue} a {int.MaxValue}");
Console.WriteLine($"uint: {uint.MinValue} a {uint.MaxValue}");
Console.WriteLine($"long: {long.MinValue} a {long.MaxValue}");
Console.WriteLine($"ulong: {ulong.MinValue} a {ulong.MaxValue}");
$ dotnet run limites.cs sbyte: -128 a 127 byte: 0 a 255 short: -32768 a 32767 ushort: 0 a 65535 int: -2147483648 a 2147483647 uint: 0 a 4294967295 long: -9223372036854775808 a 9223372036854775807 ulong: 0 a 18446744073709551615
| Tipo | Bytes | Faixa | Em palavras |
|---|---|---|---|
sbyte | 1 | -128 a 127 | pouco mais de cem |
byte | 1 | 0 a 255 | duzentos e poucos |
short | 2 | -32 768 a 32 767 | uns 32 mil |
ushort | 2 | 0 a 65 535 | uns 65 mil |
int | 4 | -2 147 483 648 a 2 147 483 647 | uns 2,1 bilhões |
uint | 4 | 0 a 4 294 967 295 | uns 4,2 bilhões |
long | 8 | -9 223 372 036 854 775 808 a 9 223 372 036 854 775 807 | uns 9,2 quintilhões |
ulong | 8 | 0 a 18 446 744 073 709 551 615 | uns 18 quintilhões |
O lado negativo sempre tem um a mais (-128 e 127): o zero ocupa uma vaga do lado positivo. E o tipo sem sinal vai até o dobro, porque usa para os positivos as vagas que seriam dos negativos. Esses tamanhos são fixos no C#: um long tem 8 bytes em qualquer computador.
Console.WriteLine(1234567.ToString("N0")); mostra 1.234.567 no Ubuntu em português (o N0 quer dizer "número com 0 casas decimais"). Os formatos estão em Formatar Números.
Quando o valor não cabe na caixa
Se você escreve um número que não cabe no tipo, o C# nem compila. Três tentativas e três erros:
byte nota = 300; Console.WriteLine(nota);
$ dotnet run naocabe.cs /home/ana/cs/naocabe.cs(1,13): error CS0031: O valor de constante "300" não pode ser convertido em "byte" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O byte vai até 255; 300 não cabe. Em inglês: Constant value '300' cannot be converted to a 'byte'. O mesmo erro aparece com uint estoque = -1;: um tipo sem sinal não aceita negativos.
int populacao = 8100000000; Console.WriteLine(populacao);
$ dotnet run naocabe2.cs /home/ana/cs/naocabe2.cs(1,17): error CS0266: Não é possível converter implicitamente tipo "long" em "int". Existe uma conversão explícita (há uma conversão ausente?) Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
Aqui a mensagem fala em long, e você nem escreveu long. É que o C# dá a todo número escrito no código (um literal) o primeiro tipo em que ele cabe, nesta ordem: int, uint, long, ulong. O 8100000000 não cabe em int nem em uint, então ele é um long, e um long não entra numa caixa int.
ulong enorme = 99999999999999999999; Console.WriteLine(enorme);
$ dotnet run naocabe3.cs /home/ana/cs/naocabe3.cs(1,16): error CS1021: Constante de integral muito grande Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
Esse número (20 dígitos) passa até do ulong, o maior inteiro comum. Em inglês: Integral constant is too large. Para números assim, veja "Inteiros ainda maiores" no fim da seção 5.
Missão: a população do mundo passa de 8 bilhões e não cabe num int. Rode para ver o erro CS0266 e troque o tipo da variável para o inteiro de 8 bytes, para mostrar 8100000000.
int populacao = 8_100_000_000; Console.WriteLine(populacao);
long populacao = 8_100_000_000; Console.WriteLine(populacao);
3. Como escrever inteiros (literais)
int milhao = 1_000_000;
int hexa = 0xFF;
int binario = 0b1010_1010;
int dez = 010;
long grande = 5_000_000_000L;
uint semSinal = 4_000_000_000U;
ulong maior = 18_000_000_000_000_000_000UL;
Console.WriteLine(milhao);
Console.WriteLine(hexa);
Console.WriteLine(binario);
Console.WriteLine(dez);
Console.WriteLine(grande);
Console.WriteLine(semSinal);
Console.WriteLine(maior);
Console.WriteLine(255.ToString("X"));
Console.WriteLine(10.ToString("B"));
$ dotnet run literais.cs 1000000 255 170 10 5000000000 4000000000 18000000000000000000 FF 1010
- 1_000_000
- O separador de dígitos C# 7: o
_só ajuda os seus olhos e é ignorado. Pode ficar entre quaisquer dígitos:1_000_000,12_34. Não aparece na saída. - 0xFF
- Hexadecimal, base 16: começa com
0xe usa os dígitos 0 a 9 e A a F (A = 10 … F = 15).FF= 15 × 16 + 15 = 255. Muito usado para cores (0xFF8000é laranja). - 0b1010_1010
- Binário, base 2 C# 7: começa com
0be só usa 0 e 1.10101010em binário é 128 + 32 + 8 + 2 = 170. - 010
- Dez mesmo! No C#, ao contrário do C e do Java, um zero à esquerda não muda a base: não existe literal octal. O zero é só ignorado.
- 5_000_000_000L
- O sufixo
Ldiz "este número élong". Aqui ele nem era preciso (o número já serialong, porque não cabe emuint), mas ele é útil para forçar uma conta a ser feita emlong(seção 4). UseLmaiúsculo: olminúsculo parece o dígito 1, e o compilador até avisa (CS0078). - 4_000_000_000U … UL
U=uint(ouulong, se não couber);UL=ulong. As letras podem ser maiúsculas ou minúsculas.- 255.ToString("X") · 10.ToString("B")
- O caminho inverso: mostrar um número em hexadecimal (
"X") ou em binário ("B", novo no .NET 8).
4. Estouro: quando a conta passa do limite
Escrever um número que não cabe é fácil de pegar: o compilador vê o número. Mas e quando o número só aparece durante o programa, como resultado de uma conta? Veja:
int pontos = int.MaxValue; Console.WriteLine(pontos); pontos = pontos + 1; Console.WriteLine(pontos); byte b = 255; b++; Console.WriteLine(b);
$ dotnet run estouro.cs 2147483647 -2147483648 « o maior int + 1 virou o MENOR int 0 « e o byte 255 + 1 virou 0
- int pontos = int.MaxValue;
- O jogador começa com o maior
intpossível, 2 147 483 647. - pontos = pontos + 1;
- Mais um ponto. O resultado certo (2 147 483 648) não cabe num
int. - -2147483648
- O número deu a volta, como o hodômetro: do maior positivo para o menor negativo. Nenhum erro, nenhum aviso: o programa simplesmente seguiu com o valor errado.
- b++;
- Soma 1 ao
byte(o++tem aula própria, ++ e --). 255 + 1 não cabe em 1 byte e volta para 0.
Isso é o estouro (overflow, "transbordar"). Diferente do C, no C# o resultado é sempre esse, em qualquer computador: a conta guarda só os bits que cabem. Mas "sempre igual" não quer dizer "certo". Um saldo que vira negativo de repente é um defeito grave.
Um tipo sem sinal dá a volta para o outro lado. A Ana tinha 3 cadernos e vendeu 5:
uint estoque = 3; uint vendidos = 5; uint resta = estoque - vendidos; Console.WriteLine(resta);
$ dotnet run subtrai.cs 4294967294 « 3 - 5 deu quase 4,3 bilhões
O uint não fica negativo: 3 - 5 = -2 vira 4 294 967 296 - 2. Por isso a recomendação é usar int para contas comuns, mesmo de coisas que "nunca são negativas". Com int, o resultado seria -2, que pelo menos mostra que faltam 2 cadernos.
O estouro escondido na conta
Guardar o resultado num tipo grande não basta, se a conta foi feita num tipo pequeno:
int anos = 100; long segundos = anos * 365 * 24 * 60 * 60; Console.WriteLine(segundos); long certo = anos * 365L * 24 * 60 * 60; Console.WriteLine(certo);
$ dotnet run escondido.cs -1141367296 « errado! 3153600000 « certo: uns 3,15 bilhões de segundos
A caixa segundos é long, mas o C# faz a conta primeiro e só depois guarda. Na conta só há int (anos, 365, 24, 60), então ela é feita em int, estoura no caminho, e o valor já errado é guardado. Na segunda linha, o 365L é long, e uma conta entre int e long é feita em long. A regra completa está em Conversão de Tipos.
5. checked: estouro vira erro
O estouro do C# é silencioso por padrão porque conferir cada conta custa um pouquinho de tempo. Quando um valor errado seria grave (dinheiro, estoque, contagens importantes), você pode pedir para o C# conferir, com a palavra checked ("conferido"):
int pontos = int.MaxValue;
Console.WriteLine("Antes da conta");
int novo = checked(pontos + 1);
Console.WriteLine(novo);
$ dotnet run checked1.cs Antes da conta Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow. at Program.<Main>$(String[] args) in /home/ana/cs/checked1.cs:line 3 $ echo $? 134
- checked(pontos + 1)
- Faz a conta conferindo: se o resultado não couber no tipo, em vez de dar a volta, o programa para com um erro.
- Unhandled exception.
- "Exceção não tratada": aconteceu um erro durante a execução e ninguém cuidou dele. As exceções saem em inglês, mesmo no Ubuntu em português.
- System.OverflowException
- O tipo do erro: "exceção de estouro".
- Arithmetic operation resulted in an overflow.
- "A operação aritmética resultou em estouro."
- at Program.<Main>$(String[] args) in /home/ana/cs/checked1.cs:line 3
- Onde foi: no programa principal (o
<Main>$é o nome que o compilador dá às instruções soltas do arquivo, aula Program.cs e o Main), no arquivochecked1.cs, linha 3. - 134
- O código de saída de um programa que morreu com exceção no Linux. O
Console.WriteLine(novo)nunca rodou.
Parar com erro parece pior do que seguir, mas é muito melhor do que guardar um saldo errado sem ninguém perceber. Mais tarde você vai aprender a tratar o erro, com try e catch.
Um bloco inteiro conferido
checked também pode envolver várias linhas, entre chaves. Toda conta de inteiros dentro do bloco é conferida:
int saldo = 2_000_000_000;
int deposito = 500_000_000;
checked
{
int total = saldo + deposito;
Console.WriteLine(total);
}
$ dotnet run checked2.cs Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow. at Program.<Main>$(String[] args) in /home/ana/cs/checked2.cs:line 5
Sem o checked, o programa mostraria -1794967296: um depósito que deixou o saldo negativo.
Missão: o estoque da Ana tem 50 000 caixas com 50 000 parafusos cada. A conta estoura e o programa mostra um número sem sentido. Envolva a multiplicação com checked( … ) para o programa parar com OverflowException em vez de mostrar o valor errado.
int caixas = 50_000;
int porCaixa = 50_000;
int parafusos = caixas * porCaixa;
Console.WriteLine("Parafusos: " + parafusos);
int caixas = 50_000;
int porCaixa = 50_000;
int parafusos = checked(caixas * porCaixa);
Console.WriteLine("Parafusos: " + parafusos);
Depois de cumprir a missão, troque os dois int por long (inclusive o de parafusos): agora o resultado, 2 500 000 000, cabe, e o checked deixa a conta passar.
Ligar o checked no programa todo
Em vez de escrever checked em cada conta, você pode ligar a conferência para o programa inteiro com a opção CheckForOverflowUnderflow ("conferir estouro para cima e para baixo"). Num arquivo solto, ela vai numa linha #:property no topo .NET 10; num projeto, no .csproj (aula O Arquivo .csproj). Com ela ligada, unchecked ("não conferido") abre uma exceção, para as contas em que dar a volta é o que você quer:
#:property CheckForOverflowUnderflow=true int pontos = int.MaxValue; int a = unchecked(pontos + 1); Console.WriteLine(a); int b = pontos + 1; Console.WriteLine(b);
$ dotnet run checkedprop.cs -2147483648 Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow. at Program.<Main>$(String[] args) in /home/ana/cs/checkedprop.cs:line 5
A linha 3, marcada com unchecked, deu a volta; a linha 5, uma conta comum, agora é conferida e parou o programa.
O editor do guia também lê a linha #:property CheckForOverflowUnderflow=true: cole este programa numa caixa e rode, e a saída é a mesma do terminal, com o -2147483648 e a OverflowException da linha 5. Num projeto, a mesma opção vai no .csproj.
Contas só com constantes: o compilador confere sozinho
Se a conta só tem valores fixos, o compilador faz a conta na hora de compilar, e ali ele sempre confere:
int pontos = int.MaxValue + 1; Console.WriteLine(pontos);
$ dotnet run estouroconst.cs /home/ana/cs/estouroconst.cs(1,14): error CS0220: A operação estoura o tempo de compilação no modo de ativação Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
A tradução para o português ficou estranha. O original em inglês é mais claro: The operation overflows at compile time in checked mode, "a operação estoura durante a compilação, no modo conferido". Se você quer mesmo esse resultado, escreva unchecked(int.MaxValue + 1): compila e vale -2147483648.
Inteiros ainda maiores
Quando nem o ulong basta, o .NET tem Int128 .NET 7 (16 bytes) e System.Numerics.BigInteger, que cresce o quanto precisar:
Console.WriteLine(Int128.MaxValue); System.Numerics.BigInteger fat = 1; for (int i = 1; i <= 30; i++) fat *= i; Console.WriteLine(fat);
$ dotnet run grandes.cs 170141183460469231731687303715884105727 265252859812191058636308480000000
A segunda linha é o fatorial de 30 (1 × 2 × 3 × … × 30), que passa de qualquer long. O for só repete a multiplicação 30 vezes (aula for). Esses tipos são mais lentos e raros no dia a dia; você vai saber quando precisar deles.
Quando dá errado
error CS0031: O valor de constante "300" não pode ser convertido em "byte"
O que significa: o número escrito não cabe no tipo (um byte vai de 0 a 255; um uint não aceita negativos).
Como consertar: use um tipo maior ou com sinal. Na dúvida, int.
error CS0266: Não é possível converter implicitamente tipo "long" em "int"…
O que significa: um número (ou uma conta) do tipo long foi guardado numa caixa int. Pode ser um literal grande, como 8100000000, ou uma variável long.
Como consertar: troque a caixa para long. Se você tem certeza de que o valor cabe, peça a conversão com (int) (aula Conversão de Tipos).
error CS0266: Não é possível converter implicitamente tipo "int" em "byte"… numa soma de dois byte
byte a = 10; byte b = 20; byte c = a + b; Console.WriteLine(c);
$ dotnet run bytesoma.cs /home/ana/cs/bytesoma.cs(3,10): error CS0266: Não é possível converter implicitamente tipo "int" em "byte". Existe uma conversão explícita (há uma conversão ausente?) Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: uma surpresa do C#: contas com byte, sbyte, short e ushort são sempre feitas em int. a + b é um int, que não entra sozinho num byte.
Como consertar: use int para a conta (int c = a + b;) ou converta o resultado: byte c = (byte)(a + b);.
error CS1021: Constante de integral muito grande
O que significa: o número escrito não cabe nem no ulong.
Como consertar: use Int128 ou BigInteger, ou, se não for um número de verdade (código de barras, número de cartão), guarde como string.
error CS0220: a operação estoura durante a compilação
O que significa: uma conta só com valores fixos, como int.MaxValue + 1, não cabe no tipo.
Como consertar: faça a conta num tipo maior (int.MaxValue + 1L, guardado num long) ou, se a volta é o que você quer, unchecked( … ).
System.OverflowException: Arithmetic operation resulted in an overflow.
O que significa: uma conta dentro de checked (ou com CheckForOverflowUnderflow ligado) passou do limite do tipo. A linha está no fim da mensagem: line 3.
Como consertar: use um tipo maior (long) ou confira os valores antes da conta. Não tire o checked só para o erro sumir: o valor estaria errado do mesmo jeito.
O que significa: estouro silencioso: a conta deu a volta. Muitas vezes a conta foi feita em int e só o resultado ia para um long.
Como consertar: faça pelo menos um número da conta ser long desde o começo (365L, ou uma variável long). Use checked para descobrir onde estoura.
Referência rápida
| Escrita | O que é |
|---|---|
int, long | Os inteiros do dia a dia (4 e 8 bytes). Também byte, sbyte, short, ushort, uint, ulong. |
int.MaxValue, int.MinValue | Maior e menor valor (vale para todos: long.MaxValue, byte.MaxValue…) |
1_000_000 | Separador de dígitos C# 7 |
0xFF, 0b1010 | Hexadecimal (255) e binário (10). Não existe octal: 010 é 10. |
5L, 5U, 5UL | Literal long, uint, ulong |
| sem sufixo | O primeiro que couber: int, uint, long, ulong |
checked(conta), checked { … } | Estouro lança OverflowException |
unchecked(conta) | Estouro dá a volta (o padrão) |
#:property CheckForOverflowUnderflow=true | Liga o checked no programa todo |
Int128, BigInteger | Inteiros maiores que o ulong |
n.ToString("N0"), "X", "B" | Mostrar com separador de milhar, em hexadecimal, em binário |
Exercícios
1. O maior int
Complete para guardar o maior valor que um int aceita:
2. Um milhão legível
Escreva um milhão usando o separador de dígitos entre os milhares:
3. Conferir a conta
Complete para o programa parar com OverflowException se a soma estourar:
4. O que int x = int.MaxValue; x = x + 1; Console.WriteLine(x); mostra?
5. Quanto vale int x = 010; em C#?
6. byte a = 10, b = 20; byte c = a + b; O que acontece?
7. Por que long s = anos * 365 * 24 * 60 * 60; (com int anos = 100) guarda um valor errado?
Missão final: quantos segundos tem um século?
Missão: o programa calcula quantos segundos existem em 100 anos (de 365 dias), mas mostra um número negativo: a conta estoura o int antes de chegar na caixa long. Conserte para mostrar Segundos em 100 anos: 3153600000. Dica: basta mudar o tipo de anos, ou pôr um L num dos números da conta.
int anos = 100;
long segundos = anos * 365 * 24 * 60 * 60;
Console.WriteLine($"Segundos em {anos} anos: {segundos}");
long anos = 100;
long segundos = anos * 365 * 24 * 60 * 60;
Console.WriteLine($"Segundos em {anos} anos: {segundos}");
Dica extra: envolva a conta errada com checked( … ) antes de consertar. O programa para com OverflowException e mostra a linha exata do estouro.
Resumo
- Família:
byte/sbyte(1 byte),short/ushort(2),int/uint(4),long/ulong(8). Na dúvida,int; acima de 2 bilhões,long. int.MaxValue= 2147483647,long.MaxValue≈ 9,2 quintilhões. Os tamanhos são iguais em qualquer computador.- Literais:
1_000_000,0xFF,0b1010, sufixosL,U,UL. Não há octal:010é 10. - Número escrito que não cabe: erro de compilação (
CS0031,CS0266,CS1021,CS0220). - Conta que não cabe: por padrão, dá a volta em silêncio (
int.MaxValue + 1=-2147483648;uint3 - 5 = 4294967294). checkedtransforma o estouro emSystem.OverflowException(código de saída 134);uncheckeddeixa dar a volta.- Uma conta só com
inté feita emint, mesmo que o resultado vá para umlong. Contas combyteeshortsão feitas emint.