Hallo,
ich habe mir eine Applikation für den AVR in c++ (gcc) geschrieben, die
prima läuft.
Jetzt möchte ich etwas ram und flash sparen, da kommt mir spontan die
malloc in den Kopf. Ich reserviere nur speicher, gebe Ihn nie frei.
Am besten etwas Beispielcode (Pseudo):
1
class buffer
2
{
3
buffer(int size);
4
uint8_t *buf;
5
}
6
7
buffer::buffer(int size); // Die Groesse des Puffers festlegen
8
{
9
buf = malloc(size); // <<--- Das soll ersetzt werden
10
}
11
12
class inbuffer(70);
13
class outbuffer(30);
14
15
int main(void)
16
{
17
// do something
18
}
Der Speicher wird nie Freigegeben. Ich brauche das nur weil ich das oft
benutze und die größe des Puffers festlegen möchte.
Dafür verbraucht malloc unnötig Flash und Ram für die Verwaltung.
Eine ne Idee wie das C++ konform geht.
Danke
Juergen
Das mit dem std::cout ist nur ein Beispiel, das demonstriert, dass man
auf den Puffer zugreifen kann. Public-Member sind natürlich nicht schön.
Wie du das ausgestaltest, ist dir überlassen
Ok,
das mit dem template ist klar soweit.
Aber brauche ich dann nicht mehr speicher weil 2 Classen erzeugt werden
mit allen Funktionen die dazu gehören ? Der Constructor ist ja nicht
alles :-)
Ich habe hier keine Ahnung wie das implementiert ist.
Ansonsten hilft vermutlich nur ein test ?
Du wolltest ausdrücklich eine C++-Lösung. In C hätte ich sowas hier
vorgeschlagen:
[c]
char inbuffer[30];
char outbuffer[70];
int main (void)
{
// ....
}
Der Konstruktor der Template-Klasse da oben ist der Default-Konstruktor,
der macht gar nichts, andere Methoden gibts auch keine. Das Objekt
besteht nur aus dem Speicherbereich und aus nichts sonst. Ist also wohl
die Frage, welche Runtime-Lib (C oder C++) das mit weniger Aufwand
hinkriegt.
Trotzdem: Wenn der Buffer auch nur minimal gemanagt sein soll (z.B.
Schutz gegen Overrun), rate ich zur C++-Implementierung, weil du da den
Index-Operator ([]) überladen und mit einem assert (index < size)
sichern kannst.
Wenn sich dir Größe erst zur Laufzeit ergibt -- etwa wenn am Anfang von
main per Kommunikation die Größe eines anzulegenden Puffers erwartet
wird oder in einer Übertragungsfunktion, kann man in dieser Funktion
alloca verwenden:
http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Variable-Length.html#Variable-Length
Der Platz wird auf dem Stack angelegt und hat den Gültigkeitsbereich
einer in der aufrufenden Funktion angelegten auto-Variablen.
Johann
Normale Basisklasse mit allen übrigen Daten und Funktionen definieren.
Einen Zeiger auf den Puffer darin speichern und per Parameter des
Konstrukturs initialisieren.
Davon wird dann ein Template abgeleitet, in dem nur der Puffer definiert
und dessen Adresse und Länge an den Basisklassenkonstruktor übergeben
wird. Des Rest erbt man von der Basisklasse, folglich entsteht kein
duplizierter Code.
Auf ähnliche Art kriegt man auch Klassen für I/O-Module für mehrere
Schnittstellen mit Interrupts in den Griff. Indem der Interrupt-Handler
im Template steht (static wg. stackframe) und darin den gemeinsamen
Handler der Basisklasse aufruft (normale member funktion).
Man kann übrigens für solche Fälle auch ein Primitiv-malloc()
schreiben. Das nimmt sich einen großen Pool Speicher (kann man
ja als uint8_t malloc_pool[4096] oder so vereinbaren) und
alloziert daraus mit einem einfachen Zeiger, der immer hochgezählt
wird, munter drauflos. Die Kosten dafür sind minimal.
> Das nimmt sich einen großen Pool Speicher (kann man> ja als uint8_t malloc_pool[4096] oder so vereinbaren)
Ich dachte, es soll RAM gespart werden, lies mal die Ursprungsfrage.
tuppes wrote:
>> Das nimmt sich einen großen Pool Speicher (kann man>> ja als uint8_t malloc_pool[4096] oder so vereinbaren)>> Ich dachte, es soll RAM gespart werden, lies mal die Ursprungsfrage.
Der Speicher für den/die Puffer muss so oder so vorhanden sein. Der
Vorschlag von Jörg benötigt lediglich zusätzlich den Platz für einen
einzigen Pointer zur Verwaltung. Ich halte dieses Trivial-Malloc für die
sinnvollste Möglichkeit.
An malloc und dem Stack misfällt mir die fehlende
Speichernutzungskontrolle. Ich ziehe es daher vor, Speicher statisch zu
allozieren. Sieht man sofort, wieviel RAM weg ist bzw. für den Stack
übrig bleibt.
A. K. wrote:
> An malloc und dem Stack misfällt mir die fehlende> Speichernutzungskontrolle. Ich ziehe es daher vor, Speicher statisch zu> allozieren. Sieht man sofort, wieviel RAM weg ist bzw. für den Stack> übrig bleibt.
Wenn man das Trivial-Malloc (wie vorgeschlagen) auf einem Array
operieren lässt, sieht man es doch auch sofort. Und so wie ich es
verstanden habe, weiß er zur Compile-Zeit, was er an Puffer braucht, und
kann daher dieses Array entsprechend dimensionieren.
Stefan Ernst wrote:
> Wenn man das Trivial-Malloc (wie vorgeschlagen) auf einem Array> operieren lässt, sieht man es doch auch sofort.
Nur muss man dummerweise die Grösse dieses Arrays irgendwo festlegen.
Also zusammenrechnen, was welcher malloc verbraucht. Bei modularer
Programmierung arg fehlerträchtig. Und liefert (wenn falsch) gern mal
Laufzeitfehler wo Fehler zu Übersetzungszeit sinnvoller sind. Ich neige
dazu, solche Jobs dem Compiler und Linker zu überlassen. Wenns geht. Und
es geht ja.
A. K. wrote:
> Nur muss man dummerweise die Grösse dieses Arrays irgendwo festlegen.> Also zusammenrechnen, was welcher malloc verbraucht. Bei modularer> Programmierung arg fehlerträchtig. Und liefert (wenn falsch) gern mal> Laufzeitfehler wo Fehler zu Übersetzungszeit sinnvoller sind.
Ja, auch nicht von der Hand zu weisen.
Das Trivial-Malloc hätte halt den Vorteil, dass der komplette
Originalcode erhalten bleiben kann, und der hatte schließlich schon den
Status "läuft prima".
A. K. wrote:
> Nur muss man dummerweise die Grösse dieses Arrays irgendwo festlegen.> Also zusammenrechnen, was welcher malloc verbraucht.
Man kann auch stattdessen einfach eine Obergrenze für die maximale
Speicheradresse vorgeben und fängt dann (wie das normale malloc)
hinter dem Ende des .bss an zu allozieren. In der Debugversion
wird dann statt der Rückgabe von NULL irgendeine auffällige Fehler-
meldung produziert (das können wild blinkende LEDs sein oder was
auch immer die Applikation so bietet). Da die Allozierung sowieso
am Anfang erfolgt, sieht man das dann sofort.
Speicher sparen war auf das in diesem Fall unnötige Ram für die
Speicherverwaltung und auf den unnötigen Flash für free(). Wobei das der
Compiler vermutlich schon einspart. Wenn ich dann noch ein wenig sparen
kann durch eine "schmalere" malloc soll es mir recht sein.
Die Idee von Jörg ist gar nicht schlecht mit dem statischen Array.
Die Größen der einzelnen Puffer sind zur compilezeit bekannt und als
Konstante vorhanden.
Ich könnte also ein
"malloc_puffer[IN_BUF_SIZE+OUT_BUF_SIZE+XYZ_BUF_SIZE]" machen.
Vorteil wäre hier auch ich würde zur compile(link)zeit sehen wie viel
RAM ich verbrauche.
Muss ich mir nur ne einfache Malloc überlegen...
@Jörg: Das mit der blinkenden LED hab ich in den Constructoren schon
verbaut :-)
Habe ich bei folgendem was übersehen (Pseudocode aus dem Stegreif):
> Die Größen der einzelnen Puffer sind zur compilezeit bekannt und als> Konstante vorhanden.
Und aus welchem Grund willst du dann die Arrays nicht ganz einfach
statisch anlegen?
Es ist ein Object für Pufferhandling mit allen notwendigen Funktionen.
Einzig die Größe des Puffers kann beim Start variabel gemacht werden.
Bei einem Objekt ist eben alles schön versteckt und gekapselt. Wäre es
ein normales "C", hätte man viele Funktionen und Arrays, die irgendwie
zusammengehören. Einfach schöner :-)
Ich werde mir auch so eine minimalloc ohne free basteln. Das legt dann
ein Statisches Array an. So sieht man auch gleich wie viel Ram man
verbraucht beim Compilieren. Das dynamische ist bei so wenig Speicher
einfach Lotterie.
Jürgen Sachs wrote:
> Es ist ein Object für Pufferhandling mit allen notwendigen Funktionen.> Einzig die Größe des Puffers kann beim Start variabel gemacht werden.
Wann nun?
Beim Start (des Programmes), oder beim Compilieren?
> Bei einem Objekt ist eben alles schön versteckt und gekapselt. Wäre es> ein normales "C", hätte man viele Funktionen und Arrays, die irgendwie> zusammengehören. Einfach schöner :-)
Ist im Grunde auch nicht anders als in C++.
Auf dieser Ebene betrachtet und wenn man auf dieser Ebene halt macht,
hat C++ ledlich 'syntactic sugar' zu bieten.
> Das dynamische ist bei so wenig Speicher> einfach Lotterie.
Sehe ich auch so. Daher: Buffer statisch (also Globale) anlegen. Dann
sagt dir der Compiler/Linker nach dem Erstellen des Programms wieviel
Speicher du verbraucht hast.