Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2313988
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: WINDOWS

2008-02-14 19:04:28

先以一个简单的例子说明问题:
这是VS2008中的native VC+项目, 同样的问题在VS2003上也是一样。
首先,test是个项目名,在磁盘上也有相应的目录,项目中的struct.h 头文件是通过Add/Existing Item...添加的,与test目录平级的一个文件,test目录下另有一个同名的struct.h文件,这两个文件内容差异如下:
上面的是test目录下的文件,下面的则是与test目录平级的文件。由于是在IDE中通过Add/Existing Item...菜单项添加的与test目录平级的struct.h文件,最合乎情理的认识是:认为编译器将理所当然地引用到该文件,则下面的测试语句将输出12(默认情况)
printf("sizeof(Constantly_Changed_Struct) = %d\n", sizeof(Constantly_Changed_Struct) );

但实际上,编译系统输出的却是8, 无论怎么处理对齐和padding, 下面的struct.h中定义的结构也一定会大于8字节。为什么?

考虑到cl.exe的/I 参数,特意手工在项目属性中添加了对上一级目录的引用,如上图所示,然而,编译器对文件中的
#include "struct.h"
这种以双引号包括的头文件处理规则是:先在当前目录,也就是项目test所在的test目录查找文件, 如果找不到该文件, 则在编译器实现所规定的系统目录下查找,在这里,是%INCLUDE%环境变量和/I 命令行选项额外指定的目录,虽然在命令行上指定了 /I "..\\", 但它的处理优化级落在对当前目录处理的后面,一旦在当前目录下找到,对头文件的查找即告成功结束。

现在把当前目录下的struct.h文件改名为new_struct.h, 重新编译系统,可以看到上面的测试语句输出了

这次的的确确找到了与test同级的,在项目中所引用的那个头文件。

由此,VC的项目管理系统可能在这里让你所见非所得,明明添加了指定的头文件,实际编译时却引用的是另一个。

上述错误并非凭空想象,在具体项目中,这样的操作产生了如上的陷阱:
1. 长期维护的一个项目,其中某个文件如struct.h定义了一个结构,该结构的具体定义在维护中经常变动
2. 需要一个小工具,依赖于上述项目,这意味着要引用上述项目中的结构定义。为该工具建立项目目录,并将struct.h文件copy至该目录下使用,随着对小工具项目的开发,主项目也同时在进行中,小工具项目被check in到主CVS系统中,与此同时,某个开发者发动了主项目中的struct.h文件中的某个结构定义。
3. 随后,小工具项目的开发者意识到不应使用复制文件的办法,这样做难以保持两个文件的同步,所以手工删除了项目中的struct.h文件,然后通过Add/Existing Item...添加了主项目中的struct.h文件, 并理所当然地认为该项目将引用主项目中的struct.h文件。
4. 编译出小工具,该小工具将在运行时调用主项目中的某个DLL,该DLL中被调用的函数使用memcpy向小工具程序提供的该结构变量的地址复制另一个结构变量,buffer overrun发生了!原因就是主项目中使用的是最新的结构,而小工具项目改而引用主项目中的struct.h文件之后,在实际编译时,编译器仍然找到当前目录下的陈旧的struct.h文件,两个结构的定义不同,所以出现了内存访问越界。

在实际中碰到此问题时,涉及的目录结构体大而复杂,有40K左右,一开始怀疑在函数中定义如此大的局部变量会耗尽栈空间,所以在该变量之前使用了static关键字,把该变量放到堆上,此时buffer overrun的错误消失了,这误导了我对问题的判断,认为很可能就是栈空间耗尽所致,实际上程序本身并不复杂,没有过深的函数调用, 也没有递归调用,只是笼统地定性判断是栈空间耗尽,实际上,链接器默认给exe程序的栈空间是1M, 对于40K来说还是足够大的。后来通过 /STACK:reserved[,commited] 参数也仍然出现此问题,才发现不应是栈空间太小的原因。

复制, 在所承载的信息实质相同时以两种不同的表现形式,如文本和二进制形式保存,不管是复制源文件内容的代码段,还是大粒度的整个文件的复制,都在实际中导致严重的不一致问题,给系统的维护者强加记忆和同步的负担。

阅读(1894) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~