书接上回,Go lang1.18首个程序的运行犹如一声悠扬的长笛,标志着并发编程的Go lang巨轮正式开始起航。那么,在这艘巨轮之上,我们首先该做些什么呢?当然需要了解最基本的语法,那就是基础变量的声明与使用。

变量的声明与使用

变量是什么玩意?是具体的数据被内存存储之后内存地址的名称。说白了就是内存中的门牌号,在go lang中声明变量有很多种方式,相对严谨的:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name int
name = 1 f.Println(name) } 1

这里用var关键字声明变量name,变量名称可以是字母或下划线开头,由一个或多个字母、数字、下划线组成。随后指定数据类型,这里是整形,接着进行赋值操作,如果没有赋值动作,go lang会自动填充一个默认值:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name int  

	f.Println(name)  

}

0

相对简单一点的声明方式:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name = 1  

	f.Println(name)  

}

如果一个变量有一个初始值,go lang将自动能够使用初始值来推断该变量的类型。因此,如果变量具有初始值,则可以省略变量声明中的类型,也就是说一个,你得提前让go lang知道这个变量的数据类型,无论是通过那种方式。

最后,类似Python中海象操作符的声明方式:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	name := 1  

	f.Println(name)  

}

海象操作符这样的声明方式可以不使用var关键字,事实上,它更像是一个连贯操作,既声明又赋值,算得上是赋值表达式。

但需要注意已经声明过的(多个变量同时声明时,至少保证一个是新变量),否则会导致编译出错:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	name = 1  

	//重复赋值  

	name := 2  

	f.Println(name)  

}

程序返回:

command-line-arguments
# command-line-arguments
.\test.go:9:2: undefined: name
> Elapsed: 1.097s
> Result: Error

另外,海象操作符声明只能被用在方法里面,而不可以用于全局变量的声明与赋值。

如果不想手动一个一个赋值,也可以进行多变量赋值的操作:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name1, name2, name3 = 1, "2", false  

	f.Println(name1)
f.Println(name2)
f.Println(name3) } 1
2
false

变量永远都必须先声明才能使用,这是放之四海而皆准的原则,不同于Python或者Ruby,go lang是静态语言,要求变量的类型和赋值的类型必须一致:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name = "你好"  

	name = 1  

	f.Println(name)  

}

这里会报类型异常的错误:

command-line-arguments
# command-line-arguments
.\test.go:11:9: cannot use 1 (untyped int constant) as string value in assignment
> Elapsed: 0.561s
> Result: Error

最后,声明了变量就需要使用,如果不用,那么声明的意义在哪儿呢?

func main() {
var a string = "abc"
fmt.Println("hello, go lang")
}

编译后会报异常:

a declared and not used

变量内存地址、占用空间大小和交换赋值

任何声明的变量都在内存中有自己的地址,我们可以通过&关键字将其获取出来:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name = "你好"  

	f.Println("name的内存地址是", &name)  

}

程序返回:

name的内存地址是 0xc00003c250

和Python的内存管理机制不同,go lang会将相同值的变量指向不同的内存地址:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	//声明变量  

	var name = "你好"  

	a := name  

	f.Println("name的内存地址是", &name)  

	f.Println("a的内存地址是", &a)  

}

程序返回:

name的内存地址是 0xc00003c230
a的内存地址是 0xc00003c240

但地址的范围是相似的,这样更方便同类型同值的变量回收,有点类似“网段”的概念。

我们也可以通过unsafe包的Sizeof方法来获取变量具体在内存中占用多少空间:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

import u "unsafe"    

func main() { // 声明 main 主函数入口  

	a := 100
f.Println("a的地址:", &a) f.Println("占用内存:", u.Sizeof(a)) }

程序返回:

a的地址: 0xc0000aa058
占用内存: 8

如果我们想交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	b, a := 1, 2  

	f.Println(&b, &a)  

	b, a = a, b  

	f.Println(b, a)  

	f.Println(&b, &a)  

}

程序返回:

0xc00012c058 0xc00012c070
2 1
0xc00012c058 0xc00012c070

由此我们可以发现,值交换了,但内存地址并未改变,可以理解为旧瓶装新酒。交换赋值的底层逻辑也和Python一样,需要有第三个隐藏变量来做值的传递。

另外,golang当中也支持匿名变量,也就是说对于我们不需要的返回值或者变量,我们可以不用额外定义一个变量去接收。否则没有用处,还会报错:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	a, _ := 1, 2  

	f.Println("a = ", a) // 1  

}

常量constant

常量(constant)表示不变的值。在程序运行时,不会被代码逻辑修改。比如数学上的圆周率、自然常数e等等:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	const value int = 1
// value = 100 // 常量是不允许被修改的
f.Println("value = ", value) }

这里通过const关键字来代替var关键字来声明常量,和JavaScript语法一致。

常量声明也可以被用于枚举场景,就是所谓的常量组:

const (
Unknown = 0
Female = 1
Male = 2
)

在常量声明表达式中,我们也可以用iota关键字进行动态声明:

package main // 声明 main 包  

import f "fmt" // 导入 fmt 包,打印字符串时需要用到  

func main() { // 声明 main 主函数入口  

	// const来定义枚举类型
const (
// 可以在const()中添加一个关键字iota, 每行的iota都会累加1, 第一行的iota默认是0
a = iota + 1 // iota = 0
b // iota = 1
c // iota = 2
) f.Println("a = ", a) // 1
f.Println("b = ", b) // 2
f.Println("c = ", c) // 3 }

变量作用域

变量的作用域可以理解为可访问指定变量的程序的某个范围。可以在类,方法,循环等中定义变量。像C / C ++一样,在Golang中,所有的标识符都是词法(或静态)作用域,即变量的作用域可以在编译时确定,也就是说,和Python不一样的是,Go lang是具备块作用域的:

//局部变量
package main
import "fmt" //主函数
func main() { //从这里开始主函数的局部作用域
//主函数内的局部变量
var myvariable1, myvariable2 int = 69, 145 // 显示变量的值
fmt.Printf("myvariable1 变量的值 : %d\n", myvariable1) fmt.Printf("myvariable2 变量的值 : %d\n", myvariable2) } // 此处主要函数的局部作用域结束

在方法或块中声明的变量称为局部变量,这些不能在函数或块之外访问。这些变量也可以在函数内的for,while语句等内部声明,但是,这些变量可以由函数内的嵌套代码块访问,这些变量也称为块变量。

如果在同一作用域中用相同的名称声明两次这些变量,则会出现编译时错误。函数执行结束后,这些变量将不存在。在循环外声明的变量也可以在嵌套循环内访问。这意味着方法和所有循环都可以访问全局变量。局部变量可被循环访问,并在该函数内执行函数。在循环体内声明的变量对循环体外不可见。

除此以外,我们可以在程序内定义全局变量:

//全局变量
package main
import "fmt" // 全局变量声明
var myvariable1 int = 100 func main() { // 主函数内部的局部变量
var myvariable2 int = 200 //显示全局变量
fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1) //显示局部变量
fmt.Printf("局部变量 myvariable2 的值是 : %d\n", myvariable2) //调用函数
display() } func display() {
// 显示全局变量
fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1) }

在函数或块之外定义的变量称为全局变量,这些变量在程序的整个生命周期中都可用。

最后,go lang也有系统的内置作用域,也就是内置的关键字变量,所以我们声明变量的时候,不能和系统关键字重名,否则系统就不知道到底该调用那个作用域的变量了:

var和const :变量和常量的声明
var varName type 或者 varName : = value
package and import: 导入
func: 用于定义函数和方法
return :用于从函数返回
defer someCode :在函数退出之前执行
go : 用于并行
select 用于选择不同类型的通讯
interface 用于定义接口
struct 用于定义抽象数据类型
break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制
chan用于channel通讯
type用于声明自定义类型
map用于声明map类型数据
range用于读取slice、map、channel数据

结语

变量是一切逻辑的基础,没有变量就不可能有运算、判断以及相关业务逻辑。如果进行类比的话,变量操作就是一门功夫的内功心法,只有掌握了内功才能用内力催动招式,同样地,掌握一门内功就可以举一反三,触类旁通,君不见go lang中使用的系统关键字也都会出现在诸如Python、Javascript等脚本语言中,所以说白了,天下武功,殊途同归,原理上大同小异,只是运使法门上略有不同,却还是有相互映照之处,下一回我们将进入到具体变量类型的修炼,欲知更多,敬请期待。

牛刀小试基本语法,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本语法和变量的使用EP02的更多相关文章

  1. 延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17

    先行定义,延后执行.不得不佩服Go lang设计者天才的设计,事实上,defer关键字就相当于Python中的try{ ...}except{ ...}finally{...}结构设计中的finall ...

  2. 仙人指路,引而不发,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中New和Make函数的使用背景和区别EP16

    Golang只有二十五个系统保留关键字,二十几个系统内置函数,加起来只有五十个左右需要记住的关键字,纵观编程宇宙,无人能出其右.其中还有一些保留关键字属于"锦上添花",什么叫锦上添 ...

  3. 清源正本,鉴往知来,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中引用类型是否进行引用传递EP18

    开篇明义,Go lang中从来就不存在所谓的"引用传递",从来就只有一种变量传递方式,那就是值传递.因为引用传递的前提是存在"引用变量",但是Go lang中从 ...

  4. 你有对象类,我有结构体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06

    再续前文,在面向对象层面,Python做到了超神:万物皆为对象,而Ruby,则干脆就是神:飞花摘叶皆可对象.二者都提供对象类操作以及继承的方式为面向对象张目,但Go lang显然有一些特立独行,因为它 ...

  5. 百亿数据百亿花, 库若恒河沙复沙,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang数据库操作实践EP12

    Golang可以通过Gorm包来操作数据库,所谓ORM,即Object Relational Mapping(数据关系映射),说白了就是通过模式化的语法来操作数据库的行对象或者表对象,对比相对灵活繁复 ...

  6. 化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07

    函数是基于功能或者逻辑进行聚合的可复用的代码块.将一些复杂的.冗长的代码抽离封装成多个代码片段,即函数,有助于提高代码逻辑的可读性和可维护性.不同于Python,由于 Go lang是编译型语言,编译 ...

  7. 层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10

    Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就"导包"就行了,无论是内部的还是外部的,使用im ...

  8. 因势而变,因时而动,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang泛型(generic)的使用EP15

    事实上,泛型才是Go lang1.18最具特色的所在,但为什么我们一定要拖到后面才去探讨泛型?类比的话,我们可以想象一下给小学一年级的学生讲王勃的千古名篇<滕王阁序>,小学生有多大的概率可 ...

  9. 兔起鹘落全端涵盖,Go lang1.18入门精炼教程,由白丁入鸿儒,全平台(Sublime 4)Go lang开发环境搭建EP00

    Go lang,为并发而生的静态语言,源于C语言又不拘泥于性能,高效却不流于古板,Python灵活,略输性能,Java严谨,稍逊风骚.君不见各大厂牌均纷纷使用Go lang对自己的高并发业务进行重构, ...

  10. 巨细靡遗流程控制,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang流程结构详解EP09

    流程结构就是指程序逻辑到底怎么执行,进而言之,程序执行逻辑的顺序.众所周知,程序整体都是自上由下执行的,但有的时候,又不仅仅是从上往下执行那么简单,大体上,Go lang程序的流程控制结构一共有三种: ...

随机推荐

  1. 文心一言 VS 讯飞星火 VS chatgpt (108)-- 算法导论10.1 6题

    六.用go语言,说明如何用两个栈实现一个队列,并分析相关队列操作的运行时间. 文心一言: 使用两个栈实现队列的基本思想是利用一个栈(stack1)来处理入队操作,另一个栈(stack2)来处理出队操作 ...

  2. CF1575I Illusions of the Desert

    prologue 还是太菜了,这个 154 行的树剖 20min 才敲完. analysis 首先,处理这个给到我们的这个式子. \[\max(| a _ u + a _ v |, | a _ u - ...

  3. 若依(ruoyi)开源系统-多数据源问题踩坑实录

    内容概要 上一节内容   介绍了用开源系统若依(ruoyi)搭建页面的过程.在实际项目中,经常遇到多数据源后者主从库的情况.本节记录若依多数据源配置过程中遇到的问题排查过程. 背景描述 1.上一节在r ...

  4. .then()方法的意思和用法

    then()方法是异步执行. 意思是:就是当.then()前的方法执行完后再执行then()内部的程序,这样就避免了,数据没获取到等的问题. 语法:promise.then(onCompleted, ...

  5. 掌握 Kubernetes 故障排除:有效维护集群的最佳实践和工具

    Kubernetes 是一款管理容器化应用程序的强大工具.然而,与任何复杂的系统一样,使用它时也可能出错.当问题出现时, 掌握有效的故障排除技术和工具非常重要. 本文将介绍以下步骤,助您了解事件收集的 ...

  6. Vue之属性

    Vue中的属性:举例 看一下就明白了 <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  7. CSS3之创建3D场景

    1.属性介绍 perspective:800    平面距离三维中方框的距离 perspective-origin:50% 50%   表示从平面上哪个位置看三维图 ,相当于是X轴和Y轴,此时表示平面 ...

  8. docker 安装、升级、修改数据目录

    1.查看系统要求 Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看CentOS的内核版本. uname -a 2.删除旧版本 yum remove docker docker-c ...

  9. 【行云流水线实践】基于“OneBuild”方法对镜像进行快速装箱

    在云原生领域,无论使用哪种编排调度平台,Kubernetes,DockerSwarm,OpenShift等,业务都需要基于镜像进行交付,我们在内部实践"Source-to-image&quo ...

  10. 1. JVM内存区块

    本篇文章主要讲解Java(JVM)在运行期间,其运行时数据区域的作用.职责与划分.包括堆内存.栈内存--虚拟机栈.本地方法栈.方法区.常量池.程序计数器等概念. 采集可以使用JavaMXBean(采集 ...