【问题标题】:When a pointer is created in scope, what happens to the pointed to variable when the pointer goes out of scope?在范围内创建指针时,当指针超出范围时,指向的变量会发生什么?
【发布时间】:2013-01-29 05:12:42
【问题描述】:

标题说明了一切。

I found an old question that is essentially the same,但我需要进一步澄清。

在这个问题中,接受的答案是:

char* text = "Hello, world"; 

这里有一个自动变量(指针) 在堆栈上创建并设置为指向常量内存中的值, 这意味着:

  • “”中的字符串字面量存在于整个程序执行过程中。
  • 您不负责“分配”或“释放”它
  • 你可能不会 更改。如果你想改变它,那么你必须分配一些 “非常量内存”并将其复制到那里。

这是说指针被删除了,而不是指针指向的数据? 如果我要在函数中创建 1,000,000 个指向字符的指针,当它们超出范围时,我的所有内存都会被释放吗?或者只是制作指针所需的内存,而将实际字符本身留在后面来占用我所有的内存?

【问题讨论】:

  • 指针不会被删除。它被破坏了,在指针的情况下,它什么也不做。

标签: c++ pointers memory scope


【解决方案1】:

字符数组将在程序的整个执行过程中一直存在,因为它们具有静态存储持续时间。这并不意味着您需要删除它们 - 它们应该在您的程序的整个持续时间内都存在。事实上,在它上面调用delete 会给你未定义的行为。你只能delete 分配给new 的东西。

指针本身具有自动存储持续时间,并在超出范围时被销毁。值得注意的是,指针必须是const char*,因为字符串字面量会为您提供const char 的数组。考虑:

void func()
{
  const char* str = "Hello";
}

包含Hello\0 的字符数组在您的程序期间存在。指针str 仅在该函数的持续时间内存在。这里不需要deleted

如果你仔细想想,这很有意义。您在源代码中编写的所有这些字符串都必须存在于您的可执行文件中的某个位置。编译器通常会将这些字符串写入可执行文件的数据段。当你运行你的程序时,可执行文件和包含你的字符串的数据段一起被加载到内存中。

如果您的程序中有两个字符串文字具有相同或重叠的文本,编译器没有理由不能将其优化为只存储其中一个。考虑:

void func()
{
  const char* str1 = "Hello";
  const char* str2 = "Hello";
  const char* str3 = "lo";
}

编译器只需在此处将字符Hello\0 写入可执行文件一次。前两个指针将指向H,第三个将指向第二个l。您的编译器可以进行这样的优化。当然,在这个例子中,编译器可以通过将字符串全部去掉来进行更进一步的优化——它们不会以任何有助于程序可观察行为的方式使用。

所以是的,如果您有一百万个不同的字符串文字,它们在某种程度上有助于程序的可观察行为,那么它们当然必须作为您的可执行文件的一部分存在。

【讨论】:

  • 所以消除我的问题的模棱两可(不是一个真正的词),在我的一个类中,我有一个函数创建一个指针unsigned char* imageData;,该指针随后被扔到另一个函数中,并返回充满了数据。在我完成对 imageData 的所有必要操作后,如果我没有显式删除它,指针将超出范围并被释放,但它指向的所有数据都将保留?
  • 这取决于内存是如何分配的。 字符串文字具有静态存储持续时间。你不能delete他们。这并不意味着当指针超出范围时字符串文字会自动销​​毁 - 它只是不会被销毁!但是,如果您通过执行new unsigned char[100] 之类的操作来分配imageData 指向的数组,则该数组具有动态存储持续时间,并且必须为delete[]d,否则会出现内存泄漏.黄金法则:只有delete/delete[] 你是什么new/new[]
  • 好吧,这是有道理的。谢谢!! :)
【解决方案2】:

如果足以让 SO 认为这是一个答案,我会说“没什么”(回答标题)。

至于你的百万个字符指针,而指针会被弹出(尽管你必须有相当多的堆栈来保存百万个指针)它们指向的数据会困扰你的记忆。

【讨论】:

  • 知道了,所以删除它们是必须的。
  • @xcdemon05 不不不,我认为“困扰你的记忆”迈克尔的意思是“只会存在于你的记忆中。”
  • @xcdemon05 是否删除它们取决于。请参阅我的回答,我希望能解释这一点。
  • 我的意思取决于它是如何分配的。实际上,我的意思是“将占用内存”,尽管我假设指向的数据是先前分配的。严格来说,无论它指向什么都是正确的,除非它在其他地方被删除。只是如果没有分配,占用内存是可以的;-)
【解决方案3】:

不幸的是,您的示例不足以说明全部情况。

首先,一些简单的即席词汇和解释:内存单元是一个(用 C++ 输入的)具有给定大小的内存区域,它包含一个 .几个内存单元可能包含相同的值,没关系。

您应该考虑 3 种类型的内存单元

  • "Hello, World!":这个内存单元是静态存储的,它存在于整个程序的持续时间内
  • void foo(int a);void foo() { int a = 5; }:内存单元a,在这两种情况下,都有自动存储时长,一旦函数foo返回,它会自动消失
  • void foo() { int* a = new 5; }:在“某处”创建一个匿名存储单元来存储值5,并创建一个具有自动存储持续时间的存储单元a 来存储匿名的地址

那么,当 指针 超出范围(消失)时会发生什么?

嗯,就是这样。指针消失。更具体地说,它所指向的内存单元并没有发生什么特别的事情。

void foo(int a) {
    int* pointer = &a;
} // pointer disappears, `a` still exists briefly

void foo() {
    int* pointer = 0;
    {
        int a;
        pointer = &a;
    } // a disappears, pointer's value does not change...
} // pointer disappears

确实,在 C 和 C++ 中:

  • 您可以保留不再存在的对象的地址 => 悬空引用
  • 您可能会丢失现有对象的所有地址 => 内存泄漏

那么当char const* text = "Hello, world"; 中的text 超出范围时会发生什么?

什么都没有。

【讨论】:

    【解决方案4】:

    指针本身占用“自动存储”(通常是堆栈)上的空间。一旦函数返回[或范围完成,但从技术上讲,几乎所有编译器都会“等待”直到函数返回,然后释放空间]。

    如果你在一个循环中调用同一个函数 100 万次,那么在任何给定时间都只会有一个指针。如果您有 100 万个函数 [和大量内存],那么当前调用的每个函数都会有一个指针。例如。

    char *foo()
    {
        char *text1 = "Hello";
        return text1;
    }
    
    void bar()
    {
        char *text2 = "World!";
        printf("%s %s!\n", foo(), text2);
    }
    
    
    void baz()
    {
        char *text3 = "Meh";
        bar();
    }
    
    int main()
    {
        char *text4 = "Main";
    
        baz();
    }
    

    当我们进入 main 时,text4 在堆栈上创建 - 它被初始化为字符串“Main”,该字符串保存在其他一些内存位中。然后当我们调用baz() 时,text3 被创建并初始化为“Meh”,它调用bar() 创建text2 并指向文本“World”,并调用foo 创建text1 和初始化为Hello。当foo 返回时,text1 内部的地址作为返回值,指针本身消失。当printf() 完成时,bar 返回,指针消失。

    只要程序正在运行,字符串“Main”、“Meh”、“Hello”和“World”就仍然存在。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多