【发布时间】:2011-10-22 06:21:34
【问题描述】:
我在 C 中实现了一棵树,现在我想定义一个包装树集。我的树在 tree.h 中有一个迭代器:
typedef struct tree_iter_t {
void *current;
tree_t *tree;
unsigned char info : 2;
} tree_iter_t;
还有一个在tree.c中获取迭代器的函数:
tree_iter_t titerator(tree_t *t) {
tree_iter_t it;
it.current = t->min;
if (t->min) it.info = 0;
else it.info = 3;
it.tree = t;
return it;
}
我可以用-Wall -O2 编译它而没有警告。对于我的树集,我在 tset.h 中定义了我的树集迭代器,如下所示:
typedef struct tset_t tset_t;
typedef struct tset_iter_t {
tree_iter_t iter;
} tset_iter_t;
以及在 tset.c 中获取它的函数。
struct tset_t {
tree_t *tree;
};
tset_iter_t tsiterator(tset_t *ts) {
tset_iter_t it;
it.iter = titerator(ts->tree);
return it;
}
当我使用gcc -Wall -c -combine tset.c tree.c 编译时,我没有问题,但是当我添加-O2 时,我在返回语句上收到警告:warning: ‘it.iter.tree’ is used uninitialized in this function。为什么 GCC 有这个问题?我错过了什么明显的东西吗?它看起来对我来说是初始化的。我跑了gcc -S -O2 tset.c 试图了解发生了什么,GCC 没有给出任何警告并产生了这个:
tsiterator:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $36, %esp
movl 12(%ebp), %edx
movl 8(%ebp), %ebx
leal -20(%ebp), %eax
movl (%edx), %edx
movl %eax, (%esp)
movl %edx, 4(%esp)
call titerator
movzbl -12(%ebp), %edx
movzbl 8(%ebx), %eax
andl $3, %edx
andl $-4, %eax
orl %edx, %eax
subl $4, %esp
movb %al, 8(%ebx)
movl -16(%ebp), %eax
movl %eax, 4(%ebx)
movl -20(%ebp), %eax
movl %eax, (%ebx)
movl %ebx, %eax
movl -4(%ebp), %ebx
leave
ret $4
我知道优化会生成一些看起来很奇怪的代码,但是这到底是怎么回事!?我所有的其他(优化的)包装函数都是 10 行汇编(只是调用树函数的常用函数调用开销)。 gcc -O2 -S -combine tset.c tree.c 给了我警告,内联滴定器,并产生了这个:
tsiterator:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %edx
pushl %ebx
movl 8(%ebp), %eax
movl (%edx), %ecx
movl 4(%ecx), %edx
movl %ecx, 4(%eax)
cmpl $1, %edx
movl %edx, (%eax)
movzbl 8(%eax), %edx
sbbl %ebx, %ebx
andl $3, %ebx
andl $-4, %edx
orl %ebx, %edx
movb %dl, 8(%eax)
popl %ebx
popl %ebp
ret $4
当我将实现更改为:
tset_iter_t tsiterator(tset_t *ts) {
tset_iter_t it;
tree_iter_t i = titerator(ts->tree);
it.iter = i;
return it;
}
没有问题。在第一种情况下,GCC 优化(或分析)了什么,为什么它会给我一个警告?
谢谢。
【问题讨论】:
-
tree_iter_t it;然后it.tree = t;。it.tree在哪里?你为什么不发布真正的代码。 -
nitpick - 通过声明位域“char info : 2;”不会节省任何内存在 tree_iter_t 中。编译器仍然必须为每个实例分配固定数量的字节,因此“info”无论如何都会成为一个完整的字节。
-
另一个 nitpick,默认情况下使用 gcc char 进行签名,因此当您在此代码中分配 it.info=3 时,您实际上将值包装为 -1(我只是试了一下以确保)。如果您打算将其设为位域,则应明确将其设为无符号,否则 (it.info == 3) 之类的代码可能无法像您预期的那样工作。
-
@ryan_s - 我已经更改了字符。祈祷我不要再改变它。
-
我喜欢您对《星球大战》的参考,但恐怕我必须告诉您,该标准只指定了
int(有符号/无符号/wahtever)和_Bool类型的位域。大多数编译器都会很乐意接受unsigned char info :2;,但它在技术上是不标准的。
标签: c gcc compiler-warnings compiler-optimization