Ist ein Array-Name ein Zeiger?

Lesezeit: 12 Minuten

Ist der Name eines Arrays ein Zeiger in C? Wenn nicht, was ist der Unterschied zwischen dem Namen eines Arrays und einer Zeigervariablen?

  • Nein, aber Reihe ist dasselbe &Array[0]

    Benutzer166390

    29. Oktober 2009 um 6:51 Uhr

  • @PST: &array[0] ergibt einen Zeiger, kein Array 😉

    – jalf

    29. Oktober 2009 um 6:55 Uhr

  • @Nava (und pst): Reihe und &Array[0] sind nicht wirklich gleich. Fallbeispiel: Größe von (Array) und sizeof(&array[0]) unterschiedliche Ergebnisse liefern.

    – Thomas Padron-McCarthy

    29. Oktober 2009 um 7:50 Uhr

  • @Thomas stimme zu, aber in Bezug auf Zeiger, wenn Sie array und &array dereferenzieren[0]erzeugen sie den gleichen Wert von array[0].ie *Array == Array[0]. Niemand meinte, dass diese beiden Zeiger gleich sind, aber in diesem speziellen Fall (auf das erste Element zeigend) können Sie auch den Namen von Array verwenden.

    – Nava Carmon

    29. Oktober 2009 um 11:12 Uhr

  • Diese können Ihnen auch beim Verständnis helfen: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752

    – Dina

    29. Oktober 2009 um 15:06 Uhr

Benutzeravatar von Thomas Padron-McCarthy
Thomas Padron-McCarthy

Ein Array ist ein Array und ein Zeiger ist ein Zeiger, aber in den meisten Fällen sind es Array-Namen umgewandelt zu Zeigern. Ein oft verwendeter Begriff ist, dass sie Verfall zu Zeigern.

Hier ist ein Array:

int a[7];

a enthält Platz für sieben Ganzzahlen, und Sie können einen Wert mit einer Zuweisung wie folgt in eine davon einfügen:

a[3] = 9;

Hier ein Hinweis:

int *p;

p enthält keine Leerzeichen für ganze Zahlen, kann aber auf ein Leerzeichen für eine ganze Zahl zeigen. Wir können es zum Beispiel so einstellen, dass es auf eine der Stellen im Array zeigt awie das erste:

p = &a[0];

Was verwirrend sein kann, ist, dass Sie auch Folgendes schreiben können:

p = a;

Das macht nicht Kopieren Sie den Inhalt des Arrays a in den Zeiger p (was auch immer das bedeuten würde). Stattdessen der Arrayname a wird in einen Zeiger auf sein erstes Element umgewandelt. Diese Aufgabe macht also dasselbe wie die vorherige.

Jetzt können Sie verwenden p ähnlich wie bei einem Array:

p[3] = 17;

Der Grund dafür ist, dass der Array-Dereferenzierungsoperator in C, [ ]wird in Form von Zeigern definiert. x[y] bedeutet: Beginnen Sie mit dem Zeiger xSchritt y Elemente nach vorne, worauf der Zeiger zeigt, und dann nehmen, was dort ist. Verwenden der arithmetischen Zeigersyntax, x[y] kann auch geschrieben werden als *(x+y).

Damit dies mit einem normalen Array wie unserem funktioniert ader Name a in a[3] muss erst in einen Zeiger umgewandelt werden (auf das erste Element in a). Dann gehen wir 3 Elemente vorwärts und nehmen, was auch immer da ist. Mit anderen Worten: Nehmen Sie das Element an Position 3 im Array. (Das ist das vierte Element im Array, da das erste mit 0 nummeriert ist.)

Zusammenfassend werden also Array-Namen in einem C-Programm (in den meisten Fällen) in Zeiger umgewandelt. Eine Ausnahme ist, wenn wir die verwenden sizeof Operator auf einem Array. Wenn a wurde in diesem Zusammenhang in einen Zeiger umgewandelt, sizeof a würde die Größe eines Zeigers und nicht des eigentlichen Arrays geben, was in diesem Fall ziemlich nutzlos wäre a bedeutet das Array selbst.

  • Eine ähnliche automatische Konvertierung wird auf Funktionszeiger angewendet – beides functionpointer() und (*functionpointer)() meinen seltsamerweise dasselbe.

    – Karl Norum

    29. Oktober 2009 um 6:52 Uhr

  • Er fragte nicht, ob Arrays und Zeiger gleich sind, sondern ob der Name eines Arrays ein Zeiger ist

    – Ricardo Amores

    29. Oktober 2009 um 6:58 Uhr

  • Ein Array-Name ist kein Zeiger. Es ist ein Bezeichner für eine Variable vom Typ Array, die eine implizite Konvertierung in einen Zeiger vom Elementtyp hat.

    – Pavel Minaev

    29. Oktober 2009 um 7:24 Uhr

  • Auch abgesehen von sizeof()der andere Kontext, in dem es kein Array->Pointer Decay gibt, ist der Operator & – in Ihrem Beispiel oben, &a wird ein Zeiger auf ein Array von 7 sein intkein Zeiger auf eine einzelne int; das heißt, sein Typ wird sein int(*)[7]die nicht implizit in konvertiert werden kann int*. Auf diese Weise können Funktionen tatsächlich Zeiger auf Arrays bestimmter Größe nehmen und die Einschränkung über das Typsystem erzwingen.

    – Pavel Minaev

    29. Oktober 2009 um 7:25 Uhr

  • @onmyway133, prüfen hier für eine kurze Erklärung und weitere Zitate.

    – Karl Norum

    12. Februar 2015 um 15:39 Uhr

Benutzeravatar von pmg
pmg

Wenn ein Array als Wert verwendet wird, repräsentiert sein Name die Adresse des ersten Elements.
Wenn ein Array nicht als Wert verwendet wird, repräsentiert sein Name das gesamte Array.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

Benutzeravatar von John Bode
Johannes Bode

Wenn ein Ausdruck vom Array-Typ (z. B. der Array-Name) in einem größeren Ausdruck vorkommt und er nicht der Operand von beiden ist & oder sizeof Operatoren, dann wird der Typ des Array-Ausdrucks von „N-elementiges Array von T“ in „Zeiger auf T“ konvertiert, und der Wert des Ausdrucks ist die Adresse des ersten Elements im Array.

Kurz gesagt, der Array-Name ist kein Zeiger, aber in den meisten Kontexten wird er behandelt als ob es war ein Zeiger.

Bearbeiten

Beantwortung der Frage im Kommentar:

Wenn ich sizeof verwende, zähle ich dann nur die Größe der Elemente des Arrays? Dann nimmt das Array „head“ mit der Längenangabe und einem Zeiger auch Platz ein (und damit mehr Platz als ein normaler Zeiger)?

Wenn Sie ein Array erstellen, ist der einzige Platz, der zugewiesen wird, der Platz für die Elemente selbst; Für einen separaten Zeiger oder irgendwelche Metadaten wird kein Speicher materialisiert. Gegeben

char a[10];

was Sie in Erinnerung bekommen, ist

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

Das Ausdruck a bezieht sich auf das gesamte Array, aber es gibt keine Objekt a getrennt von den Array-Elementen selbst. Daher, sizeof a gibt Ihnen die Größe (in Byte) des gesamten Arrays. Der Ausdruck &a gibt Ihnen die Adresse des Arrays, Dies ist die gleiche wie die Adresse des ersten Elements. Der Unterschied zwischen &a und &a[0] ist der Typ des Ergebnisses1char (*)[10] im ersten Fall u char * in dieser Sekunde.

Komisch wird es, wenn man auf einzelne Elemente zugreifen will – den Ausdruck a[i] ist definiert als das Ergebnis von *(a + i) – mit einem Adresswert versehen aversetzt i Elemente (nicht bytes) von dieser Adresse und dereferenzieren Sie das Ergebnis.

Das Problem ist, dass a ist kein Zeiger oder eine Adresse – es ist das gesamte Array-Objekt. Daher gilt die Regel in C, dass immer dann, wenn der Compiler einen Ausdruck vom Array-Typ (wie z adie Typ hat char [10]) und dieser Ausdruck ist nicht der Operand von sizeof oder unär & Operatoren wird der Typ dieses Ausdrucks in einen Zeigertyp konvertiert (“zerfällt”) (char *), und der Wert des Ausdrucks ist die Adresse des ersten Elements des Arrays. deshalb, die Ausdruck a hat den gleichen Typ und Wert wie der Ausdruck &a[0] (und damit der Ausdruck *a hat den gleichen Typ und Wert wie der Ausdruck a[0]).

C wurde von einer früheren Sprache namens B abgeleitet, und in B a war ein separates Zeigerobjekt von den Array-Elementen a[0], a[1]usw. Ritchie wollte die Array-Semantik von B beibehalten, aber er wollte sich nicht mit dem Speichern des separaten Zeigerobjekts herumschlagen. Also hat er sich davon befreit. Stattdessen konvertiert der Compiler bei Bedarf während der Übersetzung Array-Ausdrücke in Zeigerausdrücke.

Denken Sie daran, dass ich gesagt habe, dass Arrays keine Metadaten über ihre Größe speichern. Sobald dieser Array-Ausdruck zu einem Zeiger “zerfällt”, haben Sie nur noch einen Zeiger auf ein einzelnes Element. Dieses Element kann das erste einer Folge von Elementen oder ein einzelnes Objekt sein. Es gibt keine Möglichkeit, anhand des Zeigers selbst zu wissen.

Wenn Sie einen Array-Ausdruck an eine Funktion übergeben, erhält die Funktion nur einen Zeiger auf das erste Element – sie hat keine Ahnung, wie groß das Array ist (deshalb ist die gets Funktion war eine solche Bedrohung und wurde schließlich aus der Bibliothek entfernt). Damit die Funktion weiß, wie viele Elemente das Array hat, müssen Sie entweder einen Sentinel-Wert verwenden (z. B. das 0-Endzeichen in C-Strings) oder die Anzahl der Elemente als separaten Parameter übergeben.


  1. Was sich auf die Interpretation des Adresswerts auswirken *kann* – hängt von der Maschine ab.

  • Habe lange nach dieser Antwort gesucht. Vielen Dank! Und wenn Sie es wissen, könnten Sie etwas näher erklären, was ein Array-Ausdruck ist. Wenn ich sizeof verwende, zähle ich dann nur die Größe der Elemente des Arrays? Dann nimmt das Array „head“ mit der Längenangabe und einem Zeiger auch Platz ein (und damit mehr Platz als ein normaler Zeiger)?

    – Andrij Dmytruk

    7. Dezember 2017 um 21:51 Uhr

  • Und noch etwas. Ein Array der Länge 5 ist vom Typ int[5]. Daher kennen wir die Länge, wenn wir sizeof(array) aufrufen – von seinem Typ? Und das bedeutet, dass Arrays unterschiedlicher Länge wie unterschiedliche Arten von Konstanten sind?

    – Andrij Dmytruk

    9. Dezember 2017 um 13:09 Uhr

  • @AndriyDmytruk: sizeof ist ein Operator und ergibt die Zahl Byte im Operanden (entweder ein Ausdruck, der ein Objekt bezeichnet, oder ein Typname in Klammern). Also für ein Array sizeof ergibt die Anzahl der Elemente multipliziert mit der Anzahl der Bytes in einem einzelnen Element. Wenn ein int 4 Byte breit ist, dann ein 5-Element-Array von int nimmt 20 Bytes ein.

    – Johannes Bode

    9. Dezember 2017 um 23:40 Uhr

  • Ist nicht der Betreiber [ ] auch besonders? Zum Beispiel, int a[2][3];dann für x = a[1][2];obwohl es umgeschrieben werden kann als x = *( *(a+1) + 2 );hier a wird nicht in einen Zeigertyp konvertiert int* (obwohl wenn a ist ein Argument einer Funktion, in die es konvertiert werden soll int*).

    – Stan

    21. Juni 2018 um 17:17 Uhr

  • @Stan: Der Ausdruck a Typ hat int [2][3]die zum Typ “zerfällt”. int (*)[3]. Der Ausdruck *(a + 1) Typ hat int [3]die zu “zerfällt”. int *. Daher, *(*(a + 1) + 2) wird Typ haben int. a zeigt auf das erste 3-Element-Array von int, a + 1 zeigt auf das zweite 3-Element-Array von int, *(a + 1) ist das zweite 3-elementige Array von int, *(a + 1) + 2 zeigt auf das dritte Element des zweiten Arrays von intAlso *(*(a + 1) + 2) ist das dritte Element des zweiten Arrays von int. Wie das dem Maschinencode zugeordnet wird, liegt ganz beim Compiler.

    – Johannes Bode

    21. Juni 2018 um 21:03 Uhr


Grumdrigs Benutzeravatar
Grumdrig

Ein so deklariertes Array

int a[10];

weist Speicher für 10 zu ints. Sie können nicht ändern a aber Sie können Zeigerarithmetik mit machen a.

Ein Zeiger wie dieser weist Speicher nur für den Zeiger zu p:

int *p;

Es weist keine zu ints. Sie können es ändern:

p = a;

und verwenden Sie Array-Indizes, wie Sie können mit a:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

Benutzeravatar von Michael Buen
Michael Bün

Der Array-Name selbst ergibt einen Speicherplatz, sodass Sie den Array-Namen wie einen Zeiger behandeln können:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

Und andere raffinierte Dinge, die Sie mit Zeigern machen können (z. B. Addieren/Subtrahieren eines Offsets), können Sie auch mit einem Array machen:

printf("value at memory location %p is %d", a + 1, *(a + 1));

Sprachlich, wenn C das Array nicht als gerecht verfügbar gemacht hätte eine Art “Zeiger”(Pedanterweise ist es nur ein Speicherort. Er kann nicht auf einen beliebigen Ort im Speicher zeigen und kann auch nicht vom Programmierer gesteuert werden). Das müssen wir immer codieren:

printf("value at memory location %p is %d", &a[1], a[1]);

  • Verursacht dieser Code nicht UB, wenn sizeof (int*) != sizeof (void*)? Um fair zu sein, ich kenne kein System, wo dies der Fall ist.

    – 12431234123412341234123

    30. Juli 2021 um 20:16 Uhr

Ich denke, dieses Beispiel bringt etwas Licht ins Dunkel:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

Es kompiliert gut (mit 2 Warnungen) in gcc 4.9.2 und gibt Folgendes aus:

a == &a: 1

Hoppla 🙂

Die Schlussfolgerung lautet also nein, das Array ist kein Zeiger, es wird nicht als Zeiger im Speicher gespeichert (nicht einmal als schreibgeschützter), obwohl es so aussieht, da Sie seine Adresse mit dem Operator & erhalten können . Aber – oops – dieser Operator funktioniert nicht :-)), so oder so, Sie wurden gewarnt:

p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

C++ verweigert solche Versuche mit Fehlern in der Kompilierzeit.

Bearbeiten:

Das wollte ich demonstrieren:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

Wenngleich c und a “Zeigen” Sie auf denselben Speicher, können Sie die Adresse erhalten c Zeiger, aber Sie können die Adresse des nicht erhalten a Zeiger.

  • Verursacht dieser Code nicht UB, wenn sizeof (int*) != sizeof (void*)? Um fair zu sein, ich kenne kein System, wo dies der Fall ist.

    – 12431234123412341234123

    30. Juli 2021 um 20:16 Uhr

Benutzeravatar von Cosmo Sterin
CosmoSterin

Das folgende Beispiel zeigt einen konkreten Unterschied zwischen einem Array-Namen und einem Zeiger. Angenommen, Sie möchten eine 1D-Linie mit einer bestimmten maximalen Dimension darstellen, Sie können dies entweder mit einem Array oder einem Zeiger tun:

typedef struct {
   int length;
   int line_as_array[1000];
   int* line_as_pointer;
} Line;

Schauen wir uns nun das Verhalten des folgenden Codes an:


void do_something_with_line(Line line) {
   line.line_as_pointer[0] = 0;
   line.line_as_array[0] = 0;
}

void main() {
   Line my_line;
   my_line.length = 20;
   my_line.line_as_pointer = (int*) calloc(my_line.length, sizeof(int));

   my_line.line_as_pointer[0] = 10;
   my_line.line_as_array[0] = 10;

   do_something_with_line(my_line);

   printf("%d %d\n", my_line.line_as_pointer[0], my_line.line_as_array[0]);
};

Dieser Code wird ausgegeben:

0 10

Das liegt daran, dass im Funktionsaufruf to do_something_with_line Das Objekt wurde so kopiert:

  1. Der Zeiger line_as_pointer enthält immer noch dieselbe Adresse, auf die es verwies
  2. Das Array line_as_array auf eine neue Adresse kopiert wurde, die den Funktionsumfang nicht überdauert

Während Arrays also nicht durch Werte gegeben werden, wenn Sie sie direkt in Funktionen eingeben, werden sie, wenn Sie sie in Strukturen kapseln, durch Werte gegeben (dh kopiert), was hier einen großen Unterschied im Verhalten im Vergleich zur Implementierung mit Zeigern umreißt.

1426270cookie-checkIst ein Array-Name ein Zeiger?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy