Etwas für C-progger, die gern englisch lesen…

Ich finde es ja faszinierend: eine relativ kurze C-funkzjon, die vorsätzlich so geschrieben wurde, dass sie je nach gesetzten kompeiler-fläggs beim aufruf von gcc jeden int-wert zwischen 0 und 767 zurückliefern kann, dokumentiert, dass ohne genaue kenntnis der fläggs und argumente, mit denen der kompeiler aufgerufen wird, nicht vorausgesagt werden kann, was für ein ergebnis bei einem programmlauf rauskommt. 😖

Der link geht auf ein PDF in englischer sprache.

Klar ist dieser demonstrazjonskohd am ende relativ weit weg von kohd, der in realen programmen auftritt. Aber jede konstrukzjon für sich allein ist auch nicht so untypisch, dass sie rein akademisch wäre. Und es ist völlig korrektes C. Leider.

In was sind noch mal unsere betrübssysteme gekohdet? 😧

Tja, wenn man kohd schreibt, der von der implementazjon abhängt, dann bekommt man eben ergebnisse, die von der implementazjon abhängen — und in C ist es sehr, sehr einfach, solchen kohd zu schreiben. Zum beispiel hängt der wert einer literalen konstanten wie -1U von der implementazjon ab, in diesem fall von der anzahl bits in einem unsigned int und von der repräsentazjon vorzeichenbehafteter ganzzahliger werte in der CPU (zum glück gibt es inzwischen nur noch die darstellung als zweierkomplement). Und ein völlig legaler ausdruck wie (int)-1U/2 gehört dann besser nicht zu den dingen, die man beim kohden tippt. Auch nicht indirekt, etwa, indem man statt direkt -1U eine variable vom typ unsigned int mit wert 1U verwendet und da ein minuszeichen vorschreibt. Natürlich kann die deklarazjon der variablen im kwelltext meilenweit von ihrer verwendung entfernt sein, so dass man ihren typ nicht immer vor augen hat… 😦

Ein statisches analyse-werkzeug wie das olle lint oder das freie splint kann zwar mit der opzjon -strict ganz schön nerven und hirnen, macht aber auch klar, wo mögliche probleme liegen. Dies leider in einem fürchterlichen wust des nicht so wichtigen, aber dafür pedantischen und neurotischen fehlermeldens. Ich habe noch nie ein nichttriviales stück C-programm geschrieben, das ohne erschröckliche meldungen durch splint -strict durchgegangen wäre.

Wer mit solchen problemen nicht leben kann, sollte vielleicht besser in einer anderen sprache als ausgerechnet C (oder seinem bastardkind C++) kohden. 😉

C++

Damals, etwas vor meiner zeit, sagten einige über die programmiersprache C, sie sei ein B mit einem typsystem. Und etwas später, deutlich in meiner zeit, sagte Bjarne Stroustrup, dass er mit C++ begonnen habe, um ein C mit klassen zu haben. Ich würde mir sehr wünschen, dass jetzt noch jemand C++ (meinetwegen zu D++) inkrementiert, um ein C++ mit einem konzept zu erschaffen…

GNU des tages

WARNING, CLUSTERFUCK AHEAD!

Programmiert hier jemand in C++ und benutzt die libstdc++ von GNU und hat komische speicherlecks? Oder benutzt hier jemand einen modernen desktop und wundert sich, dass das dingens immer langsamer und plattenkratziger wird, wenn man es mal ein paar tage laufen lässt? Oder verwendet hier jemand eine in C++ geschriebene anwendung mit ganz großem speicherhunger?

The GNU C++ Library is Broken

Es ist etwas länger, aber die lektüre lohnt sich… auch, weil jeder mal bemerken kann, wie viel kopfschmerz so eine scheiße verursachen kann. Das beschriebene problem mit dem speicherleck in der libstdc++ dürfte (in geringerem maße) beinahe jeden menschen betreffen, der eine gegen die GNU-biblioteken gelinkte, in C++ geschriebene softwäjhr verwendet.

