小数点
的常量。字符串也一样,字符串常量声明有两种方式:var a string = `hello world`
var b string = "hello world"
单撇号
或者是双引号
时,说明其是一个字符串。解析函数如下func (s *scanner) next() {
...
c := s.getr()
for c == ' ' || c == '\t' || c == '\n' && !nlsemi || c == '\r' {
c = s.getr()
}
// token start
s.line, s.col = s.source.line0, s.source.col0
if isLetter(c) || c >= utf8.RuneSelf && s.isIdentRune(c, true) {
s.ident()
return
}
switch c {
case '"':
s.stdString()
case '`':
s.rawString()
...
}
单撇号
会调用rawString,双引号
会调用stdString,两者略微有所不同单撇号
比较简单,始终要寻找下一个配对的单撇号
func (s *scanner) rawString() {
s.startLit()
for {
r := s.getr()
if r == '`' {
break
}
if r < 0 {
s.errh(s.line, s.col, "string not terminated")
break
}
}
// We leave CRs in the string since they are part of the
// literal (even though they are not part of the literal
// value).
s.nlsemi = true
s.lit = string(s.stopLit())
s.kind = StringLit
s.tok = _Literal
}
双引号
有所不同,其调用stdString函数。func (s *scanner) stdString() {
s.startLit()
for {
r := s.getr()
if r == '"' {
break
}
if r == '\\' {
s.escape('"')
continue
}
if r == '\n' {
s.ungetr() // assume newline is not part of literal
s.error("newline in string")
break
}
if r < 0 {
s.errh(s.line, s.col, "string not terminated")
break
}
}
s.nlsemi = true
s.lit = string(s.stopLit())
s.kind = StringLit
s.tok = _Literal
}
双引号
则直接退出,当出现了字符\
,代表会对后面的字符进行转义。双引号
不能出现如下的换行符,会报错。
str := " 微信:
1131052403 "
// go/src/cmd/compile/internal/gc
func (p *noder) basicLit(lit *syntax.BasicLit) Val {
case syntax.StringLit:
if len(s) > 0 && s[0] == '`' {
// strip carriage returns from raw string
s = strings.Replace(s, "\r", "", -1)
}
// Ignore errors because package syntax already reported them.
u, _ := strconv.Unquote(s)
return Val{U: u}
default:
panic("unhandled BasicLit kind")
}
}
strings.Replace
方法将原生字符串中最后的换行符删除并对字符串 Token 进行 Unquote(strconv.Unquote(s)
),也就是去掉字符串两边的引号等无关干扰,还原其本来的面目。例如将""hello"" 转换为 "hello""hello"+"world"
,会在noder.sum函数中完成拼接。/usr/local/go/src/cmd/compile/internal/gc/noder.go
func (p *noder) sum(x syntax.Expr) *Node {
for i := len(adds) - 1; i >= 0; i-- {
add := adds[i]
r := p.expr(add.Y)
if Isconst(r, CTSTR) && r.Sym == nil {
if nstr != nil {
// Collapse r into nstr instead of adding to n.
chunks = append(chunks, r.Val().U.(string))
continue
}
nstr = r
chunks = append(chunks, nstr.Val().U.(string))
} else {
if len(chunks) > 1 {
nstr.SetVal(Val{U: strings.Join(chunks, "")})
}
nstr = nil
chunks = chunks[:0]
}
n = p.nod(add, OADD, n, r)
}
if len(chunks) > 1 {
nstr.SetVal(Val{U: strings.Join(chunks, "")})
}
return n
}
var a = "hello"
str := a + "xxs"
typecheck1
函数进行赋值和字符串拼接语义。go/src/cmd/compile/internal/gc/walk.go
func walkexpr(n *Node, init *Nodes) *Node {
case OADDSTR:
n = addstr(n, init)
}
addstr(n, init)
切片
传入func addstr(n *Node, init *Nodes) *Node {
// build list of string arguments
args := []*Node{buf}
for _, n2 := range n.List.Slice() {
args = append(args, conv(n2, types.Types[TSTRING]))
}
var fn string
if c <= 5 {
// small numbers of strings use direct runtime helpers.
// note: orderexpr knows this cutoff too.
fn = fmt.Sprintf("concatstring%d", c)
} else {
// large numbers of strings are passed to the runtime as a slice.
fn = "concatstrings"
t := types.NewSlice(types.Types[TSTRING])
slice := nod(OCOMPLIT, nil, typenod(t))
if prealloc[n] != nil {
prealloc[slice] = prealloc[n]
}
slice.List.Set(args[1:]) // skip buf arg
args = []*Node{buf, slice}
slice.Esc = EscNone
}
cat := syslook(fn)
r := nod(OCALL, cat, nil)
r.List.Set(args)
r = typecheck(r, ctxExpr)
r = walkexpr(r, init)
r.Type = n.Type
return r
}
type StringHeader struct {
Data uintptr
Len int
}
/usr/local/go/src/runtime/string.go
func concatstrings(buf *tmpBuf, a []string) string {
idx := 0
l := 0
count := 0
for i, x := range a {
n := len(x)
if n == 0 {
continue
}
if l+n < l {
throw("string concatenation too long")
}
l += n
count++
idx = i
}
if count == 0 {
return ""
}
// If there is just one string and either it is not on the stack
// or our result does not escape the calling frame (buf != nil),
// then we can return that string directly.
if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {
return a[idx]
}
s, b := rawstringtmp(buf, l)
for _, x := range a {
copy(b, x)
b = b[len(x):]
}
return s
}
func concatstring2(buf *tmpBuf, a [2]string) string {
return concatstrings(buf, a[:])
}
func concatstring3(buf *tmpBuf, a [3]string) string {
return concatstrings(buf, a[:])
}
func concatstring4(buf *tmpBuf, a [4]string) string {
return concatstrings(buf, a[:])
}
func concatstring5(buf *tmpBuf, a [5]string) string {
return concatstrings(buf, a[:])
}
大于
32字节时,会请求分配内存。Copy
拷贝func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
if buf != nil && l <= len(buf) {
b = buf[:l]
s = slicebytetostringtmp(b)
} else {
s, b = rawstring(l)
}
return
}
a := "微信:1131052403"
b := []byte(a)
c := string(b)
func slicebytetostring(buf *tmpBuf, b []byte) (str string) {
...
var p unsafe.Pointer
if buf != nil && len(b) <= len(buf) {
p = unsafe.Pointer(buf)
} else {
p = mallocgc(uintptr(len(b)), nil, false)
}
stringStructOf(&str).str = p
stringStructOf(&str).len = len(b)
memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))
return
}
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
*buf = tmpBuf{}
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s)
return b
}
单撇号
或者是双引号
两种方法大于
32字节时,会请求分配内存