cdecl - как это работает и для чего это?

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Re: cdecl - как это работает и для чего это?

Сообщение bormant » 24.03.2013 22:28:58

vitaly_l писал(а):Move(s[1], Data^, Size - 1); <== вот здесь непонятно почему s[1]??? А просто s почему нельзя? Зачем [1]???
(PChar(Data) + Size - 1)^ := #0; <== вот этот код, не понимаю, что делает?

Паскалевская короткая строка в s[0] хранит актуальную длину (ту, что возвращает Length[s]), данные начинаются с 1-го элемента -- s[1]. Для длинных строк, AnsiString, данные тоже начинаются в s[1].
-- PChar(Data) -- преобразование Data типа pointer к типу PChar,
-- PChar(Data) + Size - 1 -- вычисление указателя на последний символ выделенного блока,
-- (PChar(Data) + Size - 1)^ -- тот самый последний символ выделенного блока ...
-- (PChar(Data) + Size - 1)^ := #0 -- ... которому присваивается #0, чтобы сформировать ASCIIZ строку.

Кстати, вот немного расширенный пример для иллюстрации:
Код: Выделить всё
{$H+}// это нам дано
const
  EOL = #13#10;
type
  PChunkRec = ^TChunkRec;
  TChunkRec = record
    Size: integer;
    Data: pointer;
  end;

  TReadCallback = procedure(Chunk: TChunkRec; Data: Pointer); cdecl;

procedure LoadFromFile(var F: text; ReadCallback: TReadCallback; UserData: Pointer);
var
  Chunk: TChunkRec;
  s, l: string;
  i, n: integer;
begin
  while not SeekEof(F) do with Chunk do begin
    ReadLn(F, n);                                                           
    s := '';
    for i := n downto 1 do begin
      ReadLn(F, l); s := s + l + EOL;
    end;
    Size := Length(s) + 1;
    GetMem(Data, Size);
    Move(s[1], Data^, Size - 1);
    (PChar(Data) + Size - 1)^ := #0;
    if ReadCallback <> nil then ReadCallback(Chunk, UserData);
    FreeMem(Data);
  end;
end;

// это дописываем мы для работы LoadFromFile

procedure MyHandler(Chunk: TChunkRec; UserData: Pointer); cdecl;
begin
  with Chunk do WriteLn(Size, EOL, PChar(Data));
end;

procedure HexDump(Chunk: TChunkRec; UserData: Pointer); cdecl;
const
  HD: array [0..15] of char = '0123456789ABCDEF';
var
  p: PChar;
begin
  with Chunk do begin
    WriteLn(Size);
    p := Data;
    if p<> nil then
      while p^ <> #0 do begin
        Write(HD[Byte(p^) shr 4], HD[Byte(p^) and $F], ' ');
        Inc(p);
        if (p-Data) and $F = 0 then WriteLn;
      end;
    if (p-Data) and $F <> 0 then WriteLn;
    WriteLn;
  end;
end;

var
  f: text;

begin
  Assign(f, 'tst.txt'); Reset(f);
  LoadFromFile(f, @MyHandler, nil);
  Close(f);
  Assign(f, 'tst.txt'); Reset(f);
  LoadFromFile(f, @HexDump, nil);
  Close(f);
end.
Прогон:
Код: Выделить всё
29
Это фрагмент 1 из 1 строки

29
Это фрагмент 2
из 2 строк

30
Это фрагмент 3
из 3
строк

29
Это фрагмент 4 из 1 строки

29
9D E2 AE 20 E4 E0 A0 A3 AC A5 AD E2 20 31 20 A8
A7 20 31 20 E1 E2 E0 AE AA A8 0D 0A

29
9D E2 AE 20 E4 E0 A0 A3 AC A5 AD E2 20 32 0D 0A
A8 A7 20 32 20 E1 E2 E0 AE AA 0D 0A

