Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Padding von Strukturen

3 views
Skip to first unread message

Michael Bäuerle

unread,
Jan 26, 2024, 12:08:05 PMJan 26
to
Ich hatte kürzlich eine Diskussion zum Padding in Strukturen und
deren Größe. Es ging dabei nicht um das Alignment der Elemente in der
Struktur, sondern um das Padding am Ende, ob /sizeof/ dieses mitrechnet
oder nicht und ob es (für das Aligment in einem Array aus solchen
Strukturen) vorhanden sein muss oder nicht.

Im Kern ging es um die Frage, ob es in C90 bzw. C99 *portabel* möglich
ist die Größe einer Struktur mit der Anzahl der Elementen eines Arrays
zu multiplizieren, um den Speicher für ein solches Array z.B. mit:

size_t n = 3;
struct foo *p = malloc(n * sizeof (struct foo));

anzufordern.

Die Frage, ob sizeof das Padding am Ende einer Struktur mitrechnet,
beantwortet die Norm eindeutig. Zitat aus dem C99 draft N1256:
|
| 6.5.3.4 The sizeof operator
|
| 3 [...]
| When applied to an operand that has array type, the result is the
| total number of bytes in the array. When applied to an operand that
| has structure or union type, the result is the total number of bytes
| in such an object, including internal and trailing padding.
^^^^^^^^^ ^^^^^^^^^^^^^^^^

Die Frage, ob dieses Padding vorhanden sein *muss* (passend für das
nötige Alignment der Zielmaschine), blieb aber offen.
Zitat aus dem C99 draft N1256:
|
| 6.7.2.1 Structure and union specifiers
|
| [...]
| 12 Each non-bit-field member of a structure or union object is
| aligned in an implementation-defined manner appropriate to its
| type.
| [...]
| 15 There may be unnamed padding at the end of a structure or union.

Das würde dem Compiler auch erlauben das Padding am Ende der Struktur
wegzulassen, so dass sizeof es nicht mitrechnet, solange er z.B.:

struct foo bar[3];

mit separatem Padding korrekt anordnen kann und Zugriffe mit bar[n]
diese Anordnung berücksichtigen. Auch für Zeiger + Integer könnte das
so funktionieren. Zitat aus dem C99 draft N1256:
|
| 6.5.6 Additive operators
|
| 8 [...] When an expression that has integer type is added to or
| subtracted from a pointer, [...]
| If the pointer operand points to an element of an array object, and
| the array is large enough, the result points to an element offset from
| the original element such that the difference of the subscripts of the
| resulting and original array elements equals the integer expression.

Da steht nicht, dass der Offset ein Vielfaches der Größe der Elemente
(entsprechend dem was sizeof liefert) sein muss.

Claus Reibenstein

unread,
Jan 26, 2024, 12:41:17 PMJan 26
to
Michael Bäuerle schrieb am 26.01.2024 um 17:51:

> Ich hatte kürzlich eine Diskussion zum Padding in Strukturen und
> deren Größe. Es ging dabei nicht um das Alignment der Elemente in der
> Struktur, sondern um das Padding am Ende, ob /sizeof/ dieses mitrechnet
> oder nicht und ob es (für das Aligment in einem Array aus solchen
> Strukturen) vorhanden sein muss oder nicht.

Ja, das wird mitgerechnet, und das muss auch vorhanden sein. Das steht
sicher an irgendeiner Stelle im Standard drin (habe leider keinen zur Hand).

Deine weiteren Argumente erübrigen sich damit.

Gruß
Claus

Peter J. Holzer

unread,
Jan 26, 2024, 1:36:24 PMJan 26
to
On 2024-01-26 16:51, Michael Bäuerle <michael....@stz-e.de> wrote:
> Ich hatte kürzlich eine Diskussion zum Padding in Strukturen und
> deren Größe. Es ging dabei nicht um das Alignment der Elemente in der
> Struktur, sondern um das Padding am Ende, ob /sizeof/ dieses mitrechnet
> oder nicht und ob es (für das Aligment in einem Array aus solchen
> Strukturen) vorhanden sein muss oder nicht.
>
> Im Kern ging es um die Frage, ob es in C90 bzw. C99 *portabel* möglich
> ist die Größe einer Struktur mit der Anzahl der Elementen eines Arrays
> zu multiplizieren, um den Speicher für ein solches Array z.B. mit:
>
> size_t n = 3;
> struct foo *p = malloc(n * sizeof (struct foo));
>
> anzufordern.

