0

下面的程序没有运行,因为我们正在返回本地 int 值。

#include<stdio.h>

int *fun1()
{
    int i = 10;
    return &i;       // local i hence will not get printed in main
}

main()
{
    int *x = fun1();

    printf("%d", *x);
}

然而,在下面程序运行的同时,即使我们返回本地字符串基地址。为什么本地 char* 的概念在这里不适用?

char *fun()
{
    char *p = "ram";
    return p;             //char* is local, even though in main it gets printed. why?
}

int main()
{
    char *x = fun();
    printf("%s", x);

    return 0;
}
4

3 回答 3

8
char *fun()
{
    char *p = "ram";
    return p;             //char* is local, even though in main it gets printed. why?
}

当您编写这样的代码时,"ram"不会以相同的方式放置在堆栈int i = 10中 - 它被放置在.rodata可执行文件的(只读数据)部分中。放在堆栈上的是指向内存中该位置的指针。

想象一下,如果"ram"不是三个字符,而是一百万个字符——如果要将字符串本身压入堆栈,则每次调用函数时都必须将一百万个字节压入堆栈!即使字符串是恒定的。


由于字符串在内存中的位置是恒定的,因此返回指向该位置的指针是有效的。

于 2021-09-01T18:23:25.780 回答
2

在这个函数中

int *fun1()
{
    int i = 10;
    return &i;       // local i hence will not get printed in main
}

块范围变量i具有自动存储持续时间。这意味着退出函数后变量将不再存在,返回的指向变量的指针将无效。

在这个函数中

char *fun()
{
    char *p = "ram";
    return p;             //char* is local, even though in main it gets printed. why?
}

"ram"具有该类型的字符串文字具有char[4]静态存储持续时间。这意味着它在退出函数后将是活动的,并且返回的指向文字的指针将是有效的。

来自 C 标准(6.4.5 字符串文字)

6 在翻译阶段 7 中,将一个字节或值为零的代码附加到每个由一个或多个字符串文字产生的多字节字符序列。78) 然后使用多字节字符序列初始化一个静态存储持续时间和长度刚好足够的数组包含序列。

请注意,该函数返回指针的副本,p但指向的对象(字符串文字)具有静态存储持续时间。所以返回的指针有一个有效值。

通过将变量声明i为具有静态存储持续时间,您可以在第一个函数中实现相同的效果。例如

int *fun1()
{
    static int i = 10;
    return &i;       // local i hence will not get printed in main
}

至于你写的作为答案的问题

为什么下面不起作用?当我从 char *p = "ram" 更改为 char p[]="ram" 时?

char *fun()
{
    char p[] = "ram";
    return p;             
}

int main()
{
    char *x = fun();
    printf("%s", x);

    return 0;
}

那么该数组p就是一个本地数组,具有自动存储时长的功能。它的元素由字符串字面量的元素初始化"ram"。但这确实改变了数组存储持续时间。实际上这个数组的声明类似于

char p[] = { 'r', 'a', 'm', '\0' };

所以这个返回语句

    return 0;

返回指向数组的第一个元素的指针,该元素 (array) 在退出函数后将不再存在。所以返回的指针是无效的。

于 2021-09-01T18:42:32.483 回答
1

在代码中

char *fun()
{
    char *p = "ram";
    return p;             //char* is local, even though in main it gets printed. why?
}

变量p是函数的局部变量,它的生命周期以函数结束。但是,您返回的是p's value,而不是它的地址,所以这并不重要。

p是字符串文字的地址"ram"。字符串文字的存储方式使得它们在整个程序的生命周期内都可用,从启动到终止 - 文字在退出"ram"时不会停止存在fun,因此它的地址保持有效。

为什么下面不起作用?当我从 char *p = "ram" 更改为 char p[]="ram" 时?

现在您已经更改了字符串的存储方式和位置。它不再是存储字符串文字地址的指针,而是p存储字符串本身内容的数组。字符串的生命周期现在与函数的生命周期相同,因此一旦函数退出,存储该字符串的数组就不再存在并且返回值无效。

