Forum: Compiler & IDEs c++ new und delete


von chris (Gast)


Lesenswert?

Hallo Zusammen,

um auf einem AVR C++ zu verwenden gibt es für AVR-Studio eine schöne 
Anleitung, wie man es einstellen muss, damit man c++ Programme 
compilieren kann:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=59453

Da der AVR-GCC von sich aus keinen 'new' und 'delete' Operator hat, wird 
in dem Beitrag auch aufgeführt, wie diese künstlich herzustellen sind:
1
void * operator new(size_t size)
2
{
3
  return malloc(size);
4
}
5
6
void operator delete(void * ptr)
7
{
8
  free(ptr);
9
}

Jetzt meine Frage: kann ich damit wirklich neue Objekte erzeugen?

( Bitte die Diskussion ob C++ auf einem AVR Sinn macht, nicht beginnen )

Gruß,
chris

von Ollz (Gast)


Lesenswert?

> Jetzt meine Frage: kann ich damit wirklich neue Objekte erzeugen?

Ja, bis das RAM alle ist. Und das ist schnell alle.

> ( Bitte die Diskussion ob C++ auf einem AVR Sinn macht, nicht beginnen )

Ja, ja, in einem Diskussionsforum eine Diskussion vermeiden wollen. Wir 
sind nicht deine Angestellten, also viel Glück mit dem Versuch.

von chris (Gast)


Lesenswert?

Hier habe ich mal ein kleines Beispielprogramm erstellt. Es ergibt sich 
dabei aber folgendes Problem:

Dieser Code funktioniert
1
  CRectangle rect(1,1);
2
  int x=rect.get_x();

eine neues Rechteck Objekt lässt sich mit 'new' auch erzeugen ( 
zumindest meckert der Compiler nicht )
1
  CRectangle *r=new CRectangle(11,12);

Wenn ich allerdings auf die Funktion get_x() zugreifen will ..
1
x=*r.get_x();

meckert der Compiler:
../chCppTest.cpp:45: error: request for member 'get_x' in 'r', which is 
of non-class type 'CRectangle*'

Was mache ich falsch?

Hier ist mein kleines Beispielprogramm:
1
#include <stdlib.h> 
2
3
__extension__ typedef int __guard __attribute__((mode (__DI__)));
4
5
extern "C" int __cxa_guard_acquire(__guard *);
6
extern "C" void __cxa_guard_release (__guard *);
7
extern "C" void __cxa_guard_abort (__guard *); 
8
9
int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};
10
void __cxa_guard_release (__guard *g) {*(char *)g = 1;};
11
void __cxa_guard_abort (__guard *) {}; 
12
13
void * operator new(size_t size)
14
{
15
  return malloc(size);
16
}
17
18
void operator delete(void * ptr)
19
{
20
  free(ptr);
21
} 
22
23
class CRectangle 
24
{
25
    int x, y;
26
  public:
27
    CRectangle (int,int); // Constructor
28
    int get_x (void) { return (x) ;}
29
};
30
31
CRectangle::CRectangle(int a,int b)
32
{
33
  x=a;
34
  y=b;
35
}
36
37
int main ()
38
{
39
    CRectangle rect(1,1);
40
    int x=rect.get_x();
41
    //int *x=new int;
42
  
43
  CRectangle *r=new CRectangle(11,12);
44
45
  x=*r.get_x();
46
  
47
  return 0;
48
}

von P. S. (Gast)


Lesenswert?

chris schrieb:

> Was mache ich falsch?

Ich halte es schon fuer falsch, C auf einem Mikrokontroller zu lernen, 
aber C++?

Du musst erst den Pointer dereferenzieren (*r), dann darauf zugreifen: 
(*r).get_x(). Alternativ nimmst du r->get_x().

von Lars R. (larsr)


Lesenswert?

Hallo,

Ich vermute einfach, dass die Zeile falsch ist:

x=*r.get_x();

Besser wäre vermutlich "x=(*r).get_x();" oder "x=r->get_x();".

Viele Grüße,

Lars

von chris (Gast)


Lesenswert?

Juhu, vielen Dank an euch zwei, das war der Fehler.

Jetzt hätte ich noch eine Frage zum Programm. Die ersten Zeilen verstehe 
ich nicht:
1
__extension__ typedef int __guard __attribute__((mode (__DI__)));
2
3
extern "C" int __cxa_guard_acquire(__guard *);
4
extern "C" void __cxa_guard_release (__guard *);
5
extern "C" void __cxa_guard_abort (__guard *); 
6
7
int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};
8
void __cxa_guard_release (__guard *g) {*(char *)g = 1;};
9
void __cxa_guard_abort (__guard *) {};

>> Was mache ich falsch?
>Ich halte es schon fuer falsch, C auf einem Mikrokontroller zu lernen,
>aber C++?
Peter, Du kennst Dich bestimmt besonders gut aus, kannst Du mir die 
Zeilen erkären?

von Lars R. (larsr)


Lesenswert?

Diese Zeilen musst du als C++-Lernender auch nicht verstehen.

Der GCC benötigt diese Methoden scheinbar zur Laufzeit innerhalb der 
C++-Umgebung. Genau kann dir dies wohl nur jemand erklären, der die 
Quellen des GCC kennt.

Wichtig ist es zu wissen, dass solche Geschichten stets 
Compiler-spezifisch sind. So wie GCC "Nested Functions" unterstützt, ist 
eben hier dieser zusätzliche Aufwand erforderlich.

von Karl H. (kbuchegg)


Lesenswert?

Lars R. schrieb:
> Diese Zeilen musst du als C++-Lernender auch nicht verstehen.

Richtig.

Am besten nimmst du den Teil
1
#include <stdlib.h> 
2
3
__extension__ typedef int __guard __attribute__((mode (__DI__)));
4
5
extern "C" int __cxa_guard_acquire(__guard *);
6
extern "C" void __cxa_guard_release (__guard *);
7
extern "C" void __cxa_guard_abort (__guard *); 
8
9
int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};
10
void __cxa_guard_release (__guard *g) {*(char *)g = 1;};
11
void __cxa_guard_abort (__guard *) {}; 
12
13
void * operator new(size_t size)
14
{
15
  return malloc(size);
16
}
17
18
void operator delete(void * ptr)
19
{
20
  free(ptr);
21
}

in eine eigene Datei mem.cpp und gibst die einfach zu jedem Projekt mit 
dazu. Dann bist du den Teil los. An dieser Stelle ist tatsächlich "Aus 
den Augen, aus dem Sinn" die beste Lösung.

> Wichtig ist es zu wissen, dass solche Geschichten stets
> Compiler-spezifisch sind. So wie GCC "Nested Functions" unterstützt, ist
> eben hier dieser zusätzliche Aufwand erforderlich.

Wenn Namen nicht nur Schall und Rauch sind, würde ich sagen, dass sich 
die Runtime Guard-Bytes vor/hinter den Objekten einrichtet, um zb 
Arrayoverflows möglicherweise erkennen zu können. Ist aber nur anhand 
der Funktions-Namen geraten.

von P. S. (Gast)


Lesenswert?

chris schrieb:

>>Ich halte es schon fuer falsch, C auf einem Mikrokontroller zu lernen,
>>aber C++?
> Peter, Du kennst Dich bestimmt besonders gut aus, kannst Du mir die
> Zeilen erkären?

Nein, leider nicht. Ich habe mir schon vor vielen Jahren angewoehnt, 
meinen Code einfach zu halten. Und das klappt auch in 99% der Faelle. 
Der Nachteil ist leider, dass man dann nur selten ueber solche 
Spezialitaeten stolpert und in Runden, der mehr ueber die Sprache 
diskutieren, als sie zu benutzen, schnell mal inkompetent wirkt ;-)

von chris (Gast)


Angehängte Dateien:

Lesenswert?

Vielen Dank für eure Antworten.

Vielleicht sollten wir doch einmal eine kleine Diskussion zum Thema C++ 
auf dem AVR beginnen.

Auf die Idee c++ auf dem AVR zu verwenden bin ich bei der Betrachtung 
der Arduion TWI Biblithek gekommen ( siehe Anhang ). Meiner Meinung nach 
ist es nur durch die Verwendung des C++ Compilers möglich, einen so 
einfach zu verstehenden Code für Leute die nur den Treiber benutzen 
wollen zu generieren.
Mein Ziel ist es jetzt, diese Treiberbibliothek nicht mit der Arduino 
Entwicklungsoberfläche zu kompilieren, sondern die Klassen in eine 
Programm im AVR-Studio einzubinden.

Hier der Beispielcode des Arduino für den TWI-Receiver:
1
// Wire Slave Receiver
2
// by Nicholas Zambetti <http://www.zambetti.com>
3
4
// Demonstrates use of the Wire library
5
// Receives data as an I2C/TWI slave device
6
// Refer to the "Wire Master Writer" example for use with this
7
8
// Created 29 March 2006
9
10
#include <Wire.h>
11
12
void setup()
13
{
14
  Wire.begin(4);                // join i2c bus with address #4
15
  Wire.onReceive(receiveEvent); // register event
16
  Serial.begin(9600);           // start serial for output
17
}
18
19
void loop()
20
{
21
  delay(100);
22
}
23
24
// function that executes whenever data is received from master
25
// this function is registered as an event, see setup()
26
void receiveEvent(int howMany)
27
{
28
  while(1 < Wire.available()) // loop through all but the last
29
  {
30
    char c = Wire.receive(); // receive byte as a character
31
    Serial.print(c);         // print the character
32
  }
33
  int x = Wire.receive();    // receive byte as an integer
34
  Serial.println(x);         // print the integer
35
}

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:

> ist es nur durch die Verwendung des C++ Compilers möglich, einen so
> einfach zu verstehenden Code für Leute die nur den Treiber benutzen
> wollen zu generieren.

Na ja.
Einfach ist relativ. Wenn man C++ kann, ist das leicht zu verstehen. Man 
kann das aber auch alles in C ausdrücken und es bleibt genauso einfach.
Letztenendes geht es nur darum eine Callback-Funktion an einen 
Mechanismus zu übergeben und das ist in C und in C++ (leider) völlig 
identisch. Wenn man das komplett OOP machen würde (wozu keine 
Veranlassung besteht), würde das ein klein wenig anders aussehen :-) Da 
würde es dann keinen Receive-Event in Form eines Callbacks geben, 
sondern ein Receiver Objekt, welches ein Interface implementieren muss 
und dieser Receiver Objekt registriert sich beim Wire Objekt. Aber das 
ist eine andere Geschichte.

Tatsache ist aber: So wie der Mechanismus jetzt läuft, gibt es nicht 
wirklich einen Grund das alles in C++ zu lassen. Aber wahrscheinlich 
gibt es auch keinen wirklichen Grund das alles von C++ auf C 
umzuschreiben :-)

von chris (Gast)


Lesenswert?

Naja, die Call-Back Funktion fällt in diesem Beispiel besonders ins 
Auge.
Allerdings finde ich diese Methode auch nicht unbedingt als den gro0en 
Vorteil dieser C++ Implementierung.

Der große Vorteil liegt meiner Meinung nach in der "Polymorphie", d.h. 
dass ich die Methoden mit gleichem Namen aber unterschiedlichen 
Argumenttypen aufrufen kann.

wie z.B.

  int x=1
  Serial.println(x);

oder

  Serial.println("hallo");

Im Anhang habe ich meine Beispielprogramm als AVR-Studio Template 
erstellt, d.h. die C++ spezifischen Setups ausgelagert. Das sollte die 
Benutzung von C++ im AVR-Studio ziemlich vereinfachen.

Vielleicht könnt Ihr mal draufschauen, ob es so passt.

von chris (Gast)


Angehängte Dateien:

Lesenswert?

Anhang vergessen ... hier ist er.

von P. S. (Gast)


Lesenswert?

Genau die ueberladenen Methoden fuehren im Zusammenhang mit 
automatischer Typkonvertierung (und am besten noch Defaultparametern) zu 
den herrlichsten Fehlern.

von chris (Gast)


Lesenswert?

Hmm, rein optisch finde ich es aber sehr ansprechend. Vielleicht muss 
man die Methoden einfach gut kennen, um diese Fehler zu vermeiden.

So, jetzt bin ich schon wieder auf das Problem gestoßen, mit dem ganz zu 
Beginn meiner Annäherung an C++ auf dem AVR gekämpft habe.

Ich nehme die TWI-Klasse des Arduino ( "Wire_I2C.zip"  weiter oben 
gepostete ) und nehme diese Klasse in mein simples AVR-Studio Projekt 
mit auf ( benutze die Klasse aber noch nicht im Programm )

Dann ergibt sich folgende Fehlermeldung:

../Wire.cpp:147: undefined reference to `twi_transmit'

Wire.cpp sollte normalerweise die Funktionen aus twi.c benutzen. Ganz am 
Anfang von Wire.cpp steht:

extern "C" {
  #include <stdlib.h>
  #include <string.h>
  #include <inttypes.h>
  #include "twi.h"
}

Damit sollte doch eigentlich die Funktionen aus twi.h bekannt sein. 
Wieso findet der Kompiler die Funktion nicht? Das ist mir ein fölliges 
Rätsel.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ollz schrieb:
>> Jetzt meine Frage: kann ich damit wirklich neue Objekte erzeugen?
>
> Ja, bis das RAM alle ist. Und das ist schnell alle.

Woher weißt du denn, wie viel RAM bei mir (oder beim OP) frei ist?

von chris (Gast)


Lesenswert?

>Woher weißt du denn, wie viel RAM bei mir (oder beim OP) frei ist?

Hallo Jörg,
[bösartige ironie]
manche Menschen wissen eben, wieviel Ram bei den anderen frei ist.
Sie wissen auch, was für die anderen gut ist. Ihr Weltbild ist schon 
abgeschlossen und deshalb gehen sie davon aus, dass alles was sie für 
richtig halten automatisch auch für die anderen gilt. Ich nehme an, bei 
Ollz ist schon lange kein Ram mehr frei ;-)
[/bösartige ironie]

Ich habe mal getestet, wieviel Ram beim generieren eins neuen 
"CRectangle" Objekts reserviert wird. So wie es mir scheint, sind es nur 
die paar Bytes für die lokalen Variablen. Also bei Objekten mit wenig 
lokalen Variablen eine recht überschaubare Zahl.

Aber lassen wir das. Vielleicht hast Du als einer der Compilerentwickler 
eine Idee:
1
//Wire.cpp sollte normalerweise die Funktionen aus twi.c benutzen. Ganz am
2
//Anfang von Wire.cpp steht:
3
4
extern "C" {
5
  #include <stdlib.h>
6
  #include <string.h>
7
  #include <inttypes.h>
8
  #include "twi.h"
9
}

Wenn über das extern "C" ... zusätzlich #include "twi.h" schreibe, 
compiliert das Programm fehlerlos und ich kann es im Simulator testen. 
Komisch, oder?

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:

> Komisch, oder?

Nein, nicht wirklich.

Dieser extern "C" Rundumschlag ist wirkungslos, solange twi.c nicht auch 
wirklich im C-Modus compiliert wurde. Mach das nicht!

Mach jedes einzelne Header File auf, welches zu einem reinen C-Modul 
gehört und mache dort die extern "C" Deklaration. Und dann sorge auch 
dafür, dass twi.c auf jeden Fall neu compiliert wird.

Oder aber. Mach nirgends eine extern "C" definition und gib allen Source 
Code Files dieselbe Endung (.cpp oder .C, jenachdem was dein Compiler 
haben will um es als C++ Quellcode zu erkennen) oder stell alles so ein, 
dass es immer mit dem C++ Compiler übersetzt wird.

Dein Problem: Name mangling
Anscheinend ist twi.c mit Name-Mangling übersetzt worden (also im C++ 
Mode) und in der Verwendung schaltest du mit dem extern "C" das 
Name-mangling beim Verwender aus. Effekt: der Linker sucht nach 
tw_transmit, die in Wirklichkeit als QWHJ@tw_transmit@1AGDAJ übersetzt 
wurde.


Das alles ist: Pain in the ass
Daher: Alles so einfach wie möglich halten. Wenn du sowieso C++ arbeiten 
willst, dann nenn all deine Dateien *.cpp und du sparst dir eine Menge 
Probleme.

von chris (Gast)


Lesenswert?

Hallo Karl heinz,

danke für die Antwort. Die bringt mich jetzt etwas zum grübeln:

Eigentlich wollte ich die Arduino Treiber so verwenden wie sie sind, 
d.h. also das twi.c als twi.c lassen. Dann treten aber die von Dir 
beschriebenen Probleme auf. Wenn ich die twi.c in twi.cpp umbennene, 
muss ich in allen files wie z.B. Wire.cpp die twi.h einbinden wollen den 
Code von extern C ... rauswerfen und die H-Files direkt einbinden. Habe 
ich das richtig verstanden?
Hmm... das macht die ganze Sache natürlich wieder etwas unangenehmer, 
weil ich ja nachschauen muss, wo diese zeilen überall geändert werden 
müssen.

Das fühlt sich irgendwie "inhomogen" an.

Gruß,
chris

von P. S. (Gast)


Lesenswert?

chris schrieb:

> Sie wissen auch, was für die anderen gut ist. Ihr Weltbild ist schon
> abgeschlossen und deshalb gehen sie davon aus, dass alles was sie für
> richtig halten automatisch auch für die anderen gilt.

Andersrum gibt es auch Menschen, die sich nichts sagen lassen und jeden 
guten Rat in den Wind schieben.

Nochmal im Klartext: Du hast eine Menge Probleme, weil du dich an ein 
Projekt wagst, fuer das du zuwenig Erfahrung hast. Fuer 
Frameworkprojekte (und das hier sieht wie eins aus) braucht man viel 
Erfahrung. Und wenn du anfaengst, Sourcen unterschiedlicher Herkunft zu 
mischen, allemal.

Zu deinem aktuellen Problem: Stelle sicher, dass die *.c-Files auch vom 
C-Compiler uebersetzt werden, nicht vom C++-Compiler. Dann passt auch 
die extern "C"-Deklaration.

von Karl H. (kbuchegg)


Lesenswert?

Hier
http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html
findest du auch ein paar Tipps, wie man sich das Leben beim C-C++ Mix 
leichter machen kann.
Insbesonder das Makro __cplusplus ist oft ein Rettungsanker. Und wenn 
das bedeutet, dass du die original Arduino Treiber etwas pimpen musst, 
dann sei das so.

Aber das wichtigste ist: C Files mit dem C-Compiler. C++ Files mit dem 
C++ Compiler. Das hört sich trivial an, kann aber in ein Nightmare 
ausarten. Wenn das aus irgendeinem Grunde nicht trivial geht: Alles 
durch den C++ Compiler jagen und hoffen, dass das C sauber genug ist, 
dass es in C++ keine Probleme aufwirft.

von Karl H. (kbuchegg)


Lesenswert?

Ha, gerade auf der FAQ Seite ganz unten gefunden

[quote]
C++ doesn't try to make it impossible for bad programmers to write bad 
programs; it enables reasonable developers to create superior software.
[/quote]

Wie wahr, wie wahr.

von chris (Gast)


Lesenswert?

>Stelle sicher, dass die *.c-Files auch vom
>C-Compiler uebersetzt werden, nicht vom C++-Compiler. Dann passt auch
>die extern "C"-Deklaration.

Hallo Peter,

vielen Dank für Deine Hilfe. Du hast sicher schon einige Jahre Erfahrung 
mit AVR-Studio und C++ und kannst mir sagen, sie ich das mit AVR-Studio 
bewerkstellige.

Gruß,
chris

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn du C++ machen willst, kannst du meines Wissens AVR Studio
komplett vergessen.  Kann sein, dass du eine entsprechende C++-
Quelle noch editieren kannst, aber schon beim Generieren des
Makefiles dürfte er aufgeschmissen sein, und C++ Debuggen geht
rein gar nicht (d. h. es geht nur insoweit, wie du C++-Features
benutzt, die es auch in C gibt).

von P. S. (Gast)


Lesenswert?

chris schrieb:

> vielen Dank für Deine Hilfe. Du hast sicher schon einige Jahre Erfahrung
> mit AVR-Studio und C++ und kannst mir sagen, sie ich das mit AVR-Studio
> bewerkstellige.

Ich benutze kein AVR-Studio. Ich entwickle meine Sachen auf MacOS X und 
benutze das "uebliche" Makefile mit ein paar kleinen Aenderungen. Aber 
frag mich nicht, wo ich letzteres her hatte.

von chris (Gast)


Lesenswert?

Hallo Jörg,

>Wenn du C++ machen willst, kannst du meines Wissens AVR Studio
>komplett vergessen.

Der ganz oben gepostete Link

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=59453

beschreibt ja gerade, wie es mit AVR-Studio geht.

>Kann sein, dass du eine entsprechende C++-
>Quelle noch editieren kannst, aber schon beim Generieren des
>Makefiles dürfte er aufgeschmissen sein, und C++ Debuggen geht
>rein gar nicht

Hmm, über das Make-File von AVR-Studio weiss ich nichts. Ich konnte mein 
oben gepostetes Beispiel übersetzten und mit kleinen Abweichern auch 
debuggen.
Ich liebe die Debug-Funktion von AVR-Studio. Gerade bei Mikrocontrollern 
mit ihrer kompliziert zu programmierenden Peripherie ist es wichtig, 
dass man deren Registerinhalte und Funktion überprüfen kann. Mir hat das 
jedenfalls schon einige Stunden Fehlersuche erspaart.

Zum Problem vom Mischen von C++ und C in AVR-Studio findet sich im Link 
folgendes:

"First thing setup. I use AVR Studio, which is almost working with c++. 
Almost, because you must manually in project->Configuration 
options->Custom options set instead of avr-gcc avr-c++.exe to compile 
any of your c++ files."

d.h. der Compiler avr-gcc wird durch avr-c++ ersetzt. Ich nehme mal an, 
dass das der Grund ist, warum sich C und C++ im AVR-Studio nicht 
vernünftig mischen lässt.

Deshalb gleich meine Frage: Kann der avr-c++ nicht auch standard C 
übersetzten? Warum sollte man überhaupt ein reines C-File verwenden?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

chris schrieb:

> beschreibt ja gerade, wie es mit AVR-Studio geht.

OK, mir ist nur in Erinnerung, dass zumindest der Debugger mit
den obskureren C++-Features nicht klar kommt.

> d.h. der Compiler avr-gcc wird durch avr-c++ ersetzt. Ich nehme mal an,
> dass das der Grund ist, warum sich C und C++ im AVR-Studio nicht
> vernünftig mischen lässt.

Ja.  Normalerweise macht man die Unterscheidung über die
Dateiendung.  Suffix .c heißt, dass der C-Compiler zu benutzen
ist, Suffix .C oder .c++ oder .cpp (und noch ein paar andere)
wählen den C++-Compiler aus.  Das Kommando muss dabei aber in
jedem Falle avr-gcc heißen; wenn man explizit mit avr-c++ compiliert,
werden alle Quellen als C++ bewertet.

> Deshalb gleich meine Frage: Kann der avr-c++ nicht auch standard C
> übersetzten?

Ein ordentlich geschriebenes C-Programm sollte in der Regel auch durch
einen C++-Compiler laufen können.

Allerdings gibt es subtile Unterschiede.  Der offensichtlichste ist,
wenn in C++ reservierte Wörter als Bezeichner verwendet werden:
1
char new;
2
3
void delete(const char *filename);

Es gibt aber vieles mehr, bspw. ist die Typprüfung in C++ sehr viel
strenger als in C.  In C lassen sich void* implizit in andere
Objektzeiger überführen (und das gilt auch nicht als schlechter
Programmierstil), in C++ nicht.

Ganz haarig wird's, wenn das C-Programm benannte Elemente von Arrays
oder Strukturen initialisiert.  Dieses Feature ist in C99 in den
Standard aufgenommen worden, stammt aber im Gegensatz zu vielen
anderen C99-Erweiterungen nicht von C++ ab und existiert dort nicht.

Zu guter Letzt gibt's sicher auch genügend hinreichen schlampig
geschriebene C-Programme, die kein C++-Compiler je fressen würde.

> Warum sollte man überhaupt ein reines C-File verwenden?

Zum Beispiel, weil man es irgendwo extern her bekommen hat und
keine Lust hat, es C++-fähig zu machen.

von chris (Gast)


Lesenswert?

Witzig, nachdem ich mich wieder mit dem Thema befasse, bin ich über 
diesen Thread gestoßen, den ich schon vergessen habe.

Hier gibt es ein Beispiel für die Verwendung von "new" auf einem 
Arduino:
http://stackoverflow.com/questions/16274451/operator-new-for-arduino

von chris (Gast)


Lesenswert?

Gibt es eigentlich keine Funktion in der libc, um die verfügbare 
Speichergröße für "malloc" zu bestimmen?

Das Roboternetz schlägt folgendes vor:
http://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc

von Mark B. (markbrandis)


Lesenswert?

chris schrieb:
> Gibt es eigentlich keine Funktion in der libc, um die verfügbare
> Speichergröße für "malloc" zu bestimmen?

Sagen wir mal so:
Wenn Du sehr wenig RAM hast, wirst Du kein malloc() benutzen wollen.
Wenn Du ordentlich RAM hast, kannst Du Dir auch ein Betriebssystem 
leisten ;-)

von chris (Gast)


Lesenswert?

>Sagen wir mal so:

[ ] Du hast die Frage beantwortet.
[x] Du wolltest zeigen, dass Du auch da bist.

von Interessant (Gast)


Lesenswert?

chris schrieb:
>>Sagen wir mal so:
>
> [ ] Du hast die Frage beantwortet.
> [x] Du wolltest zeigen, dass Du auch da bist.

[ ] Du willst Hilfe
[X] Du willst keine Hilfe

von Mark B. (markbrandis)


Lesenswert?

chris schrieb:
>>Sagen wir mal so:
>
> [ ] Du hast die Frage beantwortet.
> [x] Du wolltest zeigen, dass Du auch da bist.

Schön, dann hier die Antwort auf:

> Gibt es eigentlich keine Funktion in der libc, um die verfügbare
> Speichergröße für "malloc" zu bestimmen?

Nein, in der libc gibt es so eine Funktion meines Wissens nicht. Das 
Ganze ist in der Regel betriebssystemabhängig, da nun mal das 
Betriebssystem für die Speicherverwaltung zuständig ist, wie Du sicher 
weißt. Da, wo es kein Betriebssystem gibt, ist es eben 
Hardware/Toolchain abhängig.

Siehe z.B.:

http://stackoverflow.com/questions/14386856/c-check-available-ram
http://stackoverflow.com/questions/2513505/how-to-get-available-memory-c-g
http://stackoverflow.com/questions/13480235/libc-memory-management

: Bearbeitet durch User
von chris (Gast)


Lesenswert?

>Schön, dann hier die Antwort auf:

Danke für die Links.

Hier mein Testprogramm für einen Arduino-Uno. Ich habe mal ohne in die 
Tiefe zu bohren das Verfahren aus dem RN-Netz angewendet
( 
http://rn-wissen.de/wiki/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc 
)
1
/*
2
3
    Testprogramm für den new-Operator auf einem AVR
4
    
5
    Es werden Rechtecke und Quadrate mit new erzeugt und in eine List eingetragen.
6
    Danach wird die Liste ausgegeben.
7
    Damit dies funktioniert, gibt es eine Basisklasse mit der virtuellen Funktion "flaeche".
8
    Da immer mehr Objekte erzeugt werden, soll der aktuell noch verfügbare Speicher angezeigt werden.
9
    
10
    Hardware: Arduino Uno
11
    
12
    2.7.2014 ch
13
    
14
*/
15
#define ANZAHLRECHTECKE 4
16
#define LISTSIZE 100
17
18
// Kodierung der Elemente
19
#define RECHTECK 0
20
#define QUADRAT 1
21
22
// interface
23
class Element{
24
public:
25
  Element(){};
26
  int type;
27
  virtual int flaeche()=0;
28
};
29
30
class Rechteck:public Element{
31
private:
32
  int x;
33
  int y;
34
public:
35
  int flaeche(){return x*y;}
36
  Rechteck(int xx, int yy)
37
  {
38
    type=RECHTECK;
39
    x=xx;
40
    y=yy;
41
  }
42
};
43
44
class Quadrat:public Element{
45
private:
46
  int x;
47
48
public:
49
  int flaeche(){return x*x;}
50
  Quadrat(int xx)
51
  {
52
    type=QUADRAT;
53
    x=xx;
54
  }
55
};
56
57
58
Element *List[LISTSIZE];
59
60
void setup() {
61
  Serial.begin(9600);
62
}
63
64
extern unsigned char __heap_start;
65
66
void loop() {
67
  static int count=0;
68
  if(count<ANZAHLRECHTECKE)List[count]=new Rechteck(count,234);
69
  if(count>=ANZAHLRECHTECKE)List[count]=new Quadrat(count);
70
  count++;
71
  
72
  if(count>=LISTSIZE)
73
  {
74
      Serial.println("max list size reached. stopped.");
75
      while(1);
76
  }
77
  int n;
78
  for(n=0;n<count;n++)
79
  {
80
  if(List[n]->type==RECHTECK) Serial.println("Rechteck");
81
  if(List[n]->type==QUADRAT) Serial.println("Quadrat");
82
83
  }
84
  Serial.print("Ram momentan noch frei:");
85
  Serial.println((SP- (uint16_t) &__heap_start));
86
  delay(1000);
87
}
Die Speicherverbrauchsanzeige bleibt allerdings stecken. Sie scheint das 
dynamische Allokieren mit "new" nicht zu zählen.
Jetzt muss ich wohl doch die Links durcharbeiten ....
Es sei denn, jemand weis die Lösung auf die Schnelle ...

von Jürgen S. (jurs)


Lesenswert?

chris schrieb:
> Die Speicherverbrauchsanzeige bleibt allerdings stecken. Sie scheint das
> dynamische Allokieren mit "new" nicht zu zählen.
> Jetzt muss ich wohl doch die Links durcharbeiten ....
> Es sei denn, jemand weis die Lösung auf die Schnelle ...

Die simpelste Funktion zum Ermitteln des freien RAM-Speichers, die von 
Arduino-Programmierern gerne verwendet wird, ist diese:
1
int freeRam () {
2
  extern int __heap_start, *__brkval; 
3
  int v; 
4
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
5
}

Allerdings ermittelt diese Funktion bei einem fragmentierten 
RAM-Speicher nicht den gesamten freien Speicher, sondern nur den (im 
Regelfall) größten freien Speicherbereich auf dem Heap, den man mit 
malloc() reservieren könnte. Freie RAM-Fragmente, die nach der 
Reservierung wieder zwischen anderen reservierten Speicherbereichen 
freigegeben wurden, bleiben bei dieser Funktion außen vor.

von chris (Gast)


Lesenswert?

>Die simpelste Funktion zum Ermitteln des freien RAM-Speichers,

Wunderbar, danke.

Damit funktioniert die Verbrauchsanzeige:
1
Rechteck
2
Ram momentan noch frei:1530
3
Rechteck
4
Rechteck
5
Ram momentan noch frei:1520
6
Rechteck
7
Rechteck
8
Rechteck
9
Ram momentan noch frei:1510
10
Rechteck
11
Rechteck
12
Rechteck
13
Rechteck
14
Ram momentan noch frei:1500
15
Rechteck
16
Rechteck
17
Rechteck
18
Rechteck
19
Quadrat
20
Ram momentan noch frei:1492
Die Rechtecke brauche wohl 10 Byte und die Quadrate 8 Byte.

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:

> Die Rechtecke brauche wohl 10 Byte und die Quadrate 8 Byte.

Das hättest du mit einem sizeof auch billiger ermitteln können.

Die Angabe des freien Speichers ist eine zweischneidige Sache. Denn auch 
wenn du weißt, das noch 100 Bytes frei sind, kann es durchaus sein, dass 
eine Speicheranforderung über 40 Bytes nicht erfüllt werden kann. Denn 
100 Bytes frei bedeutet nicht notwendigerweise, dass die 100 Bytes in 
einem zusammenängenden Stück frei sind. 20 Speicherlöcher mit jeweils 5 
Bytes sind in Summe auch 100 Bytes. Aber keines der freien Löcher ist 
eben groß genug, um darin eine 40 Byte Anforderung unterzubringen.

: Bearbeitet durch User
von chris (Gast)


Lesenswert?

Jürgen S. schreibt zur obigen Funktion:
"Allerdings ermittelt diese Funktion bei einem fragmentierten
RAM-Speicher nicht den gesamten freien Speicher, sondern nur den (im
Regelfall) größten freien Speicherbereich auf dem Heap, den man mit
malloc() reservieren könnte."

Soweit ich es verstehe, liefert die Funktion garantiert freien Bereich.

von Jürgen S. (jurs)


Lesenswert?

Karl Heinz schrieb:
> Die Angabe des freien Speichers ist eine zweischneidige Sache. Denn auch
> wenn du weißt, das noch 100 Bytes frei sind, kann es durchaus sein, dass
> eine Speicheranforderung über 40 Bytes nicht erfüllt werden kann. Denn
> 100 Bytes frei bedeutet nicht notwendigerweise, dass die 100 Bytes in
> einem zusammenängenden Stück frei sind.

Die von mir vorhin gepostete simple Funktion zählt allerdings keine 
freien RAM-Fragmente zusammen, sondern liefert als einzige Angabe die 
Größe des freien Speicherblocks zwischen Ende des Heaps und Anfang des 
Stacks. Und dieser RAM-Block ist immer am Stück frei.

Hier im Bild zu sehen der kleinere der freien grauen Blöcke zwischen 
__brkval und SP:
http://www.nongnu.org/avr-libc/user-manual/malloc.html

Vollständig reservieren sollte man aber auch diesen Bereich natürlich 
trotzdem nicht, weil dann keine Funktionen mehr laufen, wenn der Stack 
mit seinen Adressen nicht mehr nach unten wachsen kann, sobald eine 
Funktion aufgerufen wird.

Je nach Schachtelungstiefe von Funktionen muß da immer etwas RAM frei 
bleiben, das man nicht reservieren darf.

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.