Warnhinweise

>>#wasfehlt: Ein Gesetz, das Schriftgrößen unter 500 Punkt für den Hinweis >Flasche nicht legen< verbietet.«

Kathrin Passig I @kathrinpassig, Twitter, 16. August 2013

Man kann in der Programmierung mit sehr geringen Fähigkeiten schon einiges bewerkstelligen. Auch wer einen Hammer, ein paar Nägel, eine Säge und keine Ahnung hat, kann sich ein Häuschen bauen. Es wird vielleicht ein bisschen windschief sein und keine Architekturpreise gewinnen, aber wahrscheinlich erfüllt es einige wesentliche Hausfunktionen, zum Beispiel hält es seinen Bewohner bei Regen trocken. Beim Heimwerken wie beim Programmieren lässt sich vieles einfach erraten. Anderes findet man durch Erfahrung heraus: Zu kurz abgesägte Bretter lassen sich nicht so einfach wieder auf die richtige Länge bringen, und einmal gelöschter Code ist oft weg, wenn man sich nicht rechtzeitig um Backups oder Versionskontrolle gekümmert hat.

Ein paar Bereiche gibt es aber, in denen die richtige Vorgehensweise nicht so einfach zu erraten ist und die Folgen eines Fehlers auch nicht unmittelbar sichtbar werden. Um noch einmal den Heimwerkervergleich zu bemühen: Wer nicht weiß, dass eine DIN-Norm für das Verlegen von Leitungen unter Putz existiert, wird diese Leitungen einfach so verlegen, wie es für ihn am bequemsten ist. Der nächste Bewohner des Hauses möchte ein Bild aufhängen, schlägt einen Nagel in die Wand und trifft die Isolation eines Kabels. Auch jetzt gibt es vielleicht noch gar kein unmittelbares Feedback, dem man entnehmen könnte, dass gerade etwas Unvorteilhaftes und Unvorhergesehenes passiert ist. Irgendwann aber beginnt das Kabel zu schmoren, und eines Nachts geht das ganze Haus in Flammen auf.

In diesem Kapitel sind ein paar Hinweise auf solche unerwarteten Stolperstellen versammelt. Auch wenn Ihr Lebensmotto »Ach was, das geht auch so!« lautet, tun Sie sich und Ihren Nachmietern einen Gefallen, wenn Sie diese Punkte wenigstens überfliegen. Sie brauchen sich die Details nicht zu merken, aber eines Tages, wenn Sie Hammer und Nagel ansetzen, werden Sie vielleicht innehalten und denken: >>Da war doch was ...« Dann hat dieses Kapitel seinen Zweck erfüllt.

GET und POST

Anwendungsgebiet: Programmierung fürs Web

Folgen der Ahnungslosigkeit: Der Googlebot kommt zu Besuch und löscht alle Daten.

Mindestwissen: POST wird verwendet, wenn der Browser Daten an den Server übertragen muss, zum Beispiel beim Upload einer Datei oder beim Abschicken eines ausgefüllten Formulars. Dadurch kann auf dem Server etwas Neues entstehen (etwa ein Blogbeitrag) oder etwas Vorhandenes geändert werden (etwa das Profil eines Nutzers). POSTRequests können durch den Klick auf einen Submit-Button oder per JavaScript ausgelöst werden, nicht aber durch einen einfachen Link.

GET kommt, wie der Name schon andeutet, dann zum Einsatz, wenn Daten vom Server beschafft werden sollen, zum Beispiel die Ergebnisse einer Suche. GET soll auf dem Server keine Änderungen bewirken (abgesehen von unvermeidlichen Kleinigkeiten, denn natürlich wird die GET-Anfrage z.B. als Zugriff in den Serverlogs vermerkt). Leider ist das nur eine Empfehlung und technisch spricht nichts dagegen, mit einem GET-Request alle möglichen Verheerungen auf dem Server anzurichten. Zum Beispiel, indem Sie ein Content-Management-System für eine große staatliche Website schreiben, dessen »Seite löschen«-Links via GET funktionieren. Schon wenige Tage nach dem Start Ihrer schönen neuen Website kommt der Googlebot, um die Seiten dem Google-lndex einzuverleiben. Wie es nun mal seine Aufgabe ist, folgt er jedem der Links und vernichtet so über Nacht den gesamten Content.1

GET soll keine Nebenwirkungen haben. Der Nutzer muss einfach Reload klicken können, ohne dass etwas Unerwünschtes passiert.

GET-URLs werden in der Browser-History und in den Logfiles des Webservers gespeichert. Es wäre günstig, wenn dann nicht ausgerechnet Kundendaten und Kreditkartennummern darin vorkämen. (So handhabt es offenbar die Firma Connect 2 Cleanrooms Ltd. aus Kirkby Lonsdale, die einer Autorin dieses Buchs bei jedem Aufruf einer vom Browser gespeicherten, unschuldig benannten Seite ein neues sperriges Paket aus England zuschickte und in Rechnung stellte.)

POST ist eine Message, die wie ein Paket etwas beinhalten kann - zum Beispiel eine Datei, die hochgeladen werden soll. GET verhält sich in dieser Hinsicht eher wie ein Telegramm, hier wird alles in der URL übertragen. Das kann insbesondere bei älteren Browsern zu Problemen mit zu langen GET-Strings führen, wobei >>zu lang<< bei etwa 2.000 Zeichen anfängt.

Das Ergebnis einer via POST abgeschickten Anfrage lässt sich nicht bookmarken. Wenn man das dem Benutzer ermöglichen will, empfiehlt sich GET. Schon dieser Punkt allein spricht dafür, Suchanfragen per GET zu schicken und Bestellungen per POST.

Weil die URL alle Daten eines GET-Requests enthält, sind sämtliche Parameter für jedermann sichtbar. Auch Menschen mit ansonsten geringer Hackerneigung geraten angesichts solcher URLs häufig in Versuchung, mal probehalber einen solchen Parameter gegen einen anderen auszutauschen. (POST-Requests sind allerdings auch nicht wesentlich sicherer. Schon mit einem einfachen Browser-Add-on oder einem Kommandozeilenprogramm wie cURL lassen sich die Parameter eines POST-Requests leicht verändern. Das ist einer der Gründe dafür, dass Sicherheit serverseitig implementiert werden muss; siehe dazu Kapitel 25.)

Tröstlich: Auch alle anderen verwechseln ständig GET und POST.

Weniger tröstlich: Wir als Nutzer des Internet müssen es ausbaden und bekommen zwei Gefrierschränke statt einem geliefert.

Zeichenkodierung

Anwendungsgebiet: überall

Folgen der Ahnungslosigkeit: Man läuft dem Softwareentwickler Joel Spolsk/ in die Hände, der schon vor geraumer Zeit schrieb: »Wenn Sie im Jahr 2003 als Programmierer arbeiten und ich Sie dabei erwische, dass Sie kein Grundlagenwissen über Zeichen, Zeichensätze, Zeichenkodierung und Unicode haben, dann lasse ich Sie zur Strafe sechs Monate lang in einem U-Boot Zwiebeln schälen, das schwöre ich.« Au+erdem sehen Ihre Texte unter Umst+nden f+r Menschen mit anderen Heimatl+ndern, Schriftarten, Betriebssystemen oder Browsereinstellungen ein bi+chen merkw+rdig aus.

Mindestwissen: »Einfach nur normaler Text« existiert nicht. Jeder Text hat eine Zeichenkodierung (auch character encoding oder kurz Encoding genannt).

Zeichenkodierung heißt, dass die Zeichen in Zahlen übersetzt werden, und zwar aus historischen Gründen je nach Zeichenkodierung in verschiedene Zahlen. ASCII ist eine Zeichenkodierung aus einer Zeit, in der man sich in englischsprachigen Ländern dachte: »Ach, 7 Bit reichen doch««. Deshalb kommen einige deutsche Buchstaben in ASCII gar nicht vor, von polnischen oder chinesischen ganz zu schweigen. Später wurde eine Vielzahl zusätzlicher Zeichenkodierungen wie ISO-8859-1 (für einige westeuropäische Sprachen) oder Windows-1252 (so ähnlich, nur anders) erfunden.

Das Problem ist haariger, als man denkt. Das liegt nur zu einem Teil daran, dass die Welt schlecht ist und sich immer wieder untereinander inkompatible Konventionen bilden anstatt eines einzigen Verfahrens. Vor allem aber ist Zeichenkodierung einfach keine triviale Aufgabe.

Heute ist internationale Vereinheitlichung wichtiger als Speichersparsamkeit, deshalb ist der vorläufig letzte Schritt der Entwicklung Unicode. Betrachten Sie bei Gelegenheit 2 Spolskys lesenswertes Blog ist unter joelonsoftware.com zu linden.

wenigstens flüchtig den Wikipedia-Eintrag »Unicode«, um sich einen Eindruck vom Umfang und der Komplexität dieses Wunderwerks zu verschaffen.

Unicode ist ein Standard, keine Zeichenkodierung. Eine Reihe von verschiedenen Zeichenkodierungen - zum Beispiel UTF-8 oder UTF-16 - implementiert den Unicode-Zeichensatz. Die Übersetzung von Zeichen in Zahlen ist in allen diesen Kodierungen dieselbe, der Unterschied liegt nur darin, wie die Zahl binär dargestellt wird. Qa, es gibt da mehr als eine Möglichkeit.)

In vielen Programmiersprachen dürfen Funktions- und Variablennamen im Code nur mit Buchstaben und einigen Zeichen wie _ beginnen, nicht mit Zahlen und nicht mit anderen Zeichen. Unicode kennt - wie ASCII - die Unterscheidung zwischen Buchstaben, Zahlen und Zeichen, aber da Buchstaben aus allen Schriftsystemen der Welt kommen können, ist die Unterscheidung nicht mehr so klar. Sie könnten in vielen Programmiersprachen eine Variable /"J. nennen, weil das ein griechischer Buchstabe ist, aber nicht ©, das ist ein Zeichen. Tun Sie es aber bitte trotzdem nicht. Nutzen Sie Unicode nur für Inhalte, nicht für Programmcode, denn das verwirrt den Leser nur.

Lassen Sie die Finger von ASCII. Bei Bibliotheken haben Sie manchmal die Wahl, ob eine Funktion UTF-8 oder ASCII entgegennehmen soll. Entscheiden Sie sich in diesem Fall immer zugunsten von UTF-8, auch wenn Sie sich ganz sicher sind, nur ASCII zu brauchen. Eines Tages taucht immer jemand auf, der den Nutzernamen Swiytobor Jablonski haben möchte. Dann werden Sie sich zu Ihrer vorausschauenden Wahl beglückwünschen.

Tröstlich: Zeichenkodierung ist einer der wenigen Bereiche, in denen man als Programmierer Vorteile aus seiner nicht englischen Muttersprache zieht. Englischsprachige Programmierer vernachlässigen diese Fragen noch viel mehr als wir. Außerdem verschwindet das Problem langsam, denn mehr und mehr Software ist von Haus aus Unicode-fähig.

Zeitangaben

Anwendungsgebiet: vor allem im Web

Folgen der Ahnungslosigkeit: Ihre Benutzer in Indien werden sofort nach dem Einloggen wieder ausgeloggt, weil ihre Sessions wegen scheinbarer Untätigkeit abgelaufen sind.

Mindestwissen: Zeit ist zwar ein lineares Konzept, aber die von uns täglich verwendeten Zeitangaben sind nicht linear, sondern haben durch Schaltsekunden und den Wechsel zwischen Sommer- und Winterzeit Sprünge in die Zukunft und sogar in die Vergangenheit. Sie können sich daher nicht darauf verlassen, dass jede Minute 60 Sekunden hat. Es gibt welche, die haben 61. Monate haben nicht immer 30 oder 31 Tage, und das Jahr nicht immer 365.

Eine bestimmte Uhrzeit eines Tages ist nicht immer eindeutig. Beim Übergang von Sommer- auf Winterzeit gibt es die Stunde von 2 Uhr bis 3 Uhr zweimal. Die erste Stunde (von 2 Uhr bis 3 Uhr mitteleuropäischer Sommerzeit) wird amtlich mit 2 A und die zweite Stunde (von 2 Uhr bis 3 Uhr mitteleuropäischer Zeit) mit 2 B bezeichnet.

Zeit im Computer ist noch sprunghafter, weil die meisten Rechner eine eingebaute Quarzuhr besitzen, die periodisch mit einem Zeitserver abgeglichen wird. Diese eingebauten Quarzuhren sind ziemlich ungenau und entfernen sich daher unter Umständen merkbar von der Serverzeit, bis sie wieder angeglichen werden. Falls Sie die Systemuhr zur Synchronisation oder zum genauen Takten Ihres Programms verwenden, lesen Sie nach, ob Ihr Zielsystem eine lineare Zeit für Sie bereit hält, also eine, die nie zurückgestellt wird.

Manche Rechner und insbesondere Smartphones haben keinen voreingestellten Zeitserver, weshalb ihre lokale Zeit Sekunden oder sogar Minuten von der amtlichen Zeit abweichen kann. Wenn Sie Webapplikationen schreiben oder Mobile Apps mit Serverbackend, dann müssen Sie damit rechnen, dass die Serveruhr und die Uhren auf den Smartphones deutlich abweichende Zeiten haben. Falls Sie jemals Mobile Apps programmieren sollten, sollten Sie auch daran denken, dass ; ich die Zeitzone jederzeit ändern kann, zum Beispiel wenn ein Benutzer Ihrer App in Peking aus dem Flugzeug steigt.

Und wenn wir schon bei Zeitzonen sind: Zwar existiert mit UTC eine Standardzeitzone, die als gemeinsame Basis für Zeiten in aller Welt dienen kann, aber Sie sollten grundsätzlich immer, wenn Sie mit Zeit- und Datumsangaben hantieren, die Zeitzone mitbedenken. Wenn Sie Zeitangaben von Ihren Nutzern oder aus anderen Datenquellen beziehen, dann können Sie diese in UTC umwandeln, aber nur, wenn Ihre Anwendung intern ausschließlich mit UTC arbeitet und Sie grundsätzlich externe Zeitangaben nach UTC konvertieren und in UTC speichern. Wenn Sie Zeiten herausgeben, dann bitte mit Angabe der Zeitzone.

Timestamps sind die Zeitpunkte, an denen ein Ereignis stattgefunden hat. Das gängigste Format für Timestamps ist der Unix-Timestamp, der in Sekunden seit dem 1.1.1970 rechnet. Die Erfinder von Unix nannten diesen Zeitpunkt, der grob mit der Einführung dieses Betriebssystems zusammenfällt, ganz unbescheiden den »Beginn der Epoche«. Inzwischen gibt es in jedem Betriebssystem auch Timer, die genauer sind und beispielsweise in Millisekunden seit Anfang 1970 rechnen. Der herkömmliche Sekundentime-stamp ist eine 10-stellige Dezimalzahl, der Millisekundentimestamp eine 13-stellige. Bekommen Sie grob falsche Datumsangaben, dann haben Sie möglicherweise einen Timestamp im falschen System in ein Datum übersetzen lassen. Oder Sie haben einen UTC-Timestamp vor sich, konvertieren ihn aber mit einer Funktion, die die lokale Zeitzone verwendet.

Es gibt sehr viele Datumsformate. Speichern Sie Zeiten immer im selben Format ab und nutzen Sie hierfür möglichst IS0-8601 (also Jahr, Monat, Tag in der Form ^YY-MM-DD, eine Zeitzone passt auch hinein). Im Gegensatz zu Unix-Timestamps ist dieses Format nebenbei noch menschenlesbar.

Tröstlich: Es gibt für jede Programmierumgebung inzwischen Libraries, die die Konvertierung zwischen lokaler Zeit, Sommerzeit und UTC sowie beliebigen anderen Zeitzonen implementieren. Mit Glück sogar welche, die auch den gregorianischen Kalender beherrschen.

Zeitangaben |    231

Kommazahlen als String, Integer oder Decimal speichern

Anwendungsgebiet: selten, aber dafür überall

Folgen der Ahnungslosigkeit: Man hantiert mit falschen Zahlen und bekommt schlimmstenfalls Ärger mit dem Finanzamt.

Mindestwissen: Computer speichern Zahlen intern im Binärsystem ab, Menschen rechnen im Dezimalsystem. Da 10 keine Zweierpotenz ist, gibt es im Dezimalsystem Zahlen, die sich im Binärsystem nicht exakt speichern lassen. So wird 0,1 in den meisten Sprachen als 0,0999999... gespeichert und bei der Ausgabe wieder auf 0,1 gerundet.

Selbst wenn Computer intern dezimal arbeiten würden, könnte man grundsätzlich nicht alle Zahlen präzise in begrenztem Speicher ablegen. Im Wesentlichen gibt es eine Abwägung zwischen folgenden Punkten: Integervariablen haben einen begrenzten Wertebereich und keine Nachkommastellen, dafür gibt es keine Kompromisse bei der Präzision. Der Wertebereich von Gleitkommavariablen ist viel größer, und auch Nachkommastellen sind kein Problem, aber dafür muss immer irgendwo gerundet werden. Rechnet man mit so einer Zahl, dann addieren sich diese Ungenauigkeiten auf. In beiden Fällen kann man als Programmierer in den meisten Sprachen eine Entscheidung treffen, wie viel Speicher man investieren will, um diese Einschränkungen zu lindern.

Da wir selten mit Kommazahlen im Multimilliardstelbereich operieren und eigentlich genug Puffer für Multiplikationen haben, bevor Rundungsfehler sichtbar werden, wirkt das nicht wie ein relevantes Problem. Überraschenderweise kann die unpräzise Speicherung von Zahlen jedoch auch in ganz alltäglichen Wertebereichen für Ärger sorgen.

Ein Beispiel, das die Probleme durch die Umwandlung von dezimalen in binäre Zahlen demonstriert:

double a = 0,7; double b = 0,9; double x = a + 0,1; double y = b - 0,1; if (x == y) {

/* Dieser Vergleich wird in den meisten gängigen Sprachen fehlschlagen */

}

Es gibt Mathematikbibliotheken, die Kommazahlen beliebiger Genauigkeit verarbeiten können. Manche Sprachen haben auch Datentypen wie Decimal oder bignum eingebaut, die die geschilderten Probleme nicht haben. Verarbeiten Sie Geldbeträge oder müssen aus anderen Gründen Exaktheit sicherstellen, dann sollten Sie sich unbedingt mit solchen Bibliotheken oder Typen befassen.

Falls Sie die Möglichkeit haben, auf Integerzahlen auszuweichen, können Sie Nachkommastellen auch ganz loswerden und die geschilderten Probleme umgehen. Beispielsweise ist in einer einfachen Buchhaltung ohne Zinsberechnung die Genauigkeit für Währungsbeträge in US-Dollar oder Euro auf zwei Kommastellen begrenzt. Sie können also die

Beträge auch als ganzzahlige Centbeträge statt als Kommazahlen-Eurobeträge speichern, indem Sie sie mit 100 multiplizieren.

Vorsicht auch, wenn Sie Gleitkommazahlen aus Ihrem Programm exportieren oder aus anderen Quellen importieren. Im deutschen Sprachraum ist ein Komma üblich, um den Ganzzahlteil von den Nachkommastellen zu trennen, in den USA hingegen der Dezimalpunkt. In der Programmierung wird das amerikanische System verwendet, aber in der Buchhaltung hierzulande das deutsche, und Tabellenkalkulationsprogramme wie Excel verwenden leider auch beim Export nach CSV (siehe Kapitel 24) das nationale Format. Wer schon einmal in einem transnationalen Konzern gearbeitet hat, hat sich bestimmt schon über diesen Unterschied geärgert, während er Dateien aus dem jeweils anderen Zahlensystem zu importieren versuchte.

Mit dem Aufkommen des Web haben Textformate an Bedeutung gewonnen (XML, JSON, SOAP), in denen dieses Problem noch viel häufiger als in den früher verwendeten binären Formaten seine hässliche Fratze zeigt. Wenn Sie daher Daten über System- und damit möglicherweise Formatgrenzen hinweg austauschen, dann sollten Sie sich überlegen, ob der Verzicht auf Gleitkommazahlen eine Option ist. Sie sollten dann eine Ganzzahl und einen zugehörigen Divisionsfaktor übertragen. Wenn nicht, verwenden Sie auf jeden Fall das amerikanische System und seien Sie darauf gefasst, möglicherweise auch Zahlen in wissenschaftlicher Notation (2e-4 statt 0,0002) zu bekommen - mit denen zum Glück die parseFloat()-Funktion egal welcher Programmiersprache umgehen kann.

Tröstlich: Auch Spracherfinder fallen an dieser Stelle gelegentlich auf die Nase. Im Jahre 2011 wurde in PHP und Java ein Fehler durch die Speicherung von Kommazahlen gefunden, der ganze Webapplikationen zum Stillstand brachte (www.theregister.co.uk/ 20U/01/04/weird_php_dos_vulnl). Tröstlich ist weiterhin, dass man normalerweise auch im wissenschaftlichen Bereich nur sehr hohe Genauigkeit, aber keine Exaktheit benötigt.

Variablen als Werte oder Referenzen übergeben

Anwendungsgebiet: überall

Folgen der Ahnungslosigkeit: Verwirrung

Mindestwissen: Eine Variable ist nicht dasselbe wie ihr Wert. Diesen Satz sollte man ungefähr alle sechs Monate durchlesen und zu verstehen versuchen. Er ist weniger kryptisch, als er zunächst, und profunder, als er später klingt.

Beispiel:

varl = 5; var2 = varl;

Es wird den meisten intuitiv klar sein, dass var2 jetzt ebenfalls den Wert 5 hat. Die meisten denken »klar, denn varl ist ja 5« - das stimmt aber so nicht.

function confuse (local_var) { local_var = 7;

}

varl = 5; confuse (varl);

Hier wird es schon schwerer. Welchen Wert hat varl jetzt, 5 oder 7?

In den meisten Sprachen wird varl immer noch den Wert 5 haben, weil die Variable loc_var eine rein lokale Variable ist (siehe den Abschnitt >>Scope von Variablen« in Kapitel 26). Der Wert von varl (also die Zahl 5) wird beim Funktionsaufruf nach local_ var kopiert, die Variable varl bleibt hingegen unangetastet. Daher kann die Zuweisung von 7 an localvar den Wert von varl nicht ändern.

function confuse ($local_var) {

local_var->firstName = "Bernadette";

}

Die Funktion ruft man jetzt folgendermaßen auf:

varl = new User {

firstName => "Bernd", lastName => "Lauert"

}

confuse (varl);

Wie heißt varl jetzt mit Vornamen? In den meisten Sprachen überraschenderweise »Bernadette«. Im Gegensatz zu einfachen Variablen (beispielsweise Integern), bei denen in einem Funktionsaufruf nur der Wert übergeben wird, wird bei zusammengesetzten Typen wie Arrays, Structs, Objekten und teilweise auch bei Strings in die lokale Variable eine Referenz übergeben. Das liegt daran, dass auch varl nicht das Array »ist«, sondern nur eine Referenz auf das Array enthält. Die lokale Variable local_var schlüpft in gewisser Weise in die Haut von varl und wird das Gleiche. Entsprechend ändert sich der Inhalt von varl, wenn man den von local_var verändert.

Ein Grund für dieses unterschiedliche Verhalten ist ganz pragmatisch: Wenn man ein Array mit einer Million Einträgen hat und es als Referenz an eine Funktion übergibt, werden hinter den Kulissen an die Funktion nur ein paar Byte für die Speicheradresse übergeben, an der die Array-Inhalte liegen (die Referenz). Würde man das Array als Wert übergeben, dann müsste der gesamte Inhalt des Arrays umkopiert werden - bei großen Datenstrukturen wäre das unsäglich langsam. Einfache Datentypen werden hingegen häufig als Werte übergeben, weil ihre Darstellung im Speicher nicht besonders viel Platz verbraucht. Es wäre aufwendiger, sie als Referenzen zu übergeben.

Konzeptuell ist diese Unterscheidung zwischen einfachen und komplexen Datentypen ebenfalls sinnvoll, denn wenn man an einfache Typen wie Zahlen denkt, dann denkt man an nicht änderbare Konstanten. Die Zahl S ist für uns unveränderbar. Wenn ich also einer Variable mit dem Wert S eine 3 zuweise, dann will ich nicht die S in ihrer »Fünfhaf-tigkeit« verändern, sondern den Variablenwert ersetzen. Wenn ich einen Datensatz wie Personeninformationen in einem Array halte, dann will ich hingegen die Möglichkeit haben, Aspekte dieses Datensatzes (beispielsweise den Vornamen) zu verändern.

Manche Sprachen haben eine feste Vorstellung davon, welche Variablentypen per Referenz und welche als Wert übergeben werden, während andere (z. B. C++) flexibel sind. In C++ kann man sich für eine Variable die Referenz geben lassen und diese an eine Funktion übergeben:

void square(int x, int& result) { result = x * x;

}

Diese Funktion kann man folgendermaßen aufrufen:

int result = 0; square (5, result);

Auch wenn die Funktion keinen Rückgabewert hat, hat result nach dem Funktionsaufruf den Wert 25, weil das & den Compiler anweist, der Funktion nicht den Wert der Variable result zu übergeben, sondern eine Referenz auf diese Variable.

void

Das void legt fest, dass es keine Zuweisung in der Art result = square (5) gibt.

Es gibt Sprachen, die sehr deutlich machen, welche Variablen in einer Funktion schreibbar sind, indem sie sie zum Beispiel als IN OUT kennzeichnen. Eine solche Sprache ist die Datenbanksprache PL/SQL, die für die Programmierung der Datenbankengine von Oracle verwendet wird und deshalb in vielen großen Unternehmen eine Rolle spielt. Gehen Sie sparsam mit dem Verändern von By-Reference bzw. IN OUT-Variablen in Funktionen um. Sie werden Ihren Code besser verstehen, wenn Sie Funktionen möglichst immer nach der Form Input > Funktion -> Output, also var x = function(y) schreiben.

Programmiersprachen unterscheiden sich erheblich darin, wann sie Referenzen und wann Werte in Variablen speichern. Lernt man eine neue Sprache, dann sollte man in einer ruhigen Stunde nach »by value« und »by reference« in Kombination mit dem Namen der Sprache suchen. Leider ist es bei vielen Sprachen schwierig, ihr Verhalten zu verstehen. Ruby ist ein Beispiel für eine moderne Sprache, die alles per Referenz übergibt, sich aber an vielen Stellen so verhält, als würden Werte übergeben. Sucht man für Ruby nach den Übergabekonventionen, findet man lange Diskussionen, in denen sich kompetente Programmierer nur schwer darüber einigen können, ob die Sprache Werte oder Referenzen übergibt. Java hingegen übergibt immer »by Value«, verhält sich aber so, dass die meisten Java-Entwickler darauf schwören würden, dass für Objekte Referenzen übergeben werden.

TL;DR: Wenn Ihnen das alles viel zu lang war, sollten Sie sich eine einzige Sache merken: Variablen, die per Referenz übergeben werden, können von der Funktion, an die sie übergeben wurden, modifiziert werden. Übergibt man nur den Wert, dann ist von außen betrachtet die Variable durch die aufgerufene Funktion nicht veränderbar (immutable).

Tröstlich: Wenn man das Prinzip nicht versteht, kommt man bis zu mittelkomplexen Programmen mit Trial and Error trotzdem klar. Das Web ist voll von Kommentaren, die zeigen, dass auch viele fortgeschrittene Programmierer nicht verstanden haben, wie die Sprache ihrer Wahl die Details handhabt.

Weniger tröstlich: Unverständnis kann zu Fehlern führen, die schwer zu begreifen und zu beheben sind.

Der schwierige Umgang mit dem Nichts

Anwendungsgebiet: Datenbanken, viele Sprachen Folgen der Ahnungslosigkeit: geraufte Haare

Mindestwissen: NULL ist nicht dasselbe wie die Zahl 0, sondern so etwas wie das Vakuum -es bedeutet: »hier ist gar kein Wert, nicht einmal eine 0<<. Grob gesprochen entspricht NULL nicht 0, sondern »unbekannt«. Leider ist die genaue Bedeutung von Sprache zu Sprache subtil unterschiedlich, und bei manchen Sprachen wie SQL auch noch vom Kontext abhängig, in dem NULL verwendet wird. Das Konzept NULL wird in unterschiedlichen Sprachen unterschiedlich bezeichnet: beispielsweise als null in Java, nil in Ruby und None in Python.

In vielen Sprachen haben nicht initialisierte Variablen den Wert NULL. ln manchen Sprachen (zum Beispiel JavaScript) gibt es stattdessen undefined (Perl: undef) für eine Variable, der noch nie etwas zugewiesen wurde. (Diese Sprachen können durchaus trotzdem noch ein NULL besitzen, mit einer leicht anderen Bedeutung.)

Manche Sprachen haben andere, ähnliche Ausdrücke wie NaN (Not a Number) oder Infi-nity für das Konzept »hier steht kein sinnvoller Wert« - in diesem Fall etwas, das nicht als Zahl ausdrückbar ist, etwa »Unendlich«. NULL, undefined, Infinity und NaN unterscheiden sich erheblich, gemeinsam ist ihnen aber, dass man sie nicht für mathematische Operationen einsetzen sollte - meistens kommt entweder eine Fehlermeldung oder wieder NULL, undefined, Infinity oder NaN raus.

Aber Vorsicht. In JavaScript beispielsweise gibt es mathematische Unterschiede zwischen undefined und null:

>1 + undefined NaN

>1 + null 1

In diesem speziellen Kontext verhält sich NULL dann doch wieder wie die Zahl 0.

In SQL kann man NULL in Vergleichen nicht wie eine Zahl behandeln (also etwa select * from users where firstname = NULL), sondern muss einen anderen Vergleichsoperator verwenden: select * from users where firstname is NULL. Ähnlich in Perl: Hier muss man if defined ($var) schreiben, um zu testen, ob $var undef ist. In Java und vielen anderen Sprachen kann man hingegen völlig problemlos auf NULL testen: if myObject == null {____

Wer bei Datenbankabfragen select * from users where firstname = NULL schreibt, wird sich wundern, denn SQL-Queries, die = und NULL verwenden, liefern nie Ergebnisse zurück. Verwirrenderweise liefern auch Queries, die <> (ungleich) NULL verwenden, keine Ergebnisse zurück, weil jeder Vergleich mit NULL nur NULL ergibt.

In objektorientierten Sprachen kann ein Objekt NULL sein, etwa weil sein Konstruktor noch nicht aufgerufen wurde. Versucht man, aufWerte oder Methoden eines NULL-Objektes zuzugreifen (z. B. print user. firstname), wird das Programm mit einer Nullpointer-Exception oder einem ähnlichen Fehler beendet, denn NULL hat keinen firstname.

Boolesche Variablen, die eigentlich nur true oder false enthalten können, enthalten möglicherweise nichts, sondern sind NULL. Häufig muss man das in if/then/else-Bedin-gungen beachten - in SQL zum Beispiel ist ein Vergleich mit NULL immer NULL, in Java immer false. In JavaScript ergibt ein Vergleich der Art if myVar == null immer false -es sei denn, die Variable ist undefined oder null, dann ergibt der Vergleich true. JavaScript und auch PHP besitzen zusätzlich den Operator ===, der bei diesem Vergleich false für undefined und true für null zurückgibt.

Tröstlich: NULL in Variablen führt häufig zu schnell erkennbaren Fehlern.

Weniger tröstlich: Die genaue Bedeutung von NULL hängt stark von der Sprache ab. Man tut daher gut daran, sich fürjede Sprache wieder von Neuem mit dem Verhalten von NULL vertraut zu machen.

Rekursion

Anwendungsgebiet: Verarbeitung hierarchischer Daten (zum Beispiel Unterkategorien von Kategorien), generell alle baumartigen Strukturen

Folgen der Ahnungslosigkeit: Ihr Programm reißt den gesamten verfügbaren Speicher an sich und produziert 100% CPU-Auslastung, so dass der Computer nur noch sehr träge reagiert. Das liegt daran, dass Sie die Funktion, die sich immer wieder aufruft, mit einem fehlerhaften Kriterium für den Rekursionsabbruch geschrieben haben. Sie würde sich unendlich weiter aufrufen, wenn nicht irgendwann der verfügbare Speicher voll wäre.

Mindestwissen: Mit Rekursion kann man sehr kompakten und eleganten Code schreiben, wenn man es mit baumartigen hierarchischen Datenstrukturen zu tun hat.

Alles, was man mit Rekursion machen kann, geht auch ohne, es ist nur häufig mehr Schreibarbeit.

Wenn man ein Problem rekursiv löst, sollte man ein Auge auf die maximale Größe der Datensätze haben, die man damit verarbeiten wird. Während nichtrekursive Ansätze in der Regel bei wachsender Datenmenge einfach länger brauchen, führt bei rekursiven Ansätzen die zunehmende Schachteltiefe irgendwann zum Programmabbruch.

Wenn man eine maximale Schachteltiefe definieren kann, sollte man ihr Überschreiten als Abbruchkriterium der Rekursion nutzen.

Hier sehen Sie ein Beispiel, nämlich eine Funktion, die rekursiv einen endlosen String ausgibt:

main () {

print "ich bin eine Funktion, "; subfunction ();

}

function subfunction () {

print "die eine Funktion aufruft, "; subfunction ();

}

Aus dem Beispiel kann man schon etwas Wichtiges über Rekursion lernen: Mit ein paar Zeilen Code kann man endlose Datenmassen erzeugen. Oder den Rechner lahmlegen. Oder zumindest das Programm abstürzen lassen.

Tröstlich: Fehlerhafte Rekursionen fallen einem häufig schon beim ersten Test auf die Zehen. Im Gegensatz zu manchen obskureren Fehlerquellen produzieren sie ein ziemlich deutliches Fehlerbild, zum Beispiel Stack Overflows, »Out of Memory«-Fehler oder aufheulende Lüfter. Heutzutage muss man wegen einer fehlerhaft programmierten Rekursion auch den Rechner nicht mehr neu booten.

Usability

Anwendungsgebiet: Überall

Folgen der Ahnungslosigkeit: Die Benutzer Ihrer Software ärgern sich und verbringen mehr Zeit als nötig mit Nachdenken und Haareraufen und zu wenig Zeit mit der Abschaffung von Malaria und Postidentverfahren. Sie als Autor verbringen mehr Zeit als nötig mit Support und der Reparatur der mit einem falschen Klick von Nutzerhand zerstörten Teile Ihrer Anwendung. Mittelfristig verlieren Sie den Glauben an die Intelligenz Ihrer Mitmenschen und werden zum Wähler konservativer Parteien.

Mindestwissen: Wenn Sie Anwendungen oder Websites entwerfen, die auch von anderen Menschen als Ihnen selbst genutzt werden, lohnt es sich wirklich, eines der zahlreichen guten Blogs und Bücher über Usability zu lesen. Hier folgt das Wichtigste in Kürze.

Der Nutzer ist nie schuld. Wenn Ihre Nutzer sich beim Umgang mit Ihrer Software anstellen wie Höhlenmenschen, ist es Ihr gutes Recht, sie dafür zu beschimpfen und sich mit der flachen Hand vor die Stirn zu schlagen. Aber nur heimlich im Keller. Nein, nicht in Ihrem Blog. Nein, auch nicht im engen Mitarbeiterkreis. Denn in Wirklichkeit ist es Ihre Software, deren Unzulänglichkeit die Nutzer dazu verführt, das Falsche zu tun. Wenn Ihre Nutzer nachdenken möchten, dann lösen sie ein Sudoku oder belegen Onlinekurse am MIT. Ihre Anwendung hingegen soll kein Nachdenken erfordern, sondern auch von Höhlenmenschen zu bedienen sein. Von betrunkenen Höhlenmenschen.

Der Nutzer ist nie schuld. Man kann es nicht oft genug sagen.Wirklich nie.

Wenn es für einen bestimmten Zweck eine etablierte, bekannte Lösung gibt (z. B. Scrollbalken, die Formulierung »Login/Registrieren<<, die Konvention, dass ein Klick auf das Websitelogo zurück zur Startseite führt), dann weichen Sie von dieser Lösung bitte nicht ab. Es gibt seltene Ausnahmefälle, in denen Innovationen an solchen Stellen gerechtfertigt sein können, aber wenn Sie nicht leitender Mitarbeiter einer Apple-Entwicklungsabteilung sind, betrifft Sie keiner davon.

Gehen Sie nicht zu sehr von sich selbst aus. Der Nutzer Ihrer Software ist womöglich ein Farbenblinder aus einem Land, in dem von rechts nach links geschrieben wird, und er betrachtet Ihr Werk auf dem 30 x 1280 Pixel großen Display seiner Espressomaschine (japanisches Modell mit Internetzugang), ohne die Hände vom Lenkrad zu nehmen. Na gut, das Beispiel ist ein ganz klein wenig übertrieben. Nehmen Sie einfach an, dass andere Nutzer ein etwas kleineres, etwas schlechteres Endgerät als Sie benutzen und zwei Dioptrien mehr haben.

Wichtiges und Übliches sollte groß und leicht zu klicken sein. Gefährliches und Unübliches ist klein, anders gestaltet und an einem nicht so naheliegenden Ort untergebracht.

Zusätzliche Erklärungen sind im Idealfall unnötig. Woes nicht ohne sie geht, bringt man sie am besten genau dort unter, wo sie gebraucht werden. Fehlermeldungen zu einem falsch ausgefüllten Formular gehören nicht an den Anfang oder das Ende der Seite, sondern neben das betreffende, farbig hervorgehobene Feld. Hilfetexte sollten nicht auf unvcrlinkte Dokumente verweisen, die der Nutzer dann erst mühsam selbst ausfindig machen muss.

Tröstlich: Trotz einiger Fortschritte in den letzten Jahren ist die Aufmerksamkeit von Programmierern für Usabilityfragen immer noch so gering, dass Ihre eigenen Untaten und innovativen Benutzeroberflächen gar nicht besonders auffallen werden.

1

Zeichenkodierung I 229