Eu já vi esse padrão usado muito em C & C ++.
unsigned int flags = -1; // all bits are true
Essa é uma boa maneira portátil de fazer isso? Ou está usando 0xffffffff
ou ~0
melhor?
c++
c
binary
bit-fields
hiperlógico
fonte
fonte
-1
sempre funcione, o fato de ser necessário um comentário depois de mostrar que não é um código claro. Se a variável pretende ser uma coleção de sinalizadores, por que atribuí-la um número inteiro? Seu tipo pode ser um número inteiro, mas certamente não é semanticamente um número inteiro. Você nunca vai incrementá-lo ou multiplicá-lo. Então, eu usaria0xffffffff
não para portabilidade ou correção, mas para maior clareza.-1
continua a ser uma solução portátil e compatível com versões anteriores dos dois idiomas, mas pode afetar parte do raciocínio de outras respostas.Respostas:
Eu recomendo que você faça exatamente como demonstrou, pois é o mais direto. Inicialize para o
-1
qual sempre funcionará , independentemente da representação real do sinal, enquanto~
às vezes terá um comportamento surpreendente, pois você precisará ter o tipo certo de operando. Somente então você obterá o valor mais alto de umunsigned
tipo.Para um exemplo de uma possível surpresa, considere este:
Não necessariamente armazenará um padrão com todos os bits 1
a
. Mas ele primeiro criará um padrão com todos os bits 1 em umunsigned int
e depois o atribuiráa
. O que acontece quandounsigned long
há mais bits é que nem todos são 1.E considere este, que falhará na representação do complemento de um não-dois:
A razão para isso é que
~0
é necessário inverter todos os bits. Invertendo que irá produzir-1
na máquina de complemento de dois (que é o valor que precisamos!), Mas não deu-1
em outra representação. Na máquina de complemento de uma pessoa, ela produz zero. Assim, na máquina de complemento de uma pessoa, o acima será inicializadoa
como zero.O que você deve entender é que se trata de valores - não de bits. A variável é inicializada com um valor . Se no inicializador você modificar os bits da variável usada para inicialização, o valor será gerado de acordo com esses bits. O valor que você precisa, para inicializar
a
com o maior valor possível, é-1
ouUINT_MAX
. O segundo dependerá do tipo dea
- você precisará usarULONG_MAX
para umunsigned long
. No entanto, o primeiro não dependerá do seu tipo e é uma ótima maneira de obter o valor mais alto.Estamos não falar sobre se
-1
tem todos os bits um (que nem sempre tem). E nós estamos não falar sobre se~0
tem todos os bits um (ele tem, é claro).Mas o que estamos falando é qual é o resultado da
flags
variável inicializada . E, para isso, apenas-1
funcionará com todos os tipos e máquinas.fonte
numeric_limits<size_t>::max()
é um pouco prolixo, mas assim é o elenco ...-1
são representados por nem bits~0
. Podemos não nos importar com valores, mas o compilador sim. Não podemos ignorar o fato de que as operações funcionam com e por valores. O valor de~0
pode não ser-1
, mas esse é o valor que você precisa. Veja minha resposta e o resumo do @ Dingo.unsigned int flags = -1;
é portátil.unsigned int flags = ~0;
não é portátil porque depende de uma representação de complemento de dois.unsigned int flags = 0xffffffff;
não é portátil porque assume entradas de 32 bits.Se você deseja definir todos os bits de uma maneira garantida pelo padrão C, use o primeiro.
fonte
~0
gera umint
valor com todos os bits definidos, é claro. Mas atribuir umint
a umunsigned int
não resulta necessariamente no int não assinado tendo o mesmo padrão de bits que o padrão de bits assinado. Somente com a representação do complemento de 2 é esse sempre o caso. Na representação de complemento ou magnitude de sinal de 1s, atribuir umint
valor negativo a umunsigned int
resultado em um padrão de bits diferente. Isso ocorre porque o padrão C ++ define conversão assinada -> não assinada como o valor igual a módulo, não o valor com os mesmos bits.Francamente, acho que todos os fff's são mais legíveis. Quanto ao comentário de que é um antipadrão, se você realmente se importa que todos os bits estejam configurados / limpos, eu diria que você provavelmente está em uma situação em que se importa com o tamanho da variável de qualquer maneira, o que exigiria algo como aumento :: uint16_t, etc.
fonte
Uma maneira de evitar os problemas mencionados é simplesmente fazer:
Portátil e direto ao ponto.
fonte
flags
comoconst
.unsigned int const flags = ~0u;
~0
é um número inteiro que possui todos os bits definidos como 1, mas quando você o atribuiint
àunsigned
variávelflags
, realiza uma conversão de valor de-2**31
(assumindo 32 bitsint
) para(-2**31 % 2**32) == 2**31
, que é um número inteiro com todos os bits, exceto o primeiro, definido como 1. #u
sufixo na sua resposta. Obviamente, isso funcionaria, mas ainda tem o problema de especificar o tipo de dados que você usa (unsigned
e não é maior) duas vezes, o que pode levar a erros. O erro provavelmente aparecerá se a atribuição e a declaração inicial da variável estiverem mais distantes.Não tenho certeza de que usar um int não assinado para sinalizadores seja uma boa idéia em primeiro lugar em C ++. E quanto ao bitset e afins?
std::numeric_limit<unsigned int>::max()
é melhor porque0xffffffff
assume que int não assinado é um número inteiro de 32 bits.fonte
auto
.auto const flags = std::numeric_limit<unsigned>::max()
.Portátil? Sim .
Boa? Debatível , como evidenciado por toda a confusão mostrada neste tópico. Ser claro o suficiente para que seus colegas programadores possam entender o código sem confusão deve ser uma das dimensões que medimos para obter um bom código.
Além disso, esse método é propenso a avisos do compilador . Para eliminar o aviso sem danificar seu compilador, você precisa de uma conversão explícita. Por exemplo,
A conversão explícita exige que você preste atenção ao tipo de destino. Se você está prestando atenção ao tipo de alvo, evita naturalmente as armadilhas das outras abordagens.
Meu conselho seria prestar atenção ao tipo de destino e garantir que não haja conversões implícitas. Por exemplo:
Todos os quais são corretos e mais óbvios para seus colegas programadores.
E com o C ++ 11 : podemos usar
auto
para tornar ainda mais simples:Considero correto e óbvio melhor do que simplesmente correto.
fonte
A conversão de -1 em qualquer tipo não assinado é garantida pelo padrão para resultar em todos. O uso de
~0U
geralmente é ruim, pois0
tem tipounsigned int
e não preenche todos os bits de um tipo maior não assinado, a menos que você escreva explicitamente algo como~0ULL
. Em sistemas sãos,~0
deve ser idêntico a-1
, mas como o padrão permite representações de complemento de unidades e sinal / magnitude, estritamente falando, não é portátil.É claro que é sempre bom escrever
0xffffffff
se você sabe que precisa exatamente de 32 bits, mas -1 tem a vantagem de funcionar em qualquer contexto, mesmo quando você não conhece o tamanho do tipo, como macros que funcionam em vários tipos ou se o tamanho do tipo variar de acordo com a implementação. Se você não sabe o tipo, outra forma segura de obter todo-queridos é as macros limiteUINT_MAX
,ULONG_MAX
,ULLONG_MAX
, etc.Pessoalmente eu sempre uso -1. Sempre funciona e você não precisa pensar nisso.
fonte
~(type)0
(bem, preencha o que está certo, étype
claro). A conversão de zero ainda resulta em zero, então isso é claro, e a negação de todos os bits no tipo de destino é claramente definida. Não é tão frequente que eu realmente queira essa operação; YMMV.neg
instrução. Máquinas com comportamento aritmético com sinal falso têm opcodes aritméticos com sinal / sem sinal separados. É claro que um compilador realmente bom sempre ignoraria os códigos de operação assinados, mesmo para valores assinados, e assim obteria dois complementos gratuitamente.var = ~(0*var)
caso falhará porvar
ser um tipo não assinado mais estreito queint
. Talvezvar = ~(0U*var)
? (pessoalmente ainda prefiro-1
).Contanto que você tenha
#include <limits.h>
uma de suas inclusões, use apenasSe você quiser um valor de bits longo, poderá usar
É garantido que esses valores tenham todos os bits de valor do resultado definidos como 1, independentemente de como números inteiros assinados sejam implementados.
fonte
Sim. Como mencionado em outras respostas,
-1
é o mais portátil; no entanto, não é muito semântico e dispara avisos do compilador.Para resolver esses problemas, tente este simples auxiliar:
Uso:
fonte
ALL_BITS_TRUE ^ a
ondea
está um número inteiro assinado? O tipo permanece com número inteiro assinado e o padrão de bits (representação do objeto) depende do destino ser o complemento de 2 ou não.ALL_BITS_TRUE ^ a
gera um erro de compilação porqueALL_BITS_TRUE
é ambíguo. Poderia ser usado comouint32_t(ALL_BITS_TRUE) ^ a
, no entanto. Você pode experimentar por si mesmo em cpp.sh :) Hoje em dia eu adicionar umstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
nooperator
para ter certeza de que os usuários não tentar usarint(ALL_BITS_TRUE)
. Vou atualizar a resposta.Eu não faria a coisa -1. É bastante não intuitivo (pelo menos para mim). Atribuir dados assinados a uma variável não assinada apenas parece ser uma violação da ordem natural das coisas.
Na sua situação, eu sempre uso
0xFFFF
. (Use o número certo de Fs para o tamanho variável, é claro.)[BTW, raramente vejo o truque -1 feito no código do mundo real.]
Além disso, se você realmente se preocupam com os bits individuais em um vairable, seria boa idéia para começar a usar a largura fixa
uint8_t
,uint16_t
,uint32_t
tipos.fonte
Nos processadores IA-32 da Intel, não há problema em gravar 0xFFFFFFFF em um registro de 64 bits e obter os resultados esperados. Isso ocorre porque o IA32e (a extensão de 64 bits para o IA32) suporta apenas imediatos de 32 bits. Nas instruções de 64 bits, os imediatos de 32 bits são estendidos para 64 bits.
O seguinte é ilegal:
O seguinte coloca 64 1s em RAX:
Apenas para completar, o seguinte coloca 32 1s na parte inferior do RAX (aka EAX):
E, de fato, tive programas falhar quando quis escrever 0xffffffff em uma variável de 64 bits e, em vez disso, recebi um 0xffffffffffffffff. Em C, isso seria:
o resultado é:
Eu pensei em postar isso como um comentário em todas as respostas que diziam que 0xFFFFFFFF assume 32 bits, mas muitas pessoas responderam que eu achei que adicionaria como resposta separada.
fonte
UINT64_C(0xffffffff)
expanda para algo como0xffffffffuLL
, é definitivamente um bug do compilador. O padrão C discute amplamente valores , o valor representado por0xffffffff
é 4294967295 (não 36893488147419103231) e não há conversões para tipos inteiros assinados à vista.Veja a resposta do litb para uma explicação muito clara dos problemas.
Meu desacordo é que, estritamente falando, não há garantias para nenhum dos casos. Não conheço nenhuma arquitetura que não represente um valor não assinado de 'um a menos do que dois à potência do número de bits' como todos os bits configurados, mas aqui está o que o Padrão realmente diz (3.9.1 / 7 mais nota 44):
Isso deixa a possibilidade de um dos bits ser qualquer coisa.
fonte
Embora o
0xFFFF
(ou0xFFFFFFFF
etc.) possa ser mais fácil de ler, ele pode quebrar a portabilidade no código que, de outra forma, seria portátil. Considere, por exemplo, uma rotina de biblioteca para contar quantos itens em uma estrutura de dados possuem determinados bits definidos (os bits exatos sendo especificados pelo chamador). A rotina pode ser totalmente independente do que os bits representam, mas ainda precisa ter uma constante "todos os bits definidos". Nesse caso, -1 será muito melhor que uma constante hexadecimal, pois funcionará com qualquer tamanho de bit.A outra possibilidade, se um
typedef
valor for usado para a máscara de bits, seria usar ~ (bitMaskType) 0; se a máscara de bits for apenas do tipo de 16 bits, essa expressão terá apenas 16 bits definidos (mesmo que 'int' seja de 32 bits), mas como 16 bits serão tudo o que é necessário, as coisas devem ficar bem, desde que uma realmente usa o tipo apropriado no typecast.Aliás, as expressões do formulário
longvar &= ~[hex_constant]
têm uma pegadinha desagradável se a constante hexadecimal for muito grande para caber em umint
, mas caberá em umunsigned int
. Se umint
tiver 16 bits, entãolongvar &= ~0x4000;
oulongvar &= ~0x10000
; limpará um bit delongvar
, maslongvar &= ~0x8000;
limpará o bit 15 e todos os bits acima dele. Os valores que se encaixamint
terão o operador de complemento aplicado a um tipoint
, mas o resultado será estendido para o sinallong
, configurando os bits superiores. Valores grandes demaisunsigned int
terão o operador de complemento aplicado ao tipolong
. Valores entre esses tamanhos, no entanto, aplicarão o operador complemento ao tipounsigned int
, que será convertido em tipolong
sem extensão de sinal.fonte
Praticamente: Sim
Teoricamente: Não.
-1 = 0xFFFFFFFF (ou qualquer que seja o tamanho de um int em sua plataforma) só é verdadeiro com a aritmética de complemento de dois. Na prática, funcionará, mas existem máquinas legadas por aí (mainframes da IBM etc.) nas quais você tem um bit de sinal real em vez da representação de complemento de dois. Sua solução proposta ~ 0 deve funcionar em qualquer lugar.
fonte
Como outros já mencionaram, -1 é a maneira correta de criar um número inteiro que será convertido em um tipo não assinado com todos os bits definidos como 1. No entanto, o mais importante em C ++ é usar tipos corretos. Portanto, a resposta correta para o seu problema (que inclui a resposta à pergunta que você fez) é a seguinte:
Isso sempre conterá a quantidade exata de bits que você precisa. Constrói a
std::bitset
com todos os bits definidos como 1 pelos mesmos motivos mencionados em outras respostas.fonte
É certamente seguro, pois -1 sempre terá todos os bits disponíveis definidos, mas eu gosto de ~ 0 melhor. -1 simplesmente não faz muito sentido para um
unsigned int
.0xFF
... não é bom porque depende da largura do tipo.fonte
Eu digo:
Isso sempre lhe dará o resultado desejado.
fonte
Aproveitar o fato de atribuir todos os bits a um para um tipo não assinado é equivalente a pegar o valor máximo possível para o tipo fornecido
e estender o escopo da pergunta a todos os tipos inteiros não assinados :
A atribuição de -1 funciona para qualquer tipo de número inteiro não assinado (int não assinado, uint8_t, uint16_t etc.) para C e C ++.
Como alternativa, para C ++, você pode:
<limits>
e usarstd::numeric_limits< your_type >::max()
O objetivo poderia ser adicionar mais clareza, pois a atribuição
-1
sempre precisaria de alguns comentários explicativos.fonte
Uma maneira de tornar o significado um pouco mais óbvio e evitar repetir o tipo:
fonte
sim, a representação mostrada é muito correta, como se o fizéssemos o contrário, exigindo que um operador inverta todos os bits, mas, neste caso, a lógica é bastante direta se considerarmos o tamanho dos números inteiros na máquina
por exemplo, na maioria das máquinas, um número inteiro é 2 bytes = 16 bits, o valor máximo que pode armazenar é 2 ^ 16-1 = 65535 2 ^ 16 = 65536
0% 65536 = 0 -1% 65536 = 65535 que corresponde a 1111 ............. 1 e todos os bits são definidos como 1 (se considerarmos as classes de resíduos mod 65536), portanto, é muito direto.
eu acho
não, se você considerar essa noção, ela é perfeita para entradas não assinadas e funciona
basta verificar o seguinte fragmento de programa
int main () {
}
resposta para b = 4294967295 que é -1% 2 ^ 32 em números inteiros de 4 bytes
portanto, é perfeitamente válido para números inteiros não assinados
em caso de qualquer discrepância plzz report
fonte