Форум программистов, компьютерный форум CyberForum.ru

Байтовое хранилище данных - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 9, средняя оценка - 4.67
3BEPb_TyTa
 Аватар для 3BEPb_TyTa
18 / 18 / 1
Регистрация: 18.05.2010
Сообщений: 83
24.04.2012, 22:55     Байтовое хранилище данных #1
Привет, народ. Есть задача - в руки мне даётся структура, предназначенная для хранения байтового массива. Заполнять массив можно однотипными значениями какого-угодно типа, только в начале нужно указать сколько в байтах будет занимать один элемент в памяти. Вот примерно как это выглядит:
Описание структуры:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef CSTASH
#define CSTASH
 
struct CStash{
    int size; // Размер каждого элемента
    int amount; // Кол-во элементов
    int next; // Следующий пустой элемент
    unsigned char* storage; // указатель на "байтовый" массив
 
    void initialize(int size); // Задаёт размер елемента хранилища в байтах
    int add(void* element); // Добавление элемента
    void* fetch(int index); // Возвращает указатель на элемент с указанным индексом
    /* Расширяет байтовый массив на кол-во байтов, требуемое
    для добавления "increase" элементов*/
    void inflate(int increase); //Расширение массива для записи нового элемента
    int count();
    void printBinary();
};
 
#endif
Методы:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "CLib.h"
#include <iostream>
using namespace std;
 
const int increment = 100;
 
//Задаёт начальные значения полей(вместо конструктора)
void CStash::initialize(int sz){
    size = sz;
    amount = 0;
    storage = 0;
    next = 0;
}
 
//Добавление элемента
int CStash::add(void* element){
    inflate(1);
    unsigned char* ptr = reinterpret_cast<unsigned char*>(element);
    for(int i = amount * size - size; i < amount * size; i++){
        storage[i] = *ptr;
        ptr++;
    }
    return amount;
}
 
// Расширение массива
void CStash::inflate(int increase){
    int newAmount = amount + increase;
    int newBytes = newAmount * size;
    int oldBytes = amount * size;
    unsigned char* b = new unsigned char[newBytes];
    for(int i = 0; i < oldBytes; i++){
        b[i] = storage[i];
    }
    delete [] (storage);
    storage = b;
    amount = newAmount;
}
 
int CStash::count(){
    return next;
}
 
// Возвращает указатель на элемент массива с номером(не индексом!) "index"
void* CStash::fetch(int number){
    if(number >= 0 && number <= amount-1)
        return &(storage[number * size]);
    else
        return 0;
}
Дальше требуется используя эту структуру наполнить её n-ым кол-вом string-овых объектов. Я беру их cpp-файла
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include "CLib.h"
#include "Stack.h"
#include <assert.h>
 
using namespace std;
 
void f(char* str){
    cout << strlen(str) << endl;    
    return;
}
 
int main(int argc, char* argv[]){
    CStash* stash = new CStash;
    ifstream in("main.cpp");
    stash->initialize(80);
    string str;
    for(double i = 0; i < 10; i ++){
        getline(in, str);
        stash->add(&str);
    }
    int i = 0;
    while(stash->fetch(i)){
        cout << *reinterpret_cast<string*>(stash->fetch(i)) << endl;
        i++;
    }
    system("pause");
    return 0;
}
И вот тут начинаются проблемы. Ибо если для простых типов(int, double...) всё работает хорошо. То для string, когда я пытаюсь потом их извлечь и вывести на экран - мне выводит пять(я беру n равный пяти) одинаковых строчек. Я дебажил как умел, ничего не понятно. Помогите, люди добрые
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
24.04.2012, 22:55     Байтовое хранилище данных
Посмотрите здесь:

ввод структурированных данных в файл и вывод данных из этого файла на экран (сделать хра-нилище данных в файле). C++
C++ Использование псевдопеременных: bd (3,5) = s, где bd - база данных, s - элемент базы данных, тогда с 3 по 5 элементы базы данных заменить на элемент s.
вывести 1 байтовое целое знаковое число в 2-ом коде C++
Программа, которая позволяет ввести неограниченное количество значений и сохранить их в массив, размещенный в свободном хранилище C++
C++ Организовать добавление данных в файл, просмотр, поиск и очистку данных
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
24.04.2012, 23:49     Байтовое хранилище данных #2
3BEPb_TyTa, std::string - это non-POD-тип. Если не вдаваться в подробности, это значит, что вы не можете так просто копировать его память.

std::string (или, строго говоря, std::basic_string<char>), имеет внутренний указатель на массив этих самых char. При создании объекта этого типа, этот массив выделяется и его адрес записывается в этот указатель. При увеличении (изменении?) длины хранимой строки, массив перевыделяется, и указатель обновляется. Наконец, при уничтожении объекта, память этого массива освобождается. Не забывайте, что многие алгоритмы работают с копиями объектов, вызывая конструктор копирования.

Я глубоко подозреваю, что функция std::getline() в своём теле вызывает std::string::operator=, что приводит к перевыделению памяти под внутренний буфер. Хотя это уже детали конкретной реализации.

Таким образом, грубое копирование объекта non-POD типа, например, через memcpy, ведёт к неопределённому поведению.

Цитата Сообщение от http://en.cppreference.com/w/cpp/string/byte/memcpy
If the objects are not trivially copyable (scalars, arrays, C-compatible structs), the behavior is undefined.

Не по теме:

Единственное, что гарантируется (если мне не изменяет склероз) - это то, что CharTraits (char) по стандарту должен быть POD-типом, но это в данном случае бесполезно.



Указанный вами способ теоретически может работать с POD-типами, что вы видите на примере int, double и прочих. Хотя... Ваша программа точно так же обвалится, если сначала сделать копию C-структуры с указателем на строку, затем освободить эту строку, а затем вывести кусок памяти по указателю в копии.

Именно по-этому стоит пользоваться объектами с их конструкторами, деструкторами и прочими вкусностями.

В качестве способа хранения строки могу предложить хранение именно содержимого этого массива (доступен через std::string::c_str()).
3BEPb_TyTa
 Аватар для 3BEPb_TyTa
18 / 18 / 1
Регистрация: 18.05.2010
Сообщений: 83
25.04.2012, 10:18  [ТС]     Байтовое хранилище данных #3
talis, поменял в последнем фрагменте кода стринговый объект на динамический создаваемый.
C++
1
2
3
4
5
6
string* str;
    for(double i = 0; i < 10; i ++){
        str = new string;
        getline(in, *str);
        stash->add(str);
    }
И всё заработало Спасибо за объяснение. Я так понимаю, проблема была в том что при создании string-объекта в стеке, получалось что строка(т.к. память под неё выделяется динамически) оказывалась в другой области памяти. И соответсвенно операции с указателем на этот объект выполнялись непонятно как. Я правильно понял? -)
talis
 Аватар для talis
789 / 541 / 37
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
25.04.2012, 19:35     Байтовое хранилище данных #4
Цитата Сообщение от 3BEPb_TyTa Посмотреть сообщение
Я так понимаю, проблема была в том что при создании string-объекта в стеке, получалось что строка(т.к. память под неё выделяется динамически) оказывалась в другой области памяти. И соответсвенно операции с указателем на этот объект выполнялись непонятно как. Я правильно понял? -)
Не совсем.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
string str; // создаём string
for( double i = 0; i < 10; i++ )  // зачем double?! =-O int или unsigned!
{
        getline(in, str); // записываем в str строку. в этом месте всё
                          // предыдущее содержимое удаляется.
                          // тут может произойти перевыделение внутреннего
                          // буфера с освобождением предыдущего
 
        stash->add(&str); // копируем память str, включая указатель
                          // на внутренний буфер;
                          // в следующей итерации этот же самый str
                          // будет переписан, и этот внутренний буфер
                          // может оказаться перевыделенным,
                          // а скопированный указатель-то по-прежнему
                          // будет указывать на старый (уже несуществующий)
                          // буфер
}
Не стоит так работать с non-POD классами.

Добавлено через 1 минуту
Цитата Сообщение от 3BEPb_TyTa Посмотреть сообщение
C++
1
2
3
4
5
6
string* str;
    for(double i = 0; i < 10; i ++){
        str = new string;
        getline(in, *str);
        stash->add(str);
    }
Угу. Динамически выделяете память. А освобождать кто-то где-то будет?
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
25.04.2012, 19:47     Байтовое хранилище данных #5
Это случайно не пример из книжки Брюса Эккеля?
3BEPb_TyTa
 Аватар для 3BEPb_TyTa
18 / 18 / 1
Регистрация: 18.05.2010
Сообщений: 83
18.05.2012, 00:57  [ТС]     Байтовое хранилище данных #6
Kuzia domovenok, да, из её самой ) talis, спасибо за объяснения.
Yandex
Объявления
18.05.2012, 00:57     Байтовое хранилище данных
Ответ Создать тему
Опции темы

Текущее время: 21:23. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru