Resposta curta
Você i
será convertido em um número inteiro não assinado adicionando UINT_MAX + 1
, a adição será realizada com os valores não assinados, resultando em um grande result
(dependendo dos valores de u
e i
).
Resposta longa
De acordo com o padrão C99:
6.3.1.8 Conversões aritméticas usuais
- Se os dois operandos tiverem o mesmo tipo, nenhuma conversão adicional será necessária.
- Caso contrário, se os dois operandos tiverem tipos inteiros assinados ou os tipos inteiros não assinados, o operando com o tipo de classificação de conversão de número menor será convertido no tipo de operando com maior classificação.
- Caso contrário, se o operando que possui o tipo inteiro não assinado tiver uma classificação maior ou igual à classificação do tipo do outro operando, o operando com o tipo inteiro assinado será convertido no tipo de operando com o tipo inteiro não assinado.
- Caso contrário, se o tipo do operando com o tipo inteiro assinado puder representar todos os valores do tipo do operando com o tipo inteiro não assinado, o operando com o tipo inteiro não assinado será convertido no tipo do operando com o tipo inteiro assinado.
- Caso contrário, os dois operandos serão convertidos no tipo inteiro não assinado correspondente ao tipo do operando com o tipo inteiro assinado.
No seu caso, temos um int ( u
) não assinado e um int ( ) assinado i
. Referindo-se a (3) acima, como os dois operandos têm a mesma classificação, você i
precisará ser convertido em um número inteiro não assinado.
6.3.1.3 Inteiros assinados e não assinados
- Quando um valor com o tipo inteiro é convertido em outro tipo de número diferente de _Bool, se o valor puder ser representado pelo novo tipo, ele permanece inalterado.
- Caso contrário, se o novo tipo não estiver assinado, o valor será convertido adicionando ou subtraindo repetidamente um mais que o valor máximo que pode ser representado no novo tipo até que o valor esteja no intervalo do novo tipo.
- Caso contrário, o novo tipo é assinado e o valor não pode ser representado nele; o resultado é definido pela implementação ou um sinal definido pela implementação é gerado.
Agora precisamos nos referir a (2) acima. Você i
será convertido em um valor não assinado adicionando UINT_MAX + 1
. Portanto, o resultado dependerá de como UINT_MAX
é definido em sua implementação. Será grande, mas não excederá, porque:
6.2.5 (9)
Uma computação envolvendo operandos não assinados nunca pode exceder, porque um resultado que não pode ser representado pelo tipo inteiro não assinado resultante é reduzido por módulo, o número que é um maior que o maior valor que pode ser representado pelo tipo resultante.
Bônus: Conversão Aritmética Semi-WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
Você pode usar este link para tentar isso online: https://repl.it/repls/QuickWhimsicalBytes
Bônus: efeito colateral da conversão aritmética
Regras de conversão aritmética podem ser usadas para obter o valor UINT_MAX
inicializando um valor não assinado para -1
, ou seja:
unsigned int umax = -1; // umax set to UINT_MAX
É garantido que ele seja portátil, independentemente da representação numérica assinada do sistema, devido às regras de conversão descritas acima. Consulte esta pergunta do SO para obter mais informações: É seguro usar -1 para definir todos os bits como verdadeiros?
A conversão de assinado para não assinado não necessariamente copia ou reinterpreta a representação do valor assinado. Citando o padrão C (C99 6.3.1.3):
Para a representação de complemento dos dois que é quase universal hoje em dia, as regras correspondem à reinterpretação dos bits. Mas para outras representações (sinal e magnitude ou complemento de um), a implementação C ainda deve providenciar o mesmo resultado, o que significa que a conversão não pode apenas copiar os bits. Por exemplo, (não assinado) -1 == UINT_MAX, independentemente da representação.
Em geral, as conversões em C são definidas para operar em valores, não em representações.
Para responder à pergunta original:
O valor de i é convertido em int sem sinal, produzindo
UINT_MAX + 1 - 5678
. Este valor é então adicionado ao valor não assinado 1234, produzindoUINT_MAX + 1 - 4444
.(Diferentemente do estouro não assinado, o estouro assinado invoca um comportamento indefinido. A envolvência é comum, mas não é garantida pelo padrão C - e as otimizações do compilador podem causar estragos no código que faz suposições injustificadas.)
fonte
Referindo-se à Bíblia :
fonte
Quando uma variável não assinada e uma assinada são adicionadas (ou qualquer operação binária), ambas são implicitamente convertidas em não assinadas, o que, nesse caso, resultaria em um resultado enorme.
Portanto, é seguro no sentido de que o resultado pode ser enorme e errado, mas nunca falha.
fonte
Ao converter de assinado para não assinado, há duas possibilidades. Os números originalmente positivos permanecem (ou são interpretados como) o mesmo valor. O número originalmente negativo agora será interpretado como um número positivo maior.
fonte
Como foi respondido anteriormente, você pode alternar entre assinado e não assinado sem problemas. O caso de borda para números inteiros assinados é -1 (0xFFFFFFFF). Tente adicionar e subtrair isso e você descobrirá que pode rejeitar e fazer com que esteja correto.
No entanto, se você for transmitir de um lado para o outro, eu recomendo fortemente nomear suas variáveis para que fique claro que tipo elas são, por exemplo:
É muito fácil se distrair com questões mais importantes e esquecer qual variável é de que tipo se elas são nomeadas sem uma dica. Você não deseja converter para um sinal não assinado e depois usá-lo como um índice de matriz.
fonte
Eu serei convertido em um número inteiro sem sinal.
Seguro no sentido de estar bem definido, sim (consulte https://stackoverflow.com/a/50632/5083516 ).
As regras são escritas em fala padrão de difícil leitura, mas essencialmente qualquer que seja a representação usada no número inteiro assinado, o número inteiro não assinado conterá uma representação complementar do número 2.
A adição, subtração e multiplicação funcionarão corretamente nesses números, resultando em outro número inteiro não assinado contendo um número de complemento de dois representando o "resultado real".
divisão e conversão para tipos inteiros não assinados maiores terão resultados bem definidos, mas esses resultados não serão as representações complementares de 2 do "resultado real".
Enquanto as conversões de assinado para não assinado são definidas pelo padrão, o reverso é definido pela implementação, tanto gcc quanto msvc definem a conversão de forma que você obtenha o "resultado real" ao converter o número de complemento de 2s armazenado em um número inteiro não assinado em um número inteiro assinado. . Espero que você encontre apenas outro comportamento em sistemas obscuros que não usem o complemento 2 para números inteiros assinados.
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx
fonte
Horrible Answers Galore
Ozgur Ozcitak
Isso está completamente errado.
Mats Fredriksson
Isso também está errado. As entradas não assinadas podem ser promovidas para entradas, se tiverem precisão igual devido aos bits de preenchimento no tipo não assinado.
smh
Errado. Talvez sim e talvez não.
Errado. É um comportamento indefinido se causar excesso ou o valor for preservado.
Anônimo
Errado. Depende da precisão de um int em relação a um int não assinado.
Preço de Taylor
Errado. Tentar armazenar um valor fora do intervalo de um número inteiro assinado resulta em um comportamento indefinido.
Agora posso finalmente responder à pergunta.
Se a precisão de int for igual a int sem sinal, u será promovido a um int assinado e você obterá o valor -4444 da expressão (u + i). Agora, se eu e eu tivermos outros valores, você pode ter um comportamento indefinido e indefinido, mas com esses números exatos você obterá -4444 [1] . Este valor terá o tipo int. Mas você está tentando armazenar esse valor em um int não assinado, para que seja convertido em um int não assinado e o valor que o resultado acabará tendo (UINT_MAX + 1) - 4444.
Se a precisão de unsigned int for maior que a de um int, o int assinado será promovido a um int não assinado, produzindo o valor (UINT_MAX + 1) - 5678 que será adicionado ao outro int não assinado 1234. Caso eu e eu tenhamos outros valores, que fazem a expressão ficar fora do intervalo {0..UINT_MAX}, o valor (UINT_MAX + 1) será adicionado ou subtraído até que o resultado fique dentro do intervalo {0..UINT_MAX) e nenhum comportamento indefinido ocorrerá .
O que é precisão?
Os números inteiros têm bits de preenchimento, bits de sinal e bits de valor. Inteiros não assinados não têm um sinal de bit, obviamente. Caracteres não assinados têm ainda a garantia de não ter bits de preenchimento. O número de bits de valores que um número inteiro tem é quanta precisão ele tem.
[Pegadinhas]
O tamanho macro da macro sozinho não pode ser usado para determinar a precisão de um número inteiro se houver bits de preenchimento. E o tamanho de um byte não precisa ser um octeto (oito bits), conforme definido por C99.
[1] O estouro pode ocorrer em um dos dois pontos. Antes da adição (durante a promoção) - quando você tem um int não assinado que é muito grande para caber dentro de um int. O estouro também pode ocorrer após a adição, mesmo que o int não assinado esteja dentro do intervalo de um int, após a adição o resultado ainda pode estourar.
fonte
int
conversão paraunsigned int
quando as conversões aritméticas comuns se aplicam.int
ouunsigned int
para um desses tipos em que algo do tipounsigned int
ouint
é esperado. O "ou igual" foi adicionado no TC2 para permitir que tipos enumerados de classificação de conversão sejam iguaisint
ouunsigned int
sejam convertidos em um desses tipos. Nunca se pretendeu que a promoção descrita se convertesse entreunsigned int
eint
. A determinação de tipo comum entreunsigned int
eint
ainda é governada por 6.3.1.8, mesmo após o TC2.