Hi Ist es erlaubt einen array in einer Funktion zu deklarieren wo die länge während der Laufzeit bestimmt wird? erst geht um den uint8_t tmp[len]. Es geht um ARM GCC. uint_16 my_func(uint8_t *data, uint8_t len) { uint8_t tmp[len]; uint16_t result; for (uint8_t i = 0; i < len; i++) { tmp[i] = data++; } // do some thing with tmp and store it in result return result; }
In C ja, C++ nein (G++ unterstützt es als Erweiterung), das nennt sich VLA: https://en.wikipedia.org/wiki/Variable-length_array Da zwingt den Compiler aber dazu gewisse Verrenkungen bei der Berechnung von Stack-Pointern/Zugriffen zu machen. Ist daher ggf. für Mikrocontroller nicht optimal, im Zweifelsfall den generierten Code prüfen.
Moin, Also ich wuerde sowas ja eher mittels malloc() und statt der for-konstruktion mittels memcpy() machen. Aber ich bin auch kein Informatiker. Gruss WK
Dergute W. schrieb: > und statt der > for-konstruktion mittels memcpy() machen. Sicher? Da steht
1 | for (uint8_t i = 0; i < len; i++) { |
2 | tmp[i] = data++; |
3 | } |
Solange da kein '*' eingefügt wird, dürfte es eine Kunst sein, das Verhalten mit memcpy nachzubilden. So, wie das da steht, wird der Pointer 'data' nicht dereferenziert.
Moin, DerEinzigeBernd schrieb: > dürfte es eine Kunst sein, das > Verhalten mit memcpy nachzubilden. Nagut, erwischt ;-) Sagte ich schon, dass ich kein Informatiker bin? Gruss WK
:
Bearbeitet durch User
Du solltest überlegen, ob ein VLA überhaupt sinnvoll ist. Auf dem Stack muß eh genügend Platz für die maximale Größe des Arrays vorhanden sein. Der Speicher ist also bereits reserviert. Welchen Vorteil hätte es, das Array für einige Aufrufe kleiner zu halten? Verzögert höchsten den Crash ...
Man bräuchte so einen "Integer"-Typ, wie in Haskell. ( https://stackoverflow.com/questions/3429291/what-is-the-difference-between-int-and-integer ) Das hat aber gewisse Nachteile, wenn man hier und da die Terminierung vergisst. Außerdem ist das ganze alles andere als Alignmentfreundlich.
Hier kannst Du die Länge des Arrays nach oben abschätzen:
1 | #include <stdint.h> |
2 | #include <stdlib.h> |
3 | #include <assert.h> |
4 | |
5 | uint16_t my_func(const uint8_t* data, const uint8_t len) { |
6 | assert(data); |
7 | uint8_t tmp[256]; |
8 | for (uint8_t i = 0; i < len; i++) { |
9 | tmp[i] = *data++; |
10 | }
|
11 | return tmp[0]; |
12 | }
|
> Hier kannst Du die Länge des Arrays nach oben abschätzen
Wenn man die Obergrenze nicht weiß, darf man VLAs nicht benutzen. Im
Gegensatz zu malloc kennen VLAs keine Fehlerrückmeldung, die man
behandeln könnte - das Programm stürzt einfach ab.
Ich weiß nicht, ob eine vorherige Überprüfung der Arraygröße:
1 | void foo(int size) |
2 | {
|
3 | if (size <= 64) { |
4 | char vla[size]; |
5 | ...
|
6 | }
|
7 | }
|
überhaupt erlaubt ist. Variablen-Deklarationen werden gerne in die Funktionspräambel gezogen - bei obigem Beispiel würde das schief gehen. Sinnvoller wäre hier eh ein "char vla[64]". tl;dr: VLAs einfach nicht benutzen - sind wie mallocs ohne Fehlerbehandlung.
foobar schrieb: > Sinnvoller wäre hier eh ein "char vla[64]". Ja, in der Regel bringt einem ein VLA da keinen Vorteil. Wenn die Größe bis zu 64 Bytes sein kann, muss ja sowieso genug Speicher dafür zur Verfügung stehen. Da kann man das lokale Array auch gleich fix in dieser Größe anlegen. foobar schrieb: > tl;dr: VLAs einfach nicht benutzen - sind wie mallocs ohne > Fehlerbehandlung. malloc() benötigt allerdings mehr Ressourcen, sowohl im Bezug auf Speicher, als auch auf Ausführungszeit, und man muss an das dazugehörige free() denken. Im schlimmsten Fall handelt man sich noch Speicherfragmentierung ein.
:
Bearbeitet durch User
Rolf M. schrieb: > Im schlimmsten Fall handelt man sich noch > Speicherfragmentierung ein. Bei einem Block, der fast so groß wie das verfügbare Ram ist, ist die Gefahr allerdings gering. Oliver
Ich: > tl;dr: VLAs einfach nicht benutzen - sind wie mallocs ohne > Fehlerbehandlung. Rolf: > malloc() benötigt allerdings mehr Ressourcen Das ist nicht der Punkt. In C gibt es keine Möglichkeit, festzustellen, ob für ein bestimmtes VLA noch genügend Platz auf dem Stack ist. Entweder es klappt oder es klappt nicht. Klappt es nicht, stürzt das Programm ab. Ein VLA verhält sich also so, als würde man malloc benutzen, ohne den Rückgabewert auf NULL zu überprüfen. Jeder stuft sowas als klassischen Programmfehler ein - aber VLAs sind ok?
foobar schrieb: > Das ist nicht der Punkt. In C gibt es keine Möglichkeit, festzustellen, > ob für ein bestimmtes VLA noch genügend Platz auf dem Stack ist. > Entweder es klappt oder es klappt nicht. Klappt es nicht, stürzt das > Programm ab. Ein VLA verhält sich also so, als würde man malloc > benutzen, ohne den Rückgabewert auf NULL zu überprüfen. Jeder stuft > sowas als klassischen Programmfehler ein - aber VLAs sind ok? Dafür wurden doch schon vor 30 Jahren Exceptions erfunden. Die gibt es seit Windows NT in C und in C++ - genau für solche Fälle... Der Stack kann auch problemlos so gross wie der vorhandene RAM gesetzt werden. Dank 64 Bit ist das heute kein Problem mehr. Und wenn dir Exceptions zu esoterisch sind, dann frag doch einfach die Stackgrösse ab (falls du zu faul bist, die zur Compilezeit ausreichend gross zu setzen)? malloc hat in der Praxis auch Probleme, da kannst du testen was du willst, es kommt IMMER ein gültiger Pointer zurück, und dann crash es doch - Linux ist dafür berüchtigt. Bei solchen Systemen kannst du nur den gesamten benötigten Speicher beim Starten im voraus allokieren, UND mit Einsen (Nicht Null - das kann das OS wegoptimieren) vollschreiben. Wenn das Programm dann noch lebt, dann ist es gutgegangen...
:
Bearbeitet durch User
Udo K. schrieb: > Dafür wurden doch schon vor 30 Jahren Exceptions erfunden. > Die gibt es seit Windows NT in C und in C++ - genau für solche Fälle... > Der Stack kann auch problemlos so gross wie der vorhandene RAM gesetzt > werden. Dank 64 Bit ist das heute kein Problem mehr. Welcher Compiler generiert denn bitte exceptions, wenn VLAs überlaufen? > Und wenn dir Exceptions zu esoterisch sind, dann frag doch einfach die > Stackgrösse ab (falls du zu faul bist, die zur Compilezeit ausreichend > gross zu setzen)? Wie fragt man denn die Stackgröße ab? Und wie findet man heraus, wie viel davon schon verwendet wurde? > malloc hat in der Praxis auch Probleme, da kannst du testen was du > willst, es kommt IMMER ein gültiger Pointer zurück, und dann crash es > doch - Linux ist dafür berüchtigt. It's a feature. Mit overcommit ist das auch nicht vermeidbar. Du kannst overcommit natürlich auch abschalten.
MaWin O. schrieb: > Wie fragt man denn die Stackgröße ab? Und wie findet man heraus, wie > viel davon schon verwendet wurde? Ich habe dir ein Windows Beispiel mit dem C Quelltext angehängt, mit dem du 1 GB auf dem Stack allokieren kannst. Wenn das nicht geht, gibt es eine Exception mit einer Fehlermeldung. Die Grösse des Stacks steht im TEB (Thread Environment Block), es gibt auch offiziellere Windows Funktionen, um den abzufragen. Die habe ich gerade nicht im Kopf. Edit: Ignoriere das erste Zip File - das kann ich nicht mehr löschen.
:
Bearbeitet durch User
Udo K. schrieb: > Ich habe dir ein Windows Beispiel mit dem C Quelltext angehängt Das ist kein C-Quelltext, sondern eine hochspezifische Sonderlocke für Windows mit vermutlich dem MS-Compiler. Also mit Sicherheit nichts, was man allgemein verwenden kann. Vor allem nicht hier im Thread, wo es um ARM + GCC geht.
MaWin O. schrieb: > Also mit Sicherheit nichts, was man allgemein verwenden kann. Vor allem > nicht hier im Thread, wo es um ARM + GCC geht. Das habe ich auch nie behauptet. Wenn es dich nicht interessiert, dann halt doch einfach die Klappe.
Mal ne Frage an den TO: Warum muß da überhaupt ein Array angelegt und das übergebene Array in das neue kopiert werden? Man kann doch auch gleich mit den Membern des data-Array's rechnen. Mit anderen Worten, das was Du vor hast ist doch eigentlich nicht nötig.
Udo K. schrieb: > Wenn es dich nicht interessiert, dann > halt doch einfach die Klappe. Wieder einmal sehr freundliche Menschen unterwegs hier...
Udo K. schrieb: > malloc hat in der Praxis auch Probleme, da kannst du testen was du > willst, es kommt IMMER ein gültiger Pointer zurück, und dann crash es > doch - Linux ist dafür berüchtigt. Naja, laut POSIX nicht: im Fehlerfall kommt nullptr / NULL zurück. Was anderes ist mir in der Praxis noch nicht begegnet. Hast Du Beispiele für solche Bugs? Und ich meine jetzt nicht das unspezifizierte Verhalten von new in <=c++98.
Wilhelm M. schrieb: > Naja, laut POSIX nicht: im Fehlerfall kommt nullptr / NULL zurück. Was > anderes ist mir in der Praxis noch nicht begegnet. Mit Memory Overcommit, das normalerweise bei Linux aktiviert ist, kann man mehr Speicher alloziieren, als physikalisch vorhanden ist. Die Idee dahinter ist, dass ein Großteil des alloziierten Speichers nie verwendet wird. Das hört sich komisch an, ist aber tatsächlich so. Die eigentliche physikalische Allokation findet dann erst beim Zugriff statt. Und eben auch die Fehlerbehandlung. Diese löst dann ein Signal (SIGSEGV) aus. Wenn dies unbehandelt ist, crasht es das Programm. Deshalb gibt malloc praktisch immer einen gültigen Pointer zurück. > Hast Du Beispiele für solche Bugs? Es ist kein Bug, sondern gewolltes Verhalten.
Ach ja und der Out-Of-Memory-Killer (OOM-Killer) spielt natürlich bei Memory-Overcommit noch eine zentrale Rolle. Der kann auch beliebige Prozesse abschießen in Reaktion auf Speicherzugriffe, die keinen physikalischen Speicher haben.
MaWin O. schrieb: > Wilhelm M. schrieb: >> Naja, laut POSIX nicht: im Fehlerfall kommt nullptr / NULL zurück. Was >> anderes ist mir in der Praxis noch nicht begegnet. > > Mit Memory Overcommit, das normalerweise bei Linux aktiviert ist, kann > man mehr Speicher alloziieren, als physikalisch vorhanden ist. Das kann man bei Systemen mit dynamic paging immer. > Die Idee dahinter ist, dass ein Großteil des alloziierten Speichers nie > verwendet wird. Das hört sich komisch an, ist aber tatsächlich so. s.o.: dynamic paging > Die eigentliche physikalische Allokation findet dann erst beim Zugriff > statt. Heisst: demand paging > Und eben auch die Fehlerbehandlung. Diese löst dann ein Signal > (SIGSEGV) aus. Das macht der oom, und damit ist das System nicht mehr Posix-konform.
Wilhelm M. schrieb: > Das macht der oom, und damit ist das System nicht mehr Posix-konform. Niemand hat was von Posix-Konformität gesagt, außer du. > dynamic paging > demand paging Bei Linux heißt es Memory Overcommit.
MaWin O. schrieb: >> dynamic paging >> demand paging > > Bei Linux heißt es Memory Overcommit. Das sind zwei getrennte Dinge. Das eine ist, dass die physische Speicher-Page nicht sofort bei Allokation bereitgestellt wird, sondern erst beim ersten Zugriff. Das kenne ich unter dem Namen "lazy allocation". Das zweite ist, dass insgesamt mehr Speicher allokiert werden kann, als überhaupt (im RAM plus der Auslagerung) verfügbar ist. Das ist "memory overcommit". Dafür ist die lazy allocation natürlich eine Voraussetzung. Wilhelm M. schrieb: >> Mit Memory Overcommit, das normalerweise bei Linux aktiviert ist, kann >> man mehr Speicher alloziieren, als physikalisch vorhanden ist. > > Das kann man bei Systemen mit dynamic paging immer. Nein.
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.