Golang源码阅读笔记 - String

String用法说明

src/buildin/buildin.go文件中,对golang内建数据类型做了详细的描述,关于string的说明如下:

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string
  1. 从中我们可以获取以下信息:
    • 字符串是8比特字节的集合
    • 字符串一般但不一定是UTF-8文本
      • 个人理解,golang默认是使用UTF-8编码的,但是支持用户修改编码方式
    • 字符串可以为空"",但不能为nil
    • 字符串不可修改

string底层数据结构

// 代码位置:/src/runtime/string.go
type stringStruct struct {
	str unsafe.Pointer
	len int
}
  1. String底层由两个字段组成
    • str unsafe.Pointer: 指向底层数组的指针
    • len int: 字符串长度
  2. 由底层数据结构可知,len(string)的时间复杂度是O(1)

基础函数

  1. hasPrefix(s, prefix string): 字符串s是否有前缀字符串prefix时间复杂度O(1)

    func hasPrefix(s, prefix string) bool {
    	return len(s) >= len(prefix) && s[:len(prefix)] == prefix   // 这个 == 的原理是什么???
    }	
    
  2. index(s, t string): 判断字符串t在字符串s中首次初选的位置,时间复杂度O(n)

    func index(s, t string) int {
    	if len(t) == 0 {
    		return 0
    	}
    	for i := 0; i < len(s); i++ {
    		if s[i] == t[0] && hasPrefix(s[i:], t) {
    			return i
    		}
    	}
    	return -1
    }
    
  3. contains(s, t string) bool: 字符串s是否包含字符串t, 即通过index查看首次出现的位置时间复杂度O(n)

    func contains(s, t string) bool {
    	return index(s, t) >= 0
    }
    
  4. stringStructOf(sp *string) *stringStruct: 获取字符串sp的底层数据结构体stringStruct

    func stringStructOf(sp *string) *stringStruct {
    	return (*stringStruct)(unsafe.Pointer(sp))
    }
    
  5. rawstring(size int) (s string, b []byte): 获取字节大小为size的字符串

    // rawstring allocates storage for a new string. The returned
    // string and byte slice both refer to the same storage.
    // The storage is not zeroed.
    func rawstring(size int) (s string, b []byte) {
    	p := mallocgc(uintptr(size), nil, false)   // 先分配一段内存空间,大小为size个字节,无类型,无零值
    
    	stringStructOf(&s).str = p    // 将分配内存空间的地址,赋值给string的底层slice指针
    	stringStructOf(&s).len = size  // 字符串s大小为size个字节
    
    	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}  // 同时,定义一个slice类型(也指向同一个地址空间)赋值给b
    
    	return
    }
    

    rawstring函数给一个新字符串分配存储,返回一个字符串和一个字节slice,两者指向同一个内存空间,存储空间没有初始化零值(mallocgc函数未初始化零值)

  6. stringDataOnStack(s string): 字符串s是否在当前goroutine的栈上

    func stringDataOnStack(s string) bool {
    	ptr := uintptr(stringStructOf(&s).str)  // 获取字符串s的指针地址
    	stk := getg().stack  // 获取当前goroutine的栈指针
    	return stk.lo <= ptr && ptr < stk.hi  // 字符串s的地址是否在栈的高低地址之间
    }
    
  7. slicebytetostringtmp: 把字节slice转换成字符串。其实就是把字节slice的指针赋值给字符串s的地址指针,再加上长度n即可

    func slicebytetostringtmp(ptr *byte, n int) (str string) {
    	stringStructOf(&str).str = unsafe.Pointer(ptr)
    	stringStructOf(&str).len = n
    	return
    }
    

string拼接x + y + z

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 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
}

多个字符串拼接,其原理是先遍历所有子字符串,获取其长度l后,先在内存中分配长度l的内存空间,然后将子字符串依次赋值到新空间内。时间复杂度为O(n)

string转slice

// 定义了默认缓冲区大小为32bytes
const tmpStringBufSize = 32
type tmpBuf [tmpStringBufSize]byte

// string => Slice
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
	var b []byte
	// 如果字符串长度小于32bytes,直接使用默认缓冲区
	if buf != nil && len(s) <= len(buf) {
		*buf = tmpBuf{}
		b = buf[:len(s)]
	} else {
	// 如果字符换长度大于32bytes,需要新申请一块内存区域
		b = rawbyteslice(len(s))
	}
	// 始终产生一次数据拷贝
	copy(b, s)
	return b
}

// 重新申请一块内存,构建新的byte slice
func rawbyteslice(size int) (b []byte) {
	cap := roundupsize(uintptr(size))
	p := mallocgc(cap, nil, false)
	if cap != uintptr(size) {
		memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
	}

	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
	return
}

slice转string

func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) {
	...
	var p unsafe.Pointer
	// 如果字节数小于32,直接使用缓冲区
	if buf != nil && n <= len(buf) {
		p = unsafe.Pointer(buf)
	} else {
	// 否则,重新申请内存
		p = mallocgc(uintptr(n), nil, false)
	}
	// 构建string,内存为tmp地址或新申请内存地址,长度为n
	stringStructOf(&str).str = p
	stringStructOf(&str).len = n
	// 从ptr指针拷贝n个字节到p指针,也存在一次数据拷贝
	memmove(p, unsafe.Pointer(ptr), uintptr(n))
	return
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值