Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Чтение запределами буфера.


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

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 не фиксирует никакого выхода нигде....

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

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

Автор: 3315720 16.6.2012, 22:42
В строки по любому нужно встраивать \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 17.6.2012, 12:29
Еще немного пищи для ума...
Простой пример демонстрирующий происходящее.

Код

#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;
}
//---------------------------------------------------------------------------


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

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

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

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

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

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

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

Автор: 3315720 17.6.2012, 14:29
Цитата(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й байт....


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

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

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

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

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

Так и сделал...

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