【发布时间】:2016-05-12 19:20:47
【问题描述】:
我有兴趣在 C 中创建自己的双向链表实现。目标是使其尽可能灵活和“用户”友好。这意味着它不能仅限于一种类型的数据。我还想尽量减少列表代码之外的内存管理。也就是说,我希望列表代码处理分配和释放任何必要的内存。 (但是,当然,该列表可用于存储指向动态分配数据的指针。)
我正在使用两个结构。 “node”结构包含指向它之前的节点和紧随其后的节点的指针,指向它包含的数据的void*指针,以及数据的大小。但是,因为这是 C,所以它不能保存数据的类型。 “list”结构跟踪列表的开头和结尾、列表中元素的数量等。我已经实现了用于初始化列表并将数据附加到列表的功能。内存分配和释放以及链接似乎工作正常,并且列表似乎正确地相互链接。问题是如何在创建列表节点时实际导入数据。以下是我考虑过的方法:
通过
void*传递指向数据的指针,并将大小作为另一个参数。这可以通过添加一个宏来获取变量的地址和大小并将它们传递给函数来使用户更加友好。问题?并非我可能想要添加到列表中的所有内容都可以使用其地址。例如,考虑list_append(list, 17)。这应该在列表末尾添加一个整数有效负载值为 17 的新节点,但它不起作用,因为整数文字17无法获取其地址。将数据的大小作为一个参数传递,调用堆栈上的数据本身作为一个额外的参数传递。 C 通过
.../stdarg.h方法支持未知数量、类型和大小的参数。我想我可以使用宏来获取sizeof()被附加的项目,并将其与项目本身一起传递给附加函数。这里的问题是可变参数宏要我指定一个类型(不仅仅是类型的大小)。所以,我做了一些挖掘,发现GCC显然使用__builtin_next_arg宏来实现stdarg.h中的变量参数。显然,这会使我的代码依赖于 GCC(或者至少依赖于这个特定的宏),但它至少可以与这个特定的编译器一起工作。据称,__builtin_next_arg宏将参数列表中最后一个命名的参数的地址作为void*给出。当我在 Windows(使用 MinGW 32 位)上尝试这种方法时,它按预期工作。从__builtin_next_arg给出的值到新分配的缓冲区的简单memcpy()复制了数据。然而,当我在 Ubuntu 上使用 GCC 64 位时,一切都乱了套。__builtin_next_arg给我的地址与我期望的论点相去甚远。编译器也开始偶尔抱怨va_start的第二个参数不是列表中的最后一个参数,尽管我什至根本没有在我的代码中使用va_start。此外,无论我做什么,我得到的值似乎都是零(NULL、0)。
有什么办法可以解决这个问题吗?我基本上想要的是va_arg 的一个版本,它给出了堆栈上参数的地址。其他方法也是可以接受的。
在 C++ 中,我可以使用模板来完全避免这个问题,但我想使用 C。
【问题讨论】:
-
TL/DR。 ADT(抽象数据类型)是关键字。
-
@EugeneSh.:确实是 TL;DR。但模板只是饮食 ADT(还是“轻”?)。
-
使用最适合您问题的语言。并具有适当的抽象级别。否则你很快就会失去兴趣和/或动力,迷失在细节中。如果您暂停数周/数月,不要忘记不了解您所做的事情。
标签: c gcc macros variadic-functions