Dr. Erhard Henkes, Stand: 03.11.2003

Wie funktionieren in C++ Arrays und Pointer?

Arrays

Stellen Sie sich ein Array zunächst als eine Straße mit Häusern nur auf einer Seite vor. In jedem Haus wohnt eine Variable (z.B. Zeichen oder Zahl). Man kann ein Array auch als aneinander gereihte Kästchen sehen, die man mit Daten füllen kann. Nachfolgend finden Sie ein Array mit zehn Kästchen, von denen fünf mit den Buchstaben des Wortes HALLO belegt sind:

Imagine an array as a street with houses on only one side. In each house lives a variable (e.g. character or number). One can also think of an array as a row of boxes that can be filled with data. Below is an array with ten boxes, five of which are filled with the letters of the word HALLO:
 
 H   A   L   L   O  \0

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]

Die ersten fünf Kästchen (Zählweise: 0 bis 4) sind klar, aber wer bewohnt denn da das sechste (Index: 5) Kästchen? Dieses Zeichen, nämlich die binäre Null (ASCII-Code 0), beendet in C/C++ einen klassischen C-String. Ebenso könnten z.B. auch Zahlen bzw. Zahlen und Buchstaben im Array stehen. Die "Bewohner" der "Kästchen" nennt man auch die Elemente des Arrays. Arrays bezeichnet man auch als Felder oder Vektoren (eindimensional) bzw. Tabellen (mehrdimensional).

Ein Array ist also ein Feld mit einer festgelegten Anzahl von Elementen des gleichen Typs (bool, char, int, float, double etc.). Der Typ der Elemente und damit der Speicherbedarf pro Element wird bei der Deklaration, z.B. char eingabe[80], festgelegt. In der eckigen Klammer steht die Gesamtzahl der Elemente.

The first five boxes (counting: 0 to 4) are clear, but who lives in the sixth (index: 5) box? This character, namely the binary null (ASCII code 0), terminates a classic C-string in C/C++. Similarly, for example, numbers or numbers and letters could also be stored in the array. The "residents" of the "boxes" are also called the elements of the array. Arrays are also referred to as fields or vectors (one-dimensional) or tables (multi-dimensional).So an array is a field with a fixed number of elements of the same type (bool, char, int, float, double, etc.). The type of the elements and thus the storage requirement per element is determined during declaration, for example, char input[80]. The total number of elements is specified in the square brackets.


Wichtig:
Die Zählung der Elemente eines Arrays in C++ beginnt nicht mit [1], sondern mit [0].
Das erste Element von achtzig ist hier z.B. eingabe[0] und das letzte Element eingabe[79].

Important: The numbering of the elements in an array in C++ does not begin with [1], but with [0]. For example, the first element of eighty is eingabe[0], and the last element is eingabe[79].

 

Beispiel: Bei der Programmierung von Schleifen durchläuft man 80 Elemente, indem man den Schleifenzähler i nicht von 1 bis 80, sondern von 0 bis 79 zählt.

Example: When programming loops, one goes through 80 elements by counting the loop counter i not from 1 to 80, but from 0 to 79.

 

In den nachfolgenden kleinen Programmen lesen wir mittels der Funktion cin einen Zeichenstring ein. Diese Strings bestehen aus einzelnen Zeichen. Bei Worten sind dies Buchstaben, die als Elemente in das Array eingabe[80] eingehen. Dieses Array umfasst gemäß Deklaration Speicherplatz für 81 Elemente des Typs char. Wir werden den Umgang mit den Strings, die in dem Array gespeichert sind, nun trainieren, damit die Wirkung von Adressen und Zeigern klarer wird.

Als Beispiel benutzen wir das Wort "Jahrhundert". Der C++-Stream cin liest den String ein und speichert ihn im Array eingabe [80].
Im Speicher befinden sich daraufhin folgende Daten des Typs char:

|J|a|h|r|h|u|n|d|e|r|t|\0|

Der String wird durch "\0" abgeschlossen, das heißt wir können im Array eingabe[80] mit seinen insgesamt 80 Elementen des Typs char maximal 79 Zeichen (character) gefolgt von der binären Null ablegen. Die einzelnen Elemente des Strings (also die einzelnen Zeichen) können direkt über den Namen eingabe[i] angesprochen werden. In eingabe[0] findet sich z.B. das Zeichen 'J'. Hier werden gezielt Hochkommas benutzt, da Anführungszeichen ("...") für Zeichenketten (Strings) eingesetzt werden, und diese beinhalten an ihrem Ende "\0".

In the following small programs, we read in a character string using the cin function. These strings consist of individual characters. For words, these are letters that go into the input[80] array as elements. According to the declaration, this array includes storage space for 81 elements of type char. We will now practice handling the strings stored in the array so that the effect of addresses and pointers becomes clearer.As an example, we use the word "Jahrhundert". The C++ stream cin reads in the string and stores it in the input[80] array. The following data of type char are then stored in memory:

|J|a|h|r|h|u|n|d|e|r|t|\0|

The string is terminated by "\0", which means that in the input[80] array, we can store a maximum of 79 characters (characters) followed by the binary null out of its total 80 elements of type char. The individual elements of the string (i.e., the individual characters) can be accessed directly through the name eingabe[i]. For example, the character 'J' is found in eingabe[0]. Single quotes are used here deliberately because double quotes ("...") are used for character strings (strings), which include "\0" at their end.

 

Jedes Element hat im Array eine Nummer, z.B. die [0] für 'J'. Die Variable eingabe[0] hat jedoch auch eine Adresse im Speicher des Computers. Diese Speicheradresse des "Kästchens" eingabe[0] findet man über &eingabe[0]. Das vorangestellte & (Adress-Operator) vor dem Element des Arrays bewirkt, dass wir uns nicht auf den Inhalt, sondern auf die Speicheradresse des "Kästchens" beziehen. Zusätzlich erlaubt C++ hier eine gleichwertige Schreibweise, die jedoch am Anfang eher zur Verunsicherung beiträgt. Durch Weglassen der eckigen Klammern wird der Name des (eindimensionalen) Arrays zur Anfangsadresse:

&eingabe[0] ist die Speicheradresse von eingabe[0] und kann äquivalent als eingabe geschrieben werden.

Zusätzlich bringen wir an dieser Stelle noch den Inhalts-Operator * (auch indirection-Operator oder Dereferenzierungs-Operator genannt),
der uns den Inhalt einer Adresse übergibt:

*&eingabe[0] ist der Inhalt der Adresse und daher gleichwertig mit eingabe[0].

Each element in the array has a number, such as [0] for 'J'. However, the variable eingabe[0] also has an address in the computer's memory. The memory address of the "box" eingabe[0] can be found using &eingabe[0]. The prefix & (address operator) before the array element means that we are referring not to the content, but to the memory address of the "box". In addition, C++ allows an equivalent notation, which can be confusing at first. By omitting the square brackets, the name of the (one-dimensional) array becomes the starting address:
&eingabe[0]
is the memory address of eingabe[0] and can be written equivalently as eingabe.Additionally, at this point, we introduce the content operator * (also called indirection operator or dereferencing operator), which gives us the content of an address
:*&eingabe[0] is the content of the address and is therefore equivalent to eingabe[0].

 

Jetzt betrachten wir dies in der Programmierpraxis. Das nachstehende Programm (array001) kann uns helfen, den Aufbau von Arrays zu verstehen. Wir geben z.B. den Zeichenstring "Jahrhundert" in eingabe[80] ein und belegen damit inclusive \0 die ersten 12 Plätze also die Elemente 0 ... 11 des Arrays. Die for-Schleife gibt nun beginnend (i=0) mit eingabe[0] nacheinander die einzelnen Elemente eingabe[i] aus.
Die Bedingung eingabe[i]!='\0' beendet die for-Schleife, sobald das Zeichen '\0' gefunden wird.
'\0' wird nicht mehr ausgegeben.

Now let's look at this in programming practice. The following program (array001) can help us understand the structure of arrays. For example, we enter the character string "Jahrhundert" in eingabe[80], thus occupying the first 12 positions, i.e. elements 0 ... 11 of the array, including \0. The for-loop then outputs the individual elements eingabe[i] starting with eingabe[0]. The condition eingabe[i]!='\0' terminates the for-loop as soon as the character '\0' is found. '\0' is not output anymore.
 
//Programm array001.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  char eingabe[80];
  cin >> eingabe;
  for ( i=0; eingabe[i] != '\0'; i++ )  // != bedeutet ungleich
  cout << eingabe[i];
}

Jetzt ändern wir eingabe[i] in *&eingabe[i] ab, um die Wirkung von "Inhalt der Adresse ..." zu überprüfen.

If we change "eingabe[i]" to "*&eingabe[i]", we are effectively dereferencing the address of "eingabe[i]" to get the value stored at that address.
 

//Programm array002.cpp

...

cout << *&eingabe[i];  // identisch mit eingabe[i]

...
 

Bei beiden Programmen erhalten wir folgende Bildschirmausgabe:

Jahrhundert (eingegebener String)
Jahrhundert (ausgegebener String)

Sie haben damit Grundfunktionen kennen gelernt, um einen String in ein Array einzulesen und aus diesem wieder vollständig auszulesen.

Ändern Sie das Programm jetzt wie folgt ab:

With both programs we get the following output on the screen:Jahrhundert (entered string)
Jahrhundert (output string)You have now learned basic functions for reading a string into an array and reading it out again completely.
Now modify the program as follows:


 
//Programm array003.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  char eingabe[80];
  cin >> eingabe;
  for ( i=0; eingabe[i] != '\0'; i++ )
  cout << *eingabe;  
}

Jahrhundert
JJJJJJJJJJJ

Was bedeutet *eingabe noch einmal genau? eingabe war identisch mit &eingabe[0].
Damit wird *eingabe zu *&eingabe[0]. Der Inhalt an der Speicheradresse des nullten Elements ist das nullte Element und damit 'J'.
Dieses Element wird im Programmablauf solange ausgegeben, bis die for-Schleife auf das String-Ende '\0' stößt.

