15

假设我有一个以这种方式返回 C 字符串的简单函数:

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

我这样从 main() 调用 getString() :

  const char * s = getString();

1)根据gdb,变量ptr存储在栈上,但是ptr指向的字符串却没有:

(gdb) p &ptr
$1 = (const char **) 0x7fffffffe688

(gdb) p ptr
$2 = 0x4009fc "blah blah"

这是否意味着“blah blah”不是getString() 中的局部变量?

我想如果它是一个局部变量,我将无法将它传递给我的 main() 函数......但如果不是,它存储在哪里?在堆上?这是操作系统每次遇到字符串时都会实现的“一种”动态内存分配,还是什么?

2)如果我使用数组而不是指针,这样:

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}

编译器警告我:

warning: address of local variable ‘a’ returned

(当然程序可以编译,但它不起作用)。

实际上,如果我问 gdb,我会得到

(gdb) p &a
$2 = (const char (*)[15]) 0x7fffffffe690

但我认为const char * ptrconst char a[]基本上是一回事。看起来他们不是。

我错了吗?这两个版本之间究竟有什么区别?

谢谢!

4

5 回答 5

12

当你写

const char *ptr = "blah blah";

然后发生以下情况:编译器生成一个char []包含内容的常量字符串(类型为 )并将其存储在可执行文件的数据段中的某个位置(它基本上具有与使用关键字"blah blah"声明的变量相似的存储持续时间)。static

然后,这个字符串的地址,它在程序的整个生命周期内都是有效的,存储在ptr指针中,然后返回。一切皆好。

这是否意味着它"blah blah"不是 getString() 中的局部变量?

让我用一句残缺的英文句子来回应:是的,不是。

但是,当您声明一个数组时,如

const char a[] = "blah blah";

那么编译器不会生成静态字符串。a (实际上,这是初始化字符串时的一种特殊情况。)然后它会生成代码,为数组分配足够大的堆栈内存(它不是指针!)并用字符串的字节填充它。这a 实际上是一个局部变量,返回其地址会导致未定义的行为。

所以...

但我是这么想的,const char *ptr而且const char a[]基本上是一样的。

不,一点也不,因为数组不是指针

于 2012-12-22T16:12:00.033 回答
5

我想如果它是一个局部变量,我将无法将它传递给我的 main() 函数......但如果不是,它存储在哪里?

字符串文字通常存储在只读数据部分 ( .rodata) 中。C标准只是说它们具有静态存储持续时间。因此,您可以返回指向此类文字的指针,但数组并非如此。

在以下示例中,指向的对象p1具有静态存储持续时间,而数组p2具有自动存储持续时间。

char *f(void)
{
    const char *p1 = "hello, world";
    char p2[] = "hello, world";

    return p1; /* allowed */
    return p2, /* forbidden */
}
于 2012-12-22T16:16:05.033 回答
2

在您的函数中, a[]数组的范围在函数内getString2()。它的局部数组变量

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}  

在上述情况下,字符串"blah blah blah"复制了第一个a[],并且您尝试使用return a语句返回该数组,但不是常量字符串。

在第一个代码中getString()ptr = "blah blah";ptr 指向具有全局范围的内存。

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

"blah blah"在这种情况下,您返回合法的常量字符串的地址。

所以实际上它的范围问题。

了解C 程序的内存布局和.C

于 2012-12-22T16:07:43.580 回答
2

你是对的,因为它们不是一回事。char a[] 是在堆栈上形成的数组,然后用“blah..”填充 - 在函数内部,您基本上有 `const char a[15]; strcpy(a, "胡说八道");"

The const char *ptr = "blah blah blah";另一方面,它只是一个指针(指针本身在堆栈上),并且指针指向字符串“blah blah blah”,该字符串存储在其他地方[最有可能在“只读数据”中]。

如果您尝试更改某些内容,您会注意到很大的不同,例如: a[2] = 'e';vs ptr[2] = 'e';- 第一个将成功,因为您正在修改堆栈值,而第二个(可能)将失败,因为您正在修改只读内存,这当然不应该工作。

于 2012-12-22T16:09:00.023 回答
1

它们是不相同的。

第一个是指向字符串文字的指针。指针本身是自动存储的。该字符串位于静态的只读内存中。它是不可变的。

第二个是自动(堆栈)char数组(正如警告所说,返回是不合法的)。

于 2012-12-22T16:07:47.780 回答