在C语言中,字符串参数是否动态分配?

3

假设我有一个具有以下规格的函数:

void example(char* str)

如果我传入一个字符串参数,比如:

example("testing");

“testing”这个值是动态分配在堆上的吗?如果是,那么在使函数调用“example”的作用域被销毁后我仍然能够使用它(并需要稍后释放它),还是它是在栈上的局部变量,所以如果我希望它在哈希映射中持久存在,我需要使用malloc来创建一个新的字符串并将值存储在其中?”谢谢。

@paddy - 尽管C和C++的名称相似,但它们是不同的东西。但你说得对,肯定有这个的重复。 - mouviciel
1
我记得以前回答过这个问题,但由于该问题随后被关闭并标记为重复,因此无法将我的答案列为重复项。因此,我列出了那个重复项,它恰好是用C++表述的。 - paddy
3个回答

8
当你在程序中写入"testing"时,它会被编译为一个字符串字面量,并在编译时分配空间。当你获取指向它的指针时,它指向内存中的那个位置。你不需要使用malloc()来分配它,也不应该使用free()来释放它。但是修改它的内容也不是一个好主意,因为编译器很可能将其放在只读区域(即,它被编译为常量)--例如,下面的程序在我的Linux桌面上崩溃了:
#include <stdio.h>
int main() {
    char *a = "abc\n";
    a[0]='X';
    printf(a);
    return(0);
}

3

在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()一个字符串字面值。
  • 你绝不能写入一个字符串字面值。
  • 一个字符串字面值将在程序的整个生命周期内保持可用,因此它可以作为哈希键。

都明白了吗?还有问题吗?


2
就这个问题而言,在C语言中,字符串字面量不是const char的唯一真正原因是C最初没有以这种方式定义它们。当“const正确性”作为一种编程范式开始流行时,C语言已经有了大量的用户群体,如果字符串字面量被追溯地变成const,将会导致破坏。而C++则没有这个负担。如果你真的想要在代码中做到万无一失,尽可能地声明你的指针为const。甚至像约翰·卡马克(John Carmack)这样的权威人物也成为了一个“const纳粹”,用他自己的话说:http://www.phoronix.com/scan.php?page=news_item&px=MTI3NDQ - Joe Z

1

这通常会被存储为一个字符串字面量,位于可执行文件的只读部分。您可以通过使用-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
$

看起来这个答案解决了相同的问题。


虽然这是一种常见的实现方式,但不能保证它将被存储为只读。 - Keith Thompson
感谢您的评论,我猜这种情况偶尔会出现在代码段中,或者被编译器优化掉了。我会在我的回答中添加更新。 - Nobilis
或者它可以存储在读/写内存中。试图修改字符串常量的行为未定义,因此它可能会“工作”,这取决于实现。 - Keith Thompson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接