变量指的是值可以被改的量,常量指的则是值不可以被改变的量,在Go语言中,常量的值在编译期间就已经确定好了,且在程序运行过程中不可改变。
常量的声明语句如下,定义了常量的名字,和变量的声明语法类似
| const 名字 类型 = 初始化表达式 |
| |
| |
| const x int = 10 |
| x = 111 |
虽然常量和变量很相似,但毕竟是两种东西,所以不要硬把变量的东西往常量上套,需要注意的是
字面量(literal)也成为字面值or字面常量,就是”字面“意思上的量、是指程序中”硬编码“的量,通俗地讲,字面量就是以人类可读形式表示的固定值,有以下几种
注意注意注意!!!
”字面“与”硬编码“都是在提醒你,它只是它字面的样子,不要自己意淫它的类型,例如111这个字面量就是一系列阿拉伯数字,你可能会说它难道不是int类型吗,当然不是喽,int类型是编程语言中才有的概念,而阿拉伯数字等人类自然语言的符号早在编程语言诞生之前就已经有了,即便没有编程语言111、3.1,“hello”这些值也都是存在的,比如,你随便找个人让他看看一眼111他都认识,但你跟他讲int类型,除非他是程序员他才能听懂你在讲什么。也就是说,是编程语言将字面量111与数据类型int这两种概念联系到了一起。
综上所述,字面量与数据类型是两件事情,而在有的语言中,却会直接将字面量与特定的类型混杂在一起,这么做是不好的
| 比如111这一字面量在C语言中会认为是一个int类型,一个long类型的字面量需要写成111l,这其实是与”字面“二字的核心主旨相悖的,”字面“反映出的就应该只是它字面的东西,不应该掺杂类型的概念,因为类型这个东西是另外一件单独的事情,比如111l,这个后缀l就掺杂进了类型的概念,那么此时它的字面便不那么字面,没有那么的单纯。 |
所以,我们应该将字面值与类型当成两种概念去看,而站在巨人肩膀上的go语言,自然是作出了对应的调整,字面量只是它字面的样子,至于数据类型,那是另外一件事。
一句话:Go语言的字面常量更接近我们自然语言中的常量概念,它们本身是无类型的,在Go语言中,只有下述五种字面量可以赋值给常量
| 111这种字面量可以被称之为无类型的整数 |
| 3.1这种字面量被称之为无类型的浮点数 |
| 3.2+12i这种字面量被称之为无类型的复数 |
| true这种字面量被称之为无类型的布尔型 |
| ”egon“这种字面量被称之为无类型的字符 |
| |
| |
字面量可以用于变量中,也可以用于常量中,是不一样的
-
1、变量是必须有类型的
| =====> 变量是必须有类型的,字面量必须与变量的类型保持一致 <===== |
| (1) 如果我们没有为变量指定类型,那么字面常量会被隐式地转换成自己默认的类型 |
| |
| 字面量 默认类型 |
| 字面量为整数 ---------> int类型 |
| 字面量为浮点数 ---------> float64类型 |
| 字面量为复数 ---------> complex128类型 |
| 字面量为true或false ---------> bool类型 |
| 字面量为"egon" ---------> string类型 |
| |
| 例如 |
| var x = 1+2i |
| fmt.Printf("%T",x) |
| |
| (2) 如果我们已经为变量指定了类型,那么字面常量会被显式地转换成变量的类型,前提是转换合法,即字面量必须在相应类型的值域范围内。 |
| |
| |
| var x int = 3 |
| |
| |
| var a int = 3.1 |
| var b float32 = 3.1 |
| var c float64 = 3.1 |
| var e complex64= 3.1 |
| |
| var x float32 = math.Pi |
| var y float64 = math.Pi |
| var z complex128 = math.Pi |
| |
| var f float64 = 3 + 0i |
| f = 2 |
| f = 1e123 |
| f = 'a' |
| 上述语句的转换都是隐式的,相当于下述的显式转换 |
| var f float64 = float64(3 + 0i) |
| f = float64(2) |
| f = float64(1e123) |
| f = float64('a') |
| |
| 所以,我们可以显式地先将字面量转换成一种类型 |
| var x = int8(3) |
| var y = int64(3) |
| |
| 综上,如果要给一个变量明确的类型,我们可以有两种方式 |
| var i = int8(0) |
| var i int8 = 0 |
-
2、常量可以有类型、也可以没有类型(也只有常量可以没有类型)
| 1、字面量用于常量中,如果没有声明类型,那该常量就是无类型的,go语言中只有下述5种无类型常量 |
| const a = 111 |
| const b = 3.1 |
| const c = 3.2+12i |
| const d = true |
| const e = "egon" |
| |
| 编译器为没有明确类型的数字常量(整数、浮点数、复数)提供比基础类型更高精度的算术运算;你可以认为至少有256bit的运算精度。如下所示,ZiB和YiB的值已经超出任何Go语言中整数类型能表达的范围,但是它们依然是合法的常量 |
| const YiB = 1208925819614629174706176 |
| const ZiB = 1180591620717411303424 |
| 而且可以像下面常量表达式依然有效 |
| fmt.Println(YiB/ZiB) |
| |
| 2、也可以限定常量的类型,例如 |
| const f int = 123 |
| const g float32 = 0.0 |
| |
| 3、而且通过延迟明确常量的具体类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换,比如前面我们提及字面量在变量中的应用 |
-
3、无论是隐式或显式转换,将一种类型转换为另一种类型都要求目标可以表示原始值。对于浮点数和复数,可能会有舍入处理:
| const ( |
| deadbeef = 0xdeadbeef |
| a = uint32(deadbeef) |
| b = float32(deadbeef) |
| c = float64(deadbeef) |
| |
| |
| |
| ) |
常量定义的右值也可以是一个在编译期运算的常量表达式,比如
const mask = 1 << 3
由于常量的赋值是一个编译期行为,所以右值不能出现任何需要运行期才能得出结果的表达
式,比如试图以如下方式定义常量就会导致编译错误:
const Home = os.GetEnv(“HOME”)
原因很简单, os.GetEnv()只有在运行期才能知道返回结果,在编译期并不能确定,所以无法作为常量定义的右值。
和变量声明一样,可以批量声明多个常量;这比较适合声明一组相关的常量:
| const ( |
| e = 2.71828182845904523536028747135266249775724709369995957496696763 |
| pi = 3.14159265358979323846264338327950288419716939937510582097494459 |
| ) |
如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的。例如:
| const ( |
| a = 1 |
| b |
| c = 2 |
| d |
| ) |
| |
| fmt.Println(a, b, c, d) |
如果只是简单地复制右边的常量表达式,其实并没有太实用的价值。但是它可以带来其它的特性,那就是iota常量生成器语法。
Go语言预定义了这些常量: true、 false和iota。
iota比较特殊,可以被认为是一个可被编译器修改的常量,在且仅在同一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一
连续两个const关键字声明的iota都为0
| const x = iota |
| const y = iota |
| |
| fmt.Println(x,y) |
如果是一const关键字,声明了一组常量,iota会自第一个iota开始依次累加,没新增则一个变量累加一次
| const ( |
| a = 111 |
| b |
| c |
| d = iota |
| e = 333 |
| f |
| g |
| ) |
| fmt.Println(a,b,c,d,e,f,g) |
| |
| const ( |
| u = iota * 42 |
| v float64 = iota * 42 |
| w = iota * 42 |
| ) |
| |
| const ( |
| c0 = iota |
| c1 |
| c2 |
| ) |
| |
| const ( |
| a = 1 <<iota |
| b |
| c |
| ) |
我们可以定义一个Weekday命名类型,然后为一周的每天定义了一个常量,从周日0开始。在其它编程语言中,这种类型一般被称为枚举类型。
| type Weekday int |
| |
| const ( |
| Sunday Weekday = iota |
| Monday |
| Tuesday |
| Wednesday |
| Thursday |
| Friday |
| Saturday |
| ) |
| |
下面是一个更复杂的例子,每个常量都是1024的幂:
| const ( |
| _ = 1 << (10 * iota) |
| KiB |
| MiB |
| GiB |
| TiB |
| PiB |
| EiB |
| ZiB |
| YiB |
| ) |
不过iota常量生成规则也有其局限性。例如,它并不能用于产生1000的幂(KB、MB等),因为Go语言并没有计算幂的运算符。
所有常量的运算都可以在编译期完成,这样可以减少运行时的工作,也方便其他编译优化。当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除零、字符串索引越界、任何导致无效浮点数的操作等。
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof