go语言模拟切片的实现

go语言模拟切片的实现

切片的本质
// 定义切片类型
type Slice struct {
	Data unsafe.Pointer	// Go语言中的 万能指针类型. void * C语言中的万能指针. 没有具体数据类型,不能进行运算.
	Len int 	// 数据元素个数
	Cap int		// 可扩展的有效容量
}
C语言内存的使用
#include <stdlib.h>		//用于malloc
#include <string.h>		//用于memset和bzero
void *malloc(size_t size);//申请内存
void free(void *ptr);//释放内存
void *realloc(void *ptr, size_t size);
	//重新申请内存
	//参1:malloc 返回值
	//参2:新内存的大小(单位:字节)
void *memset(void *s,int ch,size_t n)
	//将某一块内存中的内容全部设置为指定的值
	//参1:malloc 返回值
	//参2:要设置的值
    //参3:malloc中用到的size 
void bzero(void *s, int n)
	//参1:malloc 返回值
	//参2:alloc中用到的size大小

例:

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
	// 申请内存
	int *p = (int *)malloc(100);
	*p = 666;
	printf("*p = %d, p = %p\n", *p, p);

	// 扩展内存空间
	int *q = (int *)realloc(p, 200);
	*q = 777;
	printf("*q=%d, q=%p\n", *q, q);

	// 释放内存空间
	free(p);
	free(q);

	return 0;
}
go语言中使用C代码
/*
#include <stdlib.h>
#include <string.h>
 */
import "C"		// 导入C代码

添加上述的 4 行指令,可以在.go 文件中使用 C代码
操作切片需要的方法
  1. 创建切片 Create(数据集)
  2. 打印切片 Print()
  3. 追加元素 Append(数据)
  4. 获取元素 GetData(下标)int
  5. 查找元素 SearchData(数据)下标
  6. 删除元素 Delete(下标)
  7. 插入元素 Insert(数据,下标)
  8. 销毁切片 Destroy()
创建切片 Create

获取内存地址空间的方法:

  1. 将 p 转换回成地址 (void *) —— unsafe.Pointer§
  2. 确定数据类型 : (*int)(unsafe.Pointer§)
  3. 取内存空间 *(*int)(unsafe.Pointer§)= 数据值
// 绑定创建 切片 方法	--- Create(5, 5, 1,2,3,4,5)
func (s *Slice) Create(l int, c int, Data ...int) {
	// 容错处理
	if s == nil || Data == nil {
		return
	}
	if len(Data) == 0 {
		return
	}
	if l < 0 || c < 0 || l > c || len(Data) > l {
		return
	}
	// 申请内存空间 -- 单位 : 字节  --- 返回 的是  void *  不能参与运算  .  s.Data是一个地址值.(不能运算)
	s.Data = C.malloc(C.size_t(c) * 8)
	//将这块内存空间的默认值变为0
	C.memset(s.Data, 0, C.size_t(c)*Tag)
	// 初始化 长度和容量
	s.Len = l
	s.Cap = c
	// 将 s.Data 转换成 可以计算的 数值.	将 0x420800 地址 变成数值 0x420800
	p := uintptr(s.Data)

	// 根据 Data 集合,遍历存入申请的内存中
	for _, v := range Data {
		// 将 数值 p,转换回地址值. 具体数据类型, 解引用 获取内存
		*(*int)(unsafe.Pointer(p)) = v
		p += TAG
	}
	// 释放内存
	// C.free(s.Data)
}
打印切片
// 打印切片方法
func (s *Slice) Print() {
	if s == nil {
		return
	}
	// 将 地址转换为可以计算的 数值
	p := uintptr(s.Data)

	// 按 len 循环 打印切片元素
	for i := 0; i < s.Len; i++ {
		fmt.Print(*(*int)(unsafe.Pointer(p)), " ")
		p += TAG
	}
	fmt.Println()
}
追加元素
// 追加元素 Append(数据)		--- Append(666, 777, 888)
func (s *Slice) Append(Data ...int) {
	if s == nil {
		return
	}
	// 判断是否需要扩容
	for len(Data)+s.Len > s.Cap {
		// 拓展容量为原来的 2 倍, 存储新内存空间地址
		s.Data = C.realloc(s.Data, C.size_t(s.Cap)*2*8)
		s.Cap *= 2
	}
	// 将s.Data 转换程可以运算的 数值
	p := uintptr(s.Data)

	/*	for i:=0; i<s.Len; i++ {
			p += TAG
		}*/
	// 偏移 p ,到 结尾处
	p += uintptr(s.Len) * 8

	// 按 data 循环,依次取出数据, 存入内存中
	for _, v := range Data {
		*(*int)(unsafe.Pointer(p)) = v
		p += TAG
	}

	// 修改 len
	s.Len += len(Data)
}
获取下标元素
//根据切片下标取元素
func (s *Slice) GetData(index int) int {
	if s == nil {
		return -1
	}
	if index < 0 || index >= s.Len {
		return -1
	}
	// 将 万能指针转换为可以计算的 数据
	p := uintptr(s.Data)

	// 偏移p,到 index 指代的元素位置
	p += uintptr(index) * TAG

	// 取数据值,返回
	return *(*int)(unsafe.Pointer(p))
}
已知元素获取下标
// 已知元素,返回下标值
func (s *Slice) SearchData(Data int) int {
	if s == 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
}
根据下标删除元素
// 根据下标,删除切片元素
func (s *Slice) Delete(index int) {
	if s == nil {
		return
	}
	if index < 0 || index >= s.Len {
		return
	}
	// 将万能指针,转换为可以计算的数据值
	p := uintptr(s.Data)

	// 将p 偏移到 index 位置
	p += uintptr(index) * TAG

	// 定义 变量,记录 p 指代的元素的 下一个元素
	aftp := p

	// 循环从 index 到 s.Len 依次完成 后一个元素给前一个元素赋值
	for i := index; i < s.Len; i++ {
		aftp += TAG
		*(*int)(unsafe.Pointer(p)) = *(*int)(unsafe.Pointer(aftp))
		p += TAG
	}
	// 修改 s.Len 去除一个元素
	s.Len -= 1
}
根据下标,向切片中插入数据
// 根据index ,向 切片插入数据
func (s *Slice) Insert(Data int, index int) {
	if s == nil || s.Data == nil {
		return
	}
	if index < 0 || index > s.Len {
		return
	}
	// 如果 插入的index 在切片结尾
	if index == s.Len {
		s.Append(Data)
		return
	}

	// 判断是否超出cap

	// 如果插入位置 在 中间
	p := uintptr(s.Data)

	// 将p偏移到 index 位置
	p += uintptr(index) * TAG

	// 获取插入元素完成后的,最后一个元素位置.
	temp := uintptr(s.Data) + uintptr(s.Len) * TAG

	// 循环将 index 之后的元素依次后移(前一个元素,给后一个元素赋值)
	for i := s.Len; i > index; i-- {
		*(*int)(unsafe.Pointer(temp)) = *(*int)(unsafe.Pointer(temp-TAG))
		temp -= TAG
	}

	// 循环结束后,将 p 对应的内存,写入 参数 Data
	*(*int)(unsafe.Pointer(p)) = Data

	// 修改 s.Len
	s.Len++
}
销毁切片
// 销毁 切片
func (s *Slice) Destroy() {
	if s == nil || s.Data == nil {
		return
	}
	C.free(s.Data)
	s.Data = nil		// 驱使go GC工作
	s.Len = 0
	s.Cap = 0
	s = nil

	runtime.GC()		// 手动调用 GC
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值