Fassen wir kurz zusammen, was wir bisher mittels cout in der for-Schleife ausgegeben haben:

What does *eingabe mean again exactly? eingabe was identical to &eingabe[0]. Thus, *eingabe becomes *&eingabe[0]. The content at the memory address of the zeroth element is the zeroth element itself, which is 'J'. This element is outputted repeatedly in the program until the for-loop encounters the string end '\0'.

To summarize, let's take a look at what we have outputted so far using cout in the for-loop:


 
eingabe[i]  Elemente des Arrays
 
*&eingabe[i]   Inhalte der Adressen der Elemente des Arrays 
( = Elemente des Arrays )
*eingabe   Inhalt der Startadresse des Arrays 
( = nulltes Element des Arrays )

Jetzt testen wir den Adress-Operator ohne Aufhebung durch den Inhalts-Operator:
 
// Programm array004.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  char eingabe[80];
  cin >> eingabe;
  for ( i=0; eingabe[i] != '\0'; i++ )
  cout << &eingabe[i];
}

Jahrhundert
Jahrhundertahrhunderthrhundertrhunderthundertundertndertdertertrtt

Ja, das sieht ja lustig aus! Eigentlich hatten wir unter Benutzung des Adress-Operators erwartet,
dass die Speicheradressen der Elemente des Arrays nacheinander ausgegeben werden.
Stattdessen erhält man den gesamten String ab der übergebenen Adresse.
Interessant, aber zunächst unlogisch. Sie vermuten, dass dies mit einer Sonderbehandlung von
Strings durch C++ zusammenhängt? Es war ja bereits merkwürdig, dass wir mit cin unter Angabe der Startadresse,
also eingabe (gleichbedeutend mit &eingabe[0]) den gesamten String übernehmen konnten.

Um diesen Zusammenhang vollständig zu verstehen, wiederholen wir das Ganze in gleicher Form mit einem Integer-Array.

Yes, that looks funny! Actually, using the address operator, we expected the memory addresses of the elements of the array to be output one after the other. Instead, we get the entire string starting from the given address. Interesting, but initially illogical. You suspect that this has to do with a special treatment of strings by C++? It was already strange that we could take over the entire string with cin by specifying the starting address, i.e., eingabe (equivalent to &eingabe[0]).To fully understand this connection, let's repeat the whole thing in the same way with an integer array.


 
// Programm array005.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  int eingabe[80];
  cin >> eingabe;
  for ( i=0; eingabe[i] != '\0'; i++ )
  cout << &eingabe[i]; 
}
 

Hier meldet der Compiler bei cin>>eingabe eine
"Illegal structur operation" oder
(MSVC++) "error C2679: Binaerer Operator '>>' :
Kein Operator definiert, der einen rechtsseitigen Operator vom Typ 'int [80]' akzeptiert (oder keine geeignete Konvertierung moeglich)."

Da wir wissen, dass eingabe die Kurzform für &eingabe[0] ist, tauschen wir dies aus und erhalten:

Here, the compiler reports an "Illegal structure operation" or (MSVC++) "error C2679: binary operator '>>':
no operator found which takes a right-hand operand of type 'int [80]' (or there is no acceptable conversion)."Since we know that "eingabe" is the shorthand for "&eingabe[0]," we replace it and get:

// Programm array006.cpp

...

cin >> &eingabe[0];

...

Auch hier meldet der Compiler an der Stelle cin>>eingabe eine "Illegal structur operation".
Dies ist beruhigend, da wir ja nur den gleichwertigen Programm-Code angewandt haben.
Nun wird es klarer: Die Fähigkeit von cin (oder scanf in C), einen String unter Angabe
einer Adresse der Reihe nach als Character-Variablen in ein Array des Typs char einzulesen,
ist etwas besonderes, aber leider nicht von glasklarer Logik geprägt.

Da wir jetzt endlich Adressen sehen möchten,
streichen wir den Adress-Operator und geben separat die ersten drei Elemente des Arrays ein:

Here again, the compiler reports an "Illegal structure operation" at the point cin>>eingabe. This is reassuring, as we have only applied the equivalent program code. Now it becomes clearer: The ability of cin (or scanf in C) to read in a string one character at a time as character variables into an array of type char using an address is something special, but unfortunately not characterized by crystal-clear logic.Since we finally want to see addresses, we remove the address operator and enter the first three elements of the array separately:


 
// Programm array007.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  int eingabe[80];
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  for ( i=0; i<3; i++ )
  cout << "\n" << &eingabe[i] <<"\t"<< eingabe[i]; 
}

Wenn Sie jetzt z.B. drei Integer-Zahlen eingeben, dann werden links die Speicheradressen und
rechts daneben die zuvor eingegebenen Zahlen auf dem Bildschirm dargestellt
(die Speicheradressen können bei Ihnen natürlich andere sein):

