Forum: Mikrocontroller und Digitale Elektronik C - Speicherplatzfrage


von Helmut A. (Gast)


Lesenswert?

Guten Morgen,

sehe Ich das richtig, dass Variante 1 mehr Speicherplatz braucht als 
Variante 2?
1
/*
2
*  Variante 1
3
*/
4
uint8_t test1( uint8_t byte )
5
{
6
  char buff[10] = "";
7
}
8
9
uint8_t test2( uint8_t byte )
10
{
11
  char buff[10] = "";
12
}
13
14
/*
15
*  Variante 2
16
*/
17
char buff[10] = "";
18
uint8_t test1( uint8_t byte )
19
{
20
  strcpy( buff , "123456789");
21
}
22
23
uint8_t test2( uint8_t byte )
24
{
25
  strcpy( buff , "123456789");
26
}

Wenn man die Variable einmal global deklariert, so das die anderen 
Funktion darauf zugreifen können.

von Walter (Gast)


Lesenswert?

Helmut A. schrieb:
> Ich das richtig, dass Variante 1 mehr Speicherplatz braucht als
> Variante 2?

nein, bei Variante 1 wird der Speicherplatz nur während des 
Funktionsaurufs gebraucht, bei 2 dauerhaft

von Harlekin (Gast)


Lesenswert?

Helmut A. schrieb:
> sehe Ich das richtig, dass Variante 1 mehr Speicherplatz braucht als
> Variante 2?
Der Speicherort und die Lebensdauer sind unterschiedlich. So leben die 
lokalen Variablen nur während dem Funktionsaufruf auf dem Stack. Die 
globalen Variablen hingegen, während der gesamten Programmdauer.

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

von A. S. (Gast)


Lesenswert?

Helmut A. schrieb:
> sehe Ich das richtig, dass Variante 1 mehr Speicherplatz braucht als
> Variante 2?

Nein.

Neben den Stichworten static und Stack musst Du auch noch für deinen 
Prozessor/Compiler herausfinden, wie gut er 2 identische Strings 
zusammenfasst, bzw ob er überhaupt aus Rom lesen kann, da Du hier ja 2 
völlig verschiedene Codestücke vergleichst.

von Helmut A. (Gast)


Lesenswert?

Heißt das, wenn Ich in zwei verschiedenen Funktionen innerhalb der 
Funktionen z.B "char buff[10]" anlege und in der anderen auch, werden 
mir dann 20 Bytes berechnet im Speicherverbrauch? Oder bekommt der 
Kompiler mit, dass er die auch zusammenlegen könnte?! So das Ich quasie 
nur 10 Bytes brauche insgesamt.

von Maxim B. (max182)


Lesenswert?

Nur wenn eine von dieser Funktionen aus der anderen gerufen wird.
Sonst wird erste Funktion gerufen, Speicherplatz belegt. Funktion zu 
Ende = Speicherplatz frei. Zweite Funktion nimmt wieder mal 10 bytes. Am 
Ende sind die wieder frei.

von Helmut A. (Gast)


Lesenswert?

Maxim B. schrieb:
> Nur wenn eine von dieser Funktionen aus der anderen gerufen wird.
> Sonst wird erste Funktion gerufen, Speicherplatz belegt. Funktion zu
> Ende = Speicherplatz frei. Zweite Funktion nimmt wieder mal 10 bytes. Am
> Ende sind die wieder frei.
Also spielt es wenn Ich sie nicht gerade wo anders brauche die Variable 
keine Rolle ob Ich sie in der Funktion deklariere oder extern. Solange 
Ich sie nicht zusammen aufrufe?!

von Maxim B. (max182)


Lesenswert?

Eine Rolle spielt hier, wie lange Array lebt. Global, static oder nicht.

Wenn nichts anderes steht, lebt eine Datei, die innerhalb von einer 
Funktion deklariert, nur so lange wie die Funktion selbst.

Willst du, dass die Datei auch danach ihre Wert für nächste Ausführung 
von dieser Funktion behält - so muss du sie als static deklarieren (dann 
bleibt die Datei in RAM, ist aber nicht aus anderen Funktionen 
sichtbar).
Willst du die Datei für alle zugänglich machen, so muss du die als 
global deklarieren.

Noch eine Variante: Datei als static, und Zugang für alle anderen nur 
über diese Funktion.

Also, kuck mal in Lehrbuch über Lebzeiten von Variablen.

: Bearbeitet durch User
von RP6conrad (Gast)


Lesenswert?

Wan eine Variable nur local in eine Function verwendet wird, soll die am 
besten auch local declariert werden. Globale Variablen nur wen absolut 
notwendig verwenden. Grund ist das bei grossere Programme die Structure 
uebersichtlich zu halten, und bugs zu vermeiden. Stellen sie sich for 
das sie eine Program hatte wo X-Tausend variablen von ueberall in 
Program zuganglich sein....
Speicherplatz ist mit die heutige µ-processoren meist kein Thema mehr.

von Axel S. (a-za-z0-9)


Lesenswert?

Helmut A. schrieb:
>
> sehe Ich das richtig, dass Variante 1 mehr Speicherplatz braucht

Die Frage ist schon schlecht gestellt. "Speicherplatz" ... meint was? Es 
gibt verschiedenen Speicher in einem µC.

> Wenn man die Variable einmal global deklariert, so das die anderen
> Funktion darauf zugreifen können.

Du vergleichst Äpfel mit Birnen. Nur wenn du das Array global 
definierst, kannst du überhaupt von verschiedenen Stellen darauf 
zugreifen. Die Arrays innerhalb der Funktionen sind auch nur innerhalb 
der jeweiligen Funktion überhaupt sichtbar. Mehr noch: sie werden erst 
dann angelegt, wenn die Funktion aufgerufen wird und beim Verlassen der 
Funktion wieder zerstört.

Du möchtest jetzt dein C-Lehrbuch aufschlagen und über die Lebensdauer 
und Sichtbarkeit von globalen und lokalen Variablen nachlesen.

von Helmut A. (Gast)


Lesenswert?

Axel S. schrieb:
> Du möchtest jetzt dein C-Lehrbuch aufschlagen und über die Lebensdauer
> und Sichtbarkeit von globalen und lokalen Variablen nachlesen.
Das habe Ich soweit verstanden.
Es geht mir mehr oder weniger darum, wie viel Speicherplatz letzendlich 
belegt wird.
Wenn ich zwei Funktionen habe (die eigentlich keine globale Variable 
brauchen) und die auf eine Variable drauf zugreifen die z.B 10 Bytes 
belegt. Brauche Ich nur "10" Bytes.
Wenn Ich jetzt innerhalb der beiden Funktionen jeweils ein Array mit 
"10" Bytes anlege, würde ich ja "20" Bytes benötigen in diesem moment 
oder?

von Irgendwer (Gast)


Lesenswert?

