Forum: Compiler & IDEs 32 Bitter: Einen Array packen


von Chris J. (Gast)


Lesenswert?

Hallo,

mir sind Begriffe wie aliged, Endian etc und die Zugriffsarten schon 
bekannt, dass es eben einfacher ist wenn ein uint8_t 32 Bit beansprucht, 
da der ARM nunmal einen 32 Bit Datenbus hat und 4 Bytes in einem Haps 
holt, wenn sie an einer / 4 Grenze liegen.

In meinem Fall muss ich aber zwingend dafür sorgen, dass

uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr. Und das 
als lokale automatische Var auch nicht auf dem Stack, das muss ins .data 
segment. Stack ist nur 0x400 gross und es gibt keine Fehlermeldungen 
wenn der überläuft, nur Abstürze.
Zugriffe dürfen kostspieliger sein

_attribute((packed)) lässt sich leider nur auf structs und unions 
anwenden, scheinbar.

Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann 
was er gemacht hat?

Das hier hilft mir auch nicht wirklich weiter:

https://www.uninformativ.de/blog/postings/2015-10-25/0/POSTING-de.html

Grüße,
Christian

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Chris J. schrieb:
> uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr.

Das tut es schon. Da musst du nix mehr dran machen, denn uint8_t hat ein 
Alignment von 1. Bei gegebener Paranoia kannst du (bei Verwendung von 
C++) prüfen dass es wirklich nur 1024 Bytes sind:
1
static_assert(sizeof(feld) == 1024, "Feld hat falsche Groesse!");

Chris J. schrieb:
> Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann
> was er gemacht hat?
Was hast du denn für ein Problem? Es sollte so schon funktionieren.

von Rolf M. (rmagnus)


Lesenswert?

Chris J. schrieb:
> mir sind Begriffe wie aliged, Endian etc und die Zugriffsarten schon
> bekannt, dass es eben einfacher ist wenn ein uint8_t 32 Bit beansprucht,
> da der ARM nunmal einen 32 Bit Datenbus hat und 4 Bytes in einem Haps
> holt, wenn sie an einer / 4 Grenze liegen.

In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher 
braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.

> In meinem Fall muss ich aber zwingend dafür sorgen, dass
>
> uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr.

Das tut es (muss es - das ist in C so vorgeschrieben).

> Und das als lokale automatische Var auch nicht auf dem Stack,

Lokale automatische Variablen landen immer auf dem Stack oder direkt in 
Registern (was natürlich bei einem Array dieser Größe nicht geht).

> das muss ins .data segment.

Dann musst du sie statisch machen und nicht automatisch.

> _attribute((packed)) lässt sich leider nur auf structs und unions
> anwenden, scheinbar.

Weil Arrays per Definition immer gepackt sind. Das Attribut wäre also 
für Arrays völlig sinnlos.

> Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann
> was er gemacht hat?

Du kannst dir mit objdump -t (bzw arm-none-eabi-objdump oder wie immer 
das bei dir heißt) eine Liste aller Symbole anschauen, wo auch die Größe 
drin steht. Da sollte auch deine Variable zu finden sein, sofern du sie 
statisch definierst.

von Chris J. (Gast)


Lesenswert?

Rolf M. schrieb:
> In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher
> braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.

Ok... nur handelt es sich um internen Speicher. Der ist u.a. auch 
bitradressierbar. Ist das da dann auch so? Mache ich Wind um nichts? Ich 
habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau 
die Größe hat wie errechnet, sondern etwas größer ist.  Das gab Probs 
als ich einen struct per Funk an an einen 8 Bitter verschickt habe, 
obwohl ich da auch nur mit uint_xx gearbeitet hatte und nicht mit den 
üblichen Größen wie int, char etc.

Das mit dem Stack geht leider nicht, da bleibt mir wahrscheinlich nur 
der Heap.... auch wenn der vielleicht etwas altmodisch ist.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Chris J. schrieb:
> Ok... nur handelt es sich um internen Speicher. Der ist u.a. auch
> bitradressierbar.
Bit-Adressierbar auf ARM? Wie das? Meinst du Bit-Banding? Das zählt 
nicht, da weiß C nix von.

Chris J. schrieb:
> Ist das da dann auch so?
Du meinst den ganz normalen SRAM vom Controller? Dann Ja.

Chris J. schrieb:
> Mache ich Wind um nichts?
Vermutlich.

Chris J. schrieb:
> Ich
> habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau
> die Größe hat wie errechnet, sondern etwas größer ist.
Ja, bei structs gibts ggf. Padding-Bytes, damit die Adressen aller 
Member ein Vielfaches ihres jeweiligen Alignments sind.

Chris J. schrieb:
> da bleibt mir wahrscheinlich nur
> der Heap.... auch wenn der vielleicht etwas altmodisch ist.
Altmodisch? Wurde mittlerweile was besseres gefunden? Das Layout eines 
Datentyps hat nichts mit der Art zu tun, wie es angelegt wird, das ist 
auf Stack, Heap, globalen Variablen gleich. Interessant wird's höchstens 
bei lokalen temporären Variablen in Registern. Bei korrektem C Code 
merkst du da aber überhaupt nichts von.

Warum ist das ganze überhaupt wichtig? Ist dein Code vielleicht nicht 
korrekt? Du hast ein Array, du greifst auf die Elemente zu, C sorgt 
dafür dass es funktioniert. In welchem Speicher das wie abgelegt wird 
ist völlig unerheblich. Was ist das eigentliche Problem? Willst du das 
mit DMA benutzen, direkt über eine Schnittstelle rausschicken o.ä.?

von Chris J. (Gast)


Lesenswert?

Niklas G. schrieb:
> Altmodisch? Wurde mittlerweile was besseres gefunden? Das Layout eines
> Datentyps hat nichts mit der Art zu tun, wie es angelegt wird, das ist
> auf Stack, Heap, globalen Variablen gleich. Interessant wird's höchstens
> bei lokalen temporären Variablen in Registern. Bei korrektem C Code
> merkst du da aber überhaupt nichts von.

Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe 
ich eintrage bei dem array keine Compiler Warnung kommt. Ich habe mich 
schon mal totgesucht wegen "HardFault Errors" bis ich dann merkte, dass 
mir der Stack übergelaufen ist, der auf 0x100 stand.

Trotzdem danke für die Ausführungen, wieder etwas schlauer geworden.

Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit 
habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer 
ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich 
anfing zu programmieren an der Uni.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Chris J. schrieb:
> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe
> ich eintrage bei dem array keine Compiler Warnung kommt.
Klingt komisch. Wird das array überhaupt auf nichttriviale Art genutzt 
oder wird es vielleicht wegoptimiert?

von Peter II (Gast)


Lesenswert?

Chris J. schrieb:
> ? Ich
> habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau
> die Größe hat wie errechnet, sondern etwas größer ist.  Das gab Probs
> als ich einen struct per Funk an an einen 8 Bitter verschickt habe,
> obwohl ich da auch nur mit uint_xx gearbeitet hatte und nicht mit den
> üblichen Größen wie int, char etc.

aus dem Grund verschickt man auch keine Structs einfach so. Man baut das 
Datenpaket "manuell" zusammen.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Chris J. schrieb:
> Mache ich Wind um nichts?
Ja.

Chris J. schrieb:
> Ich habe nur gemerkt, dass ein struct
Ein struct ist ja aber auch was anderes als ein Array. In einem Array 
von structs liegen auch alle structs hintereinander, wenn auch die 
Member im struct selber nicht direkt hintereinander liegen muessen 
(padding).

Chris J. schrieb:
> da bleibt mir wahrscheinlich nur
> der Heap.... auch wenn der vielleicht etwas altmodisch ist.
???

Chris J. schrieb:
> Stack ist nur 0x400 gross und es gibt keine Fehlermeldungen
> wenn der überläuft, nur Abstürze.

Chris J. schrieb:
> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe
> ich eintrage bei dem array keine Compiler Warnung kommt.
Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel 
Speicher du hast.

Chris J. schrieb:
> Ich habe mich
> schon mal totgesucht wegen "HardFault Errors" bis ich dann merkte, dass
> mir der Stack übergelaufen ist, der auf 0x100 stand.
Schau mal hier, vielleicht hilft dir das:

https://mcuoneclipse.com/2015/08/21/gnu-static-stack-usage-analysis/
https://stackoverflow.com/questions/6387614/how-to-determine-maximum-stack-usage-in-embedded-system-with-gcc

Chris J. schrieb:
> Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit
> habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer
> ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich
> anfing zu programmieren an der Uni.
Wenn du dynamischen Speicher brauchst, wird der auf dem Heap allokiert. 
Muss ja auch, weil der Stack ja staendig neu beladen und auch wieder 
abgeraeumt wird (Funktions ein- und austritt). Und dynamischen Speicher 
braucht man fuer sehr viel mehr als binary-trees. Wenn du ohne 
dynamischen Speicher (malloc/calloc/new, free/delete) auskommst ist doch 
okay. Man kann vieles auch ohne dynamischen Speicher loesen. Auf einem 
uC ist dynamische Speicher aber mit etwas vorsicht zu geniessen, ggf. 
muss man sich selbst eine Speicherverwaltung bauen.

von Achim (Gast)


Lesenswert?

Mmh, du hast also Probleme und weisst nicht welche. Und vermutest, dass 
ein Array irgendwie ein bisschen größer ist als gedacht. Eigentlich 4 
Mal so groß, aber sizeof ist zu kompliziert, deshalb fragst Du hier nach 
einer Lösung. Für ein Problem, dass Du nicht kennst aber das ja da sein 
könnte.

von Rolf M. (rmagnus)


Lesenswert?

Chris J. schrieb:
> Rolf M. schrieb:
>> In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher
>> braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.
>
> Ok... nur handelt es sich um internen Speicher.

Wo der Speicher ist, hat keinen Einfluss auf das Speicherlayout des 
Compilers.

> Ich habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau
> die Größe hat wie errechnet, sondern etwas größer ist.

Ja, aber dann waren in deiner Struct die Elemente vermutlich von 
verschiedenen Typen. In einem Array sind aber alle Elemente vom selben 
Typ und haben damit die selben Alignment-Anforderungen.

> Das mit dem Stack geht leider nicht, da bleibt mir wahrscheinlich nur
> der Heap....

Verstehe ich nicht. Warum kann die Variable nicht statisch sein? Das 
scheint genau das zu sein, was du brauchst.

Chris J. schrieb:
> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe
> ich eintrage bei dem array keine Compiler Warnung kommt.

Solange das automatisch ist, existiert es zur Compilezeit noch gar 
nicht, da es erst beim Funktionsaufruf angelegt wird. Es wird bei der 
Speicherbelegung gar nicht berücksichtigt. Wenn du es dynamisch ("Heap") 
anlegst, wäre das übrigens auch nicht anders.

> Ich habe mich schon mal totgesucht wegen "HardFault Errors" bis ich dann
> merkte, dass mir der Stack übergelaufen ist, der auf 0x100 stand.

Wie viel Platz für den Stack später zur Laufzeit nötig sein wird, weiß 
der Compiler ja auch gar nicht.

> Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit
> habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer
> ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich
> anfing zu programmieren an der Uni.

Auf dem µC braucht man es auch eher selten, bzw. sollte eher darauf 
verzichten, wenn möglich. Auf dem PC ist es unverzichtbar. So sehr, dass 
in manchen Sprachen grundsätzlich alle Objekte dynamisch angelegt 
werden. Ist also alles andere als "altmodisch".

: Bearbeitet durch User
von asdfasd (Gast)


Lesenswert?

Wie oben schon jemand schrieb, die Variable "static" machen.
1
void f(...)
2
{
3
   static char x[1024]; // in .data
4
   ...
5
}
6
7
void g(...)
8
{
9
   static char y[1024]; // in .data
10
   ...
11
}
12
void h(...)
13
{
14
   char z[1024];        // on stack
15
   ...
16
}

Die Variablen x und y belegt dann natürlich dauerhaft den Speicher, je 
1024 Bytes - beim wiederholten Aufruf von f/g findet man sogar noch den 
Inhalt des vorherigen Durchlaufs drin. Wenn du dieses "Feature" nicht 
brauchst, wäre es sinnvoller, den Stack einfach 1024 Bytes größer zu 
machen und auto-Variablen (wie z) zu benutzen.

von Rolf M. (rmagnus)


Lesenswert?

asdfasd schrieb:
> Wenn du dieses "Feature" nicht brauchst, wäre es sinnvoller, den Stack
> einfach 1024 Bytes größer zu machen und auto-Variablen (wie z) zu
> benutzen.

Dann steht eben nicht das drin, was man beim letzten mal reingeschrieben 
hat, sondern irgendwas, was zufällig an dieser Speicherstelle steht.

von Chris J. (Gast)


Lesenswert?

Kaj G. schrieb:
> Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel
> Speicher du hast.

Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der 
alles wissen, auch zur Laufzeit. Das ergibt sich aus der Codeananalyse. 
In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene 
Parser haben in den Editoren. Bei Segger siehste auch ganz genau wieviel 
welches Modul braucht. Gehen tut das schon.

Aber insgesam sind meine Sorgen gelöst, vielen Dank dafür! Auch wenn es 
nur Hobby ist.. aber eines was mich nie loslassen wird :-)

von Der Andere (Gast)


Lesenswert?

Chris J. schrieb:
> Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der
> alles wissen, auch zur Laufzeit.

Zur Laufzeit des Programms? Der Satz ergibt so keinen Sinn!

Chris J. schrieb:
> Das ergibt sich aus der Codeananalyse.
> In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene
> Parser haben in den Editoren. Bei Segger siehste auch ganz genau wieviel
> welches Modul braucht. Gehen tut das schon.

So, und wie will deine Codeanalyse zum Beispiel bei einer rekursiven 
Funktion erkennen in welcher Rekursionstiefe die Rekursion (hoffentlich) 
abbricht?

Solche Tools funktionieren immer nur für bestimmte (einfache) Szenarien. 
Sie ersetzan aber nie Brain 2.0

von Rolf M. (rmagnus)


Lesenswert?

Chris J. schrieb:
> Kaj G. schrieb:
>> Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel
>> Speicher du hast.
>
> Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der
> alles wissen, auch zur Laufzeit. Das ergibt sich aus der Codeananalyse.

Der Linker macht keine Code-Analyse.

> In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene
> Parser haben in den Editoren.

Das reicht aber bei weitem nicht, um den Stackverbrauch zu kennen. Im 
Prinzip müsstest du den kompletten Code ausführen. Allerdings hängt auch 
dann der Stackverbrauch davon ab, welche Daten man zur Laufzeit 
reinsteckt. Man muss also erstmal die Inputs kennen, die zu maximalem 
Stackverbrauch führen.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Chris J. schrieb:
> eigentlich muss der
> alles wissen, auch zur Laufzeit.
Noe, kann er ja nicht wenn die Daten erst zur Laufzeit bekannt werden 
(z.B. Daten ueber Netzwerk etc.) Wie soll Linker, Compiler, etc. das 
ausmessen? Das geht nicht. Kein Tool kann wissen, ob da zur Laufzeit 1 
Byte oder 10 TB reinkommen. Was ja auch schon gesagt wurde: Rekursion.
Mal angenommen du willst die Fakultaet per rekursiver Funktion 
berechnen. Da macht es schon einen Unterschied ob du !6 oder !42 
berechnest. Sind die Argumente aber nicht zur Compilezeit bekannt, kann 
da auch nichts Analysiert werden.

Was ich oben verlinkt habe:
Mit dem Compilerflag -fstack-usage bekommst du den statischen 
Stackverbrauch pro Funktion, daraus kannst du Pi x Daumen grob was zu 
deinem Stackverbrauch sagen.

Chris J. schrieb:
> eigentlich muss der alles wissen
Eigentlich muss der Programmierer alles wissen und sollte sich gedanken 
zu seinen Datenstrukturen machen und selbst darauf aufpassen, dass sein 
Speicher reicht. ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.