作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
今天在做代码的enhancement,其中涉及到修改一个结构体,类似于下面的代码:
- struct my_type {
-
unsigned int flag;
-
#define MY_FLAG_1 0x1
-
#define MY_FLAG_2 0x2
-
#define MY_FLAG_3 0x4
-
};
这里struct my_type->flag作为标志位,通过宏来定义不同的标志。其实我觉得这样的代码是可以接受的。但是我们的工程代码中,更多的是应用位域来表示标志位。为了风格的统一,我决定修改这个结构体,改为:
- struct my_type {
-
unsigned int flag1:1,
-
flag2:1,
-
flag3:1,
-
spare:29;
-
};
然后再修改所有使用原标志位的地方。位域确实会比使用标志位更清晰,但是会有如下的老代码:
- struct my_type t1, t2;
-
t1.flag = t2.flag
原来仅仅是对一个变量struct my_type->flag进行赋值。现在使用位域,代码就要写为
- struct my_type t1, t2;
-
-
t1.flag1 = t2.flag1;
-
t2.flag2 = t2.flag2;
-
t3.flag3 = t2.flag3;
比以前使用一个flag变量要麻烦冗余的多——这里不适用memcpy,只是为了复制标志位。这样的代码还有不少地方。改为位域,代码的清晰度和可读性是比以前好了,但是这样的代码看上去比以前麻烦的多。
新的结构体定义如下:
- struct my_type {
-
union {
-
unsigned int flag1:1,
-
flag2:1,
-
flag3:1,
-
spare:33;
-
unsigned int all_flags;
-
};
-
};
-
-
-
struct my_type t1, t2;
-
-
t1.all_flags = t2.all_flags;
我的意图很明显,通过使用union,在不增加存储空间的条件下,既可以直接使用flag1,flag2和flag3,而当复制标志位时,通过共享的存储空间,可以直接复制all_flags来复制所有的标志位。
Ok。开始测试自己的修改,发现程序运行有问题——聪明的朋友应该已经知道问题是什么了。
下面是一个示例程序:
- #include <stdlib.h>
-
#include <stdio.h>
-
#include <string.h>
-
-
struct my_type {
-
union {
-
unsigned int flag1:1,
-
flag2:1,
-
flag3:1,
-
spare:29;
-
unsigned int all_flags;
-
};
-
};
-
-
int main(void)
-
{
-
struct my_type t;
-
-
memset(&t, 0, sizeof(t));
-
t.flag1 = 1;
-
-
printf("%d %d %d\n", t.flag1, t.flag2, t.flag3);
-
-
return 0;
-
-
}
编译输出:
- [fgao@fgao-vm-fc13 test]$ ./a.out
-
1 1 1
看到程序的输出,我就知道自己想的过于草率了。当时认为flag1,flag2,flag3及spare是属于一个unsigned int的不同存储位置,可以和后面的my_flags共享一个unsigned int的存储空间。这里绝对的想岔了。
还是想引用一下C99标准关于union的定义:
—A union type describes an overlapping nonempty set of member objects, each of
which has an optionally specified name and possibly distinct type.
通过union的定义,可以清楚的看到,union针对的是member objects。只要是union的成员,都是共享一个重叠的存储空间。那么对于上面的例子来说,flag1,flag2,flag3和spare都是从union的一个unsigned int的最低位开始存储——小端从最低位开始,大端从最高位开始。也就是说flag1,flag2和flag3共享了最低位。
问题是搞清楚了,但是对自己却是一个提醒。如果说给我写出这些代码,让我review的话,我可能会立刻看出问题。但是这些代码是我自己写出的时候,却会写出这样的代码。看来,还是对union的概念不够清楚。
最后,如果想改正这个问题的话,也比较简单。
- #include <stdlib.h>
-
#include <stdio.h>
-
#include <string.h>
-
-
struct my_type {
-
union {
-
struct {
-
unsigned int flag1:1,
-
flag2:1,
-
flag3:1,
-
spare:29;
-
};
-
unsigned int all_flags;
-
};
-
};
-
-
int main(void)
-
{
-
struct my_type t;
-
-
memset(&t, 0, sizeof(t));
-
t.flag1 = 1;
-
-
printf("%d %d %d 0x%X\n", t.flag1, t.flag2, t.flag3, t.all_flags);
-
-
return 0;
-
-
}
再用一个独立的匿名struct封装flag1,flag2,flag3和spare。这样对于union来说,将把这一struct视为它的成员变量,就不会出现上面的问题了。但是这样的定义,看上去就有些奇怪了。是否选择这种方法,就是仁者见仁智者见智了。我是没有使用这种方法,而是老老实实地的复制flag1,flag2和flag3。
注:这里的代码union和struct是应用了GNU的扩展特性匿名的union和struct。
阅读(283) | 评论(0) | 转发(0) |