Chinaunix

标题: 为什么free或者delete的时候不直接将指针置成NULL? [打印本页]

作者: slow_hand    时间: 2009-11-26 10:20
标题: 为什么free或者delete的时候不直接将指针置成NULL?
为什么语言本身不在free或者delete的时候直接将指针置成NULL?这样不就完全杜绝了“野指针”的问题吗?
作者: flw    时间: 2009-11-26 10:27
因为你选择了这样的语言。
作者: slow_hand    时间: 2009-11-26 10:31
原帖由 flw 于 2009-11-26 10:27 发表
因为你选择了这样的语言。

呵呵,这回答。。。
制定C,C++语言标准的时候,那些专家也应该考虑到了这个问题吧
作者: flw    时间: 2009-11-26 10:43
原帖由 slow_hand 于 2009-11-26 10:31 发表

呵呵,这回答。。。
制定C,C++语言标准的时候,那些专家也应该考虑到了这个问题吧

因为在这个星球上不止有一种语言。
有些语言的标准的指定的时候,那些专家们做了另一个选择。
这不是平行宇宙,你可以随意选择。
作者: bruceteen    时间: 2009-11-26 10:46
老实说,可能你听了之后不舒服。
“free或者delete的时候直接将指针置成NULL”是一种自以为是的暴傻行为,所以C/C++不这么做。
作者: slow_hand    时间: 2009-11-26 10:50
原帖由 flw 于 2009-11-26 10:43 发表

因为在这个星球上不止有一种语言。
有些语言的标准的指定的时候,那些专家们做了另一个选择。
这不是平行宇宙,你可以随意选择。

好的。您的意思是说这是语言缺陷,对吧
作者: slow_hand    时间: 2009-11-26 10:51
原帖由 bruceteen 于 2009-11-26 10:46 发表
老实说,可能你听了之后不舒服。
“free或者delete的时候直接将指针置成NULL”是一种自以为是的暴傻行为,所以C/C++不这么做。

您能指出原因来吗?
作者: llry    时间: 2009-11-26 10:52
标题: 回复 #5 bruceteen 的帖子
为什么呢?
作者: flw    时间: 2009-11-26 10:54
原帖由 slow_hand 于 2009-11-26 10:50 发表

好的。您的意思是说这是语言缺陷,对吧

选择,不是缺陷
作者: zhoubug    时间: 2009-11-26 11:04
spirit of c:
1,Trust the programmer.
2,Don't prevent the programmer from doing what needs to be done.
3,Keep the language small and simple.
4,Provide only one way to do an operation.
5,Make it fast, even if it is not guaranteed to be portable.

不同的程序员会期待语言帮他们做更多的事情,对于这个问题 这几条设计精髓可以很好的回答。
另外一个直接的回答就是 NULL 是什么,或者说这个平台有没有NULL,我不知道语言标准
是不是必需要关心亦或是平台相关。

[ 本帖最后由 zhoubug 于 2009-11-26 11:06 编辑 ]
作者: c/unix    时间: 2009-11-26 11:05
提示: 作者被禁止或删除 内容自动屏蔽
作者: flw    时间: 2009-11-26 11:07
标题: 回复 #10 zhoubug 的帖子
每个平台必须要有 NULL,但位模式不必相同。
作者: slow_hand    时间: 2009-11-26 11:12
原帖由 zhoubug 于 2009-11-26 11:04 发表
spirit of c:
1,Trust the programmer.
2,Don't prevent the programmer from doing what needs to be done.
3,Keep the language small and simple.
4,Provide only one way to do an operation.
5,Make  ...

请问您在平时编程的时候,free或者delete之后是不是立即将指针置成NULL,或者类似的值?
如果是的话,完全可以把它修改到标准中去,对吧?
作者: zhoubug    时间: 2009-11-26 11:12
原帖由 flw 于 2009-11-26 11:07 发表
每个平台必须要有 NULL,但位模式不必相同。

俄,那就是说NULL是什么并不确定。
作者: slow_hand    时间: 2009-11-26 11:14
原帖由 c/unix 于 2009-11-26 11:05 发表
杜绝了野指针,但是造成了内存泄露。

Java的内存机制适合楼主的需求。

内存泄露?
作者: zhoubug    时间: 2009-11-26 11:14
原帖由 slow_hand 于 2009-11-26 11:12 发表

请问您在平时编程的时候,free或者delete之后是不是立即将指针置成NULL,或者类似的值?
如果是的话,完全可以把它修改到标准中去,对吧?

你也有说或者类似的值,那你想让标准怎么做。
作者: flw    时间: 2009-11-26 11:15
原帖由 slow_hand 于 2009-11-26 11:12 发表

请问您在平时编程的时候,free或者delete之后是不是立即将指针置成NULL,或者类似的值?
如果是的话,完全可以把它修改到标准中去,对吧?

我从不认为赋值为 NULL 和 free 是需要联动的。
该赋值为 NULL 的时候,我自然会赋值为 NULL,而不必在乎它是否刚刚 free 过。
作者: emacsnw    时间: 2009-11-26 11:21
free函数和operator delete函数的参数就是void*,不是void**吧。
作者: slow_hand    时间: 2009-11-26 11:22
原帖由 flw 于 2009-11-26 11:15 发表

我从不认为赋值为 NULL 和 free 是需要联动的。
该赋值为 NULL 的时候,我自然会赋值为 NULL,而不必在乎它是否刚刚 free 过。

我明白您的意思。
但是反过来是一定成立的,对吧。free完要立刻置成NULL。
虽然各个平台不一样,但总可以置成NULL。对吧
作者: flw    时间: 2009-11-26 11:26
原帖由 slow_hand 于 2009-11-26 11:22 发表

我明白您的意思。
但是反过来是一定成立的,对吧。free完要立刻置成NULL。
虽然各个平台不一样,但总可以置成NULL。对吧

你逻辑没学好。
把我的回复重读二十遍,仔细把玩一下。

[ 本帖最后由 flw 于 2009-11-26 11:28 编辑 ]
作者: 也是菜鸟    时间: 2009-11-26 12:08
是可以这样做,但不符合逻辑。

释放就是释放,赋值就使赋值

为贪一时之利而把逻辑弄混乱,从长远来看,没一点好处

[ 本帖最后由 也是菜鸟 于 2009-11-26 12:09 编辑 ]
作者: maxxfire    时间: 2009-11-26 12:14
原帖由 emacsnw 于 2009-11-26 11:21 发表
free函数和operator delete函数的参数就是void*,不是void**吧。


有道理。。
而且这样既可以给开发人员自己来处理,从而增加语言的自由度。另外,也可以减少一步操作,增加速度。。
作者: slow_hand    时间: 2009-11-26 12:36
原帖由 flw 于 2009-11-26 11:26 发表

你逻辑没学好。
把我的回复重读二十遍,仔细把玩一下。

我认为我理解了您的意思。
作者: slow_hand    时间: 2009-11-26 12:38
原帖由 也是菜鸟 于 2009-11-26 12:08 发表
是可以这样做,但不符合逻辑。

释放就是释放,赋值就使赋值

为贪一时之利而把逻辑弄混乱,从长远来看,没一点好处


如果把释放内存和对指针的赋值写在一起,就没有所谓的“野指针”的问题了,对吧?
作者: slow_hand    时间: 2009-11-26 12:39
原帖由 maxxfire 于 2009-11-26 12:14 发表


有道理。。
而且这样既可以给开发人员自己来处理,从而增加语言的自由度。另外,也可以减少一步操作,增加速度。。

自己来处理,不还是要把指针赋值成NULL?对吧?不会减少操作吧,只是把它移到函数外面来了。
作者: peidright    时间: 2009-11-26 12:44
debug的时候,你可以每次free()后,再p = NULL;这样有利于调试错误。
我也不知道每次free后,p的值是否会改变? 如果按照c语言的想法,应该p的值是不变的。
作者: emacsnw    时间: 2009-11-26 12:48
原帖由 slow_hand 于 2009-11-25 20:38 发表


如果把释放内存和对指针的赋值写在一起,就没有所谓的“野指针”的问题了,对吧?


int *p = new int;
int *q = p;
delete p;
p = NULL;
这时候q还是野指针吧?
作者: slow_hand    时间: 2009-11-26 12:51
原帖由 emacsnw 于 2009-11-26 12:48 发表


int *p = new int;
int *q = p;
delete p;
p = NULL;
这时候q还是野指针吧?

既然有了p,为什么还要一个q呢?
作者: emacsnw    时间: 2009-11-26 13:07
原帖由 slow_hand 于 2009-11-25 20:51 发表

既然有了p,为什么还要一个q呢?


这个p可能在某个比较复杂对象里面,该对象是某个容器的一个成员,这个对象会自动析构自身的指针成员的那种,那个容器出了scope后就会让q成为野指针,这个和要不要delete后置0没有关系,无法避免的,必须要程序员负责内存的管理。

你的想法太天真了,以为delete后把指针设为0就不会有野指针的问题。。。
作者: slow_hand    时间: 2009-11-26 13:21
原帖由 emacsnw 于 2009-11-26 13:07 发表


这个p可能在某个比较复杂对象里面,该对象是某个容器的一个成员,这个对象会自动析构自身的指针成员的那种,那个容器出了scope后就会让q成为野指针,这个和要不要delete后置0没有关系,无法避免的,必须要程 ...

嗯,这种把指针赋来赋去的情况确实不能避免野指针的问题。那是您自己用错了。
作者: langue    时间: 2009-11-26 13:33
我同样不认为 free() 之后应该自动置空,程序员应该有自己的选择,而不是简单、粗暴地 do it for you。
作者: benjiam    时间: 2009-11-26 13:40
我所有的代码在 free 以后都会设置成为NULL.
为下次扑捉 bug 提供条件。这是很个很普遍的做法。
而且我也能找到freen 以后设置为NULL 的很多优点, 简单的说 多线程的代码,当你发现一个不该出现 NULL指针的地方
出现了 你就可以找找是不是别的地方已经释放了。
当然你如果一定要在c++ 里面制作所谓的泄露,这个不是这样的写法能解决的问题。

如果你动则以 设计才是解决这类问题的终极方法, 我同意。但是我相信 大多数时候我们是看到了窟窿再开始找原因的。

还有这里很多人动则以性能作为借口,我想写程序最关键的一条是正确 而不是性能。
作者: slow_hand    时间: 2009-11-26 14:01
原帖由 benjiam 于 2009-11-26 13:40 发表
我所有的代码在 free 以后都会设置成为NULL.
为下次扑捉 bug 提供条件。这是很个很普遍的做法。
而且我也能找到freen 以后设置为NULL 的很多优点, 简单的说 多线程的代码,当你发现一个不该出现 NULL指针的地 ...

如果把赋值操作写到free里面,效率不会有啥影响吧?而且只有一份赋值操作的二进制拷贝,还可以节省空间。
作者: flw    时间: 2009-11-26 14:09
原帖由 slow_hand 于 2009-11-26 12:51 发表

既然有了p,为什么还要一个q呢?

因为指针支持赋值运算。
话说如果不支持赋值运算的话,还要指针干什么呢。
作者: slow_hand    时间: 2009-11-26 14:18
原帖由 flw 于 2009-11-26 14:09 发表

因为指针支持赋值运算。
话说如果不支持赋值运算的话,还要指针干什么呢。

当然可以赋给别的指针变量了。
那样的话,就是程序员自己不小心造成的问题。浅拷贝就是一个例子吧。
作者: peidright    时间: 2009-11-26 14:26

free的实现,跟c语言的设计,感觉没啥关系。没那么崇高。
设置为空,或者不空,都没啥讨论的意义。你认为应该设计为空,你可以再封装一次,
如果free设计为空,你看得不爽,则可以修改free的实现,写个不设置为空的代码。

无论如何,正确的代码,不应该因为上面的区别,而产生bug,一般,设置为NULL,能够让你更容易定位BUG,
但是对于某些容错的程序,如果检测指针为空,就采取其他的行为,也可能因此,而产生其他的隐藏的BUG.
作者: koolcoy    时间: 2009-11-26 15:01
我认为free后自动置成NULL没太大的问题。
持反对意见的给个例子说明一下置成NULL有不好的副作用。不要拿哲学说事,我是程序员不是哲学家

btw:不要认为C的全部都是好的
作者: wishel    时间: 2009-11-26 15:21
原帖由 koolcoy 于 2009-11-26 15:01 发表
不要拿哲学说事,我是程序员不是哲学家  


可是你在讨论语言设计的问题,而语言设计必须有明确的哲学。
作者: zhoubug    时间: 2009-11-26 16:02
原帖由 koolcoy 于 2009-11-26 15:01 发表
我认为free后自动置成NULL没太大的问题。
持反对意见的给个例子说明一下置成NULL有不好的副作用。不要拿哲学说事,我是程序员不是哲学家

btw:不要认为C的全部都是好的


你是想每次调用free这样 char *ptr ....free(&ptr);?
否则你想让语言设计怎么处理?改变它的函数调用时的传值规则?

[ 本帖最后由 zhoubug 于 2009-11-26 16:10 编辑 ]
作者: shan_ghost    时间: 2009-11-26 16:05
1、设计语言和设计库接口,想要好学好用并且稳定,就要遵循“完整且最小”原则

完整就是功能完整;最小就是没有任何一个功能和其他功能重复或可以通过组合其他几个功能拼凑出来。


2、C语言功能已经完整。现在再增加任何东西,都是在扩大已有接口。

3、扩大已有接口,实际上就是提供新的“语法糖”;那么如果没有很大好处,就不应该破坏接口的“完整且最小”基本原则。

4、包括malloc/free都不是C的关键字,何况free后置NULL

5、如前面网友所述,free后置NULL并不能保证所有指向这块内存区域的指针都被置为NULL

6、那么,free后,哪些必须手工置NULL,哪些不需要手工置NULL?或者,干脆不管系统有无置NULL,全都自己再做一遍?
显然,free后置NULL不光没有提供多少方便,反而因为无法保证一致性而制造出另外一种困扰。
这只能给初学者一种虚假的安全感或者更多的困扰。
作者: slow_hand    时间: 2009-11-26 16:38
原帖由 zhoubug 于 2009-11-26 16:02 发表


你是想每次调用free这样 char *ptr ....free(&ptr);?
否则你想让语言设计怎么处理?改变它的函数调用时的传值规则?

您说的这个问题确实存在。但是如果一开始free被设计成自动给指针赋值NULL的话,可能比现在的更好一些。这只是我个人看法,呵呵。
作者: pagx    时间: 2009-11-26 17:23
NULL 真正的含义是pointer to null object, 所以并非任何情况下都等于 0, 对于底层来说, 0 某些情况下确实是个有效的地址。所以free自动设置为0是有副作用的。
所以只有 pointer to null object 与 0 是等同的时候才应该这么做。
作者: slow_hand    时间: 2009-11-26 17:32
原帖由 pagx 于 2009-11-26 17:23 发表
NULL 真正的含义是pointer to null object, 所以并非任何情况下都等于 0, 对于底层来说, 0 某些情况下确实是个有效的地址。所以free自动设置为0是有副作用的。
所以只有 pointer to null object 与 0 是等同 ...

但是用NULL就保证没错。
作者: slow_hand    时间: 2009-11-26 17:38
原帖由 shan_ghost 于 2009-11-26 16:05 发表
1、设计语言和设计库接口,想要好学好用并且稳定,就要遵循“完整且最小”原则

完整就是功能完整;最小就是没有任何一个功能和其他功能重复或可以通过组合其他几个功能拼凑出来。


2、C语言功能已经完整 ...

那为啥有了strcpy还要strncpy?
作者: langue    时间: 2009-11-26 17:43
原帖由 slow_hand 于 2009-11-26 17:32 发表

但是用NULL就保证没错。


大神啊,求您了,用 C# 或者 Java 或者脚本语言吧……
作者: ruifengzhangyi    时间: 2009-11-26 17:53
设计的时候,专家认为free掉的指针大家肯定不会用,没想到,居然还有人用。

专家想,服了你们了,free了还用,这样吧,咱们来定个规矩:free掉之后必须置为空,这样就可以用了。

省的老有人说,为啥free掉的指针就不能用了,不是不能用,是先置为空再用。
作者: peidright    时间: 2009-11-26 18:01

1.请大家不要把free的实现和c语言的设计拉扯到一起,多么哦戳的行为!
2.all is ok!
作者: shan_ghost    时间: 2009-11-26 18:10
原帖由 slow_hand 于 2009-11-26 17:38 发表

那为啥有了strcpy还要strncpy?


因为strcpy可能写越界,而strncpy不会。

另外,strncpy要同时检查\0和已拷贝字符数两个条件,而strcpy只需要检查\0即可——注意这个检查是每拷贝一个字符都要进行的——所以大量拷贝数据时strcpy更快一些。


另外,软件开发是一个新兴的科目。比如C语言库就喜欢多次利用库函数的返回值: 返回-1就是失败,返回其他值,则这个值可能就是个指向有效数据的指针。

这个设计思想在早期内存受限、速度较慢的系统上或许是必要的;但现在,新的设计标准建议严格区分返回值的作用——比如用且只用返回值表示执行是否成功,真正的返回数据则要通过引用型参数取得;甚至更激进的、要求发生任何错误都抛异常,以免程序员偷懒忽略返回值检查的思路都有。


所以,语言设计由于图灵的功劳,可以说是一个已经解决或有明确解决方案的问题;而库的设计则因现实情况而多变。

所以,接口设计“完整且最小”是一个基本标准,但这个标准并不阻止提供额外的、更方便的接口;只是警告设计者,在添加这类看似很美丽的接口之前一定要三思。
作者: pagx    时间: 2009-11-26 18:18
原帖由 slow_hand 于 2009-11-26 17:32 发表

但是用NULL就保证没错。


的确是不错, 但是编译器并没法知道 NULL 真正的值,或许并不存在, 要知道指针只是一个内存地址值而已!  让机器去处理不确定的事情,那是强机器所难。
作者: flw    时间: 2009-11-26 18:26
原帖由 pagx 于 2009-11-26 18:18 发表

但是编译器并没法知道 NULL 真正的值,或许并不存在


作者: slow_hand    时间: 2009-11-26 19:05
原帖由 langue 于 2009-11-26 17:43 发表


大神啊,求您了,用 C# 或者 Java 或者脚本语言吧……

呵呵,我只是说说自己的想法而已嘛
作者: slow_hand    时间: 2009-11-26 19:11
原帖由 shan_ghost 于 2009-11-26 18:10 发表


因为strcpy可能写越界,而strncpy不会。

另外,strncpy要同时检查\0和已拷贝字符数两个条件,而strcpy只需要检查\0即可——注意这个检查是每拷贝一个字符都要进行的——所以大量拷贝数据时strcpy更快一些 ...

虽然完整,但比较松散,也不好用。可以弄的更紧致点。对吧?
作者: ttcn_cu    时间: 2009-11-27 01:28
楼主其实需要的不是修改free函数的实现,而是在您的特定的工程里,可以检查出是否所有所有free后面都有置null。
那么可以用一个perl脚本做全局检查,编译前扫描一遍(所谓的代码走查)。

要不要修改free是一个哲学问题(这里不可能讨论出结果),而lz需要解决的其实只是一个工程问题,两者不矛盾。
作者: sinall    时间: 2009-11-27 09:10
提示: 作者被禁止或删除 内容自动屏蔽
作者: zhaohongjian000    时间: 2009-11-27 09:49
呢滴神啊,lz要不自己实现一套逻辑引用系统吧
作者: billypeng    时间: 2009-11-27 10:50
free以后设置指针为NULL,对于操作系统这类软件来说,是浪费CPU资源。因为只有在接下来要判断该指针是否为NULL时,这个设置才有必要。

在很多情况下,程序员,特别是优秀的程序员心里非常清楚该指针是否有效。

而且由于c语言中强大的宏功能,把指针设置为空非常简单。如果在函数中设置为空了,很多时候CPU资源是白白浪费了。这对系统级程序是不可忍受的。
作者: slow_hand    时间: 2009-11-27 10:58
原帖由 ttcn_cu 于 2009-11-27 01:28 发表
楼主其实需要的不是修改free函数的实现,而是在您的特定的工程里,可以检查出是否所有所有free后面都有置null。
那么可以用一个perl脚本做全局检查,编译前扫描一遍(所谓的代码走查)。

要不要修改free是一 ...

这种代码走查的方式不太靠得住吧?因为指针可以赋来赋去。
作者: slow_hand    时间: 2009-11-27 10:59
原帖由 sinall 于 2009-11-27 09:10 发表
楼主,想必经济学十大原理可以回答您的问题。
“人们面临权衡取舍”
关于作出决策的第一课可以归纳为一句谚语:“天下没有白吃的午餐。”为了得到我们喜爱的一件东西,通常就不得不放弃另一件我们喜爱的东西。作出决策要求我们在一个目标与另一个目标之间有所取舍。

也可以从哲学的角度看看这个问题。
“矛盾时时处处存在”
矛盾中的双方,对立、斗争、作用、转换。
这就是为什么后来的Java、C#之类的语言解决了你的问题。

是的,经济学和哲学感觉是万金油啊。呵呵
作者: srdgame    时间: 2009-11-27 11:01
KISS
作者: ttcn_cu    时间: 2009-11-27 11:32
标题: 回复 #57 slow_hand 的帖子
恩,我先承认我不是一个C程序员,所有我未必能很深刻的理解为什么要把指针赋来赋去,如果工程里到处都这样的话,可读性和可维护性就要让人头疼,此外Coder肆意妄为的乱用指针的话,Architect 允不允许又是另外一回事情了.

要做大一个工程,要考虑的地方方面面有很多,有许多简历工程的规则大家一定得follow,而不应该从改变语言来出发,毕竟如你所说,即便你改了free后面的指针,如何保证其他reference的指针也被修改了,潜伏的的问题还存在.要解决你的问题,有两个方向,1 保留c,但你要处处小心,不应该乱用指针,保证每个指针都是有明确的目的,明确的生存周期,经得起architect的考验.2 换用c#之类, 因为C#的作者不仅仅考虑到了free后的指针处理,更从语言层面上解决其他指针隐患.
这两种语言都是非常优秀的

btw,应该感谢生产力的发展,感谢摩尔定律,cpu的飞速发展让非c语言的效率急速提升,使得你有选择其他语言的空间

[ 本帖最后由 ttcn_cu 于 2009-11-27 11:36 编辑 ]
作者: hanxin83    时间: 2009-11-27 14:27
指针赋来赋去的情况太多见了, 遍历稍复杂一点的数据结构, 基本上是都会出现的.
指望库函数帮你置空是不现实的~~~~有指针的地方就有野指针, 避免不了.
作者: augustusqing    时间: 2009-11-27 14:50
某门编程语言,体现的是语言设计者的“个人哲学”,不同的语言,体现了不同设计者的“个人哲学”
而C语言设计者的“哲学”,最前面有人已经告诉你了,当然也让我想起我的签名,呵呵哈哈
作者: souldemo    时间: 2009-11-27 15:46
标题: 回复 #1 slow_hand 的帖子
明显懒汉的想法,
java好像有这种机制,
比如文件对象调用close,该对象会成为null
C, C++肯定不会,
这个是由于参数的传递机制决定的,
比如你的Free(void *ptr)
函数内部是没有办法修改ptr的指向的,只能修改其中的值,

对于你这种变态的需求,
建议你定义一个宏,
#define FREE(ptr) do { free(ptr); ptr = NULL;} while(0)
这样应该可以了。
作者: shan_ghost    时间: 2009-11-27 15:54
想全自动的话可以用VB

不过,当年的VB为了初学者方便到处修扶手,后果是混乱到连微软自己都受不了,以至于从VS.NET开始彻底重做,又把扶手全拆了。

我当年维护过十几万行的TCL工程,后果是被这种语言的诸多“便利”恶心到了,以至于自己设计一套库,要把 变量先声明然后才能使用 之类“不方便”的东西找回来。

这种“方便”玩意儿,给初学者写几百上千行的程序确实方便;但拿来写上万行的工程就是恐怖。




一种工具,如果设计给专业人员用,那么就要保证加上的任何东西都要可靠、有效、稳定,并且要便于拆卸和维护。
相反,如果设计给外行用,那么就要尽一切可能给他加上防护栏,防止他碰触到危险的地方,甚至干脆把内部结构封死,做成免维护的,然后再配一张详细的说明书。

很显然,如果你想当专业木匠,那么显然你应该去买“危险”的斧子、锯子,靠自己的手艺做出家具;
否则,你应该买成品家具或者“傻瓜式”“免工具”的散件,按照说明用搭扣把这些东西连接起来。


一般来说,给行家用的工具,应该是很少几个基本元素,然后可以根据这几个元素任意发挥、推导,最终建立起任意复杂的体系——只要你没理解错,那么你的发挥就不会出问题。
这个基本元素的设计,要么有严格理论作为依据;要么有大量实践经验作为后盾,向上添加任何东西都需要慎之又慎——宁可有危险,不可妨碍功能。
相反,给外行用的工具,会是一堆华丽的大杂烩。一切意外都先替你考虑到——宁可减功能,不可有危险。


当然,程序设计这一行过于复杂,为不同目的设计的语言成千上万——那么,当你抱怨一门流行语言时,最好先看看是不是自己选错了工具。
作者: ttcn_cu    时间: 2009-11-27 16:09
标题: 回复 #63 souldemo 的帖子
不考虑lz的偷懒想法是否大家都可以接受.
如果定要一个可实现此功能的函数,应该也可以通过形参为二级指针来实现吧
作者: charming72    时间: 2009-11-27 16:13
原帖由 slow_hand 于 2009-11-26 10:20 发表
为什么语言本身不在free或者delete的时候直接将指针置成NULL?这样不就完全杜绝了“野指针”的问题吗?

我觉得还得从什么是“野指针”讲起。那些危险的操作都是因为指针被释放掉了,而又被调用(野指针)。

对于某些细心的程序员,可能从来就不存在“野指针”,因为他们不会在释放掉后继续使用它们。

所以,设不设成NULL和解决“野指针”的问题不是紧耦合关系
作者: denghaipeng    时间: 2009-11-27 16:28
我觉得楼主需要首先明白的是什么情况下需要使用free
作者: w_anthony    时间: 2009-11-27 16:31
原帖由 souldemo 于 2009-11-27 15:46 发表
对于你这种变态的需求,
建议你定义一个宏,
#define FREE(ptr) do { free(ptr); ptr = NULL;} while(0)
这样应该可以了。


此外如果delete后自动赋NULL的话,delete this就是语法错误了。
作者: ideawu    时间: 2009-11-27 17:48
如果需要free之后, 将指针置为NULL, 那么传给free的参数应该是指向指针的指针, 并且free做改动. 设计这样的API, 显然并不合适, 也很少见. C函数的参数传递是传值型(pass-by-value), 所以无法修改参数的值. 当你执行free(p)之后, p的值是无法在函数中被改变的.

[ 本帖最后由 ideawu 于 2009-11-27 17:49 编辑 ]
作者: pagx    时间: 2009-11-27 18:10
假如支持这样做的话。那么 free, delete 的参数只能是左值。也就是说 delete (char*)0x1235;  或者 free((void*)0x123); 是不合法的, 或者只能是两套不同的方式了。
typedef int * int_ptr;  int_ptr *p = new int_ptr[10]; delete[] &(p[0]);
作者: lemoncookie    时间: 2009-11-27 18:32
标题: 插一腿
不算复杂的一个问题
结果说什么的都有

“野指针”本身就是一个不严格的“野”说法,简单地就认为是指向已经释放的内存空间的指针吧
问题是,当前程序中有多少个指针指向这个释放的空间?(包括不指向头部,指向其中间的)
如果真的要消除“野指针”,就得找出所有的指向这块区域的指针
C/C++并不提供这样的机制(有这样的工作,试图在运行时刻分析一个指针指向的内存地址,但并不完善)
所以把其中一个指针置NULL没有意义
再说置NULL有多大意义?
好void* p = 0;
但我还是硬着头皮用*p = 0;来往里面写,不还是一样出错
所以消除野指针不现实,也没什么大用,还是用指针时要小心检查

[ 本帖最后由 lemoncookie 于 2009-11-27 18:49 编辑 ]
作者: slow_hand    时间: 2009-11-27 20:53
原帖由 shan_ghost 于 2009-11-27 15:54 发表
想全自动的话可以用VB

不过,当年的VB为了初学者方便到处修扶手,后果是混乱到连微软自己都受不了,以至于从VS.NET开始彻底重做,又把扶手全拆了。

我当年维护过十几万行的TCL工程,后果是被这种语言的诸 ...

这位仁兄估计是经过长期在程序设计中的浸润,才有这么多的体会。佩服!
作者: slow_hand    时间: 2009-11-27 21:27
原帖由 lemoncookie 于 2009-11-27 18:32 发表
不算复杂的一个问题
结果说什么的都有

“野指针”本身就是一个不严格的“野”说法,简单地就认为是指向已经释放的内存空间的指针吧
问题是,当前程序中有多少个指针指向这个释放的空间?(包括不指向头部, ...

另外,很多网友提到,因为free函数的值传递,不能实现在free函数内部对指针的NULL赋值。这是因为free的原始版本就没想过要NULL赋值,当然就有这个问题,对吧。
还有我觉得,不能把“野指针”的范围说的那么大。有的时候,虽然你把已经释放了的内存指针赋给了另外一个副本,也没太大关系。因为这个指针只是一个栈上的变量,出了作用域,就引用不到它了。这就是一般的程序错误了。
作者: alphayeah    时间: 2009-11-27 23:01
即使置为NULL,你就能保证下次执行的指针初始化好了吗?
所谓野指针,就是在使用未初始化的指针或指针未指向正确地址上。

NULL和delete、free一点关系都没有,是否置为NULL,很多时候就是用来在下次使用时,作为free还是重新初始化的一个重要手段。

只能说,楼主在编程方面,缺乏经验。
作者: ypyf3000    时间: 2009-11-28 01:55
这个符合“提供机制而非策略”的原则
作者: wylhistory    时间: 2009-11-28 10:37
我认为本质是形参置为NULL后对实参不会有什么影响;
这就好比以下的代码:
char *p1=p;
p1=NULL;
这时候p其实不是NULL;
所以即使设为NULL也毫无作用;
作者: b1uesky    时间: 2009-11-28 22:00
标题: 这个帖子看起来也有火的趋势
原帖由 w_anthony 于 2009-11-27 16:31 发表


此外如果delete后自动赋NULL的话,delete this就是语法错误了。

貌似出现不可预测行为?而不是语法错误?
好久没来CU,6年没碰C/C++,没想到这里还这么热闹,不知道这里的C/C++论坛热闹还是CSDN的C/C++论坛热闹。
作者: youshuang    时间: 2009-11-28 23:04
扯淡的人真多。

18楼已经说的很明白了,但是似乎只有22楼仔细阅读了所有回帖。
历史原因,接口就是那么设计的。
你愿意在释放之后赋值为NULL,自己实现自己的free函数又何尝不可。
作者: zhoulongunix    时间: 2009-11-29 19:20
即使在free或delete的时候将指针置为NULL,也不能解决“野指针”的问题。
因为,在用户的程序里,仍可能有其他的指针指向这个刚free或delete的地址。
作者: jbjbok1    时间: 2009-11-29 20:36
标题: 唉 个水灌得
看看前面几楼的水
废话一堆!
我觉得:
       free 和 delete 是释放空间,即显式的将内存空间返还给OS
这就是为什么如果程序中malloc等后没有free()会导致内存枯竭的原因!

如果直接将free那个指针p改为NULL,于那块内存空间来说,还是没有被释放给操作系统,这块空间其他的程序包括OS还是不能使用;
所谓内存泄露/
作者: yulihua49    时间: 2009-11-29 20:44
原帖由 slow_hand 于 2009-11-26 10:20 发表
为什么语言本身不在free或者delete的时候直接将指针置成NULL?这样不就完全杜绝了“野指针”的问题吗?

因为它无法改变你的指针,它只用了你的指针的内容。如果象你说的那样,应该是:
free(char **p);
不然你就自己写一个吧:
int my_free(char **p)
{
    if(!p || !*p) return -1;
    free(*p);
    *p=0;
   return 0;
}
运用:
char *p=malloc(num);
......

my_free(&p);

[ 本帖最后由 yulihua49 于 2009-11-29 20:46 编辑 ]
作者: yangjiansen    时间: 2009-11-30 10:08
没有认真考虑过这个问题,不过一般我是Free Delete后会设置成NULL的
作者: hacktao    时间: 2009-11-30 13:49
过来学习下!
作者: ilovedrv    时间: 2009-11-30 15:07
看到这贴里面的很多回答, 感觉根本就没有弄懂什么是指针
作者: slow_hand    时间: 2009-11-30 19:12
原帖由 ilovedrv 于 2009-11-30 15:07 发表
看到这贴里面的很多回答, 感觉根本就没有弄懂什么是指针

那这位小哥给讲讲到底啥是指针,指针的本质是什么吧。呵呵。
作者: happy1123    时间: 2009-12-01 10:03
如果C语言这么做了,你可能又会不满意“它凭什么把指针置为NULL,我没有让它这么做”。
C语言只会做你让它做的事情。
作者: 融化的冰山    时间: 2009-12-01 10:10
大家为什么不反过来想一下,free以后直接将指针置成NULL会有什么坏处呢?

函数会这样改变:
free(void *p)---->free(void **p)

我来列举我认为的坏处:

1.增加系统额外开销。
原来在free函数里面,p是在栈里面的数据,在执行到free以后,这个值基本上都是在cache里面进行缓冲;

修改后,访问内存变成二级内存访问,首先访问栈里面的p,然后根据p里面的内容作为地址再访问内存,这部分内存有可能不在cache中,执行完free以后,还需要把这块数据回写到内存。

由于free被执行的非常频繁,所以这些额外的系统开销也是需要考虑的。

2.修改后,增加了free执行出错的可能,因为p有可能指向一个非法地址,所以free函数执行的时候有可能出现非法内存访问错误。

3.由于C里面允许多个指针变量指向同一个地址,对于初学者来说,free以后,那些情况下应该将指针置NULL,那些情况下不用将指针置NULL将会变成一个噩梦。

来这里讨论的大部分都是有经验的程序员,至少对C语言还比较熟悉,觉得改变一下也不是不能适应。但是老师教学生的时候,就会变得非常复杂。

现在,只要说free以后,不会将指针置NULL,需要程序员手工来置NULL。
修改后呢,老师要花费很多时间,一一举例,说明那些情况下,free一块内存以后,不需要将指针置NULL,那些情况下,free以后需要将内存置NULL。

4.即使是有经验的程序员,在使用free的时候也需要非常谨慎。
在稍微复杂一点儿的项目里面,各种数据结构中,都会用到指针变量,偶尔也会出现多个指针指向同一个内存地址的情况,而且为了简化代码,很多时候会用一个指针变量来代替直接使用一个结构的指针,例如
struct CC c = b->a;
然后使用c变量来完成各个操作,在释放的时候,也是free(c);b->a=NULL;
如果修改以后,每次free的时候,同样要考虑很多事情,free里面的参数要使用那个变量,还有没有其他变量指向这个地址,需要在free的时候置NULL。

至少我认为现在的方式,我有free以后,会考虑一下那些值需要设置NULL的习惯。如果大部分时间不需要设置NULL,只有在极少部分的情况下需要设置NULL,我估计不会养成free的时候思考这些问题的习惯。

5.free的参数是一个值,在写程序的时候,如果传入一个没有初始化的值,编译器会报出警告。如果修改以后,传入的是一个地址,编译器是不会有警告,增加了程序潜在的出错几率。


我能想到的free直接置NULL的坏处有上面这些,希望大家能够把自己想到的坏处一一列举,然后和修改后的好处也一一列举,进行一个比较,相信会对这个问题有一个更加合理的看法。
作者: slow_hand    时间: 2009-12-01 20:01
1.增加系统额外开销。
原来在free函数里面,p是在栈里面的数据,在执行到free以后,这个值基本上都是在cache里面进行缓冲;

修改后,访问内存变成二级内存访问,首先访问栈里面的p,然后根据p里面的内容作为地址再访问内存,这部分内存有可能不在cache中,执行完free以后,还需要把这块数据回写到内存。

由于free被执行的非常频繁,所以这些额外的系统开销也是需要考虑的。

不知道一级访问和二级访问的开销差别有多大。但如果程序中大量使用动态分配内存的话,应该确实要考虑到这方面的影响。

2.修改后,增加了free执行出错的可能,因为p有可能指向一个非法地址,所以free函数执行的时候有可能出现非法内存访问错误。

p是一级和二级的都有可能是非法地址,对吧

3.由于C里面允许多个指针变量指向同一个地址,对于初学者来说,free以后,那些情况下应该将指针置NULL,那些情况下不用将指针置NULL将会变成一个噩梦。

来这里讨论的大部分都是有经验的程序员,至少对C语言还比较熟悉,觉得改变一下也不是不能适应。但是老师教学生的时候,就会变得非常复杂。

现在,只要说free以后,不会将指针置NULL,需要程序员手工来置NULL。
修改后呢,老师要花费很多时间,一一举例,说明那些情况下,free一块内存以后,不需要将指针置NULL,那些情况下,free以后需要将内存置NULL。

一般指针都是声明的时候,立马初始化为NULL,然后该怎么就怎么用,用完后不一定要赋值成NULL吧。

4.即使是有经验的程序员,在使用free的时候也需要非常谨慎。
在稍微复杂一点儿的项目里面,各种数据结构中,都会用到指针变量,偶尔也会出现多个指针指向同一个内存地址的情况,而且为了简化代码,很多时候会用一个指针变量来代替直接使用一个结构的指针,例如
struct CC c = b->a;
然后使用c变量来完成各个操作,在释放的时候,也是free(c);b->a=NULL;
如果修改以后,每次free的时候,同样要考虑很多事情,free里面的参数要使用那个变量,还有没有其他变量指向这个地址,需要在free的时候置NULL。

至少我认为现在的方式,我有free以后,会考虑一下那些值需要设置NULL的习惯。如果大部分时间不需要设置NULL,只有在极少部分的情况下需要设置NULL,我估计不会养成free的时候思考这些问题的习惯。

我现在觉得free的时候把指针赋值成NULL真的没有太大意义,因为赋值成NULL,为了是怕访问已释放了的堆内存,但如果你不去用的话(或者说不错用的话),即使不赋值成NULL,也没问题。赋值成NULL只能说是一种好的编码习惯。
另外struct CC c = b->a; 应该是struct CC *c = b->a;吧?

[ 本帖最后由 slow_hand 于 2009-12-1 20:17 编辑 ]
作者: ypyf3000    时间: 2009-12-01 20:29
本质就是无论是否赋值NULL,都不安全
作者: cjaizss    时间: 2009-12-02 00:09
凭什么要如此设计呢?
另外,先不说别的,即使从
void free(void*p);
这个函数的声明来看,也无法实现你所需要的设计.
作者: langue    时间: 2009-12-02 00:52
这个世界是无情的。

设计成不置空,有人骂。
设计成直接置空,还是有人骂。
设计者大吼一声:烦死了!你们爱怎么玩怎么玩去!
作者: shan_ghost    时间: 2009-12-02 10:18
标题: 回复 #91 langue 的帖子
大局利益和眼前利益的冲突而已。


对程序员:

1、指针无效后是否应该统统置为NULL

眼前利益: 很麻烦,而且无效指针根本就不应该再用到,弄个空值,然后遇到空怎么怎么,这种做法只会隐藏问题。

长远利益: NULL明确标识了某个指针是无效的。如果遇到不应该出现的空指针就立刻退出、assert或抛异常,那么就可以迅速发现并修改“使用无效指针问题”——因为指针设为NULL,结果导致Bug被隐藏,只能说你们短视,没有严格检查逻辑上不应出现的空指针。

于是,另一个眼前利益: 使用指针总是检查是否为NULL导致代码冗余,影响性能,还要多敲不少字符。

长远利益: 使用指针前总是检查是否为NULL可以尽早发现bug,并且可以通过预处理指令在发行版去掉这些检查,并不会影响性能(assert就是典型的仅在测试期检查的标准宏)。如果多敲几个字符就可以尽早发现bug的话,为什么不这样做呢?难道现在省下几个字符,会比将来少花费数小时甚至数星期查找bug效率更高吗?


2、系统标准函数库应否在free指针后自动置空

眼前利益: 少打一行代码; 避免马大哈程序员忘了置空

长远利益: 自动置空无法保证当前指针在其他地方的拷贝也同样被置空。这导致指针行为的不一致——为什么有些指针会在释放后自动变为空、另一些则保留了旧的非法值呢?

眼前利益: 在我鼻子尖前面1厘米的地方,指针从来不会被复制,从来都只存储一份

长远利益: 请使用望远镜,眺望下在你鼻子尖之前10厘米的地方——那里有各种容器类,还有排序/交换/索引算法,还有“先复制后交换”的免锁算法、“写时复制”等高性能低空间占用的优化算法。

[ 本帖最后由 shan_ghost 于 2009-12-2 10:20 编辑 ]
作者: slow_hand    时间: 2009-12-02 11:23
原帖由 langue 于 2009-12-2 00:52 发表
这个世界是无情的。

设计成不置空,有人骂。
设计成直接置空,还是有人骂。
设计者大吼一声:烦死了!你们爱怎么玩怎么玩去!


作者: lemoncookie    时间: 2009-12-02 14:43
标题: 再插一腿
再说
凭什么就要给置NULL
俺就是要在free之后再回去看看俺上次指向的地址
怎么,虽然给抄家了,记住门牌号码留个念想还不给吗?

纯属joke
:wink:
作者: mirnshi    时间: 2009-12-02 15:06
热闹非凡。闲聊几句:NULL和指针free有啥关系呀?NULL只是一个宏定义。free后的变量赋值只是为了将来做判断而已,打个标记,表示那个变量所指向的内存区已经还给OS了而已。赋值成(void*)1,(void*)2,都无所谓的。如果不赋值,改为设置标志变量,看看还有多少人没事找事地free后置个标志变量。
作者: 快乐的土豆    时间: 2009-12-02 17:20
其实吧,计算机世界里面,null代表的不是空,而是不确定。换句话说,当你的  p ==NULL,仅仅是表示,你的p的指向是“不确定”的,不一定是一个“不可用”的指针。
作者: triphop    时间: 2009-12-02 17:41
1. 出于程序效率问题考虑,没必要增加这些操作。 尽管我通常是在代码中主动置0的
2. 在下面这种情况下,你将指针设置成0,也不起什么作用
    void
   foobar(char *dummy)
   {
       if (NULL != dummy)
       {
           // stuff goes here
           free(dummy);
           // 如果编译器给你做了 dummy = 0 工作,也无济于事
        }
   }

3. 有时候,可能free(ptr)中的ptr本身就不是左值吧
  比如 使用   foobar("must die"这样的方式调用上面的foobar函数
作者: to407    时间: 2009-12-02 23:39
原帖由 slow_hand 于 2009-11-26 10:20 发表
为什么语言本身不在free或者delete的时候直接将指针置成NULL?这样不就完全杜绝了“野指针”的问题吗?


因为语言设计本身 不需要越俎代庖 去决定程序员下一步要做什么




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2