Hallo liebe Community !
ich habe eine Frage zu Sprüngen von Funktion zu Funktion.
Mein Hauptprogramm zum Einlesen eines Protokolls besteht Großteils aus
Funktionen.
while (1)
{
praemabel();
startbit();
Einlesen1();
Zwischenbit();
Einlesen2();
Wenn eine Funktion durchlaufen wurde, erfolgt der Rücksprung in das
Hauptprogramm und ruft dadurch die Funktion in der nächsten Zeile auf.
Ich möchte die Funktion "Zwischenbit" nun entweder durchlaufen und
dadurch in "Einlesen2" springen, oder es erfolgt ein Sprung innerhalb
der Funktion "Zwischenbit" zurück in "praemabel".
Ich bin nur nicht sicher was passiert, wenn ich aus einer Funktion in
eine andere Funktion Springe.
Muss/Sollte eine Funktion immer "durchlaufen/abgeschlossen" werden ?
Hat er die Ursprungsfunktion "Zwischenbit" noch irgendwo hinterlegt, was
später zu einem ungewollten Rücksprung zu "Zwischenbit" führen kann?
void Zwischenbit(void)
{
while (dcccount == 0)
{
}
if (dccbit == 1)
praemabel(); <- Hier wird ja die Funktion "Zwischenbit" nie beendet ?
}
Ich hoffe, die Frage verständlich formuliert zu haben.
Danke für eure Hilfe !!
Philipp L. schrieb:> praemabel(); <- Hier wird ja die Funktion "Zwischenbit" nie beendet ?
Nur, wenn die Funktion praemabel() eine Endlasschleife darstellt, wird
sie nicht beendet. Ansonsten führt sie am Ende einen Rücksprung durch
und landet in der nächsten Zeile nach ihrem Aufruf.
NB: Schreibt man das nicht praeambel()?
NB2: Bist du sicher, das du gleich mit einem DCC Dekoder anfangen
willst? Mach doch erstmal was einfacheres.
Philipp L. schrieb:> Ich bin nur nicht sicher was passiert, wenn ich aus einer Funktion in> eine andere Funktion Springe.
Die Aufrufe werden verschachtelt.
Philipp L. schrieb:> Hat er die Ursprungsfunktion "Zwischenbit" noch irgendwo hinterlegt
Ja! Auf dem Stack.
Gebe Zwischenbit einen Rückgabewert. In der main() prüfst du den und
rufst dementsprechend Einlesen2() auf oder springst zurück an den Anfang
der Schleife.
Es ist zwar theoretisch möglich mit setjmp() oder
Inline-Assembler-Tricks direkt in eine andere Funktion zu springen, das
bewirkt aber einen unübersichtlichen unsauberen Programmablauf. Besser
ist es, Funktionen normal zu durchlaufen und über Rückgabewerte zu
signalisieren, was danach passieren soll.
Was ist eine "Praemabel"? Meintest du vielleicht "Praeambel"?
Ich würde sowas als Automat mit einer case-Struktur aufbauen, wobei du
den case-pointer ja in deinen Unterfunktionen dann beliebig verändern
kannst.
> NB2: Bist du sicher, das du gleich mit einem DCC Dekoder anfangen> willst? Mach doch erstmal was einfacheres.
Jep, bin sicher :-)
Das Einlesen des ersten Bytes und dessen Adressauswertung funktioniert
auch schon.
Muss jetzt nur noch die nächsten Bytes einlesen.
> Gebe Zwischenbit einen Rückgabewert. In der main() prüfst du den und> rufst dementsprechend Einlesen2()
Das mit den Rückgabewerten habe ich zwar durchgelesen, aber es
funktioniert noch nicht wirklich.
Hier mal ein Test mit einem festen Rückgabewert von 5
uint8_t Zwischenbit(void)
{
while (startcount == 0)
{
}
return 5;
}
Nach durchlaufen der Funktion müsste doch in "Zwischenbit" der Wert 5
gespeichert sein.
Wenn ich dies aber in der Main prüfen möchte, erscheint der Fehler:
comparison between pointer and integer.
if (Zwischenbit == 5)
PORT_B1 = 1;
else
PORT_B1 = 0;
Kann ich also die Funktion Zwischenbit nicht als Wert abfragen?
Danke!!
Philipp L. schrieb:> Nach durchlaufen der Funktion müsste doch in "Zwischenbit" der Wert 5> gespeichert sein.
Der wird nicht "in Zwischenbit" gespeichert. Der ist temporär als Wert
des Ausdrucks "Zwischenbit()" abgelegt.
Philipp L. schrieb:> if (Zwischenbit == 5)
Das ist Blödsinn. Du vergleichst die Adresse der Funktion im Speicher
mit 5.
So geht es:
1
while(1){
2
praemabel();
3
startbit();
4
Einlesen1();
5
if(Zwischenbit()==5)
6
Einlesen2();
7
}
Du solltest dringend ein Buch über die absoluten Grundlagen lesen. So
klappt das nicht.
Philipp L. schrieb:> Kann ich also die Funktion Zwischenbit nicht als Wert abfragen?
Du solltest Dir mal Dein C-Buch zu Gemüte nehmen. Dir fehlen eklatante
Grundlagen an sehr elementaren Stellen.
Wie wird in C eine Funktion aufgerufen? Wie sieht die Syntax dafür aus?
Ich halte es nicht für sinnvoll, Dir hier mit Detailkorrektoren
"weiterzuhelfen", denn damit lernst Du die Dir fehlenden Grundlagen
nicht, sondern kopierst nur munter weiter irgendwo gefundene
Versatzstücke ohne Verständnis zusammen.
Das mag jetzt vielleicht unfreundlich und hart klingen, ist aber so.
> Du solltest Dir mal Dein C-Buch zu Gemüte nehmen.
Ist ständig neben dem Rechner und hilft mir an vielen Stellen weiter.
> if (Zwischenbit() == 5)> Einlesen2();
Danke!
Ich konnte auch mit Hilfe des Buches nicht herausfinden, dass ich eine
Funktion auch innerhalb der If-Bedingung aufrufen kann.
>Dir fehlen eklatante Grundlagen an sehr elementaren Stellen.
Das ist korrekt und wird mir auch noch an anderen Stellen begegnen.
Dafür fange ich ja mit C erst an.
ich lerne aber lieber anhand von konkreten Projekten, innerhalb dessen
ich dann verschiedene Themen erlerne.
z.B. hat es mich sehr gefreut, dass ich das Protokoll bis zum ersten
Byte lesen kann und dies auch wunderbar funktioniert.
Natürlich habe ich nebenbei auch kleine Testprogramme, in denen ich
bestimmte Grundlagen außerhalb des Hauptprojektes teste und dazu lerne.
Philipp L. schrieb:> Ich konnte auch mit Hilfe des Buches nicht herausfinden, dass ich eine> Funktion auch innerhalb der If-Bedingung aufrufen kann.
Dann ist das ein ziemlich schlechtes Buch... Das Problem an C und C++
ist dass es sehr viele Fallen gibt. Man kann alle möglichen Dinge
hinschreiben die den Anschein haben zu funktionieren, aber trotzdem
falsch sind und früher oder später dann nicht mehr gehen. Wenn man nur
mit Try&Error lernt, lernt man diese Dinge nicht.
> Man kann alle möglichen Dinge> hinschreiben die den Anschein haben zu funktionieren, aber trotzdem> falsch sind und früher oder später dann nicht mehr gehen. Wenn man nur> mit Try&Error lernt, lernt man diese Dinge nicht.
Wie kann denn ein Anfänger sonst etwas lernen.
Ich teste Befehle anhand von kleinen Testprojekten.
Wenn es funktioniert und ich der Meinung bin es verstanden zu haben,
dann kommt das nächste Thema dran.
Alle bis dahin verstanden Dinge werden verwendet.
Philipp L. schrieb:> Wie kann denn ein Anfänger sonst etwas lernen.
Mit einer weniger heimtückischen Sprache anfangen (Ruby, Python, Java)
um die Grundlagen der Programmierung zu lernen. Dann ein C-Buch von
vorne bis hinten durcharbeiten und die Übungen machen, aber dabei nicht
vorgreifen, weil man dadurch ggf. falsche Annahmen über die Ergebnisse
eines Experiments macht.
Ich glaube in C würde ich es lassen, erzeugt dort wahrscheinlich mehr
Probleme als es an Nutzen bringt.
In Assembler kannst Du jede Schweinerei machen, zu der der Controller
fähig ist. Also wenn Du dem Controller sagst JMP von einer einer
Funktion irgendwo hin, dann macht der das. Ob man es tatsächlich so
machen sollte, auch mit Hinblick darauf, daß evtl. Teile des Codes in
anderen späteren Projekten wiederverwendet werden könnten, finde ich
aber auch fragwürdig. Also wenn keine Notwendigkeit besteht, auf einzig
und allein diese Weise die letzten 20 Byte Flash zu bekommen die man
noch braucht, dann würde ich es nicht machen.
Dr. Sommer schrieb:> Mit einer weniger heimtückischen Sprache anfangen (Ruby, Python, Java)> um die Grundlagen der Programmierung zu lernen.
Dann fehlt einem aber später evtl. die Motivation sich mit solch
seltsamen mühsamen Kram wie C zu beschäftigen ;-)
Einfach mal machen passt schon. Ist ja offensichtlich Hobby. Und das
Verständnis kommt schon nebenbei.
@ Phillip
Man kann so lernen, aber es ist äusserst mühsam und erfordert, dass man
streng zwischen den ausdrücklichen Aussagen des Buches und seinen
eigenen Vermutungen bzw. Schlussfolgerungen unterscheidet.
Das ist Dir in dem Fall von
>Wenn ich dies aber in der Main prüfen möchte, erscheint der Fehler:> comparison between pointer and integer.
1
if(Zwischenbit==5)
2
PORT_B1=1;
3
else
4
PORT_B1=0;
> Kann ich also die Funktion Zwischenbit nicht als Wert abfragen?
nicht gelungen.
Es wird nämlich in dem ganzen C-Buch nicht behauptet werden, - wenn es
nicht schlichter Müll ist -, dass der Funktionsname für den Rückgabewert
dieser Funktion steht.
Das ist also nur Deine eigene Vermutung gewesen, der Du Dir entweder
nicht bewusst warst, oder die Du nicht hinterfragt hast, als der
Compiler die Fehlermeldung ausgab.
Die Fehlermeldung sagt implizit aber eindeutig, dass der Funktionsname
einem Zeiger entspricht und nicht, wie von Dir erwartet, einem Integer.
Verstehe mich recht: Ich behaupte nicht, dass Deine Vermutung von vorne
herein unplausibel war. Solche Vermutungen hängen aber stark von den
eigenen Vorerfahrungen ab und können also fallweise völlig daneben
gehen.
Wenn Du also mit Deiner Methode einigermaßen effizient lernen willst,
musst Du konsequent vorgehen. Du musst streng zwischen dem, was in dem
Buch steht und Deinen Vermutungen darüber unterscheiden, was nicht darin
steht oder was Du einfach noch nicht gelesen hast. Und Du musst Deine
Vermutungen hinterfragen, spätestens wenn sie offensichtlich nicht
zutreffen.
Kürzer und noch effizienter ist der Weg, ein Kapitel bzw. ein
Unterkapitel zunächst vollständig zu erfassen und zu verstehen.
Noch besser, der Weg, dann jeweils die Übungsaufgaben zu machen.
Und noch besser, zunächst mal das ganze Buch zu lesen - erst einmal um
einen Überblick zu bekommen und dann noch einmal, um die einzelnen
Punkte nachzuvollziehen.
Wir kommen hier mittels Text nicht weiter.
Das ist eine Grundsatzdiskussion, für die du mich kennen müsstest oder
wir ein Gespräch führen müssten.
Es ist schön, hier immer gute Antworten zu bekommen.
Dr. Sommer schrieb:> Du solltest dringend ein Buch über die absoluten Grundlagen lesen. So> klappt das nicht.
In diesem Fall wird es schon helfen, alle Warnungen des Compilers zu
aktivieren und dann auch zu beachten.
1
-Wall
bzw. in der Arduino IDE unter Voreinstellungen/Compiler Warnungen: alle.
Es gibt keinen vernünftigen Grund, Compiler-Warnungen zu ignorieren.
Deswegen gibt es auch keinen vernünftigen Grund, sie (wie Arduino es
vorgibt) nicht zu aktivieren.
Jede Warnung kann man mit den Mitteln der Sprache loswerden. Und das
sollte man auch tun - auch wenn es Mehrarbeit bedeutet. Das gilt im
Übrigen auch für alle anderen Programmiersprachen.
Moin,
Theor schrieb:> Wenn Du also mit Deiner Methode einigermaßen effizient lernen willst,Theor schrieb:> Du musst streng zwischen dem, was in dem> Buch steht und Deinen Vermutungen darüber unterscheiden,Theor schrieb:> Kürzer und noch effizienter ist der Weg, ein Kapitel bzw. ein> Unterkapitel zunächst vollständig zu erfassen und zu verstehen.> Noch besser, der Weg, dann jeweils die Übungsaufgaben zu machen.
Und wo bleibt der Spassfaktor dabei? Ich nehm' hier mal stark an, dass
es sich um hobbymaessiges programmierenlernen handelt und nicht darum,
als Absolvent eines Informatikstudienganges mit Schwerpunkt
Theaterwissenschaften/Politologie jetzt moeglichst schnell und effektiv
Sicherheitssysteme fuer Atomraketenzuendsysteme programmieren zu
muessen.
Ja, mir haben sich auch die Zehennaegel aufgerollt - aber hey - es ist
noch kein Meister vom Himmel gefallen; natuerlich programmiert man
ordentlichen Stuss am Anfang, und wenn man viel Glueck hat, wird der
Stussanteil spaeter kleiner.
Philipp L. schrieb:> Wie kann denn ein Anfänger sonst etwas lernen.> Ich teste Befehle anhand von kleinen Testprojekten.>> Wenn es funktioniert und ich der Meinung bin es verstanden zu haben,> dann kommt das nächste Thema dran.> Alle bis dahin verstanden Dinge werden verwendet.
Passt. Weiter so!
Gruss
WK
Zum Ablauf des Programms - das hat nichts mit C zu tun - beschäftige
dich mal mit Statemachines (zu Deutsch Zustandsautomaten). Damit regelt
man das Lesen und Schreiben komplizierterer Protokolle.
Zu deiner Lern"methode": Versuch und Irrtum bringt nur sehr begrenzt
etwas. Besonders, wenn du dir darauf aufbauend deine Privat-Philosophie
zu einer Programmiersprache zusammenreimst. Die Grundkonzepte lernen,
die Details intensiv studieren funktioniert besser, als sich etwas
zusammenzuträumen.
Das hat nichts mit C als Sprache zu tun. C ist auch nicht heimtückisch.
C ist ziemlich simpel. Das als heimtückisch zu bezeichnen zeugt von
einer gewissen Boshaftigkeit.
>> (Ruby,
Ruby (eine Hipster-Programmiersprache die sich auf dem absteigenden Ast
befindet) unterstützt mehrere Programmierparadigmen. Viel Spaß die als
Anfänger zu lernen und auszusortieren was man braucht.
>> Python
Eine der Hipster-Programmiersprachen schlechthin. Viel Spaß beim
Abzählen von Whitespaces. Dazu eine chaotische Anzahl von Bibliotheken
die zu 9o% sowieso nur schlecht dokumentierte Wrapper sind.
>> Java)
Verwendet im Kern immer noch eine C-Syntax. Kommt schon in der
Standard-Edition mit einem API von über 4000 Klassen. Davon sollte man
schon ein paar hundert kennen wenn man als Java-Programmierer durchgehen
möchte.
Hannes J. schrieb:> Das hat nichts mit C als Sprache zu tun. C ist auch nicht heimtückisch.
Dann erkläre doch mal kurz und verständlich, was die drei Bedeutungen
des "*" sind :-) Oder was "void (*fptr) (const volatile short*, void (*)
(void), void*, int[]) bedeutet.
Oder was an if(a << 15) falsch ist. Oder wie man Fehlerbehandlung ohne
Speicherlecks hinbekommt. Oder wie man Strings ohne Sicherheitslücken
verarbeitet. ...
Hannes J. schrieb:> unterstützt mehrere Programmierparadigma
Das ist sowieso sehr sinnvoll. Man sollte mehrere verbreitete Paradigmen
kennenlernen damit man fürs konkrete Projekt die geeigneten aussuchen
kann. Die meisten Sprachen unterstützen mehrere Paradigmen.
Hannes J. schrieb:> Viel Spaß beim> Abzählen von Whitespaces.
Das erzwungene korrekte Einrücken ist sehr sinnvoll für Anfänger - so
muss man es direkt richtig machen.
Hannes J. schrieb:> Verwendet im Kern immer noch eine C-Syntax.
Die Syntax ist unwichtig. Die Sematik ist komplett anders.
Hannes J. schrieb:> Davon sollte man> schon ein paar hundert kennen wenn man als Java-Programmierer durchgehen> möchte.
Und das ist schlimmer als die Hundert C-Funktionen die man als
C-Programmierer können muss?
Hannes J. schrieb:>>> Python>> Eine der Hipster-Programmiersprachen schlechthin. Viel Spaß beim> Abzählen von Whitespaces.
Ist von Vorteil einen guten Texteditor zu nutzen. Dann kann man mühelos
gut formatierte Listings tippen.
Und das Ergebnis ist dann lesbarer als bei den C Freaks die das halbe
Programm in eine Zeile quetschen. Und ab Ende stürzt der Kram ständig ab
weil sie bei ihrer ganzen Pointerhampelei den Überblick in ihrem Chaos
verloren haben ;-)
PS: Ja, auch in C kann man richtig, das ist mir klar. Mir ist auch klar
dasan in C++ einfache Stringverarbeitung machen kann ohne in 40 Zeilen
Quellcode mit Pointern rumzuspielen.
Das ging hier eindeutig gegen Programmierer die der Meinung sind
übersichtlicher Quellcode ist für Hipster. Deren C Quellcode kann halt
keine Sau lesen.
Dr. Sommer schrieb:>> Das hat nichts mit C als Sprache zu tun. C ist auch nicht heimtückisch.>> Dann erkläre doch mal kurz und verständlich, was die drei Bedeutungen> des "*" sind :-)
Multiplikationsoperator:
1
a=4*5;
Vier mal fünf.
Pointerdefinition/Deklaration:
1
int*p;
p ist ein Pointer auf int, also eine Variable, die auf eine andere
Variable vom Typ int zeigt.
Hier ist p nicht initialisiert, d.h. p zeigt irgendwohin.
1
inta;
2
int*p=&p;
In diesem Beispiel wird p so initialisiert, daß es auf die Variable a
zeigt.
Dereferenzierungsoperator:
1
inta=4;
2
intb;
3
int*p=&a;
4
5
b=*p;
Hier wird der Pointer dereferenziert, d.h. der Wert bestimmt, auf den
der Pointer zeigt.
Welcher Wert landet jetzt in b?
> Oder was "void (*fptr) (const volatile short*, void (*) (void), void*, int[])
bedeutet.
Das ist ein Funktionspointer, der auf eine Funktion mit der Signatur
verweist.
Der zweite Parameter der Funktion ist wiederum ein Funktionspointer auf
eine Funktion mit der Signatur
1
voidfunktion(void)
Alle anderen Parameter der Funktion sind Pointer auf verschiedene
Datentypen.
Die Funktion hat keinen Rückgabewert.
Anstelle des letzten Parameters könnte man genausgut int* schreiben, da
das in diesem Kontext exakt dasselbe bedeutet.
> Oder was an if(a << 15) falsch ist.
Falsch ist es nicht, es macht nur selten das, was der Schreiber erwartet
und das Verhalten hängt von den Umgebungsbedingungen ab.
> Oder wie man Fehlerbehandlung ohne> Speicherlecks hinbekommt.
?
> Oder wie man Strings ohne Sicherheitslücken> verarbeitet. ...
Indem man die richtigen Funktionen dafür verwendet, bzw. versteht, wo
die Probleme liegen.
Naja, in C ist Endrekursion eben kein Bestandteil der
Sprachdefinition.
Aber viele C-Compiler unterstützen Endrekursion. Allerdings ist das Eis
eben recht dünn, auf das man sich begibt, da:
a.) man Funktionen natürlich endrekursiv schreiben muss und
weder eine Warnung noch Fehlermeldung vom Compiler bekommt,
wenn man es (aus Versehen) nicht tut,
b.) man die Tail-Call-Optimierung immer einschalten muss,
c.) bei einem Compiler-Wechsel oder einem Debug-Build
einem alles um die Ohren fliegen kann.
Aber vermutlich wird diese Info vielen Schreiberlingen und Lesern hier
leider nichts nützen.
Schade eigentlich...
Moin,
Also bei so vielen Bedenkentraegern wundert's mich nicht, dass wir
technologisch den Anschluss verloren haben.
Wenn ich C lerne, was kratzen mich denn am Anfang die droelfzehn
Bedeutungen des "*"? Was kratzen mich wilde Funktionspointerkonstrukte?
Das braucht kein Mensch am Anfang.
Wenn ich mir vorstelle, als ich vor ueber einem drittel Jahrhundert am
ZX81 fast 24/7 am Werkeln war, da waere einer angekommen, der haette
gefragt: "Oh - du programmierst den ja in BASIC - Wieviele
Programmierparadigmen unterstuetzt das denn?" Und haette dann die Stirn
in Sorgenfalten gelegt, ob meiner sicher unqualifizierten Antwort...
Bloss gut, dass es damals noch keine Internetforen gab und meine Eltern
eher die Sorge hatten, dass das Kind auch mal 'nen Apfel ist und in der
Schlule nicht noch schlechter wird.
Gruss
WK
Dergute W. schrieb:> Das braucht kein Mensch am Anfang.
Die meisten Leute fangen aber nicht nur an.
Dergute W. schrieb:> Also bei so vielen Bedenkentraegern wundert's mich nicht, dass wir> technologisch den Anschluss verloren haben.
Die Logik verstehe ich nicht. Indem man nur die einfachste Technik
verwendet und auf alles komplexere verzichtet behält man den
technologischen Anschluss?
Die chinesische Technik-Leitkultur benutzt jedenfalls keine ZX81 und
auch kein Basic.
Dr. Sommer schrieb:> Oder was an if(a << 15) falsch ist. Oder wie man Fehlerbehandlung ohne> Speicherlecks hinbekommt. Oder wie man Strings ohne Sicherheitslücken> verarbeitet. ...
Kommt irgendwann die Auflösung? :-)
zitter_ned_aso schrieb:> Kommt irgendwann die Auflösung?
Wenn "a" ein signierter 16bit-Integertyp ist, ist der Shift
implementation defined.
Die Fehlerbehandlung ist m.E. nicht gut zu lösen. Gängige Ansätze nutzen
"goto". In vernünftigen Programmiersprachen gibt's dafür RAII und
Exceptions.
Das mit den Strings ist komplexer zu erklären, findet man aber bei
Google.
Moin,
Dr. Sommer schrieb:> Die meisten Leute fangen aber nicht nur an.
Da ich hier in diesem Thread schreibe, beziehe ich mich mehr auf den
Threaderoeffner als auf "die meisten Leute".
Dr. Sommer schrieb:> Die Logik verstehe ich nicht. Indem man nur die einfachste Technik> verwendet und auf alles komplexere verzichtet behält man den> technologischen Anschluss?
Ich meine es so, dass man am Anfang durchaus auch mit einfacher Technik
und "spielerischem" Ansatz arbeiten sollte, und nicht gleich alles
extrem verkopft angehen.
Gruss
WK
Dr. Sommer schrieb:> Wenn "a" ein signierter 16bit-Integertyp ist, ist der Shift> implementation defined.
Nope. Das ist bei *right*-shift so. Hat auch nichts mit 16 Bit zu tun.
leo
leo schrieb:> Nope. Das ist bei *right*-shift so.
Was ist denn dann 1<<15 auf einer Architektur mit 2er-Komplement, und
auf einer mit 1er-Komplement? Beides mal das Gleiche?
Die 15 im Beispiel hat was mit der 16 zu tun. Bei 32bit-Integern wäre es
dann problematisch bei 31.
Philipp L. schrieb:> Wenn eine Funktion durchlaufen wurde, erfolgt der Rücksprung in das> Hauptprogramm und ruft dadurch die Funktion in der nächsten Zeile auf.
Korrekt.
Jede Funktion endet hinter ihrem Aufruf.
Philipp L. schrieb:> Ich möchte die Funktion "Zwischenbit" nun entweder durchlaufen und> dadurch in "Einlesen2" springen, oder es erfolgt ein Sprung innerhalb> der Funktion "Zwischenbit" zurück in "praemabel".
Wo ist das Problem, einfach die Bedingung testen:
1
if(Bedingung)
2
Einlesen2();
Wird die Bedingung in "Zwischenbit" generiert, kann man sie einfach als
Returnwert übergeben:
Philipp L. schrieb:> praemabel(); <- Hier wird ja die Funktion "Zwischenbit" nie beendet ?
Stimmt, es kommt zu einer Rekursion, bis der Stack überläuft.
Dr. Sommer schrieb:>> Nope. Das ist bei *right*-shift so.>> Was ist denn dann 1<<15 auf einer Architektur mit 2er-Komplement, und> auf einer mit 1er-Komplement? Beides mal das Gleiche?Left shift schiebt 0 herein und ist eindeutig. Right shift is meist
arithmetisch aber eben nicht immer.
leo
Peter D. schrieb:> Philipp L. schrieb:>> praemabel(); <- Hier wird ja die Funktion "Zwischenbit" nie beendet ?>> Stimmt, es kommt zu einer Rekursion, bis der Stack überläuft.
Quatsch, sie wird natürlich nach "praemabel" beendet.
"praemabel" wird also zweimal ausgeführt.
Nur zur Info,
mir ist (und war von Anfang an) bewusst, dass meine Struktur und Ablauf
für das Einlesen und die Auswertung des Protokolls bestimmt nicht dem
bestmöglichen Code entspricht.
Aber darum geht es auch momentan noch nicht.
Ich möchte einfach nach und nach meinen Wissensstand erweitern und dies
anhand von praktischen Projekten mit Spaß umsetzen.
wenn ich mir erst alle Möglichkeiten einer Programmiersprache aneignen
soll, bevor ich dies an einem konkreten Projekt umsetze, macht das
einfach keinen Spaß.
Ich habe einige Punkte auf dem Zettel, welche ich unbedingt noch lernen
möchte und die meine Programme besser machen (z.B. Array`s)
Aber ich muss erstmal bisher gelerntes anwenden.
Philipp L. schrieb:> Ich bin nur nicht sicher was passiert, wenn ich aus einer Funktion in> eine andere Funktion Springe.
Das läßt C so gar nicht zu, wie Du das willst, und das ist auch gut so.
Der funktionierende Trick ist, daß Du Deine Funktionen mit
Rückgabewerten versiehst und dann abhängig von den Rückgabewerten eine
Folgefunktion aufrufst oder auch nicht.
Übersichtlich wird es, wenn Du das als endlichen Automaten machst, wie
Helmut schon erwähnt hatte, nur daß ich das hier nicht mit
Funktionspointern machen würde.
Endliche Automaten sind gerade embedded und bei Protokoll-Sachen ein
sehr nützliches und verbreitetes Konstrukt. Tante Google bemühen und
dann erst mit der C-Umsetzung weitermachen.
> Endliche Automaten sind gerade embedded und bei Protokoll-Sachen ein> sehr nützliches und verbreitetes Konstrukt.
Jep, das werde ich machen.
Es gibt ganz viele tolle Sachen, welche ich mir auch noch ansehen werde.
PS:
Mein DCC Programm ist jetzt "fertig".
Es liest die Bytes nacheinander in Variablen ein.
Das ganze läuft super, wenn auch bestimmt nicht optimal programmiert.
Aber ich hatte Spaß und wieder etwas gelernt.
ich brauche 510 Bytes für die Protokollauswertung.
Dr. Sommer schrieb:> Also wenn ich folgendes für i386 kompiliere> #include <stdio.h>>> int main () {> printf ("%d\n", (1 << 31));> }> kommt -2147483648 heraus.
Ja, erwartungsgemaess 0x8000.
> Gilt das auch für Rechner mit 1er-Komplement> oder Sign-And-Magnitude?
Das hat mit dem "Rechner" nichts zu tun und ist was anderes.
1
$catshift.c
2
#include<stdio.h>
3
#include<stdlib.h>
4
intmain(intargc,char*argv[]){
5
printf("%d\n",(-8)>>(argc>1?atoi(argv[1]):0));
6
return0;
7
}
8
$gccshift.c&&./a.out
9
-8
10
$gccshift.c&&./a.out10
11
-1
12
$
war dein urspruengliches Problem, i.e. der Shift-Rechts von negativen
Zahlen.
Philipp L. schrieb:> Ich bin nur nicht sicher was passiert, wenn ich aus einer Funktion in> eine andere Funktion Springe.
machbar ist das, aber du musst auch eine Ausstiegsstrategie haben, nicht
das du eine Endlosschleife programmierst!
Statemachine wurde ja schon genannt!
Auch rekursive Aufrufe sind möglich bis der Stack am Ende ist!
also immer schön auf Überlauf prüfen.
Joachim B. schrieb:> Philipp L. schrieb:>> Ich bin nur nicht sicher was passiert, wenn ich aus einer Funktion in>> eine andere Funktion Springe.
Du springst nicht in eine Funktion (zumindest nicht irgendwo hin), du
rufst diese auf - von Anfang an.
1
intf_2{
2
return3;
3
}
4
5
intmain(){// eine Funktion
6
intergebnis=f_2()// andere Funktion
7
...
8
}
Die Main-Funktion wird halt vom Startup-Code aufgerufen.
leo
leo schrieb:>> Was ist denn dann 1<<15 auf einer Architektur mit 2er-Komplement, und>> auf einer mit 1er-Komplement? Beides mal das Gleiche?>> Left shift schiebt 0 herein und ist eindeutig.
Das mit den Nullen stimmt, universell eindeutig ist es aber nicht.
1 << 15 ergibt ohne Beschränkung der Bits 32768. Dieser Wert ist in
einem 16-Bit Typ nicht mit Vorzeichen darstellbar. Folglich ist diese
Operation auf 16-Bit Maschinen undefiniert. Egal ob 2er Komplement,
1er-Komplement oder sonstwie.
Hingegen ist x << 15 für vorzeichenloses x stets definiert.
A. K. schrieb:> 1 << 15 ergibt ohne Beschränkung der Bits 32768. Dieser Wert ist in> einem 16-Bit Typ nicht mit Vorzeichen darstellbar
Na doch oder?
Zahlenbereich bei n-Bits:
von -2^(n-1) bis 2^(n-1)-1
Was bei 16 Bits folgenden Zahlenbereich ergibt:
-32768 bis 32767
zitter_ned_aso schrieb:> Was bei 16 Bits folgenden Zahlenbereich ergibt:>> -32768 bis 32767
Zeit fürs Bettchen? Seit wann ist 32768 in diesem Bereich enthalten? In
der Elektrik gibt es zwar die Regel "Rot ist Blau und Plus ist Minus"
aber nicht in C.
Ok, besser formuliert: "Dieser Wert ist in einem 16-Bit Typ mit
Vorzeichen nicht darstellbar."
zitter_ned_aso schrieb:> aber 1<<15 liefert doch die kleinste 16-Bit Zahl (Zweierkomplement)
Mathematisch gesehen kann bei x << y und positivem x nie ein negatives
Ergebnis rauskommen. Also erst unbeschränkt rechnen, dann ins Format zu
quetschen versuchen. So denkt C. Im Sinn von C wird auch ein
ausschliesslich inkrementierter positiver Wert bei vorzeichenbehafteter
Rechnung nie negativ. Auf der Maschine vielleicht schon, aber das
zählt nicht. Es kann auch eine Exception geben und die Maschine darf
dich ohrfeigen.
"The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated
bits are filled with zeros. [...] If E1 has a signed type and
nonnegative value, and E1 × 2 E2 is representable in the result type,
then that is the resulting value; otherwise, the behavior is undefined."
A. K. schrieb:>> Left shift schiebt 0 herein und ist eindeutig.>> Das mit den Nullen stimmt, universell eindeutig ist es aber nicht.>> 1 << 15 ergibt ohne Beschränkung der Bits 32768. Dieser Wert ist in> einem 16-Bit Typ nicht mit Vorzeichen darstellbar.
Du wuerfelst hier {1.2}-Komplement und Left-shift einfach durcheinander.
Mit 32bit Integern:
1
$catshift.c
2
#include<stdio.h>
3
#include<stdlib.h>
4
intmain(intargc,char*argv[]){
5
printf("%x\n",1<<(argc>1?atoi(argv[1]):0));
6
return0;
7
}
8
$gccshift.c&&./a.out31
9
80000000
Was hiesse also nicht darstellbar?
Das Left-Shift ist eindeutig logisch.
Das wird generiert und ist eindeutig:
1
shl%cl,%edx
Die Interpretation des Ergebnisses koennte variieren, wenn man denn
glaubt, der Compiler koennte 1-Komplement generieren.
leo
A. K. schrieb:> Mathematisch gesehen kann bei x << y und positivem x nie ein negatives> Ergebnis rauskommen. Also erst unbeschränkt rechnen, dann ins Format zu> quetschen versuchen. So denkt C.
Wir sprachen hier von C.
Die Mathematik kann auch mit Unendlich (und mehr) umgehen. C wurde fuer
was anderes erfunden.
leo
leo schrieb:> Du wuerfelst hier {1.2}-Komplement und Left-shift einfach durcheinander.> Mit 32bit Integern:
1 << 15 ist auf einer Maschine mit 16-Bit "int" Typ undefiniert, es sei
denn sie codiert Integers in umgedrehtem Zweierkomplement
(-32767..32768).
A. K. schrieb:> universell eindeutig ist es aber nicht
Deutlicher: Es gibt C Plattformen, auf denen 1 << 15 nicht eindeutig
ist. Dass es auch Plattformen gibt, bei denen es eindeutig ist, ändert
daran nichts.
leo schrieb:> Wir sprachen hier von C.
Ich auch. Ich beschrieb die Art, wie der C Standard denkt (ich bin mit
damit leidlich vertraut). Du hingegen denkst nicht in C, sondern in
"Maschine".
A. K. schrieb:> A. K. schrieb:>> universell eindeutig ist es aber nicht>> Deutlicher: Es gibt C Plattformen, auf denen 1 << 15 nicht eindeutig> ist. Dass es auch Plattformen gibt, bei denen es eindeutig ist, ändert> daran nichts.
Ich bitte um einen C-Compiler + Plattform als Beispiel.
Danke, leo
leo schrieb:> Ich bitte um einen C-Compiler + Plattform als Beispiel.
Es geht mir um den C Standard, nicht um reale Compiler. Ein offiziell
als undefiniert festgelegtes Verhalten wird nicht dadurch definierter,
dass bei realen Compilern ein nachvollziehbares Ergebnis rauskommt.
Mir sind auch keine Compiler bekannt, bei denen bei einem Rechtsshift
etwas anderes als das Vorzeichen reingeschoben wird. Trotzdem ist das im
Standard undefiniert.
leo schrieb:>> Deutlicher: Es gibt C Plattformen, auf denen 1 << 15 nicht eindeutig>> ist.
C-Standard ist ja ok. Aber du schriebst obiges.
Also noch einmal: ich bitte um ein Beispiel.
Danke, leo
Heiko L. schrieb:> In irgendeinem YouTube-Video habe ich mal gehört, dass so um 1970 mal> eine Architektur gab, die das Einerkomplement genutzt hat.
Beispielsweise die CDC 6600 und Nachfolger. Seymour Cray war anfangs ein
Fan davon, warum auch immer.
Ansonsten: "Ones-complement arithmetic: it lives!"
http://esr.ibiblio.org/?p=7413
Ein Zweierkomplement-System:
1<<15 = 1*(2^15) (mit negativem Vorzeichen) =-32768 (dezimal)
Und auf einem Einerkomplement-System:
1<<15 = 1000 0000 .....
Eine 1 am Anfang - also ist die Zahl negativ. Jetzt invertieren.
0111.....1 = 32767. Und noch das Vorzeichen: - 32767
Und -32768 != -32767
Also gilt
a << #bits == a * 2^(#bits)
hier auch nicht?
A. K. schrieb:> Heiko L. schrieb:>> In irgendeinem YouTube-Video habe ich mal gehört, dass so um 1970 mal>> eine Architektur gab, die das Einerkomplement genutzt hat.>> Beispielsweise die CDC 6600 und Nachfolger. Seymour Cray war anfangs ein> Fan davon, warum auch immer.>> Ansonsten: "Ones-complement arithmetic: it lives!"> http://esr.ibiblio.org/?p=7413
Na, das dürfte einen Performance-Hit geben, wenn das Zweierkomplement
per Standard festgesetzt wird. Aber wer OS2200 für irgendeinen "COBOL
Compatibility Layer" laufen hat kann bestimmt auch ein paar
Programmierer bezahlen, die einen entsprechenden Switch in den Compiler
einbauen.
zitter_ned_aso schrieb:> Also gilt> a << #bits == a * 2^(#bits)> hier auch nicht?
Das kann auch noch interessanter werden, weil besagte CDC 6600 meistens
mit 60 Bits rechnete, nicht aber wenn sie multiplizieren und dividieren
sollte. Das machte sie nämlich nur mit 48 Bits, in der entsprechenden
Fliesskomma-Einheit in Mantissenbreite.
Ob es für diese jemals einen C Compiler gegeben hat weiss ich nicht. Da
allerdings der Wirth'sche Pascal Referenzcompiler als grösste Integer
2^48-1 definierte, wird im Handbuch vorsorglich erwähnt, dass es auch
grössere Integers als die grösste geben kann.
Shift-Operationen im 1er Komplement oder sign/magnitude erklären auch,
weshalb im Shifts im C Standard auf negative Werte undefiniert sind.
Philipp L. schrieb:> Mein Hauptprogramm zum Einlesen eines Protokolls besteht Großteils aus> Funktionen.>> while (1)> {> praemabel();> startbit();> Einlesen1();> Zwischenbit();> Einlesen2();>> Wenn eine Funktion durchlaufen wurde, erfolgt der Rücksprung in das> Hauptprogramm und ruft dadurch die Funktion in der nächsten Zeile auf.>> Ich möchte die Funktion "Zwischenbit" nun entweder durchlaufen und> dadurch in "Einlesen2" springen, oder es erfolgt ein Sprung innerhalb> der Funktion "Zwischenbit" zurück in "praemabel".
unabhängig was weiter oben steht und ob es erwähnt wurde
aber für soetwas gibt es statemaschines
versuch das ganze vlt als switch/case ding aufzubauen und eine einzige
funktion daraus zu machen.
zustandswechsel wenn alles richtig ist , sonst auf anfang oder in einen
sicheren zustand.
auch durchläufe zählen ( durch timeraufruf ) kann auch eine sicherheit
bei "halben"/falschen protokollen erhöhen
A. K. schrieb:> Beispielsweise die CDC 6600 und Nachfolger. Seymour Cray war anfangs ein> Fan davon, warum auch immer.
Oh, darauf habe ich FORTRAN programmiert, Einer-Komplement ist mir dabei
nie aufgefallen oder ich habs inzwischen vergessen.
Danke,
leo