「GO基础」变量


在这里插入图片描述
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
在这里插入图片描述

  • 推荐:「stormsha的主页」👈,持续学习,不断总结,共同进步,为了踏实,做好当下事儿~
  • 专栏导航
    • Python面试合集系列:Python面试题合集,剑指大厂
    • GO基础学习笔记系列:记录博主学习GO语言的笔记,该笔记专栏尽量写的试用所有入门GO语言的初学者
    • 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
    • 运维系列:总结好用的命令,高效开发
    • 算法与数据结构系列:总结数据结构和算法,不同类型针对性训练,提升编程思维

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

💖The Start💖点点关注,收藏不迷路💖

📒文章目录

  • 1、概述
  • 2、值类型和引用类型
  • 3、打印
  • 4、简洁形式,使用 := 赋值操作符
  • 5、init 函数
  • 6、练习


1、概述

在Go语言中,声明变量的一般形式是使用var关键字,但与许多编程语言不同的是,Go选择将变量的类型放在变量的名称之后,而不是之前。这种设计选择主要是为了避免类似于C语言中含糊不清的声明形式。

例如,在C语言中,如果你想要声明两个指针变量,你可能会写:int* a, b;。然而,在这个例子中,只有a是指针,而b不是。如果你想要两个变量都是指针,你需要将它们分开书写。

相比之下,在Go中,你可以轻松地将它们都声明为指针类型,如下所示:var a, b *int。这种语法更清晰,减少了潜在的错误和混淆。你可以在Go语言的声明语法页面上找到更多关于这个话题的讨论。

其次,这种语法能够按照从左至右的顺序阅读,使得代码更加容易理解。

var a int
var b bool
var str string

var (
	a int
	b bool
	str string
)

在Go语言中,声明全局变量时通常使用因式分解关键字的写法。当一个变量被声明后,系统会自动为其赋予该类型的零值,例如int为0,float32(64)为0.0,boolfalsestring为空字符串,指针为nil。在Go中,所有内存都是经过初始化的。

变量的命名遵循骆驼命名法,即第一个单词小写,每个新单词的首字母大写,例如numShipsstartDate。但如果希望全局变量能被外部包使用,则需要将第一个单词的首字母也大写。

变量(包括常量、类型或函数)在程序中有一定的作用范围,称为作用域。如果一个变量在函数体外声明,则被认为是全局变量,可以在整个包甚至外部包(被导出后)使用,无论在哪个源文件中声明或调用。

在函数体内声明的变量称为局部变量,其作用域仅限于函数体内,参数和返回值变量也是局部变量。在if和for等控制结构中声明的变量的作用域仅限于相应的代码块内。通常情况下,局部变量的作用域可以通过代码块(用大括号括起来的部分)来判断。

虽然变量的标识符必须唯一,但可以在某个代码块的内层代码块中使用相同名称的变量,此时外部的同名变量会被暂时隐藏(结束内部代码块的执行后,隐藏的外部同名变量会重新出现,而内部同名变量会被释放),任何操作都只会影响内部代码块的局部变量。

变量可以在编译期间被赋值,赋值给变量使用运算符等号=,也可以在运行时对变量进行赋值操作。

a = 15
b = false

一般情况下,当变量a和变量b之间类型相同时,才能进行如 a = b 的赋值。

声明与赋值(初始化)语句也可以组合起来。

var identifier [type] = value
var a int = 15
var i = 5
var b bool = false
var str string = "Stormsha very NB!"

但是 Go 编译器的智商已经高到可以根据变量的值来自动推断其类型,这有点像 Ruby 和 Python 这类动态语言,只不过它们是在运行时进行推断,而 Go 是在编译时就已经完成推断过程。因此,你还可以使用下面的这些形式来声明及初始化变量:

var a = 15
var b = false
var str = "Stormsha very NB!"

var (
	a = 15
	b = false
	str = "Stormsha very NB!"
	numShips = 50
	city string
)

不过自动推断类型并不是任何时候都适用的,当你想要给变量的类型并不是自动推断出的某种类型时,你还是需要显式指定变量的类型,例如:

var n int64 = 2

然而,var a 这种语法是不正确的,因为编译器没有任何可以用于自动推断类型的依据。变量的类型也可以在运行时实现自动推断,例如:

var (
	GENDER = os.Getenv("GENDER")
	SOURCE = os.Getenv("SOURCE")
	GOROOT = os.Getenv("GOROOT")
)

这种写法主要用于声明包级别的全局变量,当你在函数体内声明局部变量时,应使用简短声明语法 :=,例如:

a := 1

