Domanda

Ho visto questo modello, molto utilizzata in C e C++.

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

Questo è un buon portatile per realizzare questo?O utilizza 0xffffffff o ~0 meglio?

È stato utile?

Soluzione

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

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 unsigned int, quindi lo assegnerà a unsigned long. Quello che succede quando ~0 ha 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 !

Il motivo è che UINT_MAX deve invertire tutti i bit. L'inversione che produrrà ULONG_MAX su un complemento a due (che è il valore di cui abbiamo bisogno!), Ma non produrrà flags su un'altra rappresentazione. Sulla macchina di un complemento, produce zero. Pertanto, su una macchina del complemento di uno, quanto sopra inizializzerà <=> a 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 <=> al valore più alto possibile, è <=> o <=>. Il secondo dipenderà dal tipo di <=> - dovrai usare <=> per un <=>. Tuttavia, il primo non dipenderà dal suo tipo ed è un bel modo per ottenere il valore più alto.

Stiamo non parlando se <=> ha tutti i bit uno (non sempre). E stiamo non parlando se <=> ha tutti i bit uno (ovviamente ce l'ha).

Ma ciò di cui stiamo parlando è il risultato della variabile <=> inizializzata. E per questo, solo <=> funzionerà con ogni tipo e macchina.

Altri suggerimenti

  • 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.

Francamente penso che tutti fff è più leggibile.Come per il commento che un antipattern, se davvero la cura che tutti i bit sono impostati/cancellato, direi che si sono probabilmente in una situazione in cui si cura circa la dimensione della variabile in ogni caso, che l'avrebbe chiamata per qualcosa come boost::uint16_t, etc.

Un modo per evitare i problemi menzionati è semplicemente fare:

unsigned int flags = 0;
flags = ~flags;

Portatile e al punto.

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

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

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

"Questo è un buon[,] portatili modo per realizzare questo?"

Portatile? .

Bene? Discutibile, come evidenziato da tutta la confusione mostrato su questo thread.Essere abbastanza chiaro che i vostri compagni di programmatori in grado di capire il codice, senza confusione, dovrebbe essere una delle dimensioni che si misura per il buon codice.

Inoltre, questo metodo è soggetto a avvisi del compilatore.Di elide l'avviso senza paralizzare il compilatore, avete 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 si sta prestando attenzione al tipo di destinazione, poi naturalmente dovrai evitare le insidie degli altri approcci.

Il mio consiglio è di prestare attenzione al tipo di destinazione e assicurarsi che non ci 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);

Tutte corretto e più evidente gli altri programmatori.

E con C++11:Siamo in grado di utilizzare auto per fare una qualsiasi di queste ancora più semplice:

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

Ritengo corretto e ovvio meglio che semplicemente corretto.

La conversione di -1 in qualsiasi tipo senza segno è garantita dallo standard per ottenere tutti. L'uso di ~0U è generalmente negativo poiché 0 ha il tipo unsigned int e non riempirà tutti i bit di un tipo senza segno più grande, a meno che non si scriva esplicitamente qualcosa come ~0ULL. Sui sistemi sani, ~0 dovrebbe 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 0xffffffff se 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 macro che funzionano su più tipi o se la dimensione del tipo varia in base all'implementazione. Se conosci il tipo, un altro modo sicuro per ottenere tutti è il limite macro UINT_MAX, ULONG_MAX, ULLONG_MAX, ecc.

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

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
    {
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Utilizzo:

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;

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

unsigned int flags = UINT_MAX;

Se vuoi bit di un lungo valore, puoi 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.

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

Nella tua situazione, 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 ti preoccupi davvero dei singoli bit in un vairable, sarebbe una buona idea iniziare a utilizzare i tipi di larghezza fissa uint8_t, uint16_t, uint32_t.

Su 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 con estensione del segno 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 in effetti 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 pubblicare 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.

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

Il mio disaccordo è che, molto strettamente parlando, 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 sono 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.

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.

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 un & Quot; tutti i bit impostati & Quot; costante. In tal caso, -1 sarà di gran lunga migliore di una costante esadecimale poiché funzionerà con qualsiasi dimensione di bit.

L'altra possibilità, se si utilizza un valore typedef per la maschera di bit, sarebbe usare ~ (bitMaskType) 0; se la maschera di bit è solo un tipo a 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 si utilizzi effettivamente il tipo appropriato nel typecast.

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

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 un std::bitset con tutti i bit impostati su 1 per gli stessi motivi menzionati in altre risposte.

È certamente 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.

Dico:

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

Questo ti darà sempre il risultato desiderato.

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 di numeri interi non firmati :

L'assegnazione di -1 opere per qualsiasi tipo intero non firmato (unsigned int, uint8_t, uint16_t , ecc.) sia per C che per C ++.

In alternativa, per C ++, puoi:

  1. Includi <limits> e usa std::numeric_limits< your_type >::max()
  2. Scrivi una funzione modello personalizzata (Ciò consentirebbe anche un controllo di integrità, vale a dire se il tipo di destinazione è davvero senza segno tipo)

Lo scopo potrebbe essere quello di aggiungere maggiore chiarezza, poiché l'assegnazione di -1 richiederebbe sempre un commento esplicativo.

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);

sì, la rappresentazione mostrata è molto corretta, come se lo facessimo in senso contrario, occorrerà che un operatore inverta tutti i bit, ma in questo caso la logica è piuttosto semplice se consideriamo la dimensione degli interi nella macchina

per 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 semplice.

Suppongo

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

controlla solo 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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top