Wie definiere und arbeite ich mit einem Array von Bits in C?

Lesezeit: 7 Minuten

Eddys Benutzeravatar
Wirbel

Ich möchte ein sehr großes Array erstellen, in das ich 0 und 1 schreibe. Ich versuche, einen physikalischen Prozess namens zufällige sequentielle Adsorption zu simulieren, bei dem Einheiten der Länge 2, Dimere, an einem zufälligen Ort auf einem n-dimensionalen Gitter abgelagert werden, ohne sich gegenseitig zu überlappen. Der Prozess stoppt, wenn auf dem Gitter kein Platz mehr für die Ablagerung weiterer Dimere ist (Gitter klemmt).

Anfangs beginne ich mit einem Gitter aus Nullen, und die Dimere werden durch ein Paar „1“ dargestellt. Bei der Ablagerung jedes Dimers wird die Stelle links vom Dimer blockiert, da sich die Dimere nicht überlappen können. Also simuliere ich diesen Vorgang, indem ich ein Tripel von Einsen auf dem Gitter hinterlege. Ich muss die gesamte Simulation viele Male wiederholen und dann die durchschnittliche Abdeckung in % berechnen.

Ich habe dies bereits mit einem Array von Zeichen für 1D- und 2D-Gitter gemacht. Im Moment versuche ich, den Code so effizient wie möglich zu gestalten, bevor ich an dem 3D-Problem und komplizierteren Verallgemeinerungen arbeite.

So sieht der Code vereinfacht in 1D aus:

int main()
{
    /* Define lattice */
    array = (char*)malloc(N * sizeof(char));

    total_c = 0;

    /* Carry out RSA multiple times */
    for (i = 0; i < 1000; i++)
        rand_seq_ads();

    /* Calculate average coverage efficiency at jamming */
    printf("coverage efficiency = %lf", total_c/1000);

    return 0;
}

void rand_seq_ads()
{
    /* Initialise array, initial conditions */
    memset(a, 0, N * sizeof(char));
    available_sites = N;
    count = 0;

    /* While the lattice still has enough room... */
    while(available_sites != 0)
    {
        /* Generate random site location */
        x = rand();

        /* Deposit dimer (if site is available) */
        if(array[x] == 0)
        {
            array[x] = 1;
            array[x+1] = 1;
            count += 1;
            available_sites += -2;
        }

        /* Mark site left of dimer as unavailable (if its empty) */
        if(array[x-1] == 0)
        {
            array[x-1] = 1;
            available_sites += -1;
        }
    }

    /* Calculate coverage %, and add to total */
    c = count/N
    total_c += c;
}

Für das eigentliche Projekt, das ich mache, handelt es sich nicht nur um Dimere, sondern auch um Trimere, Quadrimere und alle möglichen Formen und Größen (für 2D und 3D).

Ich hatte gehofft, dass ich mit einzelnen Bits anstelle von Bytes arbeiten könnte, aber ich habe herumgelesen und soweit ich das beurteilen kann, können Sie jeweils nur 1 Byte ändern, also muss ich entweder eine komplizierte Indizierung durchführen oder gibt es eine einfachere Möglichkeit?

Danke für deine Antworten

  • Beachten Sie für einmal, dass Sie an einzelnen Bits arbeiten: Wenn Effizienz von entscheidender Bedeutung ist, möchten Sie Ihre Operationen wahrscheinlich nach Möglichkeit auf mindestens ein Byte gleichzeitig anwenden (dh mehrere Koordinaten gleichzeitig betrachten). kostet also, wenn es richtig gemacht wird, nichts extra. Es lohnt sich wahrscheinlich nicht, dies zu tun, außer in den Engpassabschnitten des Codes.

    – Brian

    26. März 2010 um 17:48 Uhr

Benutzeravatar von aniliitb10
aniliitb10

Wenn ich nicht zu spät bin, Dies Seite gibt tolle Erklärung mit Beispielen.

Eine Reihe von int kann verwendet werden, um mit einem Array von umzugehen bits. Angenommen Größe von int sein 4 byteswenn wir über ein sprechen intwir beschäftigen uns mit 32 bits. Sagen wir, wir haben int A[10]bedeutet, dass wir daran arbeiten 10*4*8 = 320 bits und die folgende Abbildung zeigt es: (Jedes Element des Arrays hat 4 große Blöcke, von denen jeder a darstellt byte und jeder der kleineren Blöcke repräsentiert a bit)

Geben Sie hier die Bildbeschreibung ein

Also, um das einzustellen ktes Bit im Array A:

