当前位置: 首页 > news >正文

04 go语言(golang) - 变量和赋值过程

变量

在Go语言中,变量的定义和初始化是编程的基础部分。Go提供了多种方式来声明和初始化变量,以适应不同的使用场景。

基本变量声明

  1. 使用var关键字
    使用var关键字可以在函数内部或外部声明变量。如果在函数外部声明,该变量为全局变量。

    var str1 string = "变量1"
    var int1 int = 123
    fmt.Println(str1, int1)
    

    如果已经明确了初始值,则可以省略类型信息,让编译器自动推断类型:

    var str2 = "变量2"
    var int2 = 123
    fmt.Println(str2, int2)
    
  2. 短变量声明
    在函数内部,你可以使用更简洁的短变量声明(Short variable declarations)方式 :=。这种方式同时进行了声明和初始化,并且类型由右侧表达式推断得出。

    func main() {str3 := "变量3"int3 := 123fmt.Println(str3, int3)
    }
    

多重赋值

Go支持多个变量同时赋值:

	//var a, b, c = "1", 2, 3.55a, b, c := "1", 2, 3.55// 使用 reflect 包中的 .TypeOf 函数。返回变量的类型信息fmt.Println("a的类型为:", reflect.TypeOf(a))fmt.Println("b的类型为:", reflect.TypeOf(b))fmt.Println("c的类型为:", reflect.TypeOf(c))
	// 为相同类型的变量在同一行上声明和赋值//var a, b, c int = "1", 2, 3 // 错误,不能将字符串赋值给整数变量。 Go 不支持在同一行中为不同类型变量分配类型。var a, b, c int = 1, 2, 3fmt.Println("a的类型为:", reflect.TypeOf(a))fmt.Println("b的类型为:", reflect.TypeOf(b))fmt.Println("c的类型为:", reflect.TypeOf(c))

块声明(block declaration)语法。这种方式允许在一个 var 语句中声明多个变量,并为它们赋初始值。

	var (a = 1111b = "bbb"c bool)fmt.Println(a, b, c)

零值(Zero Values)

在Go中,如果你只是声明一个变量而没有给它赋初值,则会自动被赋予该类型的零值:

  • 数字类型(整型、浮点型等)的零值是 0
  • 布尔型的零值是 false
  • 字符串的零值是空字符串 ""
  • 指针、切片、映射、通道、函数以及接口的零值都是 nil

例如:

	var a stringvar b intvar c float64var d boolfmt.Printf("a的值:【%v】\n", a)fmt.Printf("b的值:【%v】\n", b)fmt.Printf("c的值:【%v】\n", c)fmt.Printf("d的值:【%v】\n", d)/*输出结果为:a的值:【】b的值:【0】c的值:【0】d的值:【false】*/

作用域

在Go语言中,作用域(Scope)指的是一个变量或者函数名字在程序中可以被访问的区域。

  1. 全局作用域

    • 如果一个变量是在所有函数之外定义的,则它被认为是全局变量,可以在整个包内部任何位置使用。如果需要在其他包中使用这些全局变量或函数,则它们必须首字母大写。

    test1.go

    package mainimport "fmt"var name = "全局变量"func main() {// 在同一包下的不同文件中可以访问并且修改全局变量printGlobalVar()fmt.Println(name)
    }
    

    test2.go

    package mainimport "fmt"func printGlobalVar() {fmt.Println(name)name = "修改后的全局变量"
    }
    
  2. 包级别作用域

    • 在同一个包内部,当变量或者常数被定义在所有函数之外时(但不一定非得在文件最顶端),这些标识符将对整个包内部可见。这意味着同一包下不同文件中的代码也可以访问这些变量。
    • 全局作用域的变量首字母小写,其实就相当于包级别作用域的变量
  3. 文件级别作用域

    • 这个概念在 Go 语言中并不存在。Go 语言中最小的作用域单位是包级别作用域。
  4. 局部作用域

    • 在函数内定义的变量只能在该函数内部访问,并且这种限制也适应于该函数内定义的其他嵌套块结构如if条件、循环等。
    func printLocalVar() {var localVar = "局部变量"fmt.Println(localVar)
    }
    
  5. 块级别作用域

    • Go 语言支持诸如 if、for、switch 等控制结构,并且每个结构都会引入新的块范围。例如,在 if 语句中声明并初始化一个新变量,在该 if 块及其子块中该变量是可见和可访问的,但超出此范围后就不再可见。
    func printBlockVar() {var localVar = "局部变量"// 代码块{var blockVar = "块级变量"fmt.Println(blockVar)fmt.Println(localVar) // 可以访问局部变量}//fmt.Println(blockVar) // 报错,无法访问块级变量
    }
    

