上QQ阅读APP看书,第一时间看更新
9.1 Go常量溯源
先来回顾一下C语言。在C语言中,字面值(literal)担负着常量的角色(针对整型值,还可以使用枚举常量)。可以使用整型、浮点型、字符串型、字符型字面值来满足不同场合下对常量的需求:
0x12345678 10086 3.1415926 "Hello, Gopher" 'a'
为了不让这些魔数(magic number)充斥于源码各处,早期C语言的常用实践是使用宏(macro)定义记号来指代这些字面值:
#define MAX_LEN 0x12345678 #define CMCC_SERVICE_PHONE_NUMBER 10086 #define PI 3.1415926 #define WELCOME_TO_GO "Hello, Gopher" #define A_CHAR 'a'
这种定义“具名字面值”的实践也被称为宏定义常量。虽然后续的C标准中提供了const关键字来定义在程序运行过程中不可改变的变量(又称“只读变量”),但使用宏定义常量的习惯依然被沿袭下来,并且依旧是C编码中的主流风格。
宏定义的常量有着诸多不足,比如:
- 仅是预编译阶段进行替换的字面值,继承了宏替换的复杂性和易错性;
- 是类型不安全的;
- 无法在调试时通过宏名字输出常量的值。
而C语言中const修饰的标识符本质上还是变量,和其他变量一样,编译器不能像对待真正的常量那样对其进行代码优化,也无法将其作为数组声明时的初始长度。
Go语言是站在C语言等编程语言的肩膀之上诞生的,它原生提供常量定义的关键字const。Go语言中的const整合了C语言中宏定义常量、const只读变量和枚举常量三种形式,并消除了每种形式的不足,使得Go常量成为类型安全且对编译器优化友好的语法元素。Go中所有与常量有关的声明都通过const来进行,例如:
// $GOROOT/src/os/file.go const ( O_RDONLY int = syscall.O_RDONLY O_WRONLY int = syscall.O_WRONLY O_RDWR int = syscall.O_RDWR O_APPEND int = syscall.O_APPEND ... )
上面这段标准库中的代码通过const声明了一组常量,如果非要进一步细分,可以将这组常量视为枚举的整型常量。然而你可能没想到,上面对常量的声明方式仅仅是Go标准库中的少数个例,绝大多数情况下,Go常量在声明时并不显式指定类型,也就是说使用的是无类型常量(untyped constant)。比如:
// $GOROOT/src/io/io.go const ( SeekStart = 0 SeekCurrent = 1 SeekEnd = 2 )
无类型常量是Go语言在语法设计方面的一个“微创新”,也是“追求简单”设计哲学的又一体现,它可以让你的Go代码更加简洁。接下来我们就来看看无类型常量是如何简化Go代码编写的。