Hallo,
ich stehe momentan vor der Aufgabe meinen Source code für zukünftige Projekte modularer zu gestalten. Als Beispiel soll hier ein einfacher Mittelwertfilter dienen.
Ich könnte jetzt den Mittelwertfilter als Modul definieren und den Speicher extra als Variable deklarieren. Das Modul wäre für alle Speichergrößen gleich. Die Länge wäre von der Speichergröße und der Initialisierung abhängig. Ich benötige aber keine flexible Länge die sich zur Laufzeit ändert. Ich muss jezt aber für jeden Filter den ich benutze den Speicher mit definierter Länge anlegen. Wahrscheinlich kann man dass über Macros usw. machen. Es bleiben aber wohl immer min. 2 Zeilen eine für den Speicher eine für die Initialisierung.
Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden nie wieder freigegeben. Somit würde auch keine fragmentierung auftreten. Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer gleich wäre. Natürlich wäre die Systemstartzeit etwas erhöht.
Gibt es andere Auswirkungen, die ich jetzt nicht auf dem Schirm habe? Oder ist das aus anderen Gründen ein NoGo bei der (sehr) hardwarenahen Softwareentwicklung?
Ich könnte jetzt den Mittelwertfilter als Modul definieren und den
Speicher extra als Variable deklarieren.
Ich lasse der Applikation beim Init einen Speicher notwendiger Größe übergeben. Pseudo Beispiel (ohne Fehlermeldung, Success etc).
Die Applikation stellt das RAM bereit, wie sie will. Stack, malloc, static, ... . Und Modul richtet alle instanzabhängigen Daten darin ein.
Wenn es nur eine Instanz gibt, fällt struct sModul für Header und Applikation auch noch weg. Dann ist nur der Speicher für N-Elemente zu übergeben. Teils auch anders rum: Du übergibst x Byte Speicher und bekommst die Anzahl an Elementen, die reinpassen.
@Bruno
ja. Das ist klar. Aber hier muss man eben die Struktur und den Speicher getrennt instanziieren. Mit malloc in der Init funktion würde das automatisch geschehen. Nur liegt der Speicher halt nicht bei den globalen Variablen sondern im heap
Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer
gleich wäre.
Die Laufzeit wäre bei den Durchläufen gleich, aber nicht zwangsläufig
identisch zu der bei einer schon zur Compilezeit bekannten
Speicheradresse.
Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit pointer auf festen Speicher erstelle und diese dem Modul übergebe dass er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch nicht fest stehen.
Bei Echtzeit ist natürlich schnelle Reaktion erwünscht.
Ist das so?
Besser:
Der Begriff Echtzeit charakterisiert den Betrieb von Echtzeitsystemen, die bestimmte Ergebnisse zuverlässig innerhalb einer vorbestimmten Zeitspanne, zum Beispiel in einem festen Zeitraster, liefern können
Das kann möglichst schnell bedeuten, muss aber nicht. Zu schnell kann sogar kontraproduktiv sein.
Wenn ich das richtig verstehe dann geht es um den ewigen Streit ob man dynamische Speicher (Heap) benutzen darf oder ob das böse ist.
Die Antwort lautet: kommt drauf an. Das Internet ist voll von Artikeln darüber. Chat GPT wird dir die Vor- und Nachteile ausführlichst erklären wenn du deine Randbedingung nennst.
Die kurze Antwort: Ja klar ist es in Ordnung, dynamischen Speicher bei der Initialisierung, also zur Laufzeit zu reservieren. Du darfst halt niemals in die Lage kommen dass dir der Speicher zur Laufzeit ausgeht. Dafür das von vornherein auszuschließen, gibt’s Techniken.
Mit malloc in der Init funktion würde das
automatisch geschehen. Nur liegt der Speicher halt nicht bei den
globalen Variablen sondern im heap
Du brauchts malloc (manchmal unerwünscht, weil sich nicht jeder an "nur zu Beginn" hält)
keine Freigabe/Wiederverwendung möglich
nicht alternativ auf Stack möglich
keine statische Prüfung des Speicherplatzes
Wenn der Aufrufer den Speicherplatz bereitstellt, werden viele Dinge im Modul viel einfacher. Gehst Du dann über zu beliebig vielen Instanzen (auch temporär) wird es noch einfacher, weil alle lokalen Variablen wegfallen. Man ist dann zu einem sauberen Design gezwungen. Und der Linker meckert sofort, wenn man sich beim Speicher total verschätzt hat.
Um die Laufzeit wegen der Indirektion brauchst Du Dir keine Gedanken machen.
Du brauchst kein malloc, kannst doch den Speicher im caller auch statisch anlegen. Dann kann der Compiler auch besser prüfen, ob es overflows gibt. Wenn sich die Anzahl der Mittelwertfilter Instanzen oder deren Pufferlänge nicht ändern zur Laufzeit ist doch alles tutti. In C geht's mit Makros, in C++ mit constexpr
Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für
den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden
nie wieder freigegeben.
Die Idee ist also, dass der Aufrufer den Speicher reserviert und auch die Instanz struct erstellt, nicht dein Filter Modul. Die Instanz struct wird per Zeiger übergeben und ist somit dein "Objekt" des Filtermoduls. Damit wird der state des Filters zu 100% beschrieben und du brauchst keine static variable mehr im Filtermodul und du kannst, so es denn gewünscht ist, beliebig viele Filter "Objekte" parallel erstellen.
Für C++ hat es Niklas über mir schon gezeigt. Braucht sogar kein constexpr, nur templates.
dass der Aufrufer den Speicher reserviert und auch
die Instanz struct erstellt, nicht dein Filter Modul.
den Speicher ja, aber die Instanz des structs erstellt die Init-Funktion des Moduls "in" dem reserviertem Speicher.
Das struct selber ist der Applikation gar nicht bekannt. Wenn an nur einen Ptr davon verwendet (woanders "Händler/hdl" genannt), ist das typsicher und OK.
Naja, du musst sicher sein dass in MODUL_RAM_SIZE die Größe des struct korrekt eingetragen ist, und die kann schnell mal geändert werden, und kann auch je nach Plattform oder Compiler -Optionen anders sein. Besonders "sicher" finde ich das nicht.
Außerdem fehlt da ein _Alignas bei "modul_ram"; das muss ebenso händisch konsistent gehalten werden.
Naja, du musst sicher sein dass in MODUL_RAM_SIZE die Größe des struct
korrekt eingetragen ist, und die kann schnell mal geändert werden, und
kann auch je nach Plattform oder Compiler -Optionen anders sein.
Sie muss nur >= sein und per CT_ASSERT abgesichert. Die Alternative ist, die Strukturen zu veröffentlichen. Das ist zwar in C++(-Klassen) üblich, aber möglichst zu vermeiden.
Sie muss nur >= sein und per CT_ASSERT abgesichert
Wenn sie zu groß ist, verschwendet man Speicher. Man sollte also genau treffen. Dann braucht es aber Fallunterscheidungen je nach Architektur, Compiler -Optionen etc.
Das ist zwar in C++(-Klassen) üblich, aber möglichst zu vermeiden.
Naja, ein Embedded-Projekt das so groß ist dass das zum Problem wird, kann wahrscheinlich auch einfach "new" benutzen, weil dann eh massig (SD)RAM vorhanden ist.
Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für
den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden
nie wieder freigegeben. Somit würde auch keine fragmentierung auftreten.
Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer
gleich wäre. Natürlich wäre die Systemstartzeit etwas erhöht.
malloc beim Systemstart ist nicht unübliches und ein gangbarer Weg. Einen vordefinierten Speicherbereich zu übergeben und darauf zu hoffen, das der dem Aufgerufenen langt erleichter die Pflege und Integration eher nicht. Dann lieber wirklich die gesamte Resourcenstruktur "public" machen. Bei C++ ist das quasi der Normalfall. (Ja wir werden bei uns auch zu C genötigt, nervt aber was solls...)
In manchen Fällen hat malloc/free auch zur Laufzeit Sinn, z.B. wenn es sich um Zusatzfunktionen handelt, wo man mit leben kann wenn die mal temporär nicht tun. (Z.B. Integrierte Webserver, Dateisysteme etc). Bei uns ist es so, dass beim Systemstart alle Module ihren benötigten RAM so weit es geht per malloc holen, manche Module machen es erst in der Konfigurationsphase. Zur Laufzeit ist Malloc dann in allen Echtzeit und Hauptfunktionen jedoch quasi "verboten". Das hat auch den Vorteil das man sich die ganze Fehlerbehandlung sparen kann, da es per Definition zur Laufzeit kein "Out Of Memory" geben kann.
private schränkt den Zugriff ein. Nicht die Veröffentlichung. (Das soll aber kein C/C++ Dingen werden: In beiden kann man natürlich auch nur Teile veröffentlichen und den Rest lokal erweitern)
Warum möchtest du die Interna eines Moduls für andere Komponenten der selben Anwendung (!) geheim halten?
Der Preis für statische Speicherverwaltung ist dass man die Größen der Klassen kennt. Der Compiler kann da wunderbar automatisch für sorgen. Kein schlechter Preis finde ich. Nervig wird das nur bei sehr großen Projekten, wo eine Änderung einer Klasse eine Neukompilation vieler anderer Dateien nach sich zieht, aber wie gesagt sind das nicht die Projekte, wo statische Speicherverwaltung wichtig ist.
Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit
pointer auf festen Speicher erstelle und diese dem Modul übergebe dass
er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch
nicht fest stehen.
Soll es möglichst schnell sein oder schnell genug?
Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit
pointer auf festen Speicher erstelle und diese dem Modul übergebe dass
er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch
nicht fest stehen.
Soll es möglichst schnell sein oder schnell genug?
Natürlich nur schnell genug. Also ein paar Takte für indirekte Adressierung usw. sind kein Problem. Mir ist klar dass es immer ein Kompromiss wird. Zu viel Abstraktion lohnt sich auch nicht, da man dann ja praktisch bei C++ landet und man hier das Rad nicht neu erfinden müsste.
Mir ging es nur darum ob ich etwas übersehe bei der Verwendung von Speicherallokation zur Laufzeit.
Bleibt die Frage wie das meine Kollegen sehen, da malloc bei hardwarenahen Entwicklern ja doch den Ruf hat etwas "Böses", was man auf keinen Fall verwendet, zu sein scheint.
Mal abgesehen von irgendwelchen exotischen Prozessoren, die mit unterschiedlich zu adressierenden RAM-Bereichen arbeiten koennen, wirds doch ausser fuer die Position im RAM voellig wurst sein, ob ich meinen Pointer auf ein Stueck RAM nun per
1
ptr=calloc(RUELPS,sizeof(sample_t));
oder per
1
staticsample_tptr[RUELPS];
kriege.
Ich wuerde mal vermuten, dass es da bei den Modularisierungbemuehungen noch deutlich lohnendere Ziele geben wird.
wirds
doch ausser fuer die Position im RAM voellig wurst sein,
Der Aufruf an calloc/malloc/new kostet (etwas) Laufzeit und natürlich Flash
Man sieht beim Kompilieren/Linken nicht den gesamten RAM-Verbrauch sondern kann ihn nur durch Ausführen ermitteln
Man muss alle malloc-Aufrufe mit Fehlerbehandlung versehen
Ist der Verbrauch zu hoch, bemerkt man dies erst beim Ausführen und muss dann ggf. mühselig nachvollziehen, welcher der (ggf. vielen) malloc-Aufrufe zu viel Speicher anfordert (nicht notwendigerweise der Aufruf, der dann 0 zurückgibt weil kein Speicher mehr frei ist)
Der Linker kann unbenutzte dynamisch angeforderte Speicherbereiche nicht wegoptimieren, auch wenn nie darauf zugegriffen wird; statisch allokierte Variablen aber schon, was das Umstrukturieren/Variantenbildung vereinfacht (gerade auch beim Entwickeln/Testen)
Man kann leicht versehentlich unbemerkt weitere malloc-Aufrufe nach der Initialisierungsphase einfügen sodass dann ggf. doch Fragmentierungsprobleme oder Verzögerungen auftreten
Man kann per dynamisch angeforderten Speicher auch nur dynamisch initialisieren, während statische Variablen mit fixer Initialisierung versehen werden kann, die dann vom Startup-Code sehr effizient automatisch vom Flash in den RAM kopiert wird
Ich finde
1
Filter<8>filter1;
2
Filter<4>filter2;
wunderbar simpel vom Handling her, es funktioniert einfach und effizient, und man kriegt "gratis" vom Linker die Aufsummierung/Prüfung des Speicherverbrauchs, mit Zusammenfassung im Map-File und schicker grafischer Darstellung in manchen IDEs. Man kann so natürlich auch C-Datenstrukturen "verpacken" und die Speicher-Reservierung in eine "Top-Level" C++-Datei verpacken und den Rest des Codes als C belassen.
Warum möchtest du die Interna eines Moduls für andere Komponenten der
selben Anwendung (!) geheim halten?
Es gibt viele Herangehensweisen an SW. Manche schwören auf Doxygen und erzeugen damit wunderbare PDFs zum Preis riesiger Interface-Header-Dateien.
Wir halten Header kurz mit minimalen Kommentaren. Da würden in modul.h wirklich nur 2-3 Forward-Structs drinstehen (als Hdl) und ein Haufen Funktionen für diese Hdl. Insgesamt vielleicht zwei Dutzend Zeilen. Keine Implementierungs-Details wie den Aufbau der Strukturen. Sie sind trotzdem nicht geheim, da jeder Zugriff hat, auch auf die Libs.
Der Preis für statische Speicherverwaltung ist dass man die Größen der
Klassen kennt.
Die Speicherverwaltung ist in meinem Beispiel ja auch statisch. Zum Preis eines obskuren und per CT-Assert überwachten Defines.
Keine Implementierungs-Details wie den Aufbau der Strukturen.
Wenn es dir um die Übersicht geht, kannst du ja auch zwei Header machen; einen mit der Forward-Deklaration, einen mit der kompletten Definition des structs. Nur die Source-Datei, welche die Instanz tatsächlich anlegt sowie die Datei die die Datenstruktur implementiert muss dann den Header mit der ganzen Definition inkludieren.
Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für
den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden
nie wieder freigegeben.
Kann man so machen. Aber bei
(sehr) hardwarenahen Softwareentwicklung
gibt es auch Ansätze, die weniger Resourcen benötigen wie zum Beispiel alloca: Den Speichen in main per alloca allokieren und die Speicheradresse ans Modul übergeben per Funktionsargument oder globaler Variable.
alloca benötigt weniger Resourcen als malloc weil:
alloca legt den Speicher auf dem Stack an, Allokation besteht also nur aus einer Manipulation des Stackzeigers statt einer komplexen Routine wie bei malloc. alloca spart also Programmspeicher.
alloca allokiert weniger Speicher als malloc, weil malloc noch etwas Platz für Verwaltungsinformationen braucht wie z.B. Größe des allokierten Bereichs, Listenverwaltung, etc. alloca spart also etwas RAM.
alloca spart auch Laufzeit, weil es wesentlich einfacher ist als malloc.
Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca allokierten Objekts dem einer lokalen Variable entspricht. Weil main nie verlassen wird und das Objekt nie freigegeben wird ist das aber kein Problem.
da malloc bei hardwarenahen Entwicklern ja doch den Ruf hat etwas
"Böses", was man auf keinen Fall verwendet, zu sein scheint.
Genauso wie goto.
In kleinen embedded Systemen gibt's halt das Problem, dass es für ein fehlgeschlagenes malloc keine sinnvolle Fehlerbehandlung gibt.
Soll ein ABS-System dann nur auf drei Rädern bremsen, oder so?
Entweder man weiss vorher, wieviel Speicher man benötigt, oder das Design ist fehlerhaft by Design.
Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca
allokierten Objekts dem einer lokalen Variable entspricht.
Was soll alloca dann überhaupt bringen gegenüber einer lokalen Variablen?
Alloca hätte nur dann einen Vorteil, wenn die Länge zur Compilezeit noch nicht feststeht und z.B. erst über ein Kommando übergeben wird.
Dafür muß man aber die Startadresse und die Länge immer zusätzlich mitführen.
Eine Variable ist dagegen dem Compiler bekannt, ihr Name ist die Startdaresse und sizeof() ihre Länge.
Was soll alloca dann überhaupt bringen gegenüber einer lokalen
Variablen?
alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen verwenden)
alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur
Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem
Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen
verwenden)
Das kann man auch genauso gut mit einem statisch allokierten Array machen, das man dann dem jeweils aktiven Modul übergibt. Oder einfach per union.
Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca
allokierten Objekts dem einer lokalen Variable entspricht.
Was soll alloca dann überhaupt bringen gegenüber einer lokalen
Variablen?
Semantisch gleich aber die Syntax ist etwas anders. Mit alloca:
1
#include<alloca.h>
2
3
intmain(void)
4
{
5
void*memory=alloca(get_alloc_size());
6
...
7
}
Ohne alloca:
1
intmain(void)
2
{
3
charmemory[get_alloc_size()];
4
...
5
}
Alloca hätte nur dann einen Vorteil, wenn die Länge zur Compilezeit noch
nicht feststeht und z.B. erst über ein Kommando übergeben wird.
Das geht auch mit einer Variablen, siehe obiger Code. Allerdings kann es sein, dass neuere C-Standards rumzicken, weil die Größe nicht bekannt ist.
Dafür muss man aber die Startadresse und die Länge immer zusätzlich
mitführen.
Die Länge ist dem Modul ja bekannt. Oder man kann sie übergeben als get_alloc_size() mit alloca oder auch als sizeof(memory) im 2ten Beispiel.
Eine Variable ist dagegen dem Compiler bekannt, ihr Name ist die
Startdaresse und sizeof() ihre Länge.
Bringt aber nix. Zumindest dann nicht, wenn der Speicher wie oben in main allokiert wird, weil im Modul kann man ja kein sizeof verwenden. sizeof funktioniert ja nur in main.
Einfachste Lösung ist get_alloc_size() zu übergeben oder im Modul aufzurufen. Ist immer noch weitaus billiger als malloc.
Eine denkbare Bremse wäre hingegen, wenn z.B. malloc langsames externes RAM allokiert und man nicht genug (schnelles) internes RAM dafür hat. Bei vielen kleinen embedded Systemen wird aber nur eine RAM-Art verwendet.
alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur
Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem
Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen
verwenden)
Das kann man auch genauso gut mit einem statisch allokierten Array
machen, das man dann dem jeweils aktiven Modul übergibt.
Bei einem statischen Array muss die Größe zur Compilezeit vorliegen. Bei alloca ist das nicht der Fall.
Bei einem statischen Array muss die Größe zur Compilezeit vorliegen.
Aber das hat dann nichts mit der dynamischen Umschaltung zwischen zwei Modulen zu tun. Es sind zwei orthogonale Probleme:
Größe zur Laufzeit bekannt
Zwischen 2 Modulen umschalten
Erstes kann man mit malloc, alloca, variable-length-arrays lösen. Zweites mit union oder Anlegen eines ausreichend großen Arrays. alloca ist also nicht, wie von die gesagt, die Lösung des 2. Problems.
Nur wenn beide Probleme gleichzeitig auftreten kann man alloca in Kombination mit der dynamischen Umschaltung nutzen, oder auch eine der Alternativen; die Vorteile von alloca haben nichts mit der dynamischen Umschaltung zu tun.
Bei einem statischen Array muss die Größe zur Compilezeit vorliegen.
Aber das hat dann nichts mit der dynamischen Umschaltung zwischen zwei
Modulen zu tun. Es sind zwei orthogonale Probleme:
Größe zur Laufzeit bekannt
Zwischen 2 Modulen umschalten
Erstes kann man mit malloc, alloca, variable-length-arrays lösen.
Zweites mit union oder Anlegen eines ausreichend großen Arrays. alloca
ist also nicht, wie von die gesagt, die Lösung des 2. Problems.
Beispiel wäre eine Game-App, die mehrere Spiele ausführen kann. Da läuft immer nur 1 Spiel, und daher kommen sich mehrere Spiele -- die ja immer zeitlich nachenander ausgeführt werden -- nicht in die Quere.
Ginge auch mit einer Union. Was weniger schön ist, weil alle lokalen Datenstrukturen global sichtbar sein müssen, damit main sie sieht.
Je nach verwendeter Toolchain gibt es auch Insellösungen, die keine globale Union und kein explizites Allokieren beötogen. Zum Beispiel könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven Speicher verwenden wollen:
1
externmemory1_tmemory1__asm("__heap_start");// in Modul 1
2
externmemory2_tmemory2__asm("__heap_start");// in Modul 2
Zum Beispiel
könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven Speicher
verwenden wollen:
Wenn man schon die GNU-Tools voraussetzt, kann man auch ein OVERLAY im Linkerscript definieren für die einzelnen Module, dann prüft immerhin der Linker ob der Speicher reicht.
Zum Beispiel
könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven
Speicher verwenden wollen:
Wenn man schon die GNU-Tools voraussetzt, kann man auch ein OVERLAY im
Linkerscript definieren für die einzelnen Module, dann prüft immerhin
der Linker ob der Speicher reicht.
Viele Anwender bevorzugen Lösungen, die kein eigenes Linker-Skript brauchen. Aber wer's mag...
Wobei sich der wirkliche RAM-Verbrauch erst zur Laufzeit ergibt. Zur Abschätzung hilf dann z.B.
Objekte im Static Storage brauchen eine zur Compilezeit bekannte
Größe in C.
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen umschalten möchte.
alloca braucht weniger RAM, weniger Programmspeicher und weniger
Laufzeit, wie bereits hier ausgeführt:
Beitrag "Re: Speicherverwaltung bei modularer Echtzeitsoftware"
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen umschalten möchte.
Ich hatte auch schon erwähnt, dass neuere C-Versionen VLAs nicht mögen.
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen umschalten möchte.
alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur
Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem
Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen
verwenden)
Sinnlose Diskussion wenn man meine Beiträge nicht genau liest.
Sinnlose Diskussion wenn man meine Beiträge nicht genau liest.
Naja, Deine Lösung "array bereitstellen" habe ich genauso als Beispiel auskodiert und explizit empfohlen. Da haben wir keinen Dissens.
Und dass es zwei unterschiedliche Aufgaben sind (Größer zur Compilezeit und Aufteilung auf 2 Module), ist Konsens.
Dissens gibt es quasi nur bei der Union:
weil sie 2 (oder mehr) unabhängige Module verbinden
gar nicht funktionieren, wenn es nicht 1 von 2 sondern z.B. 3-7 von 12 Modulen sind. Ein typisches Modul wäre die Instanz eines Uart-Treibers, wo abhängig vom Gegenüber 1..10k Fifo gewünscht werden und wo es 1..5 verschiedene geben kann.
Und dabei, dass alloca ein bequemer Standardweg ist und ein paar kleine Vorteile gegenüber meiner manuellen Verteilung hat.
(Dein implizit oben angebrachtes Argument, dass ab x kB oder MB alles egal ist und man darauf nicht mehr achten muss, ist in meinem Umfeld nicht stichhaltig. Die Probleme bleiben die gleichen)