【问题标题】:How are char* deallocated in C如何在 C 中释放 char*
【发布时间】:2013-11-15 18:53:42
【问题描述】:

所以我正在阅读一个类的一些代码,我对如何在 C 中释放变量感到有些困惑。

给出的代码是

#include<stdio.h>
main () {
    int n=0; 
    char *p = "hello world";
    while (*p!= 0)  {   // *p != '\0';
        putc(*p, stdout);
        p++;
    }
    printf("\np = %d", *p);
    printf("\np = %d\n", p);
}

所以我知道您不需要为 char* 释放任何内存,因为没有发生 malloc,但我不明白为什么这段代码不会泄漏任何内存...如果您要为一个字符串,从而将指针移动到下一个内存块(1字节),那么您不会丢失初始引用和您递增的所有引用点吗?如果没有参考点,该内存将如何回收,除非在此类操作发生之前由编译器保存。我将不胜感激有关如何回收的一些见解!

【问题讨论】:

  • 变量在超出范围时被释放。当有人释放堆时,堆被释放。文字值(即编译代码中的静态字符串)在程序的整个生命周期中都存在。您担心的空间是文字字符串。

标签: c string memory


【解决方案1】:

释放内存的任务被强加给该内存的所有者。仅仅因为您有一个指向某个内存区域的指针并不意味着您拥有该内存,因此也不意味着您有责任释放它。

字符串文字"hello world" 是一个具有静态存储持续时间的对象。它存储在静态内存中。静态内存始终归运行时环境所有。运行时环境知道存储在静态内存中的数据。运行时环境知道何时必须释放该数据(这很容易,因为静态内存基本上“从不”释放 - 只要您的程序运行,它就存在)。

所以,再一次,你的指针p 并不真正拥有静态区域中的任何内存。你只是碰巧用你的p引用那个记忆。担心内存的释放不是您的事。它会在时机成熟时(即程序结束时)被正确释放,并且它会在没有您和您的指针p 的任何帮助的情况下正确完成。您可以随意更改p,可以将其指向完全不同的内存位置,或者您可以毫无保留地丢弃它。随便说说,没人关心你的p

在 C 程序中您可能拥有的唯一内存是您个人使用malloc(或其他动态内存分配函数)分配的内存。因此,您必须记住最终调用free 以获得您自己分配的内存(并且您必须确保知道malloc 返回的原始值以传递给free)。所有其他类型的内存(如 staticautomatic)永远不归您所有,这意味着释放它不是您的事,完全没有必要保留原始指针值。

