[C++] Verständnisfrage zu void *Pointer

Bonsai333

Mitglied
Morgen zusammen,

Ich arbeite zur Zeit sehr viel mit Pointer, und habe verschiedene Tests dazu auch gemacht.
Die normalen Zeiger haben ja eigentliche drei Werte:

Datentyp *Pointer = GültigeAdresse;

1. Wert: &Pointer (eigene Adresse auf Stack/Heap)
2. Wert: Pointer (die Adresse auf die der Pointer zeigt, also die gespeicherte Adresse (Stack/Heap), sprich GültigeAdresse)
3. Wert: *Pointer (Wert welcher an der gespeicherten Adresse abgelegt wurde)

wie aber sieht es jetzt bei einem void *Pointer aus:

void *Pointer = (void*) Wertübergabe z.b 'A';

1. Wert: &Pointer (wie oben eigene Adresse auf Stack/Heap)
2. Wert: Pointer (jetzt kommt bei 'A' statt gespeicherte Adresse, der Wert umgerechnet in Hexadezimal Ausgabe) Hier liegt bei mir das Fragezeichen, was verwendet dieser Zeiger als gültige Adresse? Verwendet er seine eigene Adresse um diesen Wert ablegen zu können?
3. Wert: (char)Pointer (normale Wert Ausgabe)

Über Hilfe wäre ich sehr dankbar, arbeite mich mittlerweile schon 2 Wochen durch diese Thematik.Finde sowohl in Volkards C Kurs, noch unter Cpp Tutor noch C++ in 21 Tagen etwas.Eventuell kennt jemand von euch noch einen guten Link wo ich fündig werden könnte.

mfg Bonsai :)
 
Ein void-Pointer ist auch nur ein normaler Pointer. Man kann void als eigenen Datentyp sehen. Eine Funktion ohne Rückgabewert kann auch als Funktion mit Rückgabewert void betrachtet werden (nur zur Veranschaulichung, natürlich ist das nicht ganz dasselbe).

Das void beim void-Pointer soll dann im Grunde nur aussagen, dass der Pointer auf etwas Unbestimmtes zeigt. Aus dem Grund lassen sich mit einem void-Pointer auch keine Arithmetik-Funktionen durchführen (wie gross ist ein void?)

Zu deinem Punkt 2: Ob ein Wert hexadezimal oder dezimal ausgegeben wird ändert nichts am Wert. 2. ist also immer noch die Adresse auf die der Pointer zeigt.
 
Hallo Endurion

Erstmal Danke für deine Antwort,ich hätte schon eher geantwortet aber mußte leider arbeiten.
Zu dem Punkt das ein void Pointer wie normale Pointer ist, möchte ich aber dennoch wiedersprechen, denn ein normaler Pointer hat eine in ihm gespeicherte Adresse wo er eine Wert ablegen kann.Ein void Pointer hat dies eben nicht, ich glaube auch auf die Gefahr hin das gleich Steine fliegen werden, das dieser Pointer seine eigene Adresse nimmt und diesen Wert auf diese Adresse speichert, wie halt eine normale Variable auch.Das ist natürlich jetzt von mir nur eine Vermutung.Zu der Frage wie groß ein void Pointer ist, antworte ich tollkühnerweise einfach mal mit 4 Byte wie jeder andere Zeiger auch.Also ich arbeite momentan an einer einfach verketteten Liste und habe so einen void Pointer in meiner Struktur und da wird er auf jeden Fall mit 4 Byte belegt.
Die Angelegenheit mit der Funktion Rückgabe ist mir klar,damit muß ich keinen Rückgabewert haben.

Nun zu der Aussage "Man könnte sagen das void ein eigener Datentyp ist" ich denke er ist ein eigener Datentyp nur nicht typisiert. (Dies passiert erst wenn er mit Daten gefüllt wird, wahrscheinlich einer seiner Vorteile)

Aber trotzdem Danke für deine Hilfe, ich werde das denke ich mal einfach so hinnehmen müssen.
Ein Trost habe ich, wenigstens funktioniert mein Beispiel.Grins.

mfg Bonsai333
 
Bonsai333 hat gesagt.:
Zu dem Punkt das ein void Pointer wie normale Pointer ist, möchte ich aber dennoch wiedersprechen, denn ein normaler Pointer hat eine in ihm gespeicherte Adresse wo er eine Wert ablegen kann.
In einen void * kann man auch eine Adresse ablegen, die auf einen Speicherbereich zeigt (bzw. zeigen kann). Es ist zur Compilierzeit nur nicht festgelegt, von welchem Typ die Daten an dieser Adresse sind.

Ein void Pointer hat dies eben nicht, ich glaube auch auf die Gefahr hin das gleich Steine fliegen werden, das dieser Pointer seine eigene Adresse nimmt und diesen Wert auf diese Adresse speichert, wie halt eine normale Variable auch.
Wie kommst du darauf? Beziehungsweise: was genau meinst du damit (was ist „dieser Wert“ und “diese Adresse“)? Ansonsten s.o.

Das ist natürlich jetzt von mir nur eine Vermutung.Zu der Frage wie groß ein void Pointer ist, antworte ich tollkühnerweise einfach mal mit 4 Byte wie jeder andere Zeiger auch.
Es ging Endurion vermutlich vielmehr um die Größe eines void, nicht um die Größe eines void *. Letzterer kann übrigens 4 Bytes groß sein, muss aber nicht (je nach Plattform).
 
Mit der Grösse eines void meinte ich, dass man zum Beispiel die Adresse des Pointers nicht um 1 (oder einen anderen Wert) erhöhen kann. Ein typisierter Pointer geht dann Grösse-vom-Typ-in-Bytes Bytes weiter, bei einem void-Pointer geht das nicht.

Ein void-Pointer ist genauso gross wie ein typisierter Pointer (ausser Pointer bei virtuellen Objekten, die können grösser sein), in den zur Zeit üblichen 32-bit-Systemen sind das 4 Bytes. Freu dich auf die 64-bit-Systeme, da sind die Pointer grösser. Und möge uns Segment und Offset-Gehacke auf ewig erspart bleiben.

Ein void-Pointer ist intern ganz genau dasselbe wie ein typisierter Pointer. Du könntest sonst nicht einfach casten.
 
Hallo Matthias

Ja dieser Punkt mit der dem Wert an der eigenen Adresse abspeichern, habe ich nur vermutet. Ich habe eben als Test ein großes 'A' als Wert Übergabe genommen.Wenn ich jetzt mir die Adresse ausgeben lassen möchte ergibt sich folgender Wert:

Eigene Adresse: 00481D10 (Ist die eigene Heap Adressierung)
Gespeicherte Adresse: 0000 0041
Wert: A

Deshalb habe ich angenohmen das auch dieser Zeiger normalerweise einen Adresse braucht auf die er zeigt, und einen Wert ablegen kann.Nur ist diese Adresse nichts anderes als der Hex Wert von A.
Irgendwie verwirrend.Zu Endurion mit der Grösse, ich denke das habe ich dann wohl falsch verstanden.Sorry.
 
Bonsai333 hat gesagt.:
Hallo Matthias

Ja dieser Punkt mit der dem Wert an der eigenen Adresse abspeichern, habe ich nur vermutet. Ich habe eben als Test ein großes 'A' als Wert Übergabe genommen.Wenn ich jetzt mir die Adresse ausgeben lassen möchte ergibt sich folgender Wert:

Eigene Adresse: 00481D10 (Ist die eigene Heap Adressierung)
Gespeicherte Adresse: 0000 0041
Wert: A

Deshalb habe ich angenohmen das auch dieser Zeiger normalerweise einen Adresse braucht auf die er zeigt, und einen Wert ablegen kann.Nur ist diese Adresse nichts anderes als der Hex Wert von A.
Es wäre gut, wenn du den verwendeten Quelltext dazu reinstellen würdest. Ich vermute nämlich, dass du irgendwie falsch auf den Zeiger zugreifst :)
 
Morgen zusammen,

Oke dann fange ich mal mit der Listen Struktur an:
Code:
//LISTEN STRUKTUR ERSTELLEN  
typedef struct LISTEN_TYP
{
	void		*mpv_DatenZeiger;
	LISTEN_TYP	*mpst_NaechsteListe;

} LISTE, *PLISTE;
//Ende Struktur

Oke, hier meine wesentlichen Prototypen:
Code:
//PROTOTYPEN
RESULT Speicher_fuer_Liste(LISTE **ppst_Liste);
RESULT Daten_in_Liste_einfuegen(LISTE **ppst_Liste, void *pv_DatenZeiger);

