Como você define, limpa e alterna um único bit?

Respostas:

3600

Configurando um pouco

Use o operador OR bit a bit ( |) para definir um pouco.

number |= 1UL << n;

Isso definirá o nth bit de number. ndeve ser zero, se você deseja definir o 1st bit e assim por diante n-1, se deseja definir o nth bit.

Use 1ULLse numberfor maior que unsigned long; a promoção de 1UL << nnão acontece até depois de avaliar 1UL << nonde o comportamento indefinido muda mais do que a largura de umlong . O mesmo se aplica a todo o restante dos exemplos.

Limpando um pouco

Use o operador AND bit a bit ( &) para limpar um pouco.

number &= ~(1UL << n);

Isso limpará o nth th de number. Você deve inverter a string de bits com o operador NOT bit a bit (~ ) e depois AND.

Alternando um pouco

O operador XOR ( ^) pode ser usado para alternar um pouco.

number ^= 1UL << n;

Isso alternará o nth bit denumber .

Verificando um pouco

Você não pediu isso, mas eu poderia adicioná-lo.

Para verificar um pouco, desloque o número n para a direita e, em seguida, bit a bit E:

bit = (number >> n) & 1U;

Isso colocará o valor do nth bit numberna variávelbit .

Alterando o n ésimo bit para x

Definir o nth bit como um 1ou 0pode ser alcançado com o seguinte na implementação C ++ de um complemento de 2:

number ^= (-x ^ number) & (1UL << n);

O bit nserá definido se xestiver 1e limpo se xestiver 0. Se xtiver algum outro valor, você recebe lixo. x = !!xo booleanizará para 0 ou 1.

Para tornar isso independente do comportamento de negação do complemento de 2 (onde -1todos os bits foram definidos, diferentemente do complemento de 1 ou da implementação de sinal / magnitude C ++), use negação não assinada.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

ou

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

Geralmente, é uma boa ideia usar tipos não assinados para manipulação portátil de bits.

ou

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n))irá limpar o nth bit e (x << n)definirá o nth bit parax .

Também é geralmente uma boa idéia não copiar / colar código em geral e muitas pessoas usam macros de pré-processador (como a resposta do wiki da comunidade mais adiante ) ou algum tipo de encapsulamento.

Jeremy Ruten
fonte
128
Eu gostaria de observar que, em plataformas que têm suporte nativo para bit set / clear (por exemplo, microcontroladores AVR), os compiladores geralmente traduzem 'myByte | = (1 << x)' nas instruções nativas de set / clear sempre que x é uma constante, ex: (1 << 5), ou const unsigned x = 5.
Aaron
52
bit = número & (1 << x); não colocará o valor do bit x no bit, a menos que o bit tenha o tipo _Bool (<stdbool.h>). Caso contrário, bit = !! (número & (1 << x)); vai ..
Chris Young
23
por que você não muda o último parabit = (number >> x) & 1
aaronman 26/06
42
1é um intliteral, que é assinado. Portanto, todas as operações aqui operam com números assinados, o que não é bem definido pelos padrões. Os padrões não garantem o complemento de dois ou a mudança aritmética, portanto é melhor usá-lo 1U.
Siyuan Ren
50
Prefiro number = number & ~(1 << n) | (x << n);Alterar o n-ésimo bit para x.
jiasli
459

Usando a biblioteca C ++ padrão: std::bitset<N> .

Ou a versão Boost :boost::dynamic_bitset .

Não há necessidade de criar o seu próprio:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

A versão Boost permite um conjunto de bits do tamanho de tempo de execução em comparação com um conjunto de bits do tamanho de compilação da biblioteca padrão .

