golang切片拷贝的实现
作者:快刀一哥
golang切片有浅拷贝和深拷贝的区别
1、深拷贝(Deep Copy)
拷贝的是数据本身,创造一个样的新对象,新创建的对象与原对象不共享内存,新创建的对象在内存中开辟一个新的内存地址,新对象值修改时不会影响原对象值。既然内存地址不同,释放内存地址时,可分别释放。
值类型的数据,默认全部都是深复制,Array、Int、String、Struct、Float,Bool。
2、浅拷贝(Shallow Copy)
拷贝的是数据地址,只复制指向的对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化。释放内存地址时,同时释放内存地址。
引用类型的数据,默认全部都是浅复制,Slice,Map。
去若是浅拷贝,原始的切片变化后所有的切片都会变化,无论切片是否在一个函数,还是一个goroutine中,都会发生变化
如下代码为例子
package main import "fmt" func main() { // 创建一个切片 originalSlice := []int{1, 2, 3, 4, 5} // 使用切片[:]操作符创建浅拷贝 shallowCopy := originalSlice[:] shallowCopy2 := make([]int, len(shallowCopy)) copy(shallowCopy2, originalSlice) // 修改原始切片的元素 originalSlice[0] = 100 // 输出原始切片和浅拷贝的切片 fmt.Println("Original Slice:", originalSlice) fmt.Println("Shallow Copy:", shallowCopy) fmt.Println("Shallow Copy2:", shallowCopy2) }
如下代码问题困扰了好久,才发现是切片赋值的问题
func (sf *ServerSession) running(ctx context.Context, subcom []uint8) { var err error var bytesRead int defer func() { sf.conn.Close() }() go sf.prehandler() raw := make([]byte, tcpAduMaxSize) for { select { case <-ctx.Done(): err = errors.New("server active close") xlog.Errorln(err) return default: } err = sf.conn.SetReadDeadline(time.Now().Add(sf.readTimeout)) if err != nil { xlog.Errorln("set read tcp data time deadline error:", err) } count, err := sf.conn.Read(raw) if err != nil { if err != io.EOF && err != io.ErrClosedPipe || strings.Contains(err.Error(), "use of closed network connection") { return } if e, ok := err.(net.Error); ok && !e.Temporary() { return } if bytesRead == 0 && err == io.EOF { err = fmt.Errorf("remote client closed, %v", err) xlog.Errorln(err) return } // cnt >0 do nothing // cnt == 0 && err != io.EOF continue do it next } adu := raw[:count] prodata := new(ProcessData) prodata.adu = make([]byte, len(adu)) copy(prodata.adu, adu) prodata.count = count prodata.subcom = subcom Prodatach <- prodata } } func (sf *ServerSession) prehandler() { for { select { case prodata := <-Prodatach: go sf.prerunhandler(prodata) } } } func (sf *ServerSession) prerunhandler(prodata *ProcessData) { length := prodata.count adu := make([]byte, len(prodata.adu)) copy(adu, prodata.adu) subcom := prodata.subcom xlog.Debug("run handler input1 out1 adu [% X] length is:%d\n", adu, length) //可能一次读出来多个数据包 增加处理方式 @20240704 //直接在for循环中使用 for length > 0 { headdata := adu[:gaHeaderSize] head1 := new(gadataHeader) buf1 := bytes.NewReader(headdata) err := binary.Read(buf1, binary.BigEndian, head1) if err != nil { xlog.Errorln("ga server decode errr:", err) } xlog.Debugln("pre process packect SN:", head1.SN) galength := int(head1.Datalength) + gaHeaderSize if galength <= len(adu) { adu1 := adu[:galength] xlog.Debug("run handler input out adu [% X ],galength is:%d subcom is %d length is:%d\n", adu1, galength, subcom, length) sf.runhandler(adu1, galength, subcom) adu = adu[galength:] length -= galength } else { xlog.Debug("data length is %d, adu length is:%d\n", head1.Datalength, len(adu)) break } } } func (sf *ServerSession) runhandler(adu []byte, count int, subcom []uint8) { if count < gaHeaderSize { return } xlog.Debug("run handler input adu [% X ],count is:%d subcom is %d", adu, count, subcom) // xlog.Debug("recive data [% x] count is % d \n", adu, count) head1 := new(gadataHeader) data := adu[:gaHeaderSize] buf1 := bytes.NewReader(data) err := binary.Read(buf1, binary.BigEndian, head1) if err != nil { xlog.Errorln("ga server decode errr:", err) } xlog.Debugln("gaserver head sn:", head1.SN) if !Find(subcom, head1.Subcomid) { xlog.Debug("subcom: %d is not in array %d \n", head1.Subcomid, subcom) return } // check head ProtocolIdentifier // if binary.BigEndian.Uint16(adu[2:]) != tcpProtocolIdentifier { // rdCnt, length = 0, tcpHeaderMbapSize // continue // } length := int(head1.Datalength) + gaHeaderSize if count == length { xlog.Debug("start decode packet SN is:%d subcom is %d data length is:%d\n", head1.SN, head1.Subcomid, head1.Datalength) if err = sf.frameHandler(adu); err != nil { xlog.Errorln("gaserver decode error:", err) return } } else { xlog.Debug("head data length: %d is not right %d \n", head1.Datalength, count) ///此处经常出现问题 xlog.Debug("adu data is [% X] \n", adu) } } func Find(subcoms []uint8, subcom uint8) bool { for i := 0; i < len(subcoms); i++ { if subcoms[i] == subcom { return true } } return false }
出现非常奇怪的问题切片内容经常莫名奇妙的改变,最后发现是切片赋值的问题
1449[xx] 2024-07-05 16:28:03.428417 run handler input1 out1 adu [00 C4 11 0A 01 A3 00 0A 00 A1 42 79 08 20 1F 75 A0 00
00 C5 11 06 01 A3 00 18 00 79 00 00 00 00 00 C0 C2 92 00 00 00 8D 42 95 85 C5 00 97 42 92 3D 71 ] length is:50
1450 [xx] 2024-07-05 16:28:03.428496 pre process packect SN: 1961452[xx] 2024-07-05 16:28:03.448860 run handler input out adu [00 C6 11 40 01 A3 00 09 00 B6 00 05 53 65 76 65 6E 00 ],galength is:18 subcom is [17 41] length is:50
1453[xx] 2024-07-05 16:28:03.448985 run handler input adu [00 C6 11 40 01 A3 00 09 00 B6 00 05 53 65 76 65 6E 00 ],count is:18 subcom is [17 41]1454 [xx] 2024-07-05 16:28:03.449011 gaserver head sn: 198
到此这篇关于golang切片拷贝的实现的文章就介绍到这了,更多相关golang切片拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!