每天一点Go语言——变量、常量、运算符解析

每天一点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。

每天一点Go语言——变量、常量、运算符解析

4、位运算符

​ 位运算符的作用是对整数在内存中的二进制位进行操作。

下表列出了位运算符 &, |, 和 ^ 的计算:表示逻辑上的“与或非”的关系

每天一点Go语言——变量、常量、运算符解析

总结:

&:两边为真才为真

|:两边为假才为假

^:两边不同才为真

当然还有上述的左移、右移计算。举例说明:

60&13的结果为:12,计算方法是60和13转换为二进制做按位与运算,即:0011 1100 & 0000 1101= 0000 1100

60|13的结果为:61,计算方法是60和13转换为二进制做按位或运算,即:0011 1100 | 0000 1101= 0011 1101

5、赋值运算符

赋值运算符如下:

每天一点Go语言——变量、常量、运算符解析

6、其他运算符

主要就是上面所提到的&和*,分别表示为返回变量存储地址和指针变量

运算符优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:(推荐编程的时候使用括号提升优先级,也是为了增强代码的可读性)

每天一点Go语言——变量、常量、运算符解析

四、总结

​ 本文讲述了go语言中有关变量、常量、运算符的内容。变量中注意定义变量的格式与位置,尤其是简明格式,其次是常量,需要留意一个特殊常量iota。最后则是运算符,需要在实际编程的案例中使用才能体会感悟。

猜你喜欢

转载自blog.51cto.com/14557673/2484879