Ich brauche einen Weg in einem C-Präprozessor #if, um zu testen, ob ein Wert ein Array der Größe 0 erstellt

Lesezeit: 4 Minuten

Benutzeravatar von RD Florida
RD Florida

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 #definees ist nicht legal in #if.

  • Fügen Sie ein statisches Assert auf der hinzu sizeof des struct. 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

Benutzeravatar von tstanisl
tstanisl

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;

  • Warum ist offsetof(thirdElement) 36 und nicht 34?

    – theonlygusti

    8. April 2021 um 10:21 Uhr

  • @theonlygusti, es ist Compiler-abhängig. Es versucht zu platzieren float an der durch 4 teilbaren Adresse. Nummer 36 ist die erste nach 34.

    – tstanisl

    8. April 2021 um 10:36 Uhr

  • Wahrscheinlich auch eine Umbenennung wert pad zu etwas wie rawData gleichzeitig – ansonsten den Code zum Ausfüllen pad mit bekannten Daten nach dem Ausfüllen der restlichen Datenstruktur möglicherweise völlig falsch.

    – Steve

    8. April 2021 um 15:37 Uhr


  • Ohne anonyme Strukturen würde es auch funktionieren, außer dass man schreiben müsste flash.data.firstElement oder wie auch immer, denn die Zwischenstruktur heißt jetzt, oder?

    – Peter – Setzen Sie Monica wieder ein

    8. April 2021 um 20:23 Uhr

  • @JanDorniak, AFAIK, der C-Standard erfordert nur, dass Offsets aufeinanderfolgender Mitglieder streng steigend sind und dass der Offset des ersten Mitglieds 0 ist. Alles andere ist implementierungsspezifisch.

    – tstanisl

    8. April 2021 um 21:18 Uhr


1414270cookie-checkIch brauche einen Weg in einem C-Präprozessor #if, um zu testen, ob ein Wert ein Array der Größe 0 erstellt

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

Privacy policy