简体   繁体   English

如何在C中正确使用指针

[英]How to use pointers correctly in C

I am trying to add (key, value) pairs to a hashmap but cannot access the values after insertion. 我正在尝试将(键,值)对添加到哈希图中,但是在插入后无法访问这些值。

This hash table is supposed to deal with collisions as I am iterating along each hash index whenever a collision occurs. 每当发生冲突时,当我沿着每个哈希索引进行迭代时,此哈希表都应处理冲突。 I then insert it when I have reached the end of the (key, value) pair list at that index. 然后,在到达该索引的(键,值)对列表的末尾时,将其插入。

Essentially it is a basic linked list hashmap. 本质上,它是一个基本的链表哈希表。

The problem is, I keep getting a segmentation fault when I try to access the value again (and my showTable() function also fails). 问题是,当我再次尝试访问该值时,我一直遇到分段错误(并且showTable()函数也失败)。 In this test, I am simply trying to access the first (key, value) pair at each hash index after something is added at that hash index. 在此测试中,我只是在尝试在每个哈希索引处添加了一些东西之后,尝试访问每个哈希索引处的第一对(键,值)。 I am probably doing something very silly but I see it. 我做的事情可能很愚蠢,但我看到了。

I have not yet commented but I hope the code is self explanatory. 我尚未评论,但我希望代码能自我解释。 The important bit is InsertKeyValuePair() but I have added everything as a code review would also be beneficial. 重要的一点是InsertKeyValuePair()但是我添加了所有内容,因为代码审查也将是有益的。

#include <stdlib.h>
#include <stdio.h>

typedef struct TVal KeyValue;

typedef struct TVal {
    char *value;
    char *key;
    KeyValue *next;
} KeyValue;

typedef KeyValue **HashTable;

int MAX_SIZE = 200;

int HashKey(char *Key, int Max);
void InsertKeyValuePair(char *key, char *value, int Index, HashTable table);
int insert(char *Key, char *value, HashTable table, int size);
void showTable(HashTable table, int size);

int HashKey(char *Key, int Max) {
    char c = *Key;
    int Hash = 0;
    int n = 1;

    while (c != 0) {
        Hash += n * ((int)c);
        c = *(Key + n);
        n++;
    }

    return Hash % MAX_SIZE;
}

void InsertKeyValuePair(char *key, char *value, int Index, HashTable table) {
    KeyValue *cursor = *(table + Index);
    while (cursor != NULL) {
        cursor = cursor->next;
    }
    cursor = malloc(sizeof(KeyValue));
    cursor->value = value;
    cursor->key = key;
    printf("insert <K,V>(%s,%s) HashIndex = %i\n", cursor->key, cursor->value, Index);

    //Trying to access value previously inserted
    KeyValue *cursor2 = *(table + Index);
    printf("<K,V>(%s,%s)\n", cursor2->key, cursor2->value);
}

int insert(char *Key, char *value, HashTable table, int size) {
    int Index = HashKey(Key, MAX_SIZE);
    InsertKeyValuePair(Key, value, Index, table);
    return size + 1;
}

void showTable(HashTable table, int size) {
    int i;
    for (i = 0; i < size; i++) {
        KeyValue *cursor = *(table + i);

        if (cursor == NULL) 
            continue;

        while (cursor != NULL) {
            printf("==============");
            printf("<K,V>(%s,%s)\n", cursor->key, cursor->value);
            cursor = cursor->next;
        }   
        printf("==============");
    }
}

int main() {
    HashTable HTbl = malloc(sizeof(HashTable) * MAX_SIZE);
    int size = 0;

    size = insert("yeuydfdan", "wesfg", HTbl, size);
    size = insert("ywere", "rdgg", HTbl, size);
    size = insert("ye4", "3244", HTbl, size);

    //showTable(HTbl, MAX_SIZE);
}

This statement 这个说法

HashTable HTbl = malloc(sizeof(HashTable)*MAX_SIZE);

is incorrect and moreover the allocated memory is not initialized. 是不正确的,而且分配的内存没有初始化。 There should be 应该有

HashTable HTbl = calloc( MAX_SIZE, sizeof( KeyValue * ) );

or like 或喜欢

HashTable HTbl = calloc( MAX_SIZE, sizeof( *HTbl ) );

The index within the table should be calculated as some unsigned integer. 表中的索引应计算为一些无符号整数。 Otherwise in general you can get a negative index. 否则,通常可以得到一个负索引。

In function HashKey parameter Max is not used. 在函数HashKey中,不使用Max参数。

In the function InsertKeyValuePair there is changed the local variable cursor instead of the data member cursor->next or *(table+Index) . 在函数InsertKeyValuePair ,更改了局部变量cursor而不是数据成员cursor->next*(table+Index)

The loop in the function showTable shall use MAX_SIZE not size in the loop condition. 函数showTable中的循环在循环条件下应使用MAX_SIZE而不是size That is you have to pass as an argument the value of MAX_SIZE not the value of size . 也就是说,您必须将MAX_SIZE的值而不是size的值作为参数传递。

Here is a demonstrative program that shows how the program can be updated. 这是一个演示程序,显示了如何更新程序。

#include <stdio.h>
#include <stdlib.h>

typedef struct TVal KeyValue;

typedef struct TVal 
{
    const char *value;
    const char *key;
    KeyValue *next;
} KeyValue;


typedef KeyValue **HashTable;

const size_t MAX_SIZE = 200;

static size_t HashKey( const char *key, size_t max_slots ) 
{
    size_t Hash = 0;

    for ( size_t i = 0; key[i]; i++ ) Hash += ( i + 1 ) * ( unsigned char )key[i];

    return Hash % max_slots;
}

static int InsertKeyValuePair( HashTable table, const char *key, const char *value, size_t index ) 
{
    KeyValue **cursor = &table[index];

    while ( *cursor != NULL ) cursor = &( *cursor )->next;

    *cursor = malloc( sizeof( KeyValue ) );

    int success = *cursor != NULL;

    if ( success )
    {
        ( *cursor )->value = value;
        ( *cursor )->key = key;
        ( *cursor )->next = NULL;
    }

    return success;
}

int insert( HashTable table, const char *key, const char *value, size_t *size ) 
{
    size_t index = HashKey( key, MAX_SIZE );

    int success = InsertKeyValuePair( table, key, value, index );

    if ( success ) ++*size;

    return success;
}

void showTable( HashTable table, size_t size )
{
    for ( size_t i = 0; i < size; i++ )
    {
        KeyValue *cursor = table[i];

        if ( cursor != NULL )
        {
            do
            {
                puts( "==============" );
                printf( "<K,V>(%s, %s)\n", cursor->key, cursor->value );
                cursor = cursor->next;
            } while ( cursor != NULL );

            puts( "==============\n" );
        }
    }        
}

int main( void )
{
    HashTable HTbl = calloc( MAX_SIZE, sizeof( *HTbl ) );

    size_t size = 0;

    insert( HTbl, "yeuydfdan", "wesfg", &size );
    insert( HTbl, "ywere", "rdgg", &size );
    insert( HTbl, "ye4", "3244", &size );

    showTable( HTbl, MAX_SIZE );
}

The program output is 程序输出为

==============
<K,V>(ywere, rdgg)
==============

==============
<K,V>(ye4, 3244)
==============

==============
<K,V>(yeuydfdan, wesfg)
==============

Of course you should add some other functions as for example a function that deletes the table with its nodes. 当然,您应该添加一些其他功能,例如删除表及其节点的功能。

And it will be better if each node will allocate memory for a key and a value and copy there the passed arguments. 如果每个节点都为一个键和一个值分配内存并在其中复制传递的参数,那将更好。 Otherwise the table may deal in general only with string literals because they have static storage duration. 否则,该表通常只能处理字符串文字,因为它们具有静态存储持续时间。

If you will rewrite the implementation of table such a way that it will copy keys and values in nodes of the table then the structure should be defined like 如果您将重写表的实现方式,使其复制表节点中的键和值,则结构应定义为

typedef struct TVal KeyValue;

typedef struct TVal 
{
    char *value;
    const char *key;
    KeyValue *next;
} KeyValue;

