Hallo,
ich habe eine generelle Frage zum Programmieren in C.
Ich bin sonst nur verwöhntes OOP Kind. Hab bisher fast nie etwas mit
prozedualer Programmierung zu tun gehabt, ausser ein wenig PHP Gefrickel
in meinen Anfangszeiten.
Selbst in C++ hab ich bisher relativ wenig gemacht. Langsam komm ich
jedoch auf den Geschmack von Hardwarenäherem Zeug.
Mir gefällt der OOP-Ansatz auch wesentlich besser, aber auf
Mikrocontrollern steh ich dem Ganzen etwas skeptisch gegenüber.
Ich stehe jetzt vor dem Problem, dass ich ein Programm habe, dass
mehrere Queues benötigt. Eine Queue implementieren ist jetzt nicht das
Ding. Wie gesagt: EINE.
Was macht man nun, wenn ich mehrere Queues benötige? Ich brauche also
irgendwie Instanzen von dieser Queue.
Meine erste Idee war einfach einen Pointer auf den anfang eines
allokierten Speicherbereis bei jeder Funktion mitzuliefern, aber das
ganze ist auch blöd, weil ich das Ding 1. ständig überall mitführen muss
und 2. unter Umständen noch andere Daten wie die größe oder Schreib- und
Lesezeiger für einen Ringpuffer benötige. Irgendwie müsste ich die Daten
Kapseln.
Bei einem Pointer auf eine struct kann ich zwar die Metadaten wie die
Größe usw. komfortabel speichern, muss den Pointer aber immernoch von
einer Ecke in die nächste schieben.
Was macht man da nu?
Wahrscheinlich werde ich zumindest nichtt um den Pointer auf die struct
herumkommen.
Danke für die Hilfe,
Andreas
Ja, das ist das Standardverfahren in C. Viele (Programmierer, APIs)
verstecken diesen Pointer noch indem sie ihn evtl. als void* anlegen
(mit typedef) und nennen das dann "Handle".
Na, und wie machts den C++?
Im Prinzip genauso, die Erweiterung ist, daß jede Instanz einen "this"
pointer hat, der auf die eigenen Daten zeigt. In C++ wird im Prinzip
dieser Zeiger als erster Parameter jeder Methode übergeben, dies ist
aber ein versteckter Parameter, d.h. in C++ sieht man ihn nicht, aber
auf Assemblerebene ist er da....
Andreas Galauner wrote:
> Was macht man da nu?
Das, was du schon angedeutet hast. Alle Daten, die zu einer Queue
gehören (Puffer, Indizes, etc), in eine Struktur packen (das ist dann
quasi dein Objekt) und den Funktionen jeweils einen Pointer auf solch
eine Struktur mitgeben.
> Wahrscheinlich werde ich zumindest nichtt um den Pointer auf die struct> herumkommen.
Genauso wenig, wie du bei OOP um das konkrete Objekt herumkommst.
OOP:
1
ObjTyp Objekt;
2
3
Objekt.MethodeA(...);
4
Objekt.MethodeB(...);
C:
1
ObjTypObjekt;
2
3
MethodeA(&Objekt,...);
4
MethodeB(&Objekt,...);
Bei OOP hast du nur bei der Implementierung der Methoden eine etwas
kürzere Schreibweise beim Datenzugriff (jedenfalls meistens, je nach
Sprache).
OOP:
1
RetTyp ObjTyp:MethodeA (...) {
2
3
Member1 = 1;
4
Member2 = 2;
C:
1
RetTypMethodeA(ObjTyp*this,...){
2
3
this->Member1=1;
4
this->Member2=2;
Du siehst, die Unterschiede sind bei relativ simplen Objekten nicht sooo
groß.
Noch einfacher ist natuerlich, man nimmt einfach C++. Nur auf virtuals
musst du verzichten, die funktionieren nach meiner Erfahrung nicht. Wenn
du ohne nicht auskommst, musst du eben Funktionspointer nehmen.
Sorgen wuerde ich mir eher ueber Speicherfragmentierung bei Verwendung
von dynamischen Strukturen machen...
Hm, naja.
Ich hab damit größeres vor. Evtl. nehm ich doch C++. Eine
Speicherverwaltung will ich eh implementieren. Das ganze soll ein
Experiment werden, bei dem ich mal versuche nen kleineren
Betriebssystemkern zu implementieren.
Ja, ich weiß. Das gibt es schon. Das bringt mich aber insofern nicht
weiter, da ich das ganze für ein Fach im Studium machen möchte. Deswegen
die ganze Arbeit.
Da ich da jedem "Prozess", der vom Scheduler verwaltet wird, die
Möglichkeit geben möchte Speicher, auch auf dem Heap, nicht nur auf dem
Stack, zu allokieren, muss ich da irgendwie getrennte Adressräume
reinbekommen. Ist ja alles möglich. Zumindest in C++ kann ich das schön
mit dem new und delete Operator überwachen und verwalten. Jeder Prozess
bekommt eine bestimmte Menge an Arbeitsspeicher zugewiesen. Ist der
voll, ist das halt so. Da hab ich dann als Entwickler drauf zu achten.
Die Adressräume sind also in der Größe statisch, auch, wenn kein
Speicher benutzt wird. Das ganze ohne MMU dynamisch zu machen ist
einfach zu viel für nen kleinen AVR.
Hab nur gedacht, dass es in C vielleicht angebrachter wäre sowas zu
machen.
Ich will das ganze auf nem AVR machen, da ich gedacht habe, dass es
einfacher sei den Kram zu implemtieren, als auf der x86 Architektur. Nur
frag ich mich langsam, ob das so richtig ist :D
Um nen externen SRAM werd ich wahrscheinlich auch nicht rumkommen.
Wieder mehr Aufwand.
Verglichen mit dem ganzen x86 spezifischen Kram sollte sich das aber
wieder relativieren. Protected/Real mode, VESA implementierung für ne
vernünftige Grafikausgabe, GDT und IDT und der ganze Rattenschwanz dazu.
Ich glaub ich fang einfach mal in C++ an und schuster mir ne
Speicherverwaltung. Dann werd ich sehen, wie gut oder schlecht das geht
:D
Aber danke schonmal für die Tipps.
@ Andreas Galauner
da hast du dir etwas interessantes vorgenommen.
>Ich glaub ich fang einfach mal in C++ an und schuster mir ne>Speicherverwaltung. Dann werd ich sehen, wie gut oder schlecht das geht.
Würde ich vielleicht auch so machen. Viele Featers von C++
wirst eh nicht brauchen. Nachträglich in C zu übertragen
wäre auch keine grosse Sache.
Berichte mal ab und zu wie du vorankommst.
grüsse, daniel
Interessant auf jeden Fall. Ich werde mich, soweit zeitlich möglich (mir
sitzt da leider sone fiese Matheprüfung im Nacken), erstmal versuchen
mir über die Speicherverwaltung Gedanken zu machen. ich hab da in meiner
Studentenbude ein hübsches Buch über den Linux Kernel. Mal sehen
inwieweit mich das weiterbringt ohne MMU und meiner Vereinfachung.
Falls jemand interessante Verbesserungsvorschläge oder weitere Ideen
hat, kann er die gerne einbringen.
Ich will halt erstmal für mich lernen, daher kann ich nur daraus
profitieren ;)
So... jetzt widme ich mich wieder meiner Zigarre und meinem "Lecker
Bierchen" von Horst Lichter ;)
Zum wohlsein!
Hmm, das is ja mal voll gut!
Hab erstmal mit C++ angefangen. Um die Vererbung rumzukommen ist gar
nicht so einfach. Vielleicht steig ich dann doch um.
Bin grade dabei mal n grundlegendes Klassendesign zu implementieren.
Port- und Registerzugriff funktioniert schon. Darauf aufbauend
implementier ich ne Debugging Konsole übers USART, womit ich mir n paar
Statistiken und RAM Dumps usw. holen kann.
Mein selbstgebauter JTAG Adapter funktioniert nicht und ich hab jetzt
keinen Bock nach dem Fehler zu suchen ;)