C 中的字符串文字真的不可修改吗?



据我所知,字符串文字不能修改,例如:

char* a = "abc";
a[0] = 'c';

这不起作用,因为字符串文本是只读的。我只能在以下情况下修改它:

char a[] = "abc";
a[0] = 'c';

然而,在这篇文章中, 解析$PATH变量并将目录名称保存到字符串数组中,第一个答案在以下两个地方修改了字符串文字:

path_var[j]='';
array[current_colon] = path_var+j+1;

我对 C 不是很熟悉,所以任何解释将不胜感激。

在编程中,有很多规则由你来遵循,即使它们不一定是强制执行的。 而"C 中的字符串文字是不可修改的"就是其中之一。 "不应修改getenv返回的字符串"也是如此。

有一些现实世界的类比适用。 这里有一个:如果你在一个十字路口,而灯是红色的,你不应该过马路。 但是,很多时候,如果你打破规则,越过规则,你可能会侥幸逃脱。 你可能会从警察那里得到一张罚单——也可能不会。 您可能会导致崩溃 - 也可能不会导致崩溃。 但是,如果你运气好,而这些事情都没有发生,这并不意味着闯红灯过十字路口是可以的——这仍然非常违反规则。

同样,在 C 中,如果您编写一些修改字符串文字的代码或从getenv返回的字符串,您可能会侥幸逃脱。 编译器可能会给你一个警告或错误消息,也可能不会。 您的程序可能会崩溃,也可能不会崩溃。 但是,如果程序看起来可以工作,这并不意味着这些字符串实际上是可修改的——它们不是。

您链接的帖子中的代码块:

const char *orig_path_var = getenv("PATH"); 
char *path_var = strdup(orig_path_var ? orig_path_var : "");
const char **array;
array = malloc((nb_colons+1) * sizeof(*array));
array[0] = path_var;
array[current_colon] = path_var+j+1;

第一块:

  • 在第 1 行中,getenv()返回指向由orig_path_var指向的字符串的指针。get_env()返回的字符串应被视为只读字符串,因为如果程序尝试修改行为,则行为是未定义的。
  • 在第二行中,调用strdup()来复制此字符串。执行此操作strdup()方法是调用malloc()并为字符串的大小 + 1 分配内存,然后将字符串复制到内存中。
  • 由于使用了malloc(),因此字符串存储在堆上,这允许我们编辑字符串并对其进行修改。

第二块:

  • 在第 1 行中,我们可以看到array指向一个char *指针数组。数组中有nb_colons+1指针。
  • 然后在第 2 行中,array的第 0 个元素被启动为path_var(请记住,它不是字符串文字,而是字符串的副本)。
  • 在第 3 行中,array的第current_colon个元素设置为path_var+j+1。如果您不了解指针算法,这只是意味着它将path_var的第j+1个字符的地址分配给array[current_colon]

如您所见,代码没有像orig_path_var那样对const字符串文字进行操作。相反,它使用用strdup()制作的副本。这似乎是您的困惑的根源,所以看看这个:

char *strdup(const char *s);

strdup() 函数返回一个指向新字符串的指针,该字符串是字符串 s 的副本。新字符串的内存是用 malloc(3) 获得的,也可以用 free(3) 释放。

上面的文本显示了strdup()根据其手册页执行的操作。

阅读malloc()手册页也可能有所帮助。

在示例中

char* a = "abc";

令牌"abc"在程序映像中生成文本对象,并表示生成该对象地址的表达式。

在示例中

char a[] = "abc";

令牌"abc"用作数组初始值设定项,不表示文本对象。它相当于:

char a[] = { 'a', 'b', 'c', 0 };

"abc"的单个字符值是文本数据,记录在程序图像中的某处并以某种方式,但它们不能作为字符串文本对象访问。

不用说,数组a不是字面意思。修改a不构成修改文字,因为它不是一个文字。

关于评论:

这不起作用,因为字符串文本是只读的。

这是不准确的。ISO C 标准(迄今为止没有版本)没有指定程序尝试修改字符串文本时会发生什么的任何要求。这是未定义的行为。如果实现停止程序并显示某些诊断消息,那是因为未定义的行为,而不是因为它是必需的。

C 实现不需要支持字符串文本修改,这具有以下优点:

  • 符合标准的C程序可以转换为可以刻录到ROM芯片中的映像,这样就可以直接从该ROM映像访问它们的字符串文字,而无需在启动时复制到RAM中。

  • 编译器可以通过利用一个文本是另一个文本的后缀的情况来压缩字符串文本的存储。表达式"string" + 2 == "ring"可以生成 true。由于严格符合的程序不会做类似"ring"[0] = 'w'的事情,由于这是未定义的行为,因此这样的程序将避免成为意外"string"变成"stwing"的惊喜的受害者。

有几个原因,你最好不要修改它们:

  • 首先,操作系统和/或编译器可以强制执行字符串文字的不可写属性,将它们放在只读内存(例如.ROM)或.text段中。
  • 其次,编译器被允许将字符串文字合并在一起,所以如果你修改(并且成功了),你以后可能会得到惊喜,因为其他文字(已经合并,因为例如其中一个是另一个的后缀)显然无缘无故地更改。
  • 如果你需要一个可修改的初始化字符串,你可以通过分配一个带有声明的数组来实现,如 in (你可以自由修改):
char array[100] = "abc"; // initialized to { 'a' ,'b', 'c', '',
//         /* and 96 more '' characters */
// };

相关内容

  • 没有找到相关文章

最新更新