Speicherverwaltung

R

rdg

???
Hi,

ich brauche mal Eure hilfe. Ich habe folgendes:

int *pnZahlen=new int[nAnzahl];

So, ist das nun ein Zeiger? Wenn ja, warum lassen sich dann Werte einfügen?

for(int i=0; i<nAnzahl; i++){
pnZahlen = i;
cout<<"pnZahlen["<<i<<"]: "<<pnZahlen<<endl;
}


Eigentlich dürften doch nur Adressen auf welche das entspr. Feld zeigt gespeichert werden, oder?
Wenn nein, warum gibt es dann überhaupt Zeiger, ich meine Adressen könnte ich doch auch in normale Variablen einfügen.

gruß rdg
 
erstmal wirklich vielen Dank Dir für deine Antwort !!!

Warum kommt ein Zeiger auf's gleiche hinaus wie ein Array?

Wie heißt das Array auf das der Zeiger pnZahlen zeigt?
 
Ein Array ist ein Zeiger auf den ersten Wert einer Reihe von Werten.

Dein Zeiger pnZahlen ist also folglich dein Array.


Vielleicht macht es dieses bisschen Code den Zusammenhang zwischen Pointer und Array deutlicher.
Code:
int ary[3];
*ary = 5;
ary[1] = 6;
*(ary + 2) = 7;
for (int i = 0; i < 3; i++)
	cout << "ary[" << i << "]: " << ary[i] << endl;
 
Die Deklarationen:
Code:
int foo[n];
/*und*/
int *bar = new int[n];
laufen auf das selbe hinaus, mit dem Unterschied, dass der Speicher sich beim ersten Fall auf dem Stack, beim zweiten auf dem Heap befindet. Bei der Variable foo handelt es sich intern auch nur um einen Zeiger auf den Beginn des Feldes, nicht um das eigentliche Array.(Lässt sich auch durch printf("%p\n",foo) gut sehen)
Durch den []-Operator erfolgt dann die Dereferenzierung dieses Zeigers. Dadurch sind auch folgende Zugriffe gleich:
Code:
var = foo[0];
var = *foo;
/*oder*/
var2 = foo[2];
var2 = *(foo+2);

Edit: Zu langsam..
 
Also:
int foo[1]:
foo [0] = 100;
foo [1] = 200;

foo = Adresse im Speicher
foo[0] 100
foo[1] 200

*foo ware foo, also adresse von foo
oder ?
 
Erstmal müsste es int foo[2] heißen, da du 2 Elemente darin speicherst.
*foo ware foo, also adresse von foo
Mit dem Ausdruck "*foo" würdest du auf das erste Element des Feldes zugreifen, also foo[0].
 
Gut, also ist ein Zeiger lediglich ein Array, welcher auf die erste Stelle zeigt.

Doch dann bleibt Frage wozu gibt es Zeiger?

Tut mir wirklich leid, wenn ich so blöde frage, doch ich versteh es wirklich nicht ganz.
 
Gut, also ist ein Zeiger lediglich ein Array, welcher auf die erste Stelle zeigt.

Genau andersherum: Der Name des Arrays kann als Zeiger betrachtet werden, welcher auf die Adresse des ersten Array-Elements im Speicher zeigt.

Wichtig: Zwischen Zeiger und Array gibt es zwar einen großen Zusammenhang, sie sind aber nicht absolut gleichbedeutend. Ein Zeiger ist eine Variable dessen Wert man beliebig ändern (z.B. inkrementieren) kann. Bei einem Array geht das nicht. Der Arrayname verweist immer auf die gleiche Adresse im Speicher, die dem Array bei der Deklaration zugewiesen wurde.

Beispiel:
Code:
// arr zeigt auf Adresse des ersten Elements (10)
int arr[3] = {10, 20, 30};
// ptr zeigt auf Adresse des ersten Elements (10)
int *ptr = arr;

// ptr zeigt auf Adresse des zweiten Elements (20)
ptr++;
// Ungültige Operation (führt zu Compiler-Fehler)
arr++;

Doch dann bleibt Frage wozu gibt es Zeiger?

Ich empfehle dir die Lektüre des Wikipedia-Artikels zu Zeigern und des entsprechendem Kapitels in dem online verfügbaren Buch C von A bis Z.

Wenn du dann noch ungeklärte Fragen hast kannst du dich ja nochmal melden.

