Archiv verlassen und diese Seite im Standarddesign anzeigen : Eventhandler
Hallo,
wo ist der Unterschied zwischen Funktionen und Eventhandlern?
Oder ist das das selbe;)?
Hab mal in der Schule gelernt, dass man Funktionen wie z.B void rechnen (int zahl) auch ganz oben im Programm als Prototyp definieren muss. Muss genauso geschrieben werden, nur das am Schluss noch ein Semikolon ran kommt.
Also so: void rechnen (int zahl);
In den RP6 Programmen blicke ich das nicht ganz, da werden Eventhandler unterhalb von int Main() registriert und heißen dann auch noch anders, als die Funktion selber.
Zum Beispiel wird das I2CTWI_setRequestedDataReadyHandler(I2C_requestedD ataReady); registriert und das
void I2C_requestedDataReady(uint8_t dataRequestID)
ist die dazugehörige Funktion.
Danke für eure Antworten.
roy-sam
Mit setRequestedDataReadyHandler wird die Einsprungadresse von I2C_requestedDataReady gesetzt. Anspringen einer Funktion über einen Zeiger ist das Zauberwort. Oder callback .. Zu "Eventhandler" -> http://de.wikipedia.org/wiki/Ereignis_%28Programmierung%29
Und auch hier empfehle ich Dir die Nutzung von Suchfunktionen...
Hallo,
verstehe ich das dann richtig, das wenn z.B ein Bumper gedrückt wird, über den Eventhandler
„BUMPERS_setStateChangedHandler(bumpersStateChange d);“ zur Funktion
"void bumpersStateChanged(void)" gesprungen wird?
Das heist der Zeiger zeigt auf diese Funktion.
Danke
roy-sam
Fabian E.
09.04.2011, 16:45
Events sind eine einfache Möglichkeit Code auszulagern.
Dein eigener Code wird quasi benachrichtigt, dass eine gewisse Situation eingetreten ist. Ein sog. "Event".
Du kannst dann eben noch bestimmen, was in einer solchen Situation geschehen soll.
Das passiert über eine extra geschriebe Methode. Die Adresse dieser Methode (vorstellbar als der "Name") wird dann so gespeichert, dass der ursprüngliche Code,
diese Methode in einem Ereignisfall aufrufen kann.
Gäbe es diese Events nicht, dann müsstest du, um z.B. auf die Bumper zu reagieren, immer den Code in der Library umschreiben.
@roy-sam
nicht ganz...dazu schaust Du dir am besten aus den RP6Libs die Funktion:
void BUMPERS_stateChanged_DUMMY(void){}
static void (*BUMPERS_stateChangedHandler)(void) = BUMPERS_stateChanged_DUMMY;
/**
* Use this function to set the Bumpers state change handler.
*
*/
void BUMPERS_setStateChangedHandler(void (*bumperHandler)(void))
{
BUMPERS_stateChangedHandler = bumperHandler;
}
an. Dort wird der Zeiger der übergebenden Funktion bumpersStateChanged als Sprungziel eingetragen so das diese später in:
void task_Bumpers(void)
{
if(bumper_timer > 50) { // 50ms
uint8_t left = getBumperLeft();
uint8_t right = getBumperRight();
if(bumper_left != left || bumper_right != right) {
bumper_left = left;
bumper_right = right;
BUMPERS_stateChangedHandler();
}
bumper_timer = 0;
}
}angesprungen werden kann. Denn bumpersStateChanged wird ja vorher als Sprungziel gesetzt und dann angesprungen. Man könnte so wärend des Programmlaufes auch eine andere Funktion als Sprungziel ersetzen (Zeiger verbiegen sagt man dann), es ist damit quasi ein "dynamisches" Sprungziel und kein statisches wie es fest eincompiliert wäre. Das Konzept kann ansich weit mehr, so kann man z.B. Sprungtabellen und verschachtelte Eventhandler usw. anlegen, Eventhandler in Listen zu Laufzeit hinzufügen und löschen, das geht bis hin zu komplexer mehrdimensionaler Zeigerarithmetik was in der RP6 Software jedoch alles kaum bis garnicht genutzt wird. In Windows und Unix, also eigentlich in allen Multitasking Umgebungen wird dies z.B. recht excessiv z.B. beim Taskswitch genutzt und geht schon fast in Richtung "selbstmodifizierender Code".
Siehe auch: http://de.wikipedia.org/wiki/Selbstmodifizierender_Code
Umgangssprachlich stimmt deine Aussage so aber die Kompexität dahinter ist doch etwas umfangreicher. Wobei es eigentlich nicht mal komplex ist - die sich damit bietenden Möglichkeiten machen die Kompexizität und Leistungsfähigkeit aus. Das will ich mit dem Beitrag auch aufzeigen. Das Anspringen von Zeigern bzw. Zeigerarithmetik macht eines der wichtigsten Sprachkonstrukte von C aus bzw. hebt C deutlich von (ursprünglichen) Basic Dialekten und anderen Sprachen ab weshalb es zuweilen auch als "mächtig" und "systemnah" bezeichnet wird.
Man muss halt nur höllisch aufpassen das man nicht irgendwo in die Pampa springt/greift, weil man z.B. vergessen hat ein Zeiger zu initialisieren. Da es falsch verwendet aber sehr fehlerträchtig und extrem schlecht zu debuggen (Laufzeit) ist, überantwortet man Zeigerarithmetik auch gern der Programmiersprache (z.B. C++), was oft zu suboptimalen Ergebnissen und aufgeblasenem Code im Vergleich zu C führt. In manchen Fällen nimmt man es aber auch einfach als selbstverständlich hin, z.B. bei char Arrays .. besser bekannt als Strings, C kennt allerdings auch so komplexe Dinge wie Zeiger auf mehrdimensionale Zeigerarrays... z.B. als Binärbäume in komplexen Funktionen wie huffman/lzw, Hash-, Such- und Sortierfunktionen, Kryptographie...
Hier z.B. ein sehr interssantes Script zum Lernen von Zeigern in allen Variationen.
http://www2.hs-fulda.de/~klingebiel/c-vorlesung/teil7/index.htm (http://www2.hs-fulda.de/%7Eklingebiel/c-vorlesung/teil7/index.htm)
Das Wissen um Zeiger betrifft auch Aspekte wie Stack- und Speicherverbrauch, "call by reference" im Gegensatz zu "call by Value" und daraus resultierendes Laufzeitverhalten/Problemen usw. usw. usw. bis hin zu "überladen von Funktionen" aus C++ was man damit auch in C nachbilden kann, aber eben nicht zwangsläufig zu besserem Code führt...
Compilerwarnings betreffen oft Fehler in der Zeigerhandhabung weshalb man sie auch wirklich ernst nehmen sollte. Schreibt man auf ein fehlerhaften Zeiger und liest wieder ein, kann durchaus ein korrekter Wert dabei raus kommen, evtl. hat man sich bei der Aktion aber was anderes überschrieben, es kann aber auch von anderen Programmteilen überschrieben worden sein. Überschriebene Sprungadressen sind natürlich besonders gemein weil im Gegensatz zu großen Prozessoren keine MMU und/oder Supervisor Modi im Prozessor vorhanden sind, die sowas abfangen/verhindern.
Auf Maschinenebene sind einfache Zeiger eine Form der indirekten Addressierung in 2 Schritten, also quasi "lade Register A mit Zeiger" und "lade in Register B den _Inhalt_ der Speicherzelle, auf die Register A damit _zeigt_". Das erlaubt auch ein einfaches incrementieren des Zeigers A und damit ein durchlaufen des Speichers z.B. bei Strings. Anderes Beispiel, man kann ein String als "by Value" übergeben, dann läge der String aber im Stack was je nach Länge garnicht gut wäre. Übergibt man ihn "by Reference" als Zeiger wie üblich, wird lediglich der _Zeiger_ auf String im Stack übergeben, der String selbst liegt dazu irgendwo unbeweglich im Speicher. Zeiger haben eine feste Größe egal wie komplex die Datenstruktur ist, auf die sie zeigen. Das macht ein call by ref meist deutlich schneller als ein call by val, allerdings wird bei by val eine Copie der Var auf dem Stack angelegt was ggf. ausdrücklich gewünscht ist (z.B. Recursion), schutz vor Seiteneffekten. Es gibt dabei also kein "besser" oder "schlechter" sondern es kommt auf die richtige und durchdachte Verwendung an.
Eine praxisnahe Anwendung des Wissens ist z.B. neben Strings auch writeString_P und writeString aus den RP6Libs wobei das z.B. eher in Richtung von Neumann/Harvard Architektur geht. http://de.wikipedia.org/wiki/Harvard-Architektur
Grundwissen über Zeiger ist in C essentiell wichtig, allerdings auch nicht ganz einfach.
LG Rolf
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.