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"?

Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top