繁体   English   中英

C 通用链表 - 比较空指针

[英]C Generic Linked List - Compare void pointer

我正在写一个 LinkedList,因为我很喜欢它。 但是,我陷入了困境:
我会按值从列表中删除一个元素,例如 C#。
我有这个代码:

int M_List_RemoveItemFromList(List list, void* value)
{
    NodeList lastNode = NULL;
    NodeList currentNode = *(list->nodes);
    while (currentNode)
    {
        // 1. if (memcmp(value, currentNode->data, list->elementSize) == 0)
        // 2. if (value == currentNode->data)
        {
            if (lastNode)
            {
                lastNode->next = currentNode->next;
            }
            else {
                *(list->nodes) = currentNode->next;
            }
            currentNode->next = NULL;
            --list->length;
            return 0;
        }
        lastNode = currentNode;
        currentNode = currentNode->next;
    }
    return 1;
}

我评论了两行,因为它们在不同的上下文中工作。

  1. 适用于 int 等值,它会删除具有正确 int 值的元素。 它不适用于“字符串”,在这种情况下,它会删除第一个元素,这无关紧要。
  2. 适用于字符串,不适用于 int 值。

哪个是正确的方法? C#是怎么做到的?
如果您需要更多信息,请告诉我。

例子:

List list = NewList(char);
char* stringa = "Test 001";
AppendList(list, stringa);
AppendList(list, "Test 002");
AppendList(list, "Test 003");
AppendList(list, "Test 004");
AppendList(list, "Test 005");
AppendList(list, "Test 006");

PrintStringList(list);
PrintNewSection();

M_List_RemoveItemFromList(list, "Test 004"); // It removes the first element with (1. commented row) and the right one with (2. commented row)
M_List_RemoveItemFromList(list, "Test 001"); // It removes the first element with (1. commented row) and the right one with (2. commented row)
M_List_RemoveItemFromList(list, "Test 006"); // It removes the first element with (1. commented row) and the right one with (2. commented row)
PrintStringList(list);
PrintNewSection();

List list2 = NewList(int);

int a = 101;
int b = 2;
int c = 2;
    
AppendList(list2, &a);
AppendList(list2, &b);
M_List_RemoveItemFromList(list2, &c); // it doesn't remove anything with 2. and the right one with 1.
PrintIntList(list2);

我将元素添加到列表的方式:

NodeList M_List_Append(List list, void* value)
{
    NodeList element = malloc(sizeof(T_NodeList) * list->elementSize);
    if (!element) return NULL;
    element->data = value;
    NodeList tail = M_List_GetTail(list);
    if (!tail)
    {
        *(list->nodes) = element;
    }
    else
    {
        tail->next = element;
    }
    element->next = NULL;
    ++list->length;
    if (list->length > list->capacity - 2)
        M_List_IncreaseSize(list);
    return element;
}

这是列表结构:

typedef struct S_NodeList
{
    void* data;
    struct S_NodeList* next;
} T_NodeList;

#define NodeList T_NodeList*
#define HeadList NodeList*

typedef struct S_List
{
    HeadList nodes;
    size_t elementSize;
    size_t capacity;
    size_t length;
} T_List;

#define List T_List*

好的,这很棘手,但我将在@akatz 评论后回答这个问题。

我定义了一个宏:

#define List_RemoveValueFromList(list, value) \
    _Generic((value), \
    int: M_List_RemoveIntFromList, \
    char*: M_List_RemoveStringFromList) \
    (list, value);

然后我创建了两个具有不同比较的函数和一个删除的 function(为了避免重复代码):

void M_List_Remove(List list, NodeList lastNode, NodeList currentNode)
{
    if (lastNode)
    {
        lastNode->next = currentNode->next;
    }
    else {
        *(list->nodes) = currentNode->next;
    }
    currentNode->next = NULL;
    --list->length;
}