Martin York
fonte
34
+1. Não que std :: bitset seja utilizável em "C", mas como o autor marcou sua pergunta com "C ++", AFAIK, sua resposta é a melhor por aqui ... std :: vector <bool> é outra maneira, se alguém conhece seus prós e seus contras
paercebal
23
@andrewdotnich: o vetor <bool> é (infelizmente) uma especialização que armazena os valores como bits. Veja gotw.ca/publications/mill09.htm para obter mais informações ...
Niklas
71
Talvez ninguém tenha mencionado isso porque isso foi marcado como incorporado. Na maioria dos sistemas embarcados, você evita o STL como uma praga. E o suporte ao impulso é provavelmente um pássaro muito raro para ser encontrado entre a maioria dos compiladores incorporados.
Lundin
17
@ Martin É muito verdade. Além de matadores de desempenho específicos, como STL e modelos, muitos sistemas embarcados até evitam completamente todas as bibliotecas padrão, porque são difíceis de verificar. A maior parte da filial incorporada está adotando padrões como o MISRA, que requer ferramentas de análise de código estáticas (qualquer profissional de software deve estar usando essas ferramentas, não apenas as pessoas incorporadas). Geralmente, as pessoas têm coisas melhores a fazer do que executar análises estáticas em toda a biblioteca padrão - se o código-fonte estiver disponível para elas no compilador específico.
Lundin
37
@Lundin: Suas declarações são excessivamente amplas (portanto, inútil discutir sobre isso). Estou certo de que posso encontrar situações em que são verdadeiras. Isso não muda meu ponto inicial. Ambas as classes são perfeitamente adequadas para uso em sistemas embarcados (e eu sei que elas são usadas). Seu ponto inicial sobre o STL / Boost não ser usado em sistemas embarcados também está errado. Tenho certeza de que existem sistemas que não os utilizam e mesmo os sistemas que os utilizam são usados ​​criteriosamente, mas dizer que não são usados ​​não é correto (porque existem sistemas em que são usados).
Martin York
248

A outra opção é usar campos de bits:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

define um campo de 3 bits (na verdade, são três campos de 1 bit). As operações de bit agora se tornam um pouco (haha) mais simples:

Para definir ou limpar um pouco:

mybits.b = 1;
mybits.c = 0;

Para alternar um pouco:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Verificando um pouco:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Isso funciona apenas com campos de bits de tamanho fixo. Caso contrário, você precisará recorrer às técnicas de manipulação de bits descritas nas postagens anteriores.

Ferruccio
fonte
68
Sempre achei que usar campos de bits é uma má ideia. Você não tem controle sobre a ordem em que os bits são alocados (de cima ou de baixo), o que torna impossível serializar o valor de maneira estável / portátil, exceto bit por vez. Também é impossível misturar aritmética de bits DIY com campos de bits, por exemplo, criando uma máscara que testa vários bits ao mesmo tempo. Obviamente, você pode usar o && e esperar que o compilador o otimize corretamente ...
R .. GitHub PARE DE AJUDAR O GELO
34
Os campos de bits são ruins de muitas maneiras, eu quase poderia escrever um livro sobre isso. Na verdade, eu quase tive que fazer isso em um programa de campo que precisava de conformidade com MISRA-C. O MISRA-C impõe que todo comportamento definido pela implementação seja documentado, então acabei escrevendo um ensaio sobre tudo o que pode dar errado nos campos de bits. Ordem de bits, endianess, bits de preenchimento, bytes de preenchimento, vários outros problemas de alinhamento, conversões de tipo implícitas e explícitas de e para um campo de bits, UB se int não for usado e assim por diante. Em vez disso, use operadores bit a bit para menos erros e código portátil. Os campos de bits são completamente redundantes.
Lundin
44
Como a maioria dos recursos de idioma, os campos de bits podem ser usados ​​corretamente ou podem ser abusados. Se você precisar compactar vários valores pequenos em um único int, os campos de bits podem ser muito úteis. Por outro lado, se você começar a fazer suposições sobre como os campos de bit são mapeados para o int que contém, você está apenas pedindo problemas.
Ferruccio
4
@ endolith: Isso não seria uma boa ideia. Você poderia fazê-lo funcionar, mas não seria necessariamente portátil para um processador diferente, ou para um compilador diferente, ou mesmo para a próxima versão do mesmo compilador.
Ferruccio
3
@Yasky e Ferruccio, obtendo respostas diferentes para sizeof () para essa abordagem, devem ilustrar os problemas de compatibilidade não apenas entre os compiladores, mas também no hardware. Às vezes nos enganamos que resolvemos esses problemas com idiomas ou tempos de execução definidos, mas na verdade se resume a 'funcionará na minha máquina?'. Vocês incorporados têm o meu respeito (e simpatias).
Kelly S. French
181

Eu uso macros definidas em um arquivo de cabeçalho para manipular o conjunto de bits e limpar:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
Steve Karg
fonte
17
Uh Sei que este é um post 5 anos de idade, mas não haja duplicação argumento em qualquer uma dessas macros, Dan
Robert Kelly
11
BITMASK_CHECK(x,y) ((x) & (y))deve ser ((x) & (y)) == (y)de outra forma retorna resultado incorreto na máscara multibit (ex. 5vs. 3/ * Olá a todos os coveiros:) * /)
Brigadir
7
1deve ser (uintmax_t)1ou semelhantes no caso de alguém tentar usar essas macros em um longou tipo maior
MM
2
BITMASK_CHECK_ALL(x,y)pode ser implementado como #!~((~(y))|(x))
Handy999 /
3
@ Handy999 É um pouco mais fácil de ver por que as obras após a aplicação da lei e de De Morgan re-organizar para chegar!(~(x) & (y))
Tavian Barnes
114

Às vezes vale a pena usar um enumpara nomear os bits:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Em seguida, use os nomes posteriormente. Ou seja, escrever

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

para definir, limpar e testar. Dessa forma, você oculta os números mágicos do restante do seu código.

Fora isso, eu apoio a solução de Jeremy.

dmckee --- gatinho ex-moderador
fonte
1
Como alternativa, você pode fazer uma clearbits()função em vez de &= ~. Por que você está usando um enum para isso? Eu pensei que eram para criar um monte de variáveis ​​únicas com valor arbitrário oculto, mas você está atribuindo um valor definido para cada uma. Então, qual é o benefício versus apenas defini-los como variáveis?
endolith
4
@ endolith: O uso de enums para conjuntos de constantes relacionadas remonta a um longo caminho na programação c. Eu suspeito que, com os compiladores modernos, a única vantagem sobre const shortou o que quer que seja é que eles estão explicitamente agrupados. E quando você quer que eles para algo outro do que bitmasks você começa a numeração automática. No c ++, é claro, eles também formam tipos distintos, o que fornece uma verificação extra de erros estáticos.
dmckee --- gatinho ex-moderador
Você entrará em constantes enum indefinidas se não definir uma constante para cada um dos valores possíveis dos bits. Qual é o enum ThingFlagsvalor ThingError|ThingFlag1, por exemplo?
Luis Colorado
6
Se você usar esse método, lembre-se de que as constantes enum são sempre do tipo assinado int. Isso pode causar todos os tipos de erros sutis devido à promoção de número inteiro implícito ou operações bit a bit em tipos assinados. thingstate = ThingFlag1 >> 1por exemplo, chamará o comportamento definido pela implementação. thingstate = (ThingFlag1 >> x) << ypode invocar um comportamento indefinido. E assim por diante. Para estar seguro, sempre faça a conversão para um tipo não assinado.
Lundin
1
@Lundin: A partir do C ++ 11, você pode definir o tipo subjacente de uma enumeração, por exemplo: enum My16Bits: unsigned short { ... };
Aiken Drum
47

Do bitops.h de snip-c.zip:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

OK, vamos analisar as coisas ...

A expressão comum com a qual você parece estar tendo problemas em tudo isso é "(1L << (posn))". Tudo isso é criar uma máscara com um único bit ativado e que funcionará com qualquer tipo de número inteiro. O argumento "posn" especifica a posição em que você deseja o bit. Se posn == 0, esta expressão será avaliada para:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Se posn == 8, ele avaliará como:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

Em outras palavras, ele simplesmente cria um campo de 0 com um 1 na posição especificada. A única parte complicada está na macro BitClr (), na qual precisamos definir um único 0 bit em um campo de 1's. Isso é feito usando o complemento 1 da mesma expressão, conforme indicado pelo operador til (~).

Depois que a máscara é criada, ela é aplicada ao argumento, como você sugere, usando os operadores bit a bit e (&), ou (|) e xor (^). Como a máscara é do tipo longa, as macros também funcionarão com caracteres char, short, int ou long.

A linha inferior é que esta é uma solução geral para toda uma classe de problemas. É claro que é possível e até apropriado reescrever o equivalente a qualquer uma dessas macros com valores explícitos de máscara toda vez que você precisar, mas por que fazer isso? Lembre-se de que a substituição da macro ocorre no pré-processador e, portanto, o código gerado refletirá o fato de que os valores são considerados constantes pelo compilador - ou seja, é tão eficiente usar as macros generalizadas quanto "reinventar a roda" toda vez que você precisar faça manipulação de bits.

Não convencido? Aqui está um código de teste - usei o Watcom C com otimização total e sem usar _cdecl para que a desmontagem resultante fosse a mais limpa possível:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (desmontado)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------

yogeesh
fonte
3
2 coisas sobre isso: (1) ao examinar suas macros, alguns podem acreditar incorretamente que as macros realmente configuram / limpam / invertem bits no argumento, no entanto, não há atribuição; (2) seu test.c não está completo; Eu suspeito que se você executou mais casos você gostaria de encontrar um (exercício leitor) problema
Dan
19
-1 Isso é apenas uma ofuscação estranha. Nunca reinvente a linguagem C ocultando a sintaxe da linguagem atrás das macros, é uma prática muito ruim. Depois, algumas curiosidades: primeiro, 1L é assinado, o que significa que todas as operações de bit serão executadas em um tipo assinado. Tudo o que é passado para essas macros retornará como assinado por muito tempo. Não é bom. Segundo, isso funcionará de maneira muito ineficiente em CPUs menores, uma vez que impõe muito tempo quando as operações podem estar no nível int. Terceiro, as macros funcionais são a raiz de todo mal: você não tem nenhum tipo de segurança. Além disso, o comentário anterior sobre nenhuma atribuição é muito válido.
Lundin
2
Isso irá falhar se argfor long long. 1Lprecisa ser o tipo mais amplo possível (uintmax_t)1. (Você pode se safar 1ull)
MM
Você otimizou para o tamanho do código? Nas CPUs mainstream da Intel, você obtém paradas parciais de registro ao ler AX ou EAX após o retorno dessa função, porque ela grava os componentes de 8 bits do EAX. (É bom para as CPUs AMD ou outras que não renomeiam registros parciais separadamente do registro completo. Haswell / Skylake não renomeia AL separadamente, mas renomeiam AH. ).
Peter Cordes
37

Use os operadores bit a bit: & |

Para definir o último bit em 000b:

foo = foo | 001b

Para verificar o último bit foo:

if ( foo & 001b ) ....

Para limpar o último bit em foo:

foo = foo & 110b

Eu costumava ter XXXbclareza. Você provavelmente estará trabalhando com representação HEX, dependendo da estrutura de dados na qual está compactando bits.

nsanders
fonte
7
Não há notação binária em C. As constantes inteiras binárias são uma extensão não padrão.
Lundin
Use XOR para alternar um pouco:foo = foo ^ MY_MASK
Peter L
Use NOT para inverter uma máscara para fazer clara:foo = foo & ~MY_MASK
Peter L
32

Para o iniciante, gostaria de explicar um pouco mais com um exemplo:

Exemplo:

value is 0x55;
bitnum : 3rd.

O &operador é usado, verifique o bit:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Alternar ou Inverter:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| operador: defina o bit

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
kapilddit
fonte
26

Aqui está minha macro aritmética de bits favorita, que funciona para qualquer tipo de matriz inteira não assinada de unsigned charaté size_t(que é o maior tipo que deve ser eficiente para trabalhar):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Para definir um pouco:

BITOP(array, bit, |=);

Para limpar um pouco:

BITOP(array, bit, &=~);

Para alternar um pouco:

BITOP(array, bit, ^=);

Para testar um pouco:

if (BITOP(array, bit, &)) ...

etc.

