Конвертировать 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);

Правда, этот код трудно поддерживать.

Другие вопросы по тегам