Go程序设计语言2.3 变量

2.3 变量


var声明创建一个具体类型的变量,然后给它附加一个名字,设置它的初始值。每一个声明有一个通用的形式:

 

类型和表达式部分可以省略一个,但是不能都省略。如果类型省略,它的类型将由初始化表达式决定;如果表达式省略,其初始值对应于类型的零值——对于数字是0,对于布尔值是false,对于字符串是"",对于接口和引用类型(slice、指针、map、通道、函数)是nil。对于一个像数组或结构体这样的复合类型,零值是其所有元素或成员的零值。

零值机制保障所有的变量是良好定义的,Go里面不存在未初始化变量。这种机制简化了代码,并且不需要额外工作就能感知边界条件的行为。例如:

 

输出空字符串,而不是一些错误或不可预料的行为。Go程序员经常花费精力来使复杂类型的零值有意义,以便变量一开始就处于一个可用状态。

可以声明一个变量列表,并选择使用对应的表达式列表对其初始化。忽略类型允许声明多个不同类型的变量。

 

初始值设定可以是字面量值或者任意的表达式。包级别的初始化在main开始之前进行(参考2.6.2节),局部变量初始化和声明一样在函数执行期间进行。

变量可以通过调用返回多个值的函数进行初始化:

 

2.3.1 短变量声明

在函数中,一种称作短变量声明的可选形式可以用来声明和初始化局部变量。它使用name:= expression的形式,name的类型由expression的类型决定。这里是lissajous函数(参考1.4节)中的三个短变量声明:

 

因其短小、灵活,故而在局部变量的声明和初始化中主要使用短声明。var声明通常是为那些跟初始化表达式类型不一致的局部变量保留的,或者用于后面才对变量赋值以及变量初始值不重要的情况。

 

与var声明一样,多个变量可以以短变量声明的方式声明和初始化:

 

只有当它们对于可读性有帮助的时候才使用多个初始化表达式来进行变量声明,例如短小且天然一组的for循环的初始化。

记住,:=表示声明,而=表示赋值。一个多变量的声明不能和多重赋值(参考2.4.1节)搞混,后者将右边的值赋给左边的对应变量:

 

与普通的var声明类似,短变量声明也可以用来调用像os.Open那样返回两个或多个值的函数:

 

一个容易被忽略但重要的地方是:短变量声明不需要声明所有在左边的变量。如果一些变量在同一个词法块中声明(参考2.7节),那么对于那些变量,短声明的行为等同于赋值。

在如下代码中,第一条语句声明了in和err。第二条语句仅声明了out,但向已有的err变量赋了值。

 

短变量声明最少声明一个新变量,否则,代码编译将无法通过:

 

第二个语句使用普通的赋值语句来修复这个错误。

只有在同一个词法块中已经存在变量的情况下,短声明的行为才和赋值操作一样,外层的声明将被忽略。我们在本章结尾的例子中将看到。

2.3.2 指针

变量是存储值的地方。借助声明创建的变量使用名字来区分,例如x,但是许多变量仅仅使用像x[i]或者x.f这样的表达式来区分。所有这些表达式读取一个变量的值,除非它们出现在赋值操作符的左边,这个时候是给变量赋值。

指针的值是一个变量的地址。一个指针指示值所保存的位置。不是所有的值都有地址,但是所有的变量都有。使用指针,可以在无须知道变量名字的情况下,间接读取或更新变量的值。

如果一个变量声明为var x int,表达式&x(x的地址)获取一个指向整型变量的指针,它的类型是整型指针(*int)。如果值叫作p,我们说p指向x,或者p包含x的地址。p指向的变量写成*p。表达式*p获取变量的值,一个整型,因为*p代表一个变量,所以它也可以出现在赋值操作符左边,用于更新变量的值。

 

每一个聚合类型变量的组成(结构体的成员或数组中的元素)都是变量,所以也有一个地址。

变量有时候使用一个地址化的值。代表变量的表达式,是唯一可以应用取地址操作符&的表达式。

指针类型的零值是nil。测试p!= nil,结果是true说明p指向一个变量。指针是可比较的,两个指针当且仅当指向同一个变量或者两者都是nil的情况下才相等。

 

函数返回局部变量的地址是非常安全的。例如下面的代码中,通过调用f产生的局部变量v即使在调用返回后依然存在,指针p依然引用它:

 

每次调用f都会返回一个不同的值:

 

因为一个指针包含变量的地址,所以传递一个指针参数给函数,能够让函数更新间接传递的变量值。例如,这个函数递增一个指针参数所指向的变量,然后返回此变量的新值,于是它可以在表达式中使用:

 

每次使用变量的地址或者复制一个指针,我们就创建了新的别名或者方式来标记同一变量。例如,*p是v的别名。指针别名允许我们不用变量的名字来访问变量,这一点是非常有用的,但是它是双刃剑:为了找到所有访问变量的语句,需要知道所有的别名。不仅指针产生别名,当复制其他引用类型(像slice、map、通道,甚至包含这里引用类型的结构体、数组和接口)的值的时候,也会产生别名。

