释放改变位置的指针指向的内存会怎样?

时间:2022-05-30 10:44:14
给位看官,先看code

#include <iostream>
#include <cstdlib>
using namespace std;

int main()
{
    char* pi  = new char[0xff];
    memset(pi, 1, 0xff);
    
    pi+= (0xff/2);
    char* ppi = pi;
    delete[] pi; //注意这句,gcc下没有问题,vc6,7,下均报错!

    system("pause");
return 0;
}

思考了一下:为什么申请内存的时候,我们用malloc(new内部好像也是用malloc实现的),必须传一个参数用来指定需要申请的动态内存大小,而释放这块内存时,只需要传一个指向这块内存的地址就ok了?
因为编译器为我们在申请内存的时候做了“薄记”工作吧?
比如上面code中,申请了0xff大小的内存,那么编译器就会记下这个0xff大小值size,将来用指向这块内存的指针释放这块内存时候,就会先判断(根据vc的内部code)size是否匹配?
gcc单步跟不进去,看不了!
网上搜了一下,大概是说每次编译器对于我们用户申请的动态内存空间大小值,都会加上一个头部,用来薄记这块内存的大小和可用与否,然后返回一个偏移过薄记内容的地址指针;释放内存的时候,我们用户只传递给free函数一个指针,系统会将此指针先偏移簿记内容大小的地址,然后取出簿记内容,进行修改!
如此说来,delete[] pi;中,pi已经改动了位置,系统按照原来的方式进行固定簿记内容大小的便宜已经恐怕找不到簿记内容了,所以释放工作应该出错才对啊,不知道gcc如何应付了?望各位高手达人讨论!期待精彩!

27 个解决方案

#1


手册说了,如果给free的参数不是通过malloc得到的,属于未定义行为。
所以不必深究

$ man 3 free
......
       free() frees the memory space pointed to by ptr, which must  have  been
       returned by a previous call to malloc(), calloc() or realloc().  Other-
       wise, or if free(ptr) has already been called before, undefined  behav-
       ior occurs.  If ptr is NULL, no operation is performed.

#2


引用楼主 insulted 的帖子:
gcc下没有问题,vc6,7,下均报错!

楼主的编译器不少啊……

#3


GCC总感觉有点不对劲(MinGW 4.3)
不管怎样,看了楼主的帖子:有所悟。谢了。

#4


另,既然是未定义之行为,不同编译器实现自然不一样。
相对而言,VC还是比较严谨的。
GCC太死板了。
而在很多地方考虑又不够体贴用户。比如模板继承,就很麻烦的说!

#5


思考了一下:为什么申请内存的时候,我们用malloc(new内部好像也是用malloc实现的),必须传一个参数用来指定需要申请的动态内存大小,而释放这块内存时,只需要传一个指向这块内存的地址就ok了? 
因为编译器为我们在申请内存的时候做了“薄记”工作吧? 
比如上面code中,申请了0xff大小的内存,那么编译器就会记下这个0xff大小值size,将来用指向这块内存的指针释放这块内存时候,就会先判断(根据vc的内部code)size是否匹配? 
gcc单步跟不进去,看不了! 
网上搜了一下,大概是说每次编译器对于我们用户申请的动态内存空间大小值,都会加上一个头部,用来薄记这块内存的大小和可用与否,然后返回一个偏移过薄记内容的地址指针;释放内存的时候,我们用户只传递给free函数一个指针,系统会将此指针先偏移簿记内容大小的地址,然后取出簿记内容,进行修改! 
如此说来,delete[] pi;中,pi已经改动了位置,系统按照原来的方式进行固定簿记内容大小的便宜已经恐怕找不到簿记内容了,所以释放工作应该出错才对啊,不知道gcc如何应付了?望各位高手达人讨论!期待精彩!
UP~我也是这样考虑的~
等待了...

#6


引用 1 楼 *mill 的回复:
手册说了,如果给free的参数不是通过malloc得到的,属于未定义行为。 
所以不必深究 

$ man 3 free 
...... 
      free() frees the memory space pointed to by ptr, which must  have  been 
      returned by a previous call to malloc(), calloc() or realloc().  Other- 
      wise, or if free(ptr) has already been called before, undefined  behav- 
      ior occurs.  If ptr is NULL, no operation is…


