Конвертировать struct в байтовый массив и сохранить в db. Чтение БД и получение байтового массива для воссоздания структуры в C
Привет всем, извините за вопрос, но я мог найти любое подходящее решение. Я работаю в файловой системе, где я сохраняю каждый файловый узел как пару ключ-значение в базе данных GDBM. У меня есть структура, которая имеет некоторые атрибуты, и я преобразовываю это в байтовый массив
struct mystruct:
typedef struct nold{
char* name;
char* surname;
int age;
}mystruct;
Я конвертирую его как следующий в байтовый массив
dead.name="john";
dead.surname="doe";
dead.age=22;
//copy bytes of the our struct
char buffer[sizeof(dead)];
memcpy(buffer, &dead, sizeof(dead));
для хранения в базе данных мы используем базовую структуру, которая выглядит следующим образом:
typedef struct {
char *dptr;
int dsize;
} datum
Я заполняю базовую структуру следующим образом:
//create a key datum
char* k="file.txt";
key.dptr=k;
key.dsize=strlen(k)+1;
//create a value datum here I assign bytes
value.dptr=buffer;
value.dsize = sizeof(dead);
Теперь я храню в GDBM как пару ключ-значение
Затем в другом файле я читаю данные, которые храню, и пытаюсь преобразовать их обратно в структуру.
datum result;
//read
result=gdbm_fetch(file,key);
char* bytes=result.dptr;
mystruct* reborn;
reborn=(mystruct*)bytes;
//print from our new struct
printf("%s\n",reborn->name);
printf("%s\n",reborn->surname);
printf("%d\n",reborn->age);
Он печатает следующее:
E���D$�$ˈ�k����E��
$�$ˈ�k����E��
22
Ему удается вернуть структуру, но данные char * потеряны. Только целочисленные данные покрыты. Есть идеи, почему это происходит? Как это решить? Это не может быть решено путем хранения байтового массива, как насчет преобразования байтового массива в hex или base64 и сохранения его как такового.
Я действительно борюсь с этим. Спасибо заранее.
3 ответа
Указатель внутри структуры - это просто указатель на массив символов, а не сам массив символов:
typedef struct nold{
char* name;
char* surname;
int age;
}mystruct;
mystruct s;
s.name = "Salam";
Это зарезервирует пространство памяти для строки "Салам", поместит строку "Салам" и вернет указатель на s.name.
теперь вы копируете всю структуру во что-то другое, вы копируете со структурного адреса с помощью sizeof (mystruct), который на самом деле не содержит строку "Salam", он просто содержит указатель на "Salam".
Если вы хотите сделать это, вы должны предварительно выделить место для имени:
#define MAX_NAME_LEN 50
typedef struct nold{
char name[MAX_NAME_LEN];
char surname[MAX_NAME_LEN];
int age;
}mystruct;
mystruct s;
strcpy(s.name, "Salam");
теперь memcpy будет работать
mystruct d;
memcpy(&d, &s, sizeof(mystruct);
Значения указателя имеют смысл только в контексте определенного запуска конкретной программы. Их можно записать и прочитать обратно, но важно помнить, что указатель обозначает адрес, а не (непосредственно) данные, находящиеся по этому адресу, если этот адрес действительно действителен для программы, обращающейся к нему. Вы не передаете указанные данные между процессами, передавая указатель.
Возможно, вы могли бы решить проблему, поместив данные, которые вы хотите передать, непосредственно в структуру:
typedef struct {
char data[MAX_DATA_SIZE];
int dsize;
} datum;
Недостаток этого подхода заключается в том, что вам необходимо установить фиксированную верхнюю границу для размера данных, но во многих случаях это не проблема. Если вам нужны неограниченные данные, то вам нужен совершенно другой подход.
Проблема в том, что имя и фамилия являются указателями на строковые литералы. Таким образом, ваша структура содержит не саму строку, а указатели на раздел данных, в котором находятся литералы. Как только вы копируете это в свой буфер, вы фактически просто копируете значения указателя. После реконструкции эти значения указателя теперь будут указывать на произвольную область, которая не содержит ваших строк.
РЕДАКТИРОВАТЬ: Вот функция, которая передает данные в буфер:
size_t copyMyStructToBuffer(mystruct *aPerson, char **buffer) {
size_t nameLen = strlen(aPerson->name);
size_t surnameLen = strlen(aPerson->surname);
size_t structLen = nameLen + 1 + surnameLen + 1 + sizeof(int);
*buffer = malloc(structLen);
memcpy(*buffer, aPerson->name, nameLen + 1); // w/ terminator
memcpy((*buffer)[nameLen+1], aPerson->surname, surnameLen + 1); // w/ terminator
memcpy((*buffer)[nameLen+1+surnameLen+1], &aPerson->age, sizeof(int));
return structLen;
}
И как вы можете использовать это:
mystruct dead;
dead.name = "John";
dead.surname = "Doe";
dead.age = 22;
char *buff;
size_t buffLen;
buffLen = copyMyStructToBuffer(&dead, &buff);
// use buff here
free(buff);
Правда, этот код трудно поддерживать.