赋值过程

赋值过程及内存相关的知识,通过以下代码做分析

	name := "这是一段字符串"nickname := name
  1. 声明并初始化 name
    • name 被声明为一个新的局部变量,并且类型被推断为字符串(string)。它指向一个内存位置,该位置存储着字符串 "这是一段字符串"
  2. 声明并初始化 nickname
    • 接下来,另一个新变量 nickname 被声明,并使用短变量声明方式同时进行初始化。
    • 在这个赋值操作中,由于字符串在Go中是不可变的,并且赋值操作只是复制了引用(而非数据本身),因此 nickname 也会指向同一块内存地址,即指向 "这是一段字符串" 的那块内存。
    • 注意!namenickname 都指向同一个字符串字面值 "这是一段字符串" 的实际数据。这意味着他们共享相同的底层字节数组。然而,每个字符串变量(如 namenickname)本身也是一个独立的实体,拥有自己的内存地址。
  3. 内存共享
    • 重要的是理解,在这种情况下,没有新的字符串数据被创建。两个变量 namenickname 实际上共享相同的底层数据。这意味着对于只读数据如字符串来说非常高效。
  4. 垃圾回收考虑
    • 在Go语言中,当没有任何引用指向某块内存时(即不再有任何方式访问该内存),这块内存就可能被标记为可回收。只要任何一个变量仍然引用着那块内存(即使其中一个已经超出了作用域),那么该内存就不会被垃圾回收器回收。只有当所有引用(name和nickname)都消失时才会释放相关资源。

结合代码说明

