「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
常量
Pob Pike 2014年8月24日
介绍
Go是一门静态语言,它不允许不同数字类型间的操作。你不能将一个浮点数(float64)和一个整数(int)相加,也不能将一个32位整数(int32)和一个通用整数(int)相加。这些写法也是非法的:1e6*time.Second、math.Expr(1)、1<<(‘\t’+2.0)。在Go中,常量和变量不同,它很像是常数(regular number)。这篇文章将解释其中缘由。
背景:C
在之前关于Go的思考中,我们讨论过C语言和C延伸出的语言允许你混合不同数字类型会引发很多问题。许多匪夷所思的bug,异常中断和兼容性问题都是由于表达式里由不同长度的整型和符号类型(signedness)组成所导致的。即便是对经验丰富的C语言工程师,对于下面代码的计算结果也会迟疑。
1 | ini复制代码unsigned int u = 1e9; |
最后的答案是多少?它是什么类型的?有符号还是无符号的?
代码里还潜伏这bug。
C语言有一系列“通用算数转换”规则,it is an indicator of their subtlety that they have changed over the years (introducing yet more bugs, retroactively).
当设计Go时,我们决定避免这个雷区,我们不允许不同数字类型之间的混合操作。如果你想将i和u相加,你必须显示声明你最终想要得到的类型。
1 | csharp复制代码var u uint |
你可以写 uint(i)+u 或者 i+int(u),这样清楚地表达了相加操作的结果类型,你不能像C语言那样写 i+u。即使int是32bit位的,你也不能将int类型数字和int32类型数字混合操作。
这个强制要求消除类一些通常的bug和异常,他是Go中重要的属性。但它也需要代价:有时需要程序员去用笨拙的数字类型装换去装饰代码以清楚地表达出含义。
那常量怎么办?在上面的声明里,怎么合法地写 i=0 或者 u=0?0的类型是什么?
1 | csharp复制代码var i int = int(0) |
这种声明方式显然是不合理的。
我们很快意识到答案就是让数字常量的工作方式和其他C类语言不同。在更多的思考和实验之后,我们想出了我们相信最正确的设计,将程序员从总是要转换常量中解放出来,他们可以这样写:math.Sqrt(2),编译器也不会报错。
总之,在Go中,常量总是行得通的。让我们看看发生了什么。
术语
首先在Go中,const是一个关键字,用一个标量(比如:2、3.14159、”scrumptious”)来声明一个名字。这样的值(命名或其他形式)在Go中称为常量。常量也可通过由常量构建的表达式创建,比如:2+3、2+3i、Pi/2、(“go” + “pher”)。
某些语言没有常量,而另一些语言则具有常量的更一般定义或const单词的应用。例如,在C和C++中,const是一个类型限定符,可以将更多复杂值的更复杂属性进行编码。
但是在Go中,常量仅仅是单一不可变的值,现在开始我们只讨论Go中的常量。
字符串
这里有很多种类型的数字常量:整型、浮点型、rune、有符号型、无符号型、虚数型、复数型。让我们以简单的字符串常量作为开始。字符串常量很容易理解,可以在其中探索Go中常量的类型问题。
一个字符串常量是双引号闭合的文本(Go也支持原生字符串写法,即用反引号闭合1
2
arduino复制代码”Hello, 世界”
1 |
|
ini复制代码const hello = “Hello, 世界”
1 |
|
c复制代码const typedHello string = “Hello, 世界”
1 |
|
csharp复制代码var s string
s = typeHello
fmt.Println(s)
1 |
|
go复制代码type MyString string
var m Mystring
m = typedHello // Type error
fmt.Println(m)
1 |
|
ini复制代码const myStringHello MyString = “Hello, 世界”
m = myStringHello // OK
fmt.Println(m)
1 |
|
scss复制代码m = MyString(typedHello)
fmt.Println(m)
1 |
|
ini复制代码m = “Hello, 世界”
1 |
|
ini复制代码m = hello
1 |
|
go复制代码str := “Hello, 世界”
1 |
|
go复制代码str := “Hello, 世界”
1 |
|
ini复制代码var str = “Hello, 世界”
1 |
|
csharp复制代码var str string = “Hello, 世界”
1 |
|
perl复制代码fmt.Printf(“%s”, “Hello, 世界”)
1 |
|
复制代码Hello, 世界
1 |
|
go复制代码func Printf(format string, a …interface{}) (n int, err error)
1 |
|
perl复制代码fmt.Printf(“%T: %v\n”, “Hello, 世界”, “Hello, 世界”)
fmt.Printf(“%T: %v\n”, hello, hello)
1 |
|
c复制代码string: Hello, 世界
string: Hello, 世界
1 |
|
perl复制代码fmt.Printf(“%T: %v\n”, myStringHello, myStringHello)
1 |
|
css复制代码main.MyString: Hello, 世界
1 |
|
perl复制代码fmt.Printf(“%T %v\n”, 0, 0)
fmt.Printf(“%T %v\n”, 0.0, 0.0)
fmt.Printf(“%T %v\n”, ‘x’, ‘x’)
fmt.Printf(“%T %v\n”, 0i, 0i)
1 |
|
go复制代码int 0
float64 0
int32 120
complex128 (0+0i)
1 |
|
ini复制代码type MyBool bool
const True = true
const TypedTrue bool = true
var m Mybool
mb = true // OK
mb = True // OK
mb = TypedTrue // Bad
fmt.Println(mb)
1 |
|
go复制代码type MyFloat64 float64
const Zero = 0.0 // 无类型浮点常量
const TypedZero float64 = 0.0
var mf MyFloat64
mf = 0.0 // OK
mf = Zero // OK
mf = TypedZero // Bad
fmt.Println(mf)
1 |
|
rust复制代码var f32 float32
f32 = 0.0
f32 = Zero // OK: Zero is untyped
f32 = TypedZero // Bad: TypedZero is float64 not float32.
fmt.Println(f32)
1 |
|
ini复制代码const Huge = 1e1000
1 |
|
scss复制代码fmt.Println(Huge)
1 |
|
scss复制代码fmt.Println(Huge / 1e999)
1 |
|
复制代码10
1 |
|
ini复制代码Pi = 3.14159265358979323846264338327950288419716939937510582097494459
1 |
|
lua复制代码pi := math.Pi
fmt.Println(pi)
1 |
|
复制代码3.141592653589793
1 |
|
go复制代码type MyComplex128 complex128
const I = (0.0 + 1.0i)
const TypedI complex128 = (0.0 + 1.0i)
var mc MyComplex128
mc = (0.0 + 1.0) // OK
mc = I // Ok
mc = TypedI // Bad
fmt.Println(mc)
1 |
|
go复制代码const Two = 2.0 + 0i
1 |
|
perl复制代码s := Two
fmt.Printf(“%T: %v\n”, s, s)
1 |
|
go复制代码complex128: (2+0i)
1 |
|
go复制代码var f float64
var g float64 = Two
f = Two
fmt.Printf(f, “and”, g)
1 |
|
复制代码2 and 2
1 |
|
ini复制代码type MyInt int
const Three = 3
const TypedThree int = 3
var mi Myint
mi = 3 // OK
mi = Three // OK
mi = TypedThree // Bad
fmt.Println(mi)
1 |
|
go复制代码int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64
uintptr
1 |
|
go复制代码var u uint = 17
var u = uint(17)
u := uint(17)
1 |
|
go复制代码var i8 int8 = 128 // Error: too large.
1 |
|
go复制代码var u8 uint8 = -1 // Error: negative value.
1 |
|
go复制代码type Char byte
var c Char = ‘世’ // Error: ‘世’ has value 0x4e16, too large
1 |
|
ini复制代码const MaxUint32 = 1<<32 - 1
1 |
|
csharp复制代码const MaxUint = ^uint(0)
fmt.Printf(“%x\n”, MaxUint)
1 |
|
go复制代码1
1.000
1e3-99.0*10-9
‘\x01’
‘\u0001’
‘b’ - ‘a’
1.0+3i-3.0i
1 |
|
go复制代码var f float32 = 1
var i int = 1.000
var u uint32 = 1e3 - 99.0*10.0 - 9
var c float64 = ‘\x01’
var p uintptr = ‘\u0001’
var r complex64 = ‘b’ - ‘a’
var b byte = 1.0 + 3i - 3.0i
fmt.Println(f, i, u, c, p, r, b)
1 |
|
go复制代码1 1 1 1 1 (1+0i) 1
1 |
|
ini复制代码var f = ‘a’ * 1.5
fmt.Println(f)
1 |
|
复制代码145.5
1 |
|
css复制代码sqrt2 := math.Sqrt(2)
1 |
|
ini复制代码const millisecond = time.Second/1e3
1 |
|
go复制代码bigBufferWithHeader := make([]byte, 512+1e6
结果也是你期望的那样。
因为在Go里,数字常量的工作方式就像普通的数字一样。
**本文转载自:** [掘金](https://juejin.cn/post/7026538621301686309)
*[开发者博客 – 和开发相关的 这里全都有](https://dev.newban.cn/)*