Via Fefe

Meikrosoft des tages

Könnt ihr euch noch erinnern, wie meikrosoft damals den erfolg der programmiersprache java gesehen hat und prompt (nach einem von sun gestoppten versuch, ein zu java inkompatibles java an seine gläubigen mikrosklaven zu bringen) damit angefangen hat, die ganzen konzepte einmal selbst völlig inkompatibel nachzuproggen, in die eigenen entwicklungswerkzeuge einzubetten und das ergebnis .NET zu nennen. (Das müsst ihr verstehen, zu der zeit war alles ganz toll, wenn irgendwie drin klang, dass es irgendwas mit netzen oder gar diesem internetz zu tun habe oder gar besonders dafür geeignet sei, und deshalb der etwas alberne name. Tatsächlich ist .NET recht brauchbar.)

Warum mir das gerade wieder einfällt. Nun, es gibt zurzeit eine vielversprechende, allerdings noch in der entwicklung befindliche programmiersprache namens rust, die einerseits systemprogrammierung möglich macht, aber andererseits viele sicherheitsrelevante fallen und probleme der programmiersprache C vermeidet.

Ich halte rust für eine programmiersprache der zukunft — zumindest überall dort, wo es auf hardwäjhrnahes proggen und effizjenz ankommt, so etwas wie benutzerschnittstellen kann man auch in einer skriptsprache schreiben.

C ist wirklich gut geeignet für systemprogrammierung. Das sieht man allein schon daran, dass diese programmiersprache aus den siebziger jahren mit verhältnismäßig wenigen veränderungen (ja, prototypen für funkzjonen haben sie irgendwann eingeführt, und das war ein segen) immer noch aktiv benutzt wird. Viereinhalb verdammte jahrzehnte später! Das sind zwei dutzend erdzeitalter der informatik! Es gibt sonst keine so alte programmiersprache mehr, bei der man sagen kann, dass grundkenntnisse in dieser sprache zur allgemeinbildung eines programmierers gehören. (Vor anderthalb bis zwei jahrzehnten hatten einige progger noch so viel mit dem ähnlich alten FORTRAN zu tun, dass es ebenfalls zur allgemeinbildung gehörte, zu wissen, dass FORTRAN beim funkzjonsaufruf keine werte auf dem stack legt, sondern adressen von werten.)

Die eignung von C ist hier seine einfachheit; es handelt sich um eine sprache, die sehr direkt in instrukzjonen für einen prozessor übersetzt werden kann. Es gibt in C keine komplizierten, abstrakten konzepte. Alles sind structs, funkzjonen, variablen und adressen dieser objekte, ergänzt um den dreck, den der präprozessor ermöglicht — und diese werden in übersetzungseinheiten (in der regel: einzelnen kwelltext-dateien) organisiert. Die sprache selbst ist sehr minimal und dabei richtig gut entworfen, aber auch völlig ohne jedes sicherheitsnetz; die in die siebziger jahre zurückgehende standardbiblotek ist zwar ebenfalls praktisch, aber teilweise fürchterlich entworfen (man fragt sich manchmal, ob mehr kiffe oder doch noch ein bisschen LSD daran beteiligt war) und macht es sehr leicht, sehr schlimme fehler zu machen. C wird nicht benutzt, weil es toll ist. C wird benutzt, weil es da ist, weil es standardisiert ist, weil es gute kompeiler dafür gibt und weil es so leistungsfähig ist, dass man ein ganzes betrübssystem damit schreiben kann.

An der stelle setzt rust an. Es ist eine sprache, die viele sicherheitsprobleme von C beseitigt, aber immer noch effizjenten kohd erzeugt (etwa so effizjent wie gut programmiertes C++) — und damit rust für die systemprogrammierung geeignet ist, gibt es die möglichkeit, kohdpassagen als „unsicher“ zu kennzeichnen und damit explizit ohne sicherheitsnetze zu proggen. Das ist ja auch nötig, wenn man etwa einen treiber für ein stück hardwäjhr schreiben will. Der vorteil dieser vorgehensweise liegt darin, dass der potenzjell gefährliche kohd explizit vom programmierer ausgezeichnet ist, mit werkzeugen aufgespürt und für eine analyse isoliert werden kann. In C ist jede verdammte zeile gefährlich, und ein sicherheitskritischer fehler (der etwa wild speicherbereiche überschreibt oder ausliest), kann sich überall befinden und beim überfliegen des kohds unfassbar harmlos aussehen. Es ist leicht, in C fehler zu machen, und es ist schwierig, in C fehler zu finden.

Ich hoffe, dass jetzt auch völligen laien klar geworden ist, warum ich rust für eine programmiersprache der zukunft halte. Im moment ist rust noch zu jung und zu sehr in der entwicklung begriffen, als dass jemand ein größeres projekt nach rust portieren würde oder ein großes neues projekt in rust beginnen würde. Das wird sich aber ändern, denn rust wird reifen. Der bedarf an sicherer und effizienter programmierung ist da, und er wird nicht kleiner werden. Es sollen ja demnächst autos „autonom“ am straßenverkehr teilnehmen… oh, da pfeift man noch auf security-erwägungen… na, das wird man irgendwann nicht mehr tun.

Lange einleitung für eine kurze meldung, ich weiß. Hat einige menschen gelangweilt, ich weiß.

Und jetzt die meldung: Bei meikrosoft hat man das — wie üblich in ziemlich später geburt — auch gemerkt und unterstützt jetzt nicht etwa die entwicklung von rust mit eigenen beiträgen und mit portierungen von softwäjhr, bei der es auf sicherheit ankommt, sondern versucht wie damals bei java mit .NET eine art eigenes rust namens „checked C“ zu implementieren. Wäre ja auch — aus der sicht von etisch verrotteten marketingheins — schade, wenn die programmiersprache der zukunft der „mozilla foundation“ gehörte und frei wäre…

Es ist eben immer noch meikrosoft, böse und hinterfotzig wie eh und je… 😦

Wie wir früher mit arrays proggten…

Früher, als man seine softwäjhr noch so geschrieben hat, dass sie den rechner nicht in die knie zwang und so, dass sie nach möglichkeit einfach gut lief, hatten wir natürlich auch schon das problem, dass die größe eines array manchmal erst zur laufzeit feststand. Tja, dann haben wir halt mit malloc() eine ausreichend erscheinende menge speicher alloziert, uns gemerkt, wieviel das ist und wenn wir beim einfügen an die grenzen kamen, haben wir den speicherbereich mit realloc() vergrößert — in der regel durch schlichte verdopplung¹ der allokazjon. Und wenn man sortiert einfügen wollte, einfach mit binärer suche den ort zum einfügen des neuen elementes gefunden und mit memmove() platz für das neue element geschaffen, dass wir dann an die richtige stelle brachten. Nicht, dass so etwas spaß macht, aber es funkzjoniert und ist halbwegs performant.

Tja, das waren die alten zeiten, in denen man altmodische sprachen wie C verwendet hat, die einem nicht alles am kompjuter abstrahierten. Heute hingegen, heute ist alles viel viel besser. Heute fordert man einfach für jedes eingefügte element einen neuen speicherbereich an, der für ein element mehr platz bietet, kopiert den alten speicherbereich in den neuen und fügt das neue element an, um zum einfügen an die richtige stelle eine allgemeine biblioteks-sortierfunktion (meistens als mergesort oder quicksort implementiert) aufzurufen. Am besten in einer inneren for(;;)-schleife, also für eine vorab bekannte anzahl von fällen nacheinander. Und damit man nicht sofort merkt, wie hirnrissig das ist, notiert sich das ganze in schönster C++-syntax. So wird aus ganz gewöhnlicher datenverarbeitung eine implementazjon, die auch auf zeitgemäßer hardwäjhr ganz schön lahmarschig läuft. Und es gibt keine elektronen im kompjuter, die sich dabei langweilen.

