Go 编程基础 常量与运算符 04

常量的定义

  • 常量的值在编译时就已经确定
  • 常量的定义格式与变量基本相同
  • 等号右侧必须是常量或者常量表达式
  • 常量表达式中的函数必须是内置函数

常量组

命名规范

const (
    //常量一般是大写,但是这样的话,这个常量就能被外部包所调用
    MAX_CLIENT = 40
    //如果你想既不被外部调用,又保持这种命名规范,推荐这种
    _MAX_CLIENT = 40
    //或者前面加个c
    cMAX_CLIENT = 40
)

常量组定义方法一

const a,b,c = 1,"a","c"

常量组定义方法二

const (
    d,e,f = 1,2,3
)

常量组的特性

  • 在定义常量组时,如果不提供初始值,则表示将使用上行的表达式
  • 使用相同的表达式不代表具有形同的值
  • iota是常量的计数器,从0开始,组中没定义1个常量自动递增1
  • 通过初始化规则与iota可以到达枚举的效果
  • 没遇到一个const关键字,iota就会重置为0

在定义常量组时,如果不提供初始值,则表示将使用上行的表达式

const (
    a = 1
    b
    c
)

func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
1
1
1

表达式和常量表达式的区别

var aa = "123"
const (
    a = len(aa)
    b
    c
)
func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
# command-line-arguments
./main.go:7:2: const initializer len(aa) is not a constant
./main.go:7:9: invalid argument aa (type int) for len
./main.go:8:2: invalid argument aa (type int) for len
./main.go:8:2: const initializer len(aa) is not a constant
./main.go:9:2: invalid argument aa (type int) for len
./main.go:9:2: const initializer len(aa) is not a constant

const initializer len(aa) is not a constant,这里说了len(aa)不是一个常量,在编译的时候,并没有对变量aa进行一个处理,所以说这个变量aa并不是一个存在的东西, 所以len(aa)并不是一个常量表达式,并不能够赋值给左边的值

同样在常量表达式中,也只能使用go内置的函数,比如: len

正确写法
const (
    a = "123"
    b = len(a)
    c
)
func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
123
3
3

使用常量组初始化规则,每一行常量个数必须一致

const (
    a , b = "123",456
    c
)
func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
# command-line-arguments
./main.go:7:2: extra expression in const declaration

这里第一行常量个数2个,如果要使用常量组初始化规则那么第二行的常量个数也必须2个

const (
    a, b = "123", 456
    c, d
)

func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}
123
456
123
456

这里c套用a的常量表达式,d套用b的常量表达式

常量组中的枚举(iota)

iota是常量的计数器,从0开始,组中没定义1个常量自动递增1

const (
    a = 1
    b 
    c = iota
    d = iota
)

func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}
1
1
2
3

当遇到const关键字时,iota置0

const (
    a = 1
    b
    c = iota
    d = iota
)
const(
    e = iota
)
func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
    fmt.Println(e)
}
1
1
2
3
0

Go语言中的运算符

Go中的运算符均是从左至右结合,优先级(从高到低)

  • ^ ! (一元运算符)
  • * / % << >> & &^
  • + - | ^ (二元运算符)
  • == != < <= >= >
  • <- (专门用于channel处理并发)
  • &&
  • ||

那么这里大家看到^分别出现在了一元运算符和二元运算符中,那么如何区别^是一元运算符还是二元运算符呢

^作为一元运算符和作为二元运算符

//^号只有一边有数字,这时候 ^ 作为一元运算符
var a = ^2
//^号两边都有数字,这时候 ^ 作为二元运算符
var b = 1 ^ 2
fmt.Println(a)
fmt.Println(b)
-3
3

<<>>

位运算符

位运算都是在二进制的基础上计算的

6:   0110
11: 1011
  • & 两个都是1的话才是1 否则就是0
  • | 如果有一个是1那么就是1
  • ^ 两位只有一个是1才是1
  • &^ 如果在第二个数的位上是1的话,那么你就把第一个数的这个位强制为0
/*
6:  0110
11: 1011
 */
// 0010
fmt.Println(6 & 11)
// 1111
fmt.Println(6 | 11)
// 1101
fmt.Println(6 ^ 11)
// 0100
fmt.Println(6 &^ 11)
2
15
13
4

&&的运用实例,当然||的用法也一样

func main() {
    a := 1
    if (10 / a) > 1 {
        fmt.Println("OK")
    }
}
OK

那如果我们把变量a赋值为0,就直接编译不过

panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.main()
    /Users/ailuoy/code/learning/go/main.go:7 +0x11

这时候&& 的作用就用上了,当前置条件都不成立,就不会触发第二个条件

func main() {
    a := 0
    if (a > 0) && (10/a) > 1 {
        fmt.Println("OK")
    }
}

这时候因为前置条件都为假了,所以根本不会执行后面的代码

作业

尝试结果常量的iota与<<运算符实现计算机的存储单位的枚举

const (
    _ = iota
    KB float64 = 1<<(iota * 10)
    MB
    GB
    TB
    PB
    EB
    ZB
    YB
)

func main() {
    fmt.Println(KB)
    fmt.Println(MB)
    fmt.Println(GB)
    fmt.Println(TB)
    fmt.Println(PB)
    fmt.Println(EB)
    fmt.Println(ZB)
    fmt.Println(YB)
}
1024
1.048576e+06
1.073741824e+09
1.099511627776e+12
1.125899906842624e+15
1.152921504606847e+18
1.1805916207174113e+21
1.2089258196146292e+24