Helmut A. schrieb:
> ie viel Speicherplatz letzendlich
> belegt wird.

Was verstehst du unter Speicherplatz?
Flash, RAM usw.?

von Walter (Gast)


Lesenswert?

Helmut A. schrieb:
> Wenn Ich jetzt innerhalb der beiden Funktionen jeweils ein Array mit
> "10" Bytes anlege, würde ich ja "20" Bytes benötigen in diesem moment
> oder?

in "diesem Moment" bist du aber nur in einer Funktion (ich nehme mal an 
dass du mit "rekursiv" noch nichts anfangen kannst), also nur 10 Bytes

von Helmut A. (Gast)


Lesenswert?

Na die Variablen sind im RAM.

von Wolfgang (Gast)


Lesenswert?

Helmut A. schrieb:
> Wenn Ich jetzt innerhalb der beiden Funktionen jeweils ein Array mit
> "10" Bytes anlege, würde ich ja "20" Bytes benötigen in diesem moment
> oder?

Maxim B. schrieb:
> Nur wenn eine von dieser Funktionen aus der anderen gerufen wird.

von Helmut A. (Gast)


Lesenswert?

Walter schrieb:
> Helmut A. schrieb:
>> Wenn Ich jetzt innerhalb der beiden Funktionen jeweils ein Array mit
>> "10" Bytes anlege, würde ich ja "20" Bytes benötigen in diesem moment
>> oder?
>
> in "diesem Moment" bist du aber nur in einer Funktion (ich nehme mal an
> dass du mit "rekursiv" noch nichts anfangen kannst), also nur 10 Bytes
1
uint8_t test1( uint8_t byte )
2
{
3
  char buff[10] = "";
4
}
5
uint8_t test2( uint8_t byte )
6
{
7
  char buff[10] = "";
8
}
9
uint8_t test3( uint8_t byte )
10
{
11
  char buff[10] = "";
12
}
13
14
uint8_t test4( uint8_t byte )
15
{
16
  char buff[10] = "";
17
}

Genau das wollte Ich wissen!
Wenn ich jetzt diese 4 Funktionen habe, die sich aber selbst nicht 
untereinander aufrufen, benötige Ich dann nur 10 Bytes.
Habe vorher angenommen, dass mir 40 Bytes flöten gehen.

von Stefan F. (Gast)


Lesenswert?

> benötige Ich dann nur 10 Bytes.

Ja, weil das Array nur temporär auf dem Stack liegt.

von Helmut A. (Gast)


Lesenswert?

Stefan U. schrieb:
>> benötige Ich dann nur 10 Bytes.
>
> Ja, weil das Array nur temporär auf dem Stack liegt.
Das heißt wiederum Ich darf nur so viel Speicherplatz brauchen, wie groß 
der Stack ist?

von Stefan F. (Gast)


Lesenswert?

Jawoll

von Helmut A. (Gast)


Lesenswert?

Stefan U. schrieb:
> Jawoll
Gibt der Kompiler eine Warnung, wenn es zu viel wird?

von Stefan F. (Gast)


Lesenswert?

Nein, der Compiler hat keine Ahnung, wie viel Stack später zur Laufzeit 
verfügbar ist.

Wer ESP8266 unter Arduino programmiert, stößt bei String-Manipulationen 
sehr schnell an diese grenze.

von Mark B. (markbrandis)


Lesenswert?

Stefan U. schrieb:
> Nein, der Compiler hat keine Ahnung, wie viel Stack später zur Laufzeit
> verfügbar ist.

Der Compiler nicht, aber der Linker schon. (Ich nehme mal an dass es 
hier um "bare metal" Programmierung geht.)

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Helmut A. schrieb:
> Axel S. schrieb:
>> Du möchtest jetzt dein C-Lehrbuch aufschlagen und über die Lebensdauer
>> und Sichtbarkeit von globalen und lokalen Variablen nachlesen.

> Das habe Ich soweit verstanden.

Das bezweifle ich.

> Wenn ich zwei Funktionen habe (die eigentlich keine globale Variable
> brauchen) und die auf eine Variable drauf zugreifen die z.B 10 Bytes
> belegt. Brauche Ich nur "10" Bytes.
> Wenn Ich jetzt innerhalb der beiden Funktionen jeweils ein Array mit
> "10" Bytes anlege, würde ich ja "20" Bytes benötigen in diesem moment
> oder?

Es würde wesentlich mehr Spaß machen mit dir zu reden, wenn ich den 
Eindruck gewinnen könnte, daß du auch verstehst was ich dir sage.

Lokale Variablen werden genau in dem Moment angelegt, wenn die 
entsprechende Funktion aufgerufen wird. Und beim Verlassen der Funktion 
wieder gelöscht. Da dein Programm sich nicht gleichzeitig in beiden 
Funktionen befinden kann, wird immer nur höchstens eine der beiden 
Funktionen ihre 10 Bytes für lokale Variablen brauchen. Und so lange 
keine der beiden Funktionen aktiv ist, wird gar kein Speicher für die 
jeweiligen lokalen Variablen gebraucht.

Interessant wird es, wenn Funktionen sich gegenseitig aufrufen, 
möglicherweise sogar noch rekursiv. Dann müssen die lokalen Variablen 
aller Funktionen in so einer Aufruf-Kaskade ins RAM (für gewöhnlich: in 
den Teil des RAMs, der für den Stack zur Verfügung steht) passen.

Wenn die Aufruf-Kaskade jetzt noch davon abhängig ist, welche Daten 
gerade verarbeitet werden, dann wird es mindestens schwierig, in vielen 
Fällen sogar unmöglich, den maximal benötigten Speicher im Voraus 
anzugeben.

von S. R. (svenska)


Lesenswert?

Legst du innerhalb einer Funktion eine Variable an, dann liegt sie auf 
dem Stack. Verlässt du die Funktion wieder, wird der Speicherplatz 
automatisch freigegeben. Auf dem Stack liegen außerdem noch ein paar 
andere Dinge.

Legst du die Variable stattdessen global an (oder innerhalb der Funktion 
mit "static", was im Prinzip das gleiche ist), dann liegt sie im 
Datensegment. Dort existiert sie während der gesamten Laufzeit deines 
Programmes, belegt also auch dann Speicher, wenn du gerade nichts mit 
ihr anstellst.

Der Linker legt die Größe des Datensegmentes so fest, dass alle globalen 
und statischen Variablen dadrin Platz haben. Er weiß also, wieviel 
Speicher du dort benutzt und kann dich warnen, wenn es zu viel ist.

Der Teil des RAMs, der nicht für das Datensegment (und den Heap, aber 
den ignoriere ich hier) reserviert ist, steht dir als Stack zur 
Verfügung. Benutzt du mehr Stack-Speicher, als gerade frei ist, dann 
fliegt dir dein Programm um die Ohren oder fängt an, sich subtil komisch 
zu verhalten (sehr schwer zu debuggen!). Wieviel Speicher auf dem Stack 
frei ist, wissen weder Compiler noch Linker, also gibt es auch keine 
Warnungen.

Und zu guter Letzt: Auf einem Mikrocontroller bekommst du für 
ungenutzten Speicher kein Geld zurück. Lieber 40 Byte im Datensegment 
"verschwenden" und sicher sein, dass der Speicher reicht, als manchmal 
einen Stack-Overflow produzieren - gilt natürlich nur, wenn du genug 
Speicher hast.

von Walter K (Gast)


Lesenswert?

Ich habe auch noch nie verstanden, weshalb immer wieder geglaubt wird, 
man müsse globale Variablen auf Teufel komm raus verhindern.

von Cyblord -. (cyblord)


Lesenswert?

Walter K schrieb:
> Ich habe auch noch nie verstanden, weshalb immer wieder geglaubt wird,
> man müsse globale Variablen auf Teufel komm raus verhindern.

Dann wirst es wohl auch nie mehr kapieren.

Trotzdem zwei wichtige Punkte:

- mit globalen Variablen sind keine mehrfachen Instanzen von "Objekten" 
möglich.

- globale Variablen machen es schwer den Überblick über Seiteneffekte zu 
bewahren.

von Egon N. (egon2321)


Lesenswert?

Walter K schrieb:
> man müsse globale Variablen auf Teufel komm raus verhindern.

Tja, es ist nunmal best practice und findet auch beim Compiler 
Anerkennung und ist daher sehr wohl sinnvoll.

Benutzt man eine Variable nur in einem Scope kann der Compiler 
wesentlich  besser optimieren da er so weiß, dass die Variable sonst 
nirgendwo angerührt oder benötigt wird. Benutzt man globale Variablen 
muss das erst aufgelöst werden und durch irgendwelche Abhängigkeiten 
lassen sich dann diverse Optimierungen nicht anwenden. Was g++ da 
inzwischen kann ist echt genial.

von batman (Gast)


Lesenswert?

Cyblord -. schrieb:
> Trotzdem zwei wichtige Punkte:
>
> - mit globalen Variablen sind keine mehrfachen Instanzen von "Objekten"
> möglich.
>
> - globale Variablen machen es schwer den Überblick über Seiteneffekte zu
> bewahren.

2 Probleme, die man in speziell in Mikrocontrollern oft gar nicht 
findet.
Man muß beim SW-Design auch die Platform berücksichtigen.

von Cyblord -. (cyblord)


Lesenswert?

batman schrieb:
> 2 Probleme, die man in speziell in Mikrocontrollern oft gar nicht
> findet.

Unsinn. Es gibt mehr Mikrocontrollerprojekte als ein LED-Blinkprogram.
Vielleicht ist deine Erfahrung hier nicht ganz so repräsentativ wie du 
denkst.

> Man muß beim SW-Design auch die Platform berücksichtigen.

Nein, grundlegende Konzepte sind da völlig unabhängig. Man kann 
allerdings bei deren Umsetzung die Spezialitäten der Plattform 
berücksichtigen. Grundsätzlich gelten sie aber dennoch.

Es gibt keine guten Gründe für schlechten Code. Es bleibt dann eine 
Ausrede. So eine kann ich für jede Plattform und für jedes Problem 
finden.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Stoppt mal bitte..... und jetzt erstmal tief durchatmen.

Ich denke, dass die Vor- und Nachteile von Heap und Stack ausreichend 
beleuchtet wurden.

Und ich hoffe, dass wir hier vernünftig genug sind, nicht "den einen 
einzig wahren Weg" propagieren zu wollen. Sind wir doch, oder?

von Walter K (Gast)


Lesenswert?

Cyblord -. schrieb:
> - mit globalen Variablen sind keine mehrfachen Instanzen von "Objekten"
> möglich.

Wo sind mehrfache Instanzen relevant?

von Cyblord -. (cyblord)


Lesenswert?

Walter K schrieb:
> Cyblord -. schrieb:
>> - mit globalen Variablen sind keine mehrfachen Instanzen von "Objekten"
>> möglich.
>
> Wo sind mehrfache Instanzen relevant?

Praktisch überall. Wo hat man KEINE Instanzen? Allein diverse 
Interfaces, z.B. UART oder SPI oder was anderes. Man kann ein Modul 
schreiben um deren Funktionalität zu kapseln, und das dann aber einfach 
auf beliebig viele reale Interfaces aufsetzen. Dazu muss man aber eben 
die gesamten Zustandsinformationen für eine solches Modul in einer 
Struktur halten und den Funktionen mitgeben. Das ist ja quasi die 
OOP-Zu-Fuss Methode, wie sie bei C schon seit Anbegin der Zeit angewandt 
wird.

Globale Variablen sind auch nicht per se verboten. Wenn eine solche 
Variable Teil des Gesamtzustandes des Gerätes ist, kann man sie auch 
einfach global definieren. Aber meist werden halt dann schnell mal 
temporäre Variablen global gemacht, und eben nur lokal verwendete 
Zustände, von z.B. oben genannten Interface. Und das ist halt dann kein 
sauberer Stil mehr sondern Pfusch.

: Bearbeitet durch User
von batman (Gast)


Lesenswert?

Ausgerechnet UART findet in vielen Tinys gar keine Verwendung und 
entsprechend selten braucht man generell mehrere davon parallel.

Mit deinen Designrules und -Patterns aus der OO-Kiste wirst du bei 
kleinen Mikrocontrollern nicht erst beim Tiny10 scheitern. Ok, sowas 
benutzt man in deiner Welt halt einfach nicht.

von Cyblord -. (cyblord)


Lesenswert?

batman schrieb:
> Ausgerechnet UART findet in vielen Tinys gar keine Verwendung und
> entsprechend selten braucht man generell mehrere davon parallel.

Das waren Beispiele. Fast jede Controlleranwendung hat ein Interface 
nach aussen. Neben dem reinen physikalischen Interface hockt da ja meist 
ein übergeordnete Protokoll drauf, und sei es nur ein Steuerbit oder 
eine Checksumme oder eine Auswertlogik um 2 Pins abzufragen. Und schon 
hat man eine Funktionalität, die man ohne solche Konzepte nicht einfach 
und sauber duplizieren kann, wenn man eben mal 2 oder 3 davon braucht.

Das kommt ich kleinsten Controller vor und kann im kleinsten Controller 
sauber umgesetzt werden.

> Mit deinen Designrules und -Patterns aus der OO-Kiste wirst du bei
> kleinen Mikrocontrollern nicht erst beim Tiny10 scheitern. Ok, sowas
> benutzt man in deiner Welt halt einfach nicht.

Unsinn. Ich arbeite mit ATTinys seit Jahren.
Du musst mir nicht erzählen was man da braucht oder nicht braucht. Was 
du tust, sind ausreden erfinden für deinen Pfusch-Code. Dafür kann der 
Controller aber nichts.