Gruß,
Philip
 
Zuletzt bearbeitet:
Gut, also ist ein Zeiger lediglich ein Array, welcher auf die erste Stelle zeigt.

Doch dann bleibt Frage wozu gibt es Zeiger?

Tut mir wirklich leid, wenn ich so blöde frage, doch ich versteh es wirklich nicht ganz.

Ich habe gerade Zeit und Geduld :). Ein Beispiel wozu du Zeiger brauchst, ist das dynamische alloziieren von Speicher.

Angenommen du schreibst ein Programm, welches Zahlen einliest. Wieviele Zahlen das Programm einliest ist nicht gegeben. Hier kannst du nicht einfach sagen ich alloziiere mal int[X] vor weil du entweder mehr Zahlen einlesen könntest als du alloziiert hast, oder du viel zu viele Zahlen allozierst die du gar nicht brauchst -> Memoryverschwendung.

Ein Beispiel wie sich das lösen lässt, wäre mit einer einfach verketteten Liste. Eine solche wird beispielsweise in einem Struct oder mittels einer Klasse implementiert (hier als struct):
Code:
struct zahlenreihe {
zahlenreihe *next;
int zahl;
}

Wie du siehst, referenziert ein Pointer *next auf denselben Datentyp. Der Sinn davon ist, dass du bei einem Element anfängst einzulesen und - wenn du ein weiteres brauchst - mittels dem *next Pointer ein weiteres Element alloziierst (ein Pointer selbst alloziiert ja keinen Datentyp, er verweist nur darauf). Wenn du nochmals ein Element brauchst, hast du ja wieder einen *next Pointer den du zum alloziieren verwenden kannst. Wenn du beim letzten Element bist und *next nicht mehr brauchst, setzt du diesen auf NULL. Wenn du nun die Liste durchsuchen möchtest, fängst du beim ersten Element an und gehst die Liste durch, bis *next == NULL ist.

Pseudocode:
Code:
int zahl = 0;
zahlenreihe meineZahlen;
zahlenreihe *aktuellesElement = &meineZahlen;

solange(wahr) {
 cout << "Bitte Zahl eingeben, bei -1 wird abgebrochen" << endl;
 zahl = zahlEinlesen();
 if(zahl == -1) break;

 aktuellesElement->zahl = zahl;
 aktuellesElement->next = new zahlenReihe;
 aktuellesElement = aktuellesElement->next;
 aktuellesElement->next = NULL;
}

cout << "Die eingegebenen Zahlen sind:" << endl;
aktuellesElement = &meineZahlen;
solange(wahr) {
 cout << aktuellesElement->zahl << endl;
 if(aktuellesElement->next != NULL) { aktuellesElement = aktuellesElement->next; } else { break }
}

Bin mir gerade nicht sicher wegen den * und &, aber ich denke ist korrekt so. Lasse mich gerne korrigieren :).

Anmerkungen:
- es muss mind. 1 Eintrag gemacht werden, ich habs KIS mässig implementiert um zu erklären
- ja, wenn man C++ programmiert sollte man nur noch Klassen bzw. Objekte verwenden und keine Structs, ich weiss :)
 
Zuletzt bearbeitet:
Bin mir gerade nicht sicher wegen den * und &, aber ich denke ist korrekt so. Lasse mich gerne korrigieren

Das '*' ist fehl am Platz, wenn du den "->"-Operator verwendest, da dieser bereits die Dereferenzierung übernimmt:
Code:
/*Falsch:*/
*aktuellesElement->zahl = zahl;
/*Richtig:*/
aktuellesElement->zahl = zahl;
/*oder auch:*/
(*aktuellesElement).zahl = zahl;
 
Zuletzt bearbeitet:
Ah stimmt, ich falle auf das immer wieder rein.. thx für die Erklärung. Ich korrigiers gleich mal.

@rdg:
Wie gehabt alloziiert ein Zeiger selbst kein Memory.

Wenn du dies aber mit new kombinierst, gibt "new" einen Addressbereich zurueck wo die allozierten Datenbereiche liegen. Der Pointer erhält durch die Zuweisung (=) diese Addresse und du kannst dort Daten ablegen und abrufen:
int *zeiger = new int[10];

Wichtig ist, dass du deinen Zeiger nicht verlierst. Sprich am besten machst du immer einen Zeiger, welcher auf den Startbereich zeigt und einen weiteren Zeiger, mit welchem du die Daten mutierst bzw. abrufst.
 
Zuletzt bearbeitet:
So, erstmal wirklich vielen dank für die Hilfe!!!

@tr0nix
1. aktuellesElement = aktuellesElement->next;
2. aktuellesElement->next = NULL;
<- hier komm ich leider noch ein wenig durcheinander,
1. hiermit Speichere ich die Adresse von zahlenreihe.next in den Zeiger aktuellesElement
2. das darauf folgende struct wird auf NULL gesetzt
oder, ist das so richtig.

Habe wirklich bevor ich euch fragte in Bücher geschaut, doch nirgends eine befiedigende Antwort erhalten:
DATA BECKER->Grundlagen Buch:
... unären Verweis- oder Indirektionsoperator <- sorry, doch nichts klar
Galileo->c++ für c:
präsentiert Adresse und Typ einer Variablen <-Ja schon klar doch warum Startfrage?

Die Bücher die SkydiverBS mir empfohlen hat, werde ich mir gleich mal anschauen

und wirklich vielen Dank!!!
 
aktuellesElement ist ein Pointer auf ein zahlenreihe - Struct. In diesem Struct habe ich wiederrum einen Pointer, der auf die nächste Zahl verweist (*next).

Wenn ich jetzt sage, dass die Addresse von aktuellesElement auf die Addresse von aktuellesElement->next gesetzt wird, dann gehe ich einfach einen Schritt weiter zur nächsten Zahl. Da ich ja noch nicht weiss, ob diese Zahl nun die letzte sein wird, setze ich explizit aktuellesElement->next auf NULL um es vorerst als letztes Element zu kennzeichnen (ansonsten zeigt der Pointer irgendwohin ins Memory und du kannst Segfaults erzeugen wenn du darin zu arbeiten versuchst).
 
So,
hab mich weiter mit dem Problem befaßt, dabei ist mir aufgefallen:
Hoffe es bleibt übersichtlich:
Zeigerarray:

*zeig[2]:
zeig |&: 0xbff216cc inhalt: 0xbff216cc
zeig[0]|&: 0xbff216cc inhalt: 0xb7ed340f
zeig[1]|&: 0xbff216d0 inhalt: 0x804a150

int x=25;
zeig[0]=&x;
x | &: 0xbff216c8 inhalt: 25
zeig[0]| &: 0xbff216cc inhalt: 0xbff216c8

int y=70;
zeig[1]=&y;
y | &: 0xbff216c4 inhalt: 70
zeig[1]| &: 0xbff216d0 inhalt: 0xbff216c4

*zeig[0]=100:
zeig[0]| &: 0xbff216cc inhalt: 0xbff216c8

*zeig=100:
unverträgliche Typen in Zuweisung von »int« an »int* [2]«


###################################################
Zeigername wie Arrayvariable:

ary[2] = {100,101}:
-----------------------------------------
ary: 0xbff216cc &: 0xbff216cc
*ary: 100 &: 0xbff216cc
ary[0]: 100 &: 0xbff216cc
ary[1]: 101 &: 0xbff216d0

*ary = 200:
-----------------------------------------
ary: 0xbff216cc &: 0xbff216cc
*ary: 200 &: 0xbff216cc
ary[0]: 200 &: 0xbff216cc
ary[1]: 101 &: 0xbff216d0

int k=500:
*ary=k: -> *ary==ary[0]
-----------------------------------------
ary: 0xbff216cc &: 0xbff216cc
*ary: 500 &: 0xbff216cc
ary[0]: 500 &: 0xbff216cc
ary[1]: 101 &: 0xbff216d0
k: 500 &: 0xbff216c8

ary=k:
-----------------------------------------
Fehler: unverträgliche Typen in Zuweisung von »int« an »int [2]«

ary[0]=100:
-----------------------------------------
ary: 0xbff216cc &: 0xbff216cc
*ary: 100 &: 0xbff216cc
ary[0]: 100 &: 0xbff216cc
ary[1]: 101 &: 0xbff216d0
k: 500 &: 0xbff216c8

1.
Warum:
int ary[2] = {100,101};
ary: 0xbfebce74 &: 0xbfebce74
ary[0]: 100 &: 0xbfebce74
ary[1]: 101 &: 0xbfebce78
-> wenn in ary im inhalt seine eigene Adresse steht,
wie kann dann bei ary[0] mit der Adresse von ary im inhalt 100 stehen?

