Wunderschönen ;)
kurze Frage.:
Eigentlich kann man ja überhaupt keine lokalen Variablen zurück an den
Aufrufer geben, wenn ich Sie jedoch mit "static" deklariere,
funktioniert das?
Weil "static" festen Speicherplatz reserviert?
Danke im vorraus..
Ohne static wird die Variable nach dem Funktionsaufruf wieder zerstört
und bei neuem Aufruf wieder angelegt.
Wenn sie static ist, bleibt sie für immer erhalten und kann somit auch
mit einem Pointer übergeben werden. Eine static Variable solltest du
immer initialisieren!
1
uint8_t*test(){
2
staticuint8_ti=0;//WIrd nur einmal gemacht beim STart des Programm.
Nils N. schrieb:> uint8_t * test(){> static uint8_t i = 0; //WIrd nur einmal gemacht beim STart des Programm.>> i++;>> return &i;> }
geht aber auch ohne Pointer?!
UnderDog schrieb:> Was hat es denn für einen Vorteil wenn die Variable innerhalb einer> Funktion nicht "static" ist? Performance?
Eher Speicher. Wie oben erwähnt wurde, existieren statische Variablen
dauerhaft. hast du in nem etwas größeren projekt, 10 Funktionen mit je 3
lokalen Variablen, bräuchtest du mal eben 30 statische Variablen.
Underdog, überlese diesen Post mal, das ist nur für C++ und nicht für C
relevant.
Aber weil wir beim Thema sind und mir genau dieses Verhalten vor kurzer
Zeit in C++ um die Ohren geflogen ist:
Bei C++ wird die lokale statische Variable nicht beim Programmstart
sondern bei der ersten Benutzung initialisiert. Dafür baut der Compiler
im Zweifelsfall extra Code ein, der erkennt ob man im ersten Aufruf
steckt oder nicht.
* Vor C++11 ist das ganze nicht Thread-save. D.h. man kann sich ganz
gehörig in den Fuß schießen wenn man mehrere Threads benutzt.
* Ab C++11 ist die Initialisierung Thread-save mit dem Nebeneffekt, das
man bei jedem Aufruf durch eine Synchronisation durch muss.
Ihr könnt euch sicher mein dummes Gesicht vorstellen, als ich
feststellte das eine völlig harmlos aussehende Helper Funktion jedes mal
durch einen System-Mutex geht und tonnenweise Rechenzeit frisst. Einfach
mal die static Variable auf File-Level statt Function-Level zu
deklarieren brachte einen satten Performance Boost.
Glücklicherweise ist der Compiler häufig in der Lage zu erkennen wann
das nötig ist, und wann nicht. Die genauen Regeln dafür hab ich
allerdings noch nicht durchblickt.
Also aufgepasst, liebe C++ Entwickler.
Threadsafety ist natürlich noch eine ganze, andere Baustelle.
Static variablen können von überall zugegriffen werden. Entsprechend
auch aus zwei Funktionen gleichzeitig.
Herkömmliche Mikrokontroler haben keine paralellen Ausführungseinheiten.
@torus
Hast du variable wirklich global deklariert, wegen "File level"?
Eigentlich sollte das keinen Optimierungsunterschied bewirken.
Eher deklariert man dafür die variable volatile.
Alex G. schrieb:> Hast du variable wirklich global deklariert, wegen "File level"?> Eigentlich sollte das keinen Optimierungsunterschied bewirken.> Eher deklariert man dafür die variable volatile.
Ja, in meinem Fall war es eine etwas größere Tabelle, die per static
lokal in der Funktion definiert war. Static const ging leider nicht, da
sich einige Werte zur Laufzeit ändern konnten.
Der GCC hat von der Tabelle eine Kopie ins Flash gelinkt und im BSS
Segment entsprechend Speicher alloziert. Beim ersten Aufruf hat er dann
per memcpy aus dem Flash in das BSS kopiert. Mit entsprechendem Mutex
versteht sich.
Ich habe lediglich die Deklaration der Tabelle aus der Funktion heraus
geschoben und ganz gewöhnlich als static im File deklariert. Dann
verschwand die GCC Magie restlos aus der Funktion. Die Initialisierung
erfolge nun zeitlich vor main().
Nils N. schrieb:> Eine static Variable solltest du immer initialisieren!
Kann man aus Stilgründen machen, um zu zeigen, daß man dran gedacht hat.
Nötig ist das aber nicht, weil static-Variablen genau wie alle anderen
globals mit 0 initialisiert werden, wenn man nichts ausdrückliches
hinschreibt.
UnderDog schrieb:> Was hat es denn für einen Vorteil wenn die Variable innerhalb> einer Funktion nicht "static" ist? Performance?
"Normalerweise" macht man das nicht static, außer man hat gute Gründe
dafür. Nämlich den, daß man möchte, daß die Funktion sich eine Variable
über mehrere Aufrufe hinweg merken soll. Das ist zugleich aber auch eine
Falle, weil das Verhalten der Funktion dann nämlich davon abhängt, was
sie bei vorherigen Aufrufen getan hat.
Ein typisches Beispiel für static ist, daß man in einer Funktion
mitzählen will, wie oft sie aufgerufen wurde.
Nils P. schrieb:> Bei C++ wird die lokale statische Variable nicht beim Programmstart> sondern bei der ersten Benutzung initialisiert. Dafür baut der Compiler> im Zweifelsfall extra Code ein, der erkennt ob man im ersten Aufruf> steckt oder nicht.
Ist das nur beim GCC so oder bei allen Compilern? Normalerweise heißt es
bei C++ ja immer, daß man nichts bezahlt, was man nicht auch haben will?
Nop schrieb:> Ist das nur beim GCC so oder bei allen Compilern?
Wahrscheinlich bei allen.
Nop schrieb:> Normalerweise heißt es> bei C++ ja immer, daß man nichts bezahlt, was man nicht auch haben will?
Korrekt, wenn du "static" in Funktionen nicht nutzt, wird da kein Code
für generiert. Wenn du es aber nutzt, ließe es sich wohl kaum anders
umsetzen, denn der C++ Standard schreibt das Verhalten ja so vor.
Nop schrieb:> Nötig ist das aber nicht, weil static-Variablen genau wie alle anderen> globals mit 0 initialisiert werden, wenn man nichts ausdrückliches> hinschreibt
Nötig ist es dann, wenn der Startwert ungleich 0 sein soll. Also lieber
gleich angewöhnen und static immer initalisieren.
Nils P. schrieb:> Static const ging leider nicht, da> sich einige Werte zur Laufzeit ändern konnten.
Hm, dann ist doch wieder das Thema der Thread-Sicherheit da... Kann es
sein, dass der gcc das daran erkannt hat?
Dr. Sommer schrieb:> Korrekt, wenn du "static" in Funktionen nicht nutzt, wird da kein Code> für generiert. Wenn du es aber nutzt, ließe es sich wohl kaum anders> umsetzen, denn der C++ Standard schreibt das Verhalten ja so vor.
Und das wundert mich, denn bei C geht das auch ohne extra Code.
> bei C geht das auch ohne extra Code.
Nicht ganz richtig. Das compilierte Programm enthält einige
Initialisierungs-Routinen die vor der main() Funktion ausgeführt werden.
Dazu gehört unter anderem:
1) Kopieren aller Variablen mit Startwert (ungleich 0) vom Flash ins
RAM.
2) Initialisierung aller anderen globalen und statischen Variablen mit
0.
3) Einstellen des Stack-Pointers.
Nop schrieb:> Und das wundert mich, denn bei C geht das auch ohne extra Code.
C hat ja auch keine Konstruktoren, die Seiteneffekte haben könnten. Ob
die Variablen also am Anfang oder später initialisiert werden spielt da
keine Rolle.
Stefan U. schrieb:> Nicht ganz richtig. Das compilierte Programm enthält einige> Initialisierungs-Routinen die vor der main() Funktion ausgeführt werden.
Ja klar, gemeint war natürlich der Laufzeit-Overhead.
Dr. Sommer schrieb:> C hat ja auch keine Konstruktoren, die Seiteneffekte haben könnten.
Auch wieder wahr. Also hätte man das Problem nicht, wenn die fragliche
Funktion in C++ eine standalone-Funktion anstatt einer Methode wäre?
Obwohl.. doch, ja die Konstruktoren könnten ja auch so eine FUnktion
aufrufen?
Nop schrieb:> Auch wieder wahr. Also hätte man das Problem nicht, wenn die fragliche> Funktion in C++ eine standalone-Funktion anstatt einer Methode wäre?
Versteh nicht ganz was du meinst... Das Programm im Anhang gibt
1
1
2
X::X()
3
2
4
3
5
4
6
X::X()
7
5
8
6
aus; damit das klappt, müssen die beiden test()-Funktionen jeweils
prüfen, ob die "static"-Variable bereits initialisiert wurde. Das wird
per Mutex abgesichert (Aufrufe zu __cxa_guard_acquire & Co).
UnderDog schrieb:> Was hat es denn für einen Vorteil wenn die Variable innerhalb einer> Funktion nicht "static" ist? Performance?
In der Regel geht es hier weder um Speicher noch um Performance, sondern
einfach um einen sauberen Programmierstil, der hilft Bugs zu vermeiden.
Wenn möglich, sollte eine Funktion bei gleichen Parametern immer den
gleichen Rückgabewert liefern und auch sonst keine Seiteneffekte haben.
Wenn innerhalb einer Funktion eine Variable static ist, dann Teilen sich
alle Aufrufe dieser Funktion diese Variable. Das kann zu schwer
erkennbaren Bugs führen.
Peter S. schrieb:> sondern> einfach um einen sauberen Programmierstil,
Funktionen mit statischen Variablen, im Bauch, sind erstmal nicht
Wiedereintrittsfähig.
Es drohen unerwünschte Seiteneffekte.
Im Grunde sind solche Funktionen "Wegwerf Funktionen".
Nur für dieses eine Projekt tauglich.
Eher nicht wiederverwendbar.
--- wenn man damit leben kann --- dann men zu ----
Aber man muss sich dessen immer bewusst sein!
Ansonsten gilt der Leitsatz:
Nur statische Variablen, auf welche man verzichtet, sind gute statische
Variablen.
>Was hat es denn für einen Vorteil wenn die Variable innerhalb einer>Funktion nicht "static" ist? Performance?
Eine statische Variable ist global, d.h. sie ist dauerhaft im RAM und
für diese muss schon vor Beginn des Programms Speicher reserviert
werden.
Eine nicht statische Variable existiert im RAM nur temporär, d.h. sobald
du die Funktion verlässt belegt die nicht statische Variable keinen
Speicher mehr im RAM. Dafür ist der Stack da, der dynamisch wächst und
schrumpft im RAM.
Arduino F. schrieb:> Ansonsten gilt der Leitsatz:> Nur statische Variablen, auf welche man verzichtet, sind gute statische> Variablen.
Nicht alle statischen Variablen werden mit 'static' eingeleitet. Ich
vermute, dass auch deine Projekte voller statischer Variablen sind, du
es nur nicht erkennst. ;)
Stefan U. schrieb:> 1) Kopieren aller Variablen mit Startwert (ungleich 0) vom Flash ins> RAM.> 2) Initialisierung aller anderen globalen und statischen Variablen mit> 0.
Variablen kopieren? Variablen sind nur reservierte (zugeordnete)
Speicherbereiche. Sie werden initialisiert, d. h. der Speicherbereich
mit den definierten Werten beschrieben. Und dabei ist es egal, ob mit 0
oder einem anderen Wert.
Man kann den Linker anweisen, Speicherbereiche von der Initialisierung
auszuschließen. Das ist wichtig und nötig, um z. B. bei einem Reboot
(Warmstart) Daten vor dem Programmabbruch zu bewerten.
In den Startup Files findet man die ganzen Abläufe.
mapfile schrieb:> Sie werden initialisiert, d. h. der Speicherbereich> mit den definierten Werten beschrieben.
Und das erfolgt eben, indem die Vorbelegung aus dem .text Segment
(Flash) in das .data Segment (RAM) kopiert wird.
mapfile schrieb:> Und dabei ist es egal, ob mit 0> oder einem anderen Wert.
0-Initialisierte Variablen landen im .bss Segment und werden mit einer
dedizierten Schleife im Startup-Code initialisiert. So vermeidet man
große 0-Blöcke im Flash-Image. Das ganze ist natürlich aus C-Sicht
unsichtbar und wird durch die Laufzeitumgebung gemacht, von der der
Startup-Code ein Teil ist.
Arduino F. schrieb:> Funktionen mit statischen Variablen, im Bauch, sind erstmal nicht> Wiedereintrittsfähig.> Es drohen unerwünschte Seiteneffekte.>> Im Grunde sind solche Funktionen "Wegwerf Funktionen".> Nur für dieses eine Projekt tauglich.> Eher nicht wiederverwendbar.
Nicht generell.
Z.B. eine Entprellfunktion muß sich den alten Portzustand und den
Entprellzähler merken. Die einzige Hardwareabhängigkeit ist das
Porteinlesen, das man leicht als Macro oder Unterfunktion definieren
kann. Daher ist eine solche Funktion mit statischen Variablen universell
und leicht wiederverwendbar.
mapfile schrieb:> Und dabei ist es egal, ob mit 0 oder einem anderen Wert.
Nein, bei ungleich 0 wird kopiert, wie Stefan schrieb.
mapfile schrieb:> In den Startup Files findet man die ganzen Abläufe.
Dann schaue es Dir auch einmal an.
Achim S. schrieb:> Dann schaue es Dir auch einmal an.
Mache ich dauernd, ich schreibe sie ja auch. ;)
Und lies mal richtig: Es werden keine Varibalen kopiert, sondern deren
reservierten Speicherbereiche initialisiert.
Wie mich dieses Gelaber aufregt, dass man dieses und jenes nicht tun
darf, wegen gutem Programmierstil (gibts da ein Gsetzt zu?). Dass man
nie eine static nehmen soll, weil es kein guter Stil und angeblich diese
Funktionen nicht wiederverwendbar sind.
Leute, selbst ein goto kann man noch an manchen Stellen sinnvoll
benutzen. Genauso eine statische Variable - sonst könnte beim nächsten
C-Standard das beide gerne rausfliegen. Es geht hier nicht um Stil,
sondern um Elemente der Programmiersprache. Nutze es mit Bedacht, aber
nutze es - Fehler kann man wirklich überall einbauen.
mapfile schrieb:> Nicht alle statischen Variablen werden mit 'static' eingeleitet. Ich> vermute, dass auch deine Projekte voller statischer Variablen sind, du> es nur nicht erkennst. ;)
Sicher....
Du kennst meine Programme, und darfst mich für blöd erklären.
Fein, weiter so....
Wenn es dir dabei besser geht...
---------------------
Peter D. schrieb:> Nicht generell.> Z.B. eine Entprellfunktion muß sich den alten Portzustand und den> Entprellzähler merken. Die einzige Hardwareabhängigkeit ist das> Porteinlesen, das man leicht als Macro oder Unterfunktion definieren> kann. Daher ist eine solche Funktion mit statischen Variablen universell> und leicht wiederverwendbar.
Das macht sie aber nicht Wiedereintrittsfähig.
Einigkeit haben wir mit Sicherheit, darin, dass eine Entprellroutine
einen Status halten muss. Das kann sie intern tun, statisch, oder
extern, in dem man ihr einen Zeiger auf eine Struktur übergibt.
Der zweite Weg macht sie deutlich universeller.
Wesentlich wiedereintrittsfähiger, solange sich die Pointer auf den
Status unterscheiden.
Ansonsten, sehe ich nicht, über welche Entprellroutine du spricht....
Meine EntprellDinger haben keine statischen Variablen im Bauch.
Und irgendwelche Nachteile, kann ich da so nicht erkennen.
Und nein, ich finde, dass statische Variablen auch durch Macros nicht
schöner werden.
---------------
Nils N. schrieb:> (gibts da ein Gsetzt zu?)
Nein, nur einen Willen!
Funktionen mit statischen Variablen ändern ihr Verhalten in Abhängigkeit
von der Variablen.
Das ist dann halt die Frage...
Will man das?
Wenn ja, dann gut..
Wenn nein, dann ist man ein Idiot, wenn man da eine Statische Variable
einbaut.
Der Wille!
Arduino F. schrieb:> Sicher....> Du kennst meine Programme
Nö, daher schrieb ich
mapfile schrieb:> Ich> vermute, dass auch deine Projekte
Lesen, verstehen und erst dann antworten.
Das hält Puls und Blutdruck in Grenzen. ;)
Und benutzt du jetzt statische Variablen? Gerne helfe ich beim Suchen.
Wo gibt es Programme von dir?
UnderDog schrieb:> Was hat es denn für einen Vorteil wenn die Variable innerhalb einer> Funktion nicht "static" ist? Performance?
Begreife doch mal das Prinzip des Ganzen.
Also:
Normale Variablen, die du innerhalb einer Funktion deklarierst, werden
auf dem Stack angeordnet. Deshalb sind sie nach Verlassen der Funktion
quasi weg, nicht mehr zugreifbar (bzw. werden von anderem Zeugs
überschrieben).
Wenn man aber für eine Funktion eine persistente (also statische)
Variable haben will, dann muß man so eine Variable eben außerhalb
jeglicher Funktionen deklarieren. Dann landet diese Variable eben im
ganz normalen RAM und nicht auf dem Stack.
Nun kennt C blöderweise eben keine echten Units, die ihren Inhalt
kapseln und nur das nach außen lassen, was sie nach außen lassen wollen.
Deshalb sind im Prinzip alle statischen Variablen in C automatisch
globale Variablen, auf die auch von anderen Programmteilen zugegriffen
werden kann - auch dann, wenn sie nicht in der zugehörigen Headerdatei
auftauchen. Sie müssen woanders bloß mit "extern" benannt werden und -
voila - der Zugriff funktioniert. Läßt man das "extern" weg, dann findet
der Linker zwei namensgleiche Variablen im Objektcode und meckert, wenn
man ihn läßt.
Dieser Mangel an einem ordentlichen Unitsystem in C hat viele Leute
geärgert - und wie immer bei C wird der Mangel nicht abgestellt, sondern
man begegnet ihm mit einem Workaround.
Natürlich kann man in jedem Programm sich diszipliniert benehmen und
doppelte Variablennamen eben vermeiden sowie den Zugriff auf alles, was
nicht in den Headerdateien steht, sich verkneifen.
Aber C-Programmierer hassen Disziplin und so besteht der Workaround
darin, daß man mit "static" innerhalb einer Funktion eine globale
Variable erzeugt, die beim Übersetzen auf irgend eine
compilerspezifische Weise so in ihrem Namen verändert wird, daß sie mit
keiner anderen namensgleichen Variablen kollidiert. Damit ist das eine
globale Variable, die quasi nur von der Funktion angesprochen werden
kann, wo sie deklariert wurde.
Nebenbei bemerkt, finde ich diese Namensgebung miserabel, man hätte das
Ganze besser "private" nennen sollen. Ist was Ähnliches wie "typedef",
was man besser "rename" hätte nennen sollen.
W.S.
W.S. schrieb:> Deshalb sind im Prinzip alle statischen Variablen in C automatisch> globale Variablen, auf die auch von anderen Programmteilen zugegriffen> werden kann - auch dann, wenn sie nicht in der zugehörigen Headerdatei> auftauchen.
Nein, eben nicht. Markiert man eine globale Variable (außerhalb von
Funktionen) als static, erhält sie kein Linker Symbol, sondern ist für
andere Source Dateien unsichtbar. Das ist die C-variante von "Kapselung
ohne OOP".
W.S. schrieb:> Dieser Mangel an einem ordentlichen Unitsystem in C hat viele Leute> geärgert - und wie immer bei C wird der Mangel nicht abgestellt,
Wenn man in C alles so wie in Pascal machen würde, wäre es Pascal.
Arduino F. schrieb:> Einigkeit haben wir mit Sicherheit, darin, dass eine Entprellroutine> einen Status halten muss. Das kann sie intern tun, statisch, oder> extern, in dem man ihr einen Zeiger auf eine Struktur übergibt.
Static sagt dem Compiler und dem Leser, die Variable hat zwar globale
Lebensdauer, wird aber von keiner anderen Task benutzt. Dadurch läßt
sich der Code einfacher verstehen. Eine globale Struct würde hier nur
unnötig die Komplexität erhöhen.
Arduino F. schrieb:> Dann, wenn die Faulheit es einfordert.
Glaube ich nicht. Du wirst statische Variablen in allen deinen
Programmen verwenden.
Nochmal als Tipp für dich: statische Variablen müssen nicht zwingend mit
'static' eingeleitet werden. ;)
Es gibt nicht nur local statics. ;)
Peter D. schrieb:> Dadurch läßt> sich der Code einfacher verstehen. Eine globale Struct würde hier nur> unnötig die Komplexität erhöhen.
Wie machst du das denn, wenn man mehrere Ports entprellen möchte? Man
braucht ja einen Satz Variablen pro Port, aber in "static"-Variablen in
einer Funktion hat man ja nur genau einen. Da wäre es irgendwie
intuitiver, wenn man einen "struct" pro Port hat und z.B. als globale
"static" Variable anlegt, und den eben an die Entprell-Routinen
übergibt.
mapfile schrieb:> Nochmal als Tipp für dich: statische Variablen müssen nicht zwingend mit> 'static' eingeleitet werden. ;)> Es gibt nicht nur local statics. ;)
Jetzt klär uns mal auf, was das sein soll...
So etwas:
1
intfoo;
2
intmain(){}
ist ja keine statische, sondern nur eine globale Variable.
> Wie mich dieses Gelaber aufregt, dass man dieses und jenes nicht> tun darf, wegen gutem Programmierstil
Jetzt weißt du, warum so mancher gut gebildeter Programmiere mehrere
Anläufe braucht, um ein Team zu finden, mit dem zusammen arbeiten kann.
Ein anderer Knackpunkt den ich häufig erlebe ist, sich mit den
Arbeitsmitteln und Libraries/Frameworks zufrieden zu geben, die
vorgegeben wurden. Oft habe ich gehört "mit diesem alten Kram will ich
nicht arbeiten". Es hilft einer Firma jedoch wenig, wenn der eine
Missionar auf einen Schlag zig neue Sachen einbaut, mit denen die
anderen dann nicht zurecht kommen.
Fortschritt ist gut, wenn er an das Lerntempo des gesamten Teams
angepasst ist.
> Da wäre es irgendwie intuitiver, wenn man einen "struct" pro> Port hat und z.B. als globale "static" Variable anlegt, und> den eben an die Entprell-Routinen übergibt.
Womit du schon einen wesentlichen Schritt in Richtung objektorientierter
Programmierung getan hast - ganz ohne C++.
Stefan U. schrieb:>> Da wäre es irgendwie intuitiver, wenn man einen "struct" pro>> Port hat und z.B. als globale "static" Variable anlegt, und>> den eben an die Entprell-Routinen übergibt.>> Womit du schon einen wesentlichen Schritt in Richtung objektorientierter> Programmierung getan hast - ganz ohne C++.
In C++ gehts aber trotzdem besser, da könnte man das struct als template
ausführen und somit an die Portbreite (8,16,32 Pins) anpassen, wodurch
der Code portabel würde! Das Wörtchen "private" hilft da sowieso.
Dr. Sommer schrieb:> Wie machst du das denn, wenn man mehrere Ports entprellen möchte? Man> braucht ja einen Satz Variablen pro Port, aber in "static"-Variablen in> einer Funktion hat man ja nur genau einen.
Static kann auch ein Byte-Array sein. Oder man nimmt uint64_t für bis zu
64 Tasten.
Dr. Sommer schrieb:> ist ja keine statische, sondern nur eine globale Variable.
Sie ist statisch und global, das schließt sich nicht aus. Es ist besser
lokale statische Variablen zu verwenden, als globale. Bei globalen
Variablen besteht die Gefahr des Zugriffs mit Seiteneffekten. Lokale
statische Variablen schränken die Sichtbarkeit - und damit Fehler - ein.
D. h. nicht, dass lokale statische Variablen möglichst oft einzuzsetzten
sind, aber allemal sind sie besser als globale Variablen.
Ergo ist der folgende Satz Blödsinn
Arduino F. schrieb:> Nur statische Variablen, auf welche man verzichtet, sind gute statische> Variablen.
Unser Bastelfreund hat hier einfach nur falsch zitiert, denn es heisst:
Nur globale Variablen, auf die man verzichtet, sind gute globale
Variablen. ;)
Und ja, es gibt immer Situationen, in denen eine globale Variable, ein
goto, usw. sinnvoll sind. Das sind aber sehr, sehr, sehr wenige.
Peter D. schrieb:> Static kann auch ein Byte-Array sein. Oder man nimmt uint64_t für bis zu> 64 Tasten.
Und woher weiß die Funktion, wie viele Ports eine konkrete Anwendung
jetzt tatsächlich benutzt? Per #define festlegen? Ziemlich unschön...
mapfile schrieb:> Sie ist statisch und global,
Wenn
1
intfoo;intmain(){}
statisch und global ist, was ist dann
1
staticintfoo;intmain(){}
Auch statisch und global? Aber das ist ja nicht das gleiche.
mapfile schrieb:> Und ja, es gibt immer Situationen, in denen eine globale Variable, ein> goto, usw. sinnvoll sind. Das sind aber sehr, sehr, sehr wenige.
Wenn man auf eingebetteten Systemen C++- Klassen, aber keine dynamische
Speicherverwaltung nutzen möchte, ist es sinnvoll "große" Objekte als
(ggf. statische) globale Variable anzulegen, u.a. eben um den genannten
Overhead zu sparen. Gleiches gilt sowieso für große Puffer für DMA usw.
Globale (statische) Konstanten sind auch sauberer als #define's.
goto kann man z.B. in C sinnvoll für Fehlerbehandlung nutzen, weil es da
keine Exceptions gibt. Fehler zu behandeln ist kein besonders seltenes
Anliegen...
Dr. Sommer schrieb:> Objekte als> (ggf. statische) globale Variable anzulegen
Das Objekt selber kapselt aber die Variablen, die in einzelnen Methoden
genutzt werden. In C hast du keine Kapsel.
Dein Apfel/Kartoffel Vergleich läßt mich an dir zweifeln. :(
Dr. Sommer schrieb:> Wennint foo; int main () {}statisch und global ist, was ist dannstatic> int foo; int main () {}Auch statisch und global? Aber das ist ja nicht> das gleiche.
Das ist statisch, global und auf das Modul eingeschränkte Sichtbarkeit.
Entscheidend ist doch die "Haltbarkeit" der Variablen, die sie zu
statischen macht. Nach der Initialisierung bleiben sie bis zum
Programmende erhalten, quasi statisch. ;)
mapfile schrieb:> Das Objekt selber kapselt aber die Variablen, die in einzelnen Methoden> genutzt werden. In C hast du keine Kapsel.
In C kannst du die Variablen auch in ein struct packen, und dann
Funktionen für den Zugriff verwenden; genau wie man das für alle anderen
"Objekte" in C auch macht. Wenn man sich schon für C und gegen Kapselung
und C++ entscheidet, ist das bei globalen Variablen auch kein
Unterschied.
mapfile schrieb:> Das ist statisch, global und auf das Modul eingeschränkte Sichtbarkeit.
Ah. Nur globale aber nicht statische Variablen gibts also nicht?
Wie heißen eigentlich statische nicht-sichtbarkeitseingeschränkte
gloable Variablen auf Englisch, damit man das mal Googlen kann? "static
not-externally visible variables which are not static"? Das "static" im
Namen würde irgendwie das "static" im Quellcode implizieren.
mapfile schrieb:> Entscheidend ist doch die "Haltbarkeit" der Variablen, die sie zu> statischen macht. Nach der Initialisierung bleiben sie bis zum> Programmende erhalten, quasi statisch. ;)
Das ist bei allen globalen Variablen gleich.
Dr. Sommer schrieb:> In C kannst du die Variablen auch in ein struct packen, und dann> Funktionen für den Zugriff verwenden;
Und wenn die global sind, kann ich dann nur über die Funktionen oder
auch direkt auf die Struktur (und darin enthaltenen Daten) zugreifen?
Eine Struktur hat doch nichts mit Datenkapselung zu tun. Global
angelegt, kann jeder (bei static jeder im Modul) darauf zugreifen.
Dr. Sommer schrieb:> Nur globale aber nicht statische Variablen gibts also nicht?
Jepp, eine globale Variable ist immer statisch.
Dr. Sommer schrieb:> Wie heißen eigentlich statische nicht-sichtbarkeitseingeschränkte> gloable Variablen auf Englisch, damit man das mal Googlen kann?https://en.wikipedia.org/wiki/Global_variable
Und danke für den Hinweis, denn im Text steht das, genau so:
"... In compiled languages, global variables are generally static
variables, whose extent (lifetime) is the entire runtime of the program
..."
Dr. Sommer schrieb:> Das ist bei allen globalen Variablen gleich.
Jepp, die sind ja auch immer statisch. ;)
@Sommer
Bevor du jetzt weitere Veränkungen anstellts und von C++ nach Python
oder sonst was ausweichst: Im Titel steht "C". Und darüber sollten wir
in diesem Thread auch schreiben.
mapfile schrieb:> Eine Struktur hat doch nichts mit Datenkapselung zu tun. Global> angelegt, kann jeder (bei static jeder im Modul) darauf zugreifen.
Ach, womit denn sonst?
Also ich kenn das in C z.B. so:
1
structX{
2
inta,b,c;
3
};
4
voiddoSomething(structX*x){
5
x->a+=a->b+a->c;
6
}
7
intmain(){
8
structXx{1,2,3};
9
doSomething(&x);
10
}
Das ist eine ganz typische Verwendung von strukturierten Daten,
angenähert objektorientiert. C-Programmierer erkennen hier normalerweise
nichts schlechtes dran, obwohl man die "geheimen" Member a, b, c von
außen modifizieren kann. Wenn ich die "x" variable jetzt einfach global
mache, ists plötzlich schlecht?
1
structXx{1,2,3};
2
intmain(){
3
doSomething(&x);
4
}
mapfile schrieb:> Jepp, eine globale Variable ist immer statisch.> Und danke für den Hinweis, denn im Text steht das, genau so:> "... In compiled languages, global variables are generally static
Das ist eine eher theoretische Betrachtung. Ich kannte das immer so,
dass man in C mit "static" Variablen immer eine meint, an der auch
"static" dran steht, weil das sonst ziemlich verdreht wäre. Das sehen
auch andere so:
http://codingstreet.com/c-basic-questions/what-is-difference-between-global-and-static-variables-in-c/https://stackoverflow.com/a/13162135
Wenn man es ganz genau haben will, sollte man sagen dass alle globalen
Variablen "static storage duration" haben (§6.2.4 im Standard), und die
mit "static" keyword haben "internal linkage" (6.2.2). Aber einfach alle
globalen Variablen als "static/statisch" zu bezeichnen finde ich
uneindeutig/verwirrend.
mapfile schrieb:> Bevor du jetzt weitere Veränkungen anstellts und von C++ nach Python> oder sonst was ausweichst:
Das hab ich doch gar nicht gemacht.
Dr. Sommer schrieb:> Aber einfach alle> globalen Variablen als "static/statisch" zu bezeichnen finde ich> uneindeutig/verwirrend.
Der Kernpunkt dabei ist, dass die Eingenschaft statisch nicht zwingend
durch das Schlüsselwort 'static' eingeleitet werden muss. Und dass das
Schlüsselwort 'static' auch für die Einschränkung der Sichbarkeit
genutzt wird. Wenn du das verwirrend findest, wie erklärst du dir dann
das Schlüsselwort 'static' bei Funktionen ("C"-Funktionen). Sind alle
sonstigen C-Funktionen dynamisch? ;)))
Dr. Sommer schrieb:> Das ist eine ganz typische Verwendung von strukturierten Daten,> angenähert objektorientiert. C-Programmierer erkennen hier normalerweise> nichts schlechtes dran, obwohl man die "geheimen" Member a, b, c von> außen modifizieren kann.
Wo bitte schön ist da etwas mit OOP? Du hast eine lokale Variable, die
an eine Funktion übergeben wird. Du kannst 1, 2, 3 auch als einzelne
Parameter übergeben. Du hast die Einzelwerte in der Struktur zusammen
gefasst, aber keine Schale (Kapsel) drumherum. Und natürlich kann man
nicht ausserhalb der main() auf x.a zugreifen. (???) :(((
Zeig doch bitte den Zugriff anhand deines Beispiels. ;)))
Dr. Sommer schrieb:> Wenn ich die "x" variable jetzt einfach global> mache, ists plötzlich schlecht?
Ja, ist unglücklich, da (erst) jetzt jeder zugreifen kann.
1
...
2
voidfoo(void);
3
4
structXx{1,2,3};
5
intmain(){
6
doSomething(&x);
7
...
8
foo();
9
}
10
11
voidfoo(void){
12
x.a=0;
13
}
Dr. Sommer schrieb:> Das hab ich doch gar nicht gemacht.Dr. Sommer schrieb:> Wenn man auf eingebetteten Systemen C++- Klassen ...
Selbstsprechend, oder? ;)
mapfile schrieb:> Der Kernpunkt dabei ist, dass die Eingenschaft statisch nicht zwingend> durch das Schlüsselwort 'static' eingeleitet werden muss.
Sag doch bitte, die Eigenschaft "static storage duration". Alles andere
verwirrt. Dann wäre die ganze Diskussion gar nicht erst entstanden.
mapfile schrieb:> Sind alle> sonstigen C-Funktionen dynamisch? ;)))
Globale nicht-"static" Variablen sind auch nicht "dynamisch". Ich kannte
das einfach nur so, ist so auch alles intuitiv klar:
* Globale Funktionen & Variablen mit "static" nennt man auch einfach
"static" (Deutsch: "statisch")
* Globale Funktionen ohne static sind einfach "normal", ohne weiteren
Zusatz (genau genommen haben sie "external linkage").
* Globale Variablen ohne static sind einfach nur "global" (dito zu
"external linkage").
* Alle globalen Variablen existieren für die gesamte Programmdauer
(haben "static storage duration").
mapfile schrieb:> Wo bitte schön ist da etwas mit OOP?
Zusammenhängende Daten werden in einem "struct" zusammengefasst. Eine
Instanz davon ist ein "Objekt". Zu diesem Objekt gehören Funktionen
(hier ungeschickterweise "doSomething" genannt; besser wäre
"X_doSomething" o.ä.), die die Daten manipulieren. So wird es bspw. in
den großen Bibliotheken GLib und Gtk+ gemacht. Eine andere Art, in C
objektorientiert zu programmieren, ist mir nicht bekannt, außer
vielleicht dem "pImpl" Pattern, aber das funktioniert im Endeffekt
genauso.
mapfile schrieb:> Du kannst 1, 2, 3 auch als einzelne> Parameter übergeben.
Dann gehören sie aber konzeptuell nicht mehr zusammen.
mapfile schrieb:> Du hast die Einzelwerte in der Struktur zusammen> gefasst, aber keine Schale (Kapsel) drumherum.
Die gibt's in C auch nicht. Aber wir haben ja angenommen, dass man,
warum auch immer, auf C besteht.
mapfile schrieb:> Und natürlich kann man> nicht ausserhalb der main() auf x.a zugreifen.
Aber innerhalb der main(), außerhalb der zu X gehörigen Funktion
"X_doSomething". Das ist genauso falsch, lässt sich aber in C nunmal
nicht verhindern, was aber in Kauf genommen wird. Bei globalen Variablen
geht diese Verletzung der Kapselung halt nur einem größeren Bereich. Das
kann man genauso in Kauf nehmen.
mapfile schrieb:> Dr. Sommer schrieb:>> Wenn man auf eingebetteten Systemen C++- Klassen ...>> Selbstsprechend, oder? ;)
Das war eine komplett andere Diskussion.
Dr. Sommer schrieb:> Zusammenhängende Daten werden in einem "struct" zusammengefasst. Eine> Instanz davon ist ein "Objekt". Zu diesem Objekt gehören FunktionenDr. Sommer schrieb:>> Du hast die Einzelwerte in der Struktur zusammen>> gefasst, aber keine Schale (Kapsel) drumherum.> Die gibt's in C auch nicht.
Erkennst du den Widerspruch?
In C kannst du keine Objekte erzeugen! Du bekommst die Daten und
Funktionen (wären dann Mehtoden) nicht gekapselt.
Dr. Sommer schrieb:> Aber innerhalb der main(), außerhalb der zu X gehörigen Funktion> "X_doSomething". Das ist genauso falsch, lässt sich aber in C nunmal> nicht verhindern
???
Wenn du doSomething() als Objektinstanz siehst (für mich etwas sehr
weit hergeholt), solltest du x als static in doSomething() definieren.
Dr. Sommer schrieb:> Bei globalen Variablen> geht diese Verletzung der Kapselung halt nur einem größeren Bereich.
Das ist dann aber keine Kapselung mehr, oder?
@Sommer
Noch einmal zum Ursprung: Ein Fanboy haut die Parole raus, dass in "C"
lokale 'static' definierte Variablen der totale Humbug sind.
Und jetzt erklärst du mir, dass (Modul- oder auch nicht) globale
Variablen die bessere Lösung dafür sind?
(Mit OOP und Kapselung lassen wir noch aussen vor.) ;)
mapfile schrieb:> Ein Fanboy haut die Parole raus, dass in "C"> lokale 'static' definierte Variablen der totale Humbug sind.
Entweder lügst du bewusst, oder du verstehst einfach nur meine Texte
nicht.
Arduino F. schrieb:>> Ein Fanboy haut die Parole raus, dass in "C">> lokale 'static' definierte Variablen der totale Humbug sind.> Entweder lügst du bewusst, oder du verstehst einfach nur meine Texte> nicht.Arduino F. schrieb:> Ansonsten gilt der Leitsatz:> Nur statische Variablen, auf welche man verzichtet, sind gute statische> Variablen.
Hoffentlich verstehst du deine eigenen Texte noch. ;)
mapfile schrieb:> Erkennst du den Widerspruch?
Nö. Bevor wir das weiter auseinandernehmen, wie machst du denn OOP in C?
Ich kenn das so wie z.B. hier:
https://developer.gnome.org/glib/stable/glib-Strings.html#GString
Man hat ein "struct", hier "GString" wo zusammengehörige Daten
zusammengepackt werden. Auf diese greift man über eine Reihe von
Funktionen namens "g_string_xx" zu. Man kann auch direkt auf die Member
von Außen zugreifen, aber so könnte man die Daten in einen
inkonsistenten Zustand bringen. So ist das nunmal in C.
Ein solches struct könnte man so verwenden:
1
intmain(){
2
GString*str=g_string_new(NULL);
3
str->len=7;// verboten!
4
g_string_append(str,"foo");
5
g_string_free(str);
6
}
Diese Nutzung scheint von C-Programmierern akzeptiert zu werden, auch
wenn der falsche Zugriff vom Compiler nicht verhindert wird. Das ist
m.W. das übliche Schema von OOP in C, es wird in der Doku sogar von
"Object" geredet.
Wenn man jetzt die "str" Variable global macht:
1
GString*str;
2
3
intmain(){
4
str=g_string_new(NULL);
5
test();
6
g_string_append(str,"foo");
7
g_string_free(str);
8
}
9
10
voidtest(){
11
str->len=7;// verboten!
12
}
kann man halt in mehreren Funktionen "verbotene" Zugriffe durchführen.
Das ist m.M.n. jetzt auch nicht viel schlimmer. Das erkauft man eben
durch das Bestehen auf C.
mapfile schrieb:> Wenn du doSomething() als Objektinstanz siehst (für mich etwas sehr> weit hergeholt), solltest du x als static in doSomething() definieren.
Nein, als (Nachbildung einer) "Member"-Funktion. das "x" ist die Instanz
(Objekt). Es wird halt explizit als Parameter übergeben, statt implizit
als "this" wie in C++.
Eine Funktion mit lokaler "static" Variable könnte man als Instanz
ansehen, oder eine .c Datei mit diversen (ggf. "static") globalen
Variablen als ein Singleton. Finde ich beides eher unschön.
mapfile schrieb:> Das ist dann aber keine Kapselung mehr, oder?
Es ist Kapselung soweit wie C es eben ermöglicht.
mapfile schrieb:> Und jetzt erklärst du mir, dass (Modul- oder auch nicht) globale> Variablen die bessere Lösung dafür sind?
In C++ u.U. schon, aber nicht generell... Ich finde Variablen mit
"static storage duration" (also alle globalen und solche mit "static",
quasi Singletons) generell etwas unsauber, sofern sie nicht konstant
sind. Funktionslokale statische Variablen sind hinterhältigerweise auch
noch versteckt.
Bei eingebetteten Systemen ist es (mangels dynamischer
Speicherverwaltung - aber das ist eine andere Diskussion) aber oft
erforderlich, Dingen eine "static storage duration" zu verpassen. Ich
finde es persönlich am saubersten, alle solche Variablen, die in einer
Anwendung gebraucht werden, an eine Stelle (typischerweise die
main.c(pp)) zu packen, und dann darauf nur Member-Funktionen (C++) bzw.
die o.g. "Pseudo-Member-Funktionen" (C) aufzurufen. Das erfordert
natürlich, dass alles sauber in (verschachtelten) structs bzw. Klassen
verpackt ist - so braucht man nur eine Hand voll solcher globaler
Variablen, anstatt eines Riesenwusts an Integern o.ä.
Das sehe ich aber mehr als Idealvorstellung, nicht als zwingende Regel.
Die anderen Varianten (globale oder funktionslokale "static" Variablen)
würde ich nicht prinzipiell verbieten.
Dr. Sommer schrieb:> Nein, eben nicht. Markiert man eine globale Variable
kannst du eigentlich lesen? Genau dieses Detail habe ich dem TO
ausführlich erklärt.
W.S.
Dr. Sommer schrieb:> wie machst du denn OOP in C?
Gar nicht, weil es nicht geht. ;)
Aber natürlich strukturiere ich Programme, teile sie in Module auf und
verbinde die Module per Funktionsaufrufe (und nicht über globale
Variablen). Aber das ist noch lange keine OOP!
Ich kann über diesen Weg in C keine Instanzen von Objekten anlegen.
Wie du jetzt selber erkannt hast, gibt es keine Kapselung:
Dr. Sommer schrieb:> auch> wenn der falsche Zugriff vom Compiler nicht verhindert wirdDr. Sommer schrieb:> kann man halt in mehreren Funktionen "verbotene" Zugriffe durchführen
Mit dem folgenden Beitrag schlage ich dich zum Aussenminister oder
sonstigem "wasch mich, aber mach mich nicht nass" Oberdiplomaten vor.
;)))
Dr. Sommer schrieb:>> Das ist dann aber keine Kapselung mehr, oder?> Es ist Kapselung soweit wie C es eben ermöglicht.
Wenn ich das nächste Mal geblitzt werde, sage ich: Ich bin so langsam
gefahren, wie es meine Zeit zulässt. ;)))
Dr. Sommer schrieb:> Ich> finde es persönlich am saubersten, alle solche Variablen, die in einer> Anwendung gebraucht werden, an eine Stelle (typischerweise die> main.c(pp)) zu packen, und dann darauf nur Member-Funktionen (C++) bzw.> die o.g. "Pseudo-Member-Funktionen" (C) aufzurufen.
D. h. jede statische Variable (Instanz) wird global in einem zentralen
Modul definiert und erhält eigene Zugriffsfunktionen?
Nur so als Hinweis: Ich kann die Variable ausserhalb des "eine Stelle"
Moduls nicht sehen. ;)
Wenn du hierfür ein kleines C Projekt zeigen könntest. >:>>>
Arduino F. schrieb:> Einigkeit haben wir mit Sicherheit, darin, dass eine Entprellroutine> einen Status halten muss. Das kann sie intern tun, statisch, oder> extern, in dem man ihr einen Zeiger auf eine Struktur übergibt.>> Der zweite Weg macht sie deutlich universeller.> Wesentlich wiedereintrittsfähiger, solange sich die Pointer auf den> Status unterscheiden.
Nö. Beides grundfalsch.
Deine Denkweise ist schlichtweg falsch, denn du versuchst, sowas wie
eine allgemeine Entprellroutine zu etablieren, der man dann irgend einen
Input zuweist. Das ist das Grundfalsche.
Weitaus besser ist es, Ebenen in eine Firmware einzuziehen. Das
bedeutet, daß man einen lowlevel-Treiber für seine Tasten (o.ä.) hat,
der nur über eine abstrahierend definierte Schnittstelle mit den
höheren Programmschichten kommuniziert und der sich intern ansonsten um
genau die Tasten oder ähnliche Input's kümmert, die wirklich im
aktuellen Projekt an den aktuellen Stellen der Hardware vorhanden sind.
Zwei kleine Beispiele in Neudenglisch:
a) per Polling:
1
voidInitTasten(void);
2
boolIstTasteAvailable(void);
3
charGetTastenCode(void);
b) per Event:
1
voidInitTasten(void);
2
// und sonst nix, da Tastatur-Ereignisse vom LL-Treiber
3
// in die allgemeine Event-Queue eingespeist werden
Das bedeutet, daß man zum Portieren oder für ein anderes Projekt eben
nur genau diesen lowlevel-Treiber an die anderen Verhältnisse anpassen
muß. Das Gute daran ist, daß wirklich alles Zeugs inclusive
projektangepaßter Statusbits in irgendwelchen treiberinternen Variablen
innerhalb des Treibers bleiben kann (und sollte), so daß alle anderen
Programmteile mit diesen konkreten Details einfach nichts zu tun haben.
Obendrein kann dann der Treiber sehr viel einfacher und übersichtlicher
formuliert werden, als bei deiner Verfahrensweise.
W.S.
W.S. schrieb:> Deine Denkweise ist schlichtweg falsch, denn du versuchst, sowas wie> eine allgemeine Entprellroutine zu etablieren, der man dann irgend einen> Input zuweist. Das ist das Grundfalsche.
Na ja, das ist nicht unbedingt schlecht, eine universelle
Entprellroutine, Filterroutine, Skalierroutine, ...
Nimm ein anderes Beispiel, CRC-Berechnung. Wenn du mehrere low level
Treiber für unterschiedliche Schnittstellen hast, schreibst du doch auch
nicht für jeden Treiber eine extra angepasste CRC-Routine. ;)
mapfile schrieb:> Ich kann über diesen Weg in C keine Instanzen von Objekten anlegen.
Wenn du das so siehst, ist die Diskussion hinfällig. OOP ist ein
abstraktes Konzept, das von den diversen Programmiersprachen mehr oder
weniger gut unterstützt wird. Viele sind sich einig, dass man auch in C
objektorientiert programmieren kann, was aber mehr Disziplin erfordert;
siehe eben GLib, GObject usw. Wie würdest du denn die gezeigte GString
Implementation bezeichnen? Es geht ja schon über "Prozedural" oder
"strukturiert" hinaus.
mapfile schrieb:> Wenn ich das nächste Mal geblitzt werde, sage ich: Ich bin so langsam> gefahren, wie es meine Zeit zulässt. ;)))
Ich sag einfach, "Ich hab die Geschwindigkeitsangabe in Meilen/Stunde
interpretiert!"
mapfile schrieb:> D. h. jede statische Variable (Instanz) wird global in einem zentralen> Modul definiert und erhält eigene Zugriffsfunktionen?
Die Instanz erhält keine Funktionen. Die "Klasse" hat welche.
mapfile schrieb:> Nur so als Hinweis: Ich kann die Variable ausserhalb des "eine Stelle"> Moduls nicht sehen. ;)
So soll es sein.
mapfile schrieb:> Wenn du hierfür ein kleines C Projekt zeigen könntest. >:>>>
Ich mach sowas normalerweise nur in C++. Sehr rudimentär ginge es so:
Bis hier ist der Code flexibel und wiederverwendbar; erst in der main.c
werden die Instanzen angelegt und verbunden, s.d. man simpel z.B.
mehrere davon anlegen kann ohne die vorigen Dateien ändern zu müssen:
1
// main.c
2
staticstructRingBufferMyRingBuffer;
3
staticstructSensorMySensor;
4
5
voidI2C2_IRQ(void){
6
Sensor_I2C_Interrupt(&MySensor);
7
}
8
9
intmain(){
10
RingBuffer_Init(&MyRingBuffer);
11
Sensor_Init(&MySensor,&MyRingBuffer);
12
13
while(1)sleep();
14
}
So sieht man in der main.c automatisch was es so alles an Daten gibt. Es
gibt keinen "versteckten" Zustand. Nur die Konstante(!) "bufferSize" ist
woanders definiert.
W.S. schrieb:> Deine Denkweise ist schlichtweg falsch, denn du versuchst, sowas wie> eine allgemeine Entprellroutine zu etablieren, der man dann irgend einen> Input zuweist. Das ist das Grundfalsche.
Warum? Das ist das Grundprinzip der Wiederverwendbarkeit, was der
Hauptgrund für einen Großteil des Aufwands bezüglich Programmstruktur
ist. Man könnte so ein Routine ja auch für Ports am Portexpander nutzen,
wenn man eben flexibel neue Instanzen anlegen kann, indem man ein
globales struct (pro Pin) hinzufügt.
W.S. schrieb:> Zwei kleine Beispiele in Neudenglisch:> a) per Polling:void InitTasten(void);> bool IstTasteAvailable(void);> char GetTastenCode(void);
Hier sieht man genau das Problem - die Routinen greifen immer auf die
gleichen Ports zu. Wiederverwendbar wirds, wenn man pro gewünschter
Taste ein "Objekt" (in C: ein struct) anlegt, und das an die Funktion
übergibt. So wird's z.B. im Linux-Kernel gemacht; das ermöglicht erst,
z.B. mehrere Tastaturen anzuschließen. Pro Tastatur wird ein neues
"struct Keyboard" erzeugt, und dann sowas wie "get_keycode (struct
Keyboard* kbd)" aufgerufen.
Dr. Sommer schrieb:> Es geht ja schon über "Prozedural" oder> "strukturiert" hinaus.
Nö, genau das ist es.
Dr. Sommer schrieb:> Ich sag einfach, "Ich hab die Geschwindigkeitsangabe in Meilen/Stunde> interpretiert!"
Gibt es trotzdem ein Tiket, oder nicht?
=> Unwissenheit schützt ...
Dr. Sommer schrieb:> Die Instanz erhält keine Funktionen. Die "Klasse" hat welche.
??? Es gibt doch keine Klasse. Ich kann doch keine Objekte (gekapselte
Daten mit zugehörigen Methoden) instanzieren.
Dr. Sommer schrieb:> Ich mach sowas normalerweise nur in C++
Dann solltest du dabei bleiben und nicht über C philosophieren. ;)
Dr. Sommer schrieb:>> Wenn du hierfür ein kleines C Projekt zeigen könntest. >:>>>> ... Sehr rudimentär ginge es so:
Was bitte schön ist daran OOP? Du hast 08/15 C Module geschaffen, denen
du Parameter übergibst.
???
Aber danke, für dein Beispiel. Es öffnet die Augen, über deine aktuelle
Stellung in der Programmierung. Ich wünsche dir noch einen schönen
Abend. :)
mapfile schrieb:> Nö, genau das ist es.
Deiner Meinung nach ist Code ohne "private:" also kein OOP?
mapfile schrieb:> Dann solltest du dabei bleiben und nicht über C philosophieren. ;)
Na, wenn du alles besser weißt. Jedenfalls ist es gängige Meinung, dass
man in C objektorientiert programmieren kann und dass es genau so wie in
den gezeigten Beispielen geht. Wenn bei dir alles anders ist, ist die
Diskussion sinnfrei.
mapfile schrieb:> Was bitte schön ist daran OOP?
Zusammengehörige Daten werden in einer "Einheit" (hier: struct)
zusammengefasst und mit Funktionen versehen (hier: durch Namenspräfixe),
die die Daten unter Einhaltung der Invarianten bearbeiten.
Prozedural und strukturiert wäre es, wenn die Innereien des "RingBuffer"
globale ("static") Variablen in der ringbuffer.c wären, und es kein
"struct RingBuffer" gäbe.
mapfile schrieb:> Du hast 08/15 C Module geschaffen, denen> du Parameter übergibst.
Definiere "Modul". Und was hast du erwartet? Es ist eine klassische (ja,
objektorientierte) Programmstruktur, bei der funktionslokale "static"
nicht-konstante Variablen vermieden werden. Du wolltest es so genau
wissen...
mapfile schrieb:> Aber danke, für dein Beispiel. Es öffnet die Augen, über deine aktuelle> Stellung in der Programmierung. Ich wünsche dir noch einen schönen> Abend. :)
Was auch immer das heißen soll. Du hast doch außer deinen eigenen
Privat-Definitionen gängiger Begriffe auch noch nichts beigetragen; ich
wüsste nicht, was am Erläutern der "Lehrmeinung" so falsch ist.
In dieser Form müsste das ja deiner Definition nach echte
Objektorientierung sein, weil man nicht von außen auf die Member
zugreifen kann. Somit sind sowohl die lokale als auch die globale
Instanz gekapselt. Braucht aber dynamische Speicherverwaltung und ist
somit für eingebettete Systeme nur bedingt geeignet.
Dr. Sommer schrieb:> Du hast doch außer deinen eigenen> Privat-Definitionen gängiger Begriffe auch noch nichts beigetragen
Na ja, das engl. Wiki sieht das anders und teilt meine Meinung. Hab 's
ja oben irgendwo verlinkt.
Und die Definition und Begriffe zur OOP solltest du im deu. Wiki
nachschlagen:
https://de.wikipedia.org/wiki/Objektorientierte_Programmierung
Deckt sich auch mit meiner Meinung. Kannst ja Mal schauen, wie nahe du
daran bist. ;)
Dr. Sommer schrieb:> Jedenfalls ist es gängige Meinung, dass> man in C objektorientiert programmieren kann
Habe ich noch nie gesehen. Zentrale Bestandteile der OOP lassen sich in
C nicht abbilden. Siehe auch im Wiki, da du ja den Verweis auf allgm.
Wissen wünschst.
Dr. Sommer schrieb:> mit Funktionen versehen (hier: durch Namenspräfixe),> die die Daten unter Einhaltung der Invarianten bearbeiten
Heisst nichts anderes, als dass die OOP durch die (hoffentliche)
Einhaltung besonderer Wünsche (nicht C, sondern 'sommerlicher' Zusatz)
durch jeden Programmierer zu jeder Zeit erfolgt. Kein am Projekt
Beteiligter darf einen Fehler bei der Namensgebung oder den
Datenzugriffen machen. Menschliche Fehler sind generell verboten, denn
Compiler, Linker und Co. helfen nicht und lösen solche Fehler nicht auf.
Au Backe. ;)
Dr. Sommer schrieb:> Es ist eine klassische (ja,> objektorientierte) Programmstruktur
Es ist ein stinknormales C Programm, dass aus mehreren Modulen besteht.
Vgl. die Compiler Libs. Das lernt der C-ler in der dritten
Unterrichtsstunde. Da steckt wirklich nichts objektorientiertes darin,
sorry. Selbst wenn man mit ner Lupe hinguckt. :(
Dr. Sommer schrieb:> Was auch immer das heißen soll
Dass ich - als netter Mensch - dir einen schönen Abend wünsche. ;)
Dr. Sommer schrieb:> In dieser Form müsste das ja deiner Definition nach echte> Objektorientierung sein, weil man nicht von außen auf die Member> zugreifen kann.
???
Anstatt der Variablen hast du jetzt einen Pointer und allokierst
Speicher. Was ändert das an der Kapselung?
Dr. Sommer schrieb:> In dieser Form müsste das ja deiner Definition nach echte> Objektorientierung sein, weil man nicht von außen auf die Member> zugreifen kann.
Nö, ich glaube, du hast OOP nicht verstanden. Und dynamische
Speicherverwaltung auch nicht. :(
Jeder im Modul kann auf den modulglobalen Pointer zugreifen. Egal wann
der Speicher dahinter allokiert wird. Und die lokale Variable in main()
ist auch nichts besonderes. Wenn du das als OOP ansiehst, wäre jeder
Funktionsaufruf mit einer lokalen Variable OOP!
Das wird immer besser hier. ;)
Dr. Sommer schrieb:> Man kann in der main.c nicht auf die Member des Objekts direkt> zugreifen.
Natürlich kann man auf die globale Variable innerhalb der main.c
zugreifen. Warum denn nicht. Wenn man Pech hat, wurde noch kein Speicher
'hinter' dem Pointer allokiert. Aber das hat doch nichts, rein gar
nichts, mit OOP zu tun. :(((
Dr. Sommer schrieb:> Genau, die ganze Welt versteht es falsch
Nö, du bist nicht die Welt. ;)
Dr. Sommer schrieb:>> Jeder im Modul kann auf den modulglobalen Pointer zugreifen.> Logisch. Das ist der Grund, warum man globale Variablen nutzt. Aber> keiner kann die Interna kaputt machen.
Warum kann jemand, der auf den Pointer zugreifen kann, nicht die Daten -
auf die er zeigt - verändern können (wenn sie nicht const sind, im ROM
liegen, ... Nur damit nicht neue Schauplätze geöffnet werden)?
Ich gehe einmal davon aus, dass du deine Links gelesen hast. Hast du sie
auch verstanden? Ein Tipp, der Schwerpunkt liegt in Funktionszeigern,
die innerhalb der Strukturen liegen. Das sit weit weg von dem, was du
hier gezeigt hast.
Hier noch ein Zitat aus deinem Link
(https://www.codementor.io/michaelsafyan/object-oriented-programming-in-c-du1081gw2):
"... Although, in some cases, the boilerplate that this approach
generates may not always be worthwhile, writing imperative C code in OOP
style can illuminate how OOP works. The ability to write in this style
is also valuable for creating APIs that are shared between OOP and
non-OOP languages ..."
Wer ist hier wohl der Troll? ;)
mapfile an Dr. Sommer
> Und die Definition und Begriffe zur OOP solltest du im deu. Wiki> nachschlagen:> Ich gehe einmal davon aus, dass du deine Links gelesen hast. Hast du sie> auch verstanden? Ein Tipp, der Schwerpunkt liegt in Funktionszeigern,> die innerhalb der Strukturen liegen. Das sit weit weg von dem, was du> hier gezeigt hast.> ich glaube, du hast OOP nicht verstanden. Und dynamische> Speicherverwaltung auch nicht. :(
Ts ts, mein Gott habt ihr die Begriffe hier zerpflückt. Da wird einem ja
ganz schummrig von.
@ Doc Sommer
Ist irgendwie nicht dein Tag heute was?
;-)
W.S. schrieb:> Nun kennt C blöderweise eben keine echten Units, die ihren Inhalt> kapseln und nur das nach außen lassen, was sie nach außen lassen wollen.> Deshalb sind im Prinzip alle statischen Variablen in C automatisch> globale Variablen, auf die auch von anderen Programmteilen zugegriffen> werden kann - auch dann, wenn sie nicht in der zugehörigen Headerdatei> auftauchen. Sie müssen woanders bloß mit "extern" benannt werden und -> voila - der Zugriff funktioniert. Läßt man das "extern" weg, dann findet> der Linker zwei namensgleiche Variablen im Objektcode und meckert, wenn> man ihn läßt.
Das ist so nicht korrekt.
Wird in einem C-Modul außerhalb einer Funktion eine Variable
definiert, ist sie global, und nicht statisch.
Dann wird ihr Name als Symbol in der Objektdatei exportiert und der
Linker kann entsprechende Referenzen aus anderen Modulen auflösen.
Wird die Variable aber als static deklariert, ist sie statisch, und
nur innerhalb des entsprechenden C-Moduls sichtbar. Ihr Name wird
nicht als Symbol exportiert und es ist nicht möglich, mit externen
Referenzen* aus anderen Objektdateien auf diese Variable zuzugreifen,
weil der Linker diese Referenzen nicht auflösen kann.
Eine "extern"-Deklaration in einer Headerdatei ändert daran nichts.
Exakt das gleiche Verhalten gibt es mit Funktionsdefinitionen - eine als
static definierte Funktion ist nur innerhalb des C-Modules sichtbar,
in dem sie definiert wird, es gibt keinen Symbolnamen in der Objektdatei
und ein Aufruf aus anderen Modulen heraus ist nicht möglich, da der
Linker entsprechende Referenzen nicht auflösen kann.
Damit ist das von Dir gewünschte "Unit"-Kapselungsverhalten 1:1 in C
umsetzbar.
Das Schlüsselwort static hat in C aber auch noch eine andere
Bedeutung, wird es zur Definition einer Variablen innerhalb einer
Funktion verwendet, ist diese Variable keine automatische Variable,
d.h. sie landet nicht auf dem Stack und ihr Inhalt bleibt über mehrere
Funktionsaufrufe hinweg erhalten.
*) hier sind nicht "Referenzen" im C++-Sinne gemeint.
Ihr seid ja schnuckelig....
Kaum ist man ein paar Stunden nicht da...
Alex G. schrieb:> Also an der Uni lernt man auch dass Cpp objektorientiert ist. C nicht...
Wenn du damit sagen möchtest, dass C die Objekt orientierte Sichtweise,
Denkart, nicht sonderlich gut unterstützt, dann stimme ich dir zu.
Schließlich wurde, genau aus dem Grund, aus C dann "C mit Klassen", und
später dann das C++ entwickelt.
Wenn du aber damit meinst, man sollte auf die Objekt orientierte
Sichtweise verzichten, nur weil man gerade mit einer Sprache arbeiten
muss, welche das nicht direkt und vollumfänglich unterstützt, dann
liegst du (aus meiner Sicht) falsch.
------
Mal zurück zur Frage "global, oder lokal statisch?", in Bezug zum
Eingangsposting, und als Reaktion auf Posting
Beitrag "Re: C Anfängerfrage (static innerhalb einer Funktion)"
Unbestritten, ist, dass ein Programm irgendwo Informationen halten muss.
Steckt man diese Information, in eine Funktion, in Form einer lokalen
Variablen, dann muss man sie, wie es der TE vor hat/hatte, u.U. an die
Aufrufende Schicht propagieren.
Dagegen muss die globale verfügbare Information an die tieferen
Schichten weitergegeben werden.
Das ist ein klein bisschen wie die Frage: "Was hätten sie denn gerne,
Erschossen werden, oder aufgehangen?"
Dem Priester mapfile nach, scheint der HAL ein guter Platz für lokale
statische Variablen zu sein...
Dem möchte ich widersprechen.
Es ist eine Angelegenheit der Anwendung/Applikation, wie welche Hardware
genutzt wird. Dieser Wunsch, der Anwendung, muss an die tieferen
Schichten des HAL propagiert werden. Damit ist die Hardware Abstraktion,
selber, frei von Anwendungsdetails.
Wir haben einerseits Informationen, welche aus tieferen Schichten hoch
proagiert werden müssen, z.B. Tastendrücke, welche aus der Hardware
kommen, und irgendwann in der Applikation zu Entscheidungen führen.
Und andererseits Informationen, welche von dem höchsten
Entscheidungsträger aus, bis zur konkreten LED führen, damit sie dann
leuchten kann, oder eben auch nicht.
Den einen Weg erreichen wir über die Parameter einer Funktion, den
anderen, über ihren Returnwert.
So soll es sein, so ist es am schönsten.
Lokale statische Variablen sind dabei eher wie ein Stock in den
Speichen. Es muss plötzlich nicht mehr nur die Signatur einer Funktion
betrachtet werden, sondern auch noch ihr innerer Zustand. Eine Funktion,
welche dauernd ihr Verhalten ändert, ist deutlich schwerer zu
dokumentieren, und zu debuggen.
Aus meiner Sicht, "gehören" statische Dinge der Applikation.
Der obersten Schicht.
In den tieferen Schichten werden meist Bibliotheksfunktionen genutzt.
Und diese sollen möglichst universell und gut wiederverwendbar sein.
Seiteneffekte will man da eher nicht. Zumindest keine unerwünschten.
In der obersten Schicht, sehe ich keine Probleme, wenn man auf statische
Variablen zurückgreift, oder auch auf globale Dinge.
Denn die oberste Schicht ist eine "Weg werf Schicht".
Eine Wiederverwendung ist da erstmal nicht sinnvoll.
Die nächste Applikation wird an der Stelle ganz anders aussehen.
Rufus Τ. F. schrieb:> Wird in einem C-Modul außerhalb einer Funktion eine Variable> definiert, ist sie global, und nicht statisch.
Das Problem ist m.E., dass jeder eine andere Vorstellung davon hat, was
"statisch" bedeuten soll. Es ist wohl von dem Schlüsselwort "static"
hergeleitet, was aber je nach Konstrukt in C etwas anderes bedeutet. Es
ist besser, die Begriffe des Standards zu verwenden, dann gibt es
weniger Mißverständnisse.
Ein Variable ausserhalb jeder Blockstruktur heißt "global" bzw. genauer
"Programm-Global". Und diese hat "static-storage duration"! Und
"external linkage".
Schreibt man "static" davor, wir sie "Übersetzungseinheit-Global":
translation-unit global. Und hat auch static storage duration. Aber kein
external linkage mehr.
> Eine "extern"-Deklaration in einer Headerdatei ändert daran nichts.
Bei übersetzungseinheit-globalen Variablen ist die Kombination aus o.g.
Grund unzulässig.
> Exakt das gleiche Verhalten gibt es mit Funktionsdefinitionen - eine als> static definierte Funktion ist nur innerhalb des C-Modules sichtbar
Verwendet die offiziellen Begriffe, die auch klarer sind (was ist ein
Modul?): es heißt Translation-Unit.
> Das Schlüsselwort static hat in C aber auch noch eine andere> Bedeutung, wird es zur Definition einer Variablen innerhalb einer> Funktion verwendet, ist diese Variable keine automatische Variable,
Hier bedeutet "static" dann "static storage duration" und keine external
linkage.
Arduino F. schrieb:> Lokale statische Variablen sind dabei eher wie ein Stock in den> Speichen. Es muss plötzlich nicht mehr nur die Signatur einer Funktion> betrachtet werden, sondern auch noch ihr innerer Zustand. Eine Funktion,> welche dauernd ihr Verhalten ändert, ist deutlich schwerer zu> dokumentieren, und zu debuggen.
Man sollte auch nur eine Funktion nennen, was eine (reine) Funktion ist.
Eine Funktion mit Zustand ist eher ein Funktor bzw. Elementfunktion
(eines Objektes, was per def. einen Zustand hat).
> Aus meiner Sicht, "gehören" statische Dinge der Applikation.> Der obersten Schicht.
Na ja: die allerunterste Schicht, die HW, sprich der µC hat auch einen
Zustand. Deswegen sind eben die meisten "Funktionen", die auf die HW
angewendet wird, eben keine reinen Funktionen mehr.
Rufus Τ. F. schrieb:> Danke für die begriffliche Präzisierung. Inhaltlich ändert sich> dadurch jedoch nichts.
Aber es vereinfacht die Diskussion wahnsinnig, wenn man die offiziellen,
eingeführten Begriffe mit ihrer festgelegten Semantik verwendet. Zudem
findet man mit dem richtigen Begriff auch schnell mal eine Antwort z.B.
im Standard. Außerdem könnten wir die Hälfte dieses Threads streichen
... ;-)
Wilhelm M. schrieb:> Aber es vereinfacht die Diskussion wahnsinnig,
Da hast Du natürlich recht. Asche->Haupt. Ich nutze C schon so lange,
daß ich mir nie wirklich die Mühe gemacht habe, die Terminologie für
derartiges in einem der neueren Standarddokumente nachzulesen. Ich war
damals froh, auf den Systemen, mit denen ich gearbeitet habe, einen
C89-Compiler zu bekommen, denn das asthmatische K&R-C davor war ein
Graus. Damals aber war es nicht praktikabel, den Standard selbst zu
lesen (der wäre nur aufwendig aufzutreiben gewesen), da musste die
(deutsche) zweite Ausgabe des K&R und die vorzügliche deutschsprachige
Dokumentation (geschrieben von Arne Schäpers) von Turbo-C 2.0 genügen.
Tja, jeder hat so seine Defizite.
Wilhelm M. schrieb:> Na ja: die allerunterste Schicht, die HW, sprich der µC hat auch einen> Zustand. Deswegen sind eben die meisten "Funktionen", die auf die HW> angewendet wird, eben keine reinen Funktionen mehr.
Nunja...
Dass die Hardware, selber, Zustände hat, liegt in der Natur der Dinge.
Wäre auch untragbar, wenn es das nicht geben würde.
Aber das heißt ja nicht, dass man die Zugriffsfunktionen mit lokalen
statischen Variablen ausstatten sollte.
Wilhelm M. schrieb:> es vereinfacht die Diskussion wahnsinnig, wenn man die offiziellen,> eingeführten Begriffe mit ihrer festgelegten Semantik verwendet.
Ja!
Dazu:
Mir scheint, der Begriff "Funktion" in der C Welt doch recht eindeutig
zu sein.
"Funktor" gehört eher zur C++ Welt.
Arduino F. schrieb:> Wilhelm M. schrieb:>> Na ja: die allerunterste Schicht, die HW, sprich der µC hat auch einen>> Zustand. Deswegen sind eben die meisten "Funktionen", die auf die HW>> angewendet wird, eben keine reinen Funktionen mehr.>> Nunja...> Dass die Hardware, selber, Zustände hat, liegt in der Natur der Dinge.> Wäre auch untragbar, wenn es das nicht geben würde.>> Aber das heißt ja nicht, dass man die Zugriffsfunktionen mit lokalen> statischen Variablen ausstatten sollte.
Was aber an der Qualität nichts ändert.
Wilhelm M. schrieb:> Arduino F. schrieb:>> Lokale statische Variablen sind dabei eher wie ein Stock in den>> Speichen. Es muss plötzlich nicht mehr nur die Signatur einer Funktion>> betrachtet werden, sondern auch noch ihr innerer Zustand. Eine Funktion,>> welche dauernd ihr Verhalten ändert, ist deutlich schwerer zu>> dokumentieren, und zu debuggen.>> Man sollte auch nur eine Funktion nennen, was eine (reine) Funktion ist.
Nur sehr wenige C-Funktionen sind Funktionen in dem von dir gedachten
Sinne.
Wilhelm M. schrieb:> Schreibt man "static" davor, wir sie "Übersetzungseinheit-Global":> translation-unit global. Und hat auch static storage duration. Aber kein> external linkage mehr.
Genauer gesagt hat sie dann internal linkage.
Wilhelm M. schrieb:>> Das Schlüsselwort static hat in C aber auch noch eine andere>> Bedeutung, wird es zur Definition einer Variablen innerhalb einer>> Funktion verwendet, ist diese Variable keine automatische Variable,>> Hier bedeutet "static" dann "static storage duration" und keine external> linkage.
Sie hat dann gar keine Linkage, weder external noch internal.
Wilhelm M. schrieb:> Was aber an der Qualität nichts ändert.
Irgendwie scheint mir das Wort "Qualität" in diesem Zusammenhang
deplatziert zu sein.
Aus meiner Sicht macht es schon einen "Qualitätsunterschied" (wenn ich
das Wort, auch mal verwenden darf), ob man aus Mutwilligkeit,
Blauäugigkeit, statische Variablen, in Funktionen, einstreut, oder ob
die Zustände durch eine Hardware erzwungen werden, bzw. ihr aufgezwungen
werden.
Die enthaltende Alternativlosigkeit, Unabänderbarkeit, ist schon ein
Qualitätsmerkmal.
Arduino F. schrieb:> Ihr seid ja schnuckelig....> Kaum ist man ein paar Stunden nicht da...>> Alex G. schrieb:>> Also an der Uni lernt man auch dass Cpp objektorientiert ist. C nicht...>> Wenn du damit sagen möchtest, dass C die Objekt orientierte Sichtweise,> Denkart, nicht sonderlich gut unterstützt, dann stimme ich dir zu.> Schließlich wurde, genau aus dem Grund, aus C dann "C mit Klassen", und> später dann das C++ entwickelt.>> Wenn du aber damit meinst, man sollte auf die Objekt orientierte> Sichtweise verzichten, nur weil man gerade mit einer Sprache arbeiten> muss, welche das nicht direkt und vollumfänglich unterstützt, dann> liegst du (aus meiner Sicht) falsch.>
Also aus meiner Sicht sollte man eine Sprache nunmal nicht zu etwas
zwingen, wofür sie nicht gemacht ist.
Das Risiko ist sehr hoch dass man dadurch eher mehr Fehler einbaut, als
man vermeidet.
Wenn man so sattelfest in der Programmierung ist, dass dies nicht
passiert, würde man wahrscheinlich auch ohne diese Umwege gut klar
kommen.
Wilhelm M. schrieb:> Das Problem ist m.E., dass jeder eine andere Vorstellung davon hat, was> "statisch" bedeuten soll. Es ist wohl von dem Schlüsselwort "static"> hergeleitet, was aber je nach Konstrukt in C etwas anderes bedeutet. Es> ist besser, die Begriffe des Standards zu verwenden, dann gibt es> weniger Mißverständnisse.
Schon interessant, dass das in diesem Thread mehrfach vorkommt. ;)
Und Schade, dass in Wikipedia nicht die "Begriffe des Standards"
verwendet werden. Vielleicht kann ja einer von euch den Artikel
korrigieren. :)
Und ja, ich kenne die Begriffe und kann sie deuten. Aber aus Sicht eines
Anwendungsprogrammierer sind die Wirkung - wie bei Wikipedia beschrieben
- einleuchtender und ausreichend. Ihn interessiert die Sichbarkeit und
die Haltbarkeit, egal wie das im Hintergrund gemacht wird.
Arduino F. schrieb:> Dem Priester mapfile nach, scheint der HAL ein guter Platz für lokale> statische Variablen zu sein..
Da du ja auch die Inhalte deiner eigenen Posts vergisst, will ich dir
auch hier unter die Arme greifen:
Arduino F. schrieb:
> Ansonsten gilt der Leitsatz:> Nur statische Variablen, auf welche man verzichtet, sind gute statische> Variablen.
Dieser pauschalen Aussage habe ich widersprochen! Und ich schließe die
Verwendung statischer Variablen in einem HAL, einem BS, einer LIB nicht
aus, fordere sie aber nicht.
Alex G. schrieb:> Also aus meiner Sicht sollte man eine Sprache nunmal nicht zu etwas> zwingen, wofür sie nicht gemacht ist.> Das Risiko ist sehr hoch dass man dadurch eher mehr Fehler einbaut, als> man vermeidet.
Wenn du damit sagen willst, dass fehlerfreies Programmieren in C++
leichter sein soll, als die in C mit OOP-Nachbildung, dann stimmte ich
dir definitiv nicht zu. C++ ist alles andere als C mit Klassen und vom
Schwierigkeitsgrad auf einem anderen Level. Und so lange es bei
einfachen Klassen bleibt, mit vielleicht ein wenig Vererbung, dann ist
OOP in C kein Stück aufwändig. Hast du denn überhaupt schon in C OO
programmiert?
Dr. Sommer schrieb:> mapfile schrieb:>> Nochmal als Tipp für dich: statische Variablen müssen nicht zwingend mit>> 'static' eingeleitet werden. ;)>> Es gibt nicht nur local statics. ;)> Jetzt klär uns mal auf, was das sein soll...> So etwas:> int foo;> int main () { }> ist ja keine statische, sondern nur eine globale Variable.
Du wirfst hier 2 Dinge durcheinander:
1) C-Schlüsselwort "static".
2) Daten im "static storage", einer Speicherklasse.
1) ==> 2) aber nicht umgekehrt. Insbesondere ist foo von oben im static
storage.
Je nachdem, was ihr oben mit "statics" meint, führt das dann zu beliebig
viel Verwirrung.
Neben der Speicherklasse hat "static" auch Einfluß auf die Linkage. Da
in C kein Identifier eine Sichtbarkeit größer als eine Compilation Unit
hat, braucht es ein Feature, um Identifier in verschieden Units zu
verbinden, welche das gleiche Objekt referenzieren sollen: External
Linkage.
Zur weiteren Verwirrung bedeutet "extern" nicht notwendig external
Linkage wie in
1
staticintvar;// ok, tentative decl
2
externintvar;// ok, refers to var from above
und für external Linkage ist nicht unbedingt "extern" erforderlich :-)
schon toll, dieses C...zu jeder noch so einfachen frage..meint man..gibt
es eine riesige Diskussion...
Ich sag ja..Pascal/Lazarus...für 99% der Urer völlig ausreichend
Der Thread ist ja schon etwas fortgeschritten und ich habe nicht alles
gelesen, dennoch möchte ich noch auf eine *C++-Tücke* hinweisen:
Eine funktionslokale static variable wird nur EINMAL angelegt, egal
wieviele Instanzen man von einer Klasse bildet.
D.h. jede Klasse verwendet mit der entsprechenden Funktion ein und
dieselbe Variable! Das kann mächtig Ärger verusachen, wenn man das nicht
so beabsichtigt hat (in der Regel hat man es nicht beabsichtigt).
Deswegen: Bei C++ möglichst keine funktionslokale static Variablen
verwenden. ;-)
DanVet schrieb:> Der Thread ist ja schon etwas fortgeschritten und ich habe nicht alles> gelesen, dennoch möchte ich noch auf eine *C++-Tücke* hinweisen:>> Eine funktionslokale static variable wird nur EINMAL angelegt, egal> wieviele Instanzen man von einer Klasse bildet.> D.h. jede Klasse verwendet mit der entsprechenden Funktion ein und> dieselbe Variable! Das kann mächtig Ärger verusachen, wenn man das nicht> so beabsichtigt hat (in der Regel hat man es nicht beabsichtigt).> Deswegen: Bei C++ möglichst keine funktionslokale static Variablen> verwenden. ;-)
Das ist ein guter Hinweis! Sollte allerdings bei genauerer Betrachtung
auch klar sein, denn eine Klasse ist (auch) ein (spezieller) Namensraum:
Eine Klasse -> Eine Funktion -> ein static Objekt.
Bei Template: unterschiedliche Parametrierung(!) -> unterschiedliche
Templateklassen -> unterschiedliche Funktionen -> mehrere static
Objekte.
Rufus Τ. F. schrieb:> Wird die Variable aber als static deklariert, ist sie statisch, und> nur innerhalb des entsprechenden C-Moduls sichtbar. Ihr Name wird> nicht als Symbol exportiert und es ist nicht möglich, mit externen> Referenzen* aus anderen Objektdateien auf diese Variable zuzugreifen,> weil der Linker diese Referenzen nicht auflösen kann.
Du schreibst Unsinn - jedenfalls sofern du dich auf die
Mikrocontroller-Programmierung beziehst.
Also:
Richtig ist, daß JEDE Variable, die nicht auf dem Stack angeordnet ist
und auch nicht zu den Hardware-Registern gehört, auch im Objektcode
auftaucht.
Egal ob sie nun als "static" ausgewiesen ist oder nicht.
Das ist so und es ist der Linker, der all diesen Variablen ihren finalen
Speicherplatz zuweist.
Und damit er das auch bei "static" Variablen tun kann, hat eben jede
Toolchain ihre Mechanismen bei der Flag- oder Namensgebung im
Objektcode, um Kollisionen (sprich Fremdzugriff) zu vermeiden. Aber
auftauchen im Objektcode müssen sie, sonst gibt's keinen Speicherplatz
dafür. Mag ja sein, daß eine "MySecretVariable" dann umbenannt als
"$$mymodule$$$0004711" erscheint.
W.S.
Tina schrieb:> Ich sag ja..Pascal/Lazarus...für 99% der Urer völlig ausreichend
Wenn es dazu einen guten Compiler für bare-metal gäbe, wäre das für 100%
aller Leute völlig ausreichend. Aber Pascal kann man ja relativ
problemlos lesen - und genau DAS ist so vielen Programmierern ein Dorn
im Auge...
W.S.
W.S. schrieb:> Aber auftauchen im Objektcode müssen sie, sonst gibt's keinen> Speicherplatz dafür. Mag ja sein, daß eine "MySecretVariable" dann> umbenannt als "$$mymodule$$$0004711" erscheint.
Tu Dir einfach den Gefallen, mal mit dem zu Deiner Toolchain passenden
Objektdump-Werkzeug eine Objektdatei anzusehen.
Wobei: Selbst wenn Deine suggerierte Umbenennung stattfände - worin
unterscheidet sich die von der von Dir unterstellt nicht vorhandenen
Kapselungsmöglichkeit?
Und welchen magischen Mechanismus soll Pascal hier verwenden, der
effektiv anders aussieht?
W.S. schrieb:> Rufus Τ. F. schrieb:>> Wird die Variable aber als static deklariert, ist sie statisch, und>> nur innerhalb des entsprechenden C-Moduls sichtbar. Ihr Name wird>> nicht als Symbol exportiert und es ist nicht möglich, mit externen>> Referenzen* aus anderen Objektdateien auf diese Variable zuzugreifen,>> weil der Linker diese Referenzen nicht auflösen kann.>> Du schreibst Unsinn - jedenfalls sofern du dich auf die> Mikrocontroller-Programmierung beziehst.>> Also:> Richtig ist, daß JEDE Variable, die nicht auf dem Stack angeordnet ist> und auch nicht zu den Hardware-Registern gehört, auch im Objektcode> auftaucht.
Dass sie im Object-Code auftaucht bedeutet aber nicht, dass sie die
nötige Linkage hat um von anderen Compilation units aus zugreifbar zu
sein. Auf Object-Ebene zeigt sich das darin, dass entsprechende Objekte
nicht global sind. Beispiel:
a.c
1
intaaa;
2
__attribute((used))staticintbbb;
3
4
intmain(){}
b.c
1
__attribute((used))staticintbbb;
1
$gcc a.c b.c && nm a.out | sort
2
3
000000000060103c B aaa
4
0000000000601034 b bbb
5
0000000000601038 b bbb
6
...
> Egal ob sie nun als "static" ausgewiesen ist oder nicht.
Nein, eben nicht.
> Das ist so und es ist der Linker, der all diesen Variablen ihren finalen> Speicherplatz zuweist.
Ja, der weist den Speicherplatz zu. Dennoch kann man b.c::bbb nicht von
a.c aus zugreifen, und man kann a.c::bbb nicht von b.c aus zugreifen.
> Mag ja sein, daß eine "MySecretVariable" dann umbenannt als> "$$mymodule$$$0004711" erscheint.
Die kann aber immer nocht nicht von C aus zugegriffen werden, es sein
denn man ist in der gleichen Compilation Unit.
Rufus Τ. F. schrieb:> Und welchen magischen Mechanismus soll Pascal hier verwenden, der> effektiv anders aussieht?
Ich entsinne mich nicht, in diesem Thread das Wort "Pascal" zuvor je
hingeschrieben zu haben.
Aber zur Erklärung deiner Frage:
Pascal verwendet ein tatsächlich funktionierendes Unit system, das so
aussieht, daß es in jeder Unit-Quelle eine Sektion "interface" gibt,
welche all die Deklarationen, Variablen, Funktionen und sonstiges Zeugs,
was man anderen Programmteilen zur Kenntnis geben will, enthalten.
Danach kommt die Sektion "implementation", wo man deklarien und
definieren kann was man will - es bleibt strikt innerhalb des Units und
ist von außen unzugänglich.
Wie das in der konkreten Toolchain gemacht wird, müßte man nachlesen.
Ebenso, wie es gemacht wird, daß Funktionen und Prozeduren lokale
Funktionen und Prozeduren haben können.
So.
Bei einem funktionablen Unit-System gibt man lediglich an, welche Units
man zu benutzen gedenkt - und deshalb ist so ein albernes Herumfuchteln
mit #include und "extern" gar nicht erforderlich. Jaja, man kann in
Pascal auch sowas wie #include benutzen - aber es wird noch viel weniger
benötigt als goto.
Wobei der #include-Workaround um das fehlend Unitsystem in C wieder mal
herzlich unkonsistent gemacht wurde: externe Daten brauchen ein
"extern", sonst sind sie doppelt da und bei externen Funktionen galt das
bis vor einiger Zeit nicht - woran sich manche Leute partout noch immer
nicht gewöhnen können. Gottseidank ist das vorbei und man komplett alle
externen Daten und Funktionen mit "extern" kennzeichnen, ist ja auch das
einzig logisch Richtige.
Nebenbei hat man eine Entsprechung dieses Systems aus Interface und
Implementation auch bei VHDL, nur Verilog gibt sich vergleichsweise so
schlampig wie C.
W.S.
Johann L. schrieb:>> Egal ob sie nun als "static" ausgewiesen ist oder nicht.>> Nein, eben nicht.
Ja, eben DOCH!
>> Das ist so und es ist der Linker, der all diesen Variablen ihren finalen>> Speicherplatz zuweist.>> Ja, der weist den Speicherplatz zu. Dennoch kann man b.c::bbb nicht von> a.c aus zugreifen, und man kann a.c::bbb nicht von b.c aus zugreifen.
Hast du es noch immer nicht begriffen?
Variablen auf dem Stack sind kein Thema für den Linker, aber alles
Andere IST en Thema für den Linker. Egal ob static oder nicht.
Lediglich durch Maßnahmen, die in der jeweiligen Toolchain vorgesehen
sind, werden Variablen, die als static deklariert sind, nur mit den
Zugriffen aus ihrem eigenen Objektcode verlinkt. Normalerweise erfolgt
dies durch eine relativ radikale Umbenennung, die man im Quellcode
nicht machen kann, um zufällige Namensgleichheiten auszuschließen.
Noch Fragen, Kienzle?
W.S.
W.S. schrieb:> Hast du es noch immer nicht begriffen?
Doch. Du kannst davon ausgehen, dass ich ein Minimum an Vertändnis
dafür habe, welche Aufgaben Tools wie Compiler oder Linker erledigen.
Dass man hier prinzipiell via Hardware-Adresse auf ein Objekt zugreifen
kann, falls man Kenntnis über diese erlangt, sollte klar sein.
Aber auch, dass das mit der aktuellen Verwirrung, was mit "statics"
überhaupt gemneint ist, hat das ziemlich wenig bis garnix zu tun: Die
Implementation folgt dem Standard, und dieser legt Konzepte wie Static
Storage, Linkage und Semantik und Syntax von "static" fest. Was im
Gedärm der Tools geschieht — und dazu gehören hier Linker und
Binär-Format — ist für die Frage und das Verständnis des TO irrelevant,
denn sie spielen im Standard schlichweg keine Rolle.
W.S. schrieb:> Bei einem funktionablen Unit-System gibt man lediglich an, welche Units> man zu benutzen gedenkt - und deshalb ist so ein albernes Herumfuchteln> mit #include und "extern" gar nicht erforderlich. Jaja, man kann in> Pascal auch sowas wie #include benutzen - aber es wird noch viel weniger> benötigt als goto.
1.) Eine FreePascal-Unit definiert im Interface-Teil der Unit, welche
Typen, Funktionen/Prozeduren und Variablen sie anderen Units zur
Verfügung stellt.
2.) Ein(e) FreePascal Unit/Programm definiert im Uses-Teil, welche Units
sie/es benutzen will
Wo ist der funktionale Unterschied zwischen 1.) und einem C
include-File?
Wo ist der funktionale Unterschied zwischen 2.) und einem C
#include-Statement?
Ich mag Pascal auch, aber bahnbrechende Unterschiede kann ich nicht
erkennen. Wenn ich in C etwas "geheimhalten" will, muss ich halt
"static" dazuschreiben, wenn ich in Pascal etwas "veröffentlichen" will,
muss es eben ins Unit-Interface. Letztendlich ist die Funktionalität
dieselbe, der Rest ist eine Frage der persönlichen Vorlieben.
Auch ein Pascal-Binary muss irgendwann mal gebunden werden. FreePascal
beispielsweise nutzt dafür den GNU ld. Also haben wir auch beim Linken
exakt dieselbe Funktionalität bzw. Pascal kann gar nicht so viel
toller Linken als die C-Toolchain.
W.S. schrieb:> Nebenbei bemerkt, finde ich diese Namensgebung miserabel, man hätte das> Ganze besser "private" nennen sollen. Ist was Ähnliches wie "typedef",> was man besser "rename" hätte nennen sollen.
Full ACK!
Markus F. schrieb:> Wo ist der funktionale Unterschied zwischen 1.) und einem C> include-File?> Wo ist der funktionale Unterschied zwischen 2.) und einem C> #include-Statement?
Es ist eben die Funktionalität, die du offensichtlich nicht zu
erkennen vermagst.
Bei Pascal hat es eine klare Ansage innerhalb eines Units, was man zu
exportieren gedenkt und was nicht. Bei C gibt es so etwas nicht.
Ich breche das hier mal ab, schließlich geht es in diesem Thread ja um
C.
Ansonsten frag ich mal provokativ, wo der Unterschied zwischen C und
Assembler ist.
W.S.
W.S. schrieb:> Ansonsten frag ich mal provokativ, wo der Unterschied zwischen C und> Assembler ist.
Ist der C Kompiler nicht einfach nur ein aufgebohrter Macro Assembler?
Hihihi...
W.S. schrieb:> Bei Pascal hat es eine klare Ansage innerhalb eines Units, was man zu> exportieren gedenkt und was nicht. Bei C gibt es so etwas nicht.
Du weigerst Dich mit beeindruckender Hartnäckigkeit, zu akzeptieren, daß
diese "klare Ansage" genauso und mit genau der gleichen Funktionalität
in C existiert.
Aber es ist vermutlich müßig, Dich darauf hinzuweisen, haben ja schon
mehrere hier vergebens versucht.
W.S. schrieb:> Aber Pascal kann man ja relativ> problemlos lesen - und genau DAS ist so vielen Programmierern ein Dorn> im Auge...
Ein Dorn nicht gerade, aber zuviele Ballaststoffe. Wenn ein Quelltext
fast zur Hälfte aus BEGIN und END besteht, ist das einfach nur lästig
und damit nicht mehr problemlos.
In C ist die Struktur bis auf wenige Schlüsselworte in einfachen Zeichen
und der Text bleibt für die Namen. Wenn man nicht gerade absoluter
Anfänger ist, ist ein Quelltext in C viel schneller überschaubar als das
schwätzerische Pascal.
Rufus Τ. F. schrieb:> Ich glaube nicht, daß ein Programmiersprachenlesbarkeitsflamewar> diesen> Thread voranbringen würde.
Glaube ich ich auch nicht!
Wobei ich schon sagen muss, dass Pascal oft besser zu lesen ist, als
C/C++
Ich mag es. Aber nutze es kaum noch.
1
ProgramVisual;
2
3
Uses
4
Protokoll,Sheduler,Visualisierung;// und so weiter
Ich wollte auch gar nicht auf Begin und end oder sowas hinaus.
Schaut doch einfach mal in einem Pascal Forum
Stellt da einer eine Frage, gibt es eine klare Antwort.
In C bzw C++ kommt eine Antwort und dann kommen ganz viel weitere die
sagen es wäre falsch, oder das stimmt nur wenn xy oder in anderen fällen
passiert aber dieses oder jenes...
NUR darauf wollte ich hinaus.
In C gibt es zu jedem Mückenschiss 1000 Sonderfälle...
Wobei für 98% aller Fälle Lazarus Pascal oder Delphi völlig ausreichen
würde, auch für welche die das gewerblich nutzen, quälen sich doch immer
wieder welche mit C bzw C++ rum.
Derzeit etwas entschärft durch viele Neueinsteiger die die mit Python
beschäftigen, dem ich etwas skeptisch gegenüber stehe, aber zu wenig
darüber weiß als das ich dagegen ´reden könnte
Rufus Τ. F. schrieb:> Du weigerst Dich mit beeindruckender Hartnäckigkeit, zu akzeptieren, daß> diese "klare Ansage" genauso und mit genau der gleichen Funktionalität> in C existiert.
Du redest noch immer Unsinn. Was du sagst, ist schlichtweg FALSCH. Ich
geb dir mal ein Beispiel:
Quelle "eins.c":
hier deklarieren wir ne Variable und ne Funktion
aber wir schreiben davon NICHTS in eine .h hinein,
weil wir weder A noch Blabla exportieren wollen.
1
intA;
2
intBlabla(void)
3
{....
4
}
Quelle "zwei.c": hier verschaffen wir uns Zugang zu Zeugs in "eins.c"
durch Verwenden von "extern"
1
externintA;
2
externintBlabla(void);
3
4
intNaguckmal(void)
5
{intY;
6
Y=A*Blabla();
7
returnY;
8
}
Hier erzeugen wir einen Linkfehler:
1
intA;
2
3
intNaguckmal(intB)
4
{intY;
5
Y=A*B;
6
returnY;
7
}
So. In "zwei.c" greifen wir einfach auf Internes von "eins.c" zu, ohne
daß dieses in "eins.h" jemals erwähnt wurde. Das bedeutet, daß es eben
keine funktionierende Kapselung in C gibt - und meines Wissens kann man
auch nicht die Funktion Blabla als "static" deklarieren.
In Pascal hingegen sind solche Dinge einfach nicht möglich - eben wegen
des Konzeptes von Interface und Implementation. Das ist ein ziemlich
heftiger Unterschied zu C.
Es sind eben doch Welten zwischen C und Pascal - und es ist eben NICHT
die "genau .. gleiche Funktionalität".
Man könnte als anderes Beispiel heranziehen, daß es in Pascal
Parameterübergabe als Wert oder Referenz gibt, seit einiger Zeit auch
als dedizierten Input oder Output und daß es so etwas bei C eben
überhaupt nicht gibt und man sich deshalb mit Zeiger-Übergaben behelfen
muß. Es wäre auch da schlichtweg grundfalsch, von "genau gleicher
Funktionalität" reden zu wollen.
Kapierst du nun das Ganze?
Ich nehme mal an, daß deine Pascal-Kenntnisse ziemlich eingerostet sind,
ist ja auch kein Wunder, wenn man Dinge vergißt, die man zu lange nicht
ausgeübt hat. Aber sei nicht so störrisch und versuche nicht, zu
rechthabern. Ich weiß das nämlich wirklich besser als du.
W.S.
W.S. schrieb:> und meines Wissens kann man> auch nicht die Funktion Blabla als "static" deklarieren.
Das zeigt nur den wahren Unterschied zwischen C und Pascal. C lässt mehr
zu, ist dafür aber nicht idiotensicher. Gemeinsam haben beide, daß, wenn
man die Sprache nicht kennt, und nicht weiß, was man tut, es
fürchterlich schief gehen kann.
Oliver
P.S Selbstvertändlich kann man Funktionen in C als static deklarieren.
W.S. schrieb:> Du redest noch immer Unsinn. Was du sagst, ist schlichtweg FALSCH.W.S. schrieb:> meines Wissens kann man> auch nicht die Funktion Blabla als "static" deklarieren.
Du lehnst dich aber schon etwas weit aus dem Fenster, oder?
Michael R. schrieb:> W.S. schrieb:>> Du redest noch immer Unsinn. Was du sagst, ist schlichtweg FALSCH.>> W.S. schrieb:>> meines Wissens kann man>> auch nicht die Funktion Blabla als "static" deklarieren.>> Du lehnst dich aber schon etwas weit aus dem Fenster, oder?
Leider gibt es Leute, die sich nicht die Mühe machen, die Beiträge
anderer (etwa Johanns, Rufus' oder auch meinen) zu lesen und auch mal
darüber nach zu denken. Stattdessen wird immer dasselbe in immer neuen
Worthülsen wiederholt ... Schade, aber wohl nicht zu ändern.
W.S. schrieb:> So. In "zwei.c" greifen wir einfach auf Internes von "eins.c" zu, ohne> daß dieses in "eins.h" jemals erwähnt wurde. Das bedeutet, daß es eben> keine funktionierende Kapselung in C gibt - und meines Wissens kann man> auch nicht die Funktion Blabla als "static" deklarieren.
Natürlich gibt es die, nämlich mittels des Schlüsselworts static.
Damit sind wir sogar fast wieder zum Thread-Thema zurückgekehrt.
W.S. schrieb:> Du redest noch immer Unsinn. Was du sagst, ist schlichtweg FALSCH. Ich> geb dir mal ein Beispiel:>> Quelle "eins.c":> hier deklarieren wir ne Variable und ne Funktion> aber wir schreiben davon NICHTS in eine .h hinein, weil wir weder A noch> Blabla exportieren wollen.
1
intA;
2
intBlabla(void)
3
{....
4
}
Der Fehler liegt hier bei Dir und Deiner konsequenten Weigerung, C
lernen zu wollen.
Würdest Du weder A noch Blabla exportieren wollen, müsstest Du das hier
schreiben:
1
staticintA;
2
staticintBlabla(void)
3
{....
4
}
Wie wäre es, wenn Du einfach mal ein Buch über C lesen würdest?
W.S. schrieb:> Du redest noch immer Unsinn. Was du sagst, ist schlichtweg FALSCH.
Nein, genau das trifft auf dich zu!
> und meines Wissens kann man auch nicht die Funktion Blabla als "static"> deklarieren.
Dann ist dein Wissen falsch, um nicht zu sagen: kompletter Blödsinn!
Natürlich kann man Funktionen als static deklarieren, und dann ist sie
auch über eine extern-Deklaration in einem anderen C-File nicht
erreichbar. Gleiches gilt im übrigen für die Variable. Also exakt das,
von dem du felsenfest behauptest, es sei in C nicht möglich.
Wenn du schon so laut tönst, solltest du wenigstens ein bisschen Ahnung
von dem haben, wovon du sprichst, sonst machst du dich ziemlich
lächerlich.
> Ich nehme mal an, daß deine Pascal-Kenntnisse ziemlich eingerostet sind,> ist ja auch kein Wunder, wenn man Dinge vergißt, die man zu lange nicht> ausgeübt hat. Aber sei nicht so störrisch und versuche nicht, zu> rechthabern. Ich weiß das nämlich wirklich besser als du.
Die drei Sätze sind in dem Kontext nun echt der Abschuss. Ersetze
"Pascal" durch "C", und es trifft perfekt auf dich zu.