Java与Go:指针

在计算机内存中,每个变量都有一个唯一的地址,指针就是用来保存这个地址的变量。通过指针,我们可以间接地访问和修改存储在该地址处的数据。今天我们来聊一聊Java和Go指针,预告一下,我们需要借助C语言做一些小小的比较。

Java

在Java中,不存在直接的指针概念,而是通过引用来访问对象。Java中的对象是通过引用来操作的,这些引用本质上是对对象的引用,而不是指向内存地址的指针。Java的引用是一种高级抽象,它隐藏了底层内存管理的细节,开发者不需要关心对象的内存分配和释放。Java中的引用可以被认为是一种安全的指针,它提供了更高的抽象级别,可以减少内存管理错误的发生。其实大家第一门编程语言应该就是大一学习的C语言编程吧。当时学习指针的时候很懵,尤其是各种指针运算。后来学Java的对象以及引用再回来看就很好理解。

C
 Student *student = (Student*)malloc(sizeof(Student));
 
 Java
 Student student = new Student();

他们的本质是一样的,都是指向堆空间的某一块地址。之后再回去写C语言,就越写越顺,每次创建变量之前都问自己一句,在栈里还是堆里? 然后决定要不要用指针。

话说回来,sun.misc.Unsafe类提供了一种机制来进行一些底层的、不安全的操作,包括直接操作内存和执行不受限制的任意指针算术运算等。

sun.misc.Unsafe类并不是 Java 标准 API 的一部分,因此它并不受到 Java 平台的正式支持,并且可能在未来的 Java 版本中被移除或更改。它主要被用来实现 Java 核心类库和一些 Java 虚拟机的实现。

通过sun.misc.Unsafe类,你可以直接进行一些底层的内存操作,比如:

  • 分配内存
  • 释放内存
  • 修改内存中的值
  • 进行指针算术运算等
    使用sun.misc.Unsafe类需要谨慎,因为它涉及到底层的内存操作,可能会导致不稳定性和不可预测的结果。此外,由于它不是 Java 标准 API 的一部分,因此在不同的 Java 实现中,它的行为可能会有所不同。

在 Java 9 中,sun.misc.Unsafe类的一些方法被标记为不安全,并且在一些场景下会抛出 java.lang.UnsupportedOperationException 异常,这是为了增强 Java 应用程序的安全性。

总的来说,除非你对 Java 的内存模型和底层运行机制非常了解,并且对使用 sun.misc.Unsafe 类的风险有所认识,并且确实需要进行底层的内存操作,否则不建议使用 sun.misc.Unsafe 类。Java部分就到此结束,毕竟不是重点。

Go 中的指针

Go语言支持指针,但是和C语言中的指针还是有些不同。在Go中,指针是一种数据类型,它指向了一个内存地址,允许你直接访问内存中的数据。与C语言不同的是,Go语言的指针是类型安全的,不允许进行指针运算,从而减少了一些常见的指针错误。

在Go中,你可以通过使用操作符来声明指针变量,使用&操作符来获取变量的地址,使用操作符来获取指针指向的值。

依稀记得当时学习C语言的时候老师引出的指针的第一个例子:将两个int数传入一个方法,希望这两个int互换数值。这里我用Go来写:

package main

import "fmt"

// 定义一个交换函数
func swap(x, y int) {
	var i int = x
	x = y
	y = i
}

func main() {
	// 定义两个整数变量
	a, b := 5, 10

	fmt.Println("Before swapping:")
	fmt.Println("a =", a)
	fmt.Println("b =", b)

	// 调用交换函数,并接收返回值
	swap(a, b)

	fmt.Println("After swapping:")
	fmt.Println("a =", a)
	fmt.Println("b =", b)
}

结果显而易见,我们失败了。然后老师开始长篇大论的解释,然后我就睡着了。其实现在回来再想想很简单,x和y的变量在main的栈帧里面,调用swap方法无非就是创建新的栈帧,并把xy的数值复制一边传递出去。抓捕周树人和我鲁迅有什么关系?

那正确写法就是搞一个指向栈内存的指针。

package main

import "fmt"

// 定义一个交换函数
func swap(x, y *int) {
    temp := *x
    *x = *y
    *y = temp
}

func main() {
    // 定义两个整数变量
    a, b := 5, 10

    fmt.Println("Before swapping:")
    fmt.Println("a =", a)
    fmt.Println("b =", b)

    // 调用交换函数
    swap(&a, &b)

    fmt.Println("After swapping:")
    fmt.Println("a =", a)
    fmt.Println("b =", b)
}

接下来我们再看看数组和指针碰到一起会发生什么

指针和数组

指针指向数组中的某一个元素
var arr [5]int
var ptr *int
ptr = &arr[0] // 将指针指向数组的第一个元素
指针指向数组整体
// 声明一个数组
var arr [5]int
// 声明一个指向数组的指针
var ptr *[5]int
// 将指针指向数组
ptr = &arr

这里要注意类型匹配,我们之前说过长度也是类型的一部分,[5]int和[7]int是不一样的。[5]int要配合*[5]int

眼神要好
var ptrArr *[3]int //指向一个数组
var ptrArr [3]*int //每个元素指向一个数
传递数组

之前我们说过,在方法间传递数组其实是复制整个数组传递过去,相当于寻觅另外一个地方盖一座一模一样的房子。如果我们传递指向数组的指针,函数将能够修改原始数组的值。

func modifyArray(arr *[5]int) {
    // 修改数组的值
    (*arr)[0] = 100
}

func main() {
    var arr [5]int
    modifyArray(&arr)
    fmt.Println(arr) // 输出 [100 0 0 0 0]
}

指针和切片

之前我们已经学过,切片其实就是对数组进行包装,内部通过指针对数组进行操作。那么当切片和指针一起使用时,可以实现更灵活和高效的数据操作,比如动态地管理内存和访问数组的部分元素(二级指针嘛,玩C的都懂)。以下是一些示例,演示了切片和指针的结合使用:

package main

import "fmt"

func main() {
    // 创建一个切片
    slice := []int{1, 2, 3, 4, 5}

    // 创建一个指向切片的指针
    var ptr *[]int
    ptr = &slice

    // 修改切片的值
    (*ptr)[0] = 100
    (*ptr)[1] = 200

    // 打印修改后的切片
    fmt.Println(*ptr) // 输出: [100 200 3 4 5]
}

个人觉得切片指针反而比数组用起来更轻松。map也一样道理:

package main

import "fmt"

func modifyMap(m *map[string]int) {
    // 向 map 中添加新的键值对
    (*m)["d"] = 4
    (*m)["e"] = 5

    // 修改已有键的值
    (*m)["a"] = 100
}

func main() {
    // 创建一个 map
    myMap := map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
    }

    // 创建指向 map 的指针
    ptr := &myMap

    // 修改 map 内容
    modifyMap(ptr)

    // 打印修改后的 map
    fmt.Println(*ptr)
}

指针和结构体

在 Go 语言中,指向结构体的指针是一种非常常见的用法基本和C语言一样。结构体是一种用户自定义的复合数据类型,它可以包含多个不同类型的字段,而指向结构体的指针则允许我们直接访问结构体的字段,并且可以在函数之间共享结构体的实例,而不需要进行复制。以下是指向结构体的指针如何使用的详细说明:

创建结构体指针
// 定义结构体
type Person struct {
    Name string
    Age  int
}

func main() {
    // 创建结构体的指针
    var p *Person
    p = &Person{"John", 30}

    // 或者使用 new() 函数创建结构体的指针
    p = new(Person)
    p.Name = "Alice"
    p.Age = 25
}

可以理解这里的new就相当于(*Person)malloc(sizeof(Person));

操作结构体字段
访问结构体字段:
fmt.Println((*p).Name)  // 打印结构体字段 Name
fmt.Println(p.Age)       // 也可以直接使用 p.Age 访问
修改结构体字段:
p.Name = "Bob"  // 直接赋值修改结构体字段
p.Age = 40
传递结构体指针给函数:
func modifyPerson(p *Person) {
  if p != nil {
    p.Age = 50
  }
}

modifyPerson(p) // 调用函数修改结构体的字段值

总的来说,指向结构体的指针在 Go 中是非常常见的用法,它允许我们在函数间共享结构体的实例,并且可以在需要时直接访问和修改结构体的字段。使用指向结构体的指针可以避免结构体的复制,提高程序的性能和效率。

总结

Java中的指针是被隐藏的,程序员无法直接操作内存地址,而Go中的指针是一等公民,允许直接操作内存地址。本文仅仅介绍了指针操作的冰山一角,之后我们会继续介绍指针在面向对象编程中的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/473647.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL61 检索并列出已订购产品的清单

order by cust_name 升序 order by cust_name desc 降序

计算机网络面经-什么是IPv4和IPv6?

前言 Internet协议(IP)是为连接到Internet网络的每个设备分配的数字地址。它类似于电话号码,是一种独特的数字组合,允许用户与他人通信。IP地址主要有两个主要功能。首先,有了IP,用户能够在Internet上被识别…

腾讯云GPU云服务器简介_GPU服务器购买指南_GPU云服务器操作

腾讯云GPU服务器是提供GPU算力的弹性计算服务,腾讯云GPU服务器具有超强的并行计算能力,可用于深度学习训练、科学计算、图形图像处理、视频编解码等场景,腾讯云百科txybk.com整理腾讯云GPU服务器租用价格表、GPU实例优势、GPU解决方案、GPU软…

One Nav一为主题最新V4.1602版官方正版学习版

在现今数字化快速发展的时代,信息的获取与整合变得愈发重要。为此,我们推出了一款功能强大且独具特色的WordPress主题——One Nav,又称“一导航主题”。这款主题集网址、app、资源、书籍、影视等内容导航于一体,为用户提供了一站式…

java NIO群聊系统

demo要求: 1)编写一个NIO群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞) 2)实现多人群聊 3)服务器端:可以监测用户上线,离线,并实现消息转发功…

Open World Object Detection in the Era of Foundation Models

Open World Object Detection in the Era of Foundation Models 摘要介绍相关工作开放词汇物体检测开放世界目标检测类无关的目标检测3.真实世界目标检测基准3.1 数据集细节3.2 基准架构3.3 什么是一个未知对象4. 利用基准模型用于开放世界目标检测4.1 背景4.2 属性生成4.3 属性…

汽车价格的回归预测项目

注意:本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 ([www.aideeplearning.cn]) 问题描述 汽车价格预测是一个旨在预估二手车市场中汽车售价的问题。这个问题涉及到分析各种影响汽车价格的因素,如品牌、车龄、性能…

Git原理与使用(一)

目录 前言 版本控制器 Linux下的Git的安装 Git的基本操作 创建Git本地仓库 配置Git 工作区、暂存区、版本库 添加与提交 查看.git文件 前言 我们可能要写多个文档对一个产品进行描述,但是一般情况下我们可能要写多个文档,比如: 初…

图片编辑器中实现文件上传的三种方式和二进制流及文件头校验文件类型

背景 最近在 vue-design-editor 开源项目中实现 psd 等多种文件格式上传解析成模板过程中, 发现搞定设计文件上传没有使用 input 实现文件上传, 所以我研究了一下相关技术, 总结了以下三种文件上传方法 input 文件选择window.showOpenFilePicker 和 window.showDirectoryPicke…

Follow-Your-Click——点选图像任意区域对象使用短提示语即可生成视频

简介 “I2V”(图像到视频生成)旨在将静态图像转换为具有合理动作的动态视频剪辑,在电影制作、增强现实和自动广告等领域有广泛应用。然而,现有的I2V方法存在一些问题,例如缺乏对图像中需要移动的部分的精准控制&#…

RAFT: Adapting Language Model to Domain Specific RAG

预备知识 RAG介绍一文搞懂大模型RAG应用(附实践案例) - 知乎 (zhihu.com) RAG的核心理解为“检索生成” 检索:者主要是利用向量数据库的高效存储和检索能力,召回目标知识; 生成:利用大模型和Prompt工程…

Android Studio实现内容丰富的安卓校园公告助手

获取源码请点击文章末尾QQ名片联系,源码不免费,尊重创作,尊重劳动 093校园助手 1.开发环境 android stuido3.6 jak1.8 eclipse mysql tomcat 2.功能介绍 具体往下看第三节,功能截图 安卓端: 1.注册登录 2.校园公告列表…

微信小程序订阅消息(一次性订阅消息)

1、准备工作 登录微信公众平台–>订阅消息–>在公共模板库中选中一个模版–>将模版id复制,前后端都需要。 点击详情–>查看详细内容模版 复制给后端 2、相关api的使用 前端使用:wx.requestSubscribeMessage wx.openSetting wx.getSetti…

[Qt学习笔记]QPushButton点击事件和长按事件使用功能

1、背景介绍 在使用QPushButton中,一般都在UI界面直接右键添加槽函数进入代码,很少去分析每个触发事件的功能,比如需要通过长按按钮来触发相应的操作,这里点击信号不可以达到预期的效果。 2、功能分析 首先分析QPushButton的点…

13014.Linux小知识点记录

文章目录 1 工具记录1.1 串口传输文件 1 工具记录 1.1 串口传输文件 打开SecureCRT的串口,执行rx 文件名指令从桌面将可执行文件,拖拽到串口终端即可

计算机三级——网络技术(综合题第二题)

路由器工作模式 用户模式 当通过Console或Telnet方式登录到路由器时,只要输入的密码正确,路由器就直接进入了用户模式。在该模式下,系统提示符为一个尖括号(>)。如果用户以前为路由器输入过名称,则该名称将会显示在尖指号的前…

opengl日记10-opengl使用多个纹理示例

文章目录 环境代码CMakeLists.txt文件内容不变。fragmentShaderSource.fsvertexShaderSource.vsmain.cpp 总结 环境 系统:ubuntu20.04opengl版本:4.6glfw版本:3.3glad版本:4.6cmake版本:3.16.3gcc版本:10.…

【Hadoop】Hadoop 编译源码

目录 为什么要源码编译Hadoop 编译源码1前期工作准备2jar 包安装2.1安装 Maven2.2安装 ant2.3安装 glibc-headers 和 g2.4安装 make 和 cmake2.5安装 protobuf2.6安装 openssl 库2.7安装 ncurses-devel 库 3编译源码3.1解压源码到 /opt/ 目录3.2 进入到 hadoop 源码主目录 /opt…

课时70:流程控制_for循环_嵌套循环

2.4.4 嵌套循环 学习目标 这一节,我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 这里的嵌套实践,与选择语句的嵌套实践基本一致,只不过组合的方式发生了一些变化。常见的组合样式如下:for嵌套for语句for …

【Android】【Bluetooth Stack】蓝牙电话本协议分析(超详细)

1. 精讲蓝牙协议栈(Bluetooth Stack):SPP/A2DP/AVRCP/HFP/PBAP/IAP2/HID/MAP/OPP/PAN/GATTC/GATTS/HOGP等协议理论 2. 欢迎大家关注和订阅,【蓝牙协议栈】专栏会持续更新中.....敬请期待! 目录 1. 协议简述 1.1 PBAP…