刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • go语言基础

    • 反射
    • nethttp模块
    • Socket编程
    • 内置函数&库
    • Context
    • 正则表达式
    • 环境配置
    • GoPath和GoMod
    • 变量
    • 数据类型
    • 运算符
    • 数据类型相关操作
    • 基础语法
    • 函数&复合类型
    • go指针
      • 函数
      • 数组和切片
      • 结构体
      • 接口
      • 空结构体
      • 文件操作
      • 并发与通道
      • channel 原理和坑
    • go语言进阶

    • go语言实现原理

    • gin框架

    • gorm

    • go测试

    • Go语言
    • go语言基础
    bigox
    2021-03-30
    目录

    go指针

    # Go中的指针

    要搞明白Go语言中的指针需要先知道三个概念

    • 指针地址
    • 指针类型
    • 指针取值

    Go语言中的指针操作非常简单,我们只需要记住两个符号:&:取地址,*:根据地址取值

    # 关于指针

    • 我们知道变量是用来存储数据的,变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量a:=10,这个时候可以直接通过a这个变量来读取内存中保存的10这个值。在计算机底层a这个变量其实对应了一个内存地址。

    • 指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址。

      image-20220614164230583

    # 指针地址和指针类型

    • 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。

    • Go 语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:

    *int、,*int64、*string等
    
    1

    取变量指针的语法如下:

    ptr := &v
    
    1

    其中:

    • v:代表被取地址的变量,类型为T
    • ptr:用于接收地址的变量,ptr的类型就为*T,被称做T的指针类型。* 代表指针

    举个例子:

    image-20200721084549011

    package main
    
    func main() {
    	var a = 10
    	var b = &a
    	println(&a) 	//0xc00003df60
    	println(&b)		//0xc00003df68
    	println(b)		//0xc00003df60
    	println(*(&b))	//0xc00003df60
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    # 指针取值

    在对普通变量进行&操作符取地址后,会获得这个变量指针,然后可以对指针使用*操作,也就是指针取值

    // 指针取值
    var c = 20
    // 得到c的地址,赋值给d
    var d = &c
    // 打印d的值,也就是c的地址
    fmt.Println(d)
    // 取出d指针所对应的值
    fmt.Println(*d)
    // c对应地址的值,改成30
    *d = 30
    // c已经变成30了
    fmt.Println(c)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    改变内存中的值,会直接改变原来的变量值

    // 这个类似于值传递
    func fn4(x int) {
    	x = 10
    }
    // 这个类似于引用数据类型
    func fn5(x *int) {
    	*x = 20
    }
    func main() {
    	x := 5
    	fn4(x)
    	fmt.Println(x)
    	fn5(&x)
    	fmt.Println(x)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    我们创建了两个方法,一个是传入局部变量,一个是传入指针类型,最后运行得到的结果

    5
    20
    
    1
    2

    # new和make函数

    需要注意的是,指针必须在创建内存后才可以使用,这个和 slice 和 map是一样的

    // 引用数据类型map、slice等,必须使用make分配空间,才能够使用
    var userInfo = make(map[string]string)
    userInfo["userName"] = "zhangsan"
    fmt.Println(userInfo)
    
    var array = make([]int, 4, 4)
    array[0] = 1
    fmt.Println(array)
    
    1
    2
    3
    4
    5
    6
    7
    8

    对于指针变量来说

    // 指针变量初始化
    var a *int
    *a = 100
    fmt.Println(a)
    
    1
    2
    3
    4

    执行上面的代码会引发panic,为什么呢?在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。要分配内存,就引出来今天的new和make。Go 语言中new和make是内建的两个函数,主要用来分配内存。

    这个时候,我们就需要使用new关键字来分配内存,new是一个内置的函数,它的函数签名如下:

    func new(Type) *Type
    
    1

    其中

    • Type表示类型,new函数只接受一个参数,这个参数是一个类型
    • *Type表示类型指针,new函数返回一个指向该类型内存地址的指针

    实际开发中new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。举个例子:

    // 使用new关键字创建指针
    aPoint := new(int)
    bPoint := new(bool)
    fmt.Printf("%T \n", aPoint)
    fmt.Printf("%T \n", bPoint)
    fmt.Println(*aPoint)
    fmt.Println(*bPoint)
    
    1
    2
    3
    4
    5
    6
    7
    • 类似var a *int 只是声明了一个指针变量a但是没有初始化,指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值。应该按照如下方式使用内置的

    # make和new的区别

    • 两者都是用来做内存分配的;

    • make只能用于slice、map以及channel的初始化,返回的还是这三个引用类型的本身

    • 而new用于类型的内存分配,并且内存初始的值为类型的零值,返回的是指向类型的指针

      // int byte rune float bool string 这些类型都有默认零值;在变量声明的时候系统自动会分配一块内存空间; 而 point slice、map以及channel的零值是nil 系统不会默认分配空间,所以需要初始化;
      
      1
    #Go#
    上次更新: 2023/04/16, 18:35:33
    函数&复合类型
    函数

    ← 函数&复合类型 函数→

    最近更新
    01
    go与http代理
    05-24
    02
    自制申威架构k8s-reloader
    12-06
    03
    Docker Buildx 教程
    12-01
    更多文章>
    Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式