Go语言如何进行字符串比较?

huangapple go评论97阅读模式
英文:

How does Go do string comparison?

问题

Go支持字符串比较而无需任何特殊函数。Go运行时是否在幕后进行工作以进行字符串字面量的比较?

英文:

http://golang.org/ref/spec#Comparison_operators

Go supports string comparison without any special functions. Does the Go runtime do work behind the scenes to make comparisons of string literals?

答案1

得分: 57

如您在以下的汇编转储中所见,字符串比较在短路检查(第11行)后,将委托给运行时的runtime.eqstring函数(第17行),以查看两个操作数是否为相同的内存字符串(第11行):

$ cat foo.go
package main

func main() {
        a := "hello"
        b := "world"
        _ = a == b
}

$ go tool 6g -S foo.go
--- 程序列表 "main" ---
0000 (foo.go:3) TEXT    main+0(SB),$40-0
0001 (foo.go:3) LOCALS  ,$0
0002 (foo.go:4) LEAQ    go.string."hello"+0(SB),BX
0003 (foo.go:4) MOVQ    (BX),SI
0004 (foo.go:4) MOVQ    8(BX),CX
0005 (foo.go:5) LEAQ    go.string."world"+0(SB),BX
0006 (foo.go:5) MOVQ    (BX),DX
0007 (foo.go:5) MOVQ    8(BX),AX
0008 (foo.go:6) JMP     ,11
0009 (foo.go:6) MOVQ    $1,AX
0010 (foo.go:6) JMP     ,23
0011 (foo.go:6) CMPQ    CX,AX
0012 (foo.go:6) JNE     ,22
0013 (foo.go:6) MOVQ    SI,(SP)
0014 (foo.go:6) MOVQ    CX,8(SP)
0015 (foo.go:6) MOVQ    DX,16(SP)
0016 (foo.go:6) MOVQ    AX,24(SP)
0017 (foo.go:6) CALL    ,runtime.eqstring+0(SB)
0018 (foo.go:6) MOVBQZX 32(SP),BX
0019 (foo.go:6) CMPB    BX,$0
0020 (foo.go:6) JEQ     ,22
0021 (foo.go:6) JMP     ,9
0022 (foo.go:6) MOVQ    $0,AX
0023 (foo.go:7) RET     ,

--- 程序列表 "init" ---
0024 (foo.go:7) TEXT    init+0(SB),$0-0
0025 (foo.go:7) MOVBQZX initdone·+0(SB),AX
0026 (foo.go:7) LOCALS  ,$0
0027 (foo.go:7) CMPB    AX,$0
0028 (foo.go:7) JEQ     ,34
0029 (foo.go:7) CMPB    AX,$2
0030 (foo.go:7) JNE     ,32
0031 (foo.go:7) RET     ,
0032 (foo.go:7) CALL    ,runtime.throwinit+0(SB)
0033 (foo.go:7) UNDEF   ,
0034 (foo.go:7) MOVB    $2,initdone·+0(SB)
0035 (foo.go:7) RET     ,

除非您正在编写编译器或运行时,否则这不应过多关注:只需按照规范定义使用运算符,并期望比较的时间复杂度为O(n),其中n为字符串的长度。

英文:

As you can see in the following assembly dump, string comparison is delegated to a runtime.eqstring function from the runtime (line 17) after a short circuit check to see if the two operands are the same in-memory string (line 11):

$ cat foo.go
package main

func main() {
        a := "hello"
        b := "world"
        _ = a == b
}

$ go tool 6g -S foo.go
--- prog list "main" ---
0000 (foo.go:3) TEXT    main+0(SB),$40-0
0001 (foo.go:3) LOCALS  ,$0
0002 (foo.go:4) LEAQ    go.string."hello"+0(SB),BX
0003 (foo.go:4) MOVQ    (BX),SI
0004 (foo.go:4) MOVQ    8(BX),CX
0005 (foo.go:5) LEAQ    go.string."world"+0(SB),BX
0006 (foo.go:5) MOVQ    (BX),DX
0007 (foo.go:5) MOVQ    8(BX),AX
0008 (foo.go:6) JMP     ,11
0009 (foo.go:6) MOVQ    $1,AX
0010 (foo.go:6) JMP     ,23
0011 (foo.go:6) CMPQ    CX,AX
0012 (foo.go:6) JNE     ,22
0013 (foo.go:6) MOVQ    SI,(SP)
0014 (foo.go:6) MOVQ    CX,8(SP)
0015 (foo.go:6) MOVQ    DX,16(SP)
0016 (foo.go:6) MOVQ    AX,24(SP)
0017 (foo.go:6) CALL    ,runtime.eqstring+0(SB)
0018 (foo.go:6) MOVBQZX 32(SP),BX
0019 (foo.go:6) CMPB    BX,$0
0020 (foo.go:6) JEQ     ,22
0021 (foo.go:6) JMP     ,9
0022 (foo.go:6) MOVQ    $0,AX
0023 (foo.go:7) RET     ,

--- prog list "init" ---
0024 (foo.go:7) TEXT    init+0(SB),$0-0
0025 (foo.go:7) MOVBQZX initdone·+0(SB),AX
0026 (foo.go:7) LOCALS  ,$0
0027 (foo.go:7) CMPB    AX,$0
0028 (foo.go:7) JEQ     ,34
0029 (foo.go:7) CMPB    AX,$2
0030 (foo.go:7) JNE     ,32
0031 (foo.go:7) RET     ,
0032 (foo.go:7) CALL    ,runtime.throwinit+0(SB)
0033 (foo.go:7) UNDEF   ,
0034 (foo.go:7) MOVB    $2,initdone·+0(SB)
0035 (foo.go:7) RET     ,

Unless you are working on the compiler or runtime, this shouldn't concern you too much: just use the operators as the spec defines, and expect that the comparison to be O(n) with the length of the string.

答案2

得分: 31

runtime/string.goc(go1.3):

func eqstring(s1 String, s2 String) (v bool) {
    if s1.len != s2.len {
        v = false
        return
    }
    if s1.str == s2.str {
        v = true
        return
    }
    v = runtime·memeq(s1.str, s2.str, s1.len)
}

int32
runtime·strcmp(byte *s1, byte *s2)
{
    uintptr i
    byte c1, c2

    for i = 0;; i++ {
        c1 = s1[i]
        c2 = s2[i]
        if c1 < c2 {
            return -1
        }
        if c1 > c2 {
            return +1
        }
        if c1 == 0 {
            return 0
        }
    }
}

注意runtime· 分隔符是一个 Unicode 中点,而不是句号。[1]: https://code.google.com/p/go/source/browse/src/pkg/runtime/string.goc

英文:

runtime/string.goc (go1.3):

func eqstring(s1 String, s2 String) (v bool) {
	if(s1.len != s2.len) {
		v = false;
		return;
	}
	if(s1.str == s2.str) {
		v = true;
		return;
	}
	v = runtime&#183;memeq(s1.str, s2.str, s1.len);
}

int32
runtime&#183;strcmp(byte *s1, byte *s2)
{
	uintptr i;
	byte c1, c2;

	for(i=0;; i++) {
		c1 = s1[i];
		c2 = s2[i];
		if(c1 &lt; c2)
			return -1;
		if(c1 &gt; c2)
			return +1;
		if(c1 == 0)
			return 0;
	}
}

Note: The runtime&#183; separator is a Unicode middle-dot, not a period.
[1]: https://code.google.com/p/go/source/browse/src/pkg/runtime/string.goc

huangapple
  • 本文由 发表于 2013年11月27日 10:47:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/20232976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定