Die Logik der char pointer

Moin, allerseits.
Nach ein paar Monaten bin auch ich endlich mal freigeschaltet.
Ich habe eine Frage bezüglich der Logik, die hinter den char pointern steckt.

Das Schlüsselwort new legt eine Variable auf dem heap und
gibt eine Adresse zu dieser zurück.
Ein Pointer auf eine Int nimmt diese Adresse zu der Int
auf. Zahl gibt nun die Adresse zu der Zahl zurück.
Will ich aber den Wert der Int, die der Pointer adressiert,
muss ich den * (wasauchimmer operator in diesem fall)
voransetzen.

Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
	int * Zahl = new int;
	*Zahl = 10;
	cout << *Zahl << endl;

	char * Wort = new char;
	Wort = "hallo";
	cout << Wort << endl;

	char * Wort2 = "hallo2";
	cout << Wort2 << endl;

	system("PAUSE");
	return 0;
}

Wie es sich bei den chars scheinbar abspielt, werde ich wohl nie verstehen!
Ich lasse eine char in den heap setzen und die Adresse dem Pointer
auf eine char zuweisen. Nun weise ich diesem Pointer auf die char eine
Zeichenkette zu. Und ja! Ich weise sie dem Pointer zu und nicht
der Varable auf dem heap, auf den dieser Pointer zeigt!

Hilfe! Ich brauche eine stabile Denkweise!
 
Es geht mir um char Pointer.
Ich weiß schon, dass strings chararrays sind stabiler Länge
und das die Größe vom compiler automatisch zugewiesen
wird wenn man die Blockoperatoren leer lässt und alles.

Mir geht es darum, dass ich diese Logik erklärt haben will.
Wie zum Henker funktioniert das?

Einem pointer kann doch kein Wert zugewiesen werden
gescheigedenn ein ganzes array!
[Die double quotes in c++ erzeugen ja ein array,
wobei jedes Zeichen ein element ist und geben es mit einem
nullterminierenden \0 zurück]
 
Original von Friedrich
[Die double quotes in c++ erzeugen ja ein array,
wobei jedes Zeichen ein element ist und geben es mit einem
nullterminierenden \0 zurück]

sie geben dir nicht das array sondern einen pointer auf das erste element darin zurück ;)
 
Also wird hier dem char array ein Pointer zugewiesen?
Code:
char bla[] = "Hallo";

Und wenn sie einen Pointer zum ersten zeichen zurückgeben, wieso gibt
Code:
cout << bla << endl;
sie dann alle aus?
 
Original von Friedrich
Also wird hier dem char array ein Pointer zugewiesen?
Code:
char bla[] = "Hallo";

ja .. du könntest glaube ich genauso folgendes schreiben:

Code:
char* bla = "Hallo";

Und wenn sie einen Pointer zum ersten zeichen zurückgeben, wieso gibt
Code:
cout << bla << endl;
sie dann alle aus?

wahrscheinlich weils so gemacht is, dass es alles ausgibt, bis es auf den null-terminator trifft, wenn man nen charpointer übergibt ... genau kann ichs aba auch nich erklären :/
 
Nichts gibt ja eigentlich einen Pointer zurück.
Adressen werden zurückgegeben. Pointer sind Variablen, die Adressen speichern.
Wenn die double quotes nun eine Zeichenkette auf dem heap erzeugen und die
Adresse zurückgeben, wie vereinbart sich dann dessen Zuweisung an
ein char array? Eine char Variable speichert immerhin eine Zahl, die ein Zeichen
darstellen soll und keine Zahl, die die Adresse zu einem Zeichen darstellt.

Code:
char bla[] = "Hallo";
Dem char array wird hier doch niemals eine Adresse zugewiesen!?
 
Soweit ich mich noch erinnern kann, ist Arrayname== Adresse seines ersten Elementes.
(siehe da, dazu findet google was schönes ;) : http://rn.informatik.uni-bremen.de/lehre/c++/2.pdf )
Ein Array ist im Prinzip nichts anderes als ein Stück Speicher.
das "char" sagt nur aus, wie groß die einzelnen Elemente sind, damit es der Compiler weiß und gegebenfalls dem Programmierer auf die Finger hauen kann, wenn er es unsachgemäßt verwendet.
Code:
char test[]="ich Bin ein Test";
	printf("%X\n",&test);
	printf("%X\n",test);
	printf("%X\n",&test[0]);
Die Ausgabe ist identisch ;).

Edit: ich werde alt und langsam. beavisbee war schneller
 
Original von Friedrich
...
Und wenn sie einen Pointer zum ersten zeichen zurückgeben, wieso gibt
Code:
cout << bla << endl;
sie dann alle aus?
Er gibt solange Zeichen aus, bis er eine Nullterminierung findet.

Btw.: Ich habe gerade unvermittelt den Drang verspürt ein paar Beispiele zu posten, an denen die Funktion von char arrays vlt. deutlich wird (ein char<->Funktion Beispiele). Scheinbar weist du gut über char* bescheid, wenn du nun ein bischen darüber nachdenkst, warum einige Varianten funktionieren und andere nicht, wird dir das das Verständis von char* dutlich verbessern. (war bei mir jedenfals so...)
Code:
#include <stdio.h>
#include <stdlib.h>
#include <iostream.h>

/* funktioniert:*/
char* Func(char* Hi)
{
	Hi="Hi";
	return Hi;
}

int main(void)
{
	char* String;
	String = Func(String);
	cout << String ;
	return 0;
}

// funktioniert: (recht elegant, aber kompliziert)
/*
void Func(char* *Hi)
{
	*Hi="Hi";
}

int main(void)
{
	char* String;//="Bsp."
	Func(&String);
	cout << String ;
	return 0;
}*/

/* funktioniert nicht:
void Func(char* Hi)
{
	Hi="Hi\0";
}

int main(void)
{
	char* String;
	Func(String);
	cout << String ;
	return 0;
}*/


/* funktioniert:
char* Func()
{
	static char* Hi="Hi\0";
	return Hi;
}

int main(void)
{
	char* String;
	String = Func();
	cout << String ;
	return 0;
}

//Funktioniert nicht:
/*
char* Func(void)
{
	char* Hi="Hi";
	return Hi;
}

int main(void)
{
	char String[30];
	String=Func();
	return 0;
}
*/
 
@ beavisbee
Mit dem Namen des Arrays der wie ein Zeiger auf sein erstes Element verwendet werden kann hast du recht.

Mit deiner anderen Aussage jedoch nicht.

Zu deiner ersten Aussage sollte man Wissen was "hallo" ist. Nämlich ein const char [6] demzufolge sind Zuweisungen wie char* bla = "hallo"; in C++ falsch und ein aktueller Compiler wie gcc 4.x wird dir hier auch mindestens eine Warnung oder auch einen Fehler ausgeben.
Wenn du nämlich über den char* bla Zeiger versuchst Elemente zu ändern ist das Resultat undefiniert. Das das überhaupt noch möglich ist hat mit der Abwärtskompatibilität von C++ zu C zu tun.

Und dein Zweiter Codeschnipsel char bla[] = "hallo"; erzeugt auf dem aktuellen Stack ein Feld der größe 6*sizeof(char) das nicht konstant ist und demzufolge sicher geändert werden kann.

Insofern sind deine beiden Code Zeilen nicht identisch und die erste sogar falsch.
 
Nämlich ein const char [6] demzufolge sind Zuweisungen wie char* bla = "hallo"; in C++ falsch und ein aktueller Compiler wie gcc 4.x wird dir hier auch mindestens eine Warnung oder auch einen Fehler ausgeben.
Wenn du nämlich über den char* bla Zeiger versuchst Elemente zu ändern ist das Resultat undefiniert.
Mit allen Compilern die ich bisher verwendet hab (MSVC++6, Bloodshed und Borland 5) hate eine solche Zuweisung einwandfrei funktioniert. Das mag am Compiler und der Abwärtskompatibilität liegen und char bla[] = "hallo"; mag auch an sich die korrekte Notation sein, aber ich werte das nunmal nach dem Prinzip des Utilitarismus und nicht nach der Ästhetik. Demnach sind beide Schreibweisen "richtig". Außer char*="hallo"; ist eine potentielle Fehlerquelle, aber dazu müsste man wissen, wie genau der Compiler das umsetzt. Könnte auch eine Art Coercion sein...

