Форум: "Начинающим";
Текущий архив: 2007.12.30;
Скачать: [xml.tar.bz2];
Вниз
Работа с делфяными строками на билт-ин асме Найти похожие ветки
← →
Dib@zol © (2007-12-06 19:43) [0]Здравствуйте ещё раз. В рамках продвинутого изучения делфей решил разобраться, как реализовать низкоуровневую работу со строками. На первый взгляд всё просто: string-переменная - это указатель на некую область памяти, размер которой задан по смещению -4 от указателя, а количество переменных, ссылающихся на этот указатель - по смещению -8. Попробовал было я создать такой массив байт в памяти вручную. Ан нет - не получается! Функция моя выдаёт пустую строку. Выходит, я не выполнил какое-то условие для правильного создания. Начал рыться в Гугле - ничего не нашёл. Было динамическое распределение памяти, но о строках как таковых было мало вразумительного. Пожалуйста, помогите теоретическим материалом (на тему управления памятью под строки в Делфях)!
Вот моя нерабочая функция:
function CreateString(Len:DWORD):string;
asm
PUSH ECX;
ADD EAX, 8;
PUSH EAX;
CALL GetMemory;
MOV ECX, EAX;
POP EAX;
XCHG EAX, ECX;
ADD EAX, 8;
MOV DWORD PTR [EAX-8], 0;
SUB ECX, 8;
MOV DWORD PTR [EAX-4], ECX;
DEC EAX;
@loop:
MOV BYTE PTR [EAX+ECX], "q";
LOOP @loop;
INC EAX;
POP ECX;
end;
← →
Юрий Зотов © (2007-12-06 20:21) [1]В код не вникал, а в определении строки есть существенная неточность:
...это указатель на некую область памяти, которая заканчивается нулевым байтом, размер которой...
← →
Юрий Зотов © (2007-12-06 21:26) [2]> Dib@zol © (06.12.07 19:43)
Если уж есть такое неуемное желание (правда, непонятно, зачем), то переведите на Асм функцию NewStr или вот эту самоделку.
type
PAnsiStringRecord = ^TAnsiStringRecord;
TAnsiStringRecord = record
RefCount: cardinal;
BodySize: cardinal;
Body: char;
end;
function NewAnsiString(Source: string): string;
var
AnsiStringRecord: PAnsiStringRecord;
begin
GetMem(AnsiStringRecord, SizeOf(TAnsiStringRecord) + Length(Source));
AnsiStringRecord^.RefCount := 0;
AnsiStringRecord^.BodySize := Length(Source) + 1;
if Source <> "" then
CopyMemory(@AnsiStringRecord^.Body, @Source[1], AnsiStringRecord^.BodySize)
else
AnsiStringRecord^.Body := #0;
Result := PChar(@AnsiStringRecord^.Body)
end;
← →
Rouse_ © (2007-12-06 21:37) [3]хм.
А где call System.@UniqueString? Где выставление размера? Где рефы? Почему стек бьется?
Нет. Не нужно пока тебе самому писать работу со строками - изучи работу стандартного менеджера памяти сначала...
← →
oxffff © (2007-12-06 22:44) [4]
> Dib@zol © (06.12.07 19:43)
RTFM
function _NewAnsiString(length: Longint): Pointer;
{$IFDEF PUREPASCAL}
var
P: PStrRec;
begin
Result := nil;
if length <= 0 then Exit;
// Alloc an extra null for strings with even length. This has no actual cost
// since the allocator will round up the request to an even size anyway.
// All widestring allocations have even length, and need a double null terminator.
GetMem(P, length + sizeof(StrRec) + 1 + ((length + 1) and 1));
Result := Pointer(Integer(P) + sizeof(StrRec));
P.length := length;
P.refcnt := 1;
PWideChar(Result)[length div 2] := #0; // length guaranteed >= 2
end;
{$ELSE}
asm
{ -> EAX length }
{ <- EAX pointer to new string }
TEST EAX,EAX
JLE @@null
PUSH EAX
ADD EAX,rOff+2 // one or two nulls (Ansi/Wide)
AND EAX, not 1 // round up to even length
PUSH EAX
CALL _GetMem
POP EDX // actual allocated length (>= 2)
MOV word ptr [EAX+EDX-2],0 // double null terminator
ADD EAX,rOff
POP EDX // requested string length
MOV [EAX-skew].StrRec.length,EDX
MOV [EAX-skew].StrRec.refCnt,1
RET
@@null:
XOR EAX,EAX
end;
{$ENDIF}
← →
Dib@zol © (2007-12-07 09:09) [5]> А где call System.@UniqueString?
В том-то и дело, что я хотел понять, как он работает.
> Где выставление размера?
MOV DWORD PTR [EAX-4], ECX;
> Где рефы?
MOV DWORD PTR [EAX-8], 0;
> Почему стек бьется?
А он не бьётся. Количество PUSH"ей абсолютно равно количеству POP"ов. Если бы стек бился, после выполнения немедленно возник бы AV.
А остальным - спасибо большое за справочную информацию.
← →
Rouse_ © (2007-12-07 10:17) [6]
> Если бы стек бился, после выполнения немедленно возник бы AV.
Что собственно и наблюдается :)
← →
Dib@zol © (2007-12-07 10:23) [7]А у меня почему-то не наблюдается... На какой винде тестировали? Я на 98-ой, однако вчера это писалось на ХРеновине, и тоже не вызывало AV.
← →
Rouse_ © (2007-12-07 10:50) [8]Дома на ХР протестировал.
← →
Сергей М. © (2007-12-07 10:50) [9]
> Dib@zol © (07.12.07 09:09) [5]
ну ты главную-то свою ошибку осознал ?
← →
Dib@zol © (2007-12-07 10:55) [10]> ну ты главную-то свою ошибку осознал ?
Что количество символов должно быть кратно 2, и завершаться должно как минимум одним нулём? Если это оно, то да.
← →
Dib@zol © (2007-12-07 11:00) [11]ЗЫ А кто всё-таки может поделиться ссылочкой на теорию по теме "механизмы работы типа string"?
ЗЗЫ Сергей М - анкета не найдена... :)
← →
Сергей М. © (2007-12-07 11:01) [12]
> Dib@zol © (07.12.07 10:55) [10]
Это не главная ошибка.
Главная состоит в том, что ты игнорируешь неявный переданный тебе в edx параметр. Результат работы ф-ции ты обязан записать по адресу, который передан тебе в edx. А ты возвращаешь рез-т в eax, что тут же ведет к утечке памяти.
← →
Dib@zol © (2007-12-07 11:12) [13]> ты игнорируешь неявный переданный тебе в edx параметр
o_0 То есть ты хочешь сказать, что GetMemory что-то ещё пишет в EDX??? Но ведь она работает под директивой __fastcall, в которой про подобное ничего не указано! То есть использование GetMemory под чистый Паскаль, где к EDX обратиться нельзя, неизбежно приведёт к мем-лику??? И опять же, по правилам __fastcall"а результат должен передаваться через ЕАХ! Поясни пожалуйста!
← →
Сергей М. © (2007-12-07 11:24) [14]
> ты хочешь сказать, что GetMemory что-то ещё пишет в EDX?
Нет, она возвращает результат в eax.
> Но ведь она работает под директивой __fastcall
Верно. И ее результат ты совершенно справедливо забираешь из eax.
Но я не о вызове GetMemory, а о вызове твоей функции. Именно ей компилятор передает в edx адрес, по которому в рез-те ты должен записать ссылку на аллокированную тобой строку.
Вся петрушка в том, что компилятор, видя типом твоей ф-ции тип LargeString, на самом деле рассматривает твою ф-цию как процедуру, передавая тебе в полном соответствии с pascal factcall в EAX требуемый размер будущей строки и в EDX адрес, по которому он ожидает увидеть структуру, определяющую аллокированную тобой строку.
← →
Ins © (2007-12-07 11:41) [15]
> ЗЫ А кто всё-таки может поделиться ссылочкой на теорию по теме
>"механизмы работы типа string"?
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1206
http://www.rsdn.ru/article/Delphi/dynarrays.xml
← →
Dib@zol © (2007-12-07 11:47) [16]> [14] Сергей М. © (07.12.07 11:24)
Во! Спасибо, что ткнул носом! Блин, а я и забыл совсем...
Короче вот окончательный вариант:
function CreateString(Len:DWORD):string;
asm
TEST EAX, EAX;
JLE @null;
PUSH ECX;
PUSH EDX;
ADD EAX, 10;
AND EAX, -2;
PUSH EAX;
CALL GetMemory;
POP ECX;
MOV WORD PTR [EAX+ECX-2], 0;
ADD EAX, 8;
MOV [EAX-4], ECX;
MOV [EAX-8], 1;
DEC EAX;
SUB ECX, 10;
@loop:
MOV BYTE PTR [EAX+ECX], "q";
LOOP @loop;
INC EAX;
POP EDX;
MOV DWORD PTR [EDX], EAX;
POP ECX;
RET;
@null:
XOR EAX, EAX;
end;
> [15] Ins © (07.12.07 11:41)
Отдельное спасибо!
← →
Сергей М. © (2007-12-07 11:52) [17]
> я и забыл совсем
А отладчик на что ?)
Страницы: 1 вся ветка
Форум: "Начинающим";
Текущий архив: 2007.12.30;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.009 c