【讨论】:

    【解决方案2】:

    您没有泄漏任何内存,因为您没有动态分配任何内存。内存泄漏来自未释放动态分配的内存。本地分配的内存(如char *p)或静态分配的内存(如p 最初指向的字符串"hello world")不会导致泄漏。

    【讨论】:

    • 那是因为它们超出了未来 '}' 的范围,对吧?
    • 对于块范围的变量,是的,它们在将来会超出范围 };对于文件范围或全局范围的变量,它们在程序退出时由操作系统释放。 (或者我应该说“具有内部或外部链接的文件范围变量”?也许这样会更准确。
    【解决方案3】:

    您没有动态分配任何新内存,因此您不需要释放它。

    【讨论】:

      【解决方案4】:

      字符串文字"hello world" 是一个对象,它是程序本身的一部分。当"hello world" 表达式被求值时,程序本质上获得了一个指向自身片段的指针。程序运行时无法释放该内存;这相当于在程序中制造了一个“洞”。内存与程序本身具有相同的生命周期。

      在 C 语言中,程序员不需要管理与程序具有相同生命周期的内存:这是由启动程序的环境在外部管理(或管理不善,视情况而定),并处理程序终止时的后果。

      当然,内存还是要管理的;只是责任不在于C程序。 (至少,不是在提供 C 语言托管实现的环境中。某些嵌入式系统的规则可能不是这样!)

      在嵌入的程序中,字符串文字(连同程序的其余部分)实际上可以存在于 ROM 中。所以可能真的没有什么可以清理的。指针是一个地址,它指向一个芯片(或多个芯片)上的某个永久位置。

      【讨论】:

        【解决方案5】:

        简而言之:因为程序本身很短。你可以在那里做任何你想要的malloc,实际上不会发生泄漏,因为一旦进程结束,所有的内存都会交还给操作系统。在您的示例中,泄漏不会成为问题。总之,

        在您的情况下,没有发生泄漏,因为变量 p 指向一个文本字符串,该字符串位于内存的数据段中(即它是一个常量,写入可执行文件中)。这种内存不能被释放,因为它的空间是固定的。 实际上,这不是问题是错误的,因为一个非常大的可执行文件,其中有很多大常量,可能会有显着的内存占用,但无论如何 这不称为泄漏,因为内存使用可能会很大,但不会随着时间的推移而增加,这是内存泄漏的主要原因(因此得名 leak)。

        【讨论】:

        • 我觉得在不解释原因的情况下投反对票真的很不礼貌。
        • 在现代通用操作系统中,所有内存泄漏都会在程序终止时被清除。内存泄漏是程序动态分配内存并且在终止之前没有释放该内存的结果。 (在一些旧操作系统上,规则过去是不同的,比如最初的 AmigaOS,我认为旧版本的 Mac OS - 版本 9 和更可能在此之前的版本 - 也有些脆弱。嵌入式 O 中的规则可能不同/S,甚至是现代的当前版本。)您的回答暗示int main(void){void*vp=malloc(10);} 不会泄漏;确实如此!
        • 是的,好的...我说的是那个特定的情况,假设是 Win/Linux/OSX。如果你创建了一个 malloc,你应该释放它,在现代操作系统中也是如此,因为你或其他人可能决定有一天嵌入你的程序并在循环中运行你的 main,将非泄漏变成泄漏。
        【解决方案6】:

        当您在本地声明变量时,编译器知道每个变量需要多少空间,并且当您运行程序时,每个局部变量(以及每个函数调用)都会放入堆栈。就在 return 语句(或 } 括号,如果是 void 函数)之后,每个局部变量都会从堆栈中弹出,因此您不必释放它。

        当您调用 new 运算符(或纯 C 中的 malloc)时,编译器不知道数据的大小,因此内存在堆上分配运行时。

        为什么我要解释这个事实是,每当您调用 new 或 malloc(或 calloc)时,您有责任释放您不想再使用的内存。

        【讨论】:

        • Deallocation of locals 可以解释为什么你不需要释放指针本身,但它并不能真正解释为什么你不需要分配指针指向的内存。跨度>
        【解决方案7】:

        除了其他答案之外,增加 C 中的指针不会创建或丢失“引用”,也不会导致指针指向的内存的任何复制或其他更改。在这种情况下,指针只是一个恰好指向静态分配的内存区域的数字。

        递增指针不会改变指针所指向的字节。 “H”,还在。但是程序现在认为字符串以“e”开头。 (它知道字符串的结尾在哪里,因为按照惯例,C 中的字符串以null 结尾。

        没有检查指针是否指向您认为它应该指向的位置,或者根本没有任何有效区域。程不会取消分配用于字符串的内存。

        如果您将指针更改为指向内存中的“错误”位置,就会发生有趣(坏)的事情——例如页面错误、堆栈溢出和核心转储。

        【讨论】:

        • 如果你动态分配内存并且你增加一个指针而不保存对初始节点的引用,那么你肯定会丢失你的引用。如果您正在实现一个链表,并且只是决定在不首先释放您的节点的情况下将您的标头推进几个插槽,那么我不明白这不会构成丢失对内存块的引用,因为您将无法释放它,您将有后续泄漏......在这种情况下,我们使用静态分配的内存,当它的生命周期到期时从堆栈中弹出,但不是一般情况。
        • 是的,如果您遍历一个链表(并且您的列表没有向后/父指针),并且您不断添加到该列表中,您可能会泄漏(无法释放)内存。您可以通过更改指针“丢失”任何内存“引用”。但是泄漏意味着不断增长的(动态)内存池,(通常)从 malloc() 获得。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-25
        • 1970-01-01
        • 2016-03-02
        • 2022-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多