Natürlich ist ein Controller ohne SRAM ein unnützes Beispiel, weil es 
hier um C geht und nicht um Assembler. Natürlich kann man sich immer 
einen so kleinen eingeschränkte Controller überlegen, bei dem man nichts 
mehr tun kann ,als 10 ASM Befehle gerade noch linear hintereinander 
abzuarbeiten.

Aber so sieht die Embedded Welt nicht aus. Schon auf normalen Tinys und 
Megas kann man mit solchen Konzepten gut arbeiten. Der Tiny841 hat z.B. 
2 UARTs und genug Flash und SRAM.
Dafür braucht man nicht mal 32 Bitter.
Deine Beispiele taugen also nur zum plumpen polemisieren.

: Bearbeitet durch User
von Walter K (Gast)


Lesenswert?

>...Das ist ja quasi die OOP-Zu-Fuss Methode, wie sie bei C schon seit Anbegin der 
Zeit angewandt wird.

Für mich ist das OOP - Blah Blah!

Unglaublich viele Begriffe und Regeln - und nichts dahinter!

Natürlich hat auch OO seine Berechtigung - aber man sollte prozedurale 
Programmierung nicht einfach als „Pfusch „ bezeichnen

von Cyblord -. (cyblord)


Lesenswert?

Walter K schrieb:
>>...Das ist ja quasi die OOP-Zu-Fuss Methode, wie sie bei C schon seit Anbegin 
der
> Zeit angewandt wird.
>
> Für mich ist das OOP - Blah Blah!

Ja natürlich so habe ich dich auch eingeschätzt.

> Unglaublich viele Begriffe und Regeln - und nichts dahinter!

Du verstehst den Sinn nicht, das ist alles. Das ist aber auch nicht 
schlimm. Ich kann auch nicht Geige spielen. Macht mir nichts.

> Natürlich hat auch OO seine Berechtigung - aber man sollte prozedurale
> Programmierung nicht einfach als „Pfusch „ bezeichnen

Es geht aber nicht um OOP. Es geht um Kapselung und es geht um 
Abstraktion. Dafür braucht es kein OOP. Das geht auch prozedural.

Aber prozedural bedeutet eben nicht automatisch den Verzicht auf oben 
genanntes.
Es bedeutet NICHT das man einfach alle Variablen global macht und dann 
so lange im Code darauf rumschreibt bis ab und zu mal korrektes 
passiert. Das kann ich mit OOP Sprachen aber auch machen.

Es geht also darum ob man sauber programmiert oder nicht. Und ignoriert 
man Dinge wie Kapselung und Abstraktion dann programmiert man eben nicht 
sauber. Egal unter welchen Schlagworten.

Aber dann sage uns doch mal, wenn du z:B. ein Protokoll auf UART 
aufsetzen würdest, z.b. DMX. Wie würdest du dann das ganze Konzipieren 
um es auch variabel auf 2 oder 3 Uarts nutzen zu können? Wie sähe dein 
prozeduraler Ansatz hier aus? Werden wir mal konkret.

: Bearbeitet durch User
von Walter K (Gast)


Lesenswert?

„...
Es bedeutet NICHT das man einfach alle Variablen global macht und dann 
so lange im Code darauf rumschreibt bis ab und zu mal korrektes 
passiert...“

Erst unterstellst Du Anderen sowas - und belehrst Du, dass es so nicht 
geht.

Wer hat hier irgendwann behauptet, alle Variablen müssten global gemacht 
werden?

von Mark B. (markbrandis)


Lesenswert?

Walter K schrieb:
> Ich habe auch noch nie verstanden, weshalb immer wieder geglaubt wird,
> man müsse globale Variablen auf Teufel komm raus verhindern.

Muss man auch nicht. Aber man tut gut daran nach dem Motto vorzugehen:

So viel global wie nötig - so wenig wie möglich.

von Peter D. (peda)


Lesenswert?

Cyblord -. schrieb:
> Der Tiny841 hat z.B.
> 2 UARTs und genug Flash und SRAM.

Ich würde trotzdem einfach den Code duplizieren und ein putchar0, 
putchar1 usw. schreiben, die direkt auf IO-Register zugreifen, als 
umständlich über viele Pointerinstanzen auf eine Struct.
Das Duplizieren spart RAM, ist deutlich schneller und dürfte sogar 
weniger Code-Flash kosten.

von Cyblord -. (cyblord)


Lesenswert?

Peter D. schrieb:
> Ich würde trotzdem einfach den Code duplizieren und ein putchar0,
> putchar1 usw. schreiben, die direkt auf IO-Register zugreifen,

Ich weiß, ich kenne deinen Code.
So stolz wäre ich auf eine solche Einstellung aber nicht.

Außerdem geht es ja nicht um das putchar0 oder putchar1. Irgendwo im 
Code muss man ja auf den jeweiligen konkreten UART gehen. Und putchar 
tut ja auch nichts als ein Byte rauszuschreiben. Da ist ja quasi keine 
Funktionalität dahinter.
Ich spreche auch nicht von Pointern auf die IO Register. Denkst du ich 
will dass mein XYZ-Modul irgendwas von IO-Registern oder HW-Adressen 
meinen Controllers weiß?

Aber ich sprach von einem übergeordneten Protokoll welches einen UART 
verwendet. D.h. es geht darum eine spezielle Funktionalität zu kapseln. 
Die eben nicht trivial ist. Also wie kommt dieses Modul an dein putchar0 
oder putchar1? Kopierst du den gesamten Code des (z.B. DMX oder besser 
Modbus-)Moduls und setzt jeweils überall putchar0 und putchar1 ein?

Deine Antwort macht mir eher deutlich dass du aus dem klein-klein deiner 
Hardwareregister denktechnisch gar nicht rauskommst.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Cyblord -. schrieb:
> Deine Beispiele taugen also nur zum plumpen polemisieren.

Cyblord -. schrieb:
> Deine Antwort macht mir eher deutlich dass du aus dem klein-klein deiner
> Hardwareregister denktechnisch gar nicht rauskommst.

Immer wieder erstaunlich, wie Menschen anderen Menschen das vorwerfen, 
was sie selbst auch betreiben.

von Jim M. (turboj)


Lesenswert?

Cyblord -. schrieb:
> Wie würdest du dann das ganze Konzipieren
> um es auch variabel auf 2 oder 3 Uarts nutzen zu können? Wie sähe dein
> prozeduraler Ansatz hier aus?

Wenn das echt parallel laufen soll, muss man bei 2-3 UARTs aber sowas 
wie DMA einsetzen - und damit sieht der Code dann GANZ anders aus.

Die Vorstellung auf einem µC einfach was objektorientiert zu machen ist 
etwas naiv. Sobald etwas "nebenher" laufen muss (z.B. Tastenabfrage oder 
anderer Input) muss das im Code auch berücksichtigt werden (Stichwort: 
Delay).

Beispiel UART: Normaler Beispielcode wartet praktisch immer darauf das 
TX-Buffer leer ist. Wenn man das jetzt einfach objektorientiert kapselt 
dann sender man einen String bei 3 UARTs nur jeweils auf einem zur 
gleichen Zeit.

Für überschneidenes Senden müsste man dann auf andere Tricks (wie o.g. 
DMA) zurück greifen. Dann ist aber z.B. die RS485 Umschaltung plötlich 
ein Problem, denn die darf natürlich nur nach dem Senden passieren.

TL;DR Objektorientiert kann man machen, aber man muss das Gesamtbild im 
Auge behalten.

von Cyblord -. (cyblord)


Lesenswert?

Jim M. schrieb:
> Wenn das echt parallel laufen soll, muss man bei 2-3 UARTs aber sowas
> wie DMA einsetzen - und damit sieht der Code dann GANZ anders aus.

Unsinn. Empfangen per Interrupt reicht aus. Die Performance muss 
natürlich dem des System angepasst sein.

> Normaler Beispielcode
Wir reden hier von Produktivcode, Was hat irgendein Beispielcode damit 
zu tun? Nirgendwo in realen Projekten wartet ein UART auf den Empfang 
und blockiert dabei das Restsystem.

Aber das ist doch gar nicht der Punkt.
Und ja, man kann sich über SW-Architektur unterhalten ohne in 
Nebenkriegsschauplätze abzutauchen. Versuchs mal.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Cyblord -. schrieb:
> Kopierst du den gesamten Code des (z.B. DMX oder besser
> Modbus-)Moduls und setzt jeweils überall putchar0 und putchar1 ein?

Ich hatte bisher nicht die Notwendigkeit, mehrere DMXe, Modbusse usw. in 
einem MC zu verwenden. Daher wird das putchar0 oder putchar1 über 
Defines in das jeweilige Protokoll eingebunden.

Cyblord -. schrieb:
> Deine Antwort macht mir eher deutlich dass du aus dem klein-klein deiner
> Hardwareregister denktechnisch gar nicht rauskommst.

Es sei mir erlaubt, daß ich ohne jeden Grund Code auch nicht unnötig 
aufblase.

von Cyblord -. (cyblord)


Lesenswert?

Peter D. schrieb:
> Cyblord -. schrieb:
>> Kopierst du den gesamten Code des (z.B. DMX oder besser
>> Modbus-)Moduls und setzt jeweils überall putchar0 und putchar1 ein?
>
> Ich hatte bisher nicht die Notwendigkeit, mehrere DMXe, Modbusse usw. in
> einem MC zu verwenden.

Hmm ok. Das finde ich jetzt doch ein bisschen Arm. Da hätte ich etwas 
mehr erwartet als ein "brauch ich nicht, hab ich nie gebraucht".
Bestreitest du denn die Möglichkeit dass es jemals Notwendig sein 
könnte?
Wie WÜRDEST du es machen WENN du es mal brauchen würdest.

Außerdem bedenke, es sind nur Beispiele. Das Szenario ist nicht auf 
irgendwelche Bussysteme beschränkt, es ist nur für gänginge Controller 
ein häufiges Szenario. Nun gut, wohl nicht bei jedem.

> Es sei mir erlaubt, daß ich ohne jeden Grund Code auch nicht unnötig
> aufblase.

Moment, und das von jemandem der "Duplizieren" als Lösung präsentiert?

Außerdem: Das tue ich auch nicht. Ich sehe auch nicht wie Code durch 
eine sinnvolle Kapselung überhaupt aufgeblasen werden könnte. Schon gar 
nicht sinnlos. Hast du da mal ein abschreckendes Beispiel?

: Bearbeitet durch User
von Walter (Gast)


Lesenswert?

Mark B. schrieb:
> Stefan U. schrieb:
>> Nein, der Compiler hat keine Ahnung, wie viel Stack später zur Laufzeit
>> verfügbar ist.
>
> Der Compiler nicht, aber der Linker schon.

hilft aber leider auch nicht weiter wenn der Linker weiß wieviel Stack 
zur  Verfügung, aber nicht weiß wieviel Stack das Programm braucht

von Cyblord -. (cyblord)


Lesenswert?

Walter schrieb:
>> Der Compiler nicht, aber der Linker schon.
>
> hilft aber leider auch nicht weiter wenn der Linker weiß wieviel Stack
> zur  Verfügung, aber nicht weiß wieviel Stack das Programm braucht

Völlig richtig. Natürlich kann auch der Linker nicht wissen wie oft eine 
Funktion ggf. rekursiv aufgerufen wird und wie viel Stack verbraucht 
wird.

von batman (Gast)


Lesenswert?

Dafür nimmt man dann einfach einen MC mit RAM im Überfluß.

von Cyblord -. (cyblord)


Lesenswert?

batman schrieb:
> Dafür nimmt man dann einfach einen MC mit RAM im Überfluß.

Und ich dachte immer der Programmierer hat das im Griff, wie groß der 
Stack wird.
Aber in deiner Welt kopiert man wohl irgendwelche Codefetzen zusammen 
und lässt sich überraschen wie viel Speicher verbraucht wird.

von Peter D. (peda)


Lesenswert?

Cyblord -. schrieb:
> Wie WÜRDEST du es machen WENN du es mal brauchen würdest.

Da laß ich mich überraschen.

Ich hab zwar auf nem ATmega2560 alle 4 UARTs verwendet, aber für völlig 
unterschiedliche Aufgaben.
Eine liest als SPI im Timerinterrupt Tasten, Encoder ein und gibt LEDs 
aus.
Eine als SPI setzt DACs und liest ADCs.
Eine steuert als UART ein eDIPTFT43 an.
Und die 4. macht Debugausgaben.
Die Aufgaben sind also so unterschiedlich, da läßt sich nichts 
modularisieren.
Daneben gibt es noch I2C, CAN und Ethernet auf der Platine. Wobei ich 
CAN und Ethernet nicht selbst geschrieben habe. Ich kriege nur Puffer 
mit Empfangsnachrichten übergeben und schreibe in nen Sendepuffer 
zurück.

Ich bin auch kein 5d/8h Programmierer. Ich entwickele hauptsächlich 
Hardware. Allerdings habe ich gemerkt, daß es sinnvoll ist, die 
hardwarenahe Firmware selber zu stricken. Es ist nur schwer für einen 
reinen Softwerker, alle Details der Hardware zu verstehen, d.h. das 
würde viel Zeit kosten und fehlerträchtig sein. Daher mache ich einen 
kleinen Teil Software mit.
Oft weiß man auch nicht, ob ein Fehler in Hardware oder Software liegt 
und dann muß man sich eben Testroutinen selber schreiben können. Oder 
auch Workarounds, die Hardwarefehler korrigieren.

: Bearbeitet durch User
von batman (Gast)


Lesenswert?

Cyblord -. schrieb:
> Und ich dachte immer der Programmierer hat das im Griff, wie groß der
> Stack wird.

Das denken wohl viele. In der Realität wissen sie es nicht.

von dummschwaetzer (Gast)


Lesenswert?

Der angenommene maximale Stack wird bei einigen Compilern / IDE 
angegeben!
Ob die Angabe stimmt sei mal dahingestellt...

von A. S. (Gast)


Lesenswert?

Cyblord -. schrieb:
> Hmm ok. Das finde ich jetzt doch ein bisschen Arm.

Ich mache es wie Peter. Wobei der C-Code dabei automatisch kopiert wird, 
wenn die Register und Funktionen gleich, nur an anderen Stellen sind 
(aus sio1.c mit REG_X_SIO1 wird dann sio2.c mit REG_X_SIO2).

Für die Auswahl im Code gibt es bei mir je nach Familie 3 Möglichkeiten:

1) Hard kodiert Zuordnung (Modul X hat SIO1)
2) #define ModulX_Sio SIO1
3) Eine Struktur, in der die Funktionspointer der Sio zusammengefasst 
sind (Der Kontext ist ja in der Datei sioX.c). Die Verlinkung erfolgt 
dann, indem Modul X mit einem Pointer auf Sio1 initialisiert wird

Das Dritte verwende ich seit Jahren auf PIC18 (seit Hi-Tech 
Funktionspointer unterstützt)

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> Cyblord -. schrieb:
>> Hmm ok. Das finde ich jetzt doch ein bisschen Arm.
>
> Ich mache es wie Peter. Wobei der C-Code dabei automatisch kopiert wird,
> wenn die Register und Funktionen gleich, nur an anderen Stellen sind
> (aus sio1.c mit REG_X_SIO1 wird dann sio2.c mit REG_X_SIO2).

Und warum benutzt Du dafür nicht gleich C++ und templates?

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Und warum benutzt Du dafür nicht gleich C++ und templates?

Ganz einfach, ich kann kein C++.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Und warum benutzt Du dafür nicht gleich C++ und templates?
>
> Ganz einfach, ich kann kein C++.

Doch, Du kannst es ja schon ;-) Was Dir fehlt ist das Wissen um 
templates ...

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Doch, Du kannst es ja schon ;-) Was Dir fehlt ist das Wissen um
> templates ...

Z.B. in diesem Thread:
Beitrag "constexpr Argument-Wrapper"
habe ich 0,nix verstanden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Doch, Du kannst es ja schon ;-) Was Dir fehlt ist das Wissen um
>> templates ...
>
> Z.B. in diesem Thread:
> Beitrag "constexpr Argument-Wrapper"
> habe ich 0,nix verstanden.

Oh ja, constexpr kann bissl tricky sein ...

Aber in diesem Fall hier geht es ja um die simpelste Anwendung von 
templates zur Parametrierung von Funktionen (-> Funktionstemplates). Die 
Grundlagen, die Du für dieses Problem brauchst, hast Du Dir in 30min 
angelesen.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Aber in diesem Fall hier geht es ja um die simpelste Anwendung von
> templates zur Parametrierung von Funktionen (-> Funktionstemplates).

Ich bräuchte weniger Templates für die Funktionen, denn die sind ja alle 
verschieden, sondern eher für die 4 UARTs oder 4 Timer, damit ich einer 
Funktion eine davon zuweisen kann.
Ist zugegeben aber eine rein theoretische Frage, denn die Zuordnung ist 
ja durch die Hardware fest vorgegeben.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Aber in diesem Fall hier geht es ja um die simpelste Anwendung von
>> templates zur Parametrierung von Funktionen (-> Funktionstemplates).
>
> Ich bräuchte weniger Templates für die Funktionen, denn die sind ja alle
> verschieden, sondern eher für die 4 UARTs oder 4 Timer, damit ich einer
> Funktion eine davon zuweisen kann.

Ich hatte Dich so verstanden, dass Dein putchar0() und putchar1() bis 
auf die verwendeten SFRs identisch sind.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Ich hatte Dich so verstanden, dass Dein putchar0() und putchar1() bis
> auf die verwendeten SFRs identisch sind.

In einem älteren Projekt hatte ich das so gemacht und dann per Define 
ausgewählt, welches ich verwende. So richtig hat mich das aber auch 
nicht vom Hocker gehauen.
Ich hätte gern, daß ich nur ein putchar() habe und dann mit "use UART0" 
auswählen kann, welcher Port benutzt wird.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Ich hatte Dich so verstanden, dass Dein putchar0() und putchar1() bis
>> auf die verwendeten SFRs identisch sind.
>
> In einem älteren Projekt hatte ich das so gemacht und dann per Define
> ausgewählt, welches ich verwende. So richtig hat mich das aber auch
> nicht vom Hocker gehauen.
> Ich hätte gern, daß ich nur ein putchar() habe und dann mit "use UART0"
> auswählen kann, welcher Port benutzt wird.

Du könntest ein Funktionstemplate putchar<>() schreiben und zwei 
Parameter-Trait-Typen Uart0 und Uart1 (die Du aber auch wieder zu einem 
Klassentemplate zusammen fassen könntest). Damit könntest Du schreiben:
1
struct Uart0 {...};
2
struct Uart1 {...};
3
4
template<typename UART>
5
void putchar(char c) {...}
6
...
7
8
putchar<Uart0>(...);
9
putchar<Uart1>(...);

Allerdings würde man putchar() eher als Elementfunktion eines 
Klassentemplates schreiben. Etwa:
1
template<auto Number>
2
class Uart {
3
public:
4
static void putchar(char c) {...}
5
};
6
7
...
8
9
using uart0 = Uart<0>;
10
using uart1 = Uart<1>;
11
12
...
13
14
uart0::putchar(...);
15
uart1::putchar(...);

von Egon N. (egon2321)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Und warum benutzt Du dafür nicht gleich C++ und templates?
>
> Ganz einfach, ich kann kein C++.

Gibt viele die C++ wie C nutzen. Man verpasst dabei halt nur leider 
viel, aber das gibt selbst für Leute die vor Jahren C++ gelernt haben, 
selbst wenn man vor Jahren mit C++11 angefangen hat sind die neuen 
Features un C++14 und gerade C++17 genial.

Man muss halt dauernd auf dem Laufenden bleiben. Bei µC ist das 
natürlich etwas schwerer da hier teils maximal C++11 möglich ist, aber 
gerade auf x86_64 bzw. ARM bekommt man so viele Komfortfunktionen mit 
jeder neuen Version dass es sich schlichtweg lohnt diese einzusetzen.

http://www.stroustrup.com/4th.html

Das ist gut aber naja, es ist schon sagen wir etwas schwere Kost und 
teils zu ausführlich, für den Einstieg ist das hier auch nicht all zu 
schlecht:

https://www.amazon.de/Effective-Modern-Specific-Improve-2014-12-05/dp/B0184WO96Q/

Beide Bücher findet man bei div. Suchseiten zum probelesen in digitaler 
Form, alte Auflagen liegen teils frei auf Uniservern. 90€ solltest du da 
nicht gleich ausgeben. ;)

Aber selbst die Wikipedia Artikel sind relativ okay inzwischen.

https://de.wikipedia.org/wiki/Template_(C%2B%2B)

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Man hat auch immer wieder das Problem, etwas erstmal so machen zu 
müssen, daß es funktioniert. Um es dann auch noch schön zu machen, fehlt 
oft die Zeit.
Einer Funktion eine beliebige UART zuweisen zu können, wäre zwar hübsch.
Aber nicht notwendig, denn der Schaltplan der Platine steht ja schon 
fest.

von Egon N. (egon2321)


Lesenswert?

Peter D. schrieb:
> Einer Funktion eine beliebige UART zuweisen zu können, wäre zwar hübsch.
> Aber nicht notwendig, denn der Schaltplan der Platine steht ja schon
> fest.

Wenn du deine Software sauber versionierst und immer per Git/etc. 
verfügbar hast ist es halt praktisch sowas auszulagern an eine Lib. 
Entdeckst du einen Bug kannst du so diesen gleichzeitig in all deinen 
Anwendungen beheben. Ist die API für UART sauber implementiert in deiner 
Lib kannst du sogar den µC für einen ganzen Haufen Projekte ohne viel 
Arbeit wechseln. Musst  du da jeweils einzelne Funktionen usw. und 
hartcodierte Werte anpassen ist das relativ schnell fehlerträchtig und 
man macht sich nur unnötig Arbeit.

Selbst wenn du die Funktion nicht in eine Lib verlagerst kannst du so 
ziemlich simpel mit sed/etc. mehrere Dateien schnell überarbeiten.

Zudem steigt dadurch die Lesbarkeit des Codes.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Damit sind wir wieder an dem üblichen Punkt der Diskussion angekommen: 
jemandem die Vorteile eines besseren Werkzeuges klar zu machen, wenn er 
subjektiv noch nie wirklich unter den Nachteilen des schlechteren 
Werkzeugs gelitten hat, ist nur bei wenigen Menschen möglich. Nur wenige 
sind in der Lage, den Aufwand zur Aneignung neuen Wissens objektiv in 
Relation zu einer Produktivitäts- oder Qualitätssteigerung zu setzen.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Nur wenige
> sind in der Lage, den Aufwand zur Aneignung neuen Wissens objektiv in
> Relation zu einer Produktivitäts- oder Qualitätssteigerung zu setzen.

Oft ergibt sich eine Steigerung aber erst, wenn die Projektgröße und 
Projektanzahl einen bestimmten Punkt überschreitet.
Bei kleinen Projekten kann ein zu hoher Abstraktionslevel eher 
hinderlich sein.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Nur wenige
>> sind in der Lage, den Aufwand zur Aneignung neuen Wissens objektiv in
>> Relation zu einer Produktivitäts- oder Qualitätssteigerung zu setzen.
>
> Oft ergibt sich eine Steigerung aber erst, wenn die Projektgröße und
> Projektanzahl einen bestimmten Punkt überschreitet.

Bei der Thematik, über die wir hier reden, hätte es sich ja schon längst 
gelohnt, denn Du hast ja schon wohl länger über eine Lösung per CPP 
nachgedacht.

von honest_joe (Gast)


Lesenswert?

Interessant, dass es nach all diesen zahllosen Jahren immer noch Leute 
gibt, die mit cyblord diskutieren :)
Der Forentroll des Jahrhunderts: Miesepetrig, schlecht informiert und 
niemals einsichtig.

von Mark B. (markbrandis)


Lesenswert?

honest_joe schrieb:
> Interessant, dass es nach all diesen zahllosen Jahren immer noch Leute
> gibt, die mit cyblord diskutieren :)
> Der Forentroll des Jahrhunderts: Miesepetrig, schlecht informiert und
> niemals einsichtig.

Miesepetrig: Definitiv.
Schlecht informiert würde ich nicht sagen. Ahnung hat er schon.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> denn Du hast ja schon wohl länger über eine Lösung per CPP
> nachgedacht.

Nur bin ich bisher nichtmal ansatzweise zu einem Ergebnis gekommen.

von A. S. (Gast)


Lesenswert?

Peter D. schrieb:
> In einem älteren Projekt hatte ich das so gemacht und dann per Define
> ausgewählt, welches ich verwende. So richtig hat mich das aber auch
> nicht vom Hocker gehauen.
> Ich hätte gern, daß ich nur ein putchar() habe und dann mit "use UART0"
> auswählen kann, welcher Port benutzt wird.

Mmh, ich bin mir nicht sicher, was Du eigentlich möchtest und wo das 
problem dazu liegt?

Eine Funktion putchar, die aus verschiedenen Modueln aufgerufen wird und 
mal an UART0 und mal an UART1 hängt? Wenn Du mit Funktionspointern 
arbeitest ist das doch nur ein umkopieren. Und ohne ein if oder ein 
switch.

Oder verschieden Putchars, für jedes Modul eines? Da wird einfach beim 
Init des Moduls die zugehörige Sio mit übergeben und vom Modul selbst 
(mit Baudrate etc.) initialisiert.

Der Vorteil von C++ liegt vor allem in virtuellen Objekten. Mit realen 
Objekten (Uarts, Sensoren für einen bestimmten Messwert, etc) wird der 
Vorteil immer kleiner. Und bei realen Objekten kann man OOP-Kapselung 
durch automatisch kopierten Code sehr gut realisieren, da der 
this-Pointer bei den Funktionen entfallen kann (Der Kontext ist ja im 
kopierten Modul). Sobald das alles in einer Lib liegt, sorgt der Linker 
auch für die je Projekt minimale Speicherbelegung.

C (statt C++ mit Templates und new) ermöglicht eine sehr tiefe statische 
Analyse. Und der ganze Quatsch mit Namespaces und ADL, ... ja super, am 
Ende läuft es doch darauf hinaus, dass alles voll qualifiziert werden 
muss, sobald es ein wenig komplexer wird.

Wenn Du ein konkretes Problem in dem Bereich kreierst, bin ich sicher 
(um eine Kiste Bier), dass ich in C einen genauso sauberen Ansatz 
(Lesbarkeit, Codegröße, Performance) hinbekomme wie C++ Gurus hier.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
>
> Der Vorteil von C++ liegt vor allem in virtuellen Objekten.

Was sind denn in Deiner Nomenklatur "virtuelle Objekte" ...

> Mit realen
> Objekten (Uarts, Sensoren für einen bestimmten Messwert, etc)