package mainimport ("fmt""unsafe" // 代码使用 `unsafe` 包将变量的地址转换为 `stringHeader` 类型的指针,以便访问字符串的底层数据指针。
)func main() {// 情况1 变量地址不同,指向的值地址相同name := "这是一段字符串"nickname := namefmt.Println("============打印变量的地址信息============")fmt.Printf("变量'name'  地址:【%p】\n", &name)fmt.Printf("变量'nickname'  地址:【%p】\n", &nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer1 := (*stringHeader)(unsafe.Pointer(&name)).DatanicknameDataPointer1 := (*stringHeader)(unsafe.Pointer(&nickname)).Datafmt.Printf("'name'数据的指向地址: %x\n", nameDataPointer1)fmt.Printf("'nickname'数据的指向地址: %x\n\n", nicknameDataPointer1)// 情况2 变量地址不同,指向的值地址也相同,因为字符串是不可变的nickname = "这是一段字符串"fmt.Println("============打印变量的地址信息============")fmt.Printf("变量'name'  地址:【%p】\n", &name)fmt.Printf("变量'nickname'  地址:【%p】\n", &nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer2 := (*stringHeader)(unsafe.Pointer(&name)).DatanicknameDataPointer2 := (*stringHeader)(unsafe.Pointer(&nickname)).Datafmt.Printf("'name'数据的指向地址: %x\n", nameDataPointer2)fmt.Printf("'nickname'数据的指向地址: %x\n\n", nicknameDataPointer2)// 情况3 变量地址不同,指向的值地址不同nickname = "这是一段字符串!"fmt.Println("============打印变量的地址信息============")fmt.Printf("变量'name'  地址:【%p】\n", &name)fmt.Printf("变量'nickname'  地址:【%p】\n", &nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer3 := (*stringHeader)(unsafe.Pointer(&name)).DatanicknameDataPointer3 := (*stringHeader)(unsafe.Pointer(&nickname)).Datafmt.Printf("'name'数据的指向地址: %x\n", nameDataPointer3)fmt.Printf("'nickname'数据的指向地址: %x\n\n", nicknameDataPointer3)}/*
stringHeader用于获取字符串的底层数据指针,这个结构体是 Go 语言中字符串的内部表示。
`Data` 是一个 `uintptr` 类型的字段,它用来存储指向字符串数据的指针
`Len` 存储字符串的长度。
*/
type stringHeader struct {Data uintptr // 指向实际数据的指针Len  int     // 字符串的长度
}

打印结果

============打印变量的地址信息============
变量'name'  地址:【0xc000014070】
变量'nickname'  地址:【0xc000014080】
'name'数据的指向地址: 2f2a8fd
'nickname'数据的指向地址: 2f2a8fd============打印变量的地址信息============
变量'name'  地址:【0xc000014070】
变量'nickname'  地址:【0xc000014080】
'name'数据的指向地址: 2f2a8fd
'nickname'数据的指向地址: 2f2a8fd============打印变量的地址信息============
变量'name'  地址:【0xc000014070】
变量'nickname'  地址:【0xc000014080】
'name'数据的指向地址: 2f2a8fd
'nickname'数据的指向地址: 2f2b3eb

这段 Go 语言代码演示了字符串在不同情况下的内存地址和底层数据指针的变化。代码分为三个部分,每部分都打印了变量的内存地址和它们指向的字符串数据的地址。这里使用了 unsafe 包来操作指针和内存,这是 Go 语言中一个特殊的包,允许程序绕过类型安全性的限制。

  1. 情况1:创建了两个变量 namenickname,它们被赋予了同一个字符串字面量。由于字符串是不可变的,nickname 实际上是对 name 所指向的字符串的一个别名。因此,尽管 namenickname 作为变量有不同的内存地址,它们指向的字符串数据的地址是相同的。

  2. 情况2nickname 被重新赋值为与 name 相同的字符串字面量。由于字符串是不可变的,这个新的字符串字面量可能会指向与 name 相同的内存地址,或者可能会创建一个新的字符串实例,这取决于 Go 运行时的优化。在这个例子中,由于字符串内容相同,nickname 可能指向了与 name 相同的内存地址。

  3. 情况3nickname 被重新赋值为一个不同的字符串字面量,这次字符串内容与 name 不同。由于字符串是不可变的,这个新的字符串字面量将创建一个新的内存地址来存储这个新的字符串内容。因此,nickname 将指向一个新的内存地址,与 name 所指向的地址不同。

待后面我们展开学习go中的 *(指针)与&(地址)后,再回头来理解上面的赋值和内存相关,会容易理解很多。

相关文章:

04 go语言(golang) - 变量和赋值过程

变量 在Go语言中,变量的定义和初始化是编程的基础部分。Go提供了多种方式来声明和初始化变量,以适应不同的使用场景。 基本变量声明 使用var关键字: 使用var关键字可以在函数内部或外部声明变量。如果在函数外部声明,该变量为全…...

语言/图像/视频模型一网打尽!BigModel大模型开放平台助力开发者轻松打造AI新应用!

2024年8⽉28⽇,在ACM SIGKDD(国际数据挖掘与知识发现⼤会,KDD)上会议现场,智谱AI重磅推出了新⼀代全⾃研基座⼤模型 GLM-4-Plus、图像/视频理解模型 GLM-4V-Plus 和⽂⽣图模型 CogView3-Plus。这些新模型,已…...

Go语言Linux环境搭建以编写第一个Go程序

目录 文章目录 目录Go语言入门1、说明2、CentOS7安装Go3、编写第一个程序3.1、编写程序3.2、运行程序3.3、生成二进制文件4、编写第一个web程序4.1、编写代码4.2、运行程序4.3、测试访问4.4、生成二进制配置Vim-go语法高亮1)、下载和设置Vundle.vim(vim安装插件的工具)2)、…...

使用 Go 构建一个最小的 API 应用

最近有项目要使用 Go 开发,作为一个. NET Core 选手,准备先撸一个包含 CRUD 的最小 MVP 项目练手。 要创建一个 TODO 应用,会创建下面这些接口: APIDescriptionRequest bodyResponse bodyGET /todoitemsGet all to-do itemsNone…...

MySQL 日常维护指南:常见任务、频率及问题解决

MySQL 作为一种广泛使用的开源关系型数据库,随着数据量和应用复杂性的增加,定期的数据库维护对于保持系统高效运行至关重要。通过合理的日常维护,数据库管理员能够确保 MySQL 数据库的稳定性、性能以及数据的完整性。本文将介绍 MySQL 的常见…...

oracle ORA-24920:列大小对于客户机过大

问题描述 在一次读取某个视图数据过程中,当数据读取到x条时,报错ORA-24920:列大小对于客户机过大。 通过查询资料得知,oracle 数据库升级到了12c,VARCHAR2的容量也从4000升级到了32767。 所以猜测某个字段的长度超过4…...

使用 Docker compose 部署 Nacos(达梦数据库)

1. 制作镜像的源码地址 https://github.com/wangsilingwsl/nacos-dm.git 参考的开源项目:https://github.com/jeecgboot/JeecgBoot/tree/master/jeecg-boot/jeecg-server-cloud/jeecg-cloud-nacos (master分支;tag:v3.7.1&#…...

