2

strcatとセグメンテーション違反に少し問題があります。エラーは次のとおりです。

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000000
0x00007fff82049f1f in __strcat_chk ()
(gdb) where
#0  0x00007fff82049f1f in __strcat_chk ()
#1  0x0000000100000adf in bloom_operation (bloom=0x100100080, item=0x100000e11 "hello world", operation=1) at bloom_filter.c:81
#2  0x0000000100000c0e in bloom_insert (bloom=0x100100080, to_insert=0x100000e11 "hello world") at bloom_filter.c:99
#3  0x0000000100000ce5 in main () at test.c:6

Bloom_operationは次のとおりです。

int bloom_operation(bloom_filter_t *bloom, const char *item, int operation)
{
    int i;    

    for(i = 0; i < bloom->number_of_hash_salts; i++)
    {
        char temp[sizeof(item) + sizeof(bloom->hash_salts[i]) + 2];
        strcat(temp, item);
        strcat(temp, *bloom->hash_salts[i]);

        switch(operation)
        {
            case BLOOM_INSERT:
                bloom->data[hash(temp) % bloom->buckets] = 1;
                break;
            case BLOOM_EXISTS:
                if(!bloom->data[hash(temp) % bloom->buckets]) return 0;
                break;
        }    
    }

    return 1;
}

問題のある行は2番目のstrcatです。Bloom-> hash_saltsは、次のように定義された構造体の一部です。

typedef unsigned const char *hash_function_salt[33];
typedef struct {
    size_t buckets;    
    size_t number_of_hash_salts;
    int bytes_per_bucket;
    unsigned char *data;
    hash_function_salt *hash_salts;
} bloom_filter_t;

そして、それらはここで初期化されます:

bloom_filter_t* bloom_filter_create(size_t buckets, size_t number_of_hash_salts, ...) 
{
    bloom_filter_t *bloom;
    va_list args;
    int i;

    bloom = malloc(sizeof(bloom_filter_t));
    if(bloom == NULL) return NULL;

    // left out stuff here for brevity...

    bloom->hash_salts = calloc(bloom->number_of_hash_salts, sizeof(hash_function_salt));

    va_start(args, number_of_hash_salts);

    for(i = 0; i < number_of_hash_salts; ++i)
        bloom->hash_salts[i] = va_arg(args, hash_function_salt);

    va_end(args);

    // and here...
}

そして、bloom_filter_createは次のように呼び出されます。

bloom_filter_create(100, 4, "3301cd0e145c34280951594b05a7f899", "0e7b1b108b3290906660cbcd0a3b3880", "8ad8664f1bb5d88711fd53471839d041", "7af95d27363c1b3bc8c4ccc5fcd20f32");

私は何か間違ったことをしているのですが、何が起こっているのか本当に迷っています。前もって感謝します、

ベン。

4

5 に答える 5

9

いくつかの問題があります。

char temp[sizeof(item) + sizeof(bloom->hash_salts[i]) + 2];

sizeof(item)4(または64ビットプラットフォームでは8)のみを返します。実際の長さにはおそらくstrlen()を使用する必要があります。strlenでそのようにスタック上で宣言できるとは思いませんが(gccの新しいバージョンで可能であると誰かが示しているのを見たかもしれませんが、昼食に出かけるかもしれません)。

もう1つの問題は、一時配列が初期化されていないことです。したがって、最初のstrcatは配列の先頭に書き込まない場合があります。strcatを呼び出す前に、最初の要素にNULL(0)を入れる必要があります。

number_of_hash_saltsすでに切り取られたコードに含まれている可能性がありますが、構造体のメンバーを初期化したことはわかりませんでした。

于 2010-07-15T22:59:14.600 に答える
4

sizeofではなくstrlenを使用する必要があります。 item配列ではなく、ポインタとして渡されます。

この線:

char temp[sizeof(item) + sizeof(bloom->hash_salts[i]) + 2];

tempはポインタの34倍の長さ+2になります。アイテムのサイズはポインタのサイズであり、sizeof(bloom->hash_salts[i])現在はポインタの33倍のサイズです。

にstrlenを使用する必要があるためitem、実際の文字数がわかります。

次に、bloom->hash_salts[i]は、hash_function_saltcharへの33個のポインタの配列です。次のようhash_function_saltに定義する必要があるようです。

33個のポインターではなく、33個の文字を保持する必要があるためです。文字列リテラルをbloom_filter_createに渡すときは、ポインターを渡すことにも注意してください。つまり、hash_function_salt配列を初期化するには、memcpyまたはstrcpyを使用します。正確な長さがわかっている場合(ここのように)、memcpyの方が高速です。

したがって、次のようになります。

typedef unsigned char hash_function_salt[33];

とでbloom_filter_create

memcpy(bloom->hash_salts[i], va_arg(args, char*), sizeof(bloom->hash_salts[i]));

Bloom_operationに戻ると、次のようになります。

char temp[strlen(item) + sizeof(bloom->hash_salts[i])];
strcpy(temp, item);
strcat(temp, bloom->hash_salts[i]);

strlenポインタであるためitemに使用しますがsizeof、charhash_function_saltの固定サイズ配列である。に使用します。hash_function_saltにはすでに。のスペースが含まれているため、何も追加する必要はありませんNULstrcpy最初 に使用します。strcatすでにNULで終了する文字列がある場合に使用します(ここにはありません)。*を削除することに注意してください。それはあなたの間違ったtypedefに続く間違いでした。

于 2010-07-15T22:58:09.203 に答える
2

temp使用する配列サイズの計算sizeof(bloom->hash_salts[i])(これはポインターのサイズだけです)が、ポインターを逆参照して、文字列全体をにコピーしようとしますtemp

于 2010-07-15T23:00:11.383 に答える
2

まず、誰もが言ったようtempに、文字列の長さではなく、2 つのポインターのサイズに基づいてサイズを設定しました。あなたはそれを修正し、症状が への呼び出しに移動したことを報告しstrlen()ます。

これは、より微妙なバグを示しています。

bloom->hash_salts[]によって返されたポインタから配列を初期化しましたva_arg()。これらのポインターの有効期間は限られています。それらは への呼び出しよりも長持ちしないかもしれませんが、 への呼び出しよりも長持ちしないva_end()ことはほぼ確実ですbloom_filter_create()。後で、bloom_filter_operation()それらは任意の場所を指し示し、ある種の興味深い失敗に運命づけられます。

編集:これを解決するには、配列に格納されているポインターにhash_salts十分な寿命があることが必要です。これに対処する 1 つの方法は、varargs 配列からそれらをコピーして、それらにストレージを割り当てることです。次に例を示します。

// fragment from bloom_filter_create()
bloom->hash_salts = calloc(bloom->number_of_hash_salts, sizeof(hash_function_salt));
va_start(args, number_of_hash_salts);
for(i = 0; i < number_of_hash_salts; ++i)
        bloom->hash_salts[i] = strdup(va_arg(args, hash_function_salt));
va_end(args);

後で、ポインターの配列自体を解放する前に、各要素をループしhash_saltsて呼び出す必要があります。free()

初期化に必要なオーバーヘッドが大きく、解放の手間が少ない別のアプローチは、1 回の割り当てですべての文字列に十分なスペースと共にポインターの配列を割り当てることです。次に、文字列をコピーしてポインタを埋めます。非常に小さな利点を得るには、多くのコードが必要です。

于 2010-07-15T23:10:18.990 に答える
1

hash_function_salt 型が正しく定義されていますか? *が多すぎる可能性があります:

(gdb) ptype bloom
type = struct {
    size_t buckets;
    size_t number_of_hash_salts;
    int bytes_per_bucket;
    unsigned char *data;
    hash_function_salt *hash_salts;
} *
(gdb) ptype bloom->hash_salts
type = const unsigned char **)[33]
(gdb) ptype bloom->hash_salts[0]
type = const unsigned char *[33]
(gdb) ptype *bloom->hash_salts[0]
type = const unsigned char *
(gdb) 
于 2010-07-15T23:07:36.267 に答える