Die Problematik der Arrays



  • Oft beklagen sich Leute, wie kompliziert und fehleranfällig statische C-Arrays handzuhaben seien, was ja auch berechtigt ist. Die ganze spezielle Semantik kann einem, gerade wenn man noch nicht sehr viel Erfahrung besitzt, schon zu schaffen machen. Denke man zum Beispiel an Parameterübergaben von Arrays oder die implizite Konvertierung in einen Zeiger.

    Nun gut, man kann damit leben, mit der Zeit kennt man sich schliesslich besser aus. Ausserdem bieten einem die Standardbibliothek und Boost schöne Möglichkeiten, Arrays zu umgehen.

    Ich frage mich trotzdem, was die ursprüngliche Motivation der Entwickler von C war, Arrays so in den Sprachgebrauch aufzunehmen, wie sie jetzt vorhanden sind. Manchmal überlege ich mir, wie es wohl wäre, sie wie Structs zu handhaben: Kopie bei Parameterübergabe, Zuweisung erlaubt, Array wird mehr als Objekt angesehen. Die Zeigerarithmetik über operator[] bzw. operator* und operator+ könnte ja trotzdem erhalten bleiben, um auch direkt auf dem Arbeitsspeicher herumzuwerkeln.

    Da ich mir ehrlich gesagt nicht ganz erklären kann, weshalb Arrays gerade so umgesetzt wurden, frage ich euch um Rat. Waren Arrays aufgrund der maschinennahen Programmierung in C am praktischsten, wie sie jetzt sind?



  • Ja, man wollte ursprünglich eine relativ direkte Beziehung zwischen Quellcode und Maschinencode. Das ist die Einfachheit von C. Jeder Operator bewirkt eine relativ triviale Operation, will man mehr, muss man auf Bibliotheksfunktionen (wie strcpy) ausweichen. In C konnte man ursprünglich übrigens auch keine Strukturen zuweisen oder by-value übergeben.

    C++ verfolgt ein gänzlich anderes Konzept, muss sich aber trotzdem mit diesem Unterbau rumschlagen.



  • Ein Array ist nun einmal die schlichteste Repräsentation der Speicherstruktur. So genau liegt das Teil im Speicher und kann auch eben so bearbeitet werden. Alles andere ist Luxus.



  • Nexus schrieb:

    Ich frage mich trotzdem, was die ursprüngliche Motivation der Entwickler von C war, Arrays so in den Sprachgebrauch aufzunehmen, wie sie jetzt vorhanden sind.

    Die C-Syntax ist nur eine minimale Abstraktion gegenüber den Assembleranweisungen, und in Assembler kennt man in die indirekte Adressierung. Daher kommt die Zeigerarithmetik. Man hätte eine Indexprüfung wie in Pascal einbauen können, dazu hätte man aber die Länge des Arrays mitspeichern müssen. In C besteht das Array aus einer linearen Abfolge von chars -> malloc(sizeof(T)*N). Wenn man C-Strings speichern will, so werden diese Nul-Terminiert, d.h. N+1 Bytes braucht man für einen C-String der Zeichenlänge N. Wenn man nun statt dessen einen Pascalstring nimmt, dann kommt erstmal ein size_t Wert und dann N Zeichnen. Auf einen 16Bit Rechner ist damit der Speicherverbrauch nun N+2 statt N+1 Bytes. Da man meistens kurze Strings hat, und Speicher auf einem 16Bit Rechner ein extrem knappes Gut ist, hat man sich ganz bewußt für die C-Variante entschieden, da man so ein Byte einsparen konnte. Bei anderen Arrays oblag es dem Programmierer die Feldgröße mitzuspeichern, aber das ist eine bewußte Entscheidung des Programmierers gewesen. Das mag aus heutiger Sicht schlecht erscheinen, aber damals war Speichermangel ein großes Problem.

    Nexus schrieb:

    Da ich mir ehrlich gesagt nicht ganz erklären kann, weshalb Arrays gerade so umgesetzt wurden, frage ich euch um Rat. Waren Arrays aufgrund der maschinennahen Programmierung in C am praktischsten, wie sie jetzt sind?

    C wurde entwickelt um UNIX auf ein 16Bit Rechner zu portieren, nachdem die erste UNIX Version noch in Assembler gecodet war. Damals war der C-Compiler noch in "cpp" C-Präprozessor und "cc" C-Compiler aufgespalten, da beiden Programme nicht zusammen in den Arbeitsspeicher paßten. Speichermangel war also ein wirklich großes Problem, das nicht nur den Arbeitsspeicher betraf sondern auch den Platz auf Massenspeicher. Das Jahr 2000 Problem entstand auch nur, weil man damals Jahreszahlen nur als "00"-"99" speicherte, und auf das führende "19" verzichtete. Die erste Festplatte für einen IBM-Mainframe (laut Deutschlandradio Feature: 1956 Leasingpreis 10.000 DM/Monat) hatte auch nur eine Größe von ca. 5MB.



  • ~john schrieb:

    Wenn man nun statt dessen einen Pascalstring nimmt[...]

    Das stimmt so nicht.
    Standard Pascal kennt gar keine Strings, sondern nur Character-Arrays. Ähnlich wie auch in C.
    Turbo Pascal und abgeleitete haben hingegen die von Dir beschriebene Anordnung. Allerdings wird dort i.d.R. ein Byte zur Speicherung der Länge benutzt. Auf Plattformen auf denen ein Byte einem Oktett entspricht als 2^8 Bits = 256, und zwar unabhängig von Breite des Speicherbusses.



  • @ Bashar, Fellhuhn:
    Okay, das scheint einleuchtend. Wieso hat man dann aber Structs nachträglich noch komfortabler gemacht, während Arrays so belassen wurden? Wäre es für systemnahe Programmierung nicht besser, wenn Structs in C ebenso gehandhabt würden?

    Dadurch, dass man Operationen wie Kopieren von Arrays erlaubt, hat man ja keine Performance- oder Speichereinbusse, zumal es immer noch die Möglichkeit gäbe, explizit als Zeiger darauf zuzugreifen oder per Reference an Funktionen zu übergeben. Auch Zeigerarithmetik wäre weiterhin möglich, z.B. indem man mit dem Adressoperator arbeitet (der Name des Arrays würde dann das Objekt und nicht die Anfangsadresse bezeichnen oder implizit konvertierbar sein).

    @ ~john:
    Ich hab das Gefühl, du bist ein wenig von meiner Fragestellung abgewichen. Mir ging es nicht um den Speicherverbrauch oder um C-Strings, sondern um die "Spezialität" der Operationen (Übergabe by reference etc.). 😉



  • Nexus schrieb:

    @ ~john:
    Ich hab das Gefühl, du bist ein wenig von meiner Fragestellung abgewichen. Mir ging es nicht um den Speicherverbrauch oder um C-Strings, sondern um die "Spezialität" der Operationen (Übergabe by reference etc.). 😉

    Das hängt direkt zusammen. Arrays zu kopieren ist eine extrem aufwendige Sache. Daher wird nur ein Zeiger auf das Array übergeben. Hinzukommt, daß Arrays unterschiedlich groß sein können. In C und C++ wird immer während der Compilezeit die Größe der Objekte auf dem Stack berechnet. D.h. wieviel Speicher auf dem Stack reserviert werden muß, muß schon beim Compilieren bekannt sein. Das schließt variable Größen aus. Hinzu kommt, daß früher der Stack sehr klein war.

    Was man machen kann und in C1999 gemacht wurde, daß man syntaktische Spielereien einführt, die es leichter machen VLAs zu übergeben. Aber diese liegen auch nicht auf dem Stack.



  • Dass Arrays wie andere Typen standardmässig per Value statt per Reference übergeben werden, muss ja deshalb nicht ausgeschlossen werden. Structs können auch sehr gross werden. Man kann dann immer noch Arrays per Reference übergeben, was in C++ bei Containern auch meistens so angewandt wird. Trotzdem scheint die Möglichkeit, direkt auf Sprachebene ein Array zu kopieren, zu fehlen.



  • Nexus schrieb:

    ...Structs können auch sehr gross werden. ...Trotzdem scheint die Möglichkeit, direkt auf Sprachebene ein Array zu kopieren, zu fehlen.

    (nur eine Vermutung von mir) :
    Das könnte damit zusammenhängen, dass structs (und alle primitiven Typen) immer eine zur Compilezeit feststehenede Größe haben und einen eigenen Typ darstellen. (Deswegen kann man das ja auch mit statischen Arrays machen, wenn man sie in einen struct verpackt).

    ... aber es gibt eben auch "dynamische Arrays". Und da hatte man evtl.nur die Wahl zwischen:
    A) dynamische Arrays sprachtechnisch anders zu behandeln als statische oder
    😎 alle Arrays gleich - aber eben anders als primitive Typen - zu behandeln.

    Ich denke mal, die Alternative C), auch "dynamische Typen" (also solche, die zur Laufzeit unterschiedliche Größe haben können) mit Kopiersemantik durch die Sprache zu versehen, wurde eher zu aufwendig und kompliziert (z.B.: Ist dann ein char[3] ein anderer/inkompatibler Typ als char[4] ?) angesehen.

    Gruß,

    Simon2.



  • Okay, das scheint mir ein brauchbares Argument zu sein.

    Naja, schlussendlich bekommt man davon eh nichts mit, wenn man mit Containern arbeitet, aber mich hat es eben trotzdem mal interessiert. Und ich bin wieder einmal froh, nicht C programmieren zu müssen. 😉

    Vielen Dank an alle für die Antworten!


Anmelden zum Antworten