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代码
操作切片需要的方法
- 创建切片 Create(数据集)
- 打印切片 Print()
- 追加元素 Append(数据)
- 获取元素 GetData(下标)int
- 查找元素 SearchData(数据)下标
- 删除元素 Delete(下标)
- 插入元素 Insert(数据,下标)
- 销毁切片 Destroy()
创建切片 Create
获取内存地址空间的方法:
- 将 p 转换回成地址 (void *) —— unsafe.Pointer§
- 确定数据类型 : (*int)(unsafe.Pointer§)
- 取内存空间 *(*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
}