1

DBM ライブラリとして GDBM または Kyoto Cabinet を使用できるプログラムがあります。この 2 つの違いを抽象化する関数をいくつか作成し、データベース ファイルの代わりに void ポインターを渡します ( GDBM_FILEGDBMKCDB *の場合と Kyoto Cabinet の場合)。KC ではすべて正常に動作しますが、GDBM バックエンドを使用しようとすると、さまざまな関数に渡すときにデータベースが何らかの形で「失われ」ます。ポインターをキャストして逆参照し、それをGDBM関数の1つに渡そうとすると、セグメンテーション違反が発生し、デバッガーで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()使用している唯一の void ポインターです (このテスト プログラムでは、fooポインターはサニティ チェックのはずでした)。

C でのメモリ割り当ての気まぐれには、私が忘れている、または理解していない何かがあると確信しています。int を参照する void ポインターが失われないのに、GDBM データベースを参照する void ポインターが失われる/破損するのはなぜですか? 逆参照された void ポインターfooを int に格納しようとしないと、突然機能するのはなぜですか?

4

1 に答える 1