使用结构字段作为循环计数器?
Using struct field as loop counter?
问题的一些背景
如果我有这样的结构
typedef struct {
idx_type type;
union {
char *str;
int num;
} val
} cust_idx;
我有这样的循环
for (i = 0; i < some_get(x); i++) {
some_fun(z, NULL, i);
}
我想重构以使用像 some_fun(z, idx)
这样的结构,其中 idx
是我的 cust_idx
结构之一,最好将 i
保留为循环计数器并更新 idx
或更改 for header 以使用 idx.val.num
而不是 i
?
为此,假设 idx_type
是字符串和数字类型的枚举,所有其他字段都会有宏,但我只打算在这里使用 IDX_NUM
宏因为我不担心与 idx.type
有任何关系。
总结一下我的担忧:
- 是否可读?我不想留下一团乱糟糟的东西,以至于有人会阅读并摇头......
- 是否建议不要这样做?
- 以下哪个是最佳解决方案?
结构字段作为循环计数器
#define IDX_NUM(x) (x.val.num)
...
cust_idx j;
j.type = TYPE_num;
for (IDX_NUM(j) = 0; IDX_NUM(j) < some_get(x); IDX_NUM(j)++) {
some_fun(z, j);
}
这与原来的一样,但在我看来,using struct field/macro 扩展并复杂化了 for 循环 header,但它仍然是可以理解的。
用原始计数器修改结构
cust_idx j;
j.type = TYPE_num;
for (i = 0; i < some_get(x); i++) {
IDX_NUM(j) = i;
some_fun(z, j);
}
这导致逻辑上对旧代码的更改最少,但由于添加了赋值行,最终代码量最大。
指向结构字段的指针
cust_idx j;
int *i = &(j.val.num);
j.type = TYPE_num;
for ((*i) = 0; (*i) < some_get(x); (*i)++) {
some_fun(z, j);
}
我不确定这在长期 运行 中会有多好,或者是否建议不要这样做。
至于可读性,我总是更喜欢单独的循环计数器。
编辑:斜体中的以下内容不正确在这种特定情况下因为默认情况下C结构作为值副本传递堆栈,因此在循环中将 j 传递给 some_fun() 就可以了。但我会在这里留下警告,因为它适用于许多类似的情况,在这些情况下,结构或数组由指针值传递。 (又名 'passed by reference')。
在您发布的代码中尤其如此,您在其中调用一个函数,并将结构作为循环内的参数。
如果我不知道 some_fun() 的作用,我只能希望我用作循环计数器的结构成员没有被修改。希望不是策略。
所以,除非有非常充分的理由不这样做,否则我总是将可读性放在首位。请记住:如果您编写的代码超出了您自己的语法和语义能力的极限,那么调试此类代码将毫无乐趣,因为调试比编写(错误的)代码难一个数量级。 ;)
补充:您可以查看所有变体的反汇编。编译器可能会在这里做很多优化,特别是如果它可以 'see' some_fun().
问题的一些背景
如果我有这样的结构
typedef struct {
idx_type type;
union {
char *str;
int num;
} val
} cust_idx;
我有这样的循环
for (i = 0; i < some_get(x); i++) {
some_fun(z, NULL, i);
}
我想重构以使用像 some_fun(z, idx)
这样的结构,其中 idx
是我的 cust_idx
结构之一,最好将 i
保留为循环计数器并更新 idx
或更改 for header 以使用 idx.val.num
而不是 i
?
为此,假设 idx_type
是字符串和数字类型的枚举,所有其他字段都会有宏,但我只打算在这里使用 IDX_NUM
宏因为我不担心与 idx.type
有任何关系。
总结一下我的担忧:
- 是否可读?我不想留下一团乱糟糟的东西,以至于有人会阅读并摇头......
- 是否建议不要这样做?
- 以下哪个是最佳解决方案?
结构字段作为循环计数器
#define IDX_NUM(x) (x.val.num)
...
cust_idx j;
j.type = TYPE_num;
for (IDX_NUM(j) = 0; IDX_NUM(j) < some_get(x); IDX_NUM(j)++) {
some_fun(z, j);
}
这与原来的一样,但在我看来,using struct field/macro 扩展并复杂化了 for 循环 header,但它仍然是可以理解的。
用原始计数器修改结构
cust_idx j;
j.type = TYPE_num;
for (i = 0; i < some_get(x); i++) {
IDX_NUM(j) = i;
some_fun(z, j);
}
这导致逻辑上对旧代码的更改最少,但由于添加了赋值行,最终代码量最大。
指向结构字段的指针
cust_idx j;
int *i = &(j.val.num);
j.type = TYPE_num;
for ((*i) = 0; (*i) < some_get(x); (*i)++) {
some_fun(z, j);
}
我不确定这在长期 运行 中会有多好,或者是否建议不要这样做。
至于可读性,我总是更喜欢单独的循环计数器。
编辑:斜体中的以下内容不正确在这种特定情况下因为默认情况下C结构作为值副本传递堆栈,因此在循环中将 j 传递给 some_fun() 就可以了。但我会在这里留下警告,因为它适用于许多类似的情况,在这些情况下,结构或数组由指针值传递。 (又名 'passed by reference')。
在您发布的代码中尤其如此,您在其中调用一个函数,并将结构作为循环内的参数。 如果我不知道 some_fun() 的作用,我只能希望我用作循环计数器的结构成员没有被修改。希望不是策略。
所以,除非有非常充分的理由不这样做,否则我总是将可读性放在首位。请记住:如果您编写的代码超出了您自己的语法和语义能力的极限,那么调试此类代码将毫无乐趣,因为调试比编写(错误的)代码难一个数量级。 ;)
补充:您可以查看所有变体的反汇编。编译器可能会在这里做很多优化,特别是如果它可以 'see' some_fun().