Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 12 Zeiger (Pointer)
Pfeil 12.1 Zeiger deklarieren
Pfeil 12.2 Zeiger initialisieren
Pfeil 12.2.1 Speichergröße von Zeigern
Pfeil 12.3 Zeigerarithmetik
Pfeil 12.4 Zeiger, die auf andere Zeiger verweisen
Pfeil 12.4.1 Subtraktion zweier Zeiger
Pfeil 12.5 Typensicherung bei der Dereferenzierung
Pfeil 12.6 Zeiger als Funktionsparameter (call–by–reference)
Pfeil 12.6.1 Zeiger als Rückgabewert
Pfeil 12.7 Array und Zeiger
Pfeil 12.8 Zeiger auf Strings
Pfeil 12.8.1 Zeiger auf konstante Objekte (Read-only-Zeiger)
Pfeil 12.9 Zeiger auf Zeiger und Stringtabellen
Pfeil 12.9.1 Stringtabellen
Pfeil 12.10 Zeiger auf Funktionen
Pfeil 12.11 void-Zeiger
Pfeil 12.12 Äquivalenz zwischen Zeigern und Arrays
Pfeil 12.13 Der »restrict«-Zeiger


Rheinwerk Computing - Zum Seitenanfang

12.7 Array und Zeiger topZur vorigen Überschrift

Um Irrtümer gleich zu vermeiden: Arrays und Zeiger sind nicht das Gleiche, auch wenn dies im Verlauf dieses Kapitels den Anschein hat. Ein Zeiger ist die Adresse einer Adresse, während ein Array-Name nur eine Adresse darstellt. Dieser Irrtum, dass Array und Zeiger dasselbe sind, beruht häufig darauf, dass Array- und Zeigerdeklarationen als formale Parameter einer Funktion austauschbar sind, weil hierbei (und nur hierbei) ein Array in einen Zeiger zerfällt. Diese automatische »Vereinfachung« erschwert einem Anfänger das Verständnis jedoch. Weiterhin verstärkt wird dieses Missverständnis, wenn ein Zeiger auf einem Speicherblock, der mit malloc() dynamisch Speicher reserviert, wie ein Array verwendet wird (da der Speicherblock auch mit [] verwendet werden kann).


Internes

Ein Array belegt zum Programmstart automatisch einen Speicherbereich, der nicht mehr verschoben oder in der Größe verändert werden kann. Einem Zeiger hingegen muss man einen Wert zuweisen, damit dieser auch auf einen belegten Speicher zeigt. Außerdem kann der »Wert« eines Zeigers später nach Belieben einem anderen »Wert« (Speicherobjekt) zugewiesen werden. Ein Zeiger muss außerdem nicht nur auf den Anfang eines Speicherblocks zeigen.


Zuerst folgt ein Beispiel, wie mit Zeigern auf ein Array zugegriffen werden kann:

/* arr_ptr1.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   int element[8]= { 1, 2, 4, 8, 16, 32, 64, 128 };
   int *ptr;
   int i;

   ptr = element;
   printf("Der Wert, auf den *ptr zeigt, ist %d\n", *ptr);
   printf("Durch *ptr+1 zeigt er jetzt auf %d\n", *(ptr+1));
   printf("*(ptr+3) = %d\n", *(ptr+3));
   printf("\nJetzt alle zusammen : \n");
   for(i=0; i<8; i++)
      printf("element[%d]=%d \n", i, *(ptr+i));
   return EXIT_SUCCESS;
}

Durch die Anweisung

ptr = element;

wird dem Zeiger ptr die Adresse des Arrays element übergeben. Dies funktioniert ohne den Adressoperator, da laut ANSI-C-Standard der Array-Name immer als Zeiger auf das erste Array-Element angesehen wird. Hier sind der Beweis und das Beispiel dazu:

/* arr_ptr2.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   int element[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
   int i;

   printf("*element     = %d\n", *element);
   printf("*(element+1) = %d\n", *(element+1));
   printf("*(element+3) = %d\n", *(element+3));

   printf("\nJetzt alle zusammen : \n");
   for(i=0; i<8; i++)
      printf("*(element+%d) = %d \n", i, *(element+i));
   return EXIT_SUCCESS;
}

Leider sind es aber exakt solche Programmbeispiele, durch die der Eindruck entsteht, Arrays und Zeiger seien gleichwertig. Warum dies nicht so ist, habe ich bereits am Anfang erklärt.

Wenn Sie in dem eben gezeigten Beispiel unbedingt einen Adressoperator verwenden wollen, können Sie dies auch so schreiben:

ptr =& element[0]; /* identisch zu ptr=element */

Auf beide Arten wird dem Zeiger die Anfangsadresse des ersten Elements vom Array mit dem Index [0] übergeben. Der Verlauf des Programms soll jetzt genauer analysiert werden.

*(ptr+1);

Mit dieser Anweisung wird aus dem Array element der Wert 2 ausgegeben, also element[1]. Wieso dies so ist, möchte ich Ihnen wieder anhand einiger Grafiken veranschaulichen.

int *ptr;
...
int element[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };

Abbildung 12.14 Visuelle Darstellung des Zeigers und des Arrays im Speicher

Das Array hat die Speicheradresse 0022FF60 bis 0022FF7C und eine Gesamtgröße von 32 Bytes (auf 16-Bit-Systemen: 16 Bytes). Ein Element hat die Größe von vier Bytes, da int vier Bytes groß ist (auf 32-Bit-Rechnern). Daher erfolgt auch die Adressierung immer in Vierer-Schritten. Durch die Anweisung

ptr = element /* oder */ ptr =& element[0]

sieht es im Speicher folgendermaßen aus:

Abbildung 12.15 Der Zeiger »ptr« verweist auf das erste Array-Element.

Damit verweist der Zeiger auf das erste Element im Array (oder genauer: auf die Speicheradresse des ersten Elements). Danach wird mit

*(ptr+1);

die Adresse 0022FF60 um vier Bytes erhöht. Genauso läuft dies auch mit den Arrays intern ab, wenn der Indexzähler erhöht wird.

Damit der Zeiger tatsächlich auf die nächste Adresse zeigt, muss ptr+1 zwischen Klammern stehen, weil Klammern eine höhere Bindungskraft als der Dereferenzierungsoperator haben und somit zuerst ausgewertet werden. Sollten Sie die Klammern vergessen, würde nicht auf die nächste Adresse verwiesen, sondern auf den Wert, auf den der Zeiger ptr zeigt, und dieser wird um eins erhöht.

Jetzt zeigt der Zeiger ptr durch *(ptr+1) auf:

Abbildung 12.16 Die Adresse des Zeigers wurde erhöht.

Somit wäre die Ausgabe 2. Kommen wir jetzt zur nächsten Anweisung:

*(ptr+3);

Hiermit wird der Wert der Adresse auf 0022FF6C erhöht. Deshalb wird auch der Wert 8 ausgegeben:

Abbildung 12.17 Nach einer weiteren Erhöhung der Adresse des Zeigers

Um also auf das n-te Element eines Arrays zuzugreifen, haben Sie die folgenden Möglichkeiten:

int array[10];          // Deklaration
int *pointer1, *pointer2;
pointer1 = array;       // pointer1 auf Anfangsadresse von array
pointer2 = array + 3;   // pointer2 auf 4.Element von array

array[0]      = 99;    // array[0]
pointer1[1]   = 88;    // array[1]
*(pointer1+2) = 77;    // array[2]
*pointer2     = 66;    // array[3]

Dasselbe gilt auch für Funktionsaufrufe von Array-Namen. Einen Array-Parameter in Funktionen können Sie auf zwei Arten deklarieren:

int funktion(int elemente[])

// Gleichwertig mit ...

int funktion(int *elemente)

Also kann eine Funktion mit folgenden Argumenten aufgerufen werden:

int werte[] = { 1, 2, 3, 5, 8 };
int *pointer;
pointer = werte;

funktion(werte);       // 1. Möglichkeit
funktion(&werte[0]);   // 2. Möglichkeit
funktion(pointer);     // 3. Möglichkeit

Natürlich ist es auch möglich, die Adresse des n-ten Elements an eine Funktion zu übergeben:

funktion(&werte[2]);   // Adresse vom 3.Element an funktion

Hierzu ein kleines Beispiel:

/* arr_ptr3.c */
#include <stdio.h>
#include <stdlib.h>

void funktion(int *array, int n_array) {
   int i;

   for(i=0; i < n_array; i++)
      printf("%d ",array[i]);
   printf("\n");
}

int main(void) {
   int werte[] = { 1, 2, 3, 5, 8, 13, 21 };

   funktion(werte, sizeof(werte) / sizeof(int));
   return EXIT_SUCCESS;
}

Wie sieht es aber mit dem Laufzeitverhalten aus? Was passiert, wenn die Funktion mit Feldindex verwendet wird?

void funktion(int array[], int n_array)

Compiler optimieren den Code bei der Übersetzung in der Regel selbst. Die Umwandlung eines Feldindex in einen Zeiger macht dem Compiler heutzutage keine Probleme mehr. Somit dürfte es keine bemerkbaren Laufzeitverluste bei der Verwendung des Indizierungsoperators geben.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: C/C++






 C/C++


Zum Katalog: Einstieg in C






 Einstieg in C


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de