Na ja, ein Zeiger muss nicht auf ein Objekt zeigen (sorry wenn ich mal so kleinlich bin). Wenn du Integer Werte speicherst, dann liegen die irgendwo im Speicher (kannst also auch einen Zeiger darauf speichern), das gilt auch für Bytes, Strings, ..., beliebige Klassen.
Ob es Sinn macht einen Zeiger zu speichern ist immer eine andere Sache. Dein Zeiger hat den Vorteil, dass er sehr universell ist. Zudem hat er immer die volle Registerbreite deines
OS (i.d.R. also 4 Byte). Jetzt ist ein Byte aber nun mal nur ein Byte groß. Wenn du also Pointer auf das Byte speicherst... Von der Perfomance her wäre es natürlich an sich schlecht Byte zu verwenden (da müssen 3 Byte mit 0en gefüllt werden) und Platz hat man auf heutigen System offensichtlich genug (kenne wenig Programme die da an Speicher sparen, vorallem nicht bei solchen Variablen!)
Wie gesagt, man könnte über den Sinn streiten.
Wenn du ein Array von Pointern verwendest, kannst du so ziemlich alles speichern (eigentlich sogar genau alles). Dann solltest du aber schauen, wie gut die Umsetzung von TList in Delphi 3 ist (vielleicht gibt es dort ja schon eine TList). Diese ist nichts anderes als ein Array of Pointer, das geschickt von Delphi verwaltet wird. So alloziert eine TList automatisch Speicher und gibt ungenutzten auch wieder frei.
Das beantragen und verändern der Speichergröße kostet unabhängig von der eigentlich Größe nahezu konstant viel Zeit. Würdest du also bei jedem neuen Element immer die Länge um 1 vergrößern wird das schnell recht aufwändig. Vergrößerst du das Array gleich um 100 Elemente, so sparst du max. 99 solcher Aufrufe. TList bietet den Vorteil sich genau darum selbst zu kümmern.
Ich weiß wie gesagt nicht wie es da unter Delphi 3 aussieht.
Was ein dynamisches Array angeht, du könntest mit einem Array of Pointer halt alles machen, aber du hast natürlich keine Typsicherheit mehr:
Code:
var a : Array of Pointer; // wie auch immer es dann in einer Delphi 3 Umsetzung aussähe!
b : Byte;
i : Integer;
c : Cardinal;
o : TObject;
begin
setLength(a, 4);
b := 0;
i := -1;
c := 4000000000;
a[0] := @b;
a[1] := @i;
a[2] := @c;
// noch eine Falle!
a[3] := @o;
end;
Dieser Code würde funktionieren und du hättest gleich mehrere Fallen. Einerseits siehst du, dass hier 3 unterschiedliche Typen in das Array gepackt wurden. Was sich an der Stelle a[0] befindet ist aber eine 32-Bitige (vielleicht auch schon 64) Adresse. Diese Adresse wird ungleich 0 sein (die Variablen gibt es ja zu dem Zeitpunkt alle und sie sind lokal). Das heißt beim auslesen des Arrays an Stelle 0 könntest du auch in ein Char oder eine TList casten. Ob das glückt ist eine andere Sache!
Das andere Problem ist, du hast hier die Adressen lokaler Variablen stehen. Steht dies in einer Prozedur, würden die Adressen keine Gültigkeit nach dem Ende der Prozedur haben. Gut in diesem Fall wird auch das dynamische Array in dieser Prozedur angelegt, aber wenn es global wäre...
Die Adressen behalten natürlich ihre Gültigkeit (es gibt die Adresse noch), was dort steht ist aber mit dem Ende der Prozedur eher zufällig.
Hier kommt auch die weitere Falle ins Spiel. o ist ein TObject, wie du siehst wird der Konstruktor nie aufgerufen. Die gespeicherte Adresse a[3] kann aber <> nil sein. Dies liegt einfach daran, dass o lokal ist. Lokale Variablen (auch Pointer und Referenzen) sind nicht initialisiert.
Diese Probleme können immer auftauchen, wenn du mit Pointern arbeitest. Deswegen macht man es ja so ungerne. Typsicherheit kann man natürlich leicht durch typisierte Pointer erreichen oder Kapselungen, die dir das einfügen nur von einem bestimmten Datentyp erlauben und aut. casten, wenn du lesend zugreifst.
Gruß Der Unwissende