This repository has been archived on 2024-12-25. You can view files and clone it, but cannot push or open issues or pull requests.
2024-03-10 20:32:51 +03:00

134 lines
9.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
.code_block {
background: #c0c0c0;
padding: 5px;
padding-right: 20px;
border: solid 1px black;
}
</style>
<script type="text/javascript">
function toggle_show(id) {
document.getElementById(id).style.display = document.getElementById(id).style.display == 'none' ? 'block' : 'none';
}
</script>
</head>
<body>
<font size="3"><a href="../Справка.html"><=</a></font><br>
<br>
<font size="5">Сохранение ссылочных типов в блочный файл.</font><br>
<hr />
<p onClick="toggle_show('decl')"><font size="4" color="008000" style="cursor: pointer">В чём проблема ссылочных типов</font></p>
<div id=decl style="display: none">
<p class=code_block>
<t style="margin-left: 00px">type </t>
<br><t style="margin-left: 10px">r1 = record </t>
<br><t style="margin-left: 20px">b: byte; </t>
<br><t style="margin-left: 20px">i: integer; </t>
<br><t style="margin-left: 20px">s: string; </t>
<br><t style="margin-left: 10px">end; </t>
<br><t style="margin-left: 00px"> </t>
<br><t style="margin-left: 00px">begin </t>
<br><t style="margin-left: 10px">var a: r1; </t>
<br><t style="margin-left: 10px">a.b := 5; </t>
<br><t style="margin-left: 10px">a.i := 12345; </t>
<br><t style="margin-left: 10px">a.s := 'abcdef'*10000;//Это 60 000 символов, ~120KB. А значит в записи никаким образом не поместилось бы </t>
<br><t style="margin-left: 00px">end. </t>
</p>
r1, byte и integer из этого примера - размерные типы (записи), а значит они хранят свои данные прямо в переменных.<br>
А string - ссылочный тип (класс). Это значит что в поле r1.s хранится только ссылка (особый указатель) на содержимое строки.<br>
<br>
То есть, в переменной "a" хранится три значения: byte, integer и ссылка.<br>
А содержимое строки хранится где-то в оперативной памяти, куда указывает эта ссылка.<br>
<br>
"BlockFileOf&ltT>" берёт содержимое переменных одним блоком памяти.<br>
Поэтому, если попытаться сохранить содержимое r1 в типизированный файл.<br>
(отключив защиту от дурака, запрещающую ссылочные типы)<br>
В файл сохранится значение ссылки, а не строки.<br>
<br>
Кроме того, ссылки отличаются от указателей тем, что они ссылаются на значение, которое может "плавать" в оперативной памяти.<br>
Точнее, сборщик мусора может двигать эти значения, для экономии места.<br>
Это значит что если сохранить запись с ссылкой в файл и сразу загрузить эту ссылку назад.<br>
Её значение может быть уже устаревшим.<br>
<hr />
</div>
<p onClick="toggle_show('io_basics')"><font size="4" color="008000" style="cursor: pointer">Размерные массивы и строки</font></p>
<div id=io_basics style="display: none">
Посмотрите в этот файл:<br>
C:\PABCWork.NET\Samples\BlockFileOfT\Сравнение скорости.pas<br>
Найдите там тип "ValueString255".<br>
На нём будут строиться дальнейшие обноснования:<br>
Этот тип представляет строку с фиксированной длинной, а точнее длиной в 255 символов.<br>
<br>
Первое, что видно - прямо перед объявлением типа стоит атрибут "StructLayout":<br>
"LayoutKind.Explicit" разрешает применять атрибут "FieldOffset" к полям.<br>
А "Size" позволяет явно указать сколько байт выделять под данную запись.<br>
Благодаря последнему - можно выделить место сразу под все 255 символов строки, не делая 255 полей с типом "char".<br>
<br>
Далее, в записи "ValueString255" есть всего 2 поля, "length" (длина) и "body" (тело, а точнее его начало)<br>
"length" это кол-во символов во всей строке. В данном случае не может быть больше 255 символов,<br>
поэтому достаточно 1 байта чтобы хранить любую возможную длину строки.<br>
А "body" это начало самогО содержимого строки.<br>
Вообще, поле "body" не так уж и нужно, но с ним будет проще, потому что если создать указатель на "body" -<br>
он сразу оказывается и указателем на содержимое строки, без каких-либо преобразований.<br>
<br>
Далее идут методы для преобразования между обычной строкой и нашей размерной.<br>
Я сделал их "operator explicit"-ами, потому что их вызов красивый и удобный.<br>
<br>
Смотрим в первый, преобразовывающий массив символов в размерную строку:<br>
<br>
Если выполнить "Result.length := a.Length" и передать массив длинной больше 255 символов -<br>
в этой строчке произойдёт переполнение и "Result.length" запонится мусором, поэтому сначала надо уменьшить значение до "MaxLength".<br>
<br>
Далее, если длина равна нулю - ничего больше делать не надо.<br>
В противном случае - используем процедуру "CopyMem".<br>
<br>
Насчёт того, как работает "CopyMem", можно написать целую диссертацию.<br>
Но главное, что надо знать вам: если попытаться получить указатель на элемент массива ("@a[n]") -<br>
получаем утечку памяти. Однако, если передать "a[n]" в "CopyMem" как var-параметр<br>
(что, по сути, тоже создаёт указатель на этот элемент массива),<br>
то всё в порядке.<br>
<br>
В данном случае мы копируем память начиная с нулевого элемента массива<br>
Вставляем её в "Result.body"<br>
И всего копируем "Result.length*2" байт (*2 потому что каждый символ занимает 2 байта в памяти)<br>
<br>
Преобразование из "string" в размерную строку работает также, там ничего интересного.<br>
У преобразования размерной строки в массив символов всего одна особенность:<br>
Теперь результат у нас ссылочный (массив), поэтому память под него нужно выделить вручную ("new char[...]").<br>
<br>
И последнее преобразование, из "ValueString255" в "string":<br>
А вот тут очень приятно вышло - у "string" есть конструктор который идеально подходит:<br>
Первый параметр это указатель на начало блока памяти содержащего символы. То есть "@s.body" и даже тип подходит без преобразований.<br>
Второй параметр - смещение начала. У нас символы начинаются прямо на "body", поэтому 0.<br>
И последний - количество символов.<br>
<br>
Последнее, что есть в данной записи - перегрузка "ToString". Это вообще не обязательный код,<br>
но с этой функцией размерная строка будет вести себя точно так же, как и обычная, если передать её во "Writeln".<br>
Также можно добавлять и другие свои методы в данный тип и даже сделать все те же методы, как и у ссылочных строк, или даже больше.<br>
<br>
Ну а размерные массивы ничем не отличаются в реализации.<br>
Можно даже сказать, что "ValueString255" это размерный массив символов, ничего от этого не изменится.<br>
<hr />
</div>
</body>
</html>