Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Zeiger auf Zeichenketten


thorbenZ - So 21.03.04 13:44
Titel: Zeiger auf Zeichenketten
Hallo,

ich habe ein TListView und möchte jetzt jedem Item eine (lange) Zeichenkette zuordnen. Eigentlich möchte ich dafür gerne die Eigenschaft Data verwenden, nur habe ich da jetzt ein Problem: Wie kriege ich einen Pointer auf einen String gespeichert?


Delphi-Quelltext
1:
2:
var
 s: ^string;


geht schonmal nicht. Schließlich kann ich ja mit


Delphi-Quelltext
1:
GetMem(s, sizeof(string));                    


auch keinen Speicher reservieren. Woher soll er schließlich wissen, wie lang das Ding wird?
Als Zeiger auf Strings, so fiel mir dann später ein, müsste doch PChar taugen. Also:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
var
 s: PChar;
begin
 s := 'Hallo Welt';
 lstView.Selected.Data := s;
 ShowMessage(String(PChar(lstView.Selected.Data)));
end;


jetzt habe ich aber das Problem, dass vom PChar (und irgendwie nicht richtig nachvollziehbar) manchmal am Ende ein bis zwei Zeichen abgeschnitten werden, manchmal aber nicht.


Dann habe ich noch ein wenig rumprobiert und es so gemacht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
type TStringRecord = record
 cnt: String;
end;

...
var
 s: ^TStringRecord;
begin
 GetMem(s, sizeof(TStringRecord));
 s^.cnt := "Hallo Welt";
 lstView.Selected.Data := s;
 s := lstView.Selected.Data;
 ShowMessage(s^.cnt);
end;


Das letzte Beispiel funktioniert interessanterweise - aber leider nur manchmal!!! Wenn ich den Dialog, indem das ganze vorkommt, mehrmals öffne und schließe, kommt irgendwann eine "ungültige Zeigeroperation". Wenn ich im Typ TStringRecord das Feld cnt so deklariere:


Delphi-Quelltext
1:
cnt: String[255];                    


herrscht plötzlich Chaos - keine Fehlermeldungen mehr, aber der String wird irgendwie chaotisch auseinandergepflückt.

--------------------

Ich frage Euch also, wenn Ihr euch diesen schrecklichen Beitrag schon angetan habt, wie Ihr einen String als Pointer irgendwo abspeichern würdet, oder ob euch zum Thema "ungültige Zeigeroperation" was einfällt. Delphi zeigt mir nämlich auch nicht, in welcher Zeile diese angeblich auftritt.

Vielen Dank
Thorben

Moderiert von user profile iconMotzi: Topic Verschoben


Motzi - So 21.03.04 14:01

Strings sind bereits Zeiger und PChars sehr ähnlich. Ich hab dazu vor kurzem ein String-Tutorial geschrieben, das die String-Internals behandelt. Du findest es unter http://www.manuel-poeter.de ... schau dir das mal an und schau ob du selbst die Lösung findest, sonst meld dich nochmal und ich helf dir auf die Sprünge.. ;)


MSCH - So 21.03.04 15:36

Dein Ansatz mit dem Record war schon gut, machs aber mit 'ner Class und dann hast du keine Probleme mehr:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
Type
  TMyClass = Class
    Inhalt: String// 
    constructor Create(NewString: String);
  end;

constructor TMyClass.Create;
begin
   Inhalt:= NewString;
end;

....
lstView.Selected.Data := TMyClass.Create('Hallo du bist ein langer String');
...
ShowMessage(TMyClass(lstView.Selected.Data).Inhalt);
...


grez
msch


Motzi - So 21.03.04 21:09

Der Weg über die Klasse ist vollkommen unnötig..! Ein String ist bereits ein Pointer, man muss diesen also nicht erst kompliziert in einen Record oder eine Klasse verpacken, sondern (über einen entsprechenden Typecast) gleich direkt zuweisen oder aber statt des Typecasts den @-Operator verwenden und die Adresse des Strings übergeben und dann entsprechend als PAnsiChar dereferenzieren..!


thorbenZ - So 21.03.04 21:10

Vielen Dank euch beiden. Ich habe die letzte Lösung erstmal so übernommen und es läuft. Das Tutorial werde ich mir bei Gelegenheit mal zu Gemüte führen, es scheint sehr interessant zu sein.

Thorben


MSCH - So 21.03.04 21:20

viel Spass
@Motzi,
solange du nur Strings nimmst, mag es gehen; aber was ist, wenn du mehr als ein String verwerten willst, oder den String validieren möchtest? Schreibst du dann jedesmal eine Prüfroutine, oder kapselst du das gleich in eine eigene Klasse? Du hast doch eine selbst eine 19 seitige Ausarbeitung zur objektorientierten Programmierung in Pascal geschrieben.
Einer der Vorteile gegenüber C* ist ja, dass das ganze Zeiger-Geraffe quasi unsichtbar ist, warum den dann mit @ und ^ noch viel rumexperimentieren?

grez
msch


Motzi - So 21.03.04 21:34

MSCH hat folgendes geschrieben:
@Motzi,
solange du nur Strings nimmst, mag es gehen; aber was ist, wenn du mehr als ein String verwerten willst, oder den String validieren möchtest? Schreibst du dann jedesmal eine Prüfroutine, oder kapselst du das gleich in eine eigene Klasse? Du hast doch eine selbst eine 19 seitige Ausarbeitung zur objektorientierten Programmierung in Pascal geschrieben.
Einer der Vorteile gegenüber C* ist ja, dass das ganze Zeiger-Geraffe quasi unsichtbar ist, warum den dann mit @ und ^ noch viel rumexperimentieren?

Gut, für mehrere Strings oder andere Kombinationen mag das stimmen..! In so einem Fall ist man mit einer Klasse sicher besser beraten, aber solange es nur um einen einzigen simplen String geht würde ich trotzdem den direkten Weg über ein simples Typecasting Pointer(String) gehen...


AndyB - So 21.03.04 23:56

Motzi hat folgendes geschrieben:
Ich hab dazu vor kurzem ein String-Tutorial geschrieben, das die String-Internals behandelt.

Dazu kann ich noch folgende "Kritik" geben:


Zitat:
Es gibt also kein Längenbyte mehr,
sondern der String kann (theoretisch) beliebig lang werden, vorrausgesetzt, es kommt
dazwischen kein Nullzeichen vor.

Das mit dem Nullzeichen stimmt nicht, wie du weiter unten im Text dann auch klar stellst. Ein String kann auch eine gesamte Exe-Datei mit beliebig vielen Nullzeichen aufnehmen. Wenn er aber an eine Funktion übergeben wird, die einen PChar (ohne zusätzliche Längenangabe) erwartet, so geht die Längeninformation verlohren und das Nullzeichen terminiert.



Zitat:
1. “normaler” Parameter
2. const-Parameter
3. var-Parameter
4. als Pointer

Hier fehlt noch "out", bei dem der Compiler noch etwas einfügt, das du noch herausfinden musst.



Zitat:
Längenzähler

Was zählt der Längenzähler denn? Die Längen des Strings? Ich dachte ein String hat nur eine Länge. Ich würde das eher als Längenfeld (es steckt ja auch ein record dahinter) oder Längenangabe bezeichnen


Zitat:
PChar
Pchar

Kannst du dich auch mal entscheiden, wie du PChar schreiben willst.


Motzi - Mo 22.03.04 00:15

AndyB hat folgendes geschrieben:
Zitat:
Es gibt also kein Längenbyte mehr,
sondern der String kann (theoretisch) beliebig lang werden, vorrausgesetzt, es kommt
dazwischen kein Nullzeichen vor.

Das mit dem Nullzeichen stimmt nicht, wie du weiter unten im Text dann auch klar stellst. Ein String kann auch eine gesamte Exe-Datei mit beliebig vielen Nullzeichen aufnehmen. Wenn er aber an eine Funktion übergeben wird, die einen PChar (ohne zusätzliche Längenangabe) erwartet, so geht die Längeninformation verlohren und das Nullzeichen terminiert.

Naja, es kommt immer darauf an wie man es betrachtet.. beim Ausgeben wird der String beim Nullzeichen beendet...

Zitat:
Zitat:
1. “normaler” Parameter
2. const-Parameter
3. var-Parameter
4. als Pointer

Hier fehlt noch "out", bei dem der Compiler noch etwas einfügt, das du noch herausfinden musst.

Stimmt, out-Parameter hab ich nicht behandelt.. füg ich vielleicht noch dazu..

Zitat:
Zitat:
Längenzähler

Was zählt der Längenzähler denn? Die Längen des Strings? Ich dachte ein String hat nur eine Länge. Ich würde das eher als Längenfeld (es steckt ja auch ein record dahinter) oder Längenangabe bezeichnen

Hm.. werd ich vielleicht ändern..

Zitat:
Zitat:
PChar
Pchar

Kannst du dich auch mal entscheiden, wie du PChar schreiben willst.

Das liegt an der automatischen Korrektur vom Word.. dachte eigentlich ich hätte alles auf "PChar" ausgebessert.. :?

Aber danke für deine Kritik!! :)