R .. GitHub PARE DE AJUDAR O GELO
fonte
5
É bom ler, mas é preciso estar ciente dos possíveis efeitos colaterais. O uso BITOP(array, bit++, |=);em loop provavelmente não fará o que o chamador deseja.
13/07
De fato. =) Uma variante que você pode preferir é separá-lo em 2 macros, 1 para endereçar o elemento da matriz e a outra para colocar o bit no lugar, ala BITCELL(a,b) |= BITMASK(a,b);(ambos usam acomo argumento para determinar o tamanho, mas o último nunca avaliaria adesde aparece apenas em sizeof).
R .. GitHub Pare de ajudar o gelo
1
@R .. Esta resposta é muito antiga, mas eu provavelmente preferiria uma função a uma macro neste caso.
PC Luddite
Menor: a 3ª (size_t)elenco parecem estar lá apenas para segurar alguns matemática não assinado com %. Poderia (unsigned)lá.
chux - Restabelece Monica
O (size_t)(b)/(8*sizeof *(a))desnecessariamente poderia diminuir bantes da divisão. Apenas um problema com matrizes de bits muito grandes. Ainda uma macro interessante.
chux - Restabelece Monica
25

Como isso está marcado como "incorporado", assumirei que você está usando um microcontrolador. Todas as sugestões acima são válidas e funcionam (leitura-modificação-gravação, uniões, estruturas, etc.).

No entanto, durante um período de depuração baseada em osciloscópio, fiquei surpreso ao descobrir que esses métodos têm uma sobrecarga considerável nos ciclos da CPU em comparação com a gravação de um valor diretamente nos registros PORTnSET / PORTnCLEAR do micro, o que faz uma diferença real quando existem loops estreitos / altos pinos de alternância do ISR de alta frequência.

Para quem não conhece: No meu exemplo, o micro possui um registro geral de estado de pino PORTn, que reflete os pinos de saída, e PORTn | = BIT_TO_SET resulta em uma leitura-modificação-gravação no registro. No entanto, os registradores PORTnSET / PORTnCLEAR levam um '1' para significar "por favor, faça este bit 1" (SET) ou "por favor, faça este bit zero" (CLEAR) e um '0' para significar "deixe o pino em paz". portanto, você acaba com dois endereços de porta, dependendo de definir ou limpar o bit (nem sempre é conveniente), mas uma reação muito mais rápida e um código montado menor.

John U
fonte
Micro era o Coldfire MCF52259, usando C no Codewarrior. Olhar para o desmontador / asm é um exercício útil, pois mostra todas as etapas pelas quais a CPU precisa executar para realizar a operação mais básica. Também vimos outras instruções de sobrecarga da CPU em loops críticos para o tempo - restringir uma variável fazendo var% = max_val custa uma pilha de ciclos de CPU toda vez, enquanto fazemos se (var> max_val) var- = max_val usa apenas algumas instruções. <br> Um bom guia para mais alguns truques está aqui: codeproject.com/Articles/6154/…
John U
Ainda mais importante, os registros de E / S mapeados na memória auxiliar fornecem um mecanismo para atualizações atômicas. A leitura / modificação / gravação pode ser muito ruim se a sequência for interrompida.
Ben Voigt
1
Lembre-se de que todos os registros de porta serão definidos como volatilee, portanto, o compilador não pode executar nenhuma otimização no código que envolve esses registros. Portanto, é uma boa prática desmontar esse código e ver como ficou no nível do assembler.
Lundin
24

A abordagem de campo de bits tem outras vantagens na arena incorporada. Você pode definir uma estrutura que mapeie diretamente os bits em um registro de hardware específico.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

Você precisa estar ciente da ordem de compactação de bits - acho que é o MSB primeiro, mas isso pode depender da implementação. Além disso, verifique como os campos de manipuladores do compilador cruzam os limites de bytes.

Você pode então ler, escrever, testar os valores individuais como antes.

Roddy
fonte
2
Praticamente tudo sobre os campos de bits é definido pela implementação. Mesmo que você consiga descobrir todos os detalhes sobre como seu compilador os implementa, usá-los em seu código certamente o tornará não portátil.
Lundin
1
@Lundin - É verdade, mas o sistema embarcado é complicado (principalmente nos registros de hardware, que é a minha resposta) nunca será útil de qualquer maneira.
Roddy
1
Talvez não entre CPUs totalmente diferentes. Mas você provavelmente deseja que seja portátil entre compiladores e entre projetos diferentes. E há muitas "quebra de bits" incorporadas que não estão relacionadas ao hardware, como codificação / decodificação de protocolo de dados.
Lundin
... e se você tem o hábito de usar campos de bits na programação incorporada, verá que seu código X86 é mais rápido e mais enxuto. Não em benchmarks simples, onde você tem toda a máquina para esmagar o benchmark, mas em ambientes multitarefa do mundo real, onde os programas competem por recursos. Vantagem CISC - cujo objetivo original era compensar as CPUs mais rapidamente do que os barramentos e diminuir a memória.
20