Portabel: Ja, auf jeden Fall. Jeder Kompiler, bei dem das nicht
funktionieren würde, wäre ein Kandidat für den Darwin-Award.

Standard-konform: Da bin ich mir nur zu 99.9% sicher ;-). Gerade diese
"Selbstverständlichkeiten" stehen oft nirgends explizit im Standard,
sondern man muss mehrere Textstellen hernehmen und zeigen, dass
mindestens eine davon verletzt wäre, wenn die Selbstverständlichkeit
nicht gilt. Es gibt also eine geringe Wahrscheinlichkeit, dass eine
Implementation, in der das nicht gilt, zwar unbrauchbar, aber
standardkonform wäre. (Das ist aber auch nichts neues: Ich glaube kurz
nach dem Erscheinen von C89 hat jemand gezeigt, dass ein ein Compiler,
der nur ein einziges Programm übersetzen kann, standardkonform ist).


> Die Frage, ob sizeof das Padding am Ende einer Struktur mitrechnet,
> beantwortet die Norm eindeutig. Zitat aus dem C99 draft N1256:
>|
>| 6.5.3.4 The sizeof operator
>|
>| 3 [...]
>| When applied to an operand that has array type, the result is the
>| total number of bytes in the array. When applied to an operand that
>| has structure or union type, the result is the total number of bytes
>| in such an object, including internal and trailing padding.
> ^^^^^^^^^ ^^^^^^^^^^^^^^^^
>
> Die Frage, ob dieses Padding vorhanden sein *muss* (passend für das
> nötige Alignment der Zielmaschine), blieb aber offen.

Das kommt jetzt darauf an, was Du mit "muss" und "passend" meinst.

Prinzipiell könnte der Compiler immer Code erzeugen, der auf jedes Byte
einzeln zugreift, und hätte dann keine Alignment-Requirements. Das wäre
aber sehr ineffizient und für manche (hardware-nahen) Anwendungen könnte
man ihn gar nicht verwenden. Oder er könnte z.B. bei der x86-Architektur
(bei der unaligned Zugriffe von der Hardware erledigt werden und daher
nur ein bisschen langsamer sind) auf Alignment verzichten.

> Zitat aus dem C99 draft N1256:
>|
>| 6.7.2.1 Structure and union specifiers
>|
>| [...]
>| 12 Each non-bit-field member of a structure or union object is
>| aligned in an implementation-defined manner appropriate to its
>| type.
>| [...]
>| 15 There may be unnamed padding at the end of a structure or union.
>
> Das würde dem Compiler auch erlauben das Padding am Ende der Struktur
> wegzulassen, so dass sizeof es nicht mitrechnet, solange er z.B.:
>
> struct foo bar[3];
>
> mit separatem Padding korrekt anordnen kann und Zugriffe mit bar[n]
> diese Anordnung berücksichtigen.

Das ist aber genau die Situation, in der trailing padding benötigt wird.
Für ein einzelnes struct ist es nicht notwendig, da der Compiler keinen
Grund hat, auf dieses Padding zuzugreifen. Aber in einem array of struct
müssen alle Bestandteile richtig aligned sein. Wenn Du also z.B.

struct foo {
int i;
char c;
}

hast und ein int ein alignment von 4 benötigt, dann kann bar nicht so
aussehen:

+0: bar[0].i
+4: bar[0].c
+5: bar[1].i
+9: bar[1].c
+10: bar[2].i
+14: bar[2].c

Denn dann würde man auf bar[1].i und bar[2].i einen bus error bekommen
(oder zumindest würde der Zugriff länger dauern).
Das verhindert man, indem man die Struct auf ein Vielfaches des
Alignments des striktesten Bestandteils verlängert:

+0: bar[0].i
+4: bar[0].c
+5: bar[0]./* anonymous padding */
+8: bar[1].i
+12: bar[1].c
+13: bar[0]./* anonymous padding */
+16: bar[2].i
+20: bar[2].c
+21: bar[0]./* anonymous padding */

> Auch für Zeiger + Integer könnte das so funktionieren. Zitat aus dem
> C99 draft N1256:
>|
>| 6.5.6 Additive operators
>|
>| 8 [...] When an expression that has integer type is added to or
>| subtracted from a pointer, [...]
>| If the pointer operand points to an element of an array object, and
>| the array is large enough, the result points to an element offset from
>| the original element such that the difference of the subscripts of the
>| resulting and original array elements equals the integer expression.
>
> Da steht nicht, dass der Offset ein Vielfaches der Größe der Elemente
> (entsprechend dem was sizeof liefert) sein muss.

"offset" ist hier nach meinem Textverständnis auch kein Substantiv
sondern ein Partizip Perfekt.

hp

Stefan Reuther

unread,
Jan 27, 2024, 5:35:19 AMJan 27
to
Am 26.01.2024 um 17:51 schrieb Michael Bäuerle:
> Ich hatte kürzlich eine Diskussion zum Padding in Strukturen und
> deren Größe. Es ging dabei nicht um das Alignment der Elemente in der
> Struktur, sondern um das Padding am Ende, ob /sizeof/ dieses mitrechnet
> oder nicht und ob es (für das Aligment in einem Array aus solchen
> Strukturen) vorhanden sein muss oder nicht.
[...]
> Das würde dem Compiler auch erlauben das Padding am Ende der Struktur
> wegzulassen, so dass sizeof es nicht mitrechnet, solange er z.B.:
>
> struct foo bar[3];
>
> mit separatem Padding korrekt anordnen kann und Zugriffe mit bar[n]
> diese Anordnung berücksichtigen.

Ich finde ebenfalls auf die Schnelle keine explizite Aussage "ja, muss
so sein".

Allerdings gibt es dieses Beispiel in n1548 (C11) 6.5.3.4:

# EXAMPLE 2
# Another use of the sizeof operator is to compute the number of
# elements in an array:
# sizeof array / sizeof array[0]

Ein Beispiel ist zwar nicht normativ, deutet aber schon an, dass das so
gemeint ist.

`sizeof array[0]` wäre hier `sizeof(struct_type)`.


Stefan

Peter J. Holzer

unread,
Jan 27, 2024, 6:21:26 AMJan 27
to
On 2024-01-27 10:25, Stefan Reuther <stefa...@arcor.de> wrote:
> Am 26.01.2024 um 17:51 schrieb Michael Bäuerle:
>> Ich hatte kürzlich eine Diskussion zum Padding in Strukturen und
>> deren Größe. Es ging dabei nicht um das Alignment der Elemente in der
>> Struktur, sondern um das Padding am Ende, ob /sizeof/ dieses mitrechnet
>> oder nicht und ob es (für das Aligment in einem Array aus solchen
>> Strukturen) vorhanden sein muss oder nicht.
> [...]
>> Das würde dem Compiler auch erlauben das Padding am Ende der Struktur
>> wegzulassen, so dass sizeof es nicht mitrechnet, solange er z.B.:
>>
>> struct foo bar[3];
>>
>> mit separatem Padding korrekt anordnen kann und Zugriffe mit bar[n]
>> diese Anordnung berücksichtigen.
>
> Ich finde ebenfalls auf die Schnelle keine explizite Aussage "ja, muss
> so sein".

Geht mir ebenso.


> Allerdings gibt es dieses Beispiel in n1548 (C11) 6.5.3.4:
>
> # EXAMPLE 2
> # Another use of the sizeof operator is to compute the number of
> # elements in an array:
> # sizeof array / sizeof array[0]
>
> Ein Beispiel ist zwar nicht normativ, deutet aber schon an, dass das so
> gemeint ist.

Ja, das habe ich auch gefunden. Zusätzlich:

Kapitel 6.2.5, Absatz 20 beschreibt Arrays als "contiguously allocated".
Leider fallen mir zwei mögliche Interpretationen davon ein, von denen
nur eine Padding zwischen den Members ausschließt. Ist also auch nicht
ganz eindeutig.

