O que deve acontecer com a negação de um size_t (ou seja `-sizeof (foo struct)`))?
Pergunta
Estou lidando com algum código de trabalho que inclui uma expressão da forma
-(sizeof(struct foo))
i. a negação de uma size_t
, e estou claro sobre o que os padrões C e C ++ requerem de compiladores quando vêem isso. Especificamente, de olhar por aqui e em outros lugares, sizeof
retorna um valor inteiro sem sinal do tipo size_t
. Não consigo encontrar qualquer referência clara para o comportamento especificado quando negando um inteiro sem sinal. Existe alguma, e se assim for, o que é?
Edit: Ok, então existem algumas boas respostas sobre aritmética em tipos não assinados, mas não é claro que este é de fato tal. Quando este nega, é que operando em um inteiro sem sinal, ou a conversão para um tipo assinado e fazer alguma coisa com isso? É o comportamento que esperar dos padrões "Imagine que é o número negativo de magnitude semelhante e, em seguida, aplicar as regras 'estouro' para valores não assinados"?
Solução
Tanto a ISO C e normas ISO C ++ garantir que a aritmética não assinada é modulo 2 n - ou seja, para qualquer estouro ou estouro negativo, ele é "moldada". Por ISO C ++, isto é 3.9.1 [basic.fundamental] / 4:
Inteiros sem sinal,
unsigned
declarada, deve obedecer às leis de módulo aritmética 2 n , onde n é o número de bits na representação de valor desse tamanho particular de inteiro. 41...
41) Isto implica que a aritmética sem sinal não transborde, porque um resultado que não pode ser representado pelo inteiro sem sinal resultante tipo é reduzido o número de módulo que é maior do que o maior valor que pode ser representado por um número inteiro não assinado resultante tipo.
Para ISO C (99), isto é 6.2.5 / 9:
Um cálculo envolvendo operandos sem assinatura nunca pode transbordar, porque um resultado que não pode ser representada pelo tipo inteiro não assinado resultante é reduzida modulo do número que é maior do que o maior valor que pode ser representado pelo tipo resultante.
O que significa que o resultado é garantido para ser o mesmo que SIZE_MAX - (sizeof(struct foo)) + 1
.
Na ISO 14882: 2003 5.3.1.7:
[...] O negativo de um sem assinatura a quantidade é computada subtraindo seu valor de 2 n , onde n é o número de bits a promovidas operando. O tipo de o resultado é o tipo do promoveu operando.
Outras dicas
http://msdn.microsoft.com /en-us/library/wxxx8d2t%28VS.80%29.aspx
negação Unário de quantidades não assinados é realizada subtraindo o valor do operando a partir de 2 n , onde n é o número de bits de um objecto do dado não assinado tipo. (Microsoft C ++ roda em processadores que utilizam Complemento de aritmética. Por outro processadores, o algoritmo para a negação pode diferir.)
Em outras palavras, o comportamento exato será uma arquitetura específica. Se eu fosse você, eu iria evitar o uso de uma construção tão estranho.
negando um número sem sinal é útil para propagar a LSB em toda a palavra para formar uma máscara para operações bit a bit subseqüentes.
A única coisa que eu posso pensar é tão errado que faz minha cabeça doer ...
size_t size_of_stuff = sizeof(stuff);
if(I want to subtract the size)
size_of_stuff = -sizeof(stuff);
size_t total_size = size_of_stuff + other_sizes;
Overflow é uma característica!
A partir da atual C ++ projecto de norma , seção 5.3.1 frase 8:
O operando do operador
-
unário terá Tipo de aritmética ou enumeração eo resultado é a negação de seu operando. promoção integral é realizada em operandos integrais ou enumeração. A negativo de uma quantidade unsigned é calculado subtraindo-se o seu valor a partir de 2 n , onde n é o número de bits no operando promovido. O tipo do resultado é o tipo do operando promovido.
Assim, a expressão resultante é ainda não assinado e calculado conforme descrito.
Usuário @outis mencionei isso em um comentário, mas eu vou colocá-lo em uma resposta desde outis não. Se outis volta e respostas, eu vou aceitar que, em vez.
size_t
é um tipo inteiro sem sinal definido pela implementação.
A negação de um valor size_t
provavelmente lhe dá um resultado do tipo size_t
com o comportamento habitual modulo não assinado. Por exemplo, assumindo que size_t
é de 32 bits e, em seguida, sizeof(struct foo) == 4
-sizeof(struct foo) == 4294967292
, ou 2 32 -4.
exceto por uma coisa: O operador -
unário aplica o inteiro promoções (C) ou promoções integrais (C ++) (eles são essencialmente a mesma coisa) à sua operando. Se size_t
é pelo menos tão grande como int
, em seguida, esta promoção não faz nada, eo resultado é do tipo size_t
. Mas se int
é maior do que size_t
, de modo que INT_MAX >= SIZE_MAX
, então o operando de -
é "promovido" a partir size_t
para int
. Nesse caso improvável, -sizeof(struct foo) == -4
.
Se você atribuir esse valor de volta para um objeto size_t
, então ele vai estar de volta convertido para size_t
, produzindo o valor SIZE_MAX-4
que você esperaria. Mas sem essa conversão de um, você pode obter alguns resultados surpreendentes.
Agora que eu nunca ouvi falar de uma implementação onde size_t
é mais estreito do que int
, então você não é provável a correr para isso. Mas aqui está um caso de teste, usando unsigned short
como um stand-in para o tipo size_t
estreita hipotética, que ilustra o potencial problema:
#include <iostream>
int main() {
typedef unsigned short tiny_size_t;
struct foo { char data[4]; };
tiny_size_t sizeof_foo = sizeof (foo);
std::cout << "sizeof (foo) = " << sizeof (foo) << "\n";
std::cout << "-sizeof (foo) = " << -sizeof (foo) << "\n";
std::cout << "sizeof_foo = " << sizeof_foo << "\n";
std::cout << "-sizeof_foo = " << -sizeof_foo << "\n";
}
A saída no meu sistema (que tem short
de 16 bits, 32 bits int
, e 64 bits size_t
) é:
sizeof (foo) = 4
-sizeof (foo) = 18446744073709551612
sizeof_foo = 4
-sizeof_foo = -4