Verifique um pouco em um local arbitrário em uma variável do tipo arbitrário:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Uso da amostra:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

Notas: Ele foi projetado para ser rápido (dada sua flexibilidade) e não ramificado. Isso resulta em um código de máquina SPARC eficiente quando compilado o Sun Studio 8; Também testei usando o MSVC ++ 2008 em amd64. É possível criar macros semelhantes para definir e limpar bits. A principal diferença desta solução em comparação com muitas outras aqui é que ela funciona para qualquer local em praticamente qualquer tipo de variável.

John Zwinck
fonte
20

Mais geral, para bitmaps de tamanho arbitrário:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))
conta
fonte
2
CHAR_BITjá está definido por limits.h, você não precisa colocar no seu próprio BITS(e na verdade você piorar seu código ao fazê-lo)
MM
14

Este programa deve alterar qualquer bit de dados de 0 para 1 ou 1 para 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}
Gokul Naathan
fonte
14

Se você está mexendo bastante, talvez queira usar máscaras, o que tornará tudo mais rápido. As funções a seguir são muito rápidas e ainda são flexíveis (elas permitem rodar bit em bit maps de qualquer tamanho).

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

Observe que, para definir o bit 'n' em um número inteiro de 16 bits, faça o seguinte:

TSetBit( n, &my_int);

Cabe a você garantir que o número de bits esteja dentro do intervalo do mapa de bits que você passa. Observe que, para pequenos processadores endian, que bytes, palavras, dwords, qwords etc. são mapeados corretamente um para o outro na memória (principal motivo pelo qual os pequenos processadores endian são 'melhores' que os processadores big endian, ah, sinto uma guerra de chamas chegando em...).

Tim Ring
fonte
2
Não use uma tabela para uma função que possa ser implementada com um único operador. TQuickByteMask [n] é equivalente a (1 << n). Além disso, encurtar seus argumentos é uma péssima idéia. O / e% serão na verdade uma divisão, não bits / shift / bit a bit e, porque a divisão assinada por uma potência de 2 não pode ser implementada bit a bit. Você deve deixar o tipo de argumento int sem assinatura!
R .. GitHub Pare de ajudar o gelo
Qual o sentido disso? Isso apenas torna o código mais lento e difícil de ler? Não vejo uma única vantagem nisso. 1u << n é mais fácil de ler para programadores C e, esperançosamente, pode ser traduzido em uma única instrução de CPU com tick de clock. Sua divisão, por outro lado, será traduzida para algo em torno de 10 ticks, ou mesmo até 100 ticks, dependendo de quão mal a arquitetura específica lida com a divisão. Quanto ao recurso de bitmap, faria mais sentido ter uma tabela de pesquisa traduzindo cada índice de bits em um índice de bytes, para otimizar a velocidade.
Lundin
2
Quanto ao big / little endian, o big endian mapeará números inteiros e dados brutos (por exemplo, seqüências de caracteres) da mesma maneira: da esquerda para a direita msb para lsb em todo o bitmap. Embora little endian mapeie números inteiros da esquerda para a direita como 7-0, 15-8, 23-18, 31-24, mas os dados brutos ainda são da esquerda para a direita msb para lsb. Então, quão pouco endian é melhor para o seu algoritmo em particular está completamente além de mim, parece ser o oposto.
Lundin
2
@R .. Uma tabela pode ser útil se o seu plattform não pode mudar de forma eficiente, como o velho microchip MCU, mas é claro que, em seguida, a divisão da amostra é absolutamente ineficiente
jeb
12

Usa isto:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}
Peter Mortensen
fonte
5
Bem, ele usa ramificação ineficiente.
Asdf
3
@asdf O trabalho do compilador é a saída do binário mais eficiente, o trabalho do programador é escrever código claro
MM
3
Esta é uma boa demonstração de teste, configuração e limpeza de um bit específico. No entanto, é uma abordagem muito ruim para alternar um pouco.
Ben Voigt
10

Expandindo a bitsetresposta:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}
kendotwill
fonte
10

Se você deseja executar todas essas operações com programação C no kernel do Linux , sugiro usar APIs padrão do kernel do Linux.

Veja https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Nota: Aqui toda a operação acontece em uma única etapa. Portanto, todos eles são garantidos como atômicos, mesmo em computadores SMP, e são úteis para manter a coerência entre os processadores.

Jeegar Patel
fonte
9

O Visual C 2010, e talvez muitos outros compiladores, têm suporte direto para operações booleanas incorporadas. Um bit tem dois valores possíveis, assim como um booleano, para que possamos usar booleanos - mesmo que ocupem mais espaço do que um único bit. memória nesta representação. Isso funciona, até o sizeof()operador funciona corretamente.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Portanto, para sua pergunta, IsGph[i] =1ou IsGph[i] =0facilite a configuração e limpeza de bools.

Para encontrar caracteres não imprimíveis:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

Observe que não há nada de "especial" nesse código. Trata-se um pouco como um número inteiro - o que tecnicamente é. Um número inteiro de 1 bit que pode conter 2 valores e apenas 2 valores.

Uma vez, usei essa abordagem para encontrar registros duplicados de empréstimos, em que loan_number era a chave ISAM, usando o número de empréstimo de 6 dígitos como índice na matriz de bits. Savagely rápido, e após 8 meses, provou que o sistema de mainframe do qual estávamos obtendo os dados estava de fato com defeito. A simplicidade das matrizes de bits torna a confiança em sua correção muito alta - em comparação com uma abordagem de pesquisa, por exemplo.

Restabelecer Monica
fonte
std :: bitset é de fato implementado como bits pela maioria dos compiladores
galinette
@ galinette, concordou. O arquivo de cabeçalho #include <bitset> é um bom recurso nesse sentido. Além disso, o vetor de classe especial <bool> para quando você precisar alterar o tamanho do vetor. O C ++ STL, segunda edição, Nicolai M. Josuttis, aborda-os exaustivamente nas páginas 650 e 281, respectivamente. O C ++ 11 adiciona alguns novos recursos ao std :: bitset, de interesse especial para mim é uma função hash em contêineres não-ordenados. Obrigado pela atenção! Vou excluir meu comentário de cãibra no cérebro. Já chega de lixo na web. Eu não quero adicionar a isso.
3
Isso usa pelo menos um byte inteiro de armazenamento para cada um bool. Talvez até 4 bytes para configurações C89 que usam intpara implementarbool
MM
@MattMcNabb, você está correto. Em C ++, o tamanho do tipo int necessário para implementar um booleano não é especificado pelo padrão. Percebi que essa resposta estava errada há algum tempo, mas decidi deixá-la aqui, pois as pessoas aparentemente a acham útil. Para aqueles que querem pedaços de uso comentário de galinette é mais útil como é minha biblioteca pouco aqui ... stackoverflow.com/a/16534995/1899861
2
@RocketRoy: Provavelmente vale a pena mudar a frase que afirma que este é um exemplo de "operações de bit", então.
Ben Voigt
6

Use um dos operadores, conforme definido aqui .

Para definir um bit, usado int x = x | 0x?;onde ?está a posição do bit em formato binário.

Jason
fonte
2
0xé o prefixo para um literal em hexadecimal, não binário.
Ben Voigt
5

Aqui estão algumas macros que eu uso:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)
sam msft
fonte
5

Variável usada

int value, pos;

value - Data
pos - posição do bit que estamos interessados ​​em definir, limpar ou alternar.

Defina um pouco:

value = value | 1 << pos;

Limpe um pouco:

value = value & ~(1 << pos); 

Alterne um pouco:

value = value ^ 1 << pos;
Jeet Parikh
fonte
5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}
Sazzad Hissain Khan
fonte
O tipo de retorno de check_nth_bitpode ser bool.
Xeverous
@Xeverous sim, depende da intenção dos chamadores
Sazzad Hissain Khan
5

Suponhamos que poucas coisas primeiro sejam
num = 55 Integer para executar operações bit a bit (definir, obter, limpar, alternar).
n = 4Posição de bit com base em 0 para executar operações bit a bit.

