Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7319805
  • 博文数量: 159
  • 博客积分: 10424
  • 博客等级: 少将
  • 技术积分: 14605
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-14 12:45
个人简介

啦啦啦~~~

文章分类
文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(10)

2011年(116)

2010年(22)

分类: C/C++

2013-02-25 22:54:35


作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技术群:4367710
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。

==========================================================================================================================================================

今天这个主题很简单。但是这么一个初级问题,居然真正发生在我的周围。


前几天帮助同事检查一个问题。他这样描述该现象:他定义了一个全局变量,然后调用一个函数F修改了该全局变量,但是退出函数时该全局变量的值又被改了。我到他的调试环境中,先查看了一下现象。问题重现了。—— 我一直认为,所有能够重现的问题都不是问题。只要能够重现,就一定可以修正。


对于这一问题,我的第一反应是竞争引起的。于是我首先使用GDB的set scheduler-locking on,保证其它线程处于停止状态,避免竞争。又试了一次,问题还是存在。在函数退出的时候,打印了一下当时的值。然后退出,再次打印,发现其值变为了初始值。感觉确实有点奇怪。于是看了看他的代码,一看该全局变量定义在头文件中static int g_variable = 0。看到这里,尽管我不知道其它代码是怎么写的。我就想到了问题的原因。这里的全局变量g_variable肯定有两份。函数F和调用者一定在不同的文件中,它们都include了这个头文件。结果在函数F中修改了一个g_variable,而调用者中使用和查看的是另一个g_variable。解决方法是,去一个c文件中定义这个全局变量,然后到头文件中声明。


虽然我很快的解决了这个问题。但是我却想,这个问题真的是一个很初级的问题。而我这位同事已经是一名senior的开发人员了。为什么还会犯这种错误呢?这里我对事不对人。主要的原因还是对于编程的基础没有理解。头文件中不要定义全局变量,看似是一条死的规则。其实只要真正领会什么是头文件,头文件是如何include到.c源文件中的。这条规则根本不需要记忆,而是一种理解。这样会自然的就会写出正确的代码,而不会犯这样的错误。


说到这里,简单说一下头文件的知识。头文件的作用,主要是用于声明变量,函数等等,然后可以被多个源文件引用。其实我认为其根本目的,一是为了代码的整齐,更重要是为了消除重复的代码。因为多个源文件都要相同的声明,这时就可以用一行include 头文件来解决。而include,在预编译阶段,实际上是将头文件中所有的代码都插入到include的位置。真正理解了这个过程,肯定不会犯本文中的这个错误。



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

gdmmx2013-07-24 10:29:19

Sorry,没注意到g_variable 有static修饰...
static修饰之后,g_variable在每个include了该头文件的.o内都有一个local symbol(nm相关.o看到的symbol是小写),不对外export。linktime阶段不会造成symbol地址二义性。

nm最终的elf或.so的话,能看到若干个g_variable.xxx的symbol,各自.xxx后缀不同地址不同,数量取决于有多少处include了该头文件的.c。

gdmmx2013-07-24 10:19:27

fera:不要拿洋人的标准来要求中国人啦。我以前碰到过灰常灰常senior的中国工程师居然搞不懂指针,直接给函数传了个栈上没初始化的指针。

另外,头文件里定义全局变量,不加static关键字就会导致链接失败,因为变量重复定义了,链接器在链接时不知道怎么办了。如果在头文件里的全局变量用static修饰,那么在每个编译单元中(即包含了该头文件的源文件)都会有一份自己的拷贝,这个拷贝对于其他的编译单元来说是不可见的,所以根本起不到全局变量的作用。

同意LS。默认的编译参数下,这种情况在ld时是会报multi-definition error的。。没看到代码的情况下无责任猜测,这种code能通过linktime,通常是F函数和全局变量被安排在了不同的.so里,所以各自的链接都能通过,最后在runtime才加载到一起。

补充一下关于Debug手段,gdb里分别在F函数内和函数退出后各自打印一下&g_variable求地址就很明确了。

回复 | 举报

fera2013-03-08 22:38:54

gfree_wind:评论没有通知,挺郁闷的。


如果是junior的,我可以理解。作为senior的,我觉得这样的错误难以接受。

不要拿洋人的标准来要求中国人啦。我以前碰到过灰常灰常senior的中国工程师居然搞不懂指针,直接给函数传了个栈上没初始化的指针。

另外,头文件里定义全局变量,不加static关键字就会导致链接失败,因为变量重复定义了,链接器在链接时不知道怎么办了。如果在头文件里的全局变量用static修饰,那么在每个编译单元中(即包含了该头文件的源文件)都会有一份自己的拷贝,这个拷贝对于其他的编译单元来说是不可见的,所以根本起不到全局变量的作用。

回复 | 举报

gfree_wind2013-02-27 10:22:30

dyli2000:比较同意Bean_lee的看法,好的coder的成长往往经过一个短期突击式的突破后,对于博大精深的东西使用起来难免还会犯错误。接下来就需要长期全面巩固和提升。

如我回答Bean_lee一样。Junior的犯错,没问题。但是有了几年经验的人,还犯这样的错误,就是不应该。

回复 | 举报

gfree_wind2013-02-27 10:21:43

Bean_lee:好的coder都是一步步成长的,我也理解有人犯这种错误。新的博客不能及时收到这种消息,郁闷啊。交互性丧失的太多了。

评论没有通知,挺郁闷的。


如果是junior的,我可以理解。作为senior的,我觉得这样的错误难以接受。

回复 | 举报