C - 链表valgrind未初始化错误取决于添加的顺序元素

C - linked list valgrind uninitialized error depending on order elements are added

我一直在研究可以接受多种数据类型的 C 通用链表。在测试我的链表搜索功能时,我注意到一个奇怪的 valgrind 未初始化错误,我无法弄清楚。出于某种原因,如果我通过先附加一个整数,然后附加一个双精度数来初始化链表,然后搜索任何双精度数(是否存在于 LL 中),我会收到以下错误:

==64398== Conditional jump or move depends on uninitialised value(s)
==64398==    at 0x1099C0: ll_contains (gll.c:301)
==64398==    by 0x1092E8: contains_tests (gll_tests.c:17)
==64398==    by 0x10934C: main (gll_tests.c:24)
==64398== 
==64398== HEAP SUMMARY:
==64398==     in use at exit: 0 bytes in 0 blocks
==64398==   total heap usage: 6 allocs, 6 frees, 1,124 bytes allocated
==64398== 
==64398== All heap blocks were freed -- no leaks are possible
==64398== 
==64398== For lists of detected and suppressed errors, rerun with: -s
==64398== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

但是,如果我先加双精度,然后加整数,然后搜索,一切都很好。以下是错误的产生方式(第 4 行和第 5 行的顺序):

struct LL* test_ll = ll_init();
int int_val = 69;    
double double_val = 69.69;
ll_add_end(test_ll, &int_val, INT);  /* This order causes the error! */
ll_add_end(test_ll, &double_val, DOUBLE);
assert(ll_contains(test_ll, &double_val, DOUBLE) == true);

struct LL* test_ll = ll_init();
int int_val = 69;  
double double_val = 69.69;  
ll_add_end(test_ll, &double_val, DOUBLE);  /* This order causes no errors! */
ll_add_end(test_ll, &int_val, INT);
assert(ll_contains(test_ll, &double_val, DOUBLE) == true);

相关函数如下:

节点是这样创建的:

struct Node* _create_node(void* data, enum ListType type)
{
    int data_size;
    struct Node* to_add = (struct Node*)malloc(sizeof(struct Node));
    to_add->type = type;
    to_add->next = NULL;
    to_add->prev = NULL;
    /* allocate memory for node's data */
    switch (type)
    {
        case INT:
            to_add->data = malloc(sizeof(int));
            data_size = sizeof(int);
            break;
        case DOUBLE:
            to_add->data = malloc(sizeof(double));
            data_size = sizeof(double);
            break;
        case CHAR:
            to_add->data = malloc(sizeof(char));
            data_size = sizeof(char);
            break;
        case STRING:
            data_size = strlen((const char*)data) + 1;
            to_add->data = malloc(sizeof(char) * data_size);
            break;
        default:
            return false;
    }
    /* copy data by byte into newly allocated memory */
    int i;
    for (i = 0; i < data_size; i++)
    {
        *(char*)(to_add->data + i) = *(char*)(data + i);
    }
    return to_add;
}

这是将节点附加到链表的方式:

bool ll_add_end(struct LL* list, void* data, enum ListType type)
{
    struct Node* to_add = _create_node(data, type);
    to_add->prev = list->tail;
    if (list->tail)
    {
        list->tail->next = to_add;
    }
    list->tail = to_add;
    list->head = (list->count == 0) ? to_add : list->head;
    list->count++;
    return true;
} 

这是错误的包含函数。错误总是发生在指示的行:

bool ll_contains(struct LL* list, void* data, enum ListType type)
{
    struct Node* current = list->head;
    switch (type)
    {
        case INT:
            while (current != NULL)
            {
                if (*((int*)current->data) == *((int*)data))
                {
                    return true;
                }
                current = current->next;
            }
            break;
        case DOUBLE:
            while (current != NULL)
            {
                if (*((double*)current->data) == *((double*)data))  /* ERROR HERE */
                {
                    return true;
                }
                current = current->next;
            }
            break;
        case CHAR:
            while (current != NULL)
            {
                if (*((char*)current->data) == *((char*)data))
                {
                    return true;
                }
                current = current->next;
            }
            break;
        case STRING:
            while (current != NULL)
            {
                if (strcmp((char*)current->data, (char*)data) == 0)
                {
                    return true;
                }
                current = current->next;
            }
            break;
        default:
            return false;
    }
    return false;
}

经过测试,我确定错误是由于尝试访问current->data引起的,但仅在DOUBLE的情况下,然后,仅当首先将整数添加到链表时。我尝试在 create_node 函数中使用 calloc 而不是 malloc,但错误仍然存​​在。很长一段时间以来,我一直无法找出导致这种行为的原因,因此我决定寻求帮助。提前致谢。

问题是您有一个混合类型列表,但 ll_contains 强制比较一种固定类型。

所以当你按照这个顺序执行时,发生的事情是

ll_add_end(test_ll, &double_val, DOUBLE);  /* This order causes no errors! */
ll_add_end(test_ll, &int_val, INT);
assert(ll_contains(test_ll, &double_val, DOUBLE) == true);

double 值在列表中排在第一位,因此它 returns 在与 int 比较之前。

顺序颠倒时

ll_add_end(test_ll, &int_val, INT);  /* This order causes the error! */
ll_add_end(test_ll, &double_val, DOUBLE);
assert(ll_contains(test_ll, &double_val, DOUBLE) == true);

您首先拥有 int,然后是 double。所以你强制比较 intdouble

if (*((double*)current->data) == *((double*)data))  /* ERROR HERE */

current->data 实际上是指向 int 的指针。当你 malloced 它时,你可能得到的不仅仅是 4 个字节 - iirc 它将是 24 个字节。因此,当您在第 4 个未初始化之后执行此比较字节时。

在你的比较函数中,你需要在转换和检查之前检查类型 数据。

while (current != NULL)
{
   if (current->type) == type)
   switch (type)
   {
   case INT:
      if (*((int*)current->data) == *((int*)data))
      {
         return true;
      }
      break;
      // etc.
   }
   current = current->next;
}