Seite 1 von 1

struct mit buffer und Länge

Verfasst: 21.09.2025, 12:49
von starcow
Tach zusammen :-)

Ich hab mir einige Gedanken gemacht, welche Möglichkeiten es gibt, ein struct zu definieren, dass neben eines array buffers auch die Anzahl an Elementen dieses buffers speichert (statisch und unveränderlich).
(Es geht um plain C)

Auf dem Heap gibt es ja die Möglichkeit mittels "Flexible Array Member" (ab C99)

Code: Alles auswählen

struct s
{
    int cap;
    unsigned char buffer[];
};
Oder zu ANSI C Zeiten: Da hatte man sich ja das ganze einfach mittels folgender Methode ermöglicht:

Code: Alles auswählen

struct s
{
    int cap;
    unsigned char buffer[1];
};
Beide Techniken setzten nun aber bekanntlich voraus, dass der Speicher zur Laufzeit alloziert wird.
Im Data-Segment oder auf dem Stack geht das nicht.

Code: Alles auswählen

struct s * foo = malloc(sizeof(*foo) + sizeof(foo->buf[0]) * SIZE);
Da ich versuche, möglichst selten Speicher zur Laufzeit zu allozieren, platziere ich einige Elemente im Data-Segment (static oder falls möglich static const).

Ich hatte es jetzt in solchen Fällen mit einem Compound Literal gelöst:

Code: Alles auswählen

struct s_strbuf strbuf = { .cap = SIZE, .Buf = (unsigned char[SIZE]){ 0 } };
Vorteil: Sehr einfach, sehr kompakt.
Nachteil: Es ist nicht garantiert, dass das Compount Literal (also der buffer) direkt an das eigentliche struct im Speicher anschliesst: Cache-Misses/TLB-Misses.
Alternativ dazu, habe ich mir jetzt folgendes überlegt:

Code: Alles auswählen

static struct
{
    struct s_strbuf strbuf; unsigned char buf[SIZE];
}   block = { .strbuf.cap = SIZE, .strbuf.Buf = block.buf, .buf = { 0 } }; // Selbstreferenz nur bezüglich Adresse -> OK

Code: Alles auswählen

void func(struct s_strbuf * Strbuf)
{
    for(int i = 0; i < Strbuf->cap; i += 1)
        Strbuf->Buf[i] = 0xCDU;
    return;
}

int main(void)
{
    func(&block.strbuf);
    return 0;
}

Mit dieser Lösung ist der Anschluss im Speicher garantiert. So wie ich das sehe, könnte höchstens ein minimales padding durch das Alignment entstehen (je nach Typ des buffers).

Die Frage, die sich mir jetzt stellt: Lohnt sich das? Oder läuft es ohnehin fast immer darauf hinaus, dass der Compiler das Compound Literal gleich hinter dem struct platziert? Der buffer ist ja in meinem Beispiel 0 initialisiert und müsste daher im .bss Segment laden. Das struct hingegen enthält ja einen initialisierten pointer und ist daher folglich wohl im .data Segment.
Selbst wenn ich jetzt im Assemblercode nachschaue: Ohne das Verpacken in ein anonymes struct könnte sich das Speicherlayout wieder ändern (beim erneuten Compilieren).

Was denkt ihr dazu?

Edit1:
Kleiner Fehler im struct korrigiert.

Edit2:
Ich stelle erstaunt fest: mit buffer[0] = 0xCDU gibts im Terminal (cp 850, 437) tatsächlich die Deutschland-Flagge in schwarzweiss :-X

Re: struct mit buffer und Länge

Verfasst: 21.09.2025, 23:21
von Jonathan
Ich benutze jetzt ansich kein C, aber daher würde es mich interessieren, was die Anwendung hiervon ist? Also wofür brauchst du das und warum wäre das toll, und wie sieht die (schlechtere) Alternative aus, wenn man nur die C-Basics kann?

Re: struct mit buffer und Länge

Verfasst: 22.09.2025, 09:59
von Schrompf
Flagge in Schwarzweiß, wie ausm Fernsehen der 1930er... Passt gut zu 0xCDU :-D

Zum eigentlichen Thema: solche Views auf Array, Buffer, wasweißich sind ja ein übliches Konzept. Ich finde, Du hängst Da zu sehr an den Allokationsdetails, und gewichtest (für meinen Geschmack) die Verwendung der Views zu gering.

Anlegen: Ja, Du kriegst keine Garantien, dass Buffer und View-auf-den-Buffer beieinander liegen. Wenn Du Buffer und View lokal anlegst, ist durch die Deklarationsreihenfolge aber nach meinem Wissen festgelegt, dass die beieinander liegen. Ansonsten: manuell mit alloca().

Aber die Benutzung ist doch das Wichtigste. Und da würde ich so eine kleine Struktur mit 16byte Größe (Annahme: 64bit) by value rumreichen. Ich bin sicher, JEDE moderne CPU-Architektur hat mindestens 128bit breite Register, also wird mit nem modernen Compiler dieses Ding in nem hübsch ge-inlined Call nie den Speicher berühren. Und dann bleibt nur noch das Array selbst im Cache zu halten, und das ist ja als zusammenhängender Speicherblock quasi garantiert.

Re: struct mit buffer und Länge

Verfasst: 23.09.2025, 16:09
von antisteo

Code: Alles auswählen

ptr = (mystruct *)alloca(sizeof(mystruct) + array_len * sizeof(my_item));
alloca reserviert die n Bytes auf dem Stack

siehe
https://man7.org/linux/man-pages/man3/alloca.3.html

Re: struct mit buffer und Länge

Verfasst: 23.09.2025, 16:19
von Krishty
starcow hat geschrieben: 21.09.2025, 12:49Auf dem Heap gibt es ja die Möglichkeit mittels "Flexible Array Member" (ab C99)
Es ist AFAIK nicht vom Standard abgedeckt, aber alle großen Compiler unterstützen Flexible Array Inititialization für statische Daten: https://godbolt.org/z/c3xnoMqrj

Du siehst im Komplilat, dass das als ein Block angelegt wird.

Nur wenn du constexpr statt const forderst, kommt Clang 21 noch ins Stolpern.

Als nicht-Standard-Erweiterung sollte ich das vielleicht nicht nutzen, aber es ist bei mir seit Jahren drin, weil es einfach so wunderbar einfach und effizient ist …