Korrigiert mich bitte, wenn ich falsch liege ^^
 
hatte die Codeschnipsel auch nur von nem Beitrag weiter oben abgeschrieben... *asche auf mein haupt*

hab das aber gerade mal getestet: mein gcc 4.1 wirft bei keiner der beiden Varianten einen Fehler.
aber recht hast du:
wirklich sauber ist das nicht...
wenn man bei der Version char* bla = "hallo" später noch ein bla="ganzlangerstring" rein bringt, könnte es gut möglich sein, dass er irgendwelche anderen Daten überschreibt... (und wenn das dann noch mit Usereingaben verbunden ist.... da freut sich jeder haxxor :-P)
 
@ Extinction lies was ich geschrieben habe und verstehe es.
Bloodshed DevC++ ist nur eine IDE und kein Compiler. MS oder Borland Compiler sind da wohl keiner Würdigung wert. Da hatte ich neulich im IRC Bereits eine Diskussion mit einigen Leuten die auch der Ansicht waren das Standards von großen Firmen gemacht werden wie MS oder Borland aber dann bräuchten wir ANSI, ISO, DIN und co nicht mehr. Meine Aussagen beziehen sich auf mein Wissen über den C++ Standard, an den sich die Compiler halten sollten. Da du nur so sicher stellen kannst das Sprachen Plattform/Compilerunabhängig gleich funktionieren.


@ baevisbee

Code:
g++ -o habo_charptr habo_charptr.cpp habo_charptr.cpp: In function ?int main(int, char**)?:
habo_charptr.cpp:11: warning: deprecated conversion from string constant to ?char*?
habo_charptr.cpp:14: warning: deprecated conversion from string constant to ?char*?

gcc version 4.2.3 20071014 (prerelease) (Debian 4.2.2-3)
 
Zeiger und Arrays...

Sorry, daß ich mich ein wenig spät einmische.

Ich hatte gerade von Friedrich die Bestätigung bekommen, daß ER noch nicht ausreichend verstanden hat, warum das ganze so und nicht anders ist (während Ihr anderen Jungs Euch mit Philosophieren übertrefft...).

Letzte Meldung von Friedrich war:
Nichts gibt ja eigentlich einen Pointer zurück.
Adressen werden zurückgegeben. Pointer sind Variablen, die Adressen speichern.
Wenn die double quotes nun eine Zeichenkette auf dem heap erzeugen und die
Adresse zurückgeben, wie vereinbart sich dann dessen Zuweisung an
ein char array? Eine char Variable speichert immerhin eine Zahl, die ein Zeichen
darstellen soll und keine Zahl, die die Adresse zu einem Zeichen darstellt.
Code:
char bla[] = "Hallo";
Dem char array wird hier doch niemals eine Adresse zugewiesen!?

OK, ich versuchs mal:

Die beiden Zeilen...
Code:
char bla[] = ...;
char* bla = ...;
bedeuten in C(++) in Bezug auf die Variable "bla" genau das gleiche.
In beiden Fällen erzeugt der Compiler mit "bla" nichts anderes als einen Zeiger auf Daten. Und er macht das nicht zufällig. Das ist so festgelegt, daß die Variable, die da eingeführt wird, nichts anderes als ein Zeiger sein SOLL.
Das darf man ohne weiteres in Analogie zur Philosophie in Unix verstehen, alle Daten zu einem "Zeichenstrom" zu machen, egal welchem Zweck sie dienen sollen. In C(++) ist eine "native" Array-Variable halt nichts anderes als jeder andere Zeiger auch.

Unabhängig davon passiert verschiedenes, wenn Du Literale irgendwo hinschreibst.
Wenn Du irgendwo (wo es von der Syntax her legal ist, natürlich) einen String notierst in der Art
Code:
char* zeiger = "Stringinhalt"
, dann sorgt der Compiler dafür, daß im Datensegment des Programms ein paar Bytes reserviert werden, in die der Compiler den "Stringinhalt" fest (nicht zur Laufzeit variabel) einträgt. Er übergibt dem "zeiger" die Adresse dieses "Stringinhalts" (also der Stelle im Datenbereich, wo das Zeug liegt). Das hattest Du ja schonmal korrekt weiter oben notiert...

