这个foreach C宏有多邪恶?

Nic*_*unt 3 c macros hashtable c-preprocessor

这个问题的前言是,我意识到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
        ...
}
Run Code Online (Sandbox Code Playgroud)

我想提供一种迭代遍历节点的抽象方式.我考虑使用一个函数,它接受一个函数指针并将函数应用于每个节点,但我经常发现这种解决方案非常有限,所以我想出了这个宏:

#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)
Run Code Online (Sandbox Code Playgroud)

哪个可以用作:

tnode_t* n;
tbleach(mytable, n) {
    do_stuff_to(n->key, n->value);
}
Run Code Online (Sandbox Code Playgroud)

我能看到的唯一缺点是迭代器索引是表的一部分,所以显然你不能在同一个表中同时进行两个循环.我不知道如何解决这个问题,但我不认为这是一个交易破坏者,考虑到这个小宏将有多大用处.所以我的问题.

**更新**

我收集了Zack和Jens的建议,用"else"删除了问题,并在for语句中声明了迭代器.一切似乎都有效,但是visual studio抱怨使用宏时"不允许使用类型名称".我想知道这里到底发生了什么,因为它编译并运行但我不确定迭代器的作用域.

#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
Run Code Online (Sandbox Code Playgroud)

这种做法是不好的形式,如果没有,有没有办法改善它?

zwo*_*wol 7

唯一真正令人无法接受的是你已经说过的东西 - 迭代器是表格的一部分.你应该这样做:

typedef unsigned long table_iterator_t;
#define tbleach(table, iter, node) \
    for ((iter) = 0, (node) = (table)->nodes[(iter)]; \
         (iter) < (table)->node_count; \
         (node) = ((node) && (node)->next) \
                  ? (node)->next : (table)->nodes[++(iter)])

// use:
table_iterator_t i;
tnode_t *n;
tbleach(mytable, i, n) {
    do_stuff_to(n->key, n->value);
}
Run Code Online (Sandbox Code Playgroud)

我也把这个if语句简化为for-loop表达式,因为它的方式稍微安全一些(如果循环体的紧支撑之后的下一个标记就不会发生奇怪的事情else).请注意,与通常的约定不同,table->nodes[table->node_count] 读取数组条目,因此您需要为其分配空间(并确保它始终为NULL).我认为你的版本也是如此.

编辑:更正了表条目为NULL的情况的逻辑.

  • 如果线程在你周围的任何地方,那么像这样的高状态变量是一种可靠的麻烦方法. (2认同)