> For the complete documentation index, see [llms.txt](https://sunwenfei.gitbook.io/sunwenfei/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sunwenfei.gitbook.io/sunwenfei/golang/golang-ji-chu-jiao-cheng/bian-liang-leixing-chang-liang/chang-liang.md).

# 常量

#### 什么是字面量

考虑下面代码：

```go
var a int = 50
var b string = "I love Go"
var c string = b
```

这里变量`a`赋值为字面量`50`，变量`b`赋值为字面量`I love Go`，而变量`c`则赋值为变量`b`。像`5`, `-89`，`I love Go`，`67.89`等，在编程语言中称为字面量。

#### 什么是常量

常量是相对于变量而言的，考虑下面代码：

```go
var a int = 50
const b int = 50
```

这里两个量`a`和`b`虽然都是声明为`int`类型，并且赋值为50，但是`a`是变量，`b`是常量。那么常量与变量有什么区别呢？答案是常量不可重新被赋值。

```go
package main

func main() {
    const a = 55
    a = 89 // 给常量重新赋值会报错
}
```

[Go Playground在线运行](https://play.golang.org/p/ixSnZmc41lW) 这里由于`a`被声明为了`const`常量，因此后面再进行赋值会报错。

常量跟变量的另外一个不同点是，常量声明的同时需要赋值，因此下面的写法会报错：

```go
package main

func main() {
    const int a // 先声明
    a = 50 // 再赋值
}
```

[Go Playground在线运行](https://play.golang.org/p/jdAeHz-GvqX)

另外需要注意，常量值需要在编译时确定，因此不能将函数调用的返回值赋值给常量。

```go
package main

import (  
    "fmt"
    "math"
)

func main() {  
    fmt.Println("Hello, playground")
    var a = math.Sqrt(4)    // allowed
    const b = math.Sqrt(4)  // not allowed
}
```

[Go Playground在线运行](https://play.golang.org/p/kb1cGio7mRI)

该程序中，`a`是变量，因此可以赋值为函数调用返回值；但是`b`是常量，而函数`math.Sqrt(4)`的执行是运行时，因此会报错。

#### 无类型常量和有类型常量

Golang中每个变量都有对应的类型，比如：

```go
var a int = 10
var b = 20
c := 30
```

这三种不同的写法，声明的变量`a`、`b`、`c`均为`int`类型，虽然有显示声明的，有Golang自动推断的，有简写形式。

Golang中的常量与变量不同，常量包括两种：untyped常量(无类型常量)和typed常量(有类型常量)。为了方便理解，这里以香水为例来说明下。我们都知道，兰蔻(Lancome)和欧碧泉(Biotherm)都属于欧莱雅集团，假如他们公司新发明了一款香水：`邂逅`。

```go
package main

import "fmt"

type Lancome string
type Biotherm string


func main() {
    const perfume = "邂逅" // 无类型常量
    const perfume1 Lancome  = "邂逅" // 有类型常量，类型是兰蔻
    const perfume2 Biotherm = "邂逅" // 有类型常量，类型是欧碧泉
    var perfume3 Lancome = perfume // 合法
    perfume3 = perfume1 // 合法
    perfume3 = perfume2 // 不合法
    fmt.Println(perfume, perfume1, perfume2, perfume3)
}
```

[Go Playground在线运行](https://play.golang.org/p/VhPgxHZ4Zlk) 这段程序中，首先声明的常量`perfume`就是无类型常量，而后面声明的`perfume1`和`perfume2`则均是有类型常量，其常量类型分别是`Lancome`和`Biotherm`。虽然都是常量，但是用法却不同，后面声明的变量`perfume3`是`Lancome`类型，因此只能赋值为同类型常量`perfume1`或者无类型常量`perfume`。

为了加深理解我们继续深挖香水这个例子。虽然`邂逅`这款香水是欧莱雅集团出品的，但是在贴标之前是没有品牌属性的，即`const perfume = "邂逅"`，这行代码我们 并未给`perfume`常量贴标，因此不属于任何品牌，属于无类型常量。但是一旦贴标，比如`const perfume2 Biotherm = "邂逅"`，这行代码我们给`perfume2`常量贴标，贴上Biotherm品牌标志，即变为有类型常量。有类型常量是贴标产品，因此不能随便再撕下标签，贴上其他品牌的标志，比如`perfume3 = perfume2`，因此这行代码是不合法的。

初看这个特性难以理解，但是这样使得Golang的常量更加灵活，我们首先声明无类型的常量，后面用起来就会更加灵活，只要类型兼容，想赋值给什么变量就赋值给什么变量。这里再解释下这里所说的类型兼容，比如`const str = "邂逅"`中无类型常量`str`本质是一个字符串，与类型`string`、`Lancome`均兼容，这里的类型兼容用来描述无类型常量与其他类型的兼容性：

```go
type Lancome string

const str = "邂逅"
var str1 string = str
var perfume1 Lancome = str
```

#### 字符串常量

字符串常量声明如下：

```go
const hello = "Hello World"
```

参考前面对无类型常量和有类型常量的解释，这里声明了一个无类型字符串常量`hello`。 看下下面的代码你可能会疑惑，为什么说`hello`是无类型的呢，明明打印出了`string`。

```go
package main

import (  
    "fmt"
)

func main() {  
    const name = "Sam"
    fmt.Printf("type %T value %v", name, name)

}
```

[Go Playground在线运行](https://play.golang.org/p/rkb8ZENnp9S)

运行结果如下：

```
type string value Sam
```

这里解释下。前面之所以打印出了`string`，并不是说常量`name`是Golang的`string`类型。本质原因是因为这里`name`是作为了`fmt.Printf`函数的参数，而该函数这里期望接收`string`类型，又因为常量"Sam"本身是一个字符串，与`string`类型兼容，因此才打印出了`string`。

#### 布尔常量

布尔常量没什么特殊的，很简答，这里仅举个例子：

```go
package main

func main() {  
    const trueConst = true
    type myBool bool
    var defaultBool = trueConst //allowed
    var customBool myBool = trueConst //allowed
    defaultBool = customBool //not allowed
}
```

[Go Playground在线运行](https://play.golang.org/p/o9GluiH75vy) 这里由于变量`defaultBool`是布尔类型，因此可被赋值无类型常量`tureConst`，但是不能被赋值为类型不相同的有类型常量`customBool`。

#### 数值常量

数值常量包括整型、浮点型以及复数型。

```go
package main

import (  
    "fmt"
)

func main() {  
    const a = 5
    var intVar int = a
    var int32Var int32 = a
    var float64Var float64 = a
    var complex64Var complex64 = a
    fmt.Println("intVar",intVar, "\nint32Var", int32Var, "\nfloat64Var", float64Var, "\ncomplex64Var",complex64Var)
}
```

[Go Playground在线运行](https://play.golang.org/p/a8sxVNdU8M) 这里常量`a`是无类型的，值为`5`，由于是无类型的，因此可以赋值为多种兼容的类型，执行结果如下：

```
intVar 5 
int32Var 5 
float64Var 5 
complex64Var (5+0i)
```

从下面的程序我们可以看出整型变量、浮点型变量以及复数型变量被赋值常量时的默认类型分别是`int`，`float64`，`complex128`：

```go
package main

import (  
    "fmt"
)

func main() {  
    var i = 5
    var f = 5.6
    var c = 5 + 6i
    fmt.Printf("i's type %T, f's type %T, c's type %T", i, f, c)

}
```

[Go Playground在线运行](https://play.golang.org/p/YX8dkgASNvj) 执行结果如下：

```
i's type int, f's type float64, c's type complex128
```

#### 数值表达式

数值型常量声明时也不可以使用函数调用返回值来初始化，但是可以使用数值表达式来初始化，比如：

```go
package main

import (
    "fmt"
)

func main() {
    const a float64 = 5.9 / 5.9
    fmt.Printf("a's type %T value %v", a, a)
}
```

[Go Playground在线运行](https://play.golang.org/p/lS5MTJw1F3T)

执行结果如下：

```
a's type float64 value 1
```