Ich führe ja einen großteil der unbefriedigenden performanz „moderner“ anwendungen auf solche idjotie zurück…

¹Technischer hinweis: ursprüngliche allokazjon ist gern eine zweierpotenz minus eins, und zum vergrößern wird dieser wert verdoppelt und eins addiert, so dass wieder eine zweierpotenz minus eins rauskommt, also 255, 511, 1023, 2047, etc. Natürlich kann man das oft — je nach konkreter anwendung — ein bisschen intelligenter machen, aber frühzeitige optimierung ist eine der häufigsten und garstigsten fehlerkwellen, und diese schlichte implementazjon ist ganz brauchbar. Ziel des vorgehens ist es, die relativ aufwändige und langsame anforderung von speicher selten zu machen.

Der erfolg der programmiersprache C

Der vermutlich wichtigste grund, warum sich die programmiersprache C seit anfang der siebziger jahre (also über mehrere erdzeitalter der kompjutertechnik hinweg) gehalten hat, liegt darin, dass beim entwurf der programmiersprache nicht ein kaufmann die entscheidungen über sprachmerkmale getroffen hat, der kein besonderes technisches verständnis hatte und vor allem seine reklameliste mit bullschittmerkmalen abhaken wollte… und so konnte in aller ruhe ein brauchbares und vor allem minimalistisches desein der sprache erstellt werden.

Ich bin davon überzeugt, dass verkäufer der größte hemmschuh des technischen fortschritts sind.

Die ideale erste programmiersprache…

C (oder C++) sollte die erste programmiersprache sein

Die erste sprache sollte nicht eine einfache sein, sie sollte den sinn des lernenden ausbilden und auf ernsthafte informatik vorbereiten.

C ist perfekt für diesen zweck. Es zwingt lernende über speicherverwaltung und dieses ganze maschinennahe zeugs nachzudenken, und währenddessen können sie weiterhin lernen, wie man seinen kohd strukturiert.

C++ hat den zusätzlichen vorzug, dass es wirklich scheiße ist. So werden die lernenden verstehen, warum leute mit diesem java und C# angekommen sind.

Punkt neun in dieser großartigen liste 😀

-Wall -pedantic

Wie, was ist das denn für eine warnung?! Meint der kompeiler wirklich, dass eine stringkonstante in ISO-C maximal 509 zeichen haben darf?! 509! Eine krummere zahl könnte man sich kaum ausdenken…

(Ja, ich kann mir denken, dass es sich wohl um insgesamt 511 reservierte bytes handelt und dass irgendjemand gedacht hat, dass es hübsch wäre, wenn die ersten beiden bytes auch noch die länge der zeichenkette angeben. 511 ist schließlich eine „glattere“ zahl, eben einer weniger als 29, das, was man beim häcken als glatte zahl empfindet.)

Manchmal sollte man…

Manchmal sollte man blogeinträge von fröhlichen häckern besser vom ende her lesen. Als ich eben bei fefe las, wie er eine „einfache“ mustererkennung in einem string optimiert hat, da fragte ich mich ja beim lesen eine geschlagene viertelstunde lang, ob ein hartgekohdeter endlicher automat mit mindestens -O2 beim übersetzen nicht doch etwas performanter wäre und dachte schon an ein paar fröhliche (und angesichts des „problemes“ eher sinnfreie) experimente, bis ich dann endlich bei einem kaum noch wiederzuerkennden und praktisch unverständlichen stück kohd angekommen war und den abschluss las:

Update: Der eine oder andere Spezialist auf dem Gebiet wird jetzt einwenden wollen, dass wir besser einen Boyer-Moore benutzen sollen. Stimmt, ist auch trotz des kurzen Musters schneller. Aber ergibt keinen so schön eindrucksvollen Blogpost.

Und dann war meine welt wieder in ordnung… 😉

Selten genug, dass ich mal wieder fühle, dass die häckerei noch freude bereiten kann.