Go指针探秘:深入理解内存与安全性
目录
- 1. 指针的基础
- 1.1 什么是指针?
- 1.2 内存地址与值的地址
- 1.2.1 内存中的数据存储
- 1.2.2 如何理解值的地址
- 2. Go中的指针操作
- 2.1 指针类型和值
- 2.1.1 基本数据类型的指针
- 2.1.2 复合数据类型的指针
- 2.2 如何获取一个指针值
- 2.3 指针(地址)解引用
- 3. 深入理解指针
- 3.1 我们为什么需要指针?
- 3.1.1 提高程序性能
- 3.1.2 动态数据结构
- 3.1.3 与其他语言的比较
- 3.2 关于"引用"这个术语
- 3.2.1 引用与指针的区别
- 4. Go指针的特性与限制
- 4.1 Go指针的特性
- 4.1.1 零值
- 4.1.2 不支持指针算术
- 4.2 Go指针的限制
- 4.2.1 不支持指针到整数的转换
- 4.2.2 不能获取内建数据类型的地址
- 4.2.3 安全性
- 5. 总结
Go指针为程序员提供了对内存的深入管理能力,同时确保了代码的安全性。本文深入探讨了Go指针的基础概念、操作、深层理解及其特性与限制。通过深入了解其设计哲学和应用,我们可以更好地利用Go的强大功能。
关注TechLead,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。

