Объект 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;
Другие вопросы по тегам