0x0066FCB4       333
0x0066FCB8       444
0x0066FCBC       555

Der Speicherbedarf für int beträgt bei modernen Compilern 4 Byte (32 bit), bei älteren Compilern 2 Byte (16 bit).

Beachten Sie bitte, dass wir einiges verändert haben, um ein vernünftiges Programm zu erhalten.
Insbesondere haben wir die Zahlen einzeln nacheinander abgefragt (kein String),
und der Abbruch-Trick unter Prüfung des Elementes auf '\0' in der for-Schleife ist hier sinnlos.

Wir sind bewusst den Weg vom String im Character-Array zu Zahlen im Integer-Array gegangen, damit deutlich wird,
dass Besonderheiten bei der String-Ein- und -Ausgabe in Verbindung mit & und *& bestehen.
Leider gehen diese bezüglich des Adress-Operators auf Kosten der Logik.

Zunächst verbleiben wir in der Welt der Zahlen und experimentieren mit dem Inhalts-Operator,
indem wir eingabe[i] durch *&eingabe[i] ersetzen. Zusätzlich geben wir aus Neugier einfach drei weitere
Speicherplätze des Arrays aus, die wir nicht selbst vorher mit Zahlen beschicken:

If you now, for example, enter three integer numbers, the left side will show the memory addresses and on the right side the previously entered numbers are displayed on the screen (the memory addresses may be different for you):

0x0066FCB4 333
0x0066FCB8 444
0x0066FCBC 555

The memory requirement for int is 4 bytes (32 bits) for modern compilers and 2 bytes (16 bits) for older compilers.Please note that we have made some changes to get a reasonable program. In particular, we have asked for the numbers one by one (no string), and the termination trick with checking the element for '\0' in the for loop is meaningless here.We deliberately went from the string in the character array to numbers in the integer array, so that it becomes clear that there are peculiarities in string input and output in connection with & and *&. Unfortunately, these peculiarities exist at the expense of logic with regard to the address operator.First, we stay in the world of numbers and experiment with the dereference operator by replacing eingabe[i] with *&eingabe[i]. Additionally, just out of curiosity, we output three more memory locations of the array that we did not fill with numbers ourselves:

 

 
// Programm array008.cpp

#include <iostream>
using namespace std;

int main()
{
  short i;
  short eingabe[80];
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  for ( i=0; i<6; i++ )
  cout << "\n" << &eingabe[i] << "\t" << *&eingabe[i];
}

Hier funktioniert der Adress-Operator wie erwartet. Er gibt die Speicheradresse der Elemente eingabe[i] aus.
Insbesondere sehen wir an den Abständen zwischen den Hexadezimal-Adressen, dass eine short-Integer-Variable 2 Bytes Speicherplatz benötigt.

Jetzt steigen wir um auf float-Variablen und sehen, dass solcheVariablen 4 Bytes belegen. Zusätzlich prüfen wir noch einmal bezüglich
*&eingabe[i] und eingabe[i] auf Übereinstimmung. Geben Sie bitte Zahlen mit mehr als 6 Stellen nach dem Komma ein,
um die deutlich höhere Genauigkeit von double im Vergleich zu float zu erkennen:

Here, the address operator works as expected. It outputs the memory address of the elements eingabe[i]. In particular, we can see from the distances between the hexadecimal addresses that a short integer variable requires 2 bytes of storage space.Now, we switch to float variables and see that such variables occupy 4 bytes. Additionally, we double-check the match between *&eingabe[i] and eingabe[i]. Please enter numbers with more than 6 digits after the decimal point to recognize the significantly higher precision of double compared to float:


 
// Programm array009.cpp

#include <iostream>
using namespace std;

int main()
{
  short i;
  float eingabe[80];
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  for ( i=0; i<6; i++ )
  cout << "\n" << &eingabe[i] << "\t" << *&eingabe[i] << "\t" <<eingabe[i];  
}

Nehmen Sie sofort die Gelegenheit wahr und testen auch double (8 Byte) auf Speicherplatzbedarf und Genauigkeit.

Nachdem wir gesehen haben, dass C++ im Zahlenbereich bezüglich des Adress-Operators wie erwartet reagiert,
kehren wir zu den Strings zurück. Hier besteht nach wie vor die Aufgabe, Speicheradressen einzelner Elemente eines Strings
am Bildschirm auszugeben. Wir gehen von Programm array004 aus und wandeln die Adresse &eingabe[i] vor der Ausgabe
in einen Zahlenwert des Typs long um:

Take the opportunity immediately and also test double (8 bytes) for storage space requirements and precision.After seeing that C++ behaves as expected in the numerical range with respect to the address operator, we return to strings. The task remains to output memory addresses of individual elements of a string on the screen. We start from program array004 and convert the address &eingabe[i] to a long data type before outputting it:


 
//Programm array010

#include <iostream>
using namespace std;

int main()
{
  int i;
  char eingabe[80];
  cin >> eingabe;
  for ( i=0; eingabe[i] != '\0'; i++ )
  cout << "\n" << long (&eingabe[i]); 
}

Wie man sieht, haben wir jetzt das Ziel erreicht, die Speicheradressen der einzelnen Zeichen auszugeben. Als nächstes ändern Sie bitte long in double ab. Der Compiler meldet jetzt z.B.: "Cannot cast from 'char*' to 'double'." Um sicher zu gehen, dass es sich hier um eine Besonderheit bei Strings handelt, kehren wir zu Programm array008 zurück und wandeln auch hier die Adressangabe in das Long-Integer-Format um (Programm array011). Die Ausgabe der Speicheradressen erfolgt wie erwartet im Dezimalzahlen-Format.

Jetzt ändern wir auf double ab und erhalten z.B. eine ähnliche Compiler-Meldung: "Cannot cast from 'int*' to 'double'."

As you can see, we have now achieved the goal of outputting the memory addresses of each character. Next, please change long to double. The compiler now reports, for example: "Cannot cast from 'char*' to 'double'." To make sure that this is a peculiarity with strings, let's go back to program array008 and convert the address to the long integer format here as well (program array011). The output of the memory addresses is in decimal format as expected.Now we change to double and get a similar compiler message, for example: "Cannot cast from 'int*' to 'double'."
 
// Programm array011.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  int eingabe[80];
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  for ( i=0; i<6; i++ )
  cout << "\n" << long (&eingabe[i]) << "\t" << *&eingabe[i];
}

Gehen Sie Compiler-Meldungen immer auf den Grund, denn aus Fehlern lernt man. Der Compiler hat uns mitgeteilt,
dass der Ausdruck &eingabe[i] bei Strings vom Typ char* und bei Zahlen vom Typ int* ist.

Nun werden wir erforschen, was char* und int* beschreibt und warum es bei der Ausgabe mit cout verschieden reagiert?

Nach den obigen Versuchen mit Arrays und Strings kommen wir folgerichtig zu den Zeigern. Arrays, Strings und Zeiger (Pointer) sind in C++ eng miteinander verknüpft. Daher betreten wir jetzt dieses interessante Gebiet der Zeiger, das für viele leider abschreckend wirkt.

Zuvor fassen wir kurz zusammen:
Bisher experimentierten wir mit einzelnen Array-Elementen eingabe[i], den zugehörigen Speicheradressen &eingabe[i]
(Besonderheit: eingabe ist &eingabe[0]) und den Inhalten an den Speicheradressen *&eingabe[i].
Wir fanden, dass man bei Elementen in Strings die Ausgabe der Speicheradressen durch Umwandlung in eine Integer-Variable erreicht. Bei Ausgabe der "nackten" Adresse wurde nicht die Adresse, sondern der restliche String ab dieser Adresse ausgegeben (interessant, aber zunächst nicht beabsichtigt und evtl. noch nicht völlig verstanden).

Always investigate compiler messages thoroughly, as we learn from mistakes. The compiler has informed us that the expression &eingabe[i] is of type char* for strings and int* for numbers.Now we will explore what char* and int* describe and why they react differently when outputting with cout?After the above experiments with arrays and strings, we logically move on to pointers. Arrays, strings, and pointers are closely linked in C++. Therefore, we now enter this interesting area of pointers, which unfortunately appears daunting to many.

Before that, let's briefly summarize:
So far, we experimented with individual array elements eingabe[i], the associated memory addresses &eingabe[i] (special case: eingabe is &eingabe[0]), and the contents at the memory addresses *&eingabe[i]. We found that for elements in strings, the output of memory addresses is achieved by converting them into an integer variable. When outputting the "bare" address, not the address, but the rest of the string from that address was output (interesting, but not initially intended and possibly not completely understood yet).


Was ist ein Zeiger?

Ein Zeiger ist eine Variable, die die Speicheradresse einer anderen Variable beinhaltet.

Um Zeigervariablen von anderen Variablen schnell unterscheiden zu können, ist es üblich,
die Namen solcher Zeiger zur Unterscheidung mit einem kleinen p (pointer) zu beginnen.

C++ schafft bei der Deklaration der Zeiger leider Verwirrung: Als Kennzeichen einer Zeigervariable wird * vor dem Namen eingesetzt.
Dieses Zeichen wird bereits als Inhalts-Operator verwendet. Zusätzlich ist * auch das Zeichen für die Multiplikation.
Hier hilft nur Übung und Gewöhnung.

Als Typ des Zeigers deklariert man den Typ der Array-Elemente, auf die er zeigt.
Nachfolgend finden Sie ein Beispiel (ausgehend von Programm array008),
in dem Speicheradressen in der for-Schleife einem Zeiger zugewiesen werden:

What is a pointer?
 

A pointer is a variable that holds the memory address of another variable.

