slice 实现原理

Posted lurenq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了slice 实现原理相关的知识,希望对你有一定的参考价值。

package main

/*
#include <stdlib.h>
 */
import "C"
import (
	"unsafe"
	"fmt"
)

type Slice struct {
	Data unsafe.Pointer //万能指针类型 对应C语言中的void*
	len  int            //有效的长度
	cap  int            //有效的容量
}

const TAG = 8

/*
func main() {
	//定义一个切片
	//1、数据内存地址 2、len 有效数据长度 3、cap 可扩容的有效容量  24字节
	var s []int

	//unsafe.Sizeof 计算数据类型在内存中占的字节大小
	fmt.Println(unsafe.Sizeof(s))
}
*/
/*
func main(){
	var i interface{}
	i=10//只支持== !=

	//类型断言  是基于接口类型数据的转换
	//value,ok:=i.(int)
	//if ok{
	//	fmt.Println("整型数据:",value)
	//	fmt.Printf("%T
",value)
	//}
	//反射获取接口的数据类型
	//t:=reflect.TypeOf(i)
	//fmt.Println(t)

	//反射获取接口类型数据的值
	v:=reflect.ValueOf(i)
	fmt.Println(v)

	i1:=10
	i2:=20

	if reflect.Typeof(i1)==reflect.Typeof(i2){
		v1:=reflect.Valueof(i1)
		v2:=reflect.Valueof(i2)
		结果=v1+v2
	}
}
*/
//Create(长度 容量 数据)
func (s *Slice) Create(l int, c int, Data ...int) {
	//如果数据为空返回
	if len(Data) == 0 {
		return
	}
	//长度小于0 容量小于0 长度大于容量 数据大于长度
	if l < 0 || c < 0 || l > c || len(Data) > l {
		return
	}
	//ulonglong unsigned long long  无符号的长长整型
	//通过C语言代码开辟空间 存储数据
	//如果堆空间开辟失败 返回值为NULL 相当于nil 内存地址编号为0的空间
	s.Data = C.malloc(C.ulonglong(c) * 8)
	s.len = l
	s.cap = c

	//转成可以计算的指针类型
	p := uintptr(s.Data)
	for _, v := range Data {
		//数据存储
		*(*int)(unsafe.Pointer(p)) = v
		//指针偏移
		p += TAG
		//p+=unsafe.Sizeof(1)
	}
}

//Print 打印切片
func (s *Slice) Print() {
	if s == nil {
		return
	}

	//将万能指针转成可以计算的指针
	p := uintptr(s.Data)
	for i := 0; i < s.len; i++ {
		//获取内存中的数据
		fmt.Print(*(*int)(unsafe.Pointer(p)), " ")
		p += TAG
	}

}

//切片追加
func (s *Slice) Append(Data ...int) {
	if s == nil {
		return
	}
	if len(Data) == 0 {
		return
	}

	//如果添加的数据超出了容量
	if s.len+len(Data) > s.cap {
		//扩充容量
		//C.realloc(指针,字节大小), go 语言 2 倍扩容。
		s.Data = C.realloc(s.Data, C.ulonglong(s.cap)*2*8)
		//改变容量的值
		s.cap = s.cap * 2
	}

	p := uintptr(s.Data)
	for i := 0; i < s.len; i++ {
		//指针偏移
		p += TAG
	}

	//添加数据
	for _, v := range Data {
		*(*int)(unsafe.Pointer(p)) = v
		p += TAG
	}
	//更新有效数据(长度)
	s.len = s.len + len(Data)
}

//获取元素 GetData(下标)  返回值为int 元素
func (s *Slice) GetdData(index int) int {
	if s == nil || s.Data == nil {
		return 0
	}
	if index < 0 || index > s.len-1 {
		return 0
	}

	p := uintptr(s.Data)
	for i := 0; i < index; i++ {
		p += TAG
	}
	return *(*int)(unsafe.Pointer(p))
}

//查找元素 Search(元素)返回值为int 下标
func (s *Slice) Search(Data int) int {
	if s == nil || s.Data == nil {
		return -1
	}

	p := uintptr(s.Data)
	for i := 0; i < s.len; i++ {
		//查找数据  返回第一次元素出现的位置
		if *(*int)(unsafe.Pointer(p)) == Data {
			return i
		}
		//指针偏移
		p += TAG
	}
	return -1
}

//删除元素 Delete(下标)
func (s *Slice) Delete(index int) {
	if s == nil || s.Data == nil {
		return
	}
	if index < 0 || index > s.len-1 {
		return
	}

	//将指针指向需要删除的下标位置
	p := uintptr(s.Data)
	for i := 0; i < index; i++ {
		p += TAG
	}

	//用下一个指针对应的值为当前指针对应的值进行赋值
	temp := p
	for i := index; i < s.len; i++ {
		temp += TAG
		*(*int)(unsafe.Pointer(p)) = *(*int)(unsafe.Pointer(temp))
		p += TAG
	}

	s.len--
}

//插入元素 Insert(下标 元素)
func (s *Slice) Insert(index int, Data int) {
	if s == nil || s.Data == nil {
		return
	}
	if index < 0 || index > s.len-1 {
		return
	}

	//如果插入数据是最后一个
	//if index == s.len-1 {
	//	p := uintptr(s.Data)
	//
	//	//for i := 0; i < s.len; i++ {
	//	//	p += TAG
	//	//}
	//	p += TAG * uintptr(s.len-1)
	//	*(*int)(unsafe.Pointer(p)) = Data
	//	s.len++
	//	return
	//}
	//调用追加方法
	if index == s.len-1 {
		s.Append(Data)
		return
	}

	//获取插入数据的位置
	p := uintptr(s.Data)
	for i := 0; i < index; i++ {
		p += TAG
	}
	//获取末尾的指针位置

	temp := uintptr(s.Data)
	temp += TAG * uintptr(s.len)

	//将后面数据依次向后移动
	for i := s.len; i > index; i-- {
		//用前一个数据为当前数据赋值
		*(*int)(unsafe.Pointer(temp)) = *(*int)(unsafe.Pointer(temp - TAG))
		temp -= TAG
	}

	//修改插入下标的数据
	*(*int)(unsafe.Pointer(p)) = Data
	s.len++
}

//销毁切片
func (s *Slice) Destroy() {
	//调用C语言  适释放堆空间
	C.free(s.Data)
	s.Data = nil
	s.len = 0
	s.cap = 0
	s = nil
}

 

以上是关于slice 实现原理的主要内容,如果未能解决你的问题,请参考以下文章

Go切片实现

JS常用代码片段2-值得收藏

JS常用代码片段2-值得收藏

16个必备的JavaScript代码片段

原来go语言slice切片底层原理这么简单

原来go语言slice切片底层原理这么简单