【发布时间】:2010-09-05 19:56:21
【问题描述】:
对于编译器和检查字符串是否为空的最佳实践,哪个更有效?
- 检查字符串长度是否== 0
- 检查字符串是否为空(strVar == "")
另外,答案是否取决于语言?
【问题讨论】:
标签: string optimization language-agnostic compiler-construction
对于编译器和检查字符串是否为空的最佳实践,哪个更有效?
另外,答案是否取决于语言?
【问题讨论】:
标签: string optimization language-agnostic compiler-construction
是的,这取决于语言,因为不同语言的字符串存储不同。
Length = 0。[0] == 0。 .IsNullOrEmpty.等等。
【讨论】:
在使用 C 样式(空终止)字符串的语言中,与 "" 相比会更快。这是一个 O(1) 操作,而 C 风格字符串的长度是 O(n)。
在将长度存储为字符串对象的一部分的语言(C#、Java、...)中,检查长度也是 O(1)。在这种情况下,直接检查长度会更快,因为它避免了构造新的空字符串的开销。
【讨论】:
在使用 C 风格(以空结尾)字符串的语言中,与 "" 相比会更快
其实最好检查一下字符串的第一个char是否为'\0':
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
在 Perl 中还有第三个选项,即字符串未定义。这与 C 中的 NULL 指针有点不同,只是因为访问未定义字符串时不会出现分段错误。
【讨论】:
在 .Net 中:
string.IsNullOrEmpty( nystr );
字符串可以为空,所以 .Length 有时会抛出 NullReferenceException
【讨论】:
String.IsNullOrEmpty() 仅适用于 .net 2.0 及更高版本,对于 .net 1/1.1,我倾向于使用:
if (inputString == null || inputString == String.Empty)
{
// String is null or empty, do something clever here. Or just expload.
}
我使用 String.Empty 而不是 "" 因为 "" 将创建一个对象,而 String.Empty 不会 - 我知道它是一些小而琐碎的东西,但是当我不需要它们时,我仍然宁愿不创建对象! (Source)
【讨论】:
对于 C 字符串,
if (s[0] == 0)
会比任何一个都快
if (strlen(s) == 0)
或
if (strcmp(s, "") == 0)
因为您将避免函数调用的开销。
【讨论】:
假设您的问题是 .NET:
如果你想验证你的字符串是否为 null 以及使用 IsNullOrEmpty,如果你已经知道你的字符串不为 null,例如在检查 TextBox.Text 等时,不要使用 IsNullOrEmpty,然后出现你的问题。
所以在我看来,String.Length 的性能不如字符串比较。
我对它进行了测试(我也用 C# 进行了测试,结果相同):
Module Module1
Sub Main()
Dim myString = ""
Dim a, b, c, d As Long
Console.WriteLine("Way 1...")
a = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString = ""
Next
b = Now.Ticks
Console.WriteLine("Way 2...")
c = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString.Length = 0
Next
d = Now.Ticks
Dim way1 = b - a, way2 = d - c
Console.WriteLine("way 1 took {0} ticks", way1)
Console.WriteLine("way 2 took {0} ticks", way2)
Console.WriteLine("way 1 took {0} ticks more than way 2", way1 - way2)
Console.Read()
End Sub
End Module
结果:
Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2
这意味着比较不仅仅是字符串长度检查。
【讨论】:
读完这个帖子后,我做了一个小实验,得出了两个截然不同且有趣的发现。
考虑以下内容。
strInstallString "1" string
以上内容是从 Visual Studio 调试器的本地窗口复制而来的。以下三个示例都使用相同的值。
if (strInstallString == "") === if (strInstallString == string.Empty)
以下是 Visual Studio 2013 调试器的反汇编窗口中显示的这两种基本相同情况的代码。
if ( strInstallString == "" )
003126FB mov edx,dword ptr ds:[31B2184h]
00312701 mov ecx,dword ptr [ebp-50h]
00312704 call 59DEC0B0 ; On return, EAX = 0x00000000.
00312709 mov dword ptr [ebp-9Ch],eax
0031270F cmp dword ptr [ebp-9Ch],0
00312716 sete al
00312719 movzx eax,al
0031271C mov dword ptr [ebp-64h],eax
0031271F cmp dword ptr [ebp-64h],0
00312723 jne 00312750
if ( strInstallString == string.Empty )
00452443 mov edx,dword ptr ds:[3282184h]
00452449 mov ecx,dword ptr [ebp-50h]
0045244C call 59DEC0B0 ; On return, EAX = 0x00000000.
00452451 mov dword ptr [ebp-9Ch],eax
00452457 cmp dword ptr [ebp-9Ch],0
0045245E sete al
00452461 movzx eax,al
00452464 mov dword ptr [ebp-64h],eax
00452467 cmp dword ptr [ebp-64h],0
0045246B jne 00452498
if (strInstallString == string.Empty) 没有显着差异
if ( strInstallString.Length == 0 )
003E284B mov ecx,dword ptr [ebp-50h]
003E284E cmp dword ptr [ecx],ecx
003E2850 call 5ACBC87E ; On return, EAX = 0x00000001.
003E2855 mov dword ptr [ebp-9Ch],eax
003E285B cmp dword ptr [ebp-9Ch],0
003E2862 setne al
003E2865 movzx eax,al
003E2868 mov dword ptr [ebp-64h],eax
003E286B cmp dword ptr [ebp-64h],0
003E286F jne 003E289C
从以上由.NET Framework 4.5 的NGEN 模块生成的机器代码清单,我得出以下结论。
针对空字符串文字的相等性测试和 System.string 类的静态 string.Empty 属性实际上是相同的。两个代码 sn-ps 之间的唯一区别是第一个 move 指令的来源,两者都是相对于 ds 的偏移量,这意味着两者都引用烘焙常量。
测试空字符串是否相等,无论是作为文字还是 string.Empty 属性,都会建立一个有两个参数的函数调用,它通过返回零来指示 不等式。我基于几个月前执行的其他测试得出这个结论,在这些测试中,我遵循了一些我自己的代码,跨越了托管/非托管的鸿沟并返回。在所有情况下,任何需要两个或更多参数的调用都将第一个参数放在寄存器 ECX 中,将第二个参数放在寄存器 EDX 中。我不记得后来的论点是如何通过的。尽管如此,呼叫设置看起来更像 __fastcall 而不是 __stdcall。同样,预期的返回值总是出现在寄存器 EAX 中,这几乎是通用的。
测试字符串的长度会设置一个单参数函数调用,它返回 1(在寄存器 EAX 中),这恰好是被测试字符串的长度。
鉴于立即可见的机器代码几乎相同,我能想象到的唯一原因是字符串相等性在 Shinny 报告的字符串长度上具有更好的性能是执行比较的双参数函数明显优于从字符串实例中读取长度的单参数函数。
结论
原则上,我避免将空字符串作为文字进行比较,因为空字符串文字在源代码中可能会显得模棱两可。为此,我的 .NET 帮助程序类早就将空字符串定义为常量。尽管我使用 string.Empty 进行直接的内联比较,但该常量仍可用于定义其他值为空字符串的常量,因为不能为常量分配 string.Empty 作为它的值。
这个练习一劳永逸地解决了我对与 string.Empty 或我的助手类定义的常量进行比较的成本(如果有的话)的任何担忧。
但是,它也提出了一个令人费解的问题来替换它;为什么与 string.Empty 比较比测试字符串的长度更有效?或者由于循环的实现方式,Shinny 使用的测试是否无效? (我觉得这很难相信,但话说回来,我以前也被愚弄过,我相信你也一样!)
我一直认为 system.string 对象是计数字符串,与我们早已从 COM 知道的长期建立的基本字符串 (BSTR) 基本相似。
【讨论】:
其实IMO最好的判断方法是字符串类的IsNullOrEmpty()方法。
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.
更新:我假设 .Net,在其他语言中,这可能会有所不同。
【讨论】:
在这种情况下,直接检查长度更快,因为它避免了构造新的空字符串的开销。
@DerekPark:这并不总是正确的。 "" 是一个字符串字面量,所以在 Java 中,几乎可以肯定它已经被实习了。
【讨论】:
@内森
其实最好检查一下字符串的第一个char是否为'\0':
我几乎提到了这一点,但最后没有提到,因为使用空字符串调用strcmp() 并直接检查字符串中的第一个字符都是 O(1)。您基本上只需为额外的函数调用付费,这非常便宜。但是,如果您真的需要绝对的最佳速度,那么一定要进行直接的 first-char-to-0 比较。
老实说,我一直使用strlen() == 0,因为我从来没有写过一个程序,这实际上是一个可测量的性能问题,我认为这是表达检查的最易读的方式。 p>
【讨论】:
同样,不懂语言,是无法分辨的。
但是,我建议您选择对后续维护程序员最有意义的技术,并且必须维护您的工作。
我建议编写一个明确执行您想要的功能的函数,例如
#define IS_EMPTY(s) ((s)[0]==0)
或类似的。现在毫无疑问,您正在检查。
【讨论】: