Forum: Compiler & IDEs c (compiler) Frage


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Mat. K. (matthias_kornfield)


Lesenswert?

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;
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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

von DerEinzigeBernd (Gast)


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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
von foobar (Gast)


Lesenswert?

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 ...

von rbx (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
}

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> uint8_t tmp[256];

255 würde reichen.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

> 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.

von foobar (Gast)


Lesenswert?

s/int size/unsigned size/

von Rolf M. (rmagnus)


Lesenswert?

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
von Oliver S. (oliverso)


Lesenswert?

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

von foobar (Gast)


Lesenswert?

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?

von Udo K. (udok)


Lesenswert?

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
von MaWin O. (mawin_original)


Lesenswert?

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.

von Udo K. (udok)


Angehängte Dateien:

Lesenswert?

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
von MaWin O. (mawin_original)


Lesenswert?

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.

von Udo K. (udok)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von MaWin O. (mawin_original)


Lesenswert?

Udo K. schrieb:
> Wenn es dich nicht interessiert, dann
> halt doch einfach die Klappe.

Wieder einmal sehr freundliche Menschen unterwegs hier...

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von MaWin O. (mawin_original)


Lesenswert?

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.

von MaWin O. (mawin_original)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von MaWin O. (mawin_original)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.