Como ficar um pouco?

  1. Para obter o nthdeslocamento certo num, nvezes. Em seguida, execute AND bit a bit &com 1.
bit = (num >> n) & 1;

Como funciona?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

Como definir um pouco?

  1. Para definir um número específico de número. Turno esquerdo 1 nvezes. Em seguida, execute a operação OR bit a bit |com num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Como funciona?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

Como limpar um pouco?

  1. Turno esquerdo 1, nvezes ie 1 << n.
  2. Realize complemento bit a bit com o resultado acima. Para que o enésimo bit fique desabilitado e o restante do bit fique definido, ou seja,~ (1 << n) .
  3. Por fim, execute a operação AND bit a bit &com o resultado acima e num. Os três passos acima juntos podem ser escritos como num & (~ (1 << n));

Passos para limpar um pouco

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Como funciona?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Como alternar um pouco?

Para alternar um pouco, usamos o ^operador XOR bit a bit . O operador XOR bit a bit avalia como 1 se o bit correspondente de ambos os operandos for diferente; caso contrário, avalia como 0.

O que significa alternar um pouco, precisamos executar a operação XOR com o bit que você deseja alternar e 1.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

Como funciona?

  • Se o bit para alternar for 0, então 0 ^ 1 => 1.
  • Se o bit para alternar for 1, então 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Leitura recomendada - Exercícios do operador Bitwise

Pankaj Prakash
fonte
Obrigado pela explicação detalhada. Aqui está o link para o problema prática para BIT Magia ligação
Chandra Shekhar
4

Como você define, limpa e alterna um único bit?

Para resolver uma armadilha comum de codificação ao tentar formar a máscara:
1nem sempre é grande o suficiente

Que problemas acontecem quando numberé do tipo mais amplo 1?
xpode ser muito grande para a mudança que 1 << xleva ao comportamento indefinido (UB). Mesmo que xnão seja muito grande, ~pode não ser suficiente para gerar bits mais significativos.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

Para garantir que 1 seja amplo o suficiente:

O código pode ser usado de forma 1ullpedanática (uintmax_t)1e permitir que o compilador otimize.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Ou elenco - o que gera problemas de codificação / revisão / manutenção, mantendo o elenco correto e atualizado.

number |= (type_of_number)1 << x;

Ou promova gentilmente 1forçando uma operação matemática que seja tão larga quanto o tipo de number.

number |= (number*0 + 1) << x;

Como a maioria das manipulações de bits, melhor trabalhar com não assinados tipos em vez de assinados os

chux - Restabelecer Monica
fonte
Olhar interessante em uma pergunta antiga! Nem number |= (type_of_number)1 << x;nem number |= (number*0 + 1) << x;apropriadas para definir o bit de sinal de um tipo assinado ... Por uma questão de fato, nem é number |= (1ull << x);. Existe uma maneira portátil de fazer isso por posição?
chqrlie
1
@chqrlie IMO, a melhor maneira de evitar definir o bit de sinal e arriscar UB ou BID com turnos é usar tipos não assinados . O código assinado de turno altamente portátil é complicado demais para ser aceitável.
chux - Restabelece Monica
3

Uma versão do modelo C ++ 11 (colocada em um cabeçalho):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
Joakim L. Christiansen
fonte
Este código está quebrado. (Além disso, por que você tem ;após suas definições de função?)
melpomene
@melpomene O código não está quebrado, eu testei. Você quer dizer que não será compilado ou que o resultado está errado? Sobre o extra ';' Não me lembro, eles podem ser removidos de fato.
Joakim L. Christiansen
(variable & bits == bits)?
Melpomene
Obrigado por perceber, era para ser((variable & bits) == bits)
Joakim L. Christiansen
use std::bitsetem c ++ 11
pqnet 25/10/19
0

Este programa é baseado na solução acima do @ Jeremy. Se alguém quiser brincar rapidamente.

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true
Balaji Boggaram Ramanarayan
fonte
-2

Tente uma destas funções na linguagem C para alterar n bit:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Ou

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Ou

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}
Vincet
fonte
value << npode causar comportamento indefinido
MM /