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