Jetzt in der Hauptfunktion Speicher bereitstellen für die Liste:
Code:
//HAUPTFUNKTION
RESULT main()
{
	cout<<"IN DER MAIN FUNKTION:\n\n";

	//Deklarierung unseres Listen Zeigers
	LISTE *pst_Liste = NULL;

	//Wir holen uns einen gültigen Speicher auf dem Heap 
	//für unsrer Liste
	if(Speicher_fuer_Liste(&pst_Liste) != OK)
	{
		cout<<"FEHLER BEI SPEICHERBEREITSTELLUNG\n\n";
	}
	cout<<"ZURUECK IN DER MAIN FUNKTION:\n";
	cout<<"EIGENE ADRESSE VON &pst_Liste:\t\t\t\t"<<&pst_Liste<<"\n";
	cout<<"GESPEICHERTE ADRESSE JETZT AUF HEAP von pst_Liste:\t"<<pst_Liste<<"\n";
	cout<<"ADRESSIERUNG von mpv_DatenZeiger:\t\t\t"<<pst_Liste->mpv_DatenZeiger<<"\n";
	cout<<"ADRESSIERUNG von mpst_NaechsteListe:\t\t\t"<<pst_Liste->mpst_NaechsteListe<<"\n\n";

Jetzt in der Funktion für Speicher bereitstellen:
Code:
//Funktion um Speicher für unsrer Liste bereitzustellen
RESULT Speicher_fuer_Liste(LISTE **ppst_Liste)
{
	cout<<"IN DER FUNKTION SPEICHER BEREITSTELLEN:\n";

	//Versuch Speicher auf dem Heap bereitzustellen
	*ppst_Liste = (LISTE*) malloc(sizeof(LISTE));

	//Prüfen ob wir einen gültigen Speicher haben
	if(*ppst_Liste == NULL)
	{
		return SPEICHER_FEHLER;
	}

	//Wir setzen unsere Zeiger innerhalb der Struktur auf NULL
	(**ppst_Liste).mpv_DatenZeiger		= NULL;
	(*ppst_Liste)->mpst_NaechsteListe	= NULL;
	
	cout<<"EIGENE SPEICHERADRESSE von &ppst_Liste\t\t\t"<<&ppst_Liste<<"\n";
	cout<<"GESPEICHERTE ADRESSE von ppst_Liste\t\t\t"<<ppst_Liste<<"\n";

	return OK;
}

Oke, nach meiner Ausgabe habe ich jetzt folgende Adressierungen:
Eigene Adresse von Normalen Listen Zeiger: 0012FF7C (Stack Adressierung)
Eigene Adresse von Doppel Listen Zeiger: 0012FF2C (Stack Adressierung)
Gespeicherte Adresse von Doppel Listen Zeiger: 0012FF7C (Stack Adresse des Normalen Zeigers)
Gespeicherte Adresse von Normalen Zeiger: 01110030 (Heap Adressierung)

Alles klar, jetzt zu meinen HAUPTPROBLEM die Daten einfügen:
In der Hauptfunktion sieht das folgendermaßen aus:
Code:
	//Wir fügen Daten in unsere Liste ein
	if(Daten_in_Liste_einfuegen(&pst_Liste,(void*)'A' ) != OK)
	{
		cout<<"FEHLER BEIM DATEN EINFUEGEN IN LISTE!\n";
		return OK;
	}

	cout<<"ZURUECK IN DER MAIN FUNKTION:\n";
	cout<<"EIGENE ADRESSE VON &pst_Liste:\t\t\t\t"<<&pst_Liste<<"\n";
	cout<<"GESPEICHERTE ADRESSE JETZT AUF HEAP von pst_Liste:\t"<<pst_Liste<<"\n";
	cout<<"WERT von mpv_DatenZeiger:\t\t\t\t"<<(char)pst_Liste->mpv_DatenZeiger<<"\n";
	cout<<"ADRESSIERUNG von mpv_DatenZeiger:\t\t\t"<<pst_Liste->mpv_DatenZeiger<<"\n";
	cout<<"ADRESSIERUNG von mpst_NaechsteListe:\t\t\t"<<pst_Liste->mpst_NaechsteListe<<"\n\n";

	//Wir fügen ein zweitesmal Daten in die Liste
	if(Daten_in_Liste_einfuegen(&pst_Liste,(void*)'B') != OK)
	{
		cout<<"FEHLER BEIM DATEN EINFUEGEN IN LISTE!\n";
		return OK;
	}
	
	cout<<"ZURUECK IN DER MAIN FUNKTION:\n";
	cout<<"EIGENE ADRESSE VON &pst_Liste:\t\t\t\t"<<&pst_Liste<<"\n";
	cout<<"GESPEICHERTE ADRESSE JETZT AUF HEAP von pst_Liste:\t"<<pst_Liste<<"\n";
	cout<<"WERT von mpv_DatenZeiger:\t\t\t\t"<<(char)pst_Liste->mpv_DatenZeiger<<"\n";
	cout<<"ADRESSIERUNG von mpv_DatenZeiger:\t\t\t"<<pst_Liste->mpv_DatenZeiger<<"\n";
	cout<<"ADRESSIERUNG von mpst_NaechsteListe:\t\t\t"<<pst_Liste->mpst_NaechsteListe<<"\n\n";


usw.. Somit füge ich dann Listen Element für Element ein.Die Funktion für Daten einfügen lasse ich mal weg.

Oke, nach dem Einfügen habe ich jetzt folgende Adressierungen und Werte:
IM ERSTEN FALL: LISTE LEER.
Eigene Adresse von Normalen Listen Zeiger: 0012FF7C (Stack Adressierung)
Gespeicherte Adresse von Normalen Listen Zeiger: 01110030 (Heap Adressierung)
Gespeicherte Adresse von mpv_DatenZeiger: 0000 0041 (Hexadezimaler Wert)
Wert von mpv_DatenZeiger: A (nach casting in Datentyp char)
Gespeicherte Adresse von mpst_NaechsteListe: 0000 0000

IM ZWEITEN FALL: LISTE HAT BEREITS DATEN.
Eigene Adresse von Normalen Listen Zeiger: 0012FF7C (Stack Adressierung)
Gespeicherte Adresse von Normalen Listen Zeiger: 01110100 (neue Heap Adressierung für Listen Element)
Gespeicherte Adresse von mpv_DatenZeiger: 0000 0042 (Hexadezimaler Wert)
Wert von mpv_DatenZeiger: B (nach casting in Datentyp char)
Gespeicherte Adresse von mpst_NaechsteListe: 01110030 (zeigt auf die vorher erstellte Listen Adressierung im Heap)

Sorry das ganze ist etwas lang geraten, aber ich wußte nicht genau wo ich hätte anfangen sollen, und hoffe das es übersichtlich genug ist, das man mir helfen kann.
Danke an Matthias und Endurion das ihr euch die Zeit nehmt oder bis jetzt auch genommen habt.

mfg Bonsai333 :)
 
Vielleicht hilft dir folgendes weiter:
Code:
void *pData;

pData = (void *)'A';
// 'A' ist eine Konstante (65).
// pData zeigt also nun auf die Adresse 65
// (vermutlich nicht das, was wir wollten!)

// Also reservieren wir uns ein Byte im Heap...
pData = new char;
// Und beschreiben es mit dem gewünschten Wert.
// Dazu müssen wir unseren void-Zeiger in einen
// char-Zeiger casten und ihn dereferenzieren:
*((char *)pData) = 'A';
// *pData = 'A'; wäre nicht möglich gewesen, da
// (*pData) vom Typ void ist, 'A' aber vom Typ
// char. Daher erst das Casting.

// Mal sehen, ob es geklappt hat:
cout << "Adresse: " << pData << endl;
cout << "Wert:    " << *((char *)pData) << endl;
// cout << "Wert: " << (char)pData << endl; hätte
// nicht den gewünschten Effekt, da wir dadurch
// nur die Speicheradresse als Zeichen interpre-
// tieren würden. Die Dereferenzierung ist hier
// schon nötig. Davor natürlich noch das Casting,
// damit wir nach der Dereferenzierung den
// richtigen Datentyp (char) vorliegen haben.

// Nett sein und den zuvor reservieren Speicher
// wieder freigeben :)
delete pData;
 
Hi Matthias,

erstmal danke für die ausführliche Antwort.Wenn ich deine Erklärung richtig verstehe, muß ich sozusagen meine Compiler mitteilen (wohlgemerkt zusätzlich) für was für einen Datentyp er einen Speicher reservieren soll auf dem Heap.Und nach der Speicherreservierung erst mittels Casting und Derefenzierung die Wertzuweisung vollführen.Erst dann hätte ich eine Adresse (also eine nicht Wert interpretierte Adresse meine ich).

Das würde für mich aber dann folgendes Problem aufwerfen.Ich würde mir ja dann sozusagen die Flexibilität meines void Zeigers zunichte machen, oder anders gesagt wenn ich eh weiß was ich für einen Datentyp später habe, kann ich ja gleich einen typisierten Daten Zeiger erstellen und mir den void Zeiger schenken.

Das ist irgendwie ein Problem.Aber gut ich denke es wird wohl nicht anders gehen.Aber danke nochmal für die Antwort hat mir schon geholfen.

mfg Bonsai333 :)
 

Neue Beiträge

Zurück