当前位置: 首页 > 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(...

7.4.分块查找

一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) ​遍历字符串​:通过外层循环逐一检查每个字符。​遇到 ? 时处理​: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: ​与…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 ​…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四&#xff…...