DBM ライブラリとして GDBM または Kyoto Cabinet を使用できるプログラムがあります。この 2 つの違いを抽象化する関数をいくつか作成し、データベース ファイルの代わりに void ポインターを渡します ( GDBM_FILE
GDBMKCDB *
の場合と 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 に格納しようとしないと、突然機能するのはなぜですか?