Дороговкази в Delphi
Покажчик - це всього лише спеціальна змінна. На відміну від звичайних змінних, які зберігають значення певного типу, покажчик зберігає ад-рес осередки пам'яті. Назва "покажчик" обумовлено тим, що покажчик не з-тримає конкретного значення, а вказує на комірку пам'яті, в якій зберігається 1 потрібне значення.
У Delphi існує два типи вказівників: типізовані і нетипізовані (загальні). Типізовані покажчики можна використовувати тільки з змін-ними конкретного типу, в той час як нетипізовані покажчики можуть укази-вать на будь-які дані.
Покажчик P може використовуватися для вказівки на будь-яку комірку пам'яті, ко-торая містить цілочисельне значення.
Поки не варто намагатися використовувати покажчик Р. оскільки він не ініціал-зований. Коли курсор не инициализирован, він вказує на випадкову осередок пам'яті. Ви намагаєтесь отримати доступ покажчика, який вказує на невірну осередок пам'яті, подібна спробі стрибка з висоти без парашута. Навряд чи це можна вважати приємним проведенням часу.
Тепер покажчик Р вказує на комірку пам'яті змінної I. Якщо потрібно з'ясувати точне розташування змінної I в пам'яті, необхідно преобразо-вать тип покажчика в ціле число і відобразити його на екрані:
Лістинг 9.1. Використання простого типизированного покажчика
01. program Project1;
Другим типом покажчика в Delphi є Pointer. Pointer являє собою тип нетипізованого покажчика, який може використовуватися для вказівки на змінну будь-якого типу даних. Щоб працювати зі значенням, на яке укази-кість нетіпізірованньіл покажчик, спочатку нетипізований покажчик потрібно привести до іншого типу покажчика і виконати його розіменування. Приведення до іншого типу покажчика не представляє особливої складності, оскільки в Delphi каж-дий тип даних вже володіє відповідним типом покажчика. Наприклад, покажчиком на змінну типу Char є PChar, покажчиком на рядок - PString. покажчиком на целочисленную змінну - PInteger і так далі.
У лістингу 9.2 демонструється використання типу Pointer і його приведення до інших типів покажчиків.
Лістинг 9.2. Використання нетілізірованного покажчика
Для резервування та роботи з нестандартним блоком пам'яті необхідно ис-користувати процедури GetMem і FreeMem. Обидві процедури приймають два пара-метри: покажчик, який повинен бути пов'язаний із зарезервованим блоком пам'яті, і ціле значення, що задає кількість зарезервованих байт пам'яті. При виклику процедури FreeCall передача другого параметра не обов'язкова. У разі його передачі передається значення має відповідати кількості байтів, зарезервованих процедурою GetCall.
У наступному прикладі демонструється застосування процедури BlockRead для зчитування всього файлу в динамічно зарезервований блок пам'яті (резуль-тат показаний на рис. 9.1).
Лістинг 9.3. Завантаження файлу в динамічно зарезервований блок пам'яті
01. program Project1;
02.
03. uses
04. SysUtils;
05. procedure ReadFile (var P: Pointer; const AFileName: string);
06. var
07. Src: file;
08. BytesRead: Integer;
09. BufferPos: Pointer;
10. begin
11. AssignFile (Src, AFileName);
12. <$I ->
13. Reset (Src, 1);
14. <$I +>
15. if IOResult = 0 then
16. begin
17. if P <> nil then FreeMem (P);
18. GetMem (P, FileSize (Src));
19. BytesRead: = - 1;
20. BufferPos: = P;
21. while BytesRead <> 0 do
22. begin
23. BlockRead (Src, BufferPos ^, 1024. BytesRead);
24. Inc (Integer (BufferPos), BytesRead);
25. end;
26. CloseFile (Src);
27. end; // if IOResult
28. end;
29. procedure RemoveFile (var P: Pointer);
30. begin
31. FreeMem (P);
32. P: = nil;
33. end;
34. var
35. FilePtr: Pointer;
36. begin
37. ReadFile (FilePtr, 'c: \ data.txt');
38. WriteLn (string (FilePtr));
39. RemoveFile (FilePtr);
40. ReadLn; end.
Код завантаження файлу в пам'ять визначено в процедурі ReadFile. Перш ніж ре-зервіровать новий блок пам'яті, процедура повинна визначити, вказує чи вка-затель, переданий як параметр Р, на блок пам'яті. Якщо так, то необхідно освоєння-бодіть старий блок пам'яті. В іншому випадку виникне серйозний витік па-мяти.
Процедура GetMem використовує функцію FileSize для визначення точного обсягу пам'яті (в байтах), необхідного для зберігання всього файлу. Після того як пам'ять зарезервована, процедура починає зчитування файлу в пам'ять.
Мал. 9.1. Приведення типу даних блоку пам'яті в рядок
Ймовірно, найбільш незвичайною частиною процедури є локальний указу-тель Buf ferPos. Перш ніж почати зчитування файлу в цикл е while, процедура присвоює покажчик вказівником Bu f fer Pos:
BlockRead (Src, BufferPos ", 1024. BytesRead);
Після того як процедура BlockRead збереже 1024 байти в блоці пам'яті, що не-обходимо оновити покажчик BufferPos. Якщо цього не зробити, процедура BlockRead виконає зчитування всього файлу, але при цьому перезапише старі дані останніми 1024 байти з файлу.
Щоб відобразити весь текстовий файл, досить перетворити тип даних блоку пам'яті в рядок:
WriteLn (string (FilePtr));
Numbers: array [1..20] of Integer; PI. PInteger;