That is in any case the key should not be changed and should be declared with the qualifier const . 也就是说,无论如何都不应该更改键,而应使用限定符const对其进行声明。

There are multiple problems in your code: 您的代码中存在多个问题:

  • The hash table is not initialized to NULL , causing segmentation faults when trying to dereference the pointers it contains. 哈希表未初始化为NULL ,从而在尝试取消对其包含的指针的引用时导致分段错误。 Allocating with calloc() would fix this problem. calloc()分配将解决此问题。
  • It is confusing and error prone to hide pointers behind typedefs. 将指针隐藏在typedef后面会造成混淆,并且容易出错。
  • The allocation in main should read HashTable HTbl = calloc(sizeof(*HTbl), MAX_SIZE); main的分配应读取HashTable HTbl = calloc(sizeof(*HTbl), MAX_SIZE);
  • the insertion code in InsertKeyValuePair does not link the new pair at the end, nor at the beginning of the hashtable bucket list. InsertKeyValuePair中的插入代码不会在哈希表存储区列表的末尾或开头链接新对。
  • it is advisable to use unsigned arithmetics to compute the hash key to avoid overflow issues. 建议使用unsigned算术来计算哈希键,以避免溢出问题。
  • the pointer notation *(table + Index) is confusing. 指针符号*(table + Index)令人困惑。 You should use the array notation table[Index] instead. 您应该改用数组符号table[Index]
  • there seems to be some confusion between the length of the hashtable ( MAX_SIZE ) and the number of entries in the hashtable ( size ). 哈希表的长度( MAX_SIZE )和哈希表中的条目数( size )之间似乎有些混淆。 Renaming the variables appropriately may improve readability. 适当重命名变量可以提高可读性。 It is also probably better to pass the count by address and return a success indicator. 最好按地址传递计数并返回成功指示符。

Here is a corrected version: 这是更正的版本:

#include <stdlib.h>
#include <stdio.h>

typedef struct TVal KeyValue;

typedef struct TVal {
    const char *value;
    const char *key;
    KeyValue *next;
} KeyValue;

typedef KeyValue **HashTable;

static unsigned int HASH_SIZE = 200;

static unsigned int HashKey(const char *key);
static KeyValue *InsertKeyValuePair(const char *key, const char *value, int index, HashTable table);
static int insert(const char *Key, const char *value, HashTable table, int *countp);
static void showTable(HashTable table);

static unsigned int HashKey(const char *key) {
    unsigned int hash = 0;
    size_t n;

    for (n = 0; key[n] != 0; n++) {
        hash += n * (unsigned char)key[n];
    }
    return hash % HASH_SIZE;
}

static KeyValue *InsertKeyValuePair(const char *key, const char *value, int index, HashTable table) {
    KeyValue *cursor;
    cursor = malloc(sizeof(KeyValue));
    if (cursor != NULL) {
        KeyValue **cursorp = &table[index];
        while (*cursorp != NULL) {
            cursorp = &(*cursorp)->next;
        }
        cursor->value = value;
        cursor->key = key;
        cursor->next = NULL;
        *cursorp = cursor;
    }
    return cursor;
}

static int insert(const char *key, const char *value, HashTable table, int *countp) {
    int index = HashKey(key);
    if (InsertKeyValuePair(key, value, index, table)) {
        *countp += 1;
        return 1;
    }
    return 0;
}

static void showTable(HashTable table) {
    unsigned int i;
    for (i = 0; i < HASH_SIZE; i++) {
        KeyValue *cursor = table[i];

        if (cursor == NULL)
            continue;

        while (cursor != NULL) {
            printf("==============");
            printf("<K,V>(%s,%s)\n", cursor->key, cursor->value);
            cursor = cursor->next;
        }
        printf("==============\n");
    }
}

int main() {
    HashTable HTbl = calloc(sizeof(*HTbl), HASH_SIZE);
    int count = 0;

    insert("yeuydfdan", "wesfg", HTbl, &count);
    insert("ywere", "rdgg", HTbl, &count);
    insert("ye4", "3244", HTbl, &count);

    showTable(HTbl);
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM