如何在内存中分配C字符串?
假设我有一个以这种方式返回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()函数……但如果不是,它存储在哪里? 在堆上? 这是OS每次点击字符串时实现的“一种”动态内存分配,还是什么?
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 * ptr和const char a []基本上是一回事。 看起来他们不是。
我错了吗? 什么是两个版本之间的差异?
谢谢!
当你写作
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[]
基本上是一回事。
不,完全没有,因为数组不是指针 。
我想如果它是一个局部变量,我将无法将它传递给我的main()函数……但如果不是,它存储在哪里?
字符串文字通常存储在只读数据部分( .rodata
)中。 C标准只是说他们有静态存储持续时间。 因此,您可以返回指向此类文字的指针,但不是数组的情况。
在以下示例中, p1
指向的对象具有静态存储持续时间,而数组p2
具有自动存储持续时间。
char *f(void) { const char *p1 = "hello, world"; char p2[] = "hello, world"; return p1; /* allowed */ return p2, /* forbidden */ }
你是对的,他们不是一回事。 char a []是在堆栈上形成的数组,然后填充“blah ..” – 在函数内部,你基本上是`const char a [15]; strcpy(a,“blah blah blah”);“
The const char *ptr = "blah blah blah";
另一方面,它只是一个指针(指针本身在堆栈上),指针指向字符串“blah blah blah”,它存储在其他地方[很可能是“只读数据”]。
如果你试图改变某些东西,你会注意到一个很大的不同,例如: a[2] = 'e';
vs ptr[2] = 'e';
– 第一个将成功,因为你正在修改堆栈值,其中第二个(可能)将失败,因为你正在修改一个只读内存,当然这应该不起作用。
在函数中, 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"
的地址,这是合法的。
实际上它的Scope问题。
有助于了解C程序的内存布局和C中的 变量范围 。
他们不一样。
第一个是指向字符串文字的指针。 指针本身处于自动存储中。 该字符串位于静态只读内存中。 这是不可改变的。
第二个是自动(堆栈) char
数组(正如警告所说,返回的是不合法的)。