// NOTE: if using "uint8_t A[]" instead of "int A[]" then divide by 8, not 32
void  SetBit( int A[],  int k )
{
    int i = k/32;        //gives the corresponding index in the array A
    int pos = k%32;      //gives the corresponding bit position in A[i]

    unsigned int flag = 1;   // flag = 0000.....00001

    flag = flag << pos;      // flag = 0000...010...000   (shifted k positions)

    A[i] = A[i] | flag;      // Set the bit at the k-th position in A[i]
}

oder in der gekürzten Version

void  SetBit( int A[],  int k )
{
    A[k/32] |= 1 << (k%32);  // Set the bit at the k-th position in A[i]
}

ähnlich klar ktes Bit:

void  ClearBit( int A[],  int k )                
{
    A[k/32] &= ~(1 << (k%32));
}

und um zu testen, ob die ktes Bit:

int TestBit( int A[],  int k )
{
    return ( (A[k/32] & (1 << (k%32) )) != 0 ) ;     
}

Wie oben gesagt, können diese Manipulationen auch als Makros geschrieben werden:

// Due order of operation wrap 'k' in parentheses in case it
// is passed as an equation, e.g. i + 1, otherwise the first
// part evaluates to "A[i + (1/32)]" not "A[(i + 1)/32]"
#define SetBit(A,k)     ( A[(k)/32] |= (1 << ((k)%32)) )
#define ClearBit(A,k)   ( A[(k)/32] &= ~(1 << ((k)%32)) )
#define TestBit(A,k)    ( A[(k)/32] & (1 << ((k)%32)) )

  • Bei der Entscheidung, ob Sie aus Effizienzgründen mit den Funktionen oder Makros arbeiten, lohnt es sich, den generierten Maschinencode für Ihren Compiler zu vergleichen, um festzustellen, ob es einen Unterschied gibt (z. B. “gcc -O2 -S”. Wenn Sie diese von anderen Modulen aufrufen, siehe stackoverflow.com /Fragen/5987020/…). Wenn der Compiler gut genug ist, sollte der generierte Code für die Funktionen auf den höchsten Optimierungsstufen den Makros entsprechen. Der Vorteil des Festhaltens an den Funktionen besteht darin, dass sie für Redakteure, Debugger (auf niedrigeren Optimierungsstufen) und Menschen leichter zu verstehen sind.

    – jwmülllich

    29. Juni 2015 um 23:17 Uhr

  • Die Größe eines int hängt von Ihrem Compiler ab. Gehen Sie nicht davon aus, dass ein Int 4 Bytes groß ist. Prüfen. Auf kleinen Mikros kann ein Int 16 Bit betragen.

    – schnell_jetzt

    9. März 2016 um 8:23 Uhr

  • Es wäre sinnvoller, 1) zu verwenden unsigned int Anstatt von int beim Umgang mit Bits, 2) verwenden sizeof(unsigned)*CHAR_BIT Anstatt von 32oder 3) einfach verwenden uint32_t. unsigned int/sizeof(unsigned) wäre vielleicht eine bessere Idee, wenn Sie Architekturen mit unterschiedlichen unterstützen möchten int Größen, bei denen der Zugriff auf ein 32-Bit-Int mehr als 1 Anweisung erfordern würde.

    – Groo

    24. April 2019 um 8:31 Uhr


  • Ja, ich stimme zu, aber ich habe nur den Inhalt des Links dargestellt, falls dieser Link nicht mehr zugänglich ist (er wurde tatsächlich von jemandem angefordert und dieser Kommentar wird jetzt gelöscht

    – aniliitb10

    25. April 2019 um 7:36 Uhr


  • Ist x != 0 in TestBit unbedingt oder nützt es irgendeinen Vorteil?

    – Analphabet

    11. Januar 2020 um 13:47 Uhr


Benutzeravatar von nategoose
Nategans

typedef unsigned long bfield_t[ size_needed/sizeof(long) ];
// long because that's probably what your cpu is best at
// The size_needed should be evenly divisable by sizeof(long) or
// you could (sizeof(long)-1+size_needed)/sizeof(long) to force it to round up

Nun kann jedes long in einem bfield_t sizeof(long)*8 Bits enthalten.

Sie können den Index eines benötigten Big berechnen, indem Sie:

bindex = index / (8 * sizeof(long) );

und Ihre Bitnummer durch

b = index % (8 * sizeof(long) );

Sie können dann die Länge nachschlagen, die Sie benötigen, und dann das benötigte Bit daraus ausblenden.

result = my_field[bindex] & (1<<b);

oder

result = 1 & (my_field[bindex]>>b); // if you prefer them to be in bit0

Der erste kann auf einigen CPUs schneller sein oder kann Ihnen das Zurückschalten ersparen, wenn Sie Operationen zwischen demselben Bit in mehreren Bit-Arrays ausführen müssen. Es spiegelt auch das Setzen und Löschen eines Bits im Feld genauer wider als die zweite Implementierung. einstellen:

my_field[bindex] |= 1<<b;

klar:

my_field[bindex] &= ~(1<<b);

Sie sollten daran denken, dass Sie bitweise Operationen auf die Longs anwenden können, die die Felder enthalten, und das ist dasselbe wie die Operationen auf den einzelnen Bits.

Sie werden wahrscheinlich auch einen Blick auf die Funktionen ffs, fls, ffc und flc werfen wollen, falls verfügbar. ffs sollte immer verfügbar sein in strings.h. Es ist nur für diesen Zweck da – eine Reihe von Bits. Wie auch immer, es ist der erste Satz und im Wesentlichen:

int ffs(int x) {
    int c = 0;
    while (!(x&1) ) {
        c++;
        x>>=1;
    }
    return c; // except that it handles x = 0 differently
}

Dies ist eine übliche Operation, für die Prozessoren eine Anweisung haben müssen, und Ihr Compiler wird wahrscheinlich diese Anweisung generieren, anstatt eine Funktion wie die von mir geschriebene aufzurufen. x86 hat übrigens eine Anleitung dafür. Oh, und ffsl und ffsll sind die gleichen Funktionen, außer dass sie long bzw. long long sind.

  • Ein Byte ist nicht unbedingt 8 Bit lang! Technisch jeder long in deiner bfield_t kann halten CHAR_BIT * sizeof (long) Bits, nicht 8 * sizeof (long) Bits, es ist genau das auf vielen Architekturen CHAR_BIT gleich 8.

    – Eichhörnchen

    28. März 2018 um 21:56 Uhr


Davids Benutzeravatar
David

Sie können & (bitweises Und) und << (Linksverschiebung) verwenden.

Beispiel: (1 << 3) ergibt binär "00001000". Ihr Code könnte also so aussehen:

char eightBits = 0;

//Set the 5th and 6th bits from the right to 1
eightBits &= (1 << 4);
eightBits &= (1 << 5);
//eightBits now looks like "00110000". 

Dann skalieren Sie es einfach mit einem Array von Zeichen und ermitteln Sie das entsprechende Byte, das zuerst geändert werden soll.

Für mehr Effizienz könnten Sie im Voraus eine Liste von Bitfeldern definieren und sie in ein Array einfügen:

#define BIT8 0x01
#define BIT7 0x02
#define BIT6 0x04
#define BIT5 0x08
#define BIT4 0x10
#define BIT3 0x20
#define BIT2 0x40
#define BIT1 0x80

char bits[8] = {BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, BIT8};

Dann vermeiden Sie den Overhead der Bitverschiebung und können Ihre Bits indizieren, indem Sie den vorherigen Code in Folgendes umwandeln:

eightBits &= (bits[3] & bits[4]);

Wenn Sie C++ verwenden können, können Sie alternativ auch einfach eine verwenden std::vector<bool> die intern als Vektor von Bits definiert ist, komplett mit direkter Indizierung.

  • Verwenden std::vector<bool> wird ihm keine optimale Leistung bringen, da er am Ende zwei Suchen haben wird, um ein Paar Bits zu erhalten. Ob diese Strafe ausreicht, um die Erstellung einer eigenen Variante von zu rechtfertigen std::vector<bool> hängt davon ab, ob die Suchen (und Zuweisungen) selbst einen Engpass darstellen.

    – Brian

    26. März 2010 um 18:13 Uhr

  • Angenommen, C++ wäre eine Option (das OP erwähnte nur C), würde ich ohne zu zögern mit einem beginnen std::vector<bool>, einfach aus Gründen der Prägnanz und Lesbarkeit. Wenn ich dann eine bessere Leistung benötigte, würde ich ein Profil erstellen, um herauszufinden, wo der Engpass war. (Es könnte sehr gut in rand() sein und nicht in der Vektorsuche).

    – David

    26. März 2010 um 18:30 Uhr

  • Anstatt von char bits[8] = { ... }; du könntest es tun #define bits(x) BIT##x.

    – Chris Lutz

    26. März 2010 um 19:55 Uhr

  • Ich denke du meinst |= und | statt &= und &.

    – Wirbel

    27. März 2010 um 17:40 Uhr

  • „Für mehr Effizienz“ ⇒ es kommt auf die Architektur an. Vielleicht könnte Shift manchmal billiger sein als Array-Zugriff. Mit anderen Worten, dies ist eine sehr kleine “Verbesserung”, wenn überhaupt. Machen Sie sich keine Sorgen, es sei denn, Sie müssen es wirklich tun. Vorzeitige Optimierung ist die Wurzel allen Übels.

    – Denilson Sá Maia

    28. September 2011 um 9:25 Uhr

Benutzeravatar von 18446744073709551615
18446744073709551615

bitarray.h:

#include <inttypes.h> // defines uint32_t

//typedef unsigned int bitarray_t; // if you know that int is 32 bits
typedef uint32_t bitarray_t;

#define RESERVE_BITS(n) (((n)+0x1f)>>5)
#define DW_INDEX(x) ((x)>>5)
#define BIT_INDEX(x) ((x)&0x1f)
#define getbit(array,index) (((array)[DW_INDEX(index)]>>BIT_INDEX(index))&1)
#define putbit(array, index, bit) \
    ((bit)&1 ?  ((array)[DW_INDEX(index)] |= 1<<BIT_INDEX(index)) \
             :  ((array)[DW_INDEX(index)] &= ~(1<<BIT_INDEX(index))) \
             , 0 \
    )

Verwenden:

bitarray_t arr[RESERVE_BITS(130)] = {0, 0x12345678,0xabcdef0,0xffff0000,0};
int i = getbit(arr,5);
putbit(arr,6,1);
int x=2;            // the least significant bit is 0
putbit(arr,6,x);    // sets bit 6 to 0 because 2&1 is 0
putbit(arr,6,!!x);  // sets bit 6 to 1 because !!2 is 1

BEARBEITEN die dokumente:

“dword” = “double word” = 32-Bit-Wert (unsigned, aber das ist nicht wirklich wichtig)

RESERVE_BITS: number_of_bits --> number_of_dwords
    RESERVE_BITS(n) is the number of 32-bit integers enough to store n bits
DW_INDEX: bit_index_in_array --> dword_index_in_array
    DW_INDEX(i) is the index of dword where the i-th bit is stored.
    Both bit and dword indexes start from 0.
BIT_INDEX: bit_index_in_array --> bit_index_in_dword
    If i is the number of some bit in the array, BIT_INDEX(i) is the number
    of that bit in the dword where the bit is stored.
    And the dword is known via DW_INDEX().
getbit: bit_array, bit_index_in_array --> bit_value
putbit: bit_array, bit_index_in_array, bit_value --> 0

getbit(array,i) holt das dword, das das Bit i enthält, und Verschiebungen das Doppelwort Rechts, so dass das Bit i zum niederwertigsten Bit wird. Dann ein bitweise und mit 1 werden alle anderen Bits gelöscht.

putbit(array, i, v) überprüft zuerst das niedrigstwertige Bit von v; wenn es 0 ist, müssen wir das Bit löschen, und wenn es 1 ist, müssen wir es setzen.
Um das Bit zu setzen, machen wir a bitweise bzw des Dwords, das das Bit und den Wert 1 enthält nach links verschoben durch bit_index_in_dword: dieses Bit ist gesetzt und andere Bits ändern sich nicht.
Um das Bit zu löschen, machen wir a bitweise und des Dwords, das das Bit und die enthält bitweise Ergänzung von 1 nach links verschoben von bit_index_in_dword: Bei diesem Wert sind alle Bits auf Eins gesetzt, mit Ausnahme des einzigen Null-Bits an der Position, die wir löschen möchten.
Das Makro endet mit , 0 weil es sonst den Wert von dword zurückgeben würde, wo das Bit i gespeichert ist, und dieser Wert ist nicht aussagekräftig. Könnte man auch verwenden ((void)0).

Benutzeravatar von Paul R
Paul R

Es ist ein Kompromiss:

(1) Verwenden Sie 1 Byte für jeden 2-Bit-Wert – einfach, schnell, aber verbraucht 4x Speicher

(2) Bits in Bytes packen – komplexer, etwas Leistungsaufwand, verbraucht minimalen Speicher

Wenn Sie genügend Speicher zur Verfügung haben, wählen Sie (1), andernfalls überlegen Sie sich (2).

  • @Paul: Nein, es verbraucht 4x so viel Speicher, da er 2-Bit-Zahlen in 1 Byte speichern würde. Ich denke jedoch aus der Frage des OP, dass er sich bereits für (2) entschieden hat.

    – Brian

    26. März 2010 um 17:44 Uhr

  • @ Brian: Danke – ich habe diesen Teil verpasst – ich werde meine Antwort entsprechend aktualisieren.

    – PaulR

    26. März 2010 um 19:12 Uhr


  • @Paul: Nein, es verbraucht 4x so viel Speicher, da er 2-Bit-Zahlen in 1 Byte speichern würde. Ich denke jedoch aus der Frage des OP, dass er sich bereits für (2) entschieden hat.

    – Brian

    26. März 2010 um 17:44 Uhr

  • @ Brian: Danke – ich habe diesen Teil verpasst – ich werde meine Antwort entsprechend aktualisieren.

    – PaulR

    26. März 2010 um 19:12 Uhr


1410800cookie-checkWie definiere und arbeite ich mit einem Array von Bits in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy