我在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
时,我会在return语句中收到警告:warning: ‘it.iter.tree’ is used uninitialized in this function
。为什么海湾合作委员会有这个问题?我错过了一些明显的东西吗它看起来初始化给我。我跑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-ofh汇编行(只是调用树函数的常用函数调用开销)。 gcc -O2 -S -combine tset.c tree.c
给了我警告,内联的titerator,并产生了这个:
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优化(或分析)是什么,为什么它会给我一个警告?
感谢。
答案 0 :(得分:1)
我认为警告是一个错误。你使用哪个gcc?当我使用gcc 4.0和4.2编译(无可否认是单个文件)时,我不会发生这种情况。
以下是优化汇编程序的注释版本。我在这里看不到任何未分配的东西,这就是为什么我认为警告不对。我已经猜到了树的结构。
tsiterator:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %edx ; edx has pointer to ts
pushl %ebx
movl 8(%ebp), %eax ; eax has pointer to retval
movl (%edx), %ecx ; ecx has ts->tree (?)
movl 4(%ecx), %edx ; edx has ts->tree->min (?)
movl %ecx, 4(%eax) ; store ts->tree into retval->iter->tree
cmpl $1, %edx
movl %edx, (%eax) ; store ts->tree->min into retval->iter->current
;; This and the cmpl instruction above is all
;; handling the bitmasking for retval->iter->info.
;; Probably would be more efficient to not use a
;; bit mask here, as the compiler could remove the
;; second "and" and "or" instructions.
movzbl 8(%eax), %edx ; get current value of retval->iter->info
sbbl %ebx, %ebx ; ebx = 0 if unsigned edx < 1, else -1
andl $3, %ebx ; mask off new value
andl $-4, %edx ; mask off old value
orl %ebx, %edx ; combine old | new
movb %dl, 8(%eax) ; store combined into retval->iter->info
popl %ebx
popl %ebp
ret $4
编辑:请注意,编译器会仔细保留tree_iter_t.info
中随机未初始化垃圾的高6位。
答案 1 :(得分:0)
我不明白为什么gcc可能会抱怨那个特定的字段tree
,但他是对的,你从函数返回的结构部分没有被初始化。事实上,您应该基本上只初始化变量的一个字段,然后使用0
自动初始化其他字段。在您的情况下,最简单的方法是使用C99的指定初始值设定项:
tree_iter_t it = { .tree = t };
然后您也可以在之后跳过该字段的分配。