每天一点Go语言——变量、常量、运算符解析
前言
前面简单说过有关Go语言的基础语法及其数据类型,大致上了解了Go语言程序的组成,而本文将讲述近乎所有编程语言都涉及的变量、常量以及运算符相关内容。
一、变量
何为变量?
对该专业名词解释:计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。Go语言中的变量由字母、数字、下划线组成,首字母不可以为数字。耳熟能详了哈~
笔者的理解:我编写程序的时候需要使用一个值,并且我一直想要重复使用,而且有时候这个值会发生变化等等,所以我需要声明一个变量来被这个值赋之。
怎么声明变量?
变量声明一般格式:
var 变量名 变量类型
var:变量声明关键字,表示声明变量
其次是变量的名字,最后是变量的类型(如int)
当然也有其他的变化格式,我们直接看实例:
实例1.1——变量格式写法1
package main
var a1 int = 10
var a2 = "hello world"
var b1 = 11
var b2 string = "你好,世界"
//c1 := 12 //这种不带声明格式的只能在函数体中出现,否则会报错:non-declaration statement outside function body
var c2 bool
func main(){
println(a1,a2,b1,b2,c2)
c1 := 12 //这种变量声明要注意:c1变量没有被提前声明过才可以这样赋值
println(c1)
}
运行结果:
10 hello world 11 你好,世界 false
12
当然变量也可以一起声明,程序执行时会自动推断变量类型(与Python相像)
实例1.2——变量格式写法2
package main
//写法一:
var v1, v2, v3 int
//写法二:
var v11, v22, v33 = 4, 5, "hello"
//写法三:(需写在全局变量中,不可以写在函数体内部)
var (
vv1 int
vv2 bool = true
)
func main(){
v1, v2, v3 = 1, 2, 3
println(v1,v2,v3)
println(v11,v22,v33)
println(vv1,vv2)
}
运行结果:
1 2 3
4 5 hello
0 true
补充说明:
在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。 编译器对此会自动推断a为int类型,b为bool类型。
这种简短形式的变量声明需要注意以下事项:
- 只能被用在函数体内部;
- 相同的代码块中,不可以如此重复声明变量;
另外,变量需要先声明后使用(所有方式都一样);当声明了局部变量(函数体内部),而未使用,也会编译出错;但是全局变量可以不使用。
值类型与引用类型
值类型
int、float、bool、string这些基本类型都属于值类型。值类型的典型特征就是:我们所赋值的操作在计算机中存储和使用的过程是这样的(先做了解即可):
定义变量到内存中,值类型的变量存储在栈中,赋值,是将该值进行了一个拷贝再将这个拷贝的内容赋予给变量,变量(值类型)与拷贝的值之间的关系:变量直接指向内存中的值。
可以通过&变量名获取变量的内存地址,每次获取的地址可能不一样。
实例1.3——值类型变量内存地址
package main
var a int = 10
func main(){
println(a)
println(&a)
}
运行结果:
10
0x4a4018
引用类型(先作了解)
Go语言中的引用类型有:映射(map)、数组切片(slice)、通道(channel)、方法和函数。
Go语言存在垃圾回收器,因此在一个本地变量不再被使用时就会被回收掉,此时本地变量的生命周期由它们的作用域决定。对于管理本地变量的生命周期,我们需要用到指针,使用指针就可以使得对应变量的生命周期独立与作用域。
使用指针能控制变量的生命周期,不受作用域的影响,另外变量在传递过程中使成本最小化,并且可以修改变量的内容,而不是对复制的值进行操作。当然,指针也是一个变量,表示的是存放(保存了)另一个变量的内存地址,任何被指针保存的内存地址的变量都可以通过指针来修改内容。
&和*理解:
- 操作符& : 当作二元操作符时,是按位与操作;当作一元操作符时,是返回该变量的内存地址
- 操作符* : 当作二元操作符时,是相乘的操作;当作一元操作符(解引用操作符)时,是返回该指针指向的变量的值,其实就是解除变量的指针引用,返回该变量的值。
实例1.4——指针的创建与使用
package main
import "fmt"
func main(){
a := 3
p := &a //获取变量a的内存地址,并且将其赋值给p
fmt.Printf("a的值为: %v,a的指针为: %v,p指向的变量的值为: %v\n",a,p,*p)
}
运行结果:
~~~a的值为: 3,a的指针为: 0xc4200160d8,p指向的变量的值为: 3
也就是说*p和a的值是相等的,可以交换使用。两者都与同一块内存地址相关联,任意一个变量进行修改操作都会影响到另一个变量的值,但是若变量p被赋值其他变量的指针就不行了
### 空指针
当一个指针被定义后没有分配到任何变量,值就为nil,叫做空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
#### 实例1.5——空指针
~~~go
package main
import "fmt"
func main() {
var ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr )
}
~~~运行结果:ptr 的值为 : 0
# 二、常量
Go语言常量就是值的标识符,程序运行时,不会被修改。常量中的数据类型只可以是bool型、数字型和字符串型。
常量定义格式:
~~~go
const identifier [type] = value
解释:const ——关键字 ,表示该变量将定义为常量不可更改
实例2.1——常量定义与使用
package main
import "fmt"
const m string = "hello" //显式类型定义
const n = "world" //隐式类型定义
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" //多重赋值
area = LENGTH * WIDTH
fmt.Printf("面积为 : %d\n", area)
println(m, n)
println(a, b, c)
//m = "wrong" //错误示范
//println(m) //编译报错:cannot assign to m,说明const变量不可以更改
}
运行结果:
面积为 : 50
hello world
1 false str
当然,常量也可用作枚举
const (
Yes = 0
No = 1
Notsure = 2
)
iota
iota,一个特殊常量,可以认为是一个能够被编译器修改的常量
实例2.2——iota
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
运行结果
~~~0 1 2 ha ha 100 100 7 8
从给出结果的现象可以知道:
1、在go语言编程中,iota是一个会在程序运行过程中自动变换的常量;
2、在给const修饰的变量赋值时,每次会隐藏性自增1,无论是不是在赋予给定的值;
3、当赋予给定的值给变量后,该变量之后的变量又未赋予其他的给定的值时,其后面的变量的值和该变量相等;就如上面的d和e,f和g ;但是有例外(看下面的例子)
4、而当变量回归赋值iota时,将按照自增的结果赋值给变量。
如下面的例子:
#### 实例2.3——有趣的iota
~~~go
package main
import "fmt"
const (
i=1<<iota //"<<"——此符号表示的位运算符,左移;此时对应关系为i——>1<<iota:1左移0位;则i的值为1
j=3<<iota //此时对应关系为:j——>3<<iota为3,iota的值为1,则左移1位,即0011——>0110=6
k //但是此时关系为k——>3<<iota,而不是6<<2,iota为2,k的值为12
l
m=iota
)
func main() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
fmt.Println("m=",m)
}
运行结果:
i= 1
j= 6
k= 12
l= 24
m= 4
三、运算符
近乎所有的程序语言都有运算符,运算符的作用无外乎两个:数学运算和逻辑运算
Go语言内置的运算符有:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、其他运算符
1、算数运算符
包括:+、-、*、/、%、++、-- 后三个表示取余,自增,自减
实例3.1——算法运算符演示
package main
import "fmt"
func main() {
var a int = 21
var b int = 10
var c int
c = a + b
fmt.Printf("加法: c 的值为 %d\n", c )
c = a - b
fmt.Printf("减法: c 的值为 %d\n", c )
c = a * b
fmt.Printf("乘法: c 的值为 %d\n", c )
c = a / b
fmt.Printf("除法: c 的值为 %d\n", c )
c = a % b
fmt.Printf("取余: c 的值为 %d\n", c )
a++
fmt.Printf("a自增: 值为 %d\n", a )
a--
fmt.Printf("a自减: 值为 %d\n", a )
}
运行结果:
加法: c 的值为 31
减法: c 的值为 11
乘法: c 的值为 210
除法: c 的值为 2
取余: c 的值为 1
a自增: 值为 22
a自减: 值为 21
2、关系运算符
包括 == 、!=、>、<、 >=、 <= 分别对应:判断是否等于,是否不等于,大于,小于,大于等于,小于等于
本文对此不做代码演示了,因为涉及到控制流程语句下一篇会讲
3、逻辑运算符
下图表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
4、位运算符
位运算符的作用是对整数在内存中的二进制位进行操作。
下表列出了位运算符 &, |, 和 ^ 的计算:表示逻辑上的“与或非”的关系
总结:
&:两边为真才为真
|:两边为假才为假
^:两边不同才为真
当然还有上述的左移、右移计算。举例说明:
60&13的结果为:12,计算方法是60和13转换为二进制做按位与运算,即:0011 1100 & 0000 1101= 0000 1100
60|13的结果为:61,计算方法是60和13转换为二进制做按位或运算,即:0011 1100 | 0000 1101= 0011 1101
5、赋值运算符
赋值运算符如下:
6、其他运算符
主要就是上面所提到的&和*,分别表示为返回变量存储地址和指针变量
运算符优先级
有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:(推荐编程的时候使用括号提升优先级,也是为了增强代码的可读性)
四、总结
本文讲述了go语言中有关变量、常量、运算符的内容。变量中注意定义变量的格式与位置,尤其是简明格式,其次是常量,需要留意一个特殊常量iota。最后则是运算符,需要在实际编程的案例中使用才能体会感悟。
有疑问加站长微信联系(非本文作者)