| Home | Delphi | Download | Spaß | Impressum | eMail | xwatch.de |
![]() ![]() | PChar vs. String |
Dieser Variablentyp sorgt immer mal wieder für Verwirrung. Um zum Beispiel Speicher zu sparen, will ein Programmierer in einem Record statt Strings PChars verwenden:
Type TDaten = Record
Name: PChar;
Vorname: PChar;
{usw.}
End;
[...]
Var person: TDaten;
Begin
person.Name:='Gates';
An dieser Stelle kommt es nun zum Fehler:
Delphi löst eine Exception aus und moniert, dass an eine ungültige
Adresse geschrieben wird. Warum dies? Der Typ PChar ist ein Zeiger
auf ein Array Of Char. Das heißt, in einer Variable vom Typ
PChar kann man die Adresse speichern, an der die Zeichenkette abgelegt
werden soll, nicht jedoch die Zeichenkette selbst.
Type TDaten = Record
Name: Array [0..20] Of Char;
Vorname: Array [0..20] Of Char;
{usw.}
End;
[...]
Var person: TDaten;
Begin
person.Name:='Gates';
Bei dieser Version muss man nun allerdings
sicherstellen, dass in die Felder tatsächlich nur Namen mit maximal
20 Buchstaben geschrieben werden. Einen Char "Platz" braucht Delphi nämlich
noch, um das Ende der Zeichenkette zu markieren.
![]() ![]() | PChar als Parameter |
Um beispielsweise die Prozedur FindExecutable aufzurufen, benötigt man scheinbar zwei Parameter vom Typ PChar.
Procedure FindExecutable(file, dir: PChar; result: THandle);Heißt das nun, dass man hier wieder umständlich mit Arrays hantieren müsste, um diese Funktion aufzurufen?
var fname, dir: String;
res: THandle;
Begin
fname:='SOL.EXE';
dir:='WIN';
FindExecutable(PChar(fname),PChar(dir),res);
Intern verwaltet Delphi Strings
sehr ähnlich wie Arrays of Char. PChar(fName) tut
nichts anderes als die Adresse zu berechnen, an der die Zeichenkette gespeichert
ist, die durch die String-Variable fName repräsentiert wird.
{$H+} für lange Strings eingeschaltet
sein (per Voreinstellung ist er das). Wenn man auf lange Strings verzichten
will oder D1 benutzt, klappt das Typecasting nicht so einfach. Hier muss
man entweder mit Arrays arbeiten oder die Funktion StrPCopy verwenden,
die einen Pascal-String in ein Null-terminiertes Array of Char umwandelt.
![]() ![]() | Dynamische Arrays |
Seit Version 4.0 sind dynamische Arrays Bestandteil von Delphi. In älteren Versionen muss man ein wenig "tricksen", um ähnliche Funktionalität zu bekommen:
Um dynamische Arrays in D1-D3 zu deklarieren, gibt es zwei Methoden. Ob man bei der Deklaration Variante 1 oder 2 bevorzugen soll, darueber streiten sich die Experten. In beiden Fällen ist nur wichtig, dass man nicht auf Elemente zugreift, für die kein Speicherplatz allokiert worden ist.
Variante 1:
{$RANGECHECK OFF}
type
TDynArray = array[0..0] of TIrgendwas;
PDynArray = ^TDynArray;
Variante 2:
const
MAXITEMS = 1000; { Maximale Anzahl der Einträge }
type
TDynArray = Array[0..MAXITEMS] of TIrgendwas;
PDynArray = ^TDynAray;
Die Benutzung funktioniert - unabhängig
davon, welche der beiden Varianten in der Deklaration gewählt wurde
- wie folgt:
var
anzahl: Integer;
DynArray: PDynArray;
{ Initialisierung }
anzahl := 0;
DynArray := nil;
{ Element hinzufuegen }
Inc(anzahl);
ReAllocMem(DynArray, anzahl * SizeOf(TIrgendwas));
{ letztes Element loeschen }
Dec(anzahl);
ReAllocMem(DynArray, anzahl * SizeOf(TIrgendwas));
{ alle Elemente loeschen bzw. gesamten Array-Speicher wieder freigeben }
ReAllocMem(DynArray, 0);
{ Zugriff auf Element Nr. x (0 < x <= anzahl) }
{ Element schreiben }
if x <= anzahl then DynArray^[x-1]:= irgendwas;
{ Element lesen }
if x <= anzahl then irgendwas :=DynArray^[x-1];
Folgende Hinweise zu dem Beispiel-Source:
Bevor man Daten als dynamische Arrays programmiert, sollte man sich fragen, ob es nicht sinnvoller ist, von vornherein auf dynamische Strukturen zu setzen. Delphi bietet hierfür beispielsweise TList an. Dieser Listentyp erlaubt beliebig viele Einträge und übernimmt dabei automatisch Bereichsprüfungen. Darüber hinaus bietet er den Vorteil, auch nachträglich Einträge an beliebiger Stelle einzufügen oder zu entfernen.
- Wenn TIrgendwas ein Record ist, greift man auf einzelne Felder mit DynArray^[x-1].FeldX zu.
- Man kann keine Speicherblöcke mit mehr als 64 KB (D1) bzw. 2 GB (D2/D3) allokieren.
- Die Funktion ReAllocMem ist in D1 anders deklariert. (siehe Online-Hilfe)
Type Liste : TList; TIrgendwas = ... PIrgendwas = ^TIrgendwas; i: Integer; Var Irgendwas: PIrgendwas; ... Liste := TList.Create; ... i := Liste.Add(New(Irgendwas)); PIrgendwas(Liste[i]^) := ...; ... Dispose(PIrgendwas(Liste[i]^)); Liste.Delete(i); ... Liste.Free;
- Man kann beliebig oft mit New(Irgendwas) neue Elemente erzeugen und an Liste übergeben. Die Verwaltung der Zeiger übernimmt Liste. Es hängt also kein Zeiger "in der Luft".
- Delete setzt im Gegensatz zu Remove nur den Zeiger auf nil. Mit Pack werden diese nil-Zeiger aus der Liste entfernt.
![]() ![]() | TStrings |
TStrings stellt nur das ,,Skelett'' für eine Stringliste bereit. Diese Klasse
ist hauptsächlich für die Entwicklung weiterer Klassen gedacht, die mit TStrings
dann ihre Stringverwaltung implementieren, indem sie die entsprechenden virtuellen Methoden von
TStrings überschreiben.
Ein Beispiel für ein solches Objekt ist TStringList. Dieses ist von TStrings
abgeleitet und implementiert tatsälich die benötigten Methoden. Wenn man also eine
solche Stringverwaltung vorgefertigt haben möchte, benutze man TStringList
statt TStrings.
![]() ![]() | Gleichheit von Fließkomma-Variablen prüfen |
Fließkommawerte werden nur dann als gleich angesehen, wenn sie dasselbe Bitmuster aufweisen. Zwei Fließkommawerte, die sich um ein winziges Epsilon unterscheiden, haben verschiedene Bitmuster.
Hat man nun die beiden Werte auf verschiedene Weise ermittelt, wobei die Wege arithmetisch durchaus gleichwertig sein können, bekommt man mit hoher Wahrscheinlichkeit ein numerisch anderes Ergebnis.
Man sollte daher statt Gleichheit oder Ungleichheit auf "Ähnlichkeit" prüfen:function equals(a,b:extended): boolean; begin result:=(abs(a-b)<epsilon); end;
epsilon setzt man auf einen kleinen Wert, wie klein hängt von den
Anforderungen an der Anwendung ab.
![]() ![]() | Kalenderwoche ermitteln |
Irgendwann mal irgendwo abgeschrieben:
function WeekOfYear(D : TDateTime) : Byte;
var
aDate : Integer;
MinDate : tDateTime;
MaxDate : tDateTime;
Day, Month, Year : Word;
FirstDate : Integer;
begin
aDate := Trunc(D);
MinDate := EncodeDate(1900, 1, 1);
MaxDate := EncodeDate(3999, 12, 31);
if (aDate < MinDate) or (aDate > MaxDate) then begin
Result := 0;
exit;
end;
aDate := aDate + 3 - ((5 + DayOfWeek(aDate)) MOD 7);
DecodeDate(aDate, Year, Month, Day);
FirstDate := Trunc(EncodeDate(Year, 1, 1));
Result := 1 + (aDate - FirstDate) DIV 7;
end;
Frequently Asked Questions about Calendars von Claus Tøndering
Die Kalender-FAQ enthält geschichtliche Informationen über die Zeitrechnung sowie die wichtigsten Kalender-Algorithmen.