C中的指针范围

时间:2015-07-22 21:11:13

标签: c pointers puts

在下面的代码中,无法打印任何内容的解释是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位置的数据是字符串“Are not 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 - 地址的较小大小是否意味着什么?)中的数据在整个代码中保持不变。找出这会改变行为的原因很酷。

5 个答案:

答案 0 :(得分:4)

变量msg在get_message()

的堆栈上分配
char msg [] = "Aren’t pointers fun?";

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

当调用puts()时,堆栈很可能被修改,覆盖“不是指针的乐趣。”

答案 1 :(得分:1)

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

get_message返回后,字符串保持不变,但是已取消分配,即其内存空间可供重用。

答案 2 :(得分:1)

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

为了澄清,让我们再次查看main函数,并提供两条评论供参考:

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?";

(这种看似微小的变化!),现在字符串存储在程序的初始化数据段中,我们返回一个指向 的指针,因为它'在程序的初始化数据段中,它基本上永远存在。 (是的,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 */

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

ptr = "Goodbye";

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

ptr = "Supercalafragalisticexpialadocious";

这些是基本的差异,但正如这个问题所指出的,另一个很大的区别是数组arr无法在函数中声明并从函数返回(除非你使它成为{{1} }}),而指针static可以。

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

在getMessage函数中,消息使用的内存位于堆栈上,而不是堆上。它仍然是一个指针,只是堆栈上的一个位置。一旦函数返回,堆栈改变(获得返回ip等)的意思,虽然消息MIGHT仍然在内存中的相同位置,但绝对没有保证。如果有任何其他东西放入堆栈(例如另一个函数调用),那么很可能它将被覆盖。你的信息消失了。

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

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

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

编辑:尽管提到可能仍然在堆栈上,但从来没有假装它。大多数语言甚至不允许这样做。