1. 指针的基础
1.1 什么是指针?
指针是一种变量,其存储的是另一个变量的内存地址,而不是值本身。在很多编程语言中,当我们需要直接访问内存或者希望通过一个变量间接操作另一个变量时,会使用到指针。
示例:
var a int = 42
var p *int = &a
fmt.Println(p) // 打印变量a的内存地址
1.2 内存地址与值的地址
每一个变量都存储在内存中的一个位置上,这个位置被称为该变量的内存地址。而当我们谈论一个变量的地址时,我们实际上是在讨论这个内存地址。
1.2.1 内存中的数据存储
计算机的内存是按照字节(bytes)组织的,每个字节都有一个唯一的地址。一个变量占用的字节数取决于其类型,例如,一个 int 类型在64位系统上通常是8字节。
示例:
var x int64 = 123456789
fmt.Println(&x) // 打印变量x的内存地址
1.2.2 如何理解值的地址
当我们使用&操作符来获取一个变量的地址时,我们实际上获取的是指向该变量内存起始位置的指针。
示例:
var y string = "OpenAI"
fmt.Println(&y) // 打印变量y的内存地址
在上面的示例中,变量y存储了字符串"OpenAI",但&y给我们返回的是这个字符串存储在内存中的地址。
2. Go中的指针操作
2.1 指针类型和值
在Go中,每种数据类型都有与之关联的指针类型。指针类型的定义是前置一个*到原始数据类型前面。例如,int的指针类型是*int。
2.1.1 基本数据类型的指针
示例:
var age int = 30
var agePointer *int = &agefmt.Println(age) // 打印原始变量值:30
fmt.Println(agePointer) // 打印age变量的内存地址
2.1.2 复合数据类型的指针
Go中的复合数据类型(例如slices、maps、channels、arrays、structs)也有其对应的指针类型。
示例:
type Person struct {Name stringAge int
}var person Person = Person{"Alice", 28}
var personPointer *Person = &personfmt.Println(person) // 打印结构体值:{Alice 28}
fmt.Println(personPointer) // 打印结构体的内存地址
2.2 如何获取一个指针值
要获取一个变量的指针值,可以使用&操作符。
示例:
var fruit string = "apple"
pointerToFruit := &fruitfmt.Println(fruit) // 打印原始值:apple
fmt.Println(pointerToFruit) // 打印fruit的内存地址
2.3 指针(地址)解引用
要获取指针指向的原始值,我们使用*操作符进行解引用。这允许我们间接地访问和修改指针指向的值。
示例:
var number int = 100
pointerToNumber := &numberfmt.Println(*pointerToNumber) // 通过解引用获取原始值:100// 修改指针指向的值
*pointerToNumber = 200
fmt.Println(number) // 原始变量值被修改为:200
3. 深入理解指针
3.1 我们为什么需要指针?
指针在编程中是一个重要的工具,特别是在需要高性能、灵活性或者对内存使用有严格要求的场景中。
3.1.1 提高程序性能
指针可以减少数据复制的需要,从而提高程序的执行速度。
示例:
考虑一个场景,我们需要交换两个大的数据结构的值。
type LargeStruct struct {Data [1000]int
}func swapWithoutPointer(a, b LargeStruct) {a, b = b, a
}func swapWithPointer(a, b *LargeStruct) {*a, *b = *b, *a
}var x, y LargeStruct
// 使用指针交换
swapWithPointer(&x, &y)
在上面的例子中,使用指针的方法可以避免复制两次大的数据结构,从而更为高效。
3.1.2 动态数据结构
很多动态数据结构(如链表、树、图)都依赖于指针来实现。
示例:
type Node struct {Value intNext *Node
}// 创建链表
first := Node{Value: 1}
second := Node{Value: 2}
third := Node{Value: 3}first.Next = &second
second.Next = &thirdfmt.Println(first.Value) // 1
fmt.Println(first.Next.Value) // 2
3.1.3 与其他语言的比较
与其他一些语言(如C、C++)相比,Go在指针的使用上更为安全。Go不允许进行指针运算,这降低了因为错误操作而导致的程序错误的可能性。
3.2 关于"引用"这个术语
在其他一些编程语言中(如C++、Java),"引用"与"指针"是两个不同的概念,但在Go中,我们主要使用指针,而不是引用。
3.2.1 引用与指针的区别
在某些语言中,引用是一个别名,它表示某个变量。而指针则是一个变量,其值是另一个变量的地址。
示例: 在Go中,我们不使用引用,而是使用指针来实现间接引用。
var original int = 10
pointerToOriginal := &original*pointerToOriginal = 20fmt.Println(original) // 输出:20
在上述示例中,通过指针,我们修改了original变量的值。
4. Go指针的特性与限制
4.1 Go指针的特性
4.1.1 零值
在Go中,指针的零值是nil。这意味着如果你声明一个指针变量但没有明确初始化,它的值就是nil。
示例:
var ptr *int
fmt.Println(ptr == nil) // 输出:true
4.1.2 不支持指针算术
与C和C++不同,Go不支持指针算术操作。这是为了确保更高的内存安全性。
示例:
在C或C++中,你可以做这样的操作:
int arr[10];
int *ptr = &arr[0];
ptr++;
但在Go中,类似的操作是不被允许的。
arr := [10]int{}
ptr := &arr[0]
// ptr++ // 这行会报错,因为Go不支持
4.2 Go指针的限制
4.2.1 不支持指针到整数的转换
在某些低级编程环境中,你可能需要将指针转换为整数进行某些操作,或者反之。但在Go中,这样的操作是不允许的,以确保程序的安全性和可读性。
4.2.2 不能获取内建数据类型的地址
在Go中,例如对于切片的元素或map的值,我们不能直接获取其地址。
示例:
m := map[string]int{"Alice": 25}
// ptr := &m["Alice"] // 这行会报错
4.2.3 安全性
Go的设计者们故意限制了指针的某些能力,以提高程序的安全性。例如,你不能在Go中进行指针算术,也不能随意地将指针与整数之间进行转换。
5. 总结
Go语言为现代编程提供了一种独特的途径。它不仅结合了经典的C风格语法,还引入了一系列新颖的设计哲学。这其中,Go对指针的处理尤为出色,它既维护了指针的功能性,又增强了代码的安全性。
深入的内存管理: Go语言通过指针让开发者有机会深入了解和管理内存。与直接操作值相比,指针为数据操作带来了更大的灵活性,特别是在处理大型数据结构或希望避免数据复制时。
安全性与简洁性的权衡: 通过消除指针算术和严格的类型限制,Go确保了程序员在操作指针时的安全性。这种设计选择可能限制了某些低级操作的能力,但它大大降低了因为误用指针而导致的程序错误的风险。
高级与低级的结合: 尽管Go提供了高级的数据结构如切片、映射等,但它仍然允许程序员通过指针进行低级的内存操作。这为开发者提供了无与伦比的灵活性,使他们既可以编写高性能的代码,又不失代码的可读性和可维护性。
关注TechLead,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。
相关文章:
Go指针探秘:深入理解内存与安全性
目录 1. 指针的基础1.1 什么是指针?1.2 内存地址与值的地址1.2.1 内存中的数据存储1.2.2 如何理解值的地址 2. Go中的指针操作2.1 指针类型和值2.1.1 基本数据类型的指针2.1.2 复合数据类型的指针 2.2 如何获取一个指针值2.3 指针(地址)解引用…...
Oracle12c之Sqlplus命令行窗口基本使用
Oracle12c之Sqlplus命令行窗口基本使用 文章目录 Oracle12c之Sqlplus命令行窗口基本使用1. 连接1. 超级用户2. 普通用户1. 创建普通用2. 连接 2. 修改用户连接数1. 查看默认连接最多用户数1. PL/SQL developer中查看2. Sqlplus中查看 2. 查看目前已经连接的用户数3. 修改用户连…...
react和antd学习笔记
概论 react是前端框架,antd是组件库。前端框架和组件库的区别与联系 nodejs 脚本语言需要一个解析器才能运行,JavaScript是脚本语言,在不同的位置有不一样的解析器,如写入html的js语言,浏览器是它的解析器角色。而对…...
寒假作业2月5号
第四章 堆与拷贝构造函数 一 、程序阅读题 1、给出下面程序输出结果。 #include <iostream.h> class example {int a; public: example(int b5){ab;} void print(){aa1;cout <<a<<"";} void print()const {cout<<a<<endl;} …...
滑动窗口(一)
文章目录 Leetcode209. 长度最小的子数组题目解法一(暴力求解)(超时)解法二(滑动窗口) Leetcode3. 无重复字符的最长子串题目解法一(暴力求解)解法二(滑动窗口) Leetcode1004. 最大连…...
寒假 day1
1、请简述栈区和堆区的区别? 2、有一个整形数组:int arr[](数组的值由外部输入决定),一个整型变量: x(也 由外部输入决定)。要求: 1)删除数组中与x的值相等的元素 2)不得创建新的数组 3)最多只允许使用单层循环 4)无需考虑超出新数组长度后面的元素,所以…...
DATAX改造支持geometry类型数据同步
数据库使用postgresql安装了postgis插件存储了geometry空间数据,想使用datax做数据同步,但datax本身不支持geometry类型数据,如何改造呢? 1.首先下载已改造支持geometry类型的datax引擎,下载地址 https://download.c…...
Vue中keep-alive的作用、原理及应用场景
在进行Vue开发的过程中,我们经常会遇到需要进行组件缓存的场景,这时候Vue提供的keep-alive组件就派上了用场。keep-alive组件是Vue内置的一个抽象组件,它可以将其包裹的组件进行缓存,提高组件的性能,同时也可以节省服务…...
SpringBoot集成Redisson实现限流(二)
1. 简介 Springboot集成Redisson默认的限流器为令牌桶型限流器,底层是通过lua脚本去实现的。 通过lua脚本我们可以去实现一个滑动窗口限流器,利用ZSET格式数据就可以轻松实现。 springboot集成Redisson就不做讲解,可以参考:sprin…...
【2024美赛E题】985博士解题思路分析(持续更新中)!
【2024美赛E题】985博士解题思路分析! 加群可以享受定制等更多服务,或者搜索B站:数模洛凌寺 联络组织企鹅:936670395 以下是E题老师的解题思路(企鹅内还会随时更新文档): 2024美赛E题思路详解…...
北朝隋唐文物展亮相广西,文物预防性保护网关保驾护航
一、霸府名都——太原博物馆收藏北朝隋朝文物展 2月1日,广西民族博物馆与太原博物馆携手,盛大开启“霸府名都——太原博物馆北朝隋文物展”。此次新春展览精选了北朝隋唐时期150多件晋阳文物珍品。依据“巍巍雄镇”“惊世古冢”“锦绣名都”三个单元&am…...
回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)
回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制) 目录 回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制&…...
ubuntu离线安装k8s
目录 一、前期准备 二、安装前配置 三、安装docker 四、安装cri-dockerd 五、部署k8s master节点 六、整合kubectl与cri-dockerd 七、网络等插件安装 八、常见问题及解决方法 一、前期准备 ①ubuntu系统 本地已安装ubuntu系统,lsb_release -a命令查看版本信…...
学成在线:媒体资源管理系统(MAM)
媒体资源管理系统(MAM) 媒体资源管理系统(Media Asset Management)是建立在多媒体、网络、数据库和数字存储等先进技术基础上的一个对各种媒体及内容进行数字化存储、管理以及应用的总体解决方案,可以满足媒体资源拥有者收集、保存、查找、编辑、发布各种信息的要求,为媒体资源…...
18个8年以上服务器开发经验的面试题(2)
目录 1.问:如何设计一个系统来确保在可能出现网络分区和故障的分布式环境中的数据一致性?...
【SpringBoot】applicationContext.getBeansOfType(class)获取某一接口所有实现类,应用于策略模式
一、问题的提出 在实际工作中,我们经常会遇到一个接口及多个实现类的情况,并且在不同的条件下会使用不同的实现类。 二、应用场景 springboot 项目中通过 ApplicationContext.getBeansOfType(class) 获取某一接口的所有实现类,并通过枚举完…...
AJAX-入门
定义 概念:AJAX是浏览器与服务器进行数据通信的技术 使用 1.先使用axios库,与服务器进行数据通信 1)基于XMLHttpRequest封装、代码简单、月下载量在14亿次 2)Vue、React项目中都会用到axios 2.再学习XMLHttpRequest对象的使用…...
学术写作|第二篇论文写作记录|GPT4论文润色Prompt
本文目录 写作时间安排如何写出初稿?找谁修改?1. 找AI修改2. 找师姐、师兄、老师、同行/外行修改论文修改意见集锦(反复观看)最好用的GPT4指令禁止转载,未经允许的任何引用。 写作时间安排 第二篇工作的idea去年就想出来了,一直被其他事情干扰,错过了N个会议… 在寒假…...
力扣热门100题刷题笔记 - 10. 正则表达式匹配
力扣热门100题 - 10. 正则表达式匹配 题目链接:10. 正则表达式匹配 题目描述: 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符 * 匹配零个或多个前面的那一个元素 所谓匹配ÿ…...
4.0 HDFS 配置与使用
之前提到过的 Hadoop 三种模式:单机模式、伪集群模式和集群模式。 单机模式:Hadoop 仅作为库存在,可以在单计算机上执行 MapReduce 任务,仅用于开发者搭建学习和试验环境。 伪集群模式:此模式 Hadoop 将以守护进程的…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
