bug前提条件
当模块比较多,头文件较多,某个结构体类型会在当前模块中重新声明进而引用其成员,而不直接包含其他模块的头文件。这样的好处是不引入不需要的类型声明到此模块,头文件包含的交叉;坏处是,增加了bug的几率,耦合太大!比如下面一种情况发生而导致bug:
已知两个模块A和B,同一个结构类型struct node在两个模块中分别声明,其中B模块无意或者有意调整了结构类型中的某些域。那么这个时候,若B模块中引用A模块中此类型实例然后访问成员变量,就会引发bug!如下重现示例
bug重现示例代码
moduleA.c
#include <stdio.h>
struct node{
char *name;
void *data;
};
//定义测试变量
struct node new = {
"TuoLuoFuSiJi",
NULL
};
//测试输出
extern test_output(void);
void output(void)
{
printf("%s\n", new.name);
}
int main(int argc, char *argv[])
{
printf("call output:\n");
output();
printf("call test_output:\n");
test_output();
return 0;
}
moudleB.c
#include <stdio.h>
//这里重新声明,调整域的顺序
struct node{
void *data;
char *name;
};
//引用moduleA中的new实例
extern struct node new;
void test_output(void)
{
if(new.name)
printf("%s\n", new.name);
else
printf("name is null\n");
}
Makefile
run: moduleA.o moduleB.o
cc -o run moduleA.o moduleB.o
执行结果:
结果分析:
在模块B中,对name的访问偏移量计算是依据本模块中的声明决定的。name的偏移量是4!同理,在模块A中,name的偏移量是0.在模块B中访问模块A中实例new时,
按照偏移量4计算,得到的确是data的值,显然为NULL。
解决办法:
建议办法
1-往上提升;公共的类型声明,定义成一个全局性的头文件,多个模块共同使用,而不隶属与任何一方,避免交织
2-合作协商;两个模块的绝对一致。即一方改变(比如增删成员,或者改域名),必须通知另一方
3-模块接口化;如果外面模块需要访问本模块的实例,那么本模块应该提供接口,而不是让其他模块直接访问自己模块的实例!这样是最理想的。也符合高内聚,低耦合原则。
个人觉得(3)是最理想的解决办法。