*mill:请注意,我这里的pi就是先前malloc,calloc realloc函数返回之pointer啊!

#7


说明你的gcc没有做安全检查,或者在检查到错误以后没有报给你而已
另外一种可能的解释:malloc/free和new/delete的行为可能随编译器不同而不同的,如果内存分配信息放在你所分配的内存前面,那么移动指针的唯一结果就是delete时找不到那块内存,自然就报错。但是如果你的记录信息不是这么保存的,也就是delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题(我以前实现过这样的内存分配系统)。

所以:结论就是:这种行为的结果是未定义的。系统只保证你delete时的指针等于分配时能正确释放,但是如果你没满足这个前提,谁也不知道系统会做什么

#8


引用 6 楼 insulted 的回复:
*mill:请注意,我这里的pi就是先前malloc,calloc realloc函数返回之pointer啊!

可是pi已经改动了位置阿
delete或者free看到的不是一个变量名,而是一个地址值,所以这里的pi已经不是当初用new得到的那个了

#9


以前我也研究过这个问题,但没有楼主这么深入的研究

我就看源代码,知道了是有个标记信息。

学到很多,谢谢

#10


楼主很能思考,应该自己看下malloc.h.

#11


结果未定义,你移动了指针后,这块内存还算不算malloc new得来的,和编译器有关系,自己自己认为

#12


引用 7 楼 arong1234 的回复:
说明你的gcc没有做安全检查,或者在检查到错误以后没有报给你而已 
另外一种可能的解释:malloc/free和new/delete的行为可能随编译器不同而不同的,如果内存分配信息放在你所分配的内存前面,那么移动指针的唯一结果就是delete时找不到那块内存,自然就报错。但是如果你的记录信息不是这么保存的,也就是delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题(我以前实现过这样的内存分配系统)。 


to arong:"delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题",请问,是多大一个范围?
我将code改为:

char* pi  = new char[0xfffffff];
    memset(pi, 1, 0xfffffff);
    
    pi+= (0xfffffff/2); // 这么大的范围不小吧?问题依然如故!

阿荣:如果你当年的实现不牵扯保密问题,可以讲讲你的所谓的“某种算法”吗?

#13


引用 10 楼 hairetz 的回复:
楼主很能思考,应该自己看下malloc.h. 


to hairetz :h文件里都是函数声明吧?没用具体实现啊!
你能看到,给粘过来吧?谢谢!

#14


我以前的帖子,可能有些帮助,不过当时我的实验不够成熟,只提到了对象个数的簿记信息,没有提到内存大小的信息,但这两个簿记信息是紧挨着的(我试验用的那个版本的g++)。只需要对象个数 + 起始地址 + 内存大小,就可以释放内存了。

http://topic.csdn.net/u/20081018/18/fc784fb4-b2f6-4b49-8ef4-4ba5e8fbc133.html


其实如果非要弄清楚,去看gcc的源代码罢。。。

#15


运行环境问题and so on

#16


14楼的帖子不错!
建议大家感兴趣都应该去瞅瞅,呵呵!

#17


14楼的帖子不错!
建议大家感兴趣都应该去瞅瞅,呵呵!

#18


1楼正解
这个涉及到free在未定义行为时的具体实现,看起来gcc做了空操作

#19


理论上来说,给定一个地址去free,只要地址确实是某次堆分配的内存中的某个位置。是可以通过遍历的方式找到开头位置的。但是这样做非常的不合适。最直接的问题是效率太低,因为要搜索所有已分配的内存块。另外,c++这种语言的灵活性要求程序员自己合理的管理内存。随便传一个地址,系统帮你释放了,你下次又传另一个地址进来,有的搜索,而且发现搜索的事已经释放了的位置,系统就不用干别的了,cpu就用来做释放内存的搜做操作就满负荷了,太低效。

所以,合理的管理好你的内存,用正确的方式。

#20


19楼说的很正统,谢谢!

#21


不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了,你要避免犯这种错误,而不是去看错误后系统会怎么样。研究这个到底有什么意义呢?做正确的事情,而不是把事情搞清楚。我都不愿意说把事情搞正确了,因为这永远不可能正确

#22


我举那个例子只是说明你这种研究毫无意义,不是告诉你某种可能的算法会怎么怎么实现。
引用 12 楼 insulted 的回复:
引用 7 楼 arong1234 的回复:
阿荣:如果你当年的实现不牵扯保密问题,可以讲讲你的所谓的“某种算法”吗? 

#23


free()是针对malloc()分配的内存操作;
如果你的操作对象不是malloc()分配的,那么:
(1).你的操作是错误的;
(2).其结果是未定义的;

#24


引用 21 楼 arong1234 的回复:
不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了,你要避免犯这种错误,而不是去看错误后系统会怎么样。研究这个到底有什么意义呢?做正确的事情,而不是把事情搞清楚。我都不愿意说把事情搞正确了,因为这永远不可能正确 

不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了

呵呵。。这句话说的很正点。。

#25


呵呵 学习中

#26


pool问题~~~学习~~

#27


感谢各位参与讨论!

#1


手册说了,如果给free的参数不是通过malloc得到的,属于未定义行为。
所以不必深究

$ man 3 free
......
       free() frees the memory space pointed to by ptr, which must  have  been
       returned by a previous call to malloc(), calloc() or realloc().  Other-
       wise, or if free(ptr) has already been called before, undefined  behav-
       ior occurs.  If ptr is NULL, no operation is performed.

#2


引用楼主 insulted 的帖子:
gcc下没有问题,vc6,7,下均报错!

楼主的编译器不少啊……

#3


GCC总感觉有点不对劲(MinGW 4.3)
不管怎样,看了楼主的帖子:有所悟。谢了。

#4


另,既然是未定义之行为,不同编译器实现自然不一样。
相对而言,VC还是比较严谨的。
GCC太死板了。
而在很多地方考虑又不够体贴用户。比如模板继承,就很麻烦的说!

#5


思考了一下:为什么申请内存的时候,我们用malloc(new内部好像也是用malloc实现的),必须传一个参数用来指定需要申请的动态内存大小,而释放这块内存时,只需要传一个指向这块内存的地址就ok了? 
因为编译器为我们在申请内存的时候做了“薄记”工作吧? 
比如上面code中,申请了0xff大小的内存,那么编译器就会记下这个0xff大小值size,将来用指向这块内存的指针释放这块内存时候,就会先判断(根据vc的内部code)size是否匹配? 
gcc单步跟不进去,看不了! 
网上搜了一下,大概是说每次编译器对于我们用户申请的动态内存空间大小值,都会加上一个头部,用来薄记这块内存的大小和可用与否,然后返回一个偏移过薄记内容的地址指针;释放内存的时候,我们用户只传递给free函数一个指针,系统会将此指针先偏移簿记内容大小的地址,然后取出簿记内容,进行修改! 
如此说来,delete[] pi;中,pi已经改动了位置,系统按照原来的方式进行固定簿记内容大小的便宜已经恐怕找不到簿记内容了,所以释放工作应该出错才对啊,不知道gcc如何应付了?望各位高手达人讨论!期待精彩!
UP~我也是这样考虑的~
等待了...

#6


引用 1 楼 *mill 的回复:
手册说了,如果给free的参数不是通过malloc得到的,属于未定义行为。 
所以不必深究 

$ man 3 free 
...... 
      free() frees the memory space pointed to by ptr, which must  have  been 
      returned by a previous call to malloc(), calloc() or realloc().  Other- 
      wise, or if free(ptr) has already been called before, undefined  behav- 
      ior occurs.  If ptr is NULL, no operation is…


*mill:请注意,我这里的pi就是先前malloc,calloc realloc函数返回之pointer啊!

#7


说明你的gcc没有做安全检查,或者在检查到错误以后没有报给你而已
另外一种可能的解释:malloc/free和new/delete的行为可能随编译器不同而不同的,如果内存分配信息放在你所分配的内存前面,那么移动指针的唯一结果就是delete时找不到那块内存,自然就报错。但是如果你的记录信息不是这么保存的,也就是delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题(我以前实现过这样的内存分配系统)。

所以:结论就是:这种行为的结果是未定义的。系统只保证你delete时的指针等于分配时能正确释放,但是如果你没满足这个前提,谁也不知道系统会做什么

#8


引用 6 楼 insulted 的回复:
*mill:请注意,我这里的pi就是先前malloc,calloc realloc函数返回之pointer啊!

可是pi已经改动了位置阿
delete或者free看到的不是一个变量名,而是一个地址值,所以这里的pi已经不是当初用new得到的那个了

#9


以前我也研究过这个问题,但没有楼主这么深入的研究

我就看源代码,知道了是有个标记信息。

学到很多,谢谢

#10


楼主很能思考,应该自己看下malloc.h.

#11


结果未定义,你移动了指针后,这块内存还算不算malloc new得来的,和编译器有关系,自己自己认为

#12


引用 7 楼 arong1234 的回复:
说明你的gcc没有做安全检查,或者在检查到错误以后没有报给你而已 
另外一种可能的解释:malloc/free和new/delete的行为可能随编译器不同而不同的,如果内存分配信息放在你所分配的内存前面,那么移动指针的唯一结果就是delete时找不到那块内存,自然就报错。但是如果你的记录信息不是这么保存的,也就是delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题(我以前实现过这样的内存分配系统)。 


to arong:"delete的参数只要落在一个给定的范围,他总能通过某种算法找到那块内存,就没问题",请问,是多大一个范围?
我将code改为:

char* pi  = new char[0xfffffff];
    memset(pi, 1, 0xfffffff);
    
    pi+= (0xfffffff/2); // 这么大的范围不小吧?问题依然如故!

阿荣:如果你当年的实现不牵扯保密问题,可以讲讲你的所谓的“某种算法”吗?

#13


引用 10 楼 hairetz 的回复:
楼主很能思考,应该自己看下malloc.h. 


to hairetz :h文件里都是函数声明吧?没用具体实现啊!
你能看到,给粘过来吧?谢谢!

#14


我以前的帖子,可能有些帮助,不过当时我的实验不够成熟,只提到了对象个数的簿记信息,没有提到内存大小的信息,但这两个簿记信息是紧挨着的(我试验用的那个版本的g++)。只需要对象个数 + 起始地址 + 内存大小,就可以释放内存了。

http://topic.csdn.net/u/20081018/18/fc784fb4-b2f6-4b49-8ef4-4ba5e8fbc133.html


其实如果非要弄清楚,去看gcc的源代码罢。。。

#15


运行环境问题and so on

#16


14楼的帖子不错!
建议大家感兴趣都应该去瞅瞅,呵呵!

#17


14楼的帖子不错!
建议大家感兴趣都应该去瞅瞅,呵呵!

#18


1楼正解
这个涉及到free在未定义行为时的具体实现,看起来gcc做了空操作

#19


理论上来说,给定一个地址去free,只要地址确实是某次堆分配的内存中的某个位置。是可以通过遍历的方式找到开头位置的。但是这样做非常的不合适。最直接的问题是效率太低,因为要搜索所有已分配的内存块。另外,c++这种语言的灵活性要求程序员自己合理的管理内存。随便传一个地址,系统帮你释放了,你下次又传另一个地址进来,有的搜索,而且发现搜索的事已经释放了的位置,系统就不用干别的了,cpu就用来做释放内存的搜做操作就满负荷了,太低效。

所以,合理的管理好你的内存,用正确的方式。

#20


19楼说的很正统,谢谢!

#21


不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了,你要避免犯这种错误,而不是去看错误后系统会怎么样。研究这个到底有什么意义呢?做正确的事情,而不是把事情搞清楚。我都不愿意说把事情搞正确了,因为这永远不可能正确

#22


我举那个例子只是说明你这种研究毫无意义,不是告诉你某种可能的算法会怎么怎么实现。
引用 12 楼 insulted 的回复:
引用 7 楼 arong1234 的回复:
阿荣:如果你当年的实现不牵扯保密问题,可以讲讲你的所谓的“某种算法”吗? 

#23


free()是针对malloc()分配的内存操作;
如果你的操作对象不是malloc()分配的,那么:
(1).你的操作是错误的;
(2).其结果是未定义的;

#24


引用 21 楼 arong1234 的回复:
不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了,你要避免犯这种错误,而不是去看错误后系统会怎么样。研究这个到底有什么意义呢?做正确的事情,而不是把事情搞清楚。我都不愿意说把事情搞正确了,因为这永远不可能正确 

不要进行这种“在我坚持错误的情况下系统会怎么样”的研究了

呵呵。。这句话说的很正点。。

#25


呵呵 学习中

#26


pool问题~~~学习~~

#27


感谢各位参与讨论!