Форум: "Основная";
Текущий архив: 2002.11.28;
Скачать: [xml.tar.bz2];
Вниз
Физическое расположение STRING в памяти Найти похожие ветки
← →
Tano (2002-11-16 12:26) [0]Господа мастера!
Меня очень интересуют детали физического размещения в памяти переменной типа String.
Вопрос может быть и тревиальный, но в Delphi Help это не описано, а на форумах я встречал противоречивые сведения (как ни странно).
Интересует именно такая вещь: непрерывна ли строка в памяти и при изменении ее размера SetLength(S) происходит выделение линейного участка памяти либо какими-то ухищрениями она делится на части (когда в конце очередного блока стоит указатель на следующий блок).
Задача: реализовать копирование ассемблерным кодом или просто командой Move переменную(String) в/из выделенного LocalAlloc участка памяти.
← →
MBo (2002-11-16 12:33) [1]Непрерывна.
← →
Opuhshii (2002-11-16 13:17) [2]"в Delphi Help это не описано" - описано, см.Object Pascal Referense-Memory Management....
← →
Юрий Зотов (2002-11-16 13:47) [3]String - это указатель на 12-байтовую структуру, в которой находятся счетчик ссылок на строку (смещение -8), длина ее тела в байтах (смещение -4) и указатель на тело строки (смещение 0). Само же тело строки хранится отдельно и размещено непрерывным экстентом памяти.
Поэтому копировать тело строки S можно очень даже просто (Move, CopyMemory), но начинать надо с адреса @S[1], а не с @S.
← →
Tano (2002-11-16 18:22) [4]Большое спасибо!
← →
Fantasist (2002-11-16 20:07) [5]
> ). Само же тело строки хранится отдельно и размещено непрерывным
> экстентом памяти.
Проведем эксперемент. Сам я его уже не раз проводил, поэтому код готовый есть:
var
Form1: TForm1;
s:string;
implementation
{$R *.dfm}
procedure Dump(pt:pointer);
var
p:PChar;
a:array [0..19] of char;
i:integer;
s:string;
begin
p:=PChar(pt);
dec(p,8); //будем просматривать память со смещения -8
s:="Address "+IntToHex(Integer(p),8)+": ";
for i:=0 to 18 do
begin
if (p^ in ["a".."z"]) or (p in ["0".."9"]) then
s:=s+" "+a[i]+" "
else
s:=s+" #"+IntToStr(Integer(a[i]))+" ";
Inc(p);
end;
Form1.Memo1.Lines.Add(s);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s2,s3:string;
begin
s:="abcd";
s2:="efg";
s3:=s;
Dump(pointer(s));
Dump(pointer(s2));
Dump(pointer(s3));
end;
Если интересует output, то вот он:
Address 00BD3CC4: #2 #0 #0 #0 #4 #0 #0 #0 a b c d #0 #0 #0
Address 00451348: #255 #255 #255 #255 #3 #0 #0 #0 e f g #0 #85 #139 #236
Address 00BD3CC4: #2 #0 #0 #0 #4 #0 #0 #0 a b c d #0 #0 #0
Теперь должно все должно быть очевидно. Delphi6.
← →
Fantasist (2002-11-16 20:19) [6]А теперь, еще один веселый момент:
procedure TForm1.Button1Click(Sender: TObject);
var
s2,s3:string;
begin
s:="abcd";
s2:="efg";
s3:=s;
Dump(pointer(s));
Dump(pointer(s2));
Dump(pointer(s3));
Dump(Pointer(@s[1])); //добавим эту строчку
end;
и output:
Address 00BD3CC4: #2 #0 #0 #0 #4 #0 #0 #0 a b c d #0 #0 #0
Address 00451358: #255 #255 #255 #255 #3 #0 #0 #0 e f g #0 #85 #139 #236
Address 00BD3CC4: #2 #0 #0 #0 #4 #0 #0 #0 a b c d #0 #0 #0
Address 00BD1FB8: #1 #0 #0 #0 #4 #0 #0 #0 a b c d #0 #0 #67
Что мы видим? Компилятор распознает опасную операцию использования адреса елемента строки и создает для нее отдельную копию.
Значения s и @s[1] - совершенно одинаковы.
← →
Fantasist (2002-11-16 20:27) [7]Ой, только что заметил, то код для функции Dump привел кривой. Я ее слегка для другого использовал и показал версию на половину подредактированную. Надо так:
procedure Dump(pt:pointer);
var
p:PChar;
i:integer;
s:string;
begin
p:=PChar(pt);
dec(p,8);
s:="Address "+IntToHex(Integer(p),8)+": ";
for i:=0 to 14 do
begin
if (p^ in ["a".."z"]) or (p^ in ["0".."9"]) then
s:=s+" "+p^+" "
else
s:=s+" #"+IntToStr(Integer(p^))+" ";
Inc(p);
end;
Form1.Memo1.Lines.Add(s);
end;
← →
Ученик (2002-11-16 21:10) [8]>Юрий Зотов © (16.11.02 13:47)
По-моему Вы ошибаетесь, в этой структуре хранится и текст строки
← →
Tano (2002-11-16 21:23) [9]> Fantasist
Интересный пример. В описании String говориться, что если нескольким переменными String присваивается одна и та же строка (переменная), то храниться только одна копия в памяти и в первоначальной переменнной только увеличивается счетчик ссылок ( 2Opuhshii, кстати, об этом счетчике в Delphi Help и не говорилось ;) - thanks 2 Юрий Зотов!), а при попытке изменить какую-либо переменную, ссылающуюся на исходную строку - для нее создается отдельная копия. В Вашем примере, Fantasist, это видно по первым цифрам дампов, но учтите, что Dump - написанная на Delphi функция и передача в нее строки - уже как бы создание другой переменной, указывающей на передаваемую строку. Чтобы обойти такие хитрости менеджера строк я использую ASM, куда передаю адреса строки и блока памяти: rep movsb и все!
Вообще говоря, я уже реализовал то, что написано в subj->Задача, но грызть начали смутные сомнения, что благополучная работа - просто частный случай при небольших длинах строк. Мне важна уверенность в линейности. Thanks to ALL!
2 Ученик: неверно: ShortString=String[n] - паскалевская строка, нулевой байт - длина, остальные 1..n (n<=255) - буквы,
а AnsiString=String - именно то, о чем говорит Юрий Зотов.
← →
Ученик (2002-11-16 21:34) [10]>Tano © (16.11.02 21:23)
Речь шла о структуре, если S : String, то S действительно указатель на структуру, но состав ее несколько иной
← →
Ученик (2002-11-16 21:36) [11]>Ученик © (16.11.02 21:34)
Точнее указатель на часть этой структуры - само тело строки, остальные ее части по указаным Юрием Зотовым смещениям
← →
Fantasist (2002-11-17 00:12) [12]2Tano:
Ваши рассуждения смотряться как-то странно, после моего примера. По моему в нем все очевидно. С количеством ссылок тоже не все так прямолинейно - как видно, для локальных переменных string счетчик ссылок не храниться - локальные переменные будут уничтоженны при выходе из функции в любом случае, так что хранение ссылок не нужно. С соответсвующими последствиями. Например при присвоении глобальной или члену класса переменной string значение локальной переменной string - будет произведенно копирование.
> но учтите, что Dump - написанная на Delphi функция и передача
> в нее строки - уже как бы создание другой переменной...
А это-то тут при чем? Нет тут никакой хитрости компилятора. Если у вас есть указатели p1 и p2 и если вы произведете присваивание p1:=p2, то p1 и p2 остануться отдельными переменными, но значение у них будет одно и то-же, а значит и область памяти просматриваемая по их значениям будет одна и та же.
> что Dump - написанная на Delphi функция и передача в нее
> строки - уже как бы создание другой переменной, указывающей
> на передаваемую строку
Нету никакой передачи строки. Передаются только целые значения - в нашем случае мы называем их указателями. Если вам в асме понятнее то пожалуйста, скомпилированный код:
Dump(pointer(s));
move eax,[s]
call Dump
В коде вообще нет такого понятия как строка. Для компилятора, единственное отличие строки string от обычного указателя на Char(кроме хранения длинны и ссылок), это необходимость генерации специального кода, при операции над этой переменно.
← →
Tano (2002-11-17 10:20) [13]
> Fantasist © (17.11.02 00:12)
Вы к примеру не дали комментария, что именно там очевидно, поэтому я решил, что Вы разъясняете принцип хранения строк одинакового содержания.
С тем, что говорится в последнем сообщении, я знаком и не спорю - просто не понял Вас.
Еще вопрос о команде Move(S, D, Count). Она копирует из S в D (то есть рассматривает S и D как указатели на данные в программе). Как она отнесется к переменой p:Pointer. Она распознает тип и использует хранящийся адрес, либо будет рассматривать p как буффер в 4 байта? Дело в том, что Move я практически никогда не использовал (за 7 лет Pascal и Delphi), вопросы копирования областей памяти решал иначе. Как Move решает проблему перекрытия диапазонов (в Helpe я нашел только, что они не проверяются)? Я на asm написал маленькую процедуру, которая проверяет перекрытие и без разрушения данных может передвинуть пересекающиеся блоки и вперед и назад (т.е. по адресу приемника данные будут в целости, но могут наложиться на источник) - мне приходится вставлять куски в имеющийся блок => надо его раздвигать.
Попутно: можно ли как-то в Run-time выяснить тип переменной (вроде SizeOf для размера)?
P.S.Если кому кажется, что меня тянет на изобретение велосипеда, быть может... Иногда просто нет времени найти точное описание готовых решений или поэкспериментировать и тогда бывает проще сделать самому.
← →
Fantasist (2002-11-17 10:46) [14]
> Еще вопрос о команде Move(S, D, Count).
Команда Move совершенно примитивна - работает с безтиповыми переменными, поэтому можно запихнуть в нее любые переменные. Никаких проверок типа соответсвенно не делается и никак особо не обрабатываются. Все можно посмотреть в исходнике этой функции.
> можно ли как-то в Run-time выяснить тип переменной
Странный вопрос. Тип переменной всегда точно известен на момент компиляции. Единственный случай, где уместно говорить о RTTI - это выяснения типа переменной на которую указывает некоторый указатель. В этом случае, для простых типов такое естественно не поддерживается - слишком бы много памяти пришлось бы расходовать. Соответсвенно, если у вас есть pointer вы не сможете узнаеть, что он указывает на Integer. Delphi иожет генерировать RTTI для классов, и автоматически генерирует ее для классов унаследованных от TPersistent. Управляется ключом {$M}
← →
Anatoly Podgoretsky (2002-11-17 11:12) [15]Tano © (17.11.02 10:20)
Ну если тебе недостаточно того что написано в справочной системе по поводу процедуры Move, то всегда можешь посмотреть исходный код. Но если у тебя есть желание дублировать код из Move, то конечно никто не мешает.
P.S. насчет P.S. - это точно тянет
← →
DiamondShark (2002-11-17 12:12) [16]Мать вашу, теоретики!
Какие еще, нахрен, структуры, Move и прочий бред?
var
S: string;
+------------+
| S: string |---------+
+------------+ |
\____ ____/ |
\/ |
Указатель |
(4 байта) |
|
|
|
Где-то в куче V
+----------+--------+-----+-----+- -+----+
| RefCount | Length | "A" | "B" | ... | #0 |
+----------+--------+-----+-----+- -+----+
\___ ____/ \__ __/ \__________ ________/
\/ \/ \/
4 байта 4 байта Length+1 байт
Это и есть string, который AnsiString.
А копировать строку лучше всего вот так:
S1 := S2;
Если надо гарантировать физическое копирование, а не просто инкремент счетчика ссылок, то вызвать
UniqueString(S2);
Чего еще не ясно?
← →
Ученик (2002-11-17 13:49) [17]>DiamondShark © (17.11.02 12:12)
То что Вы нарисовали, как это называется в практике ?
← →
DiamondShark (2002-11-17 15:34) [18]
> Ученик © (17.11.02 13:49)
То что я нарисовал, в практике называется ASCII Chart :)
← →
Ученик (2002-11-17 15:43) [19]>DiamondShark © (17.11.02 15:34)
:-)
← →
Tano (2002-11-17 21:22) [20]
> Fantasist © (17.11.02 10:46)
Да, стормозилссс, там действительно есть исходник. Более того, там все так же, как в моей функции, только передача параметров поумнее, НО своей функции я передаю готовые адреса (Pointer или Cardinal), а Move берет адреса передаваемых переменных (!) => переменную Pointer рассмотрит как 4-байтовый буффер. Именно поэтому я задал вопрос о определении типа в RT. Move не в курсе, что я прошу скопировать из переменной по указанному адресу!
Дело в том, что как я уже говорил у меня есть необходимость изменять размер динамического блока без потери информации и вставлять в него данные. Это я делаю через группу команд LocalAlloc, LocalReAlloc, LocalFree, а их параметры :Cardinal. Таким образом у меня есть с одной стороны обычная String, с другой стороны какой-то линейный адрес в памяти.
> DiamondShark © (17.11.02 12:12)
Уточню: мне нужно не строку в строку копировать, а строку в/из выделенного динамически выделенного блока памяти, который для Delphi просто куча байт и его структура известна только мне (там и длины строк и указатели на них не соответствуют формату String)
Я на форуме встречал вопросы о том, как сохранить набор строк переменного размера в файл. Были разные предложения, но все методы решения не особо удобны в обращении.
Сейчас занимаюсь написанием компонента, который по функциям является базой данных, но при этом не требует установки в систему дополнительных компонент и удобен для применения в Delphi (просто типы элементов соответствуют Delphi-ийским) и легко конструируется в RT (т.е. добавление нового поля или изменение его типа так же просто, как добавление новой записи).
← →
Fantasist (2002-11-17 23:28) [21]
> DiamondShark © (17.11.02 12:12)
> Мать вашу, теоретики!
> Какие еще, нахрен, структуры, Move и прочий бред?
Интересно, а того слепка со string что я привел недостадочно? Обязательно нужно рисунок нарисовать? Тем не менее, это можно назвать структурой, а переменную string указателем на последний ее член. Теоретически правильно, хоть и менее понятно.
> Я на форуме встречал вопросы о том, как сохранить набор
> строк переменного размера в файл. Были разные предложения,
> но все методы решения не особо удобны в обращении.
IMHO ничего удобнее TStream не видел. По типу микрософтовской сериализации. Объект записывает все свои поля в Stream, и потом точно так же их прочитывает.
> А копировать строку лучше всего вот так:
>
> S1 := S2;
>
> Если надо гарантировать физическое копирование, а не просто
> инкремент счетчика ссылок, то вызвать
>
> UniqueString(S2);
Как-то неоднозначно. Если работаешь со строками, то зачем тебе заботиться о гарантии реального копирования, если об этом очень усердно заботиться компилятор? А если тебе надо скопировать string куда-то еще, то вряд ли тебе нужен UniqueString.
← →
Tano (2002-11-18 00:20) [22]Насчет TStream ничего не имею против, но моя задача сохранять именно наборы переменных, как в базе данных. Я не стал использовать стандатные методы работы с базами данных, поскольку требуется очень компактное хранение (объем предполагается не маленький, а синхронизация баз через дискету :( Кроме того, конечные пользователи - законченые ламеры, некоторые вообще боятся компьютеров. Требуется написать что-то, по надежности похожее на стальной шарик (чтобы не сломали :-))) с очень спецефичным интерфейсом (как справочные компьютеры на вокзале ;)
Сижу, пишу, крышеу придерживаю, чтобы не съехала 8-|...
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.11.28;
Скачать: [xml.tar.bz2];
Память: 0.53 MB
Время: 0.011 c