Войти

Показать полную графическую версию : запись структур в VС++


Drey
14-02-2004, 20:11
Проблема в следующем, есть bmp, gif или любой другой файл и мне нужно его прочитать. Для начала надо прочитать заголовок, который представлен в виде структуры. Но он читается не правильно, из-за “word alignment”, т.е. то, что меньше 4байт увеличивается до 4, что сами понимаете, в данном случае критично. Как убрать это выравнивание? (программа – Win32 Console Application)

Prisoner
16-02-2004, 11:14
Возможно поможет директива #pragma pack n или опция компилятора -a.

Исправлено: Prisoner, 11:15 16-02-2004

Drey
16-02-2004, 18:05
А можно немного поподробнее? как японяла надо примерно так:
#pragma pack(1) //работает почему-то только с 1, и то не совсем
struct {
char a;
long int b;
int c;
} info;
#pragma pack
а что означает 1 в pack(1) и как правильно записать дерективу компилятору "-a"

vasketsov
16-02-2004, 19:57
1) 1 - выравнивание в байтах
2) push забыла :)

Добавлено:

То есть, должно быть

#pragma pack(push, 1)

КОД

#pragma pack(pop)

Drey
21-02-2004, 18:48
Еще один вопросик, правда немного не на тему, но все же.... в общем еод должен считывать переменную, модифицировать ее и еласть на место, но этого не делает, в чем проблема?

f = fopen(f_name, "rb+");
unsigned char cur;
for (int i=0; i<350; i++) {
cur = fgetc(f);
fseek(f, -1, SEEK_CUR);
cur &= 0xF0;
fputc(cur, f);
}
fclose(f);

hasherfrog
26-02-2004, 11:05
Drey
Код в общем-то правильный, но при длине файла меньше 350 байт будет работать некорректно. Вообще нужна куча проверок. Открылся ли файл, перевелся ли указатель... Иначе код небезопасный.

Исправлено: hasherfrog, 11:06 26-02-2004

Drey
26-02-2004, 19:38
а откуда берутся эти 350 байт... в смысле с чем это связано и как выполнять проверки?

bilytur
27-02-2004, 02:52
1 Создай буфер char buf[350];
2 считай туда свой фал
3 Изменяй в буфере что ты хочешь
4 Закатай буфер обратно в файл

hasherfrog
27-02-2004, 09:36
bilytur
Чтение блоком  - первое, что приходит в голову, :) Другое дело, что это не всегда то, что нужно. Если у файла размер не известен заранее, а сам файл двоичный, fgetc и fputc имеют право на жизнь. Ведь по сути это fread/fwrite(&ch,1,1, F). Поскольку размер файла скорее всего небольшой (но может быть и большим), память выделять статично - вроде как накладно, динамически - муторно. Вот тут и нужен "несолидный" fputc
Drey
Я что-то не понял, вопрос в том, где ошибка, или в том, что это такое и что оно вообще делает? :(
Я серьезно отношусь к вопросам. Если Вам нечего делать, делайте это в другом месте...

Drey
27-02-2004, 16:37
bilytur, да, вообще так конечно можно сделать, но это не есть хорошо, потому что, во-первых, считывать нужно несколько блоков, во-вторых, размер блоков заранее неизвестен, в-третьих, не факт, что считывать придется из одного файла - в смысле количество файлов может быть и больше одного, именно поэтому я делаю потоковую обработку, т.е. чтение-запись.
Так, а теперь про код - он вроде правильный и работать должен правильно, но почему-то не работает, я никак не пойму почему. Прикол в том, что если переписать то же самое в виде:
f = fopen(f_name, "rb+");
unsigned char cur;
for (int i=0; i<350; i++) {
fseek(f, i, SEEK_SET);
cur = fgetc(f);
fseek(f, i, SEEK_SET);
cur &= 0xF0;
fputc(cur, f);
}
fclose(f);
все работать будет замечательно. Вот и в чем проблема того кода?

Добавлено:

hasherfrog, что он делает я знаю - сам писал... но на всякий случай уточню - считывает символ (указатель смещается на следующий), затем возвращаем указатель и записываем на его место измененный символ, т.е., грубо говоря, просто заменяем символ.

bilytur
28-02-2004, 02:41
Я как-то писал утилитку(в учебных целях) которая табуляцию заменяеи пробелами. С параметром 2, 4 или 8 пробела на табуляцию. Удобно для некоторых исходников. Но не в этом дело.
Первый вариант был что-то вроде вашего (fputc fgetc)
Второй я читал блоками, размером 0x8000
Так вот второй вариант раз в 100 быстрее оказался.
На больших файлах весьма заметно.
Да и диск жалко дергать взад вперед. (Еслиб небыло кеширования то он быстро бы загнуля :))
А размер файла не имеет значения. Хоть Гигабайт.

