Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
3-92946
Youri
2002-11-12 11:21
2002.11.28
User abort!


1-93036
Степ
2002-11-19 11:51
2002.11.28
---|Ветка была без названия|---


1-93056
Вася Танков
2002-11-18 17:15
2002.11.28
как изменить кодировку


1-93053
BOBAH
2002-11-19 14:31
2002.11.28
Можно-ли задать маску для линии?


1-92973
DVM
2002-11-15 21:45
2002.11.28
Как узнать что открылось новое окно?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский