0

在 Turbo C/C++ 时代,您可以轻松地修改字符串,例如

char * str = "Hello";
str[1] = '1'; //it will make it H1llo;

现在它们被存储了.bss,你不能直接修改它们。为什么?这不会使修改字符串变得困难吗?

是否有任何快速修复(但没有副作用)?我可以strdup()喜欢函数,但直接修改字符串真的很有趣。

4

3 回答 3

10

正如您所观察到的,标准不允许您修改字符串文字内容,并且现代编译器/操作系统会相应地安排内存权限。

原因是编译器可能会在代码的很多地方看到一个字面量,例如:

in x.cpp:    std::cerr << "Error" << separator << msg << '\n';

in y.cpp:    if (x == "Error") ...

in z.cpp:    q = "StackOverflowError";

避免所有这些字符串文字分别出现在可执行映像和加载的进程内存中是非常可取的;相反,编译器可以安排一个包含“StackOverFlowError\0”的内存区域,并在使用点使用指向相关起始字符(无论是“S”还是“E”)的指针。

如果您被允许修改该值 - 可能决定您希望 x.cpp 显示“Alert”而不是“Error”,它也可能无意中破坏 y.cpp 和 z.cpp 中的代码。

有快速修复吗?

好吧,如果取决于你认为什么坏了。如果您的意思是修改字符串文字的方法,那么不...由于上述原因,这是未定义的行为,并且内存保护机制将随操作系统等而变化。如果您的意思是能够修改文本数据类似的方式,然后是的:char* s = "abc";放入s堆栈,但它指向您观察到的那个 .bss 数据。如果你改为写:

char s[] = "abc";

然后s仍然在堆栈上,但现在是一个有 4 个字符空间的数组,字符串文字仍然在 .bss 中,但是每当该行运行时,它就会从后者复制到前者,之后您就可以修改基于堆栈的复制 ala s[1] = 'x';

当然,将数据放入 astd::string通常是更好的方法。

于 2013-06-27T08:14:42.433 回答
3

修改字符串文字在 C 标准下是未定义的行为。

您可以使用较旧的编译器执行此操作这一事实并不意味着它在所有编译器中都是合法的,它们只是过去更加宽松,并没有保护您免于编写此内存区域。

你可以做:

char str[] = "Hello";
str[1] = '1';

这将创建一个可变数组,并使用字符串文字值的副本(包括终止符)char对其进行初始化。\0

于 2013-06-27T08:06:15.087 回答
2

回答“为什么”的问题。其他人已经提到的微不足道的答案是“这是未定义的行为”。这意味着该标准允许编译器执行编译器想要的任何操作。

但为什么它是未定义的行为?原因是在大型软件项目中,您通常会得到数千个重复的字符串,也许您有一个宏,如果它检测到一些不一致并打印出一条消息,它会使程序崩溃。这个宏在数千个地方重复,每次都打印相同的消息。如果字符串是可修改的,则消息必须在最终的二进制文件中重复数千次。为了防止许多链接器在二进制文件中执行重复数据删除,这意味着如果您printf("foo\n"); printf("foo\n");的程序中有两个"foo\n"字符串将位于相同的内存位置。现在想象你有:char *foo = "foo\n"; printf("foo\n");编译器可能认为它可以对字符串和你程序中的其他地方进行重复数据删除*foo = 'b';. 现在 printf 将不正确。为了防止这种情况,修改字符串文字是未定义的行为,现代编译器将使用该事实来删除字符串(比我这里的示例更高级),并希望以修改字符串文字会崩溃的方式生成程序。

于 2013-06-27T08:26:09.200 回答