人工智能 | 阿里通义千问大模型

简介 通义千问系列模型为阿里云研发的大语言模型。千问模型基于 Transformer 架构,在超大规模的预训练数据上进行训练得到。预训练数据类型多样,覆盖广泛,包括大量网络文本、专业书籍、代码等。同时,在预训练模型的基础之上&…...

Windows环境下Qt Creator调试模式下qDebug输出中文乱码问题

尝试修改系统的区域设置的方法: 可以修复问题。但会出现其它问题: 比如某些软件打不开,或者一些软件界面的中文显示乱码! 暂时没有找到其它更好的办法。...

java防止表单重复提交的注解@RepeatSubmit

代码解释 RepeatSubmit 是一个自定义注解,通常用于防止表单重复提交。这个注解可以应用于控制器方法上,以确保同一个请求在一定时间内不会被多次提交。以下是一些常见的参数和用法: value: 注解的名称或描述。 interval: 两次请求之间的最小间…...

HTTP快速入门

HTTP报文结构 HTTP 协议主要由三大部分组成: ● 起始行(start line):描述请求或响应的基本信息; ● 头部字段(header):使用 key-value 形式更详细地说明报文; ● 消息正…...

Nacos简介

Nacos是一个开源的动态服务发现、配置管理和服务管理平台,由阿里巴巴集团开发并开源。它提供了服务注册与发现、配置管理、动态DNS服务、服务健康监测、权重和流量管理等核心特性,非常适合构建云原生应用和微服务架构。 Nacos的核心功能包括&#xff1a…...

基于深度学习的稳健的模型推理与不确定性建模

基于深度学习的稳健模型推理与不确定性建模,是现代AI系统中至关重要的研究方向。随着深度学习在各类应用中的成功,如何保证模型在面对未知或不确定性输入时仍能做出稳健的推理,并能够量化这种不确定性,成为关键问题。稳健性与不确…...

C语言 sizeof 的介绍,以及sizeof计算数组名、 数组首地址、数组的元素之间的区别

一、sizeof 介绍 sizeof 是 C 语言中的一个运算符,用于计算数据类型或变量在内存中占用的字节数。用于计算数据类型或变量所占的内存大小,以字节为单位。它可以在编译时计算其操作数的大小,并返回一个 size_t 类型的值。它可以帮助了解不同类…...

深入理解Oracle闪回技术

引言: Oracle 闪回(Flashback)是一组强大的功能,用于恢复数据库中的数据或对象到过去的某个时间点或状态,而无需进行传统的基于备份和恢复的操作。 Oracle 闪回的主要类型 1. 闪回查询(Flashback Query&…...

Go 语言初探

Google 公司有一个传统,允许员工利用 20% 的工作时间开发自己的实验项目。2007 年 9月,UTF-8 的设计者之一 Rob Pike(罗布.皮克)在 Google 的分布式编译平台上进行 C++ 编译时,与同事 Robert Griesemer (罗布.格里泽默)在漫长的等待中讨论了编程语言面临的主要问题。他们一…...

使用ROS资源编排一键部署LNMP建站环境,手动整理教程

LNMP是目前主流的网站服务器架构之一,适合运行大型和高并发的网站应用,例如电子商务网站、社交网络、内容管理系统等。LNMP分别代表Linux、Nginx、MySQL和PHP。本文阿里云服务器网aliyunfuwuqi.com介绍如何使用阿里云资源编排服务(ROS&#x…...

猎板PCB镍钯金工艺你了解多少?

PCB镍钯金工艺,也称为ENEPIG(Electroless Nickel Electroless PALLADIum Gold)工艺,是一种在PCB表面处理中使用的先进工艺。这种工艺通过在PCB线路板上形成一层镍钯合金层,有效地提高了线路板的耐氧化性、耐腐蚀性和可…...

热更新解决方案2 —— Lua语法相关知识点

概述 开发环境搭建 Lua语法 1.第一个Lua程序 2.变量 print("******变量*******"); --lua当中的简单变量类型 -- nil number string boolean -- lua 中所有的变量声明 都不需要声明变量类型 它会自动的判断类型 -- 类似C# 中的var --lua中的一个变量 可以随便赋值 ——…...

【c++ arx选项板】

static void xlArx_gmenu(void) {if (!g_pPaletteSetEx){g_pPaletteSetEx=CTunnelSectionPaletteSetEx::Instance(...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...