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

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络&#xf…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...