procedure Encode(const Source; var Dest; DataSize: Integer);
procedure Decode(const Source; var Dest; DataSize: Integer);
Tutaj po prostu przekazywane są dwa adresy, czyli na początek bloku źródłowego i docelowego, a także liczba bajtów do przetworzenia. Równie dobrze te sygnatury mogłyby wyglądać tak:
procedure Encode(ASource, ADest: Pointer; ADataSize: Integer);
procedure Decode(ASource, ADest: Pointer; ADataSize: Integer);
i efekt byłby identyczny, tyle że wszystko byłoby klarowne — funkcja chce adresów na dowolne dwa bloki danych (stąd nietypowany wskaźnik), w wywołaniu jawnie trzeba by użyć operatora pozyskania adresu (lub po prostu przekazać zmienną wskaźnikową).
Podobną papkę z mózgu robi funkcja Move
, bo też ma parametry beztypowe. Mając wskaźniki na dane, można łatwo spaść z rowerka, podając je do Move
— jeśli zgubi się dereferencję przy którymś, to albo zostanie skopiowany adres pointera zamiast danych (bug trudny do wychwycenia), albo dane zostaną skopiowane do zmiennej wskaźnikowej, zamiast w miejsce, na które ten wskazuje. Drugi przypadek to jeszcze większe bagno, bo jeśli nie poleci błąd segmentacji, to nadpisze się inne zmienne lokalne w ramce stosu i wyjdą z tego banany.
Moja opinia jest taka, że parametry beztypowe to zło i należy ich unikać. Lepiej musieć napisać tę małpkę przed nazwą zmiennej, niż później tracić masę czasu na szukanie bugów. Mimo wszystko warto wiedzieć jak to wszystko działa, bo w przyszłości na pewno będzie się miało z tym do czynienia.