Forum: Mikrocontroller und Digitale Elektronik Sprung aus Funktion zu Funktion ?


von Philipp L. (viech)


Lesenswert?

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 !!

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

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.

von Helmut -. (dc3yc)


Lesenswert?

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.

von Philipp L. (viech)


Lesenswert?

> 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!!

von Dr. Sommer (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Philipp L. (viech)


Lesenswert?

> 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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Philipp L. (viech)


Lesenswert?

> 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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von test (Gast)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

@ 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.

von Philipp L. (viech)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

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?

von test (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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
int a;
2
int *p = &p;

In diesem Beispiel wird p so initialisiert, daß es auf die Variable a 
zeigt.

Dereferenzierungsoperator:
1
int a = 4;
2
int b;
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
1
void funktion(const volatile short*, void (*) (void), void*, int[])

verweist.

Der zweite Parameter der Funktion ist wiederum ein Funktionspointer auf 
eine Funktion mit der Signatur
1
void funktion(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.

Beitrag #5730646 wurde von einem Moderator gelöscht.
von Dr. Sommer (Gast)


Lesenswert?

Na, hoffentlich hat er das jetzt so direkt verstanden...

von Experte (Gast)


Lesenswert?

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...

von Dergute W. (derguteweka)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

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.

von zitter_ned_aso (Gast)


Lesenswert?

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? :-)

von Dr. Sommer (Gast)


Lesenswert?

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.

von Dergute W. (derguteweka)


Lesenswert?

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

von leo (Gast)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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:
1
int main(void)
2
{
3
// ...
4
if (Zwischenbit() == true)
5
  Einlesen2();
6
// ...
7
}
8
9
bool Zwischenbit(void)
10
{
11
// ...
12
if (Bedingung)
13
  return true;
14
return false;
15
}

von Peter D. (peda)


Lesenswert?

Philipp L. schrieb:
> praemabel();   <- Hier wird ja die Funktion "Zwischenbit" nie beendet ?

Stimmt, es kommt zu einer Rekursion, bis der Stack überläuft.

von leo (Gast)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

leo schrieb:
> Left shift schiebt 0 herein und ist eindeutig.

Achso, dann hatte ich das falsch in Erinnerung. Wo steht das genau?

von Peter D. (peda)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

Also wenn ich folgendes für i386 kompiliere
1
#include <stdio.h>
2
3
int main () {
4
  printf ("%d\n", (1 << 31));
5
}
kommt -2147483648 heraus. Gilt das auch für Rechner mit 1er-Komplement 
oder Sign-And-Magnitude?

von Philipp L. (viech)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Philipp L. (viech)


Lesenswert?

> 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.

: Bearbeitet durch User
von leo (Gast)


Lesenswert?

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
$ cat shift.c 
2
#include <stdio.h>
3
#include <stdlib.h>
4
int main(int argc, char*argv[]) {
5
    printf("%d\n", (-8) >> (argc > 1 ? atoi(argv[1]) : 0));
6
    return 0;
7
}
8
$ gcc shift.c && ./a.out
9
-8
10
$ gcc shift.c && ./a.out 10
11
-1
12
$
war dein urspruengliches Problem, i.e. der Shift-Rechts von negativen 
Zahlen.

von Joachim B. (jar)


Lesenswert?

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.

von leo (Gast)


Lesenswert?

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
int f_2 {
2
  return 3;
3
}
4
5
int main() {   // eine Funktion
6
  int ergebnis = f_2()   // andere Funktion
7
  ...
8
}

Die Main-Funktion wird halt vom Startup-Code aufgerufen.

leo

von leo (Gast)


Lesenswert?

Joachim B. schrieb:

leo schrieb:
> int f_2 {
1
int f_2(void) {

Sorry,
leo

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von zitter_ned_aso (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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."

: Bearbeitet durch User
von zitter_ned_aso (Gast)


Lesenswert?

positiv? natürlich nicht.

aber 1<<15 liefert doch die kleinste 16-Bit Zahl (Zweierkomplement)

Warum lässt du das Vorzeichen weg?

von (prx) A. K. (prx)


Lesenswert?

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."

: Bearbeitet durch User
von leo (Gast)


Lesenswert?

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
$ cat shift.c 
2
#include <stdio.h>
3
#include <stdlib.h>
4
int main(int argc, char*argv[]) {
5
    printf("%x\n", 1 << (argc > 1 ? atoi(argv[1]) : 0));
6
    return 0;
7
}
8
$ gcc shift.c && ./a.out 31
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

von leo (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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".

von leo (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von leo (Gast)


Lesenswert?

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

von Heiko L. (zer0)


Lesenswert?

In irgendeinem YouTube-Video habe ich mal gehört, dass so um 1970 mal 
eine Architektur gab, die das Einerkomplement genutzt hat.

von (prx) A. K. (prx)


Lesenswert?

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

: Bearbeitet durch User
von zitter_ned_aso (Gast)


Lesenswert?

Und wie ist es mit der Aussage:
1
a << #bits == a * 2^(#bits)



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?

von Heiko L. (zer0)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

NB: Wer mal einen TCP/IP Stack implementiert hat, dem wird eine 
Einerkomplement-Addition begegnet sein. In der Checksum-Berechnung.

von Heiko L. (zer0)


Lesenswert?

Ja, und, also? "Das Phantom lebt"?

von gdfgsdgsg (Gast)


Lesenswert?

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

von leo (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.