'多用途'链表执行在纯C
-
09-09-2019 - |
题
这不正是一个技术问题,因为我知道的C种类的足够做的事情,我需要(我的意思是,不要让语言得到你的方法'),因此这个问题基本上是一个'什么方向走'的问题。
情况是:我目前正在采取的一个算法高级课程,并且为了不断增长作为程序员的,我需要利用纯C,以实现的实际任务(它的工作:几乎任何小小的错误你做实际上迫使你完全明白你在做什么为了解决它)。在执行,我明显地遇到的问题具有来实施"基本"数据结构从地上爬起来:实际上不仅是联的清单,但也栈、树木等等。
我着重列出了在本主题,因为这是通常的结构是我的最终使用一个新城市中的程序,无论是作为一个'主要'的结构或作为一个'助手'的结构对于其他更大的人(例如,哈希树解决冲突通过使用联的列表)。
这要求列元素存储的许多不同的类型。 我假设在这里作为前提,我不想重新编码的清单为每一个类型。所以,我可以拿出这些备选方案:
- 制定一个清单的效指针(有点不雅;难以调试)
- 使只有一个名单,但有一个 联盟 作为"元素类型",包含的所有元素的类型,我将使用的程序(更易于调试;废物的空间,如果件是不是都一样大小)
- 使用一个预处理宏再生的代码,用于每种类型的风格 SGLIB,'模仿'C++'s STL(创造性的解决方案;不会浪费空间;元素具有明确的类型,他们实际上是当他们返回; 任何更改名单的代码可以真正的戏剧性)
- 你的想法/方案
使这个问题很清楚:其中之一以上是最好的?
PS:因为我基本上在学术方面,我也很感兴趣,认为工作人员纯C那里的行业。我的理解是,大多数纯C程序员都在埋设备的地区,在那里我不认为这种问题我面对的是共同的。然而,如果有人知道它是如何做'在现实世界中,我将非常感兴趣,你的意见。
解决方案
一个 void *
是一点点的痛苦在一个链表既然你已经来管理它的分别分配到名单本身。一种办法,我已在过去使用的是有一个变量的中小型结构,如:
typedef struct _tNode {
struct _tNode *prev;
struct _tNode *next;
int payloadType;
char payload[1]; // or use different type for alignment.
} tNode;
现在我意识到,不 看看 变量的大小,但是让我们分配一个结构,因此:
typedef struct {
char Name[30];
char Addr[50];
} tPerson;
tNode *node = malloc (sizeof (tNode) - 1 + sizeof (tPerson));
现在你有一个节点,对于所有意图和目的而言,看起来是这样的:
typedef struct _tNode {
struct _tNode *prev;
struct _tNode *next;
int payloadType;
char Name[30];
char Addr[50];
} tNode;
或者,在图表形式(在那里 [n]
装置 n
字节):
+----------------+
| prev[4] |
+----------------+
| next[4] |
+----------------+
| payloadType[4] |
+----------------+ +----------+
| payload[1] | <- overlap -> | Name[30] |
+----------------+ +----------+
| Addr[50] |
+----------+
也就是说,假设你知道如何解决的有效负载正确的。这是可以做到如下:
node->prev = NULL;
node->next = NULL;
node->payloadType = PLTYP_PERSON;
tPerson *person = &(node->payload); // cast for easy changes to payload.
strcpy (person->Name, "Bob Smith");
strcpy (person->Addr, "7 Station St");
投线简单地转换的地址 payload
字符的(在 tNode
类型)是一个实际地址 tPerson
有效载荷的类型。
使用这种方法,你可以随身携带任何类型有效载荷你想在一个节点,即使是 不同的有效载荷的类型在各个节点, ,而不浪费空间的一个联盟。这种浪费的可看出如下:
union {
int x;
char y[100];
} u;
其中96字节是浪费的每一次你的店整数类型的列表(用于4个字节的整数)。
有效载荷型的 tNode
可以让你轻易地检测是什么类型有效负荷这一点是,所以你的代码可以决定如何处理它。你可以使用沿线的东西:
#define PAYLOAD_UNKNOWN 0
#define PAYLOAD_MANAGER 1
#define PAYLOAD_EMPLOYEE 2
#define PAYLOAD_CONTRACTOR 3
或者(可能是更好的):
typedef enum {
PAYLOAD_UNKNOWN,
PAYLOAD_MANAGER,
PAYLOAD_EMPLOYEE,
PAYLOAD_CONTRACTOR
} tPayLoad;
其他提示
我在.002:
- 制定一个清单的效指针(有点diselegant;难以调试)
这不是一个不错的选择,恕我直言,如果你必须写在C。你可能会添加API方法,以允许的应用程序提供一个print()方法为便于调试。类似的方法可以援引时(例如)项中添加或删除清单。(用于连接的清单,这通常是不必要的,但为更复杂的数据结构--哈希表,例如)--它有时可以是一个救星。)
- 使只有一个名单,但有一个联盟作为"元素类型",包含的所有元素的类型,我将使用的程序(更易于调试;废物的空间,如果件是不是都一样大小)
我想避免这样的瘟疫。(嗯,你有没有问。) 有一个手动配置的编写时依赖的数据结构,其中包含类型是最糟糕的世界。再有,恕我直言。
- 使用一个预处理宏再生的代码,用于每种类型的风格SGLIB(sglib.sourceforge.net),'模仿'C++'s STL(创造性的解决方案;不会浪费空间;元素具有明确的类型,他们实际上是当他们返回;任何更改名单的代码可以真的急剧)
有趣的想法,但由于我不知道SGLIB,我不能说比这更多。
- 你的想法/方案
我会去的第一个选择。
我已经做到了这一点过去,在我们的代码(已转化为C++),并且在该时间,决定在void*的方法。我只是做了这种灵活性的-我们几乎始终存储指针在清单不管怎么说,和简单的解决方案,和可用性,它超过了(对我)的缺点的其他办法。
这就是说,有一个时间在那里引起一些讨厌的错误,难以调试,所以这绝对不是一个完美的解决方案。我认为这仍然是一个我会走,但是,如果我是这样了。
我没有编码C在几年但是 巧舌如簧 要求提供"一个大的实用功能为串的和共同的数据结构",其中有联系的名单。
虽然人们很容易认为关于解决这样的问题,技术使用的另一种语言,说,泛型的,在实践中很少的一个胜利。可能有一些罐头的解决方案,得到它的权利的大部分时间(并告诉你他们的文件时,他们得到这是错误的),采用,可能会错过点的分配,因此我认为两次。对于一个非常少数的情况下,它可能是feasable推出自己的,但是对于项目的任何合理的规模,它不可能是值得调试的努力。
相反,当编程语言x,你应该用语的语言。不要写java当你使用蟒蛇。不要写C时使用的方案。不要写C++当你使用C99。
我自己,我很可能最终使用类似人的建议,但是实际使用的联合char[1]和void*和国,使共同的情况下便利(和enumed类型标志)
(我也可能最终实现一个斐波纳契树,只是因为那听起来整洁,你只能实现RB树木以前多次失去了它的味道,即使是更为常见的情况下,它会使用。)
编辑: 基于你的评论,看起来你已经有了一个很好的情况下使用一个罐头的解决方案。如果您教练允许的话,和语法,它提供了舒适的感觉了,给它一个旋转。
这是一个很好的问题。有两种解决方案我,如:
戴维*汉森的 C接口和实现 使用一个列表中的
void *
指针,这是对我来说足够好.我 学生, 我写了一个 awk脚本生成特定类型列出的功能.相比,预处理宏,它需要一个额外的建立的一步,但该系统的操作更加透明程序没有很多经验。它真的有助于使情况下,对参数化的多态性,其中他们看到后来在他们的课程。
这是什么一定的职能,看起来像:
int lengthEL (Explist *l); Exp* nthEL (Explist *l, unsigned n); Explist *mkEL (Exp *hd, Explist *tl);
Awk脚本是一个150行恐怖;它搜索C码
typedef
s和产生一定的名单的功能为每一个。这是非常旧的;我大概可以做得更好,现在:-)
我不会给名单的工会一天的时间(或空间,我的硬盘驱动)。它是不安全,这是不可扩展的,因此您可能会以及只使用 void *
并用它来完成。
一个改进,使得一个名单的void*将使得一个名单的结构,包含一个void*和一些元数据有关什么的void*点,包括其类型的尺寸,等等。
其他的想法:嵌入一个Perl口齿不清或解释。
或者去一半:链接与Perl图书馆,并使它成为一个列表中的Perl SVs或东西。
我可能会去的无效方法,我自己,但是它发生在我身上,你可以存储数据为XML。随后的列表可以有一个char*为的数据(其中分析的需求无论出于何子的元素,你需要)....