Gibt es ein Bit-Äquivalent von sizeof() in C?

Lesezeit: 7 Minuten

Benutzer-Avatar
erukiform

Sizeof() funktioniert nicht, wenn es auf Bitfelder angewendet wird:

# cat p.c
  #include<stdio.h>
  int main( int argc, char **argv )
  {
    struct { unsigned int bitfield : 3; } s;
    fprintf( stdout, "size=%d\n", sizeof(s.bitfield) );
  }
# gcc p.c -o p
  p.c: In function ‘main’:
  p.c:5: error: ‘sizeof’ applied to a bit-field

… offensichtlich, da es keine Fließkomma-Teilgröße oder so etwas zurückgeben kann. Allerdings warf es eine interessante Frage auf. Ist Gibt es ein Äquivalent in C, das Ihnen die Anzahl der Bits in einer Variablen/einem Typ mitteilt? Idealerweise würde es auch für normale Typen funktionieren, wie z verkohlen und intzusätzlich zu Bitfeldern.

Aktualisieren:

Wenn es kein sprachliches Äquivalent von sizeof() für Bitfelder gibt, was ist die effizienteste Art, es zu berechnen – zur Laufzeit! Stellen Sie sich vor, Sie haben Schleifen, die davon abhängen, und Sie möchten nicht, dass sie brechen, wenn Sie die Größe des Bitfelds ändern – und kein fairer Betrug und die Bitfeldgröße und die Schleifenlänge zu einem Makro machen. 😉

  • Ziemlich sicher, dass das Layout der Struktur zur Kompilierzeit bestimmt wird. Während es also im Prinzip zur Laufzeit überprüft werden könnte (obwohl C keine Möglichkeit bietet, dies zu tun, wenn ich die Antworten unten richtig lese), wäre es nach der Kompilierung unveränderlich (mit einem bestimmten Compiler auf einer bestimmten Plattform). es kann natürlich je nach Compiler und Plattform variieren, basierend auf Wortgrenzenoptimierungen usw.).

    – Linde

    14. April 2019 um 1:53 Uhr

Benutzer-Avatar
schott

Sie können die Größe von Bitfeldern in C nicht bestimmen. Sie können jedoch die Größe in Bits anderer Typen ermitteln, indem Sie den Wert von verwenden CHAR_BITgefunden in <limits.h>. Die Größe in Bits ist einfach CHAR_BIT * sizeof(type).

Gehen Sie nicht davon aus, dass ein C-Byte ein Oktett ist, das ist es wenigstens 8 Bit. Es gibt tatsächlich Maschinen mit 16 oder sogar 32 Bit Bytes.

Zu deiner Bearbeitung:
Ich würde sagen, ein Bit-Feld int a: n; hat eine Größe von n Bit per Definition. Die zusätzlichen Füllbits, wenn sie in eine Struktur eingefügt werden, gehören zur Struktur und nicht zum Bitfeld.

Mein Rat: Verwenden Sie keine Bitfelder, sondern verwenden Sie (Arrays of) unsigned char und mit Bitmasken arbeiten. Auf diese Weise ist viel Verhalten (Überlauf, kein Auffüllen) gut definiert.

  • +1 cool, wusste nichts über CHAR_BIT. Was wäre, wenn Sie die Bitfeldgröße zur Laufzeit berechnen müssten?

    – kegelförmig

    23. Juli 2010 um 16:05 Uhr

  • @Dummy00001: Tut mir leid, aber du liegst falsch. Der C99-Standard gibt a untere Grenze für CHAR_BIT als 8. Und in Anhang J 3.4 heißt es ausdrücklich als Umsetzung definiert Verhalten “Die Anzahl der Bits in einem Byte.”

    – schott

    23. Juli 2010 um 16:29 Uhr

  • @Dummy00001: Ich stimme zu, dass ein Maschinenbyte != C-Byte. Aber ein Zeichen hat nicht immer 8 Bit. 5.2.4.2.1: "Their implementation-defined values shall be equal *or greater* in magnitude (absolute value) to those shown, with the same sign." Und dann zeigt: "- number of bits for smallest object that is not a bit-field (byte) CHAR_BIT 8". Und 6.2.6.1: "Values stored in non-bit-field objects of anyother object type consist of n×CHAR_BIT bits, where n is the size of an object of that type, in bytes."

    – schott

    23. Juli 2010 um 18:23 Uhr


  • Wann wird das dumm CHAR_BIT Argument endlich sterben? Auf allem außer DSPs und über 30 Jahre alten Legacy-Mainframes, CHAR_BIT ist 8. POSIX erfordert CHAR_BIT==8Windows ist an x86 gebunden, wo CHAR_BIT==8, und das gesamte Internet und die Interoperabilität zwischen vernetzten Maschinen basieren auf Oktetts. Sofern Sie kein sehr ungewöhnliches Ziel haben (in diesem Fall wird Ihr Code wahrscheinlich sowieso nicht portierbar sein), macht es absolut keinen Sinn, auch nur über die Möglichkeit nachzudenken CHAR_BIT!=8.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    24. Juli 2010 um 7:40 Uhr

  • +1 auf Ihren Rat: Mein Rat: Verwenden Sie keine Bitfelder, sondern verwenden Sie (Arrays of) unsigned char und mit Bitmasken arbeiten. Auf diese Weise ist viel Verhalten (Überlauf, kein Auffüllen) gut definiert. Ich empfehle uint8_t ist alles, statt unsigned charfür Klarheit und Deutlichkeit.

    – Gabriel Staples

    6. November 2020 um 22:09 Uhr


Es ist unmöglich, mit sizeof() eine Größe eines Bitfelds zu finden. Siehe C99:

  • 6.5.3.4 The sizeof operatorBitfeld wird eindeutig nicht von sizeof() unterstützt
  • 6.7.2.1 Structure and union specifiers Hier wird klargestellt, dass das Bitfeld kein eigenständiges Mitglied ist.

Andernfalls können Sie versuchen, dem Bitfeldmitglied -1u (Wert mit allen gesetzten Bits) zuzuweisen und dann den Index des höchstwertigen Bits zu finden. zB (ungetestet):

s.bitfield = -1u;
num_bits = ffs(s.bitfield+1)-1;

man ffs für mehr.

  • +1 Dies sieht näher nach einem optimalen Längenfinder aus. was ist ffs()?

    – kegelförmig

    23. Juli 2010 um 16:29 Uhr

  • @eruciform: ffs = ersten Satz finden. eine Funktion (häufig direkt einem CPU-Befehl zugeordnet), um das erste Bit zu finden, das im int gesetzt ist. Bits werden von 1 an nummeriert. Wenn input int 0 ist, dann ist return auch 0.

    – Dummy00001

    23. Juli 2010 um 17:10 Uhr

  • Hübsch! Das ist definitiv eine Funktion, die ich noch nie gesehen habe. und hier dachte ich, ich hätte im Laufe der Jahre die mit Spinnweben verkrusteten Ecken von C weitgehend besucht!

    – kegelförmig

    23. Juli 2010 um 20:41 Uhr

  • Es gibt keine (tragbare) Möglichkeit ffs wird zur Kompilierzeit berechnet, daher ist dies im Allgemeinen ineffizient. Jedochhängen Ihre Schleifen wahrscheinlich nicht von der Anzahl der Bits ab, sondern nur von Bitschleifen. In diesem Fall können Sie ein Bitfeld mit -1 initialisieren und so etwas tun for (counter.bf=-1; counter.bf; counter.bf>>=1). (Tipp: Dies funktioniert nur, wenn Ihr Bitfield ist unsigned.)

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    24. Juli 2010 um 7:43 Uhr


  • Beachten Sie, dass ffs eine POSIX-Funktion ist und nicht auf allen Plattformen oder für Datentypen größer als ein int verfügbar ist. Natürlich können Sie Ihre eigene Implementierung rollen, aber das wäre ein bisschen langsam.

    – Chiara Coetzee

    4. Januar 2012 um 8:51 Uhr


Benutzer-Avatar
Viktor Suárez

Ich habe diese Lösung implementiert[1]

#include <stdio.h>
#define bitoffsetof(t, f) \
    ({ union { unsigned long long raw; t typ; }; \
    raw = 0; ++typ.f; __builtin_ctzll(raw); })

