Der 08/15-Programmierer steht allerdings einem ganz anderen Anspruch gegenüber. Selbst wenn sich bei ihm Chipstüten neben Bergen von Konzeptnotizen türmen, hat er oft unter dem Ordnungsanspruch der Qualität und Portierbarkeit zu arbeiten. Aber wie soll das gehen, wenn man schon seine Maus unter den Papierhaufen suchen muss?
Es soll ja Leute geben, die aufräumen, bevor die Putzfrau kommt. Ich gehöre nicht dazu, und dennoch schaffe ich es, meinen Schreibtisch so weit in Ordnung zu halten, dass sich Sachen wiederfinden lassen. Ein kleines Problem stellen vielleicht Notizzettel dar, auf denen ich mir wichtige Telefonnummern notiert habe und die unter dem Papier wieder zum Vorschein kommen.
Diese Zettel haben erstaunlich viel mit meiner Schwiegermutter gemeinsam: Sie geben einem nie die volle Information. Es scheint der Lieblingssport meiner Schwiegermutter zu sein, sich ihrer Umwelt mit Halbsätzen mitzuteilen. Das hat alles seine gute und seine schlechte Seite. Schlecht ist, dass man mit "Das willst Du doch sicherlich in braun …" nicht viel anfangen kann, wenn es der erste Satz ist, den sie zu mir sagt.
Mein Verstand exerziert dann immer Dinge der Art "Braun? Was in braun? Bananen vielleicht?" durch. Klare Vorteile hat es bei Erstsätzen wie "Habe ich nicht recht, wenn ich so etwas zu ihr sage." Ein einfaches "Ja." erspart einem das 4653298475 Stunden dauernde Lamento über die Nachbarin, die dies gesagt hat, um jenen eines auszuwischen etc. etc. etc.
So sind dann meine Notizzettel: Ich habe vergessen, einen Namen zur Nummer zu schreiben – und den Mut, einfach diese Nummer anzurufen um herauszufinden, wer sich dahinter verbirgt, habe ich nicht immer.
Eines der größten Syntax-Verbrechen der deutschen Sprache ist sicherlich der Halsnasenohrenarzt. Die n-fach-Formulierung von Nasen und Ohren steht in klarem Missverhältnis zur anatomischen Realität. Ich habe – wie so ziemlich alle – nur eine Nase. Wäre es da nicht richtiger, die Nase in die Einzahl zu setzen und HalsNaseOhrenarzt zu sagen? Gerade noch erträglich hätte ich den Bezug zu seinem Behandlungsfeld gefunden. Ein schlichter HälseNasenOhrenarzt wäre somit numerisch korrekt gewesen, widerspräche jedoch wieder anderen Docktoren, denn dann zählten wir Zähnearzt, Füßeorthopäde und Häuserarzt zu den korrekten Bezeichnungen. Nee, lieber nicht.
Ich glaube, dass zu den sinnlosesten Gestalten, die auf dieser Erde wandeln, die Kunstkritiker gehören. Sie lassen sich nur in tiefschürfender Art und Weise über Kunstobjekte aus, um deren Transzendenz, Mattigkeit, Lebensfreude oder was sonst zu beschreiben. Mir scheint, es ist eher eine Kunst zu verstehen, was so ein Kritiker von sich gibt, als der ganze Rest.
Glauben schenken darf man so einem Menschen ohnehin nur darin, ob ihm das Essen während einer Vernissage geschmeckt hat. Sein Urteilsvermögen ist nämlich eher künstlich und geleitet durch sein persönliches Engagement in Kunst als Wertanlage, und deren Kurs wird nur an der Tratschbörse notiert.
Es gibt einen schönen Ausdruck aus der Welt der Computer, der mir immer wieder im Zusammenhang mit Kunstkritik einfällt: Es ist der Begriff Glitsch (engl. glitch). Vor vielen Jahren durfte ich eines der Beispiele für Glitsch selbst an einem bootenden PC beobachten: "Tastatur nicht gefunden – Drücken sie F4". Woher der Ausdruck kommt, weiß ich nicht. Vielleicht hat ihn ein Kunstkritiker erfunden…
Zu den nervigen Dingen im echten Leben gehören Endlosschleifen. Unglaublich aber wahr – es gibt sie auch hier. Da braucht man nur auf den Kinderspielplatz zu gehen und die Lütten zu beobachten: "Mein Pappa fährt aber ein größeres Auto als Deiner." "Nein." "Doch." "Nein." "Doch." "Nein." "Doch." "Nein." "Doch." "Nein." "Doch." … Oder: Wer kennt nicht die Art von Telefongesprächen, in dem jeder der Teilnehmer das letze Wort haben will und man sich immer wieder Erwiderungen in die Ohren brüllt und zäh versucht, dem anderen das Ende des Gesprächs abzuringen.
Natürlich bietet Linux dem Programmierer etwas gegen fehlende Grundinformation, Syntaxverbrechen, Sinnlosigkeit und Endlosschleifen. Es ist der Befehl lint. Nicht zu verwechseln mit Lindt – das ist ein Süßwarenfabrikant und alles andere als sinnlos.
Mit Lint ist der Programmierer mit einem Werkzeug ausgestattet, das seinen Quellcode auf viele Probleme hin untersucht. Dabei meckert Lint soviel an, dass es dem fortgeschrittenen Programmierer schon wieder zu lästig sein könnte. Damit die Software aber nicht allzuviel meckert, wartet eine ganze Reihe von Optionen auf ihren Einsatz.
Mit Sicherheit ist der beste Weg, Unix-Befehle zu erlernen, derjenige, sie zu benutzen. Das ist mit Lint nicht anders. Aber mehr noch: Der Befehl lint hilft einerseits dem Programmieranfänger, Fehler in seiner Denkweise zu finden, und andererseits dem etwas fortgeschritteneren Programmierer, seinen Kodierungsstil zu verbessern.
Für die ersten Schritte schaut man sich am Besten ein kleines Programm an, dem es hier und da an Sauberkeit mangelt, und jagt es durch Lint.
Das folgende C-Programm (Listing 1, sample.c) soll keine schlaue Aufgabe übernehmen, es dient nur der Verdeutlichung, was man alles richtig und falsch machen kann und wie Lint einem dabei unter die Arme greifen kann.
Listing 1
sample.c
01 #include <stdio.h>
02
03 int f(void)
04 {
05 return 0;
06 }
07
08 int g(void)
09 {
10 return 1;
11 }
12
13 int rueckgabe(void)
14 {
15 return 2;
16 }
17
18 int main()
19 {
20 int unbenutzt;
21 long lang;
22 short kurz;
23 long undefiniert;
24 long effektlos;
25
26 lang=3;
27 kurz=lang;
28 lang=undefiniert;
29 effektlos=1;
30 lang==effektlos;
31 if(1==0)
32 {
33 lang=4;
34 }
35 while(lang > f()) g();
36 printf("LinuxUser!\n");
37 rueckgabe();
38 }
Ruft man nun lint sample.c auf, erhält man folgende Ausgabe für den geschriebenen Code (wenn Lint Ausgaben für Bibliotheken ausgibt, so können diese fürs erste ignoriert werden).
volker@maus> lint sample.c sample.c: sample.c(28): warning: undefiniert may be used before set [158] sample.c(20): warning: unbenutzt unused in function main [192] sample.c(27): warning: kurz set but not used in function main [191] sample.c(38): warning: function main falls off bottom without returning value [217]
Es werden verschiedene Warnungen ausgegeben:
Nachdem sich Lint schon früh zu einem Standard zur Qualitätsuntersuchung von C-Quellcode entwickelt hat, stiegen die Ansprüche an ein solches Werkzeug. Es gibt eine Reihe von Fehlerkategorien, die man in seinem C-Programm erzeugen kann, die Lint jedoch nicht findet.
Daher wurde das bessere Lint, LCLint, geboren, welches heute im NASA Langley Research Center weiter entwickelt wird. Dem Programmierer steht das Kommando lclint zur Verfügung, welches nicht nur eine ganze Reihe der traditionellen Lint-Überprüfungen wie Typüberprüfung, Benutzung undefinierter Variablen, ignorierte Funktionsergebnisse oder unbenutzte Deklarationen zur Verfügung stellt, sondern dem Profi Funktionalitäten für Macros, Aliasing, Namenskonventionen, Speicher-Sharing etc. bietet.
Um sich mit LCLint vertraut zu machen, versucht man am besten die gleiche Taktik wie mit Lint: Man probiert es aus. Der erste Versuch bringt bereits eine ganze Reihe von Information:
> lclint sample.c
Für den Anfänger ist es aber einfacher, LCLint mit der Option -weak (für schwache Überprüfung) aufzurufen, denn die meisten C-Programme produzieren derart viele Meldungen, dass man vor lauter Text zurückschreckt. Das hat LCLint aber nicht verdient und soll daher zuerst mit der Beginner-Option betrachtet werden:
>lclint -weak sample.c
Was dabei herauskommt, ist schon weitaus überschaubarer und erinnert nicht von ungefähr an die Ausgabe des Klassikers Lint:
sample.c: (in function main) sample.c:27,3: Assignment of long int to short int: kurz = lang To ignore type qualifiers in type comparisons use +ignorequals. sample.c:20,7: Variable unbenutzt declared but not used A variable is declared but never used. Use /*@unused@*/ in front of declaration to suppress message. (-varuse will suppress message)
Erster Bonus zu Lint: LCLint gibt die Spalte aus, in der es etwas gefunden hat. Hier bemerkt LCLint in der Zeile 27 ab der Spalte 3 die Zuweisung der vier Byte langen long-Variablen auf die ein Byte lange short-Variable. Mit der Option -weak wird zusätzlich erkannt, dass es eine Variable gibt, die zwar deklariert wurde aber nicht benutzt wird.
Was uns dieses erste kleine Beispiel lehrt, ist, wie man beim Aufruf von LCLint Optionen setzen oder deaktivieren kann. Sollen Fehlermeldungen zum Typkonflikt nicht mehr ausgegeben werden, so geht das einfach – wie schon der Fehlertext beschreibt – mit:
> lclint -weak +ignorequals sample.c
Hier sehen wir eine etwas Unix-untypische Vorgehensweise beim Setzen der Optionen: Während mit einem Pluszeichen eine Option gesetzt wird, deaktiviert ein Minus sie wieder. Die Plus/Minus-Optionen sollen Klarheit verschaffen, können aber aufgrund ihrer vom Unix-Standard abweichenden Form Verwirrung stiften. Daher ist es sehr angenehm, dass Fehlermeldungen sagen, wie eine Option zu setzen ist (im obigen Beispiel mit -varuse will suppress message).
Es ist noch zu bemerken, dass LCLint über 300 Optionen kennt, die man nicht alle eintippen möchte, wenn man LCLint aufruft. Daher können die Standardoptionen in der Datei .lclintrc im aktuellen Verzeichnis dauerhaft gesichert werden. Die hier gesetzten Optionen überschreiben die allgemeinen Einstellungen in der Datei ~/.lclintrc im Home-Verzeichnis des Anwenders. Letztendlich ist die Angabe einer Option beim Aufruf natürlich maßgeblich und überschreibt die vorbestimmten Einstellung der Datei .lclintrc.
In Listing 2 (using.c) sieht man die Möglichkeit, stilisierte Kommentare (engl. stylized comments) zu benutzen. Sie sind einer der ganz großen Vorteile von LCLint gegenüber dem klassischen lint-Befehl, da sie es ermöglichen, LCLint-Anweisungen und Zusatzinformationen in den Quelltext einzubauen.
Listing 2
using.c
1 int main()
2 {
3 int ungebraucht;
4 /*@unused@*/
5 int braucheIchNoch;
6
7 return 0;
8 }
Auf dieses Listing reagiert LCLint mit der folgenden Ausgabe:
using.c: (in function main) using.c:3,7: Variable ungebraucht declared but not used A variable is declared but never used. Use /*@unused@*/ in front of declaration to suppress message. (-varuse will suppress message)
Der Nichtgebrauch der Variablen braucheIchNoch wurde LCLint durch die Kommentierung mit /*@unused@*/ als gültig gemeldet. Es erfolgt keine Fehlermeldung. Gleichzeitig ist die Variable ungebraucht nicht entsprechend markiert; demzufolge wird sie von LCLint angemeckert.
Doch nun zum besseren Lint. Dazu lassen wir einfach die Option -weak einmal weg und sehen uns an, was LCLint standardmäßig ausgibt, wenn wir es auf unser Listing 1 losjagen (Kasten "Ausgabe von lclint sample.c").
Ausgabe von
lclint sample.csample.c: (in function main) sample.c:27,3: Assignment of long int to short int: kurz = lang To ignore type qualifiers in type comparisons use +ignorequals. sample.c:28,8: Variable undefiniert used before definition An rvalue is used that may not be initialized to a value on some execution path. (-usedef will suppress message) sample.c:30,3: Statement has no effect: lang == effektlos Statement has no visible effect — no values are modified. (-noeffect will suppress message) sample.c:35,21: Return value (type int) ignored: g() Result returned by function call is not used. If this is intended, can cast result to (void) to eliminate message. (-retvalint will suppress message) sample.c:37,3: Return value (type int) ignored: rueckgabe() sample.c:38,2: Path with no return in function declared to return int There is a path through a function declared to return a value on which there is no return statement. This means the execution may fall through without returning a meaningful result to the caller. (-noret will suppress message) sample.c:20,7: Variable unbenutzt declared but not used A variable is declared but never used. Use /*@unused@*/ in front of declaration to suppress message. (-varuse will suppress message)
Dass LCLint tatsächlich noch tiefgründigere Sachen kann, zeigt das folgende Listing 3 (reihenfolge.c):
Listing 3
reihenfolge.c
01 extern int glob;
02
03 extern int aenderglob(void)
04 /*@globals glob@*/
05 /*@modifies glob@*/;
06
07 int main(void)
08 {
09 int i=1;
10 int x;
11
12 x = i++ * i;
13 i+=aenderglob() * glob;
14
15 return 1;
16 }
Bei der Prüfung mit LCLint bekommt man folgendes Ergebnis:
$ lclint reihenfolge.c
reihenfolge.c: (in function main)
reihenfolge.c:12,13: Expression has undefined behavior (value of right operand
modified by left operand): i++ * i
Code has unspecified behavior. Order of evaluation of function parameters or
subexpressions is not defined, so if a value is used and modified in
different places not separated by a sequence point constraining evaluation
order, then the result of the expression is unspecified. (-evalorder will
suppress message)
reihenfolge.c:13,21: Expression has undefined behavior (value of right operand
modified by left operand): aenderglob() * glob
LCLint kann ganz gut Endlosschleifen erkennen. Dazu betrachten wir folgendes kleines Beispiel (Listing 4, endlos.c):
Listing 4
endlos.c
01 extern int f(void)
02 /*@modifies nothing@*/;
03
04 int main(void)
05 {
06 int i=1;
07
08 while(0 < f()) i++;
09
10 return 1;
11 }
LCLint gibt dann aus:
endlos.c: (in function main)
endlos.c:8,9: Suspected infinite loop. No value used in loop test ([result of
f]) is modified by test or loop body.
This appears to be an infinite loop. Nothing in the body of the loop or the
loop test modifies the value of the loop test. Perhaps the specification of a
function called in the loop body is missing a modification. (-infloops will
suppress message)LCLint erkennt am stilisierten Kommentar /*@modifies nothing@*/, dass die Funktion keine Variable ändert, und nimmt daher an, dass der Rückgabewert für die Funktion konstant ist. Das riecht förmlich nach einer Endlosschleife, und LCLint erkennt diese.
Das Programm LCLint ist unter http://lclint.cs.virginia.edu/ zu finden. Dort gibt es eine ausführliche Dokumentation. Das zugehörige RPM-Paket gehört zum Umfang der meisten Distributionen (meine SuSE hat es jedenfalls). Insbesondere gilt für den LCLint: Probieren, probieren und weiter probieren.
Wer LCLint richtig anwendet, der findet bereits im Vorfeld viele Fehler. Ich jedenfalls habe meiner Firma unlängst vorgeschlagen, die C-Programme, die wir von außerhalb einkaufen, vorher durch LCLint qualitätszusichern. Und das beweist schon, dass sich LCLint nicht nur für den kleinen Mann, sondern auch für die ganz großen Firmen eignet…
Glossar
Docktoren
Docktor [m] – ein alberner Werftarbeiter
lint
(Syntax: lint [-abceghprvwxzHF]) Das lint-Kommando kennt eine ganze Reihe von Optionen. Hier sind die wichtigsten aufgeführt.
ANSI C
ANSI, American National Standard Institute. Ein 1918 gegründeter gemeinnütziger Zusammenschluss von Unternehmer- und Industriegruppen, der sich mit der Entwicklung freiwilliger Standards beschäftigt. ANSI C korrigierte Unzulänglichkeiten der Sprache C.