【发布时间】:2011-06-29 22:21:24
【问题描述】:
这个问题的前言是,我意识到 C 宏是一个敏感的主题。很多时候,它们可以通过更安全且不受诸如增量参数等经典问题的非宏观解决方案来完成;所以有了这个,我在 C 中有一个哈希表实现,其中链接节点用于冲突。我敢肯定大多数人已经看过一百万次了,但它有点像这样。
typedef struct tnode_t {
char* key; void* value; struct tnode_t* next;
} tnode_t;
typedef struct table_t {
tnode_t** nodes;
unsigned long node_count;
unsigned long iterator; // see macro below
...
}
我想提供一种遍历节点的抽象方式。我考虑使用一个函数,它接受一个函数指针并将函数应用于每个节点,但我经常发现这种解决方案非常有限,所以我想出了这个宏:
#define tbleach(table, node) \
for(node=table->nodes[table->iterator=0];\
table->iterator<table->node_count;\
node=node?node->next:table->nodes[++table->iterator])\
if (node)
可以这样使用:
tnode_t* n;
tbleach(mytable, n) {
do_stuff_to(n->key, n->value);
}
我能看到的唯一缺点是迭代器索引是表的一部分,因此显然您不能在同一个表中同时进行两个循环。我不知道如何解决这个问题,但考虑到这个小宏会有多大用处,我不认为它会破坏交易。所以我的问题。
** 更新 **
我采纳了 Zack 和 Jens 的建议,用“else”消除了问题,并在 for 语句中声明了迭代器。一切似乎都正常,但视觉工作室抱怨在使用宏的地方“不允许使用类型名称”。我想知道这里到底发生了什么,因为它可以编译并运行,但我不确定迭代器的作用域。
#define tbleach(table, node) \
for(node=table->nodes[0], unsigned long i=0;\
i<table->node_count;\
node=node?node->next:table->nodes[++i])\
if (!node) {} else
这种方法是不好的形式吗?如果不是,有什么方法可以改进它?
【问题讨论】:
-
我不禁想像邪恶博士。 “它是一个邪恶的宏吗?”
-
你不能也将迭代器传递给宏吗?例如:tbleach(mytable, iterator, n)... 这将允许您运行多个循环。
-
您是否反对将第三个参数用作迭代器?
-
您的哈希表的实际表是否经过优化打包?我认为它们通常是基于散列访问的,所以它们根本不是从 0..n 开始按顺序填充的。此外,仅当您停止关注下一个链接时,迭代器才会增加,但仍与看起来可疑的 node_count 进行比较。
-
@unwind 测试表明,打包大约是 3 个节点中的 1 个,因此虽然没有优化前沿,但我们的速度很快。至于命名node_count,table stuct 中还有一个value_count 的值。命名可能会更好,但这是另一个话题。
标签: c macros hashtable c-preprocessor