# 常量

#### 什么是字面量

考虑下面代码：

```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
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sunwenfei.gitbook.io/sunwenfei/golang/golang-ji-chu-jiao-cheng/bian-liang-leixing-chang-liang/chang-liang.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