To quickly distinguish pointer variables from other variables, it is common practice to prefix the names of such pointers with a small letter p (for "pointer"). Unfortunately, C++ can be confusing when declaring pointers: as an indicator of a pointer variable, the * symbol is used before the variable name. This symbol is already used as the content operator, and it is also used as the multiplication operator. The only solution to this confusion is practice and habituation. The type of the pointer is declared as the type of the array elements to which it points. Below is an example (based on program array008), in which memory addresses are assigned to a pointer in a for-loop:


 
// Programm array012.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  int eingabe[80];
  int *p_eingabe;   // Deklaration des Zeigers mittels *...
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  cout<< "Adresse" << "\t" << "Zeiger" << "\t" << "Array-Element";
  for ( i=0; i<6; i++ )
  { p_eingabe = &eingabe[i];
    cout << "\n" << &eingabe[i] << "\t" << p_eingabe << "\t" << *&eingabe[i];
  } 
}

Das nächste Programm demonstriert uns die Besonderheit der Pointerarithmetik:
Erhöht man den Zeigerinhalt um 1, z.B. mit p_eingabe = p_eingabe + 1 oder mit p_eingabe++,
dann wird die Speicheradresse nicht um eins erhöht, sondern um die Zahl, die dem Variablen-Speicherbedarf entspricht,
also hier bei int um 4 Byte. Man rückt den Zeiger in einem Array sozusagen von Wert zu Wert weiter.
Solche Fähigkeiten sind für den Gebrauch in Schleifen nützlich.

Wir werden dies im nächsten Programm sofort austesten:

The next program demonstrates the peculiarity of pointer arithmetic: When the pointer content is increased by 1, for example with p_eingabe = p_eingabe + 1 or with p_eingabe++, the memory address is not increased by one, but by the number corresponding to the variable's memory requirement, which is here 4 bytes for int. One is moving the pointer from value to value in an array. Such abilities are useful for use in loops.We will test this immediately in the next program:
 
// Programm array013.cpp

#include <iostream>
using namespace std;

int main()
{
  short i;
  short eingabe[80];
  short *p_eingabe;
  p_eingabe = eingabe;
  cout<< "Erste Zahl: ";
  cin >> eingabe[0];
  cout<< "Zweite Zahl: ";
  cin >> eingabe[1];
  cout<< "Dritte Zahl: ";
  cin >> eingabe[2];
  cout<< "Zeiger" << "\t" << "Array-Element";
  for ( i=0; i<3; i++, p_eingabe++ )
  cout << "\n" << p_eingabe << "\t" << *p_eingabe; 
}

Im vorstehenden Programm arbeitet p_eingabe stellvertretend für das bisherige &eingabe[i].
Durch Aufnahme von p_eingabe++ in die for-Anweisung wird der Zeiger jeweils um einen Wert
("Variablenbreite", z.B. 2 Bytes bei short oder 4 Bytes bei float etc.) weitergerückt.
*p_eingabe gibt uns den Inhalt der Variable, die an dieser Speicherstelle beginnt.
Als Zeiger beinhaltet p_eingabe diese Adresse.
Experimentieren Sie an dieser Stelle mit der Pointerarithmetik,
indem Sie short durch float etc. ersetzen:

In the previous program, p_eingabe works as a replacement for the previous &eingabe[i]. By including p_eingabe++ in the for loop, the pointer is advanced by one value ("variable width", e.g. 2 bytes for short or 4 bytes for float, etc.) at a time. *p_eingabe gives us the contents of the variable that begins at this memory location. As a pointer, p_eingabe contains this address. Experiment with pointer arithmetic at this point by replacing short with float, etc.:

// Programm array014.cpp

...

float eingabe[80];
float *p_eingabe;

...

Da wir den Zusammenhang zwischen Zeiger und Adresse nun kennen,
probieren wir dies sofort an unserem interessanten Beispiel mit einem String aus:

As we now understand the relationship between pointers and addresses, let's try it out on our interesting example with a string:
 
// Programm array015.cpp

#include <iostream>
using namespace std;

int main()
{
  int i;
  char eingabe[80];
  char *p_eingabe;
  p_eingabe = eingabe; //Zeiger auf Startadresse setzen
  cin >> eingabe;
  for (i=0;i<=80;i++,p_eingabe++)
  cout << *p_eingabe; 
}

Wir stellen fest, dass *p_eingabe analog zu *&eingabe[i] arbeitet.
Was passiert, wenn wir das Zeichen * weglassen und damit den Zeiger direkt ausgeben?

// Programm array016.cpp

...

cout << p_eingabe;

...

Dies ergibt das von den Strings bekannte Bild:
p_eingabe+i funktioniert hier also analog zu &eingabe[i] und gibt den gesamten String beginnend ab der im Zeiger befindlichen Adresse aus.

In unserem Experimentierfeld mit Arrays, Strings, Adressen, Inhalten und Zeigern halten wir kurz inne und betrachten im Überblick
folgende Gleichwertigkeiten von Ausdrücken bezüglich Adressen und Zeigern
(p_eingabe soll hierbei durch Zuweisung die Startadresse des Arrays enthalten):

We observe that *p_eingabe works analogously to *&eingabe[i]. What happens if we leave out the * symbol and output the pointer directly?// Program array016.cpp...cout << p_eingabe;...This produces the same result as with strings: p_eingabe+i works analogously to &eingabe[i] and outputs the entire string starting from the address contained in the pointer.In our field of experimentation with arrays, strings, addresses, contents, and pointers, let's take a moment to consider the following equivalences of expressions with respect to addresses and pointers (where p_eingabe contains the starting address of the array through assignment):


 
&eingabe[0]
eingabe
p_eingabe
&p_eingabe[0]

Startadresse = Adresse des Elementes 0
 

&eingabe[i]
eingabe+i
p_eingabe+i
&p_eingabe[i]

Startadresse = Adresse des Elementes i
 

Durch die Vielfalt sieht das Ganze verwirrend aus. Nehmen wir dies Schritt für Schritt unter die Lupe, um die Regeln zu verstehen:

Betrachten wir zunächst die Äquivalenz folgender Paarungen von Ausdrücken:

eingabe + i <---> p_eingabe + i

&eingabe[i] <---> &p_eingabe[i]

Die erste Zeile zeigt die Äquivalenz zwischen Adresse und Zeiger, die wir bereits verwendet haben.
Wichtig ist diesbezüglich die spezielle Pointerarithmetik: Adressen oder Zeiger werden entsprechend der "Variablenbreite" im Speicher bewegt. Der Ausdruck p_eingabe+5 bedeutet somit, dass der Zeiger von der Adresse des "ersten Kästchens" (Element[0]) auf die Adresse des "sechsten Kästchens" (Element[5]) weiterrückt. Gewöhnungsbedürftig ist, dass der Name eines (eindimensionalen) Arrays gleichzeitig als Adresse von Element[0] verwendet wird. Durch Wegfall des Adress-Operators oder des Hinweises auf einen Zeiger (nur gegeben, wenn Sie p... im Namen verwenden) erscheint der einfache Array-Name wie eine gewöhnliche Variable.

Dass eingabe identisch mit &eingabe[0] ist, wissen Sie bereits.

The variety of expressions may seem confusing. Let's take a closer look at each step to understand the rules:First, let's consider the equivalence of the following pairs of expressions:eingabe + i <---> p_eingabe + i&eingabe[i] <---> &p_eingabe[i]The first line shows the equivalence between address and pointer that we have already used. It is important to note the special pointer arithmetic: addresses or pointers are moved in memory according to the "variable width". Thus, the expression p_eingabe+5 means that the pointer moves from the address of the "first box" (Element[0]) to the address of the "sixth box" (Element[5]). It may take some getting used to that the name of a (one-dimensional) array is also used as the address of Element[0]. By omitting the address operator or the pointer reference (only given if you use p... in the name), the simple array name appears as an ordinary variable.You already know that eingabe is identical to &eingabe[0].

Allgemein gilt:

In general:

Array + i  <---> &Array[i]

Infolge der obigen Regel gilt unter Verwendung des Inhalts-Operators bzw. unter Weglassen des Adress-Operators:


Due to the above rule, using the content operator or omitting the address operator, we have:

Allgemein gilt:


In general:


*(Array+i) <---> Array[i]


Nur der Vollständigkeit halber sei hier angemerkt, dass über die Kette
 

For completeness' sake, it should be noted that through the chain:

Array[i] <---> *(Array+i) <---> *(i+Array) <---> i[Array]


folglich auch 
Array[i]  und  i[Array]  gleichwertig sind, wenn dies beim direkten Betrachten auch nicht verständlich ist.

Der Compiler schluckt dies kommentarlos, während beim Betrachter
teilweise ernsthafte Verständnisprobleme entstehen.

Auf der Ebene der Array-Elemente, am Beispiel eingabe[80], gilt daher folgende Gleichwertigkeit der Begriffe:

therefore, Array[i] and i[Array] are equivalent, although this may not be immediately understandable when viewed directly.

The compiler accepts this silently, while for the viewer, serious comprehension problems sometimes arise.

At the level of the array elements, using the example of eingabe[80], the following equivalence of terms applies:


 
eingabe[0]
*(eingabe)
*(p_eingabe)
p_eingabe[0]

Startelement = Element 0
 

eingabe[i]
*(eingabe+i)
*(p_eingabe+i)
p_eingabe[i]

Startelement = Element i
 

Nun wollen wir das Ganze in einem Programm vergleichend testen. Das nachfolgende Programm definiert ein double-Array mit sechs Zahlen und ein char-Array mit dem String "Jahr". Anschließend werden sämtliche Begriffe angewendet, um die jeweiligen Wirkungen und die gleichartigen Verhaltensweisen zu überprüfen:

Now we want to test everything in a program for comparison. The following program defines a double array with six numbers and a char array with the string "Jahr". Then, all the terms are applied to check their respective effects and similar behaviors:
 
 
// Programm array017.cpp

#include <iostream>
using namespace std;

int main()
{
  short i;
  long j;
  double zahl[6];
  double *p_zahl;
  char eingabe[]="Jahr";
  char *p_eingabe;
  p_zahl = zahl; //Zeiger auf Startadresse setzen
  p_eingabe = eingabe; //Zeiger auf Startadresse setzen

  zahl[0]= 333.333;
  zahl[1]= 45564.66666;
  zahl[2]= 0.0;
  zahl[3]= 545.347564877;
  zahl[4]= -999.9999999;
  zahl[5]= -111.000000001;

  for ( i=0; i<=5; i++ )
  {
      cout << long(&zahl[i])   << "\t" << zahl[i]     << "\n";
      cout << long(zahl+i)     << "\t" << *(zahl+i)   << "\n";
      cout << long(p_zahl+i)   << "\t" << *(p_zahl+i) << "\n";
      cout << long(&p_zahl[i]) << "\t" << p_zahl[i]   << "\n";
  }

  cout << "\1 " << "Kurze Pause" <<" \1";

  for ( j=0; j<80000000; j++ ); // Leere Warteschleife
  

  cout << "\n";

  for ( i=0; i<=3; i++ )
  {
      cout << long(&eingabe[i])   << "\t" << eingabe[i]     << "\n";
      cout << long(eingabe+i)     << "\t" << *(eingabe+i)   << "\n";
      cout << long(p_eingabe+i)   << "\t" << *(p_eingabe+i) << "\n";
      cout << long(&p_eingabe[i]) << "\t" << p_eingabe[i]   << "\n";
  } 
}
 
 

Nachdem das vorstehende Programm die Wirkung der verschiedenen Programmcodes für Array-Elemente und Array-Zeiger
sowohl für Zahlen als auch für einen String gezeigt hat, sollten Sie selbst mit diesem Programm ein wenig experimentieren:

Ändern Sie z.B. die Deklaration des Zahlenarrays von 'double zahl[6];' auf 'double zahl[5]' ab und beobachten Sie, was nach dem Start passiert.
Der Compiler akzeptiert diese zu enge Festlegung (nur fünf anstelle sechs Zahlen) problemlos. Bei verschiedenen PC-Konstellation kann es hier beim Programm-Start zu einem Systemabbruch kommen. Ein manchmal schwierig zu findender Fehler (Kleine Ursache - große Wirkung). Achten Sie bei eigenen Programmen daher immer darauf, dass bei der Deklaration eines Arrays die Zahl der Elemente ausreichend ist, um alle Werte aufzunehmen.

Spielen Sie mit der Warteschleife:
Ändern Sie 'long j;' in 'short j;' ab. Was geschieht? Die Pause hört nicht mehr auf!
Sie haben eine Endlosschleife geschaffen, da man im short-Bereich (-32768 ... 32767) nicht bis auf 80000000 zählen kann!
Die Zahl 80000000 wurde ursprünglich für einen 486er mit 50 MHz entworfen. Sollte ihr Rechner schneller sein, was ich für Sie hoffe,
erhöhen Sie die Zahl (natürlich mit 'long j;'). Sollten Sie 2147483647 überschreiten, so drehen Sie erneut in einer Endlosschleife.

Geben Sie die Adressen der sechs Zahlenwerte im Hexadezimal-Format aus.

Ändern Sie von double auf float ab, und untersuchen Sie die Auswirkungen bei mehr als sechs Nachkommastellen.

Schreiben Sie das Programm so um, dass Sie sechs verschiedene Zahlen selbst eingeben können (for-Schleife / cin).

Im Ausgabe-Teil der Zeiger und Elemente gibt es sehr viele Spielarten. Setzen Sie hier nach Belieben * und & hinzu
und beobachten Sie die Wirkung auf den Compiler oder das Programm.
Sie könnten auch Typ-Umwandlungen vornehmen
(z.B. char ---> int, String als ASCII-Code ausgeben).

Viel Spaß weiterhin mit Arrays und Zeigern.

After the preceding program has shown the effect of the different program codes for array elements and array pointers for both numbers and strings, you should experiment with this program yourself:
For example, change the declaration of the number array from 'double zahl[6];' to 'double zahl[5]' and observe what happens after starting. The compiler accepts this too narrow specification (only five instead of six numbers) without any problems. Depending on the PC configuration, a system crash may occur at program start. This can be a difficult-to-find error (small cause - big effect). Therefore, always make sure that when declaring an array, the number of elements is sufficient to hold all values.Play around with the waiting loop: Change 'long j;' to 'short j;'. What happens? The pause never ends! You have created an endless loop since you cannot count up to 80000000 in the short range (-32768 ... 32767)! The number 80000000 was originally designed for a 486 with 50 MHz. If your computer is faster, which I hope for you, increase the number (of course with 'long j;'). If you exceed 2147483647, you will again end up in an endless loop.Output the addresses of the six number values in hexadecimal format.Change from double to float and examine the effects with more than six decimal places.Rewrite the program so that you can enter six different numbers yourself (for loop / cin).There are many variations in the output part of the pointers and elements. Add * and & as desired and observe the effect on the compiler or the program. You could also make type conversions (e.g., char ---> int, output string as ASCII code).

Have fun with arrays and pointers.