Объект GDBM, переданный через указатель void, потерян / поврежден
У меня есть программа, которая может использовать GDBM или Kyoto Cabinet в качестве библиотеки DBM. Я написал несколько функций, чтобы абстрагироваться от разницы между ними, и я передаю пустые указатели вместо файла базы данных (GDBM_FILE
в случае GDBM и KCDB *
в случае киотского кабинета). Все с KC работает нормально, однако, когда я пытаюсь использовать бэкэнд GDBM, база данных как-то "теряется" при передаче ее различным функциям. Когда я пытаюсь привести указатель и разыменовать его, а затем передать его одной из функций GDBM, он segfaults и в отладчике жалуется на отсутствие файла db.
Вот некоторый код, который может воспроизвести проблему:
#include <gdbm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
void *
dbopen (void)
{
printf ("opening\n");
GDBM_FILE database = gdbm_open ("test.db", 512, GDBM_WRCREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, NULL);
if (!database)
{
printf ("cannot open database\n");
return NULL;
}
void *db = &database;
return db;
}
void
dbclose (void *db, void *foo)
{
printf ("%d\n", *(int *)foo);
GDBM_FILE database = *(GDBM_FILE *)db;
if (!database)
{
printf ("database lost\n");
return;
}
printf ("closing\n");
gdbm_close (database);
return;
}
void
fun (void *db, void *foo)
{
GDBM_FILE database = *(GDBM_FILE *)db;
datum key, value;
int bar = *(int *)foo; /* and yet, if I remove this line and the next */
printf ("%d\n", bar); /* one, it works! */
printf ("%d\n", *(int *)foo);
if (!database)
printf ("no db?\n");
key.dptr = "baz";
key.dsize = 4;
value.dptr = "quux";
value.dsize = 4;
printf ("storing\n");
gdbm_store (database, key, value, GDBM_REPLACE);
printf ("all done\n");
return;
}
int
main (void)
{
int foo = 5;
void *dbp = dbopen ();
void *foop = &foo;
fun (dbp, foop);
dbclose (dbp, foop);
}
Когда я запускаю этот код, он вызывает ошибку "файл не найден" при вызове gdbm_close()
, Как отмечают комментарии, если я не сохраню явно другой указатель void на int, тогда программа будет работать нормально.
В моей реальной программе, он теряется, когда я звоню gdbm_store()
и это единственный пустой указатель, который я использую (в этой тестовой программе foo
указатель должен был быть просто проверкой работоспособности).
Я уверен, что во капризах распределения памяти в C есть что-то, что я забыл или не понимаю. Почему указатель void, ссылающийся на базу данных GDBM, теряется / повреждается, а указатель void, ссылающийся на int, - нет? Почему, когда я не пытаюсь сохранить разыменованный указатель void foo
для int, это вдруг работает?
1 ответ
Ваша проблема заключается в получении адреса указателя, возвращенного из gdbm_open
вместо этого, если его значение. Запутанная часть происходит от определения GDBM_FILE
- это указатель на структуру:
typedef struct {int dummy[10];} *GDBM_FILE;
Вы получаете его адрес, то есть местоположение указателя, а не значение указателя:
void *db = &database;
Проблема может быть решена путем замены строк преобразования на:
void *db = (void *)database;
а также
GDBM_FILE database = (GDBM_FILE)db;