Cosa dovrebbe succedere alla negazione di un size_t (vale a dire `-sizeof (struct foo)`))?
Domanda
ho a che fare con un po 'di codice sul posto di lavoro che include un'espressione della forma
-(sizeof(struct foo))
vale a dire. la negazione di un size_t
, e sono poco chiare su ciò che gli standard C e C ++ richiedono di compilatori quando vedono questo. In particolare, da guardarsi intorno qui e altrove, sizeof
restituisce un valore intero senza segno di tipo size_t
. Non riesco a trovare alcun riferimento chiaro per il comportamento specificato quando negando un intero senza segno. C'è qualche, e se sì, che cosa è?
Edit: Ok, quindi ci sono alcune buone risposte per quanto riguarda l'aritmetica sui tipi senza segno, ma non è chiaro che questo è in realtà tale. Quando questo nega, è vero opera su un intero senza segno, o la conversione a un tipo firmato e fare qualcosa con questo? È il comportamento di aspettarsi dagli standard "immaginare è il numero negativo di grandezza simile e quindi applicare le regole 'overflow' per valori senza segno"?
Soluzione
Sia standard ISO C e ISO C ++ garantire che l'aritmetica senza segno è modulo 2 n - cioè per ogni overflow o underflow, si "avvolge". Per ISO C ++, questo è 3.9.1 [basic.fundamental] / 4:
interi senza segno, dichiarato
unsigned
, devono rispettare le leggi dell'aritmetica modulo 2 n dove n è il numero di bit nella rappresentazione valore di quella particolare dimensione di interi. 41...
41) Ciò implica che l'aritmetica senza segno non trabocchi perché un risultato che non può essere rappresentato dal numero intero senza segno risultante tipo viene ridotto modulo il numero che è maggiore di uno rispetto al massimo valore che può essere rappresentato dal numero intero senza segno risultante tipo.
Per ISO C (99), è 6.2.5 / 9:
Un calcolo coinvolge operandi unsigned può mai troppopieno, perché un risultato che non può essere rappresentato dal tipo intero senza segno risultante viene ridotta modulo il numero che è maggiore di uno rispetto al massimo valore che può essere rappresentato dal tipo risultante.
Il che significa che il risultato è garantito per essere lo stesso di SIZE_MAX - (sizeof(struct foo)) + 1
.
In ISO 14882: 2003 5.3.1.7:
[...] Il negativo di un unsigned quantità viene calcolata sottraendo il suo valore da 2 n , dove n è il numero di bit in l'operando moted pro-. Il tipo di il risultato è il tipo di promozione operando.
Altri suggerimenti
http://msdn.microsoft.com /en-us/library/wxxx8d2t%28VS.80%29.aspx
negazione unaria di quantità non firmati viene eseguita sottraendo il valore dell'operando da 2 n , dove n è il numero di bit in un oggetto della dato tipo senza segno. (Microsoft C ++ gira su processori che utilizzano complemento a due aritmetica. D'altra processori, l'algoritmo di negazione possono differire.)
In altre parole, il comportamento esatto sarà specifico architettura. Se fossi in te, vorrei evitare di utilizzare un costrutto così strana.
negando un numero senza segno è utile per propagare la lsb attraverso la parola per formare una maschera per le successive operazioni bit per bit.
L'unica cosa che posso pensare è così sbagliato mi fa male la testa ...
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 è una caratteristica!
corrente C ++ progetto di norma , sezione 5.3.1 frase 8:
L'operando dell'operatore unario
-
deve avere l'aritmetica o l'enumerazione tipo e il risultato è la negazione del suo operando. promozione integrale viene eseguita su operandi integrali o enumerazione. Il negativo di una quantità senza segno viene calcolato sottraendo il valore tra 2 n , dove n è il numero di bit nella operando promosso. Il tipo del risultato è il tipo dell'operando promosso.
Quindi, l'espressione risultante è ancora unsigned e calcolato come descritto.
@outis utente menzionato questo in un commento, ma ho intenzione di metterlo in una risposta dal Outis non ha fatto. Se Outis torna e risposte, io accettare che, invece.
size_t
è di tipo intero senza segno implementazione definita.
La negazione di un valore size_t
probabilmente si dà un risultato di tipo size_t
con il solito comportamento modulo non firmato. Ad esempio, supponendo che size_t
è di 32 bit e sizeof(struct foo) == 4
, poi -sizeof(struct foo) == 4294967292
o 2 32 -4.
Tranne che per una cosa: L'operatore -
unario si applica il promozioni interi (C) o promozioni integrali (C ++) (sono essenzialmente la stessa cosa) alla sua operando. Se size_t
è almeno largo quanto int
, allora questa promozione non fa nulla, e il risultato è di tipo size_t
. Ma se int
è più ampio di size_t
, in modo che INT_MAX >= SIZE_MAX
, allora l'operando di -
viene "promosso" dal size_t
a int
. In tal caso improbabile, -sizeof(struct foo) == -4
.
Se si assegna il valore di nuovo ad un oggetto size_t
, allora sarà riconvertito in size_t
, ottenendo il valore SIZE_MAX-4
che ci si aspetterebbe. Ma senza una tale conversione, è possibile ottenere alcuni risultati sorprendenti.
Ora non ho mai sentito parlare di un'implementazione in cui size_t
è più stretto int
, quindi non sei probabile imbattersi in questo. Ma qui è un banco di prova, utilizzando unsigned short
come uno stand-in per la stretta tipo size_t
ipotetica, che illustra il potenziale 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";
}
L'output sul mio sistema (che ha 16 bit short
, 32 bit int
, e 64 bit size_t
) è:
sizeof (foo) = 4
-sizeof (foo) = 18446744073709551612
sizeof_foo = 4
-sizeof_foo = -4