Der Standard erlaubt Padding explizit für Integer-Typen, Structs und
Unions und definiert das Verhalten. Padding innerhalb eines Arrays wird
soweit ich sehe nirgends erwähnt. Das kann man jetzt auch wieder auf
zwei Arten auslegen ("Ist offensichtlich nicht vorgesehen, sonst stünde
es da" oder "Ist nicht verboten, daher ist es erlaubt").

Die Rationale zu C89 schreibt auf S. 51:

| * if necessary, a hole is placed on the end to make the structure big
| enough to pack tightly into arrays and maintain proper alignment.

Das ist natürlich auch nicht normativ.

Insgesamt würde ich sagen, dass die Intention des Standards klar ist,
auch wenn der normative Text nicht eindeutig ist.

hp

Bonita Montero

unread,
Jan 28, 2024, 9:45:31 AMJan 28
to
Wird wohl keinen Compiler geben der es wagt, das anders
handzuhaben, auch wenn der Standard das nicht vorsähe.

Michael Bäuerle

unread,
Jan 29, 2024, 9:08:05 AMJan 29
to
Peter J. Holzer wrote:
> On 2024-01-27 10:25, Stefan Reuther <stefa...@arcor.de> wrote:
> >
> > [...]
> > Allerdings gibt es dieses Beispiel in n1548 (C11) 6.5.3.4:
> >
> > # EXAMPLE 2
> > # Another use of the sizeof operator is to compute the number of
> > # elements in an array:
> > # sizeof array / sizeof array[0]
> >
> > Ein Beispiel ist zwar nicht normativ, deutet aber schon an, dass das so
> > gemeint ist.
>
> Ja, das habe ich auch gefunden. Zusätzlich:
>
> Kapitel 6.2.5, Absatz 20 beschreibt Arrays als "contiguously allocated".
> Leider fallen mir zwei mögliche Interpretationen davon ein, von denen
> nur eine Padding zwischen den Members ausschließt. Ist also auch nicht
> ganz eindeutig.
>
> Der Standard erlaubt Padding explizit für Integer-Typen, Structs und
> Unions und definiert das Verhalten. Padding innerhalb eines Arrays wird
> soweit ich sehe nirgends erwähnt. Das kann man jetzt auch wieder auf
> zwei Arten auslegen ("Ist offensichtlich nicht vorgesehen, sonst stünde
> es da" oder "Ist nicht verboten, daher ist es erlaubt").
>
> Die Rationale zu C89 schreibt auf S. 51:
>
> | * if necessary, a hole is placed on the end to make the structure big
> | enough to pack tightly into arrays and maintain proper alignment.
>
> Das ist natürlich auch nicht normativ.
>
> Insgesamt würde ich sagen, dass die Intention des Standards klar ist,
> auch wenn der normative Text nicht eindeutig ist.

Sehe ich auch so. Ich war nur nicht sicher, ob ich vielleicht etwas
übersehen habe.

Michael Bäuerle

unread,
Jan 29, 2024, 9:08:05 AMJan 29
to
Peter J. Holzer wrote:
> On 2024-01-26 16:51, Michael Bäuerle <michael....@stz-e.de> wrote:
> >
> > [...]
> > Die Frage, ob sizeof das Padding am Ende einer Struktur mitrechnet,
> > beantwortet die Norm eindeutig. Zitat aus dem C99 draft N1256:
> > |
> > | 6.5.3.4 The sizeof operator
> > |
> > | 3 [...]
> > | When applied to an operand that has array type, the result is the
> > | total number of bytes in the array. When applied to an operand that
> > | has structure or union type, the result is the total number of bytes
> > | in such an object, including internal and trailing padding.
> > ^^^^^^^^^ ^^^^^^^^^^^^^^^^
> >
> > Die Frage, ob dieses Padding vorhanden sein *muss* (passend für das
> > nötige Alignment der Zielmaschine), blieb aber offen.
>
> Das kommt jetzt darauf an, was Du mit "muss" und "passend" meinst.
>
> Prinzipiell könnte der Compiler immer Code erzeugen, der auf jedes Byte
> einzeln zugreift, und hätte dann keine Alignment-Requirements. Das wäre
> aber sehr ineffizient und für manche (hardware-nahen) Anwendungen könnte
> man ihn gar nicht verwenden. Oder er könnte z.B. bei der x86-Architektur
> (bei der unaligned Zugriffe von der Hardware erledigt werden und daher
> nur ein bisschen langsamer sind) auf Alignment verzichten.

Mit GCC und gepackten Strukturen:
------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>


struct foo
{
int w;
char x;
int y;
char z;
} __attribute__((packed));


int main(void)
{
struct foo bar[2] = { { 1, 'a', 2, 'b' }, { 3, 'c', 4, 'd' } };

printf("sizeof (struct foo) : %u\n", (unsigned int)sizeof (struct foo));
printf("sizeof bar : %u\n", (unsigned int)sizeof bar);
printf("bar[1].y : %d\n", bar[1].y);

return 0;
}
------------------------------------------------------------------------

Ergibt auf NetBSD/amd64:
|
| sizeof (struct foo) : 10
| sizeof bar : 20
| bar[1].y : 4

Der Assembler-Code sieht gleich aus, wie zu erwarten war.
GCC für SPARC müsste da verschiedenen Code erzeugen.

Stefan Reuther

unread,
Jan 29, 2024, 12:04:45 PMJan 29
to
Am 29.01.2024 um 14:18 schrieb Michael Bäuerle:
> Peter J. Holzer wrote:
>> Prinzipiell könnte der Compiler immer Code erzeugen, der auf jedes Byte
>> einzeln zugreift, und hätte dann keine Alignment-Requirements. Das wäre
>> aber sehr ineffizient und für manche (hardware-nahen) Anwendungen könnte
>> man ihn gar nicht verwenden. Oder er könnte z.B. bei der x86-Architektur
>> (bei der unaligned Zugriffe von der Hardware erledigt werden und daher
>> nur ein bisschen langsamer sind) auf Alignment verzichten.
>
> Mit GCC und gepackten Strukturen:
> ------------------------------------------------------------------------
> #include <stdlib.h>
> #include <stdio.h>
[...]
> Ergibt auf NetBSD/amd64:
> |
> | sizeof (struct foo) : 10
> | sizeof bar : 20
> | bar[1].y : 4
>
> Der Assembler-Code sieht gleich aus, wie zu erwarten war.
> GCC für SPARC müsste da verschiedenen Code erzeugen.

Probier's aus: https://gcc.godbolt.org/z/sP8GGzvza

Ich hab hiermit probiert:

struct x {
int a;
char b;
} __attribute__((packed));

int get(struct x* p)
{
return p->a;
}

Ja, für SPARC (oder RISC-V, ARM-V7, ...) stoppelt der gcc da den int aus
4 Einzel-Byte-Loads zusammen.


Stefan

Rainer Weikusat

unread,
Jan 30, 2024, 11:06:21 AMJan 30
to
Michael Bäuerle <michael....@stz-e.de> writes:

[...]

> | 6.5.6 Additive operators
> |
> | 8 [...] When an expression that has integer type is added to or
> | subtracted from a pointer, [...]
> | If the pointer operand points to an element of an array object, and
> | the array is large enough, the result points to an element offset from
> | the original element such that the difference of the subscripts of the
> | resulting and original array elements equals the integer expression.
>
> Da steht nicht, dass der Offset ein Vielfaches der Größe der Elemente
> (entsprechend dem was sizeof liefert) sein muss.

Ist er ja auch nicht. Wie kommt man denn um Himmels Willen auf so eine
vollkommen groteske Idee?

-----
#include <stdio.h>

struct salami {
char *nirgendwo;
int irgendwas[2];
};

static struct salami spd[5];

int main(void)
{
struct salami *daresalami;

daresalami = spd + 3;
printf("Offset %zd, Elementgroesse %zd\n", daresalami - spd, sizeof(*spd));
return 0;
}
----

Gibt für mich

Offset 3, Elementgroesse 16

aus. Das drei kein Vielfaches von 16 ist, sollte klar sein.

Bonita Montero

unread,
Feb 2, 2024, 2:26:20 AMFeb 2
to
Ja, man kann das auch mutwillig missverstehen was Michael
geschrieben hat. Er meinte natürlich die Byte-Offsets, also
wenn Du vorher die Pointer nach char * gecastet hättest.

0 new messages