... und was sind "reale Objekte"?

Du meinst wahrscheinlich: Sensoren für Messgrößen, was der Sensor 
liefert ist dann der Messwert.

> C (statt C++ mit Templates und new)

Was hat nun wieder template-Mechanik mit dyn. Allokation zu tun?

> ermöglicht eine sehr tiefe statische
> Analyse. Und der ganze Quatsch mit Namespaces und ADL, ...

Und warum sind namespaces Quatsch?

Liest sich so wie gerührt und geschüttelt.

von A. S. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was sind denn in Deiner Nomenklatur "virtuelle Objekte" ...

Objekte, die keinen festen, physischen Objekten entsprechen (oder 
zugehörig sind), in die das System eingebettet ist. Beispiel Auto:

 * Das Rad vorne links ist ein reales Objekt. Auch wenn es viele 
Eigenschaften mit dem Rad vorne rechts gemein hat, gibt es ähnlich viele 
konkrete Eigenschaften, die konkret behandelt werden müssen. Hier ist 
der Vorteil von C++ vs. C gering. Es gibt wenig sinnvolle Codeteile, die 
auch ein 6tes Rad händeln.

 * Die konkrete CD im Player, eine Straßen im Navi sind virtuelle 
Objekte. Es wird nur ein abstrahiertes Modell der Objekte verwendet, 
Straßenbelag und Interpret werden vielleicht angezeigt, spielen aber 
keine Rolle. Dem Code ist es egal, ob die Karte 20000 Straßen mehr hat 
oder sich die Abspielposition der 70ten CD merkt.

Wilhelm M. schrieb:
> Du meinst wahrscheinlich: Sensoren für Messgrößen, was der Sensor
> liefert ist dann der Messwert

Hier ist die Unterscheidung: Im Auto ist der Radsensor v.l. ein 
konkreter, "realer" Sensor. In einer Visualisierung oder einem 
Diagnoseprogramm, wo z.B. alle Sensoren aufgelistet werden, ist er ein 
rein virtuelles Objekt, z.B. mit dem Text-Attribut "Drehzahl Rad VL".

Wilhelm M. schrieb:
> Und warum sind namespaces Quatsch?

Weil Du bei größeren Projekte feststellst, dass manche Dinge nicht so 
sind, wie sie scheinen und beabsichtigt waren (durch ADL&co)

Wilhelm M. schrieb:
> Was hat nun wieder template-Mechanik mit dyn. Allokation zu tun?

nichts. Warum? Es ging darum, dass dyn. Allokation die statische Analyse 
des Speicherbedarfs erschweren kann.

Gruß
achim

von Peter D. (peda)


Lesenswert?

Achim S. schrieb:
> Eine Funktion putchar, die aus verschiedenen Modueln aufgerufen wird und
> mal an UART0 und mal an UART1 hängt?

Nein.
Es soll eine Funktion sein, wo ich zur Compilezeit einfach zuweisen 
kann, welche HW-Ressourcen sie belegt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> Wilhelm M. schrieb:
>> Was sind denn in Deiner Nomenklatur "virtuelle Objekte" ...

>  * Die konkrete CD im Player, eine Straßen im Navi sind virtuelle
> Objekte.

Sag doch einfach, die Straße ist eine (konkrete / generische) Klasse, 
die Exemplare davon sind Objekte. Das ist die "offizielle" Nomenklatur 
in OOD/OOP. "Virtuelle Objekte" ist Dein persönlicher Sprachgebrauch.

Daneben gibt es technische Klassen, die keine Entsprechung in der realen 
Welt haben.

> Wilhelm M. schrieb:
>> Du meinst wahrscheinlich: Sensoren für Messgrößen, was der Sensor
>> liefert ist dann der Messwert
>
> Hier ist die Unterscheidung: Im Auto ist der Radsensor v.l. ein
> konkreter, "realer" Sensor. In einer Visualisierung oder einem
> Diagnoseprogramm, wo z.B. alle Sensoren aufgelistet werden, ist er ein
> rein virtuelles Objekt, z.B. mit dem Text-Attribut "Drehzahl Rad VL".

Mir ging es um die Unterscheidung: Messwert vs. Messgröße.

> Wilhelm M. schrieb:
>> Und warum sind namespaces Quatsch?
>
> Weil Du bei größeren Projekte feststellst, dass manche Dinge nicht so
> sind, wie sie scheinen und beabsichtigt waren (durch ADL&co)

Und wie wäre es ohne namespaces?

> Wilhelm M. schrieb:
>> Was hat nun wieder template-Mechanik mit dyn. Allokation zu tun?
>
> nichts. Warum?

Weil Du es in einen Zusammenhang gestellt hast.

> Es ging darum, dass dyn. Allokation die statische Analyse
> des Speicherbedarfs erschweren kann.

Aber templates ja wiederum nicht.

Du bist in Deinen Begriffen und Aussagen sehr unscharf.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Achim S. schrieb:
>> Eine Funktion putchar, die aus verschiedenen Modueln aufgerufen wird und
>> mal an UART0 und mal an UART1 hängt?
>
> Nein.
> Es soll eine Funktion sein, wo ich zur Compilezeit einfach zuweisen
> kann, welche HW-Ressourcen sie belegt.

Bei C kann/will ich Dir nicht helfen, ein/zwei C++-Möglichkeiten habe 
ich Dir oben aufgezeigt.

von A. S. (Gast)


Lesenswert?

Peter D. schrieb:
> Es soll eine Funktion sein, wo ich zur Compilezeit einfach zuweisen
> kann, welche HW-Ressourcen sie belegt.

Da Du kein #defines möchtest, nehme ich an, dass Dein putchar kompiliert 
in einer LIB liegt.

Mit Funktionpointern im einfachsten Fall:
1
/* in Headern */
2
struct sUart
3
{
4
  void Init(int Baudrate);
5
  void put(int c);
6
  void (*recv)(int c);
7
  ...
8
};
9
10
extern struct sUart Uart0;
11
extern struct sUart Uart1;
12
13
/* putchar in lib */
14
extern int putchar(int c);
15
/* Uart to use with putchar. */
16
extern (const) struct sUart *StdoutUart; 
17
18
/* in main.c: variante 1, putchar(Firmware) initialisiert selber */
19
const struct sUart *StdoutUart = &Uart0;
20
21
/* variante 2, putchar macht nichts solange ptr=0 */
22
struct sUart *StdoutUart = 0;
23
24
void main(void)
25
{
26
#if variante2
27
    Uart0.Init(9600);
28
    StdoutUart = &Uart0;
29
#endif
30
31
    printf("hallo Welt");
32
}

von batman (Gast)


Lesenswert?

So könnte man auch gleich noch alle AVR-Libs, egal ob Mega oder Tiny auf 
eine reduzieren, völlig hardwareunabhängig.

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.