字符串不变性是通过语句起作用,还是通过语句中的字符串起作用?
例如,我了解以下代码将在堆上分配两个字符串。
string s = "hello ";
s += "world!";
"hello" 将保留在堆上,直到垃圾收集;并且 s 现在引用“hello world!” 在堆上。但是,以下行在堆上分配了多少个字符串...1 或 2?另外,是否有工具/方法来验证结果?
string s = "goodbye " + "cruel world!";
字符串不变性是通过语句起作用,还是通过语句中的字符串起作用?
例如,我了解以下代码将在堆上分配两个字符串。
string s = "hello ";
s += "world!";
"hello" 将保留在堆上,直到垃圾收集;并且 s 现在引用“hello world!” 在堆上。但是,以下行在堆上分配了多少个字符串...1 或 2?另外,是否有工具/方法来验证结果?
string s = "goodbye " + "cruel world!";
编译器对字符串连接有特殊处理,这就是为什么第二个例子只有一个字符串。而“实习”意味着即使你运行这条线 20000 次,仍然只有 1 个字符串。
重新测试结果......最简单的方法(在这种情况下)可能是查看反射器:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] string s)
L_0000: ldstr "goodbye cruel world!"
L_0005: stloc.0
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(string)
L_000c: ret
}
如您所见 ( ldstr
),编译器已经为您完成了这项工作。
实际上,可能 3. 一个 const 字符串表示“goodbye”,一个 const 字符串表示“cruel world”,然后一个新字符串表示结果。
您可以通过查看生成的代码来确定。这取决于编译器,(实际上,取决于语言,这并不明显)但您可以通过使用 -a 标志(我认为,查看手册页)来读取 g++ 的输出以获取中间代码.
不要相信你对字符串的“了解”。您可能会查看源代码以了解字符串的实现。例如你的例子:
string s = "goodbye " + "cruel world!";
在java中会分配一个字符串。Java 玩了一些非常可爱的技巧,而且很难被超越——除非你需要,否则永远不要优化!
然而,目前,据我所知,使用这个:
String s="";
for(int i=0;i<1000;i++)
s+=" ";
创建一个 1000 个空格的字符串仍然往往效率极低
在循环中追加非常糟糕,但除此之外它可能与 StringBuilder 一样有效。
这里要小心,因为在编译时知道字符串值时,编译器可以进行一些非常不同的优化。如果您使用的字符串直到运行时才知道(从配置文件、数据库或用户输入中提取),您将看到一些非常不同的 IL。
如果您只是要做一两个字符串连接,我不会担心。
但是,如果你有很多连接,或者你有一个循环,那么你肯定要采取预防措施。在 Java 世界中,这意味着您使用 StringBuffer 而不是连接字符串。
如果不只是在一行中,则可以通过将第一个字符串放入 StringBuffer,进行连接并返回结果字符串来完成两个字符串的连接。
自己创建 StringBuffer 可能看起来有点矫枉过正,但无论如何都会发生这种情况。-
无论如何不要过早地优化,但不要低估字符串连接的性能有多差。这不是对象的创建,而是它导致的 GC 工作。
(ASP.NET 升级工程师)Tess Ferrnandez 的博客上有一个实验室,它展示了字符串连接如何使服务器瘫痪的(相当极端的,理所当然的)示例。
如果编译器是“智能”的,它只会是一个带有“再见残酷世界!”的字符串。