下面这个例子展示了如何通过 runtime 包在运行时获取所在的操作系统类型,以及如何通过 os 包中的函数 os.Getenv() 来获取环境变量中的值,并保存到 string 类型的局部变量 path 中。

package main

import (
	"fmt"
   "runtime"
	"os"
)

func main() {
	var goos string = runtime.GOOS
	fmt.Printf("当前操作系统是: %s\n", goos)
	source := os.Getenv("SOURCE")
	fmt.Printf("资源位置是:%s\n", source)
}

如果你在 Windows 下运行这段代码,则会输出 The operating system is: windows 以及相应的环境变量的值;如果你在 Linux 下运行这段代码,则会输出 The operating system is: linux 以及相应的的环境变量的值。

2、值类型和引用类型

程序中所用到的内存在计算机中使用一堆箱子来表示(这也是人们在讲解它的时候的画法),这些箱子被称为“字”。根据不同的处理器以及操作系统类型,所有的字都具有 32 位(4 字节)或 64 位(8 字节)的相同长度;所有的字都使用相关的内存地址来进行表示(以十六进制数表示)。

所有像 intfloatboolstring 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:

在这里插入图片描述

另外,像数组和结构 (struct)这些复合类型也是值类型。

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:

在这里插入图片描述

可以通过&i来获取变量i的内存地址,例如:0xf840000040(每次的地址可能不同)。值类型的变量的值存储在栈中。内存地址会因机器而异,即使相同的程序在不同的机器上执行也会有不同的内存地址,因为每台机器可能有不同的存储器布局和位置分配。

更复杂的数据通常需要使用多个字,这些数据一般使用引用类型保存。引用类型的变量r1存储的是r1的值所在的内存地址(数字),或者内存地址中第一个字所在的位置。

在这里插入图片描述

内存地址被称为指针,它实际上存储在另一个字中。引用类型的指针可以指向连续内存地址中的多个字(连续内存布局),这是效率最高的存储形式;也可以将这些字分散存储在内存中,每个字都指示下一个字所在的内存地址。

当使用赋值语句r2 = r1时,只有引用(地址)被复制。如果r1的值被改变,则所有引用该值的指针都会指向修改后的内容,在这个例子中,r2也会受到影响。

在Go语言中,指针属于引用类型,其他引用类型还包括slicesmapschannel。被引用的变量存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间。

3、打印

函数Printf可以在fmt包外部使用,因为它以大写字母P开头,主要用于向控制台打印输出。通常使用的格式为:

func Printf(format string, list of variables to be printed)

格式化字符串为:"当前操作系统是: %s\n"

这个格式化字符串可以包含一个或多个格式化标识符,例如:%..,其中..可以被不同类型的标识符替换,如%s表示字符串标识符,%v表示使用类型的默认输出格式的标识符。这些标识符对应的值从格式化字符串后的逗号开始按顺序添加,如果参数超过1个,也需要用逗号分隔。这些占位符可以很好地控制格式化输出的文本。

函数fmt.SprintfPrintf的作用完全相同,但它将格式化后的字符串作为返回值返回给调用者,因此可以在程序中使用包含变量的字符串。

函数fmt.Printfmt.Println会自动使用格式化标识符%v对字符串进行格式化,两者都会在每个参数之间自动添加空格,后者还会在字符串末尾添加换行符。例如:

fmt.Print("年龄:", 18)

将输出:"年龄:18"

4、简洁形式,使用 := 赋值操作符

在变量初始化时,我们可以省略变量类型,让系统自动推断。在概述中的声明语句中,都是使用var关键字声明的变量。因此,我们可以将它们简写为a := 50b := false

ab的类型(intbool)将由编译器自动推断。这是使用变量的首选形式,但只能在函数体内使用,不能用于全局变量的声明和赋值。使用操作符:=可以高效地创建一个新的变量,这被称为初始化声明。

在相同的代码块中,我们不能对相同名称的变量再次使用初始化声明,例如:a := 20 是不被允许的,编译器会提示错误 "no new variables on left side of :="。但是,a = 20 是可以的,因为这是给相同的变量赋予一个新的值。

如果你在定义变量a之前使用它,会得到编译错误 "undefined: a"

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:

func main() {
   var a string = "NB"
   fmt.Println("stormsha ")
}

尝试编译这段代码将得到错误 a declared and not used

此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用 fmt.Printf("stormsha ", a) 会移除错误。

但是全局变量是允许声明但不使用。

其他的简短形式为:

同一类型的多个变量可以声明在同一行,如:

var a, b, c int

多变量可以在同一行进行赋值,如:

a, b, c = 5, 7, "abc"

上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:

a, b, c := 5, 7, "abc"

右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 “abc”。这被称为 并行 或 同时 赋值。如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a。

空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。

_实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。

并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:

val, err = Func1(var1)

5、init 函数

除了在全局声明中初始化,变量也可以在init()函数中初始化。init()函数是一种特殊的函数,不能被显式调用,而是在每个包完成初始化后自动执行,且执行优先级高于main()函数。

每个源文件可以包含多个init()函数,同一源文件中的init()函数按照从上到下的顺序执行。如果一个包有多个源文件包含init()函数,官方鼓励但不保证按照文件名的顺序调用。初始化总是以单线程的方式按包的依赖关系顺序执行。

init()函数的一个可能用途是在程序开始执行之前对数据进行检查或修复,以确保程序状态的正确性。

// trans/init.go
package trans

import "math"

var Pi float64

func init() {
   Pi = 4 * math.Atan(1) // init() function computes Pi
}

在它的 init() 函数中计算变量 Pi 的初始值。

如下示例中导入了包 trans(需要 init.go 目录为 ./trans/init.go )并且使用到了变量 Pi:

package main

import (
   "fmt"
   "./trans"
)

var twoPi = 2 * trans.Pi

func main() {
   fmt.Printf("2*Pi = %g\n", twoPi) // 2*Pi = 6.283185307179586
}

init() 函数也经常被用在当一个程序开始之前调用后台执行的 goroutine,如下面这个例子当中的 backend()

func init() {
   // setup preparations
   go backend()
}

6、练习

推断以下程序的输出,然后编译并执行它们,验证和你的推断是否一致。

练习1

package main

var a = "G"

func main() {
   n()
   m()
   n()
}

func n() { print(a) }

func m() {
   a := "O"
   print(a)
}

练习2:

package main

var a = "G"

func main() {
   n()
   m()
   n()
}

func n() {
   print(a)
}

func m() {
   a = "O"
   print(a)
}

练习3:

package main

var a string

func main() {
   a = "G"
   print(a)
   f1()
}

func f1() {
   a := "O"
   print(a)
   f2()
}

func f2() {
   print(a)
}

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏、分享下吧,非常感谢!👍 👍 👍

🔥🔥🔥道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

💖The End💖点点关注,收藏不迷路💖

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

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

相关文章

【java】(软考)面向对象---责任链解析

目录 责任链的意义 手写笔记 ​编辑 责任链的意义 当您把请求给出时,如果某对象不能实现您的操作,责任链会自动把您的请求传给它的下一级 从而避免请求的发送者和接受者之间的耦合关系 这里以2007年下半年试题七进行说明 题目描述 某企业的采购审批…

EFK安装与使用!!!

一、将你的项目进行打包。 二、上传到docker, 启动项目 三、修改前端的代理路径 四、EFK相关配置 1、docker-compose.yml: version: 3 services:kibana:image: kibana:7.14.0ports:- "5601:5601"environment:- ELASTICSEARCH_HOSTShttp://19…

Excel数据处理:使用条件格式

选中你要特别体现的列 然后在开始处点击条件格式 标记大于1500000的值 点击突出显示 点击大于 输入1500000 设置单元格格式 点击条件格式 点击清除 去掉表头的格式 标记重复值强调显示 公式的使用 要去锁定单元格的值(加上F4) 比较后一列相同位置…

数据结构(七)——B树和B+树

7.4.1_1 B树 5叉查找树 //5叉排序树的结点定义 struct Node {ElemType keys[4]; //最多4个关键字struct Node &child[5]; //最多5个孩子int num; //结点中有几个关键字 }; 如何保证查找效率? eg:对于5叉排序树,规定…

kafka学习1 - 线程、进程消息通信方式、JMS模型、Kafka原理图

1、线程和线程之间的数据交互 两个不同的线程之间是可以通过堆内存的方式进行数据交互的; T1线程的数据发送得到堆内存,T2线程就可以共享堆内存的数据。 存在的问题: T1线程发送数据的速率是50/s,T2线程消费的速率是30/s&#…

Jmeter-非GUI模式下运行jmeter脚本-适用于服务器上持续集成测试

背景 大部分Jmeter脚本都是部署在Linux上运行,利用Jenkins做接口自动化,定时巡检任务。 执行命令 1.进入jmeter的目录,bin文件夹 cd C:\path\to\jmeter\bin2.运行脚本文件 jmeter -n -t D:\{脚本文件目录}\xxx.jmx -l D:\{脚本文件目录}…

mac可以玩steam吗 mac安装steam教程 苹果电脑能打steam游戏吗 苹果电脑怎么安装windows 苹果mac电脑配置AI功能的M4芯片

众所周知,Steam作为一个热门的游戏平台,深受国内外玩家的喜爱,平台中包含了无数的游戏,在作战时玩家们能够与朋友们互动聊天,还能匹配好友组队,同时还能增进与同伴的默契度。 但是最近有玩家们提问说&#…

Python编程技巧揭秘:深入理解Lambda函数,如何使用匿名函数简化你的代码

文章目录 1. Lambda函数2. 在实际应用中使用Lambda2.1 使用Lambda函数进行列表排序2.2 在高阶函数中使用Lambda 3. Lambda的局限性和注意点 在这篇文章中,将深入探讨Python中的Lambda函数,这是一种强大的编程工具,可以以更简洁、高效的方式编…

C语言:内存动态开辟

一、malloc和free 1.如果开辟成功,则返回一个指向开辟好空间的指针。 2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。 3.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在…

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 一、检查服务是否开启 找到 【Docker Desktop Service】,然后,启动他; 你也可以直接设置为“自动” 找到服务,右键》属性》启动类型:自动》点击…

java解决常见递归问题

最基本的,斐波那契数列,阶乘(0,1的阶乘均为1) 返回字母“x”第一次出现的位置 使用递归编写一个函数,读取一个字符串,返回字母“x”第一次出现的位置。例如,字符串 "abcdefgh…

014Node.js时间格式包silly-datetime安装与使用

下载: https://www.npmjs.com/网站上下载silly-datetime 安装 npm i silly-datetime --save var sd require(silly-datetime);console.log(new Date()); //2024-04-18T04:40:38.505Zvar dsd.format(new Date(), YYYY-MM-DD HH:mm);console.log(d); //2024…

B树(B-tree)

B树(B-tree) B树(B-tree)是一种自平衡的多路查找树,主要用于磁盘或其他直接存取的辅助存储设备 B树能够保持数据有序,并允许在对数时间内完成查找、插入及删除等操作 这种数据结构常被应用在数据库和文件系统的实现上 B树的特点包括: B树为…

前端常用的数据加密方式

前端开发中,数据安全是至关重要的一个方面。数据加密是保护用户隐私和信息安全的关键方法之一。 前端常用的数据加密方式涵盖了对传输数据的加密、存储数据的加密以及客户端与服务器端之间通信的加密。 1. 对称加密算法 对称加密算法使用相同的密钥进行加密和解密…

STM32H750外设ADC之双重 ADC 模式

目录 概述 1 双重 ADC 模式介绍 1.1 双重 ADC模式 1.2 双重 ADC 模式的类型 2 双重 ADC 模式寄存器的配置 3 模式功能实现 3.1 注入同步模式 3.2 支持独立注入的常规同步模式 3.2.1 中断的方式 3.2.2 DMA 读取常规数据 3.3 支持独立注入的交替模式 3.3.1 中断触发…

Java面试八股之简述Servlet体系结构

简述Servlet体系结构 Servlet是Java Web开发中的核心组件,用于接收和响应HTTP请求,生成动态内容。它具有平台无关性、协议无关性和动态内容生成能力,遵循明确的生命周期。尽管现代Web开发中更多使用高级框架,但Servlet作为基础&a…

安装WSL2

PS C:\Users\pc> wsl --set-default-version 2 有关与 WSL 2 关键区别的信息,请访问 https://aka.ms/wsl2操作成功完成。PS C:\Users\pc> wsl --update 正在检查更新。 已安装最新版本的适用于 Linux 的 Windows 子系统。PS C:\Users\pc> wsl --shutdownPS…

中霖教育:报名一级建造师查社保吗?

一级建造师考试报名是否需要查社保?不同地区的要求不一样,有些地区需要考生提供社保,但是有些地区则无需出示社保证明。建议考生详细阅读当地考试通知,以确认是否涉及社保的相关要求。 在要求提供社保证明的地区,报名…

Jira搭建过程

看到很多小伙伴对jira有兴趣,我们今天就来分享一下jira的搭建吧 首先要明白jira是什么? 看来搭建jira也是我们测试人员需要具备的技能之一了.下面是详细的大家步骤: 1.系统环境准备 Centos 7.5 Mysql 5.6 Java1.8 2.软件安装包 atlassian-jira-software-7.13.0-x64.bin …

c++的学习之路:26、AVL树

摘要 本章主要是说一下AVL树的实现,这里说的是插入的底层原理 目录 摘要 一、原理 二、四种旋转 1、左单旋 2、右单旋 3、左右双旋 4、右左双旋 三、代码实现 1、节点创建 2、插入 3、旋转 4、判断是否平衡 5、测试 四、代码 一、原理 前面说了搜索…