2.
Bei Zeigerarray, warum:
*zeig[0]=100:
zeig[0]| &: 0xbff216cc inhalt: 0xbff216c8

???
 
Zuletzt bearbeitet:
Ein Zeiger selbst hat eine Addresse (A) und zeigt auf eine Addresse (B) welche einen Inhalt (C) hat.

Wenn du &zeiger ausgibst, kriegst du A. wenn du zeiger ausgibst kriegst du B. Wenn du *zeiger ausgibst, kriegst du C.

Wenn du einen Array erstellst, ist A und B gleichermassen beim ersten Element [0] im Array. Das Zweite Element [1] ist bei der Addresse A + Datentypgrösse.

Daraus folgt:
Addresse von array[X] = &array + X * datentypgrösse

Das ganze ist so, weil du wenn du ein Array spezifizierst, du gleich noch die ganzen Daten alloziierst. Sprich im Virtuellen Memory kannst du dann einen ganzen Block am Stück beziehen. Deshalb ist A == B.

Beispiel (Klammer zeigt Addressbereich wo sich die Variable befindet):
int A = 2; (0x00, 0x01)
int B = 3; (0x02, 0x03)
int *C; (0x04)
C = &A;

Folglich:
&C = 0x04
C = 0x00
*C = 2

Bei einem Array:
int A[2] = {1, 2}; (0x00. 0x01, 0x02, 0x03)

Folglich:
&A = 0x00;
&A[0] = 0x00;
&A[1] = 0x02;


----

Ich habe das ganze nicht getestet, Addresswerte sind nicht realistisch. Meinem Verständniss nach sind die Beispiele korrekt - bei Fehlern bitte gerne korrigieren.
 
Hi, erstmal wieder vielen Dank für deine Unterstützung !!!

Addresse (A) , Addresse (B) und Inhalt (C)
hat sehr geholfen, jetzt wo du es schreibst ist es natürlich vollkommen logisch, bin einfach nicht darauf gekommen. vielen dank.

So, ich glaub ich habs jetzt auch mit dem Array,
Das Array ist wie eine logische partition, welche am Anfang mit der Anfangsadresse beginnt.

Der Inhalt der ersten Adresse müsste aufgesplittet werden und wichtige informationen zum Array hineingeschrieben. Dahinter welches sich immer noch in der Startadresse befindet, ist der Platz für den Inhalt für ary[0].
Mit Größenberechnung müsste es nachzuweisen sein.
 
Zuletzt bearbeitet:
Du musst dir vorstellen, dass hinter jeder Addresse Daten liegen. Meines Wissens - dafür lasse ich mich aber nicht auspeitschen :p - 1 Byte.

Wenn du also eine Integer Variable hast, welche auf einem "normalen" 32 Bit System normalerweise 2 Byte belegt, hast du eine Startaddresse mit 1 Byte und eine darauffolgende Addresse mit dem zweiten Byte. Aufgrund von diesen Informationen ergibt sich dann auch der Werterange:
2 Byte = 2 * 8 Bit = 16 Bit = 2^16 Möglichkeiten = 65536 Möglichkeiten.

Ein normales int geht von -32768 bis +32767, ein unsigned int (also ohne Vorzeichen) von 0 bis 65535.

Davon gibts denke ich reichlich Literatur, aber wenn man mal was nicht versteht gibt das Buch natürlich keine Antwort.
 
Hey,
ich glaub ich habs, es gibt in wirklichkeit gar kein ary.
cout<<ary; <- fehler
sondern nur:
ary[0]
ary[1]
...
daher ary = ary[0]
ist auch logisch, denn wozu bräuchte man es.

*ary[0]
Der Inhalt der Adresse vom Inhalt der Adresse von ary[0];
Da keine Adresse enthalten, der direkte Inhalt von ary[0].
 
Zuletzt bearbeitet:
Aeh ich glaub du bist aufm richtigen Weg aber noch nicht ganz :). Auf alle Faelle redest du schon verwirrt wie ich damals als ich Pointerarihmetik gelernt habe :p.
 

Ähnliche Themen

Fehler bei Zeigern ?

C Programm unter Unix

Modulfehler?

qt Anfängerprobleme

funktion threaden...

Zurück
Oben