【问题标题】:Pointer scopes in CC中的指针范围
【发布时间】:2015-07-22 21:11:13
【问题描述】:

在下面的代码中,什么都打印失败的解释是get_message()返回的指针超出范围:

char *get_message() {
    char msg [] = "Aren’t pointers fun?";
    return msg ;
}

int main (void) {
    char *foo = get_message();
    puts(foo);
    return 0;
}

在gdb中运行,发现foo位置的数据是字符串“Aren't pointers fun?”:

Old value = 0x0
New value = 0x7fffffffde60 "Aren’t pointers fun?"

(这似乎与指出超出范围的指针的数据保留在内存中的答案一致),但“puts”的文档说明第一个数据是从给定的地址复制的:在这种情况下可能是 0x7fffffffde60。

因此:为什么没有输出?

编辑:感谢您的回答: 我在 gdb 中运行原始代码完成,对 puts 的调用确实改变了存储 foo 的地址处的数据。

(gdb) p foo
$1 = 0x7fffffffde60 "Aren’t pointers fun?"
(gdb) n

11      return 0;
(gdb) p foo
$2 = 0x7fffffffde60 "`\336\377\377\377\177"

有趣的是,当我将 change_msg() 的代码更改为:

char *get_message() {
        char *msg = "Aren’t pointers fun?";
    return msg ;
} 

在这种情况下,foo 处的数据(地址 0x4005f4 - 地址的较小大小是否意味着什么?)在整个代码中保持不变。找出为什么这会改变行为会很酷

【问题讨论】:

  • 为什么没有输出?您调用了未定义的行为。
  • 数组不是指针。
  • @Olaf:虽然这是真的,但我看不出它与这里有什么关系。
  • @undur_gongor:OP 返回一个本地数组。如果msg 是一个指针,它会起作用。
  • @undur_gongor:感谢您提供的信息。我不说返回类型,而是msg。试试char *msg = "Hello"; return msg;。根据他的其余问题,OP混淆了指针和数组:“......超出范围的指针的数据......”指针的数据是它指向的地址。数组的数据就是内容。

标签: c pointers puts


【解决方案1】:

变量msg分配在get_message()的栈上

char msg [] = "Aren’t pointers fun?";

一旦 get_message() 返回,该方法的堆栈就会被拆除。无法保证返回到foo 的指针现在指向的内存中的内容。

当 puts() 被调用时,堆栈可能会被修改,覆盖“Aren't pointer's fun”。

【讨论】:

  • gdb 显示字符串仍然存在并由 foo 指向。问题不存在。
  • @Yves:什么时候?对 puts 的调用可能会修改堆栈。
  • 没错。 get_message 返回后,消息仍然存在。
  • 是的,在puts 中,堆栈的那部分会被覆盖。
  • @YvesDaoust:是的,但是当 puts() 尝试访问它时,对 puts() 的调用已经修改了堆栈。函数调用返回地址和可能的参数(取决于编译器)将被放置在堆栈上。
【解决方案2】:

很可能调用puts会修改堆栈并覆盖字符串。

刚从get_message返回,字符串保持不变,但被释放,即它的内存空间可供重用。

【讨论】:

    【解决方案3】:

    这里真正的问题不是“为什么它不起作用?”。问题是,“为什么从get_message返回后字符串似乎存在,但仍然不起作用?”

    为了澄清,我们再看main函数,有两个cmets供参考:

    int main (void) {
        char *foo = get_message();
        /* point A */
        puts(foo);
        /* point B */
        return 0;
    }
    

    我刚刚在 gdb 下编译并运行了它。事实上,在point A,当我在gdb 中打印出变量foo 的值时,gdb 显示它指向字符串"Aren’t pointers fun?"。但是,puts 未能打印该字符串。然后,在point B,如果我再次在gdb 中打印出foo,它就不再是以前的字符串了。

    正如前面几位评论者所解释的那样,解释是函数get_message 将字符串留在堆栈中,不能保证在堆栈中保留很长时间。在get_message 返回之后,在调用任何其他内容之前,它仍然存在。但是当我们调用puts,并且puts 开始工作时,它使用堆栈的同一部分作为它自己的本地存储,所以某个时候在那里(在puts 设法实际打印字符串之前),字符串得到销毁。


    回答 OP 的后续问题:当我们有

    char *get_message() {
        char msg [] = "Aren’t pointers fun?";
        return msg ;
    }
    

    字符串位于堆栈上的数组msg 中,我们返回一个指向该数组的指针,这不起作用,因为数组中的数据最终会消失。如果我们将其更改为

    char * msg = "Aren’t pointers fun?";
    

    (看起来如此微小的变化!),现在字符串存储在程序的初始化数据段中,我们返回一个指向 that 的指针,因为它在程序的初始化数据段中,它基本上永远存在。 (是的,get_message 最终返回一个看起来不同的地址这一事实很重要,尽管我不会过多地了解它是更低还是更高。)


    底线是数组和指针是不同的。巨大的不同。线

    char arr[] = "Hello, world!";
    

    与看起来非常相似的线几乎没有关系

    char *ptr = "Hello, world!";
    

    现在,它们是一样的,你可以同时做这两件事

    printf("%s\n", arr);
    

    printf("%s\n", ptr);
    

    但如果你试着说

    arr = "Goodbye";    /* WRONG */
    

    你不能,因为你不能分配给一个数组。如果你想要一个新字符串,你必须使用strcpy,并且你必须确保新字符串的长度相同或更短:

    strcpy(arr, "Goodbye");
    

    但是如果你用指针尝试strcpy

    strcpy(ptr, "Goodbye");    /* WRONG */
    

    现在 that 不起作用,因为ptr 指向的字符串常量是不可写的。在指针的情况下,您可以(并且经常必须)使用简单的赋值:

    ptr = "Goodbye";
    

    在这种情况下,将其设置为更长的字符串也没有问题:

    ptr = "Supercalafragalisticexpialadocious";
    

    这些是基本的区别,但正如这个问题所指出的,另一个很大的区别是数组 arr 不能有效地在函数中声明并从函数返回(除非你将其设为 static),而指针ptr可以。

    【讨论】:

      【解决方案4】:

      msg 的生命周期在从函数 get_message 返回时结束。返回的指针指向生命周期已结束的对象。

      访问它会产生未定义的行为。任何事情都有可能发生。

      在你的情况下,前msg 的内存似乎已经被 0 覆盖了。

      这与“范围”无关。您可以通过制作msg static 来修复您的代码。这不会改变范围,但会改变它的生命周期(也就是存储持续时间)。

      【讨论】:

        【解决方案5】:

        在您的 getMessage 函数中,您的消息使用的内存在堆栈上,而不是在堆上。它仍然是一个指针,只是指向堆栈上的一个位置。一旦函数返回,堆栈改变(以获取返回 ip 等)意味着,虽然消息可能仍然在内存中的相同位置,但绝对不能保证。如果有其他东西将某些东西放入堆栈(例如另一个函数调用),那么它很可能会被覆盖。你的消息不见了。

        更好的方法是使用 malloc 动态分配内存,以确保字符串在堆中(尽管这会导致谁拥有指针并负责释放它的问题。)

        如果你必须做这样的事情,我已经看到它使用静态完成:

        static char * message = "I love static pointers";
        

        编辑:尽管提到它可能仍然在堆栈上,但永远不要假设它是。大多数语言甚至不允许这样做。

        【讨论】:

        • 如果要声明并返回char * message = "I love pointers",则不需要static。在这种情况下,static 的好处是使数组返回定义明确:static char message[] = "I love static arrays"
        猜你喜欢
        • 1970-01-01
        • 2015-09-30
        • 2022-01-25
        • 1970-01-01
        • 1970-01-01
        • 2011-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多