我在试着弄清楚如果我试图从"中间"释放一个指针会发生什么. 例如,请查看以下代码:

char *ptr = (char*)malloc(10*sizeof(char));

for (char i=0 ; i<10 ; ++i)
{
    ptr[i] = i+10;
}
++ptr;
++ptr;
++ptr;
++ptr;
free(ptr);

我遇到了一个带有未处理异常错误消息的崩溃.

谢谢

推荐答案

当你对一个块进行malloc时,它实际上分配的内存比你要求的要多.这个额外的内存用于存储信息,例如分配的块的大小,到块链中下一个空闲/使用的块的链接,有时还有一些"保护数据",帮助系统检测您是否写入了分配块的末尾.此外,大多数分配器会将内存的总大小和/或起始部分四舍五入到字节的倍数(例如,在64位系统上,它可能会将数据对齐到64位(8字节)的倍数,因为对处理器/总线来说,从未对齐的地址访问数据可能更困难、效率更低),因此,最后可能会出现一些"填充"(未使用的字节).

当您释放指针时,它会使用该地址来查找添加到分配块开头(通常)的特殊信息.如果传入不同的地址,它将访问包含垃圾的内存,因此其行为未定义(但最常见的情况是会导致崩溃)

稍后,如果释放()块,但不要"忘记"指针,将来可能会意外地try 通过该指针访问数据,并且行为未定义.可能出现以下任何情况:

  • 内存可能放在一个空闲块列表中,因此当您访问它时,它仍然碰巧包含您留在那里的数据,并且代码正常运行.
  • 内存分配器可能已经将(部分)内存分配给了程序的另一部分,这可能会覆盖(部分)您的旧数据,因此当您读取它时,您会收到垃圾,这可能会导致代码出现意外行为或崩溃.或者,您将重写其他数据,导致程序的另一部分在将来的某个时候行为异常.
  • 内存可能已经返回到操作系统(不再使用的内存"页面"可以从地址空间中删除,因此该地址不再有可用内存——实际上是应用程序内存中未使用的"洞").当您的应用程序试图访问数据时,将出现一个硬内存故障并终止您的进程.

这就是为什么在释放指针指向的内存后确保不使用指针很重要的原因——最好的做法是在释放内存后将指针设置为NULL,因为您可以轻松地测试NULL,并且try 通过NULL指针访问内存将导致错误的但为consistent的行为,这更容易调试.

C++相关问答推荐

Zig将std.os.argv转换为C类型argv

C中的ATONE会扰乱SEN/CLUTE GMS应用程序中的其他字符串

segfault在C中使用getline()函数

当打印字符串时,为什么在c中没有使用常量限定符时我会收到警告?

如果我释放其他内容,返回值就会出错

我可以在C中声明不同长度数组的数组而不带变量名吗?

双指针指向常量双指针的指针类型赋值不兼容

将宏值传递给ARM链接器,该链接器将变量放置在特定位置

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

';\n&39;和';\r&39;中的';\n&39;之间有什么关系?

错误...的多个定义(&Q)首先在这里定义&

为四维数组中的Dim-1和Dim-3重新分配空间

如何在不读取整个字符串的情况下删除UTF8字符串的尾随空格以提高性能?

Linux分段故障(核心转储)

使用正则表达式获取字符串中标记的开始和结束

将非连续物理内存映射到用户空间

如何在MSVC中使用intSafe.h函数?

atoi函数最大长-长误差的再创造

在NASM中链接Linux共享库时出错-';将R_ X86_64_;foo';

如何找出C中分配在堆上的数组的大小?