Wenn Du bei Arrays die Größe in den eckigen Klammern angibst, legt der Compiler immer entsprechend viel Speicherplatz im Datenbereich an. Der Zeiger selbst ist dann aber immer noch nichts anderes als ein Zeiger. Er "kennt" die eventuelle Mehrdimensionalität eines Array nicht und "weiß" auch nichts über die definierte größe des Arrays. Das muß man in C(++) selbst verwalten.

DAZU benutzt man in aller Regel (um Flüchtigkeitsfehlern vorzubeugen und massenhafte Wiederholungen zu vermeiden) Datenstrukturen alias Klassen, wo man sich die Datenmenge notiert und gegebenenfalls die Mehrdimensionalität behandeln läßt.

Der Vorteil dabei ist, daß man sehr viel Flexibilität hat, WIE man mit seinen Daten umgehen möchte. Einschließlich dessen, daß man mit KEINEM Overhead zur Verwaltung von der Sprache an sich belastet wird.
Der Nachteil ist, daß man auf dem Grundniveau von C(++) praktisch nicht viel mehr hat, als wenn man in Assembler programmiert (nur etwas handhabbarer und vor allem lesbarer).

Hilft das, dem Ziel näher zu kommen?
 
RE: Zeiger und Arrays...

Original von Harry Boeck
Wenn Du bei Arrays die Größe in den eckigen Klammern angibst, legt der Compiler immer entsprechend viel Speicherplatz im Datenbereich an. Der Zeiger selbst ist dann aber immer noch nichts anderes als ein Zeiger. Er "kennt" die eventuelle Mehrdimensionalität eines Array nicht und "weiß" auch nichts über die definierte größe des Arrays. Das muß man in C(++) selbst verwalten.

hm.. Danke für deine profesionelle Aufklärung, Harry.
Leider laggt es mit meinem Verstand noch ein bisschen, das zu verstehen.

Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
	char bla1[] = "hallo";
	cout << sizeof(bla1) << endl; // 6
	cout << bla1 << endl;

	char* bla2 = "hallo";
	cout << sizeof(bla2) << endl; // 4
	cout << bla2 << endl;

	system("PAUSE");
	return 0;
}
Wieso ist bla1 hier denn zum Bleistift 6 bytes groß und bla2 nur 4?

Und wie kann ichd as verstehen, dass der compiler bei Angabe
im Blockoperator Speicher entsprechend der Größe anlegt?
Ich kann doch auch bei einem char array der ersten Schreibweise
Code:
char bla[] = "hallo";
noch via
Code:
	bla1[5] = '!';
was zuweisen, auch wenn das Feld nur 0 bis 4 hat.
Es wird dann nur Bullshit ausgegeben...

// edit
Hoppla. Damit überschreibe ich ja den Terminator.

Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
	char bla1[] = "hallo";
	cout << sizeof(bla1) << endl; // 6
	bla1[5] = '!';
	bla1[6] = '\0';
	cout << bla1 << endl;
	cout << sizeof(bla1) << endl; // 6

	system("PAUSE");
	return 0;
}
so..

Und das hier gibt einen Programmabsturz
Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
	int * zahlen;
	*(zahlen+0) = 10;
	*(zahlen+1) = 20;
	*(zahlen+2) = 30;

	cout << *zahlen << endl;

	system("PAUSE");
	return 0;
}
 
RE: Zeiger und Arrays...

Original von Friedrich
Wieso ist bla1 hier denn zum Bleistift 6 bytes groß und bla2 nur 4?

Weil hier deutlich wird, dass ein Array doch nicht gleich einem Pointer ist...
Bei dem Pointer bekommst du die Anzahl der Bytes zurück, die zum Speichern der Adresse verwendet werden.
Beim sizeof() auf's Array bekommst du die Anzahl der Bytes zurück, die das gesamte Array belegt.

Original von Friedrich
Und wie kann ich das verstehen, dass der compiler bei Angabe
im Blockoperator Speicher entsprechend der Größe anlegt?

Wenn du in den eckigen Klammern eine Zahl x angibst, dann wird halt genau für x Elemente Platz reserviert - egal, ob du alle Elemente füllst oder nicht.
Wenn du char bla[]="Hallo" verwendest, wird ein const char bla[6] draus (Anzahl der Zeichen + \0-Terminierung)


Original von Friedrich
Ich kann doch auch bei einem char array der ersten Schreibweise
Code:
char bla[] = "hallo";
noch via
Code:
	bla1[5] = '!';
was zuweisen, auch wenn das Feld nur 0 bis 4 hat.
Es wird dann nur Bullshit ausgegeben...

wie du schon selbst gesehen hast:
// edit
Hoppla. Damit überschreibe ich ja den Terminator.

du schreibst damit einfach über dein Array hinaus - das KANN gut gehen, aber es ist definitiv eine Gefahr, da du unkontrolliert auf Speicherbereiche schreibst, in denen du nichts zu suchen hast.
 
Ich hab nochmal was ausprobiert:
Bei einigen Borland Compilern gibt es tatsächlich Probleme, wenn man char* nicht richtig initialisiert. Ist also eine Fehlerquelle und ist zu vermeiden.
Beispiel:
Code:
char* X="";
cin >> X; // z.B.: Wie gehts?
cout << "Hallo!";
Gibt in Borland oftmals "ie gehts?" aus. char* X=""; belegt den char array nur mit einer Nullterminierung. Interessieren würde mich nun, warum ausgerechnet der Teil nach dem Nullterm ausgegeben wird. Hallo! wir nicht ausgegeben und der weitere Programmablauf is gestört... (Prog hängt, Anweisungen werden übergangen/falsch ausgeführt)
-> char* X=""; <=> tödliche Falle ^^


du schreibst damit einfach über dein Array hinaus - das KANN gut gehen, aber es ist definitiv eine Gefahr, da du unkontrolliert auf Speicherbereiche schreibst, in denen du nichts zu suchen hast.
Genau das hab ich in meinem Beispiel auch gemacht. Was schief gehen KANN, seht ihr ja ;)

Btw: Dieser post mal nicht ganz so offtopic und philosopphisch ^^
 
RE: Zeiger und Arrays...

Original von beavisbee
das KANN gut gehen, aber es ist definitiv eine Gefahr, da du unkontrolliert auf Speicherbereiche schreibst, in denen du nichts zu suchen hast.
Wie denn das jetzt?
Also tut der Blockoperator bei der ersten Schreibweise etwas,
was bei der zweiten nicht passiert. Es wird festgelegt,
welche Speicherstellen zu den einzelnen Zeichen dieses Arrays
zugehören? Aber wie und wo genau?
 
RE: Zeiger und Arrays...

Original von Friedrich
Und das hier gibt einen Programmabsturz
Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
	int * zahlen;
	*(zahlen+0) = 10;
	*(zahlen+1) = 20;
	*(zahlen+2) = 30;

	cout << *zahlen << endl;

	system("PAUSE");
	return 0;
}
Was da ungefähr abläuft ist folgendes:

int * zahlen;
>> Compiler legt einen Pointer auf einen Integer an, der vorerst undefiniert ist, das heißt, der Pointer enthält Zufallsdaten.
*(zahlen+0) = 10;
>> Der Rechner schreibt eine 10 an die Adresse des Pointers (also an irgendeinen zufälligen Ort)
*(zahlen+1) = 20;
>> Der Rechner schreibt eine 20 an die Adresse des Pointers + 2 oder 4 (je nach dem, was der Compiler standardmäßig für Ints vorgibt)

Fazit: Das Programm schreibt einfach wild im Speicher rum.
Du musst erst einmal Speicher reservieren, auf den ein Pointer zeigen kann. C++ macht das nicht automatisch. Hier also die korrigierte Version:
Code:
#include <iostream>
using namespace std;

int main(int argc, char * argv[])
{
        int * zahlen = new int[3];
        *(zahlen+0) = 10;
        *(zahlen+1) = 20;
        *(zahlen+2) = 30;

        for (int i = 0; i < 3; i++) {
                cout << zahlen[i] << endl;
        }

        system("PAUSE");
        return 0;
}
 
Zurück
Oben