Drey
28-02-2004, 16:59
ладно, это я переделаю на чтение блоками, а какого размера надо блоки брать?

bilytur
01-03-2004, 02:17
Drey
Размер ровное (двоичное) какое-нибудь число, лучше если кратное размеру кластера минимально это 512 байт.
А так исходи из собственных ресурсов 32K (0x8000) например неплохо.

Ну и раз пошла такая пьянка, еще пару слов.
По поводу первого твоего вопроса
#pragma pack(1) - Это конечно хорошо, но не портабельно.
Если ты делаешь программку для одной платформы с интеловсим процем, то так конечно проще всего.
Но если твой код будет компилироваться под другие платформы (процесоры), то там возможны засады.
Интеловские процы пишут младший байт сначала, на других системах возможно все будет наоборот, и тогда твои (bmp, gif) будут не валидными.

Drey
01-03-2004, 15:02
bilytur, да, я читал на MSDN про эту фишку и там говориться, что лучше использовать ключевые слова, только есть одно "но" - они не воспринимаются компилятором, т.е. он говорит, что данная версия это не поддерживает.
Ладно, если #pragma pack(1) это не то, то что же тогда то? как это исправить?
Причем надо читать отдельно заголовок и отдельно содержимое - это приниципиально.


Исправлено: Prisoner, 0:24 2-03-2004

ivank
01-03-2004, 16:54
Читай/пиши побайтово (можно, что бы побыстрей, в буффер весь заголвок, а из него побайтово "расфасовывать"). Тогда не будет проблем с переносимостью между big/little-endian арзитектурами, с выравниванием проблем тоже не будет. По-моему, все портабельные программы работающие с бинарными данными делают именно так.

bilytur
02-03-2004, 02:23
Ну ivank Как всегда прав.
Вот небольшой пример.
Две функции для чтения и записи 32-разрядного целого.

__int32 ReadINT(void* pS)
{
 unsigned char* p = (unsigned char*)pS;
 __int32 res = p[3];
 res = (res<<8) | p[2];
 res = (res<<8) | p[1];
 res = (res<<8) | p[0];
 return  res;
}

void WriteINT(__int32 V, void* pD)
{
 unsigned char* p = (unsigned char*)pD;
 *p++ = V; V>>=8;
 *p++ = V; V>>=8;
 *p++ = V; V>>=8;
 *p   = V;
}

Как использовать. Пример:
Например по смещению 38 надо считать число, прибавить 5 и записать назад.

char Buf[SIZE]; // Это твой буфер

int a = ReadINT(&Buf[38]);
a += 5;
WriteINT(a, &Buf[38]);

А вообще если тебе действительно не нужна портабельность то не заморачивайся с этим. :)

Drey
02-03-2004, 17:17
прикольный код... правда я не понял, что он делает??? если p - указательна char, то после одного смещения на 8 бит он должен тихо сканчаться...
и, как я понял из вышесакзанного надо правильнее
char Buf[512];
int a = ReadINT(&Buf)

bilytur
03-03-2004, 02:27
Код правильный, даже синтаксических ошибок нет.
программа – Win32 Console Application
Если пишешь под виндоус то используй #pragma pack(push, 1)
И живи спокойно. Портабельность тебе судя по всему не грозит. :)




© OSzone.net 2001-2012