Ich habe eine Struktur, die muss auf 64 KB auffüllen, um perfekt in ein eingebettetes Projekt zu passen, sodass es einen Flash-Block ausfüllt. Es gibt also eine #define
die die Elemente in der Struktur mit addiert sizeof()
und ermittelt, wie groß die pad[]
am Ende muss sein, damit die Gesamtgröße 64 KB beträgt.
Zum Beispiel :
#define SIZE_OF_MY_PAD (0x10000 - (sizeof(uint16_t) + sizeof(uint8_t)*32 + ... ))
typedef struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
...
uint8_t pad[SIZE_OF_MY_PAD];
};
Das hat lange super funktioniert, bis wir das Pad plötzlich in bestimmten Build-Konfigurationen gar nicht brauchen, weil es schon exakt 64k ist. Dies führt dazu, dass der Code fehlschlägt, da unser Compiler (nicht GCC) dies nicht zulässt pad[0]
.
Ich habe verschiedene Möglichkeiten ausprobiert, um einen Präprozessorwert zu erstellen, den ich in einem verwenden kann #if
Anweisung, wenn dies erkannt wird, aber es scheitert immer daran, obwohl sizeof()
ist legal drin #define
es ist nicht legal in #if
.
Dieses Problem kann ohne die Notwendigkeit eines Präprozessors mit Hilfe von anonymen Structs gelöst werden, die in C11 eingeführt wurden.
Definieren Sie den Flash-Typ als Union, die Member enthält, die in eine anonyme Struktur eingebettet sind. Machen char _pad[0x10000]
das andere Mitglied der Vereinigung, um die Gesamtgröße des eingeführten Typs zu erzwingen.
typedef union {
struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
float thirdElement;
};
char _pad[0x10000];
} flash_t;
Diese Lösung ist robust gegenüber Änderungen am Layout der Strukturmitglieder. Darüber hinaus vermeidet dies das Problem der Definition eines Arrays der Länge Null, das vom C-Standard technisch verboten ist (obwohl es in GCC zulässig ist). Zusätzlich kann man ein statisches Assert hinzufügen, um zu prüfen, ob die maximale Größe des Flashs überschritten wurde.
Beispielprogramm:
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
typedef union {
struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
float thirdElement;
// int kaboom[20000]; // will trigger assert if uncommented
};
char _pad[0x10000];
} flash_t;
_Static_assert(sizeof(flash_t) == 0x10000, "Oops, flash_t got too large");
int main() {
flash_t flash;
printf("offsetof(flash.firstElement) = %zi\n", offsetof(flash_t, firstElement));
printf("offsetof(flash.secondElementArray) = %zi\n", offsetof(flash_t, secondElementArray));
printf("offsetof(flash.thirdElement) = %zi\n", offsetof(flash_t, thirdElement));
printf("sizeof(flash) = %zi\n", sizeof flash);
return 0;
}
Erzeugt die erwartete Ausgabe:
offsetof(flash.firstElement) = 0
offsetof(flash.secondElementArray) = 2
offsetof(flash.thirdElement) = 36
sizeof(flash) = 65536
BEARBEITEN
-
Wie im Kommentar des Gewerkschaftsmitglieds vorgeschlagen _pad
könnte umbenannt werden in _rawData
weil Semantik von _pad
unterscheidet sich von pad
in der Frage.
-
Wenn ein Mitglied pad
innerhalb des Typs erforderlich ist, könnte man ihn als flexibles Mitglied am Ende der anonymen Struktur hinzufügen.
typedef union { struct { ...; uint8_t pad[]; }; char _rawData[0x10000]; } flash_t;
Fügen Sie ein statisches Assert auf der hinzu
sizeof
desstruct
. Sie können die Größe der Auffüllung nicht während der Kompilierzeit anpassen, aber Sie können die Kompilierung fehlschlagen lassen, falls sie nicht korrekt ist.– Eugen Sch.
7. April 2021 um 19:05 Uhr
Das ist zwar eine gute Frage, aber das ist in der etwas wahrscheinlich besser gelöst Linkernicht der Compiler.
– Rohr
8. April 2021 um 8:37 Uhr
@rd-florida Können Sie feststellen, welchen Compiler Sie verwenden? Ist es C11-kompatibel?
– RossPresser
8. April 2021 um 13:11 Uhr
@pipe: Ich bin anderer Meinung. Halten Sie den Linker davon fern. Ihr Verstand wird es Ihnen danken.
– Josua
8. April 2021 um 21:14 Uhr
Ich würde empfehlen, diesen Code in einem eigenen Abschnitt zu platzieren (
#pragma section
) und platzieren Sie dann den Abschnitt an der gewünschten Stelle im Linker-Skript.– Praline
9. April 2021 um 19:49 Uhr