Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Работа с типами данных


Автор: unreg 29.11.2004, 03:55
Столкнулся вот с такой интересной штукой: нужно читать данные из файла, у меня есть описание формата этого файла. Первая структура которая в него пишется выглядит примерно так:

имя тип длина
----------------------------
time time_t 4 байта
Vmax int 2 байта
Smax int 2 байта
FR float 4 байта
(типы данных из Си)

по этому описанию я создал свою структуру:

TStruct = record
time: cardinal;
Vmax:word;
Smax:word;
FR: single; (потому, что этот тип занимает 4 байта и представляет число с плавающей точкой)

далее через TFileStream считываю эту структуру. Первые три поля заполняются правильными значениями, а последнее совсем не тем чем надо. Тогда вместо Single ставлю array [0..3] of byte и опять считываю. далее, значение поля FR преобразую так: AValue:=Single(Struct.FR) и получаю правильное значение. Как это объяснить? Почему я не могу сразу считать в переменную типа Single и мне нужно действовать через массив?



Автор: ~FoX~ 29.11.2004, 09:57
Single весит 2 байта.
Используй Real.

Добавлено @ 09:57
Заранее извеняюсь. мог и напутать

Автор: Guest 29.11.2004, 10:52
Цитата
Заранее извеняюсь. мог и напутать

Что, собственно, и сделал. :-)

Автор: p0s0l 29.11.2004, 14:20
Попробуй писать не record, а packed record...

ЗЫ: позже, посмотрев еще раз вопрос, понял, что дело не в выравнивании... smile
Тут и так всё выравнено...

Автор: _hunter 30.11.2004, 13:21
Цитата
а последнее совсем не тем чем надо

ну и показал бы чем оно заполняется ( желательно в bin ) и чем должно

Автор: dm9 30.11.2004, 14:19
Извини, что немного не в тему, но, кажется, к сишному типу int будет ближе smallint, а не word smile Если не unsigned, конечно.

Автор: _hunter 30.11.2004, 14:24
с какой это радости ( или под какую ось/компилятор )?
из справки:
Цитата

Delphi Size/Values C++ implementation
SmallInt 16-bit integer short
Integer 32-bit integer int

Автор: dm9 30.11.2004, 14:55
_hunter, конечно, могу что-то не понимать, но сказано было, что сишный int занимает 2 байта:

Цитата(unreg @ 29.11.2004, 03:55)
Vmax int 2 байта
Smax int 2 байта


Я уточнил, что если это так, и int этот не unsigned, то правильнее использовать SmallInt, а не Word.

А под какой компилятор? Ну, под Borland C++ 3.1, например... Под ДОС. 32-разрядные версии уже будут использовать 4-байтный int (опять-таки, насколько я знаю), там надо брать Integer или LongWord в случае беззнакового числа.

Автор: unreg 1.12.2004, 08:02
Я выбрал word потому, что отрицательных значений эти параметры не могут принимать физически. Так же они не могут принимать значения выше верхнего предела SmallInt. Разницы для меня в данном случае нет. Word - писать короче. Да и нет с ними проблемы.
Что возвращается:
если поставить Single, то 2,07446823360182E-40 (неправильно).
Если array [0..3] of byte, то 50,0126495361328 (правильно). B двоичном сейчас нет возможности показать.

Автор: _hunter 1.12.2004, 12:34
а хоть в hex ( к нему и приводить проще и видно лучше чем так )

Автор: unreg 1.12.2004, 14:35
Че-то, вообще, бред какой-то...

Вот структура:

TStruct = record
time: cardinal;
Vmax:word;
Smax:word;
FR: array [0..3] of byte;

Завел еще массив a:array [0..3] of byte;

Прочитал структуру и сделал Move(Struct.FR,a,4);
массив заполнился следующим: 60 0 72 66. (правильно)

Изменил описание FR на Single;

Прочитал структуру и сделал Move(Struct.FR,a,4);
массив заполнился следующим: 72 66 2 0. (не правильно)

Не смотря на мои эксперименты, остальные поля заполнялись верно.

А теперь самое интересное:

Изменил описание массива а: var a:array [0..5] of byte;
В структуре, поле FR описал точнно так же.
Прочитал структуру и сделал Move(Struct.FR,a,6);
массив заполнился следующим: 60 0 72 66 2 0 (дурдом на выезде) Да, еще забыл сказать, что эта струтктура в файле не единственная, он большой и там много всего.

Тем не менее:
Читаю по одному смещению. Количество байт строго определено. Что за кака?



Автор: <Spawn> 1.12.2004, 14:54
Покажи как ты читаешь данные в структуру.

Я толька что проверил вот этот вариант:

Цитата
TStruct = record
time: cardinal;
Vmax:word;
Smax:word;
FR: array [0..3] of byte;

Завел еще массив a:array [0..3] of byte;

Прочитал структуру и сделал Move(Struct.FR,a,4);
массив заполнился следующим: 60 0 72 66. (правильно)

Изменил описание FR на Single;

Прочитал структуру и сделал Move(Struct.FR,a,4);
массив заполнился следующим: 72 66 2 0. (не правильно)


И у меня все работает нормально:

Код

TStruct = record
 time: cardinal;
 Vmax:word;
 Smax:word;
 FR: Single;
end;

var
 Form1: TForm1;
 struct: TStruct;
 a: array[0..3] of byte;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 Move(Struct.FR, a, 4);
 ShowMessage(Format('%d %d %d %d', [a[3], a[2], a[1], a[0]]))
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 LongRec(Struct.FR).Bytes[0] := 66;
 LongRec(Struct.FR).Bytes[1] := 72;
 LongRec(Struct.FR).Bytes[2] := 0;
 LongRec(Struct.FR).Bytes[3] := 60;
end;


Отсюда напрашивается один вывод - ты как то не верно читаешь данные в TStruct.FR

Автор: p0s0l 1.12.2004, 17:54
Судя по описанию - всё-таки возможно проблема в выравнивании...
Только непонятно, откуда она берётся - ведь структура уже выравнена. Если бы вместо Cardinal был Word, то да, была бы 100% такая проблема, если в опциях компилятора выставлено выравнивание 4 или 8 байт...

Единственное предположение, что ты где-то до описания этой структуры или в модулях, прописанных в Uses сделал описание кардинала таким:
Код
type cardinal = word;
Поэтому, если используешь массив байтов, то массив не выравнивается, т.к. байты не подлежат выравниванию.
Если делаешь Single - то он выравнивается по границе 4 байт, т.е. до FR вставятся два левых ненужных байта...

Но опять же если бы ты переопределил тип Cardinal, то первые три поля структуры заполнились бы не теми данными (ведь time должен быть 4 байта)...

Чтобы не гадать, скажи, что выдаёт у тебя SizeOf(TStruct) в случаях, если FR : array[0..3] of byte и FR : Single;

Также, всё-таки, на всякий случай (чем черт не шутит ?), напиши так:
Код
TStruct = packed record
time: cardinal;
Vmax:word;
Smax:word;
FR: single;

Или в опциях компилятора выстави Record Field Alignment в 1, или напиши вначале {$A1}

Также вместо массива FR : array [0..3] of byte попробуй написать FR : DWORD или FR : Integer, и cделать Move (как ты уже делал) - что будет в таком случае ?

Либо же проблема со чтением, как предположил <Spawn>...

Автор: unreg 6.12.2004, 05:30
Извиняюсь, что долго не отвечал. Возможности не было.
PoSol, действительно, размер структуры меняется. Полностью моя структура выглядит так:
Код

type
  PMainStruct = ^TMainStruct;
  TMainStruct = record
  time:integer;
  VMax:word;
  SMax:word;
  OK:word;
  NPeriod:word;
  nMaxV:word;
  imaxV:word;
  nmaxS:word;
  imaxS:word;
  Na:word;
  Fr1:word;
  Reserv:word;
  fr:Single;//array [0..3] of byte;
  PREJ:word;
  PREJ2:word;
  DREJ:word;
end;


В случае с Single: SizeOf(TMainStruct) = 40,
в случае с array [0..3] of byte: SizeOf(TMainStruct) = 36.

Читаю структуру из файла:

var F:TFileStream;
//читаю по нулевому смещению, структура находится в начале файла;
F.Read(MainStruct,SizeOf(TMainStruct));

Видимо, дело действительно в выравнивании. Где в компиляторе находятся эти опции? И, если возможно, по-подробнее про выравнивание расскажите. Если я правильно понял, то под Single выделяется 8 байт как на Real, но значение имеют только 4?


Автор: <Spawn> 6.12.2004, 05:45
Сделай packed record, как тебе уже сказал p0s0l, и будет у тебя структура занимать 36 байт.

Код

TMainStruct = packed record
...

Автор: unreg 8.12.2004, 04:10
да я в курсе как делается packed record. smile Я так делал, но чисто механически. Мне саму суть выравнивания кто-нибудь расскажет? В каких случаях применять его? Или ссылку, где почитать можно?

Автор: p0s0l 11.12.2004, 16:26
Цитата
В каких случаях применять его?
Когда тебе нужно, тогда и применяй smile
В твоём случае, С++ прога не выравнивает поля структуры - значит и тебе не нужно выравнивать...

Цитата(unreg @ 6.12.2004, 05:30)
Где в компиляторе находятся эти опции?
Project -> Options -> Compiler -> Field Alignment

Цитата(unreg @ 6.12.2004, 05:30)
Если я правильно понял, то под Single выделяется 8 байт как на Real, но значение имеют только 4?

Не совсем. Во-первых, всего 6 байт. Первые 2 байта - это "левые" байты, для выравнивания поля fr... Потом уже идёт 4 байта Singl'а... Почему тогда 40, а не 38 ? Потому что в конце структуры идут еще 2 выравнивающих байта (чтобы не только поля в структуре были выравнены, но и сами структуры)...

Автор: unreg 15.12.2004, 04:44
Спасибо. Буду пользоваться, как ты и посоветовал. smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)