namespace cpp

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


lernen:ungarn

Kérem ket kílo kényér. - Kőszönöm.

Hungarian Notation is the tactical nuclear weapon of source code obfuscation techniques; use it! […] Consider this real world example: a_crszkvc30LastNameCol. It took a team of maintenance engineers nearly 3 days to figure out that this whopper variable name described a const, reference, function argument that was holding information from a database column of type Varchar[30] named LastName which was part of the table's primary key. […] One follow-on trick in the Hungarian notation is "change the type of a variable but leave the variable name unchanged". This is almost invariably done in windows apps with the migration from Win16
WndProc(HWND hW, WORD wMsg, WORD wParam, LONG lParam)
to Win32
WndProc(HWND hW, UINT wMsg, WPARAM wParam, LPARAM lParam)
where the w values hint that they are words, but they really refer to longs. The real value of this approach comes clear with the Win64 migration, when the parameters will be 64 bits wide, but the old w and l prefixes will remain forever.
— Roedy Green: How to write unmaintainable code
Verzichten Sie auf die ungarische Notation. C++ ist streng typisiert, und es gibt keinen Grund, den Typ im Variablennamen zu wiederholen. Bei benutzerdefinierten Typen (Klassen) ist die ungarische Notation schnell am Ende. Ausnahmen von dieser Regel können Präfixe sein, die Sie vor die Namen von Zeigern (p), Referenzen (r) oder Elementvariablen einer Klasse (m_) setzen.
— Jesse Liberty: C++ in 21 Tagen
We have made an explicit decision to not use Hungarian Notation.
— Keith Gabryelski: Wildfire C++ Programming Style

Dieser Text diskutiert Vor- und Nachteile der sogenannten ungarischen Notation. Notwendig ist diese Diskussion, da diese Notation in Microsoft-C/C++-Programmierwerkzeugen (Windows-APIs und MFC-Anwendungsrahmen) genutzt wird und von da aus auf eigene Anwendungsprogramme ausstrahlt.

Ungarisch für Hungrige

Kérem ket kílo kényér. - Kőszönöm.

Eine Lektion Ungarisch. Zuerst zählen wir: Égy, kettő, három, … Um ehrlich zu sein, ich habe vergessen, wie weitergeht. Es ist 20 Jahre her. … Mit dem obigen Zungenbrecher habe ich in einem Dorfladen das Personal zum Schmunzeln gebracht - und das Gewünschte erhalten.

Neben Erinnerungen an Fahrradurlaube habe ich als Physiker auch ein beeindruckendes Buch eines ungarischen Akademikers im Regal [Károly Simonyi: Kulturgeschichte der Physik]. Der Sohn desselben heißt auch Charles Simonyi (nur amerikanisch geschrieben) und führte als Programmierer bei Microsoft die Bezeichnungsweise ein, die Uneingeweihten zurecht "hungarian" erscheint. (Wir würden sagen: Das kommt uns spanisch vor.)

Ungarische Notation

Die Grundidee von Simonyis Schreibweise ist, jedem Bezeichner (Variablennamen u.a.) Kürzel (Präfixe) voranzustellen, die den Typ verraten:

int   nAnzahl;             // n    number
int*  pnZeigerAufGanzzahl; // p    pointer
char* lpszName;            // lpsz long pointer to string zero terminated
HWND  hWndFenster;         // h    handle (Zeiger auf verdeckte Struktur)
class CKlassenname {       // C    class
  int mnGanzzahlAttribut;  // m    member
  //  ...
};

Namen in einer Bibliothek werden weitere (Hersteller-)Kürzel wie

RW...  (Rogue Wave)
wx...  (wxWindows-Bibliothek)
Q...   (TrollTech's Qt)
gtk... (Gimp tool kit)

vorangestellt, um Kollisionen zwischen gleichnamigen Prozeduren unterschiedlicher Bibliotheken umgehen. Eine ausführliche Beschreibung (der Original-Aufsatz von 1972) findet sich in der MSDN-Bibliothek [msdn] und wurde durch [Petzold] und die Windows-API verbreitet. Mittlerweile gibt davon viele Spielarten. Für Visual Basic gibt es über 100 Kürzel zu memorieren…

Für und Wider

Zur Einführung der Schreibweise trug der laxe Umgang von C mit den Typen bei. Die Typisierung ist nicht streng. Auch inkompatible Typen können ineinander umgewandelt werden. Das kann ein Segen sein, wenn man genau weiß, was man gerade tut:

char * const screen = 0xB8000000; // screen address in DOS text mode

(C-Programmierer sollten das immer wissen!), aber auch ein Fluch. Wie schnell erfolgt unabsichtlich eine Zuweisung eines int-Wertes an einen Zeiger oder umgekehrt, weil ein * vergessen wurde. C-Compiler akzeptieren nahezu alles, ohne unerfahrene oder unachtsame Programmierer zu warnen. Aus diesem Grund wurden sogar Werkzeuge wie lint geschaffen, die Schludrigkeiten aufdecken sollen. Erinnert sei an den "Unix hell trip", als beim Übergang auf ein anderes System der Quelltext eines UNIX-Derivats (Sun-OS auf System V) auf mögliche Fehler getestet wurde. Die lint-Prüfung ergab eine fünfstellige Zahl von Warnungen.

Simonyis Stil bringt beim Benutzen einer Variable ins Bewusstsein, welcher Typ gerade vorliegt. Dies kann vorteilhaft sein. Aber auch Nachteile können nicht übersehen werden.

Der erste Einwand ist subjektiv. Präfixe erschweren die Lesbarkeit des Quelltextes:

dGesamtpreis = dEinzelpreis * nAnzahl;  // mit nur einem Prefix
gesamtpreis  =  einzelpreis * anzahl;   // noch nicht einsichtig?

Die Präfixe können sich, konsequent genutzt, zu unaussprechbaren Vorsilbenmonstern auswachsen.

Ein zweiter, schwererer Einwand: Die starre Bindung der Bezeichner an irgendwann willkürliche festgelegte Typen behindert den Typwechsel dann, wenn er notwendig wird. Nicht nur der Typ bei der Deklaration, sondern auch der Variablenname muss überall in schon getestetem Code geändert werden. Oder man macht es wie Microsoft, dass Typ und Variablenpräfix sich auseinander entwickeln. Schließlich sind auf 32-Bit-Systemen alle Pointer long pointer. Faktisch ist die Notation nur eine Kommentierungstechnik. Und das oberste Gesetz aller Kommentare ist, dass sie lügen, sagt Robert C. Martin in [ootips].

Stimmen Quelltext und Kommentar nicht überein, so sind vermutlich beide falsch…

Daraus ergibt sich ein dritter, noch schwererer Einwand: Abstraktionen sollen stabil, die Implementation jedoch flexibel und anpassbar sein. Der Durchgriff der untersten Schicht führt zum umgekehrten Verhalten: Am stabilsten ist der Unterbau (Abb.). Entwurfsentscheidungen sind einfacher zu ändern als die Umsetzungsdetails. Deutlicher gesagt: Es ist einfacher, einen Entwurf zu unterlaufen als ihn zu erfüllen [Martin, S. 237].

 Allgemeinheit
 Abstraktion                       Abstraktion
 ^                                 ^
 |Architektur                      |        Architektur
 |   *                             |             * 
 |     *    "Hauptreihe"           |           *
 |       *                         |         *
 |         *                       |       *
 |           *                     |     *
 |     Implementations-            |Details
 |            Details              |Daten
 +------------------>              +------------------>
 stabil      instabil              stabil      instabil

Abb. Wie Software sein sollte und wie sie traditionell ist [Martin, S. 236f].

Vierter Einwand: Die Variablennamen auf Typen festzulegen, läuft dem Prinzip der Abstraktion zuwider, elches grundlegend für den Objekt-Gedanken in der Programmierung ist. Welchen Sinn macht es, darüber nachdenken zu müssen, ob die message nun eine lpszMessage oder eine RuntimeExceptionMessage ist? Von welcher Art die miteinander agierenden Objekte sind, ergibt sich erst im Verlauf des Entwurfes. Meist ergeben sich während der Entwurfszyklen noch mehrfache Änderungen. Software-Entwicklung ist eine langfristige, iterative Tätigkeit. Entwickeln Sie Software so, dass sie Änderungen gewachsen ist. Lesen Sie die letzten drei Kapitel von [Stroustrup], die sich mit dem Software-Prozess und nicht mit Sprachdetails befassen.

Alternative Sprachmittel in C++

Die Typprüfung in C++ ist strenger als in C, wenn auch nicht vollständig. Die Umwandlung von Zahlen in Zeiger und umgekehrt muss bewusst erfolgen. Schalten Sie alle Warnungen ein (compilerspezifisch #pragma warn all, Option -wall …) und beherzigen Sie diese. Schreiben Sie Quelltext so, dass er sich wie ein Roman liest (hoffentlich ein spannender). Verhunzen Sie ihren lpszQuelltext nicht mit dwUnzaehligen MSVChWndPrefixen! Namensräume lassen qualifizierte Angaben zu, welcher Name aus welcher Bibliothek importiert wird. Mittlerweile sind Namensräume Bestandteil aller Compiler, die den ANSI-C++ Standard ISO 14882:1998 implementieren.

Wir sind alle infiziert

char s[] = "Hallo Welt";
int a[10];
for(int i=0; i<10; i++)
  a[i] = i*i;

Haben Sie solchen Quelltext schon gesehen? Fällt Ihnen etwas auf? Richtig. Da ist sie! Die Zeichenkette s = string, das Feld a = array, die Ganzzahlvariable i = integer oder index sind auch schon eine Art Ungarische Notation. Der eigentliche Name fehlt ganz, das Wort besteht nur aus dem Präfix. Für use-and-forget-Bezeichner ist das übliche Technik. Wegen der Buchstabenknausrigkeit wird das manchmal auch schwäbische Notation genannt. Bei genauem Nachdenken ist die ungarische Notation schon so alt wie die erste höhere Programmiersprache. Fortran hat(te) implizite Typregeln: i, j, k, l, n, m sind INTEGER, die anderen Buchstaben am Namensanfang legen REAL-Typ fest.

GOD is REAL unless declared INTEGER.

Ist das wieder nur ein Krieg um Worte? Nein, einer um Vorsilben, um unaussprechliche.

Die Ungarn sind schuld...

… und das alles wird den Ungarn angelastet. Arme Ungarn. Was mich daran ärgert: In der ungarischer Notation ist immer von Vorsilben (Präfixen) die Rede. Dabei ist Ungarisch, soweit ich das weiß, eine konsequente Postfix-Sprache. Die Flexion / Konjugation erfolgt über Nachsilben:

Kőszönöm. (Ich danke.)
Kőszönjünk. (Wir danken.)

Die ungarische Sprache betont immer die erste Silbe. Versuchen Sie mal, den Oberbegriff für "Messer, Gabel, Löffel" auf ungarisch auszusprechen: Észbestek. (Achtung, joke!) Das sz ist im Unterschied zum Polnischen (es gibt auch eine Polnische Notation, sogar eine Umgekehrt Polnische Notation, auf alten Taschenrechnern) kein Zischlaut, sondern der S-Laut, wogegen s als sch auszusprechen ist: In Székesféhervár (Felsenweißburg) wurde der Ikarus-Bus gebaut. ny wird als weiches n gesprochen. Akzentzeichen über Vokalen á é í ó ú, auch bei ő und ű mit schrägen Strichen, dehnen den Laut. Die Ungarn sollen meinen bescheidenen Kenntnissen nach sehr empfindlich auf Aussprache-Unterschiede achten: Egészségedre ("Auf dein Wohl") und egész seggedre ("Auf deinen Hintern" oder etwas ähnlich Beleidigendes) unterscheiden sich wohl nur in einer Vokallänge oder in einem Laut. (Wer's besser weiß, soll mich bitte be[nach]richtigen!) Hüten Sie Ihre schwere Zunge, wenn Sie aus dem Weinkeller kommen… Geringe semantische Distanz ist nicht nur im Ungarischen gefährlich: Benennen Sie Variablen immer so, dass einfache Buchstabenverwechslungen einen Compilerfehler erzeugen und nicht ein (stillschweigend) falsches Programm, das seine Werte woanders holt/speichert, als Sie eigentlich geplant hatten.

Und falls Sie in Ungarn mal Hunger, nichts zu essen, und keinen Selbstbedienungsladen haben, sondern die Verkäuferin um das bitten müssen, was Ihnen fehlt, gestikulieren Sie nicht wild herum. Man könnte Sie für einen dieser ungehobelten (deutschen) Ausländer halten. Sagen Sie einfach:

Kérem ket kílo kényér. 

(Bitte zwei Kilo Brot. — Damals und auf dem Lande gab es Brot noch vierpfundweise.) Sie werden nicht nur Brot, sondern auch noch ein Lächeln ernten — für Ihre schlechte Aussprache. Das (Weizen-)Brot ist übrigens sehr hell und würde bei uns Weißbrot heißen. Werden Sie daraufhin gefragt, wo Sie herkommen, können Sie immer noch sagen: Mí éz német. (Ich bin deutscher. — Und bitte: Sprechen Sie "deutscher" auch klein aus…)

In diesem Sinne: Jó nápot kívanok. (Guten Tag wünsche ich.) oder: Jó nápot kívanjünk. (… wünschen wir.)

Quellen

Weiterführende Literatur:

  • [Petzold] Charles Petzold: Programming Windows. Microsoft Press.
  • [Stroustrup] Bjarne Stroustrup: Die C++ Programmiersprache. 3. oder 4. Auflage. Addison-Wesley (1997). ISBN 3-8273-1296-5

P.S. Als Simonyi im Frühjahr 2007 als Astronaut um die Welt flog, habe ich mir den [msdn]-Artikel noch mal angesehen. Er enthält im letzten Teil folgendes <Zitat>"real-life" situation</Zitat>-Kleinod:

14      while (*pch!=0
15         wHash=(wHash<>11+*pch++;

Dies ist nun wahrlich kein C-Quelltext… Der letzte Satz des Artikels ist allerdings richtig:

Naming conventions cannot guarantee good code, however; only the skill of the programmer can.
— Charles Simonyi
lernen/ungarn.txt · Zuletzt geändert: 2014-07-13 16:15 (Externe Bearbeitung)