为什么字符串在新编译器中不可修改
Why string is not modifiable in new compilers
在Turbo C/C++时代,您可以像在中一样轻松地修改字符串
char * str = "Hello";
str[1] = '1'; //it will make it H1llo;
现在它们存储在.bss
中,不允许您直接修改它们。为什么?这难道不会使修改字符串变得困难吗?
有什么快速的治疗方法(但没有副作用)吗?我可以使用类似strdup()
的函数,但直接修改字符串确实很有趣。
正如您所观察到的,标准不允许您修改字符串文字的内容,现代编译器的/OS会相应地安排内存权限。
原因是编译器可能会在代码的许多地方看到使用的文字,例如:
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';
。
当然,将数据放入std::string
通常是更好的方法。
在C标准下,修改字符串文字是未定义的行为。
事实上,你可以用旧的编译器来做这件事,这并不意味着它在所有编译器中都是合法的,它们只是过去更宽容,并没有保护你不写这个内存区域。
你可以做:
char str[] = "Hello";
str[1] = '1';
这将创建一个可变的char
数组,并使用字符串文字值的副本(包括