Was ist der Zweck von Arrays in C, wenn Zeiger die Arbeit hätten erledigen können?

8

Arrays und Zeiger sind in C nicht dasselbe, obwohl sie verwandt sind und auf ähnliche Weise verwendet werden können. Bisher sind wir uns alle einig.

Ich verstehe jedoch nicht, warum Arrays in C enthalten waren, wenn Zeiger ihre Arbeit perfekt hätten erledigen können.

Ich sage nicht, die Array-Notation zu entfernen (z. B. a [5] oder int a [4] = {0,1,2,3};), was sehr nützlich und praktisch ist. Aber Sie könnten dieselbe Notation als kosmetische Maßnahme auch auf Zeigern verwenden (wie dies der Fall ist). Die Array-Notation ist also kein Grund, Arrays zu haben, sondern nur die Notation!

Der einzige Unterschied, den ich sehe, ist, dass Arrays konstante Zeiger sind und die Größe des Speichers, auf den sie zeigen, nicht geändert werden kann. Dies kann aber auch mit Zeigern erreicht werden, indem sie genau konstant gemacht werden (der Speicher hätte keine feste Größe, aber ich bin mir nicht sicher, ob dies ein Problem ist).

Warum also nicht nur Zeiger haben und den Programmierer entscheiden lassen, wie sich der Zeiger verhalten soll (dh konstant, nicht konstant, feste Größe, variable Größe usw.)?

Daniel Scocco
quelle
6
Warum char haben, wenn Sie mit Byte dasselbe erreichen können?
WuHoUnited
2
Im Ernst, wie wollen Sie die Speicherzuordnung mit einem Zeiger erreichen?
WuHoUnited
5
Persönlich stelle ich mir Zeiger als (manchmal) vor, Arrays zu sein und nicht umgekehrt. In vielen anderen Sprachen haben Zeiger kein Array-ähnliches Verhalten. Wenn Sie über einen Zeiger auf ein Array zugreifen möchten, verwenden Sie entweder einen Zeiger-zu-Array-Typ oder Sie führen eine Zeigerarithmetik durch (Zählen in Bytes, nicht in Elementen). Ich würde eine ganze Menge Geld wetten, dass die Erfinder von C daran gedacht haben, Zeiger zu optimieren, um ein bequemeres Array-ähnliches Verhalten zu erzielen. Ich bezweifle sehr, dass sie bereits ein Array-ähnliches Zeigerverhalten hatten, und entschied dann: "Ich weiß, lassen Sie uns Arrays hinzufügen zu".
Steve314
3
Warum 0xAB haben, wenn Sie mit 171 dasselbe erreichen können?
E-MEE
2
Warum zusammengesetzte Ausdrücke wie, x = a + b * 2;wenn Sie dasselbe mit einer Folge einfacher Ausdrücke wie erreichen könnten x = b; x*=2; x+=a;?
Fortan

Antworten:

14

Arrays sind zusammenhängende Speicher, die auf dem Stapel erstellt werden. Ohne diesen syntaktischen Zucker können Sie keinen zusammenhängenden Stapelspeicher garantieren, und selbst wenn Sie könnten, müssten Sie einen separaten Zeiger zuweisen, um die Zeigerarithmetik ausführen zu können (es sei denn, Sie wollten dies tun *(&foo + x), was ich nicht bin sicher, aber es könnte die L-Wert-Semantik verletzen, ist aber zumindest ziemlich umständlich und würde nach einer Art syntaktischem Zucker schreien). In Bezug auf das Design handelt es sich auch um eine Form der Kapselung, da Sie mit einem einzigen Bezeichner auf die Sammlung verweisen können (für den sonst ein separater Zeiger erforderlich wäre). Und selbst wenn Sie sie zusammenhängend zuweisen und einen separaten Zeiger zuweisen könnten, um sie zu referenzieren, hätten Sie entweder int fooForSomething, fooForSomethingElse... was einiges an Kreativität erzwingt, wenn Ihre Sammlung wächst, sodass Sie vielleicht überlegen, mit zu vereinfachen int foo1, foo2 ..., das wie ein Array aussieht, aber schwieriger zu warten ist.

kylben
quelle
Oh, und übergeben Sie den Wert für foo1, foo2 ... wird schnell chaotisch, besonders wenn Sie eine variable Anzahl von Mitgliedern zulassen möchten.
Kylben
2
Nur als Automatik deklarierte Arrays landen auf dem Stapel. Alles andere (dh statics) landet je nach Umgebung normalerweise woanders.
Blrfl
Es ist kein "syntaktischer Zucker", es ist viel tiefer - ein wichtiger Teil des C-Typ-Systems sowie der berüchtigte Zerfall von Arrays.
SK-Logik
1
Was ist der Unterschied zwischen einem Array auf dem Stapel und alloca ()
Sylvanaar
@sylvanaar, siehe diese SO-Frage: stackoverflow.com/questions/1018853/… Ich hatte noch nie davon gehört, aber es ist anscheinend nicht standardisiert und gefährlich in der Art, wie ich erwarten würde, dass so etwas gefährlich ist.
Kylben
12

Die Array-Notation ist praktisch, leichter zu lesen und weniger fehleranfällig. Es bietet einen Formalismus über Zeiger. Es mag syntaktischer Zucker sein, aber wir alle brauchen ab und zu ein bisschen Süße, nicht wahr?

Wie bei allen Abstraktionen geben Sie ein wenig Flexibilität auf, um die Abstraktion zu vereinfachen.

Robert Harvey
quelle
1
Wie ich in der Frage erwähne, bin ich nicht gegen die Array-Notation. Dieselbe Notation könnte jedoch auch über Zeigern funktionieren. Wo werden also Arrays selbst benötigt?
Daniel Scocco
1
@ DanielScocco - Vielleicht könnte es. Aber es wurde trotzdem so gemacht. Ende der Geschichte. Niemand kann dies wirklich beantworten, da niemand weiß, was zu dieser Zeit im Kopf des Autors vorging.
Turm
10

Ich bin überrascht, dass noch niemand etwas über mehrdimensionale Arrays kommentiert hat.

Wenn Sie eine Matrix aus "verschachtelten Zeigern" haben (sagen wir int **p), ist das, was Sie in jeder "Zeile" (äußere Dimension) haben, ein Zeiger, der auf das erste Element in dieser Zeile zeigt. Für den Zugriff auf einen Wert sind also zwei Speicherzugriffe erforderlich. Außerdem ist der erforderliche Speicher erforderlich sizeof(*int)*n + n*m*sizeof(int).

Im zweidimensionalen Array-Szenario int p[n][m]erfordert der Zugriff auf ein Element nur einen Speicherzugriff, da die Adresse der Zeile berechnet und nicht nachgeschlagen wird. und der benötigte Speicher ist gerade n*m*sizeof(int).

Ein anderer Ort, an dem ein Array nicht durch einen Zeiger ersetzt werden kann, befindet sich innerhalb von Strukturen.

struct s {
    int[2];
    float;
}

ist definitiv nicht dasselbe wie

struct s {
   *int;
   float;
}

Die Größe des Arrays ist dort wichtig, und Zeiger haben diese Informationen nicht.

Also ja, eindimensionale Arrays und einzelne Zeiger sind meistens austauschbar, aber ihre Ähnlichkeiten enden dort.

fortran
quelle
Zeigen Sie uns den Unterschied im Assembler. Mehrdimensionale Arrays, Strukturpackung und Polsterung sind nur Möglichkeiten, einen Bezugsrahmen bereitzustellen, der für die Menschen sinnvoll ist, die die Sprache verstehen und verwenden müssen.
Sylvanaar
@ Sylvanaar meinst du das ernst? Müssen Sie den Assembler wirklich sehen, um sicherzugehen, dass die erste Struktur Platz für zwei Ganzzahlen und einen Gleitkommawert bietet und die zweite nur zum Speichern einer Speicheradresse und eines Gleitkommas?
Fortan
1
Ich folge dir nicht. Was hat der Befehlssatz mit all dem zu tun? Die Operation fragte, was Sie mit Arrays und nicht mit Zeigern tun könnten, und das Deklarieren einer Struktur mit einem Array fester Größe kann nicht mit Zeigern emuliert werden. Bei mehrdimensionalen Arrays können Sie natürlich nur einen Teil des Speichers haben und den Index der Zelle i,janhand berechnen i*cols+j, aber ich denke, dass es ein Grund genug ist, die Existenz von Array-Typen zu rechtfertigen, wenn Sie dies nicht alleine tun müssen.
Fortan
2
"Durch das Deklarieren von Strukturen [...] werden statische Speicherzuordnungen im kompilierten Image erstellt." Nein, das Deklarieren einer Variablen (vom Strukturtyp oder einem anderen Typ) führt dies aus. Durch das Deklarieren einer Struktur wird dem Compiler lediglich mitgeteilt, wie groß dieser Typ ist und welche Offsets jedes seiner Mitglieder aufweist. Sie können niemals eine oder nur lokale Variablen zuweisen.
Random832
1
+1 für die eindeutige Identifizierung des praktischen Werts, dass Arrays fester Größe für den Compiler ein "Typ" sind - mehrdimensionale Arrays, und insbesondere die Tatsache, dass Sie sie mit dem Compiler, der die Zeigerarithmetik ausführt, trivial indizieren können Mit der richtigen Größe für Sie fallen Sie natürlich und elegant von den anderen einfachen Sprachregeln ab, indem Sie Arrays zu einem echten "Typ" machen - ohne das könnten Sie denselben syntaktischen Zucker erzielen, aber die Logik müsste in beiden Fällen spezieller sein im Compiler und in der Sprachspezifikation.
mtraceur
2

Warum sollte ich keine Arrays für Werttypen verwenden können?

int a[4] = {0,1,2,3};

Demian Brecht
quelle
Sie könnten das immer noch verwenden, selbst wenn wir nur Zeiger hätten, nicht wahr? Der Compiler müsste nur wissen, was zu tun ist.
Daniel Scocco
2
@ DanielScocco und wie würde es wissen, was zu tun ist? Arrays
Ratschenfreak
Einverstanden - Wie würden Sie die Funktion nennen, die Platz für eine Folge von Elementen gleichen Typs als lokale oder globale Variable reserviert, wenn nicht als Array? Ein Zeigertyp ist ein Zeigertyp - er gibt nicht an, ob es sich um eine Sequenz handelt oder welche Sequenzgröße.
Steve314
4
@DanielScocco Was mit einem Array ohne Zeiger geliefert wird, sind Informationen über die Größe. Ein (Zeiger, Größe) Paar oder ein (Zeiger, Zeiger) Paar kann also tatsächlich genauso viele Informationen enthalten wie der Name eines Arrays. Ob dies bedeutet, dass Arrays Zeigerpaare sind (oder umgekehrt), scheint eher eine metaphysische Frage zu sein, daher bin ich nicht wirklich daran interessiert, diese zu beantworten. (Die Antwort ist wahrscheinlich nein, da Zeigerpaare eher wie Slices aussehen und daher mehr als nur auf ein Array verweisen können.)
Luc Danton
1
@ Daniel Scocco, Arrays hat eine Größe . Sie können ein Array auf einem Stapel zuweisen, Sie können ein Array in eine zusammenhängende Struktur einfügen, Sie können es innerhalb einer Union verwenden. Wenn der Array-Typ nicht vorhanden wäre, wäre dies nicht möglich.
SK-Logik
1

Wie würden Sie mit Plattformen wie dem 8031 ​​ohne externen Speicher umgehen, die dies nicht unterstützen mallocoder alloca? Vielleicht vergessen Sie, dass C nicht nur für großes Eisen, sondern auch für Aufzugssteuerungen und Toaster geeignet ist.

David Schwartz
quelle
1

In C ist die Array-Notation innerhalb von Ausdrücken immer einfach eine Zeigerarithmetik. Alle Verwendungen einer Array-ID in einem Ausdruck werden sofort von "Array von T" in "Zeiger auf T" konvertiert, und der Wert wird in einen Zeiger auf das erste Element des Arrays konvertiert. Die Array-Notation (z. B. a[1][2]) wird immer in Zeigerarithmetik (z *(*(t+1)+2). B. ) erweitert.

Die Array-Notation in Deklarationen und Definitionen ist jedoch etwas ganz anderes. Ein Array-Deklarator beschreibt ein "Array vom Typ T ", wobei die Werte dieses Typs Sequenzen von Elementen vom Typ T sind . Bei einer Definition eines Array-Objekts geht es darum, eine bequeme und leicht verständliche Array-Notation zu verwenden, um die entsprechende Speichermenge für das gewünschte Array von Objekten zuzuweisen, sodass sich die Array-ID auf diesen Speicher bezieht, ohne dass er wie ein Zeiger aussieht. Tatsächlich ist die Array-Notation in einer Deklaration oder Definition ein Makro, das einen Ausdruck mit sizeof()und arithmetisch generiert , und im Fall einer Definition das Äquivalent vonalloca() für Auto-Arrays oder gleichwertig im Linker für globale Arrays und alles zur Kompilierungszeit (außer bei C99-Arrays mit variabler Länge).

Die Verwendung der Array-Notation in Ausdrücken und die Verwendung von Array-Typen ist nicht so eng miteinander verbunden, obwohl es Tradition und Redewendung ist, die Array-Notation in Ausdrücken zu verwenden, um auf die Speicherung in Objekten zu verweisen, die als Arrays deklariert und / oder definiert sind. Sie können die Array-Notation genauso einfach mit einem Zeigertyp verwenden, um die Zeigerarithmetik übersichtlicher und aussagekräftiger zu gestalten. Tatsächlich entspricht in C ein Ausdruck der Form e1[e2]genau dem Ausdruck *((e1)+(e2)). Die übliche binäre Konvertierung wird auf die beiden Operanden angewendet, und das Ergebnis ist immer ein Wert . Da der Indirektionsoperator ( *) einen Zeiger als Operanden haben muss, muss einer von e1und e2ein Zeiger und der andere eine Ganzzahl sein, aber es spielt keine Rolle, welcherda die anfängliche unäre Konvertierung für jedes "Array von T" darin besteht, es in "Zeiger auf T" zu konvertieren. Die Array-Notation in Ausdrücken ist praktisch ein Compiler-Makro (auf Sprachebene) zum Generieren von Zeigerarithmetikausdrücken.

C funktioniert also bereits so, wie Sie es vorschlagen, aber Sie verwechseln die Verwendung der Notation in zwei sehr unterschiedlichen Kontexten.

Greg A. Woods
quelle