#define bitsizeof(t, f) \
    ({ union { unsigned long long raw; t typ; }; \
    raw = 0; --typ.f; 8*sizeof(raw)-__builtin_clzll(raw)\
        -__builtin_ctzll(raw); })

struct RGB565 { unsigned short r:5, g:6, b:5; };
int main()
{
    printf("offset(width): r=%d(%d) g=%d(%d) b=%d(%d)\n",
        bitoffsetof(RGB565, r), bitsizeof(RGB565, r),
        bitoffsetof(RGB565, g), bitsizeof(RGB565, g),
        bitoffsetof(RGB565, b), bitsizeof(RGB565, b));
}

$ gcc bitfieldtest.cpp && ./a.out

Versatz (Breite): r=0(5) g=5(6) b=11(5)

[1] https://twitter.com/suarezvictor/status/1477697986243272706

UPDATE: Ich habe bestätigt, dass dies zur Kompilierzeit gelöst ist:

void fill(int *x)
{
    x[0]=bitoffsetof(RGB565, r);
    x[1]=bitsizeof(RGB565, r);
    x[2]=bitoffsetof(RGB565, g);
    x[3]=bitsizeof(RGB565, g);
    x[4]=bitoffsetof(RGB565, b);
    x[5]=bitsizeof(RGB565, b);
}

Assembler-Ausgabe:

fill:
.LFB12:
    .cfi_startproc
    movl    $0, (%rdi)
    movl    $5, 4(%rdi)
    movl    $5, 8(%rdi)
    movl    $6, 12(%rdi)
    movl    $11, 16(%rdi)
    movl    $5, 20(%rdi)
    ret

  • Interessanter Ansatz, aber Sie könnten hinzufügen, dass sich der Wert von Füllbits ändern kann, wenn ein Bitfeldwert geändert wird, daher ist nicht garantiert, dass der obige Code funktioniert.

    – chqrlie

    2. Januar um 18:22 Uhr

  • Ich denke, dass dieses Problem nicht möglich ist, da die aktuelle Implementierung nicht auf eine vorhandene Instanz der Struktur zugreift, sondern eine erstellt, um sie zu zählen, und sie als ersten Schritt auf Null initialisiert. Dann wird dieser verworfen. Ich denke, der Compiler kann das alles zur Kompilierzeit lösen und nur die benötigte Konstante bereitstellen. Das Makro erhält nur einen Strukturtyp und den Namen des Feldes, von außen können Sie nichts anfassen. Stellt dies klar, dass es kein Problem sein kann?

    – Viktor Suárez

    3. Januar um 19:46 Uhr

  • Ich bestätige, dass der Compiler das zur Kompilierzeit löst, siehe Update

    – Viktor Suárez

    3. Januar um 19:58 Uhr


  • Es beweist nur, dass ein bestimmter Compiler, der den Code zur Kompilierzeit ausführt, bei diesem Test gut abschneidet. Tatsache ist, dass in diesem speziellen Test keine Füllbits vorhanden sind, sodass das Verhalten gut definiert ist (mit Ausnahme des Aliasing-Problems, nämlich des Lesens von einem Union-Mitglied, an das zuletzt nicht geschrieben wurde, ein anderes, aber problematisches Problem), aber das Beispiel des OP struct { unsigned int bitfield : 3; } s; hat mindestens 5 Füllbits, deren Wert sich wann ändern könnte s.bitfield eingestellt ist.

    – chqrlie

    3. Januar um 23:14 Uhr


Verwenden Sie eine Reihe von #define-Anweisungen, um die Bitbreiten in der Definition der Struktur anzugeben, und verwenden Sie dann dasselbe #define beim Drucken oder was auch immer.

Sie erhalten dasselbe ‘einmal definieren, viele Male verwenden’, obwohl Sie die Bitfeldgrößendefinitionen haben, die Ihren globalen Namensraum überladen:

# cat p.c
#include<stdio.h>
int main( int argc, char **argv )
{
   #define bitfield_sz 3
   struct { unsigned int bitfield : bitfield_sz; } s;
   fprintf( stdout, "size=%d\n", bitfield_sz );
}
# gcc p.c -o p
# ./p
size=3
#

1379390cookie-checkGibt es ein Bit-Äquivalent von sizeof() in C?

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

Privacy policy