Quais são as melhores práticas em relação a não assinados?

39

Eu uso ints não assinados em todos os lugares, e não tenho certeza se devo. Isso pode ser de colunas de id de chave primária de banco de dados a contadores, etc. Se um número nunca deve ser negativo, então sempre usarei um int não assinado.

No entanto, noto pelo código de outro que ninguém mais parece fazer isso. Existe algo crucial que eu estou negligenciando?

Edit: Desde esta questão eu também notei que em C, retornar valores negativos para erros é um lugar-comum ao invés de lançar exceções como em C ++.

    
por wting 01.08.2011 / 12:17
fonte

11 respostas

27

Is there something crucial that I'm overlooking?

Quando os cálculos envolvem tipos assinados e não assinados, bem como tamanhos diferentes, as regras para promoção de tipo podem ser complexas e levar a comportamento inesperado .

Acredito que esta seja a principal razão pela qual o Java omitiu os tipos int não assinados.

    
por 01.08.2011 / 13:02
fonte
17

Acho que Michael tem um ponto válido, mas a razão pela qual todo mundo usa int o tempo todo (especialmente em for (int i = 0; i < max, i++ ) é que aprendemos dessa maneira. Quando cada único exemplo em um livro ' como aprender programação ' usa int em um loop for , muito poucos questionarão essa prática.

A outra razão é que int é 25% menor que uint e todos somos preguiçosos ...; -)

    
por 01.08.2011 / 13:50
fonte
10

Codificar informações de intervalo em tipos é Uma boa coisa. Ele impõe o uso de números razoáveis em tempo de compilação.

Muitas arquiteturas parecem ter instruções especializadas para lidar com int - > float conversões. A conversão de unsigned pode ser mais lenta (um pouquinho) .

    
por 01.08.2011 / 12:44
fonte
8

A mistura de tipos assinados e não assinados pode levá-lo a um mundo de dor. E você não pode usar todos os tipos não assinados porque encontrará coisas que possuem um intervalo válido que inclui números negativos ou que precisam de um valor para indicar um erro e -1 é mais natural. Portanto, o resultado final é que muitos programadores usam todos os tipos inteiros assinados.

    
por 13.08.2011 / 16:20
fonte
6

Para mim, tipos são muito sobre comunicação. Usando explicitamente um int não assinado, você me diz que os valores assinados não são válidos. Isso me permite adicionar algumas informações ao ler seu código, além do nome da variável. Idealmente, um tipo não-anônimo me diria mais, mas me dá mais informações do que se você tivesse usado ints em todos os lugares.

Infelizmente nem todos estão muito conscientes sobre o que seu código comunica, e essa é provavelmente a razão pela qual você vê ints em todos os lugares mesmo que os valores sejam pelo menos não assinados.

    
por 01.08.2011 / 14:03
fonte
5

Eu uso unsigned int em C ++ para índices de array, principalmente, e para qualquer contador que comece com 0. Eu acho que é bom dizer explicitamente "essa variável não pode ser negativa".

    
por 01.08.2011 / 12:52
fonte
3

Você deve se preocupar com isso quando estiver lidando com um número inteiro que pode realmente se aproximar ou exceder os limites de um int assinado. Como o máximo positivo de um inteiro de 32 bits é 2.147.483.647, você deve usar um int não assinado se souber que a) nunca será negativo e b) pode chegar a 2.147.483.648. Na maioria dos casos, incluindo chaves de banco de dados e contadores, eu nunca vou abordar esses tipos de números, então não me preocupo preocupando-me em saber se o bit de sinal é usado para um valor numérico ou para indicar o sinal.

Eu diria: use int a menos que você saiba que precisa de um int não assinado.

    
por 01.08.2011 / 12:25
fonte
3

É uma troca entre simplicidade e confiabilidade. Quanto mais bugs forem detectados em tempo de compilação, mais confiável será o software. Diferentes pessoas e organizações estão em diferentes pontos ao longo desse espectro.

Se você já fez alguma programação de alta confiabilidade em Ada, você até usa tipos diferentes para variáveis como distância em pés x distância em metros, e o compilador sinaliza se você acidentalmente atribui um ao outro. Isso é perfeito para programar um míssil guiado, mas é um exagero (trocadilho intencional) se você está validando um formulário da web. Não há necessariamente nada de errado com qualquer forma, desde que ela atenda aos requisitos.

    
por 01.08.2011 / 19:05
fonte
2
Estou inclinado a concordar com o raciocínio de Joel Etherton, mas chego à conclusão oposta. Do jeito que eu vejo, mesmo que você saiba que é improvável que os números cheguem aos limites de um tipo assinado, se você sabe que números negativos não acontecerão, então há muito pouca razão para usar a variante assinada de um tipo.

Pelo mesmo motivo, em algumas instâncias selecionadas, usei BIGINT (inteiro de 64 bits) em vez de INTEGER (inteiro de 32 bits) nas tabelas do SQL Server. A probabilidade de os dados atingirem o limite de 32 bits dentro de um período de tempo razoável é minúscula, mas se isso acontecer, as consequências em algumas situações podem ser bastante devastadoras. Apenas certifique-se de mapear os tipos entre os idiomas corretamente, ou você acabará com estranhezas interessantes muito longe daqui ...

Dito isso, algumas coisas, como valores de chaves primárias do banco de dados, assinadas ou não assinadas realmente não importam, porque a menos que você esteja reparando manualmente dados quebrados ou algo assim, você nunca estará lidando com o valor diretamente ; é um identificador, nada mais. Nesses casos, a consistência é provavelmente mais importante do que a escolha exata da assinatura. Caso contrário, você acaba com algumas colunas de chave estrangeiras que são assinadas e outras que não são assinadas, sem nenhum padrão aparente para ela - ou aquela esquisitice interessante novamente.

    
por 01.08.2011 / 16:35
fonte
1

Eu recomendaria que os contextos de armazenamento de dados e intercâmbio de dados restritos a espaço externo geralmente usem tipos assinados. Na maioria dos casos em que um inteiro assinado de 32 bits seria muito pequeno, mas um valor sem sinal de 32 bits seria suficiente para hoje, não demorará muito para que o valor não assinado de 32 bits também seja grande o suficiente.

Os tempos primários que se deve usar tipos não assinados são quando um está reunindo vários valores em um maior (por exemplo, convertendo quatro bytes em um número de 32 bits) ou decompõe valores maiores em números menores (por exemplo, armazenando um número de 32 bits) como quatro bytes), ou quando se tem uma quantidade que se espera que seja "repassada" periodicamente e é preciso lidar com ela (pense em um medidor de energia residencial; a maioria deles tem dígitos suficientes para garantir que eles não rolem entre as leituras, se forem lidas três vezes por ano, mas não o suficiente para garantir que elas não rolem durante a vida útil do medidor). Tipos não assinados muitas vezes têm "estranheza" suficiente para serem usados apenas nos casos em que sua semântica é necessária.

    
por 13.07.2012 / 23:42
fonte
0

Eu uso ints não assinados para tornar meu código e sua intenção mais claros. Uma coisa que faço para me proteger contra conversões implícitas inesperadas ao fazer aritmética com tipos assinados e não assinados é usar um short não assinado (normalmente 2 bytes) para minhas variáveis não assinadas. Isso é eficaz por algumas razões:

  • Quando você faz aritmética com suas variáveis curtas não assinadas e literais (que são do tipo int) ou variáveis do tipo int, isso garante que a variável não assinada sempre será promovida a um int antes de avaliar a expressão, pois int sempre tem uma maior rank do que curto. Isso evita qualquer comportamento inesperado que faça aritmética com tipos assinados e não assinados, assumindo que o resultado da expressão se encaixa em um int assinado, é claro.
  • Na maior parte do tempo, as variáveis não assinadas que você está usando não excederão o valor máximo de um byte de 2 bytes curto (65.535)

O princípio geral é que o tipo de variáveis não assinadas deve ter uma classificação mais baixa do que o tipo das variáveis assinadas para garantir a promoção ao tipo assinado. Então você não terá nenhum comportamento inesperado de estouro. Obviamente, você não pode garantir isso o tempo todo, mas (a maioria) é viável para garantir isso.

Por exemplo, recentemente eu tive algum loop para algo assim:

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

O literal '2' é do tipo int. Se eu fosse um int não assinado em vez de um curto não assinado, então na subexpressão (i-2), 2 seria promovido para um int não assinado (já que unsigned int tem uma prioridade mais alta que o int assinado). Se i = 0, então a sub-expressão é igual a (0u-2u) = algum valor massivo devido ao estouro. Mesma idéia com i = 1. No entanto, como i é um curto não assinado, ele é promovido para o mesmo tipo do literal '2', que é assinado int e tudo funciona bem.

Para segurança adicional: no caso raro em que a arquitetura que você está implementando faz com que int seja de 2 bytes, isso pode fazer com que ambos os operandos na expressão aritmética sejam promovidos para unsigned int no caso em que a variável curta não assinada não se encaixa no int de 2 bytes assinado, o último dos quais tem um valor máximo de 32.767 < 65,535. (Veja link para mais detalhes). Para proteger-se contra isso, você pode simplesmente adicionar um static_assert ao seu programa da seguinte forma:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

e não irá compilar em arquiteturas onde int é de 2 bytes.

    
por 12.12.2018 / 14:34
fonte