我拿了你的代码并添加了一个实用程序1来显示内存中各种项目的地址和内容。首先我们从p指针的第一个版本开始:

#include <stdio.h>
#include "dumper.h"

char *fun( void )
{
  char *p = "ram";

  char *names[] = { "p", "\"ram\"" };
  void *addrs[] = { &p, "ram" };
  size_t sizes[] = { sizeof p, sizeof "ram" };

  puts( "In fun: ");

  dumper( names, addrs, sizes, 2, stdout );

  return p;
}

int main( void )
{
  char *x;
  
  char *names[] = { "x", "\"ram\"" };
  void *addrs[] = { &x, "ram" };
  size_t sizes[] = { sizeof x, sizeof "ram" };

  puts( "Before call to fun:" );
  dumper( names, addrs, sizes, 2, stdout );

  x = fun();

  puts( "After call to fun:" );
  dumper( names, addrs, sizes, 2, stdout );

  return 0;
}

这是输出:

Before call to fun:
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              x  0x7ffee2451a40   a0   1a   45   e2    ..E.
                 0x7ffee2451a44   fe   7f   00   00    ....

          "ram"     0x10d7aef04   72   61   6d   00    ram.

In fun: 
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              p  0x7ffee24519b8   04   ef   7a   0d    ..z.
                 0x7ffee24519bc   01   00   00   00    ....

          "ram"     0x10d7aef04   72   61   6d   00    ram.

After call to fun:
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              x  0x7ffee2451a40   04   ef   7a   0d    ..z.
                 0x7ffee2451a44   01   00   00   00    ....

          "ram"     0x10d7aef04   72   61   6d   00    ram.

首先,请注意字符串文字在和"ram"中具有相同的地址。同样,分配字符串文字,以便它们在程序的整个生命周期内都可用。您会注意到它的地址远低于其他项目,这表明它位于非常不同的内存区域中。funmain

再次注意,p它只存储字符串的地址,而不是其内容。因此,即使p不复存在,它的价值仍然有效。

现在,我们更改该代码,使其p成为数组,而不是指针:

#include <stdio.h>
#include "dumper.h"

char *fun( void )
{
  char p[] = "ram";

  char *names[] = { "p", "\"ram\"" };
  void *addrs[] = { &p, "ram" };
  size_t sizes[] = { sizeof p, sizeof "ram" };

  puts( "In fun: ");

  dumper( names, addrs, sizes, 2, stdout );

  return p;
}

int main( void )
{
  char *x;
  
  char *names[] = { "x", "\"ram\"" };
  void *addrs[] = { &x, "ram" };
  size_t sizes[] = { sizeof x, sizeof "ram" };

  puts( "Before call to fun:" );
  dumper( names, addrs, sizes, 2, stdout );

  x = fun();

  puts( "After call to fun:" );
  dumper( names, addrs, sizes, 2, stdout );

  return 0;
}

现在我们的输出如下所示:

Before call to fun:
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              x  0x7ffee059ea40   98   ea   59   e0    ..Y.
                 0x7ffee059ea44   fe   7f   00   00    ....

          "ram"     0x10f661efc   72   61   6d   00    ram.

In fun: 
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              p  0x7ffee059e9bc   72   61   6d   00    ram.

          "ram"     0x10f661efc   72   61   6d   00    ram.

After call to fun:
           Item         Address   00   01   02   03
           ----         -------   --   --   --   --
              x  0x7ffee059ea40   bc   e9   59   e0    ..Y.
                 0x7ffee059ea44   fe   7f   00   00    ....

          "ram"     0x10f661efc   72   61   6d   00    ram.

p现在存储字符串本身的内容,而不是存储字符串文字的地址。一旦fun退出,p(以及它所包含的字符串)将不复存在。


  1. 在此处获得
于 2021-09-01T19:03:15.483 回答