È sicuro usare -1 per impostare tutti i bit su true?


132

Ho visto questo schema usato molto in C & C ++.

unsigned int flags = -1;  // all bits are true

È un buon modo portatile per farlo? O sta usando 0xffffffffo ~0meglio?


1
Non capisco, puoi per favore spiegare?
corazza,

8
Penso che il significato del codice sia chiaro è la domanda più importante. Anche se -1funzionerà sempre, il fatto che sia necessario un commento dopo che mostra che non è un codice chiaro. Se la variabile è pensata per essere una raccolta di flag, perché assegnarle un numero intero? Il suo tipo può essere un numero intero, ma certamente non è semanticamente un numero intero. Non lo aumenterai mai né lo moltiplichi. Quindi userei 0xffffffffnon per portabilità o correttezza, ma per chiarezza.
Cam Jackson

@CamJackson il commento non lo è e chiunque scriva codice C potrebbe avere familiarità con il modo in cui i valori sono rappresentati.
Miglia rotta

Inizialmente la domanda era correttamente etichettata come C e C ++. Le lingue potrebbero essere divergenti in quanto C ++ ha una proposta per richiedere il complemento a due. Detto questo, non cambia il fatto che -1rimanga una soluzione portatile e retrocompatibile per entrambe le lingue, ma potrebbe influenzare alcuni dei ragionamenti in altre risposte.
Adrian McCarthy,

Risposte:


154

Ti consiglio di farlo esattamente come hai mostrato, poiché è il più diretto. Inizializza a -1cui funzionerà sempre , indipendentemente dalla rappresentazione del segno reale, mentre ~a volte avrà un comportamento sorprendente perché dovrai avere il giusto tipo di operando. Solo allora otterrai il valore più alto di un unsignedtipo.

Per un esempio di una possibile sorpresa, considera questo:

unsigned long a = ~0u;

Non memorizzerà necessariamente un modello con tutti i bit 1 in a. Ma prima creerà un modello con tutti i bit 1 in un unsigned int, quindi lo assegnerà a a. Quello che succede quando unsigned longha più bit è che non tutti sono 1.

E considera questo, che fallirà nella rappresentazione del complemento di un non-due:

unsigned int a = ~0; // Should have done ~0u !

La ragione di ciò è che ~0deve invertire tutti i bit. Inversione che produrrà -1su una macchina a complemento a due (che è il valore di cui abbiamo bisogno!), Ma non produrrà -1su un'altra rappresentazione. Sulla macchina di un complemento, produce zero. Pertanto, su una macchina del complemento di uno, quanto sopra verrà inizializzato aa zero.

La cosa che dovresti capire è che si tratta di valori, non di bit. La variabile è inizializzata con un valore . Se nell'inizializzatore si modificano i bit della variabile utilizzata per l'inizializzazione, il valore verrà generato in base a tali bit. Il valore necessario, per inizializzare aal valore più alto possibile, è -1o UINT_MAX. Il secondo dipenderà dal tipo di a- dovrai usare ULONG_MAXper un unsigned long. Tuttavia, il primo non dipenderà dal suo tipo ed è un bel modo per ottenere il valore più alto.

Stiamo Non parlando di se -1ha tutti i bit uno (non sempre ha). E non stiamo parlando del fatto che abbia tutti i bit uno (ovviamente ce l'~0 ha).

Ma quello di cui stiamo parlando è il risultato della flagsvariabile inizializzata . E per questo, funzionerà solo-1 con ogni tipo e macchina.


9
perché è garantito che -1 sia convertito in tutti? È garantito dallo standard?
jalf

9
la conversione che si verifica è che aggiunge ripetutamente uno in più di ULONG_MAX fino a quando non rientra nell'intervallo (6.3.1.3 nella bozza C TC2). In C ++ è lo stesso, basta usare un altro modo per formalizzarlo (modulo 2 ^ n). Tutto dipende dalle relazioni matematiche.
Johannes Schaub - litb

6
@litb: il casting -1 è certamente un buon modo per ottenere valori massimi senza segno, ma non è veramente descrittivo; questo è il motivo per cui esistono le costanti _MAX (SIZE_MAX è stato aggiunto in C99); certo, la versione C ++ numeric_limits<size_t>::max()è un po 'prolissa, ma lo è anche il cast ...
Christoph,

11
"Non stiamo parlando del fatto che -1 abbia tutti i bit uno (non sempre ha). E non stiamo parlando del fatto che ~ 0 abbia tutti i bit uno (ovviamente ha)." - whaat ??? Pensavo che il punto fosse quello di impostare tutti i bit su 1. Ecco come funzionano le bandiere..no ?? Guardi i pezzi . A chi importa del valore?
mpen

14
@Segna l'interrogatore. Chiede "È sicuro usare -1 per impostare tutti i bit su true". Questo non chiede su quali bit -1sia rappresentato, né chiede quali bit ~0ha. Potremmo non preoccuparci dei valori, ma il compilatore lo fa. Non possiamo ignorare il fatto che le operazioni funzionano con e in base ai valori. Il valore di ~0potrebbe non essere -1, ma questo è il valore che ti serve. Vedi la mia risposta e il riepilogo di @ Dingo.
Johannes Schaub - litb

49
  • unsigned int flags = -1; è portatile.
  • unsigned int flags = ~0; non è portatile perché si basa su una rappresentazione a due complementi.
  • unsigned int flags = 0xffffffff; non è portatile perché presuppone ints a 32 bit.

Se si desidera impostare tutti i bit in un modo garantito dallo standard C, utilizzare il primo.


10
In che modo ~ 0 (ovvero l'operatore del complemento) si affida alla rappresentazione del complemento a due?
Drew Hall,

11
Hai questo all'indietro. L'impostazione flag su -1 che si basa su una rappresentazione a due complementi. In una rappresentazione segno + magnitudine meno uno ha solo due bit impostati: il bit segno e il bit meno significativo della grandezza.
Stephen C. Steel,

15
Lo standard C richiede che il valore int di zero abbia il suo bit di segno e tutti i bit di valore siano zero. Dopo il complemento, tutti quei pezzi sono uno. I valori di un int con tutti i bit impostati sono: Sign-and-magnitude: INT_MIN Complemento di uno: -0 Complemento di due: -1 Quindi l'istruzione "unsigned int flags = ~ 0;" assegnerà qualunque valore sopra corrisponda alla rappresentazione intera della piattaforma. Ma il "-1" del complemento a due è l'unico che imposta tutti i bit di flag su uno.
Dingo,

9
@Stephen: concordato sulla rappresentazione. Ma quando un valore int viene assegnato a un int senza segno, il non firmato non ottiene il suo valore adottando la rappresentazione interna del valore int (tranne nei sistemi a complemento a due in cui generalmente funziona). Tutti i valori assegnati a int senza segno sono modulo (UINT_MAX + 1), quindi l'assegnazione di -1 funziona indipendentemente dalla rappresentazione interna.
Dingo,

20
@Mark: stai confondendo due operazioni. ~0fornisce un intvalore con tutti i bit impostati, ovviamente. Ma l'assegnazione di an inta an unsigned intnon comporta necessariamente che int senza segno abbia lo stesso modello di bit del modello di bit con segno. Solo con una rappresentazione del complemento a 2 è sempre così. Su un complemento di 1 o sulla rappresentazione della grandezza del segno, l'assegnazione di un intvalore negativo a un unsigned intrisultato in un modello di bit diverso. Questo perché lo standard C ++ definisce la conversione firmata -> non firmata come valore modulo-uguale, non con gli stessi bit.
Steve Jessop,

25

Francamente penso che tutte le fff siano più leggibili. Per quanto riguarda il commento che è un antipattern, se ti interessa davvero che tutti i bit siano impostati / cancellati, direi che probabilmente ti trovi in ​​una situazione in cui ti preoccupi comunque della dimensione della variabile, che richiederebbe qualcosa come boost :: uint16_t, ecc.


Ci sono un certo numero di casi in cui non ti interessa molto, ma sono rari. Ad esempio, algoritmi che funzionano su set di dati di N bit suddividendoli in blocchi di dimensioni di bit (senza segno) * CHAR_BIT ciascuno.
MSalters

2
+1. Anche se la dimensione del tipo di dati è maggiore del numero di F (ovvero, non hai impostato tutti i bit su true), poiché stai impostando esplicitamente il valore, sei almeno consapevole di quali bit sono "sicuri" da usare "..
mpen

17

Un modo per evitare i problemi menzionati è semplicemente fare:

unsigned int flags = 0;
flags = ~flags;

Portatile e al punto.


2
Ma poi perdi la possibilità di dichiarare flagscome const.
David Stone,

1
@DavidStoneunsigned int const flags = ~0u;

@Zoidberg '- Che non funziona su sistemi diversi dal complemento a due. Ad esempio, su un sistema di grandezza dei segni, ~0è un numero intero con tutti i bit impostati su 1, ma quando lo si assegna intalla unsignedvariabile flags, si esegue una conversione del valore da -2**31(assumendo un 32 bit int) a (-2**31 % 2**32) == 2**31, che è un numero intero con tutti i bit tranne il primo impostato su 1.
David Stone,

Questo è anche un motivo per cui questa risposta è pericolosa. Sembra che la risposta di @Zoidberg sia identica, ma in realtà non lo è. Tuttavia, come persona che legge il codice, dovrei pensarci per capire perché hai preso due passaggi per inizializzarlo e possibilmente essere tentato di cambiarlo in un solo passaggio.
David Stone,

2
Ah sì, non ho notato il usuffisso nella tua risposta. Ovviamente funzionerebbe, ma ha ancora il problema di specificare due volte il tipo di dati che usi ( unsignede non di dimensioni maggiori), il che potrebbe causare errori. Tuttavia, è probabile che l'errore si presenti se l'assegnazione e la dichiarazione della variabile iniziale sono più distanti.
David Stone,

13

Non sono sicuro che usare un int senza segno per le bandiere sia una buona idea in primo luogo in C ++. Che dire di bitset e simili?

std::numeric_limit<unsigned int>::max()è migliore perché 0xffffffffpresuppone che int senza segno sia un numero intero a 32 bit.


Mi piace questo a causa del suo standard, ma è troppo prolisso e ti fa dichiarare il tipo due volte. L'uso di ~ 0 è probabilmente più sicuro poiché 0 può essere qualsiasi tipo intero. (Anche se sono consapevole che puzza troppo di C.)
Macke

Il fatto che sia prolisso può essere visto come un vantaggio. Ma mi piace anche ~ 0.
Edouard A.

2
Puoi mitigare il prolisso con la macro UINT_MAX standard, dato che stai comunque programmando il tipo unsigned int.

2
@Macke Puoi evitare di specificare il tipo in C ++ 11 con auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone,

11
unsigned int flags = -1;  // all bits are true

"È un buon [,] modo portatile per raggiungere questo obiettivo?"

Portatile? .

Buona? Discutibile , come evidenziato da tutta la confusione mostrata su questo thread. Essere abbastanza chiari che i tuoi colleghi programmatori possano capire il codice senza confusione dovrebbero essere una delle dimensioni che misuriamo per un buon codice.

Inoltre, questo metodo è soggetto agli avvisi del compilatore . Per eludere l'avvertimento senza paralizzare il compilatore, avresti bisogno di un cast esplicito. Per esempio,

unsigned int flags = static_cast<unsigned int>(-1);

Il cast esplicito richiede di prestare attenzione al tipo di destinazione. Se stai prestando attenzione al tipo di bersaglio, eviterai naturalmente le insidie ​​degli altri approcci.

Il mio consiglio è di prestare attenzione al tipo di target e assicurarsi che non vi siano conversioni implicite. Per esempio:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Tutto ciò è corretto e più ovvio per i tuoi colleghi programmatori.

E con C ++ 11 : possiamo usare autoper rendere ancora più semplice una di queste:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Considero corretto e ovvio meglio che semplicemente corretto.


10

La conversione di -1 in qualsiasi tipo senza segno è garantita dallo standard per ottenere tutti. L'uso di ~0Uè generalmente negativo poiché 0ha tipo unsigned inte non riempirà tutti i bit di un tipo più grande senza segno, a meno che non si scriva esplicitamente qualcosa del genere ~0ULL. Su sistemi sani, ~0dovrebbe essere identico a -1, ma poiché lo standard consente rappresentazioni di complemento e segno / grandezza, a rigor di termini non è portatile.

Ovviamente va sempre bene scrivere 0xffffffffse sai che hai bisogno esattamente di 32 bit, ma -1 ha il vantaggio che funzionerà in qualsiasi contesto anche quando non conosci le dimensioni del tipo, come le macro che funzionano su più tipi o se la dimensione del tipo varia in base all'implementazione. Se si conosce il tipo, un altro modo sicuro per ottenere tutti-quelli è la macro limite UINT_MAX, ULONG_MAX, ULLONG_MAX, etc.

Personalmente uso sempre -1. Funziona sempre e non devi pensarci.


FWIW, se intendo "tutti i 1 bit" che uso ~(type)0(bene, typeovviamente riempi il diritto ). Se si lancia zero, si ottiene ancora uno zero, quindi è chiaro e negare tutti i bit nel tipo di destinazione è definito in modo abbastanza chiaro. Non è così spesso che in realtà voglio quell'operazione; YMMV.
Donal Fellows

6
@Donal: hai semplicemente torto. C specifica che, quando si converte un valore che non rientra in un tipo senza segno, i valori vengono ridotti modulo 2 ^ n dove n è il numero di bit nel tipo di destinazione. Questo vale sia per i valori firmati che per i tipi non firmati più grandi. Non ha nulla a che fare con il complemento a due.
R .. GitHub smette di aiutare ICE il

2
Essi fanno implementare lo stesso, che è banale; basta usare un codice operativo di sottrazione senza segno anziché uno con segno o negun'istruzione. Le macchine che hanno un comportamento aritmetico con firma fasulla hanno codici operativi aritmetici separati con o senza segno. Ovviamente un compilatore davvero valido ignorerebbe sempre i codici operativi firmati anche per i valori firmati e otterrebbe quindi due complementi gratuitamente.
R .. GitHub smette di aiutare ICE il

1
@R .: Il var = ~(0*var)caso non riuscirà per varessere un tipo senza segno più stretto di int. Forse var = ~(0U*var)? (personalmente preferisco comunque -1, però).
Caf

1
Questa risposta è molto più chiara di quella di Johannes Schaub. Tuttavia, l'assegnazione di un numero intero letterale negativo a un tipo senza segno senza un cast comporta in genere un avviso del compilatore. Sopprimere l'avviso richiede un cast, il che significa che devi comunque prestare attenzione al tipo di destinazione, quindi puoi anche usare UINT_MAX o ULONG_MAX ed essere chiaro piuttosto che fare affidamento su un piccolo dettaglio nello standard confonde chiaramente molti dei tuoi colleghi programmatori .
Adrian McCarthy,

5

Finché hai #include <limits.h>una delle tue inclusioni, dovresti semplicemente usare

unsigned int flags = UINT_MAX;

Se vuoi un pezzo di bit, potresti usare

unsigned long flags = ULONG_MAX;

A questi valori è garantito che tutti i bit di valore del risultato siano impostati su 1, indipendentemente da come vengono implementati gli interi con segno.


1
le costanti che hai suggerito sono in realtà definite in limits.h - stdint.h contiene i limiti per i tipi di numeri interi aggiuntivi (numeri interi di dimensioni fisse, intptr_t, ...)
Christoph

5

Sì. Come menzionato in altre risposte, -1è il più portatile; tuttavia, non è molto semantico e attiva gli avvisi del compilatore.

Per risolvere questi problemi, prova questo semplice aiuto:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Uso:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

3
Come programmatore C, codice come questo mi fa brutti sogni di notte.
Jforberg,

E cosa succede quando lo usi in un contesto come ALL_BITS_TRUE ^ adove si atrova un numero intero con segno? Il tipo rimane con segno intero e il bit-pattern (rappresentazione dell'oggetto) dipende dal fatto che il target sia il complemento di 2 o meno.
Peter Cordes,

No, ALL_BITS_TRUE ^ adà un errore di compilazione perché ALL_BITS_TRUEè ambiguo. Potrebbe essere usato come uint32_t(ALL_BITS_TRUE) ^ a, tuttavia. Puoi provarlo tu stesso su cpp.sh :) Oggi aggiungerei un static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");in operatorper essere sicuro che gli utenti non provino a usare int(ALL_BITS_TRUE). Aggiornerò la risposta.
Diamond Python,

3

Non farei la cosa -1. È piuttosto non intuitivo (almeno per me). L'assegnazione di dati firmati a una variabile non firmata sembra essere una violazione dell'ordine naturale delle cose.

Nella tua situazione, io uso sempre 0xFFFF. (Usa il numero giusto di F per la dimensione variabile del corso.)

[A proposito, vedo molto raramente il trucco -1 fatto nel codice del mondo reale.]

Inoltre, se si interessa davvero i singoli bit in un vairable, sarebbe buona idea per iniziare a utilizzare il fixed-width uint8_t, uint16_t, uint32_ttipi.


2

Sui processori Intel IA-32 è OK scrivere 0xFFFFFFFF in un registro a 64 bit e ottenere i risultati previsti. Questo perché IA32e (l'estensione a 64 bit di IA32) supporta solo gli immediati a 32 bit. Nelle istruzioni a 64 bit gli immediati a 32 bit sono estesi a 64 bit.

Quanto segue è illegale:

mov rax, 0ffffffffffffffffh

Quanto segue inserisce 64 1 in RAX:

mov rax, 0ffffffffh

Solo per completezza, quanto segue inserisce 32 1 nella parte inferiore di RAX (aka EAX):

mov eax, 0ffffffffh

E infatti ho avuto problemi con i programmi quando volevo scrivere 0xffffffff in una variabile a 64 bit e invece ho ottenuto uno 0xffffffffffffffff. In C questo sarebbe:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

il risultato è:

x is 0xffffffffffffffff

Ho pensato di postare questo come commento a tutte le risposte che dicevano che 0xFFFFFFFF assume 32 bit, ma così tante persone hanno risposto che ho pensato di aggiungerlo come risposta separata.


1
Congratulazioni, hai trovato un bug del compilatore!
tc.

Questo è stato documentato ovunque come un bug?
Nathan Fellman,

1
Supponendo che si UINT64_C(0xffffffff)espanda a qualcosa di simile 0xffffffffuLL, è sicuramente un bug del compilatore. Lo standard C discute ampiamente dei valori , il valore rappresentato da 0xffffffffè 4294967295 (non 36893488147419103231) e non ci sono conversioni in vista di tipi interi con segno.
tc.

2

Vedi la risposta di litb per una spiegazione molto chiara dei problemi.

Il mio disaccordo è che, molto rigorosamente, non ci sono garanzie per entrambi i casi. Non conosco alcuna architettura che non rappresenti un valore senza segno di "uno inferiore a due alla potenza del numero di bit" come tutti i bit impostati, ma ecco cosa dice lo standard (3.9.1 / 7 plus nota 44):

Le rappresentazioni dei tipi integrali definiscono i valori mediante un sistema di numerazione binaria pura. [Nota 44:] Una rappresentazione posizionale per numeri interi che utilizza le cifre binarie 0 e 1, in cui i valori rappresentati dai bit successivi sono additivi, iniziano con 1 e vengono moltiplicati per la potenza integrale successiva di 2, tranne forse per il bit con la posizione più alta.

Ciò lascia la possibilità che uno dei bit sia qualsiasi cosa.


In generale, non possiamo essere sicuri del valore dei bit di riempimento. E se vogliamo, allora potremmo essere in pericolo dal momento che potremmo generare una rappresentazione trappola per loro (e potrebbe aumentare i segnali). Tuttavia, lo std richiede che il carattere senza segno non abbia bit di riempimento e in 4.7 / 2 nello standard c ++ si dice che convertendo un numero intero in un tipo senza segno, il valore della variabile non firmata risultante è il valore più piccolo congruente al valore intero di origine, (modulo 2 ^ n, n == numero di bit nel tipo senza segno). quindi (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1 ha tutti i bit impostati in un sistema di numerazione binaria pura.
Johannes Schaub - litb

Se vogliamo davvero avere tutti i bit 1 nella rappresentazione dell'oggetto di un tipo senza segno, avremmo bisogno di memset. Ma potremmo generare una rappresentazione trap in questo modo :( Comunque, un'implementazione probabilmente non ha motivo di buttare via un po 'i loro numeri interi senza segno, quindi la userà per memorizzare i suoi valori. Ma hai un buon punto - non c'è nulla di fermarsi un'interpretazione derivante dal fatto di avere alcune sciocche sciocchezze, penso (a parte in char / firmata char / unsigned char, che non deve avere quelle). +1 ovviamente :)
Johannes Schaub - litb

Alla fine, penso che lo standard potrebbe essere più chiaro a quale rappresentazione si riferisce in 4.7 / 2. Se si riferisce alla rappresentazione dell'oggetto, allora non c'è più spazio per i bit di riempimento (ho visto persone litigare in quel modo con cui non vedo nulla di sbagliato). Ma penso che parli della rappresentazione del valore (perché tutto in 4.7 / 2 riguarda comunque i valori - e quindi i bit di padding potrebbero annidarsi accanto ai bit di valore.
Johannes Schaub - litb

1
Lo standard sembra avere chiaramente in mente rappresentazioni di "2, complemento di 1 e grandezza firmata", ma non vuole escludere nulla. Punto interessante anche sulle rappresentazioni di intrappolamento. Per quanto ne so, il bit che ho citato è la definizione di "sistema di numerazione binaria pura" per quanto riguarda lo standard - il bit "tranne" alla fine è davvero il mio unico dubbio sul fatto che il casting -1 sia garantito lavoro.
James Hopkin,

2

Sebbene 0xFFFF(o 0xFFFFFFFF, ecc.) Possa essere più facile da leggere, può interrompere la portabilità nel codice che altrimenti sarebbe portabile. Si consideri, ad esempio, una routine di libreria per contare quanti elementi in una struttura di dati hanno determinati bit impostati (i bit esatti specificati dal chiamante). La routine può essere totalmente agnostica su ciò che rappresentano i bit, ma deve comunque avere una costante "tutti i bit impostati". In tal caso, -1 sarà di gran lunga migliore di una costante esadecimale poiché funzionerà con qualsiasi dimensione di bit.

L'altra possibilità, se typedefsi utilizza un valore per la maschera di bit, sarebbe usare ~ (bitMaskType) 0; se la maschera di bit è solo un tipo di 16 bit, quell'espressione avrà solo 16 bit impostati (anche se 'int' sarebbe altrimenti 32 bit) ma poiché 16 bit saranno tutto ciò che è richiesto, le cose dovrebbero andare bene a condizione che uno utilizza effettivamente il tipo appropriato nel typecast.

Per inciso, le espressioni della forma longvar &= ~[hex_constant]hanno un gotcha cattivo se la costante esadecimale è troppo grande per adattarsi a un int, ma si adatta a un unsigned int. Se an intè 16 bit, allora longvar &= ~0x4000;oppure longvar &= ~0x10000; cancellerà un bit di longvar, ma longvar &= ~0x8000;cancellerà il bit 15 e tutti i bit sopra di esso. Ai valori che rientrano intverrà applicato l'operatore complemento a un tipo int, ma il risultato verrà esteso a long, impostando i bit superiori. I valori troppo grandi per unsigned intcui l'operatore del complemento verrà applicato al tipo long. I valori compresi tra quelle dimensioni, tuttavia, applicheranno l'operatore complemento al tipo unsigned int, che verrà quindi convertito in tipo longsenza estensione del segno.


1

Praticamente: Sì

Teoricamente: No.

-1 = 0xFFFFFFFF (o qualunque dimensione un int sia sulla tua piattaforma) è vero solo con l'aritmetica del complemento a due. In pratica, funzionerà, ma ci sono macchine legacy là fuori (mainframe IBM, ecc.) In cui hai un bit di segno reale piuttosto che una rappresentazione di complemento a due. La tua soluzione ~ 0 proposta dovrebbe funzionare ovunque.


6
L'ho detto anche io. Ma poi ho capito che avevo torto, dato che -1 firmato converte sempre in max_value unsigned secondo le regole di conversione, indipendentemente dalle rappresentazioni del valore. Almeno, lo fa in C ++, non ho lo standard C a portata di mano.
Steve Jessop,

6
c'è un'ambiguità. -1 non è 0xFFFFFFFF. Ma -1 è 0xFFFFFFFF se convertito in un int senza segno (con 32 bit). Questo è ciò che rende questa discussione così difficile penso. Molte persone hanno in mente cose molto diverse quando parlano di quelle stringhe di bit.
Johannes Schaub - litb

1

Come altri hanno già detto, -1 è il modo corretto di creare un numero intero che verrà convertito in un tipo senza segno con tutti i bit impostati su 1. Tuttavia, la cosa più importante in C ++ sta usando i tipi corretti. Pertanto, la risposta corretta al tuo problema (che include la risposta alla domanda che hai posto) è questa:

std::bitset<32> const flags(-1);

Questo conterrà sempre l'esatta quantità di bit necessari. Costruisce a std::bitsetcon tutti i bit impostati su 1 per gli stessi motivi menzionati in altre risposte.


0

È sicuramente sicuro, poiché -1 avrà sempre tutti i bit disponibili impostati, ma mi piace ~ 0 meglio. -1 non ha molto senso per un unsigned int. 0xFF... non va bene perché dipende dalla larghezza del tipo.


4
"0xFF ... non va bene perché dipende dalla larghezza del tipo" Penso che sia l'unica strada sana di mente. Dovresti definire chiaramente cosa significa ogni flag / bit nel tuo programma. Quindi, se definisci che stai usando i 32 bit più bassi per memorizzare i flag, dovresti limitarti a usare quei 32 bit, sia che la dimensione effettiva dell'int sia 32 o 64.
Juan Pablo Califano

0

Dico:

int x;
memset(&x, 0xFF, sizeof(int));

Questo ti darà sempre il risultato desiderato.


4
Non su sistemi con caratteri a 9 bit!
tc.

0

Sfruttando il fatto che assegnare tutti i bit a uno per un tipo senza segno equivale a prendere il valore massimo possibile per il tipo dato
ed estendere l'ambito della domanda a tutti i tipi interi senza segno :

L'assegnazione di -1 funziona per qualsiasi tipo intero senza segno (unsigned int, uint8_t, uint16_t, ecc.) Sia per C che per C ++.

In alternativa, per C ++, puoi:

  1. Includi <limits>e usastd::numeric_limits< your_type >::max()
  2. Scrivi una funzione personalizzata personalizzata (ciò consentirebbe anche un controllo di integrità, ovvero se il tipo di destinazione è davvero un tipo senza segno)

Lo scopo potrebbe essere quello di aggiungere più chiarezza, poiché l'assegnazione -1avrebbe sempre bisogno di un commento esplicativo.


0

Un modo per rendere il significato un po 'più ovvio e tuttavia evitare di ripetere il tipo:

const auto flags = static_cast<unsigned int>(-1);

-6

sì, la rappresentazione mostrata è molto corretta, come se lo facessimo in senso contrario, dovrai richiedere a un operatore di invertire tutti i bit, ma in questo caso la logica è abbastanza semplice se consideriamo la dimensione degli interi nella macchina

ad esempio nella maggior parte delle macchine un numero intero è 2 byte = 16 bit il valore massimo che può contenere è 2 ^ 16-1 = 65535 2 ^ 16 = 65536

0% 65536 = 0 -1% 65536 = 65535 che corrisponde a 1111 ............. 1 e tutti i bit sono impostati su 1 (se consideriamo le classi di residui mod 65536) quindi è molto dritto.

suppongo

no, se consideri questa nozione, è perfetta per gli ints non firmati e funziona davvero

basta controllare il seguente frammento di programma

int main () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

risposta per b = 4294967295 che è -1% 2 ^ 32 su numeri interi a 4 byte

quindi è perfettamente valido per numeri interi senza segno

in caso di discrepanze si prega di segnalare


9
Due commenti: primo, ti sbagli di grosso sulla dimensione degli interi sulla "maggior parte" delle macchine. In secondo luogo, è molto difficile 2 leggere due volte di un tipo sommario di lingua segreta. Per favore , inglese semplice .
Konrad Rudolph,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.