int M_List_RemoveIntFromList(List list, int value)
{
    NodeList lastNode = NULL;
    NodeList currentNode = *(list->nodes);
    while (currentNode)
    {
        if (value == *((int*)currentNode->data))
            //if (value == currentNode->data)
        {
            M_List_Remove(list, lastNode, currentNode);
            return 0;
        }
        lastNode = currentNode;
        currentNode = currentNode->next;
    }
    return 1;
}

int M_List_RemoveStringFromList(List list, char* value)
{
    NodeList lastNode = NULL;
    NodeList currentNode = *(list->nodes);
    while (currentNode)
    {
        if (strcmp(value, (char*)(currentNode->data)) == 0)
        {
            M_List_Remove(list, lastNode, currentNode);
            return 0;
        }
        lastNode = currentNode;
        currentNode = currentNode->next;
    }
    return 1;
}

任何改进都是绝对接受的!

这个示例代码...

 List list = NewList(char);

... 建议NewList作为宏实现。 鉴于List显然是具有名为elementSize的成员的结构类型,并且宏仅使用类型名称,因此宏必须根据类型名称计算elementSize (否则不对其进行初始化)。

但是该宏应该如何从char类型中知道作为成员呈现的实际字符串的长度? 你是否真的打算当成员是字符串时,它们都必须是相同的长度? 因为这是比较元素是否相等的含义

 memcmp(value, currentNode->data, list->elementSize)

我想NewList只是使用sizeof(type)来设置elementSize ,但sizeof(char)是1,并且使用那个elementSizememcmp将只比较每个字符串的第一个字符。 (所以尝试测试字符串"a""b""c" 。)也就是说,字符串方法 (1) 的问题是您指定了错误的类型。 对于您提供的特定测试字符串,您可能会使用

List list = NewList(char[9]);

,因为所有列表元素都是char的 9 个元素 arrays 。

另一方面,您的方法 (2) 比较存储在列表中的指针的值。 这在语义上完全不同,但不一定是错误的。 它评估指定的指针是否指向与存储在列表中的相同的 object。 您(不)幸运地发现它完全适用于您的字符串测试 - 您的编译器显然正在合并相同的字符串文字,这不是必需的。

哪个是正确的方法? C#是怎么做到的?

C# 的方式也不是,至少如果“C#”是指它的List<T> class。 根据该类的文档

List<T> class 同时使用相等比较器和排序比较器。

  • ContainsIndexOfLastIndexOfRemove等方法对列表元素使用相等比较器。 类型T的默认相等比较器确定如下。 如果类型T实现了IEquatable<T>泛型接口,则相等comparer是该接口的Equals(T)方法; 否则,默认相等比较器为Object.Equals(Object)

  • BinarySearchSort等方法对列表元素使用排序比较器。 [...]

IList<T>的其他实现可能会有所不同。 事实上,它的文档(继承自ICollection<T> )明确表示“实现在确定对象相等性的方式上会有所不同”。

C 中最接近的等效项是为您的List结构提供一个指向 function 的成员,以用于比较元素和测试值是否相等。 例如,

/*
 * returns less than zero, zero, or greater than zero, corresponding to
 * whether o1 is to be considered less than, equal to, or greater than o2
 */
typedef int compare_func(const void *o1, const void *o2);

typedef struct S_List {
    HeadList nodes;
    // size_t elementSize; // probably not needed
    size_t capacity;
    size_t length;
    compare_func *compare;
} T_List;

用户需要指定适合元素类型的 function。 此类功能的示例可能包括:

int compare_strings(const void *o1, const void *o2) {
    return strcmp((const char *) o1, (const char *) o2);
}

int compare_ints(const void *o1, const void *o2) {
    int i1 = *(int *)o1;
    int i2 = *(int *)o1;

    if (i1 < i2) return -1;
    else if (i1 > i2) return 1;
    else return 0;
}

诸如M_List_RemoveItemFromList()类的函数需要在适合列表的意义上比较对象是否相等,将使用指定的 function 来执行此操作。 相等比较将如下所示:

if (list->compare(value, currentNode->data) == 0) // ...

暂无
暂无

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

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