Hallo,
ich bin mal wieder über das #define USE_FULL_ASSERT gestolpert. Was
genau kann ich damit machen? Wie wende ich es an? Was verbirgt sich
dahinter?
1
#ifdef USE_FULL_ASSERT
2
voidassert_failed(uint8_t*file,uint32_tline)
3
{
4
//printf("Wrong parameters value: file %s on line %d\r\n", file, line);
Florian schrieb:> ich bin mal wieder über das #define USE_FULL_ASSERT gestolpert. Was> genau kann ich damit machen?
Den besagten Code, der sich zwischen
1
#ifdef USE_FULL_ASSERT
2
...
3
...besagterCode
4
...
5
#endif
von der Compilierung ausschliessen oder mitcompilieren lassen.
Irgendwo vor diesem besagtem Code gibt es ein
1
#define USE_FULL_ASSERT
steht es so da, dann wird der nachfolgende besagte Code mitcompiliert,
es gibt dann die Funktion.
Wirfst du dieses #define raus, zb in dem du es auskommentierst
1
// #define USE_FULL_ASSERT
dann wird besagter Code nicht mitcompiliert. Die Funktion existiert dann
nicht.
Ist zb nützlich, wenn man in der Debug-version sich ein paar spezielle
Funktkionen machen will, die man im endgültigen Code nicht mit
ausliefern will, die man aber auch nicht löschen will, weil man sie
irgendwann mal (beim nächsten zu fixenden Bug) brauchen könnte.
Also entweder habe ich mich nicht klar ausgedrückt oder Ihr mich falsch
verstanden. Vieleicht ja auch beides :-)
Was Präprozessorbefehle sind ist mir klar!
Ich möchte wissen wann das "assert_failed" angesprungen wird. Wann wird
es angesprochen und wie kann ich mir diese Funktion zunutze machen.
Florian schrieb:> Ich möchte wissen wann das "assert_failed" angesprungen wird.
Wie wärs, wen du dir einfach mal im Code ansiehst, wo überall Aufrufe zu
dieser Funktion vorkommen?
Standardfunktion ist das keine. Auch kein dokumentierter
Standardmechanismus. Wenn du also eine Antwort willst: Die Antwort
steckt im Code der vor die auf dem Bildschirm angezeigt wird.
dieser Fehler müsste zu einem "assert_failed" führen
es werden die Übergabeparameter auf plausibilität geprüft
und "RCC_AHB1Periph_GPIOB" ist in dieser Funktion nicht gültig
Gruss Uwe
Uwe B. schrieb:> ich vermute mal es geht um eine STM32 CPU> und die "standard peripheral libs" von ST> (falls nicht meinen Beitrag ignorieren)> RCC_AHB2PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);>> dieser Fehler müsste zu einem "assert_failed" führen> es werden die Übergabeparameter auf plausibilität geprüft> und "RCC_AHB1Periph_GPIOB" ist in dieser Funktion nicht gültig>> Gruss Uwe
Richtig. Darum geht es :-)
Weisst Du ob man auch eigene Sachen auf Plausibilität prüfen kann oder
ist das nur fir die Std. Lib. eigenen Funktionen?
Florian schrieb:> Richtig. Darum geht es :-)
Hättest du auch mal sagen können.
> Weisst Du ob man auch eigene Sachen auf Plausibilität prüfen kann oder> ist das nur fir die Std. Lib. eigenen Funktionen?
Ich denke, du liest da etwas zuviel heraus.
So ein assert Makro (und darauf läuft es ja letzten Endes hinaus) ist im
Prinzip eine recht einfache Sache.
Das ist einfach nur eine Bedingung, die true ergeben muss. Ist sie das
nicht, dann wird irgendwas gemacht, zb eine Meldung irgendwo ausgegeben.
Da steckt im Grunde nicht viel dahinter.
und das benutzt du dann um zb überprüfen zu lassen, ob zum Bleistift
Funktionsargumente im erlaubten Bereich sind
1
voidsetValue(intx)
2
{
3
// x darf nicht kleiner als 5 und nicht größer als 10 sein
4
5
assert(x>=5&&x<=10);
6
7
....
8
}
"assert" bedeutet auf englisch einfach nur "behaupten, feststellen,
versichern".
D.h. an dieser Stelle steht im Code mehr oder weniger: Hier gilt die
Behauptung, dass das x sich in diesem Bereich 5 bis 10 bewegen wird.
Behaupten kann man vieles, wir überprüfen das".
Der Sinn eines assert besteht darin, dass man seinen Debug Code mit
derartigen Prüfungen spicken kann. Da es sich um ein Makro handelt, kann
man ganz leicht durch ein anderes #define dafür sorgen, dass im
Auslieferungscode diese Überprüfungen vom Compiler entfernt werden und
damit dann keine Rechenzeit mehr benötigen.
Ein Assert ist also eine Debughilfe, die während der Entwicklungszeit
zum tragen kommt, damit man möglichst schnell auf potentielle Fehler
hingewiesen wird. Wird die Funktion SetValue mit einem Wert von 3
aufgerufen, obwohl das eigentlich nicht sein dürfte, dann kriegt man
sofort eine entsprechende Meldung (wie auch immer die dann aussieht) und
man kann dem nachgehen, wie es zu einem derartigen Aufruf gekommen ist.
Und natürlich kannst du derartige Assertions auch für deine eigenen
Zwecke benutzen. Für alles Mögliche.
Aber automatisch (und genau deshalb denke ich, du liest da zuviel
heraus) passiert da überhaupt nichts. Wenn du etwas zu Debug-Zwecken
überprüft haben willst, dann musst du die entsprechende Bedingung an der
entpsrechenden Stelle hinschreiben.
Die Funktion assert_failed hat ganz offensichtlich die Aufgabe, genau
diese Meldung zu generieren. Wobei das natürlich auf einem µC nicht so
einfach ist, wenn man kein Terminal oder dergleichen zur Verfügung hat.
http://www2.hs-fulda.de/~klingebiel/c-stdlib/assert.htm
Danke Euch beiden für die ausführlichen Erklärungen!
@Karl Heinz
Ja Du hast recht. Ich habe da wirklich zu viel rausgelesen :-)
@Uwe
Danke für das Beispiel. Daran habe ich die Anwendung sehr gut verstehen
können. Ich denke auch, das es die Mühe nicht Wert ist. Bei kritischen
Werten (z.B. Array-Index) frage ich die Werte sowieso immer noch mal zur
Sicherheit im Code ab.
Gruß
Florian
Florian schrieb:> können. Ich denke auch, das es die Mühe nicht Wert ist. Bei kritischen> Werten (z.B. Array-Index) frage ich die Werte sowieso immer noch mal zur> Sicherheit im Code ab.
Das musst du sowieso. EIn assert ist nicht mit einer sauberen
Fehlerbehandlung zu verwechseln. Ein assert ist ein Debug-Werkzeug,
damit Fehler nicht unentdeckt bleiben. Selbst wenn dann der Code in
weiterer Folge damit klar kommt. Bei einem assert geht es darum, dass
der Programmierer möglichst schnell merkt, dass irgendwas nicht stimmt
und sich darum kümmern kann.
Was man zb oft asserted, sind Parameter die Pointer sind
1
voidstrComplexOperation(constchar*str)
2
{
3
assert(str);
4
5
machmitstreinekomplexeOperation
6
}
die Funktion darf nicht mit einem NULL Pointer aufgerufen werden. Macht
auch nicht viel Sinn. Trotzdem kann es natürlich schon mal passieren.
Natürlich wird niemand schreiben
1
...
2
strComplexOperation(NULL);
3
...
aber wenn der String aus einer Datenstruktur kommt, sagen wir mal aus
einem Array
1
char*texte[20];
2
intnrTexte;
3
4
...
5
for(i=0;i<nrTexte;i++)
6
strComplexOperation(texte[i]);
7
...
und du dir einen Hund reingehauen hast, so dass nrTexte um 1 zu gross
ist, dann kann es schon passieren, dass die Funktion mit einem NULL
Pointer gerufen wird. Der Fehler liegt dann nicht in der Funktion, er
liegt auch nicht im Array sondern in einem Fehler ganz woanders.
Trotzdem wirkt er sich so aus, dass die Funktion etwas illegales tun
würde.
Ohne assert kommt man nur über Umwege drauf, dass da irgendwas abgeht.
Mit dem assert meldet das System sofort: Sieh dir das bitte mal an - die
Funktion wurde mit einem NULL Pointer gerufen, obwohl das eigentlich
nicht sein sollte.
Und das ist dann schon ein Fortschritt, weil dir das Programm selber
helfen kann, dich auf Fehlersituationen aufmerksam zu machen.
Aber ein assert ist keine Fehlerbehandlung in dem Sinne, dass damit
alles gut wäre. Wenn diese strComplexOperation Funktion Teil einer
Library ist, und somit allgemein verwendbar sein soll, dann bleibt es
nicht aus, dass man da dann noch eine saubere Fehlerbehandlung einbaut
1
voidstrComplexOperation(constchar*str)
2
{
3
assert(str);
4
5
if(str){
6
machmitstreinekomplexeOperation
7
}
8
}
Der assert bleibt natürlich trotzdem drinnen, denn in der Debug Version
des Programms will man ja haben, dass das Programm aktiv an der
Fehlersuche teilnimmt. In der Release Version des Programms werden die
assert durch ein entsprechendes #define still gelegt und sind dann für
deinen Anwender nicht mehr vorhanden. Dann muss die Fehlerbehandlung die
noch vorhandenen Probleme behandeln können - aber diesmal ohne Meldung
an dich :-)
//printf("Wrong parameters value: file %s on line %d\r\n", file, line);
5
while(1)
6
;
7
}
8
#endif
eine Wendung hat, die der Umgebung eines µC geschuldet ist. Hier wird ja
nichts ausgegeben, sondern der µC einfach in einer Endlosschleife
gefangen gehalten. Die Funktion ist daher eher als eine Art Vorlage
gedacht. So nach dem Muster: sieh dir mal den printf an. So würde das
gehen, wenn es einen funktionierenden printf gäbe. Wenn du ein LCD hast,
dann musst du eben den printf durch eine entsprechende LCD Ausgabe
ersetzen. Oder durch eine Ausgabe auf die UART. Oder was du eben hast,
so dass sich das Programm zur Laufzeit bemerkbar machen kann.
Klar, mit einer Endlosschleife, in der der µC hängt, macht er sich auch
in einem gewissen Sinne bemerkbar. Aber der Informationsgehalt davon ist
dann schon recht dürftig.