Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Чтение запределами буфера. Выход за пределы буфера и слипание строк 
:(
    Опции темы
3315720
  Дата 16.6.2012, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



Здравствуйте. Неожиданно возникла проблема. Есть структура примерно такого вида:
Код

struct MyStruct
{
    char String1[32];
    char String2[32];
    char String3[32];
};

Которую я записываю в файл.
Код

FileWrite(iFileHandle, Record, sizeof(MyStruct));


И затем считываю из файла
Код

FileRead(iFileHandle, Buffer, sizeof(MyStruct));


Так вот пока длина строк менее 32, все отлично. Но как только они достигают полного размера начинают слипаться. т.е. считываются соседние строки за пределом буфера. Получается примерно так:
String1[32] = строка1строка2строка3\x16
String2[32] = строка2строка3\x16
String3[32] = строка3\x16

Так вот вопрос как ограничить чтение из буфера без применения нуль-терминированной строки. Очень неохото вставлять 33й символ конца строки. Файл бинарный и считывается только моей прогой а не блокнотом. Лишние символы не нужны.
Вот еще этот символ \x16 прилепляется к последней строке. Он также находится за пределами буфера но CodeGuard не фиксирует никакого выхода нигде....

Это сообщение отредактировал(а) 3315720 - 16.6.2012, 22:48
PM MAIL   Вверх
artsb
Дата 16.6.2012, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2280
Регистрация: 17.7.2007
Где: центр Вселенной

Репутация: 39
Всего: 64



Я бы сделал так:
читал/писал структуру не целиком, а по отдельным параметрам. Причём, для строк, первый байт - это длина строки.
Так будет даже правильней, т.к. вы писали:
Цитата(3315720 @  16.6.2012,  21:27 Найти цитируемый пост)
Лишние символы не нужны.

Просто в вашем случае, получается так, что даже если строка имеет длину 5 символов, в файл всё равно запишется 32 (остальное будет мусор). Поэтому, ИМХО, мой способ самый верный в данной ситуации. Причём, при чтении структуры целиком могут возникнуть проблемы с выравниванием в разных средах разработки.

Это сообщение отредактировал(а) artsb - 16.6.2012, 21:49


--------------------
Чем отличается умный человек от мудрого?
Умный - выпутается из любой ситуации.
Мудрый - просто в неё не попадёт.
PM MAIL   Вверх
3315720
Дата 16.6.2012, 22:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



В строки по любому нужно встраивать \0, иначе без разницы. Длина строки в файле не критична, повторюсь файл не текстовый, а бинарный. Мне не нужно бить его на строки вставляя непечатные символы. Даже не читая и не записывая, а просто присвоив строкам значения длинной 32 к ней прилипает следующая строка. И такая конструкция
Код

AnsiString a = ojMyStruct->String1;

Присвоит строке а значение "строка1строка2строка3\x16". Проблема не в считывании/записи файла, а в считывании из оперативной памяти.

замена char[32] на AnsiString[32] решает проблему, но насколько это правильно и чем может грозить?
К тому же возникли ошибки при удалении экземпляров структур при char[32] четко срабатывал new и delete, а при AnsiString[32] - delete вызывает "access violation..." и "abnormal program termination" (аварийное завершение программы).

Это сообщение отредактировал(а) 3315720 - 16.6.2012, 22:52
PM MAIL   Вверх
3315720
Дата 17.6.2012, 12:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



Еще немного пищи для ума...
Простой пример демонстрирующий происходящее.

Код

#include <iostream>
#include <windows>

struct MyStruct
{
    char String1[7];
    char String2[7];
    char String3[7];
};
using namespace std;

//---------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);

    MyStruct *Struct = new MyStruct;
    strncpy(Struct->String1,(char*)"Строка1",7);
    cout << "Инициализация первой строки: " << endl;
    cout << "Значение первого буфера: " << Struct->String1 << endl;
    cout << "Значение второго буфера: " << Struct->String2 << endl;
    cout << "Значение третьего буфера: " << Struct->String3 << endl;
    cout << endl;
    strncpy(Struct->String2,(char*)"Строка2",7);
    cout << "Инициализация второй строки: " << endl;
    cout << "Значение первого буфера: " << Struct->String1 << endl;
    cout << "Значение второго буфера: " << Struct->String2 << endl;
    cout << "Значение третьего буфера: " << Struct->String3 << endl;
    cout << endl;
    strncpy(Struct->String3,(char*)"Строка3",7);
    cout << "Инициализация третьей строки: " << endl;
    cout << "Значение первого буфера: " << Struct->String1 << endl;
    cout << "Значение второго буфера: " << Struct->String2 << endl;
    cout << "Значение третьего буфера: " << Struct->String3 << endl;
    cout << endl;

    system("PAUSE");

    Struct->String1[6] = '\0';
    Struct->String2[6] = '\0';
    Struct->String3[6] = '\0';

    cout << endl;
    cout << "Значение буферов после вставки терминатора '\\0': " << endl;
    cout << "Значение первого буфера: " << Struct->String1 << endl;
    cout << "Значение второго буфера: " << Struct->String2 << endl;
    cout << "Значение третьего буфера: " << Struct->String3 << endl;
    cout << endl;

    system("PAUSE");

    delete Struct;
    return 0;
}
//---------------------------------------------------------------------------


PM MAIL   Вверх
artsb
Дата 17.6.2012, 13:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2280
Регистрация: 17.7.2007
Где: центр Вселенной

Репутация: 39
Всего: 64



Цитата(3315720 @  17.6.2012,  12:29 Найти цитируемый пост)
Еще немного пищи для ума...
Простой пример демонстрирующий происходящее.

Ну да, нулевой байт нужен. А как иначе определить длину строки. Кстати, в вашем примере последнем, ни в коем случае нельзя копировать строки таким образом:
Код

strncpy(Struct->String3,(char*)"Строка3",7)

Хотите вы или нет, но функция strncpy() добавит в конец нулевой символ. А значит вылезет за пределы доступной памяти и подпортит что-нибудь.
Если уж очень нужно, то копируйте с помощью memcpy()
Код

memcpy((void*)Struct->String3, (void*)"Строка3", 7);

Ну и "доставать" эту строку придётся с помощью memcpy(), т.к. все функции для работы со строками требуют, чтобы в конце был ноль.

Но я не вижу смысла так заморачиваться из-за одного несчастного нулевого байта в конце.

Это сообщение отредактировал(а) artsb - 17.6.2012, 13:04


--------------------
Чем отличается умный человек от мудрого?
Умный - выпутается из любой ситуации.
Мудрый - просто в неё не попадёт.
PM MAIL   Вверх
3315720
Дата 17.6.2012, 14:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



Цитата(artsb @ 17.6.2012,  13:03)
Кстати, в вашем примере последнем, ни в коем случае нельзя копировать строки таким образом:
Код

strncpy(Struct->String3,(char*)"Строка3",7)

Хотите вы или нет, но функция strncpy() добавит в конец нулевой символ. А значит вылезет за пределы доступной памяти и подпортит что-нибудь.

strncpy никуда не вылазит, самостоятельно ничего не пишет. Что и демонстрирует пример.
http://ru.wikipedia.org/wiki/Strncpy

Вы видимо спутали strncpy с strlcpy, последняя как раз таки следит за тем чтобы ноль обязательно был в конце. 
strcpy вообще ни зачем не следит и запросто выходит за пределы буфера.

Может есть вариант бинарного хранения символов не в char, а как-то еще? Меня вполне устроит какой-нибудь Buffer длинной 32 байта.

П.С. 
memcpy не решает проблему.
Видимо такова реализация char.
как вариант я вижу ручной перебор символов, что бы не вылазить за пределы при чтении или терминатор "\0". Первое это маразм. Прийдется всетаки вводить 33й байт....



Это сообщение отредактировал(а) 3315720 - 17.6.2012, 16:09
PM MAIL   Вверх
artsb
Дата 17.6.2012, 19:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2280
Регистрация: 17.7.2007
Где: центр Вселенной

Репутация: 39
Всего: 64



Цитата(3315720 @  17.6.2012,  14:29 Найти цитируемый пост)
Может есть вариант бинарного хранения символов не в char, а как-то еще? Меня вполне устроит какой-нибудь Buffer длинной 32 байта.

Так у вас и так буфер используется. Просто вы не хотите ставить нулевой символ в конце. Но все функции из стандартной библиотеки написаны с таким расчётом, что в конце должен быть нулевой байт. Так что, какой бы тип буфера вы не использовали, у вас не получится использовать ваши "строки" в стандартных функциях. Придётся писать что-то своё. Кстати, можно написать не структуру, а класс и перегрузить для него операции ввода/вывода. Но опять таки, тут возникает проблема - а что если длина строки будет меньше 32? Как вы будете определять длину строки, чтобы произвести с ней операции?

Конечно, можно сделать так:
При инициализации структуры/класса всегда все строковые переменные обнулять полностью. делать тоже самое при изменении строки.
Далее, при обращении к строке, делать проверку - если последний байт равен 0, то считать, что строка имеет длину меньше 32 символов, иначе - ровно 32. И уже во втором случае, будете работать со строкой через strncpy() smile и подобными функциями, где можно указывать длину.

Но лучше, ИМХО:
Цитата(3315720 @  17.6.2012,  14:29 Найти цитируемый пост)
Прийдется всетаки вводить 33й байт....



--------------------
Чем отличается умный человек от мудрого?
Умный - выпутается из любой ситуации.
Мудрый - просто в неё не попадёт.
PM MAIL   Вверх
3315720
Дата 18.6.2012, 11:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 59
Регистрация: 28.2.2008

Репутация: нет
Всего: нет



[QUOTE=artsb,17.6.2012,  19:38]
Цитата(3315720 @  17.6.2012,  14:29 Найти цитируемый пост)
Но лучше, ИМХО:
Цитата(3315720 @  17.6.2012,  14:29 Найти цитируемый пост)
Прийдется всетаки вводить 33й байт....

Так и сделал...
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++ Builder"
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по С++ Builder обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Настоятельно рекомендуем заглянуть в DRKB (Delphi Russian Knowledge Base) - крупнейший в рунете сборник материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C++ Builder | Следующая тема »


 




[ Время генерации скрипта: 0.0474 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.