30
9D E2 AE 20 E4 E0 A0 A3 AC A5 AD E2 20 33 0D 0A
A8 A7 20 33 0D 0A E1 E2 E0 AE AA 0D 0A

29
9D E2 AE 20 E4 E0 A0 A3 AC A5 AD E2 20 34 20 A8
A7 20 31 20 E1 E2 E0 AE AA A8 0D 0A

Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: cdecl - как это работает и для чего это?

Сообщение vitaly_l » 24.03.2013 23:10:52

Спасибо большое.
Так ещё более понятно.
И наверно последний вопрос:
заменив: (PChar(Data) + Size - 1)^ := #0 вот на такую строку: (12345)^ := #0; будет присваивается #0, чтобы сформировать ASCIIZ строку?



.
Последний раз редактировалось vitaly_l 24.03.2013 23:29:24, всего редактировалось 2 раз(а).
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: cdecl - как это работает и для чего это?

Сообщение bormant » 24.03.2013 23:25:30

Нет. Операция ^ -- разыменование указателя -- не применяется к числу.
А ASCIIZ -- это последовательность из символов, оканчивающаяся символом с кодом 0.

ps. Забудьте пока про стек в памяти, вы употребляется эти слова не к месту.
Последний раз редактировалось bormant 24.03.2013 23:29:59, всего редактировалось 1 раз.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: cdecl - как это работает и для чего это?

Сообщение vitaly_l » 24.03.2013 23:28:48

Только не пугайтесь такому вопросу (если такое невозможно) - просто мне нужно понять... Хотя наверно это чушь... Я просто пытаюсь интерпретировать это как номер строки стэка в памяти... (забывая что у стэка есть только начало и конец) Просто вот эта строчка не укладывается в моём понимании: (PChar(Data) + Size - 1)^.... для меня это число... точнее адрес в памяти.

Спасибо дальше я найду в поиске.


.

Добавлено спустя 5 минут 12 секунд:
bormant писал(а):Забудьте пока про стек в памяти, вы употребляется эти слова не к месту

Просто у меня нет ясного понимания что именно делает такой код... Поэтому я спрашивал, чтобы отсечь лишнее.

К данным типа pchar с помощью операции сложения можно добавлять целые значения, что соответствует смещению начала ASCIIZ-строки на указанное число позиций. Аналогично выполняется операция вычитания.
Последний раз редактировалось vitaly_l 25.03.2013 02:19:46, всего редактировалось 3 раз(а).
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: cdecl - как это работает и для чего это?

Сообщение bormant » 24.03.2013 23:39:17

Тот стек, о котором шла речь в соглашениях о вызове -- это особенность процессоров, имеющих регистр указатель стека (SP -- stack pointer) и набор команд по работе с ним, причем в их состав входят и команды вызова/возврата -- адрес возврата сохраняется на стеке во время вызова подпрограмм.

По поводу число/не число. Указатель на ячейку памяти в конечном итоге тоже является числом -- адресом этой ячейки. Вот только размер и структура этого адреса не всегда однозначно ложится на те или иные целые типы, да и семантика от указателей требуется несколько иная, например в части инкремента/декремента.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: cdecl - как это работает и для чего это?

Сообщение vitaly_l » 24.03.2013 23:43:28

bormant писал(а):По поводу число/не число.

Я нашёл - Вы смещали начало ASCIIZ-строки на указанное число позиций, а потом закончили строку присвоив #0; <== правильно?
Вот пример, если кто столкнётся с подобной "дилеммой".
Код: Выделить всё

const
    pch1: pchar = 'hello world';
    pch2: pchar = 'привет мир';
var
    p_str: pchar;
    n: longint;
begin
    p_str := pch1 + 6;
    writeln(p_str); // world
    writeln(pch2 + 7); // мир

    p_str := 'This is world';
    writeln(p_str);

    n := pch2 - pch1;
    writeln(n);
readln
end.

Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: cdecl - как это работает и для чего это?

Сообщение debi12345 » 25.03.2013 10:04:41

