假设我有一个具有以下规格的函数:
void example(char* str)
如果我传入一个字符串参数,比如:
example("testing");
“testing”这个值是动态分配在堆上的吗?如果是,那么在使函数调用“example”的作用域被销毁后我仍然能够使用它(并需要稍后释放它),还是它是在栈上的局部变量,所以如果我希望它在哈希映射中持久存在,我需要使用malloc来创建一个新的字符串并将值存储在其中?”谢谢。
假设我有一个具有以下规格的函数:
void example(char* str)
如果我传入一个字符串参数,比如:
example("testing");
"testing"
时,它会被编译为一个字符串字面量,并在编译时分配空间。当你获取指向它的指针时,它指向内存中的那个位置。你不需要使用malloc()
来分配它,也不应该使用free()
来释放它。但是修改它的内容也不是一个好主意,因为编译器很可能将其放在只读区域(即,它被编译为常量)--例如,下面的程序在我的Linux桌面上崩溃了:#include <stdio.h>
int main() {
char *a = "abc\n";
a[0]='X';
printf(a);
return(0);
}
在C语言中,被双引号括起来的字符串"like this"被称为 字符串字面值。
像上面给出的那样,字符串字面值不是动态分配的。通常它们是在编译或链接时分配的,并且可能分配在只读内存中。(这就是为什么在C++中,字符串字面值是const char
而不是char
的原因。)
在幕后,一些编译器将 "testing" 存储在一个字符串表中,生成一个特殊的指针指向它。这大致相当于:
char *const compiler_generated_pointer_to_testing = (char *) (compiler_generated_string_table + 12345);
...
const char compiler_generated_string_table[] = {
...
't', 'e', 's', 't', 'i', 'n', 'g', 0,
...
};
...
example( compiler_generated_pointer_to_testing );
这只是其中一种实现方式,许多其他实现方式也是合法的。无论如何,具体的实现细节可能并不重要。真正需要记住的主要点有:
const
,即使编译器不要求你将指向它们的指针声明为const char *
。foo("testing")
和bar("testing")
)不能保证是不同的指针,也不能保证它们是相同的指针值。free()
一个字符串字面值。都明白了吗?还有问题吗?
const char
的唯一真正原因是C最初没有以这种方式定义它们。当“const正确性”作为一种编程范式开始流行时,C语言已经有了大量的用户群体,如果字符串字面量被追溯地变成const
,将会导致破坏。而C++则没有这个负担。如果你真的想要在代码中做到万无一失,尽可能地声明你的指针为const
。甚至像约翰·卡马克(John Carmack)这样的权威人物也成为了一个“const纳粹”,用他自己的话说:http://www.phoronix.com/scan.php?page=news_item&px=MTI3NDQ - Joe Z这通常会被存储为一个字符串字面量,位于可执行文件的只读部分。您可以通过使用-S
标志编译程序来手动验证。它将生成一个名为name_of_your_app.s
的汇编可执行文件,在其中您可以找到字符串字面量(它将位于所谓的data segment中)。
偶尔编译器会将其放入代码段中,或者根据优化级别直接进行优化处理(最容易通过创建一个未在任何地方使用的字符串字面量,然后在GCC上使用-O3
标志进行编译来检查)。
以下是一个(人为制造的)示例:
int main()
{
char *a = "Hai!";
return 0;
}
$ gcc -S main.c
main.c: In function ‘main’:
main.c:9:11: warning: unused variable ‘a’ [-Wunused-variable]
$ cat main.s | grep Hai
.string "Hai!"
$ gcc -S -O3 main.c
main.c: In function ‘main’:
main.c:9:11: warning: unused variable ‘a’ [-Wunused-variable]
$ cat main.s | grep Hai
$
看起来这个答案解决了相同的问题。