Страница 1 из 1

Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 09:06:25
Putnick
Здравствуйте!
Возникло непонимание. Есть такой код:
Код: Выделить всё
program prog1;
type
  PRaw=^Traw;
  TRaw=array [0..5] of Currency;
var
  a,b,c:Currency;
  tmp:PRaw;
  i:integer;
begin
  tmp:=@a;
  for i:=0 to 5 do
  tmp^[i]:=i;
  WriteLn(a,' ',b,' ',c);
  ReadLn
end.

При компиляции в Лазарусе 1.0 на выходе имеем:
Код: Выделить всё
a=0;
b=2;
c=4;

Хотя, как мне казалось, должно было быть:
Код: Выделить всё
a=0;
b=1;
c=2;

или даже ошибка доступа - ведь память за переменной С не выделена.
Подскажите, в чём я ошибаюсь?

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 09:52:13
.wOvAN
в b и с произвольные значения поскольку им ничего не присваивается.

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 10:02:07
SSerge
Бгг.... Как нравится мне смотреть, как люди сами придумывают себе проблемы.
Итак, имеем типичный индусский код с наведенными ошибками.
Автор кода из каких то собственных умозаключений вдруг решил, что все переменные будут размещены последовательно и он их здорово отмапит на массив; при этом забывает о выравнивании и прочем. А потом еще и присваивает области памяти, даже по его же коду распределенной для трех элементов, пять элементов размапленного массива. Даже если массив действительно мапится, то два то элемента за пределами выделенной компилятором области.

Реальный пример, как делать вообще нельзя. Никогда. Ни при каких обстоятельствах.

Putnick, изучи какой нибудь язык типа явы, у которого нет указателей и привыкни работать с переменными как подобает в языках высокого уровня, а не как поступали в ассемблере ZX Spectrum.

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 13:15:22
Putnick
SSerge писал(а):Бгг.... Как нравится мне смотреть, как люди сами придумывают себе проблемы.

Ну, вообще-то, как по мне, не самый плохой способ понять как что-то работает. Тем более, что я как раз хотел избежать проблемы - если переменные обрабатываются по одинаковому алгоритму (скажем, проверка правильности ввода в каком-нибудь StringGrid'е), то почему бы не обрабатывать их в цикле?
SSerge писал(а):Автор кода из каких то собственных умозаключений вдруг решил, что все переменные будут размещены последовательно и он их здорово отмапит на массив; при этом забывает о выравнивании и прочем.

То, что переменные будут размещены последовательно мне казалось КРАЙНЕ логичным. Да, собственно, пример это подтверждает (с оговоркой). А вот на счёт "выравнивания и прочего" - если не трудно, по-подробнее? Мне, по старой памяти, казалось, что выравнивание бывает на 8/16/32 бита, а тут - на 128 получается...
SSerge писал(а):А потом еще и присваивает области памяти, даже по его же коду распределенной для трех элементов, пять элементов размапленного массива. Даже если массив действительно мапится, то два то элемента за пределами выделенной компилятором области.

Ну, об этом я, как бы, писал:
или даже ошибка доступа - ведь память за переменной С не выделена.

В остальном, уважаемый SSerge, благодарю Вас за ответ. И буду в двойне благодарен, если просветите на счёт "выравнивания и прочего".

С уважением, Алексей.

Добавлено спустя 14 минут 42 секунды:
.wOvAN писал(а):в b и с произвольные значения поскольку им ничего не присваивается.

Ага, строго произвольные 2 и 4 :D

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 13:45:34
SSerge
Там может быть и выравнивание не причем;
интересу ради, сравните значение tpm с адресом переменной "а" после того, как надругались над четвертым и пятым элементами массивов; даже если все линейно, то вы присвоением то часть указателя переписали :)

В любом случае, явные ошибки алгоритмики налицо; если сильно хочется посмотреть, что именно происходит, что ж не поставить отладчиком точку останова и взглянуть на код дизассемблера?

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 20:25:51
Vapaamies
Putnick писал(а):А вот на счёт "выравнивания и прочего" - если не трудно, по-подробнее? Мне, по старой памяти, казалось, что выравнивание бывает на 8/16/32 бита, а тут - на 128 получается...

Начиная с процессоров Pentium IV (?), оптимальным стало считаться выравнивание на границу 8 байт. К тому времени размеры установленного ОЗУ на клиентских машинах подросли, и программисты стали меньше считаться с расходами. Плюс, выравнивание на 8 байт полезно для всяких MMX/SSE, которые, бывает, и пользуются, -- в FastMM и всяких FastCode.

Короче, в Delphi 5, если мне не изменяет память, {$A+} выравнивал на 4 байта, а в Delphi 6 -- уже на 8.

Кроме того, при наличии достаточного числа регистров значения могут быть размещены в них, а не в памяти. На платформе x64 уже много регистров, чтобы это считалось справедливым намного чаще, чем для куцей регистровой модели 32-битного x86. Не уверен, правда, что тип Currency размещается в регистрах... Тут уже надо доку конкретно FPC смотреть.

Как бы то ни было, согласен с SSerge: так делать никогда нельзя!

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 22:26:42
zub
>>То, что переменные будут размещены последовательно мне казалось КРАЙНЕ логичным
да логично и пока вроде так и есть, но при очередном обновлении компилятора всё может поменяться. расположение локальных переменных в стеке задом наперед объявлению или в алфавитном порядке ничуть не менее логично. Упование на такие логичности сулит разгоебание непонятных багов с каждой новой версией компилятора. Например ссылка у вас есть только на переменню а (tmp:=@a;) и то что b и c лежат в памяти а не в регистрах - везение и игра опций оптимизации компилятора и целевой платформы. Компилятор не понимает такой способ изменения переменных (b,c) и может сделать c ними всё что душе угодно, например вообще их выкинуть как неиспользуемые (пока есть WriteLn(a,' ',b,' ',c) не выкинет)

>>по старой памяти, казалось, что выравнивание бывает на 8/16/32 бита, а тут - на 128 получается...
В данном случае выравнивание 16, т.е. a это [0], b это [2] и c это [4]

>>так делать никогда нельзя!
Так делать нельзя, но указатели ипользовать можно и иногда нужно (хотел привести хотел привести пример где без указателей никак, но чтото не сиог придумать :D )

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 22:38:14
Putnick
Vapaamies писал(а):Кроме того, при наличии достаточного числа регистров значения могут быть размещены в них, а не в памяти. На платформе x64 уже много регистров, чтобы это считалось справедливым намного чаще, чем для куцей регистровой модели 32-битного x86. Не уверен, правда, что тип Currency размещается в регистрах... Тут уже надо доку конкретно FPC смотреть.

Как бы то ни было, согласен с SSerge: так делать никогда нельзя!

Благодарю за разъяснения, о регистрах/ММХах не подумал.
Хотя, забавно - вот такое:
Код: Выделить всё
program test;
type
  tst=record
   a,b,c:currency
  end;
  PRaw=^TRaw;
  TRaw=array [0..2] of currency;
var
  t:tst;
  tmp:PRaw;
  i:integer;
begin
  tmp:=@t.a;
  for i:=0 to 2 do
    tmp^[i]:=i;
  writeLn(t.a,' ',t.b,' ',t.c);
  Readln;
end.

работает.
Впрочем, Zub - отдельное ему спасибо - всё очень вдумчиво и подробно разъяснил, на счёт везения и зависимости от особенностей компилятора и платформы, которые могут и поменяться.

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 10.10.2012 22:55:46
zub
>>В данном случае выравнивание 16, т.е. a это [0], b это [2] и c это [4]
Тут я погоречился походу, в массиве свое выравнивание, в стеке свое. они пересеклись на 2 и 4. т.е. выравнивание в стеке больше в 2 раза выравнивания в массиве?

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 11.10.2012 00:06:39
Vapaamies
Putnick писал(а):
Код: Выделить всё
  tst=record
   a,b,c:currency
  end;
  PRaw=^TRaw;
  TRaw=array [0..2] of currency;


Не уверен, что сработает в Delphi. Для гарантии в Delphi я бы объявил tst как packed record.

В моем предыдущем сообщении я нигде не утверждал, что указателями пользоваться нельзя. Указателями пользоваться можно и нужно, иногда даже очень нужно. Необходимо лишь, чтобы они указывали на выделенную память, а не в молоко.

Продолжая разговор о способах размещения, могу добавить, что есть такая штука, как оптимизация размещения в стеке согласно фактическому использованию. Delphi об этом сообщает подсказкой "xxx never used" -- когда переменная полностью выкидывается. А если не выкидывается, компилятор использует хитрый алгоритм, деля код на неявные блоки -- от первого использования переменной до последнего, и каждая переменная существует только в рамках этого блока -- прямо как в Си. Сообщение отладчика "xxx inaccessible here due to optimization" -- как раз про это. В длинной процедуре часть локальных переменных может быть размещена поверх других, уже вышедших из области видимости. Это внутренняя кухня компилятора, и предсказать ее никак невозможно.

Добавлено спустя 8 минут 15 секунд:
zub писал(а):Тут я погоречился походу, в массиве свое выравнивание, в стеке свое.

В массивах нет выравнивания, по крайней мере в Delphi.

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 12.10.2012 06:31:28
SSerge
Putnick писал(а):Хотя, забавно - вот такое:

Код: Выделить всё
program test;
type
tst=record
a,b,c:currency
end;
PRaw=^TRaw;
TRaw=array [0..2] of currency;
var
t:tst;
tmp:PRaw;
i:integer;
begin
tmp:=@t.a;
for i:=0 to 2 do
tmp^[i]:=i;
writeLn(t.a,' ',t.b,' ',t.c);
Readln;
end.


работает.



А чего забавного то; выхода размапленного массива за пределы выделенной для данных памяти нет;
Если в первом примере ограничитесь массивом из трех элементов, то можете получить ту же картину; хотя, повторюсь, подход принципиально неправильный :D

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 12.10.2012 10:02:11
Putnick
SSerge писал(а):А чего забавного то; выхода размапленного массива за пределы выделенной для данных памяти нет;
Если в первом примере ограничитесь массивом из трех элементов, то можете получить ту же картину; хотя, повторюсь, подход принципиально неправильный :D

Уточняю - даёт корректный результат - 0 1 2
тогда как, если вытащить А В С из записи в переменные, в результате мы получим - 0 2 0 (последний 0 - "мусор", начальное состояние ячейки памяти, соответствующей переменной С).
Именно это я и нахожу забавным. Получается, память выделяется:
для отдельных переменных
Код: Выделить всё
А:_:В:_:С

а для элементов записи
Код: Выделить всё
Т.А:Т.В:Т.С

Неправильность подхода признаю.

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 12.10.2012 10:51:44
SSerge
К вашему первому коду, при ограничении цикла до тройки:
Код: Выделить всё
  writeln(@a=@(tmp^[0]),' ',@b=@(tmp^[2]),' ',@c=@(tmp^[4]));
> TRUE TRUE TRUE


Выравнивание в действии, однако :D

Re: Тип Currency. Подскажите, в чём прикол?

СообщениеДобавлено: 09.08.2013 11:55:05
naomika
ведь память за переменной С не выделена.
Подскажите, в чём я ошибаюсь?

Ответ заключен в самом вопросе :D