(PChar(Data) + Size - 1)^ := #0

А разве "-1" здесь правильно ? "+Size" как раз указывает на следующий за последним байтом данных.
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5759
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Re: cdecl - как это работает и для чего это?

Сообщение alexey38 » 25.03.2013 11:02:51

vitaly_l писал(а):запускается TReadCallback из вот такой функции.
LoadFromStream(Stream: TStream; ReadCallback: TReadCallback; UserData: Pointer): LongWord;


Вы похоже залезли в дебри сами не желая этого, и у Вас получилась каша из особенностей программирования на ассемблере до форматов файлов. Это полезно изучать, но нужно не в куче, а последовательно. Интересует стек, значить изучайте стек, причем есть работа со стеком на уровне ассемблера, а есть стек, как удобная для некоторых алгоритмов форма хранения данных, в т.ч. применяемая на паскале. Принцип один и тот же, но назначение разное. Одно Вы можете знать, но не влияете на это. А другое Вы можете использовать, если это Вам надо.

В Вашем случае важно, чтобы когда Вы вызываете функцию LoadFromStream, то во втором параметре Вы указали адрес функции, имеющей тоже тип cdecl.
Приведите фактический пример из Вашей программы вызова LoadFromStream, а также фактический пример из Вашей программы, где описана процедура, указанная по втором параметре.
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: cdecl - как это работает и для чего это?

Сообщение vitaly_l » 25.03.2013 11:54:32

debi12345 писал(а):А разве "-1" здесь правильно ? "+Size" как раз указывает на следующий за последним байтом данных.

Он там прибавлял единицу, поэтому теперь отнимает - всё правильно.

Добавлено спустя 5 минут 51 секунду:
alexey38 писал(а): и у Вас получилась каша из особенностей программирования

Мы просто общались и пришли к определённому результату.
Я просто ищу решение.


.
Последний раз редактировалось vitaly_l 25.03.2013 12:08:07, всего редактировалось 2 раз(а).
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: cdecl - как это работает и для чего это?

Сообщение bormant » 25.03.2013 12:04:05

debi12345 писал(а):А разве "-1" здесь правильно ?

Если бы Size = Length(s), то вы были бы правы, но там было написано
Код: Выделить всё
    Size := Length(s) + 1;
    GetMem(Data, Size);
    Move(s[1], Data^, Size - 1);
    (PChar(Data) + Size - 1)^ := #0;
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: cdecl - как это работает и для чего это?

Сообщение vitaly_l » 25.03.2013 13:51:05

В общем cdecl - у меня начал работать...
В смысле результат тестового текстового файла вывелся в TMemo через с-декларацию cdecl...
Получается что, к cdecl - должна быть "приклеена" @функция... как в примерах, доброго bormant'a.
Либо получается что, нужна Dll, как говорит добрый debi12345.

Суть в том что, у меня не текстовый файл, а форматированный и dll никаких нет...
И @функций, как в примерах bormant'a, в модуле тоже нет... и обращение там без @FunctionName. Но файл же как-то открывался?
И в описании к модулю написано что, файл - должен открываться без dll и он на 50% считывается модулем, кроме кусочков с cdecl.

В data - лежат банальные координаты (таинственного острова..., а там капитан Немо... Хреново быть художником... Вокруг уже море... чайки.. я на острове... где-то здесь капитан Немо... пойду искать Наутилус...

.

Добавлено спустя 3 часа 11 минут 28 секунд:
Спиральная раковина Наутилуса диаметром 15—23 см разделена на 35—38 камер, последовательно соединённых длинным сифоном. Моллюск живёт в передней, самой большой камере. Раковина используется как поплавок и балласт. Нагнетая в камеры раковины биогаз, или откачивая его из них, наутилус способен всплывать к поверхности воды или погружаться в её толщу. Некоторые наутилусы жили на Земле пятьсот миллионов лет назад...
)

Спасибо всем!
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Пред.

Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 22

Рейтинг@Mail.ru
cron