指针对于flag包是很关键的,它使用程序的命令行参数来设置整个程序内某些变量的值。为了说明,下面这个变种的echo命令使用两个可选的标识参数:-n使echo忽略正常输出时结尾的换行符,-s sep使用sep替换默认参数输出时使用的空格分隔符。因为这是第4版,所以包名字叫作gopl.io/ch2/echo4。

 

 

flag.Bool函数创建一个新的布尔标识变量。它有三个参数:标识的名字("n"),变量的默认值(false),以及当用户提供非法标识、非法参数抑或-h或-help参数时输出的消息。同样地,flag.String也使用名字、默认值和消息来创建一个字符串变量。变量sep和n是指向标识变量的指针,它们必须通过*sep和*n来访问。

当程序运行时,在使用标识前,必须调用flag.Parse来更新标识变量的默认值。非标识参数也可以从flag.Args()返回的字符串slice来访问。如果flag.Parse遇到错误,它输出一条帮助消息,然后调用os.Exit(2)来结束程序。

让我们运行一些echo测试用例:

 

2.3.3 new函数

另外一种创建变量的方式是使用内置的new函数。表达式new(T)创建一个未命名的T类型变量,初始化为T类型的零值,并返回其地址(地址类型为*T)。

 

使用new创建的变量和取其地址的普通局部变量没有什么不同,只是不需要引入(和声明)一个虚拟的名字,通过new(T)就可以直接在表达式中使用。因此new只是语法上的便利,不是一个基础概念。

下面两个newInt函数有同样的行为。

 

每一次调用new返回一个具有唯一地址的不同变量:

 

这个规则有一个例外:两个变量的类型不携带任何信息且是零值,例如struct{}或[0]int,当前的实现里面,它们有相同的地址。

因为最常见的未命名变量都是结构体类型,它的语法(参考4.4.1节)比较复杂,所以new函数使用得相对较少。

new是一个预声明的函数,不是一个关键字,所以它可以重定义为另外的其他类型,例如:

 

自然,在delta函数内,内置的new函数是不可用的。

2.3.4 变量的生命周期

生命周期指在程序执行过程中变量存在的时间段。包级别变量的生命周期是整个程序的执行时间。相反,局部变量有一个动态的生命周期:每次执行声明语句时创建一个新的实体,变量一直生存到它变得不可访问,这时它占用的存储空间被回收。函数的参数和返回值也是局部变量,它们在其闭包函数被调用的时候创建。

例如,在1.4节中的lissajous示例程序中:

 

变量t在每次for循环的开始创建,变量x和y在循环的每次迭代中创建。

那么垃圾回收器如何知道一个变量是否应该被回收?说来话长,基本思路是每一个包级别的变量,以及每一个当前执行函数的局部变量,可以作为追溯该变量的路径的源头,通过指针和其他方式的引用可以找到变量。如果变量的路径不存在,那么变量变得不可访问,因此它不会影响任何其他的计算过程。

因为变量的生命周期是通过它是否可达来确定的,所以局部变量可在包含它的循环的一次迭代之外继续存活。即使包含它的循环已经返回,它的存在还可能延续。

编译器可以选择使用堆或栈上的空间来分配,令人惊奇的是,这个选择不是基于使用var或new关键字来声明变量。

 

这里,x一定使用堆空间,因为它在f函数返回以后还可以从global变量访问,尽管它被声明为一个局部变量。这种情况我们说x从f中逃逸。相反,当g函数返回时,变量*y变得不可访问,可回收。因为*y没有从g中逃逸,所以编译器可以安全地在栈上分配*y,即便使用new函数创建它。任何情况下,逃逸的概念使你不需要额外费心来写正确的代码,但要记住它在性能优化的时候是有好处的,因为每一次变量逃逸都需要一次额外的内存分配过程。

垃圾回收对于写出正确的程序有巨大的帮助,但是免不了考虑内存的负担。不需要显式分配和释放内存,但是变量的生命周期是写出高效程序所必需清楚的。例如,在长生命周期对象中保持短生命周期对象不必要的指针,特别是在全局变量中,会阻止垃圾回收器回收短生命周期的对象空间。

时间: 2025-01-24 03:29:13

Go程序设计语言2.3 变量的相关文章

Go程序设计语言导读

前 言 The Go Programming Language "Go是一种开源的程序设计语言,它意在使得人们能够方便地构建简单.可靠.高效的软件."(来自Go官网golang.org) Go在2007年9月形成构想,并于2009年11月发布,其发明人是Robert Griesemer.Rob Pike和Ken Thompson,这几位都任职于Google.该语言及其配套工具集使得编译和执行既富有表达力又高效,而且使得程序员能够轻松写出可靠.健壮的程序. Go和C从表面上看起来相似,而

《C语言程序设计》一 1.1 程序和程序设计语言

1.1 程序和程序设计语言 1.1.1 程序与程序设计 程序并不是计算机程序设计中独有的概念,在日常生活中我们也常见到这个词,例如一个会议的日程.一场演出的节目单等,这些程序都是由人的一项项的活动组成的,身处其中时通常需要按部就班地一步步完成一系列动作,有序地完成每一项活动也就实现了程序的目标.可以说,对这种活动过程细节动作的描述就是一个"程序". 日常生活中的程序性活动与计算机里的程序执行类似,这一点有助于我们理解计算机的工作方式.日常生活中的程序性活动里有更多变数,许多事情并不是完

【软考教程】程序设计语言基础

继续软考教程的学习,在第1章,我们主要是对计算机的组成和工作原理做了一定了解,可以说,在他的带领下,我们对计算机硬件方面的知识,有了更加坚实的基础. 第2章,程序设计语言基础知识.不看内容,光从标题来看,并不会觉得陌生."程序设计语言",至今,我们已经接触过不止一种了,从最初的VB.到后来的VB.NET.C#.C++,软考前个阶段,我们还见到了早有耳闻的Java世界. 那么,下面就跟随着"设计语言"的脚步,一起发现编程路上美丽的风光吧. 想要对程序设计语言有个宏观上

Go语言中的变量声明和赋值的方法

  Go语言中的变量声明和赋值的方法           这篇文章主要介绍了Go语言中的变量声明和赋值的方法,十分的细致全面,有需要的小伙伴可以参考下. 1.变量声明和赋值语法 Go语言中的变量声明使用关键字var,例如 代码如下: var name string //声明变量 name = "tom" //给变量赋值 这边var是定义变量的关键字,name是变量名称,string是变量类型,=是赋值符号,tom是值.上面的程序分两步,第一步声明变量,第二步给变量赋值.也可以将两步合到

struct-C语言结构体变量指针问题,求助

问题描述 C语言结构体变量指针问题,求助 #include #include #include #define N 10 typedef struct { char name[10]; double price; struct { int year;int month;int day ;}date; }STREC; int fun(STREC a,double p) { int i,j=0; double q; for(i=0; i < N ; i++,a++) { q=a->price; i

最受欢迎的十大程序设计语言排行榜更新(10月版)

在最受欢迎的程序设计语言排行榜上最值得关注的是Ruby从一年前的第十三名上升到第十位.无论如何,按照TIOBE程序设计区域指标的数据,这是个神奇的事情.TIOBE程序设计区域指标用于标识程序设计语言的普及度,并根据世界范围的熟练工程师.课程和第三方供应商对程序设计语言的实际使用率,每月更新一次数据. 流行的搜索引擎Google. MSN和Yahoo!都被用于计算此指标的评估结果,根据TIOBE所言.TIOBE在它的网站上发出如下告诫: 注意:TIOBE指标不是为了衡量哪个程设计语言是最好的或者是

c语言编程-这段C程序设计语言书上的代码,运行后按回车只换行并没有输出最长的行,为什么

问题描述 这段C程序设计语言书上的代码,运行后按回车只换行并没有输出最长的行,为什么 #include#define MAXLINE 1000int getline(char line[]int maxline);void copy(char to[]char from[]);main(){int len;int max;char line[MAXLINE];char longest[MAXLINE];max = 0;while ((len = getline(lineMAXLINE))>0)i

《程序分析方法》——1.2 程序设计语言的发展趋势

1.2 程序设计语言的发展趋势 Turbo Pascal编译器的主要编写者以及.NET框架.Delphi和C#之父--Anders Hejlsberg认为,相对于近几十年来计算机硬件的飞速发展,程序语言的改进不是很明显,主要的原因在于人们更关注"工具"."框架"或"开发方法"的变革和创新,而忽略了语言的改进.程序设计离不开程序设计语言,程序设计语言与"工具"."框架"或"开发方法"等一样

c语言-C语言如何使变量不清零

问题描述 C语言如何使变量不清零 C语言如何使变量不清零C语言如何使变量不清零C语言如何使变量不清零 解决方案 使用静态变量,除非人为改动,系统不会动的 解决方案二: 是否清零,是你自己控制的啊 解决方案三: 你不清就好了,电脑不会自动清零的 解决方案四: 简单一点就使用static静态变量.如果你是在所有函数外面设置的,那就是全局静态变量,在每一个函数里面都可以调用.如果你是在某一个函数里面 设置的,那就是局部静态变量,你可以把该变量的地址传到想要使用的函数里面,这样虽然变量名时在创建该变量的

Python v3.2发布 面向对象直译式计算机程序设计语言

Python是一种面向对象.直译式计算机程序设计语言,也是一种http://www.aliyun.com/zixun/aggregation/17547.html">功能强大而完善的通用型语言,已经具有十多年的发展历史,成熟且稳定.这种语言具有非常简捷而清晰的语法特点,适合完成各种高层任务,几乎可以在所有的操作系统中运行.目前,基于这种语言的相关技术正在飞速的发展,用户数量急剧扩大,相关的资源非常多. 虽然Python可能被粗略地分类为"脚本语言"(script lan