文章目录
- 整数
- 浮点数
- 复数
- 布尔值
- 字符串
- 字符串字面量
- Unicode
- UTF - 8
- 字符串和字节 slice
- 字符串和数字的相互转换
- 常量
- 常量生成器 iota
- 无类型常量
整数
分类
- Go 的整数类型按大小分有 8 位、16 位、32 位、64 位 ,同时有符号整数包括
int8
、int16
、int32
、int64
,无符号整数包括uint8
、uint16
、uint32
、uint64
。int
和uint
大小依平台而定,可能是 32 位或 64 位 。 rune
是int32
同义词,用于指明 Unicode 码点 ;byte
是uint8
同义词,强调原始数据 。uintptr
用于底层编程存放指针,大小不明确 。
范围
- 有符号整数以补码表示,最高位为符号位,
n
位有符号整数取值范围是-2^(n - 1)
到2^(n - 1) - 1
;无符号整数取值范围是0
到2^n - 1
,如int8
取值范围是-128
到127
,uint8
取值范围是0
到255
。
运算
- 二元操作符:涵盖算术、逻辑和比较运算,按优先级分五级,同级别运算满足左结合律 。算术运算符(
+
、-
、*
、/
、%
)中,取模运算%
仅用于整数,除法运算/
在整数相除时结果为整数 。运算结果超出类型范围会溢出,溢出高位部分丢弃 。比较运算符用于比较同类型整数,结果为布尔型 。 - 一元操作符:有一元加法(
+x
,等同于0 + x
)和一元减法(-x
,等同于0 - x
) 。 - 位运算符:包括
&
(位运算 AND )、|
(位运算 OR )、^
(位运算 XOR )、&^
(位清空 AND NOT )、<<
(左移 )、>>
(右移 ) 。位运算符对操作数逐位运算,不涉及算术进位或正负号 。左移运算x << n
等价于x
乘以2^n
,右移运算x >> n
等价于x
除以2^n
向下取整 ,有符号整数右移按符号位填充 。
类型转换
- 不同整数类型间转换需显式转换 。算术和逻辑(不含移位 )二元运算符要求操作数类型相同 ,否则需转换为同一类型 。整数与浮点型相互转换可能改变值或损失精度 ,浮点型转整型会舍弃小数部分 。
- 底层数据结构相同但数据名称不同也需类型转化,如
tpye MyInt int
,MyInt
类型数据无法赋值给int
类型数据。
Go中无隐式类型的转化。
格式化输出
- 整数可写成十进制、八进制(以
0
开头 )、十六进制(以0x
或0X
开头 ) 。使用fmt
包输出整数时,可用%d
(十进制 )、%o
(八进制 )、%x
(十六进制 )等谓词指定进制 。
浮点数
- Go 语言有
float32
和float64
两种浮点数类型,算术特性遵循 IEEE 754 标准 。float32
有效数字约 6 位,float64
约 15 位 ,应优先选用float64
,因float32
运算易累积误差 。
范围与表示
math
包给出浮点值极限,math.MaxFloat32
约为3.4e38
,math.MaxFloat64
约为1.8e308
,最小正浮点数分别约为1.4e - 45
和4.9e - 324
。浮点数在源码中可写成小数(小数点前后数字可省略 )或科学记数法(如6.02214129e23
) 。- 通过
fmt.Printf
的%g
谓词可输出浮点值,也可用%e
(有指数 )或%f
(无指数 ) 。
特殊值
math
包含创建和判断 IEEE 754 标准特殊值(正无穷大、负无穷大、NaN )的函数 。math.IsNaN
判断参数是否为非数值,math.NaN
返回非数值 。与 NaN 比较除!=
外总不成立 。
运算与应用
- 函数返回浮点型且可能出错时,最好单独报错 。
复数
- Go 语言有
complex64
和complex128
两种复数类型,分别由float32
和float64
构成 。
操作函数
complex
函数用于根据给定实部和虚部创建复数,如var x complex128 = complex(1, 2)
创建复数1 + 2i
。real
函数提取复数实部,imag
函数提取虚部,如real(x*y)
、imag(x*y)
。
表示
- 在源码中,浮点数或十进制整数后紧跟
i
表示虚数(实部为 0 ),如3.141592i
、2i
,复数常量可与其他常量相加,如1 + 2i
。可用==
和!=
判断复数是否相等,需实部和虚部都相等 。 math/cmplx
包提供复数运算库函数,如cmplx.Sqrt(-1)
计算复数平方根 。
布尔值
bool
型值即布尔值,只有true
(真 )和false
(假 )两种取值 。if
和for
语句中的条件以及比较操作符(如==
、<
)的运算结果都是布尔值 。一元操作符!
表示逻辑取反,如!true
为false
,且(!true==false)==true
。代码风格上,x==true
常简化为x
。
运算规则
- 布尔值可通过
&&
(逻辑与 )和||
(逻辑或 )组合运算,存在短路行为:当&&
左边操作数为false
,或||
左边操作数为true
时,右边操作数不会计算 。例如s != "" && s[0] == 'x'
,若s
为空字符串,不会计算s[0]
。 &&
和||
优先级高于==
和<
,形如'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9'
这样的条件表达式无需加括号 。
与C++,Java等,运算规则相同。
与数值的转换
- 布尔值不能隐式转换成数值(如 0 或 1 ),反之亦然 。若需转换,要使用显式
if
语句,如if b { i = 1 }
。
字符串
- 字符串是不可变的字节序列,可包含任意数据,主要用于存储人类可读文本,文本字符串按 UTF - 8 编码的 Unicode 码点序列解读 。
操作函数与运算
- 长度获取:内置
len
函数返回字符串字节数,如len("hello, world")
结果为12
。 - 字符访问:通过下标操作
s[i]
获取第i
个字符(0 ≤ i < len(s)
),访问越界会触发宕机异常 。非 ASCII 字符的 UTF - 8 码点可能占多个字节,所以第i
个字节不一定是第i
个字符 。 - 子串生成:用
s[i:j]
生成子字符串,从下标i
(含 )到j
(不含 ),结果长度为j - i
,i
和j
默认值分别为0
和len(s)
,下标越界或j < i
会触发宕机异常 。 - 字符串连接:用加号
+
连接两个字符串生成新字符串,如"goodbye" + s[5:]
。 - 字符串比较:可通过
==
和<
等比较运算符比较,按字节进行字典排序 。
不可变性
- 字符串值不可改变,不能直接修改字符串内部字节,如
s[0] = 'L'
会编译报错 。可通过+=
等方式生成新字符串并赋值给原变量实现拼接等操作 。字符串不可变性使得字符串及其子串可安全共用底层内存,复制和子串生成操作开销低 。
存储方式
字符串字面量
常规字符串字面量
- 形式:带双引号的字节序列,如
"Hello, 世界"
。由于 Go 源文件按 UTF - 8 编码,字符串也按 UTF - 8 解读,可写入 Unicode 码点 。 - 转义序列:以反斜杠
\
开始,用于插入特定字节或表示 ASCII 控制码等,如\a
(“警告” 或响铃 )、\n
(换行符 )、\"
(双引号 )、\\
(反斜杠 )等 。还可包含十六进制(\xhh
,h
为十六进制数字 )或八进制(\ooo
,o
为八进制数字 )转义字符来表示单个字节 。
原生字符串字面量
- 形式:用反引号包裹,如:
const GoUsage = `Go is a tool for manageing Go source code.
Usage:go command [arguments]
...`
- 特性:转义序列不起作用,内容严格按字面写法,包括反斜杠和换行符,可跨多行书写,回车符会被删除以保证字符串在各平台值相同 。适用于正则表达式、HTML 模板、JSON 字面量、命令行提示信息等场景 。
Unicode
从 ASCII 到 Unicode 的演进
-
早期软件主要处理 ASCII 字符集,US - ASCII 码用 7 位表示 128 个字符,包含英文字母、数字、标点等,能满足早期计算机行业需求,但无法支持其他语言的文字体系 。随着互联网发展,数据包含多种语言,ASCII 无法满足需求,于是出现了 Unicode 。
-
定义:Unicode 囊括世界上所有文书体系的字符,给每个字符赋予标准数字,即 Unicode 码点 。
-
Go 语言实现:在 Go 语言中,这些字符记号称为文字符号(
rune
),Unicode
第 8 版定义超 100 种语言文字的 12 万个字符码点 。Go 采用int32
类型保存单个文字符号,rune
是int32
的别名 。可将文字符号序列表示成int32
值序列,即 UTF - 32 或 UCS - 4 编码,每个码点编码长度固定为 32 位 。但这种编码因多数文本是 ASCII 码(每个字符 8 位 ),会造成存储空间浪费,且多数常用字符用 16 位就能容纳。
UTF - 8
编码原理
- UTF - 8 是以字节为单位对 Unicode 码点作变长编码,由 Ken Thompson 和 Rob Pike 发明 。每个文字符号用 1 - 4 个字节表示,ASCII 字符编码占 1 字节,与传统 ASCII 码一致 ;若最高位是 110,编码占 2 个字节;若最高位是 1110,编码占 3 个字节;若最高位是 11110,编码占 4 个字节 。
编码特性
- 紧凑兼容:紧凑且兼容 ASCII,从左向右解码不超过 3 字节就能定位一个字符,能快速搜索字符,无需考虑前文,字符顺序与 Unicode 码点顺序一致,编码本身不会嵌入 NUL 字节 。
- 操作便利:基于 UTF - 8 编码的字符串操作无需解码即可进行,如判断前缀(
HasPrefix
函数 )、后缀(HasSuffix
函数 )、包含子串(Contains
函数 )等 。
转义与码点表示
Unicode 转义符可用于文字符号,码点值小于 256 的可写成单个十六进制数转义形式,更高码点需用\u
或\U
转义 。不同转义序列形式的字符串字面量,实质字符串值相同 。
字符串操作与解码
- 统计与遍历:可使用
unicode/utf8
包中的函数,如RuneCountInString
统计字符串中文字符号数目 。通过for i, r := range s
循环可隐式解码遍历 UTF - 8 编码字符串,对于非 ASCII 文字符号,下标增量大于 1 。 - 显式解码:
utf8.DecodeRuneInString
函数可返回文字符号本身和其按 UTF - 8 编码所占字节数 ,用于显式解码操作 。
类型转换相关
[]rune
转换用于 UTF - 8 编码字符串时,返回其 Unicode 码点序列 。- 将文字符号类型的
slice
转换成字符串,会输出各文字符号的 UTF - 8 编码拼接结果 。 - 整数转换为字符串时,按文字符号类型解读,产生代表该文字符号值的 UTF - 8 编码 。
字符串和字节 slice
标准包功能
- strings 包:提供字符串搜索、替换、比较、修整、切分与连接等操作函数 。
- bytes 包:功能类似 strings 包,用于操作字节 slice(
[]byte
),且提供Buffer
类型,能高效处理字符串构建,避免多次内存分配和复制 。 - strconv 包:用于布尔值、整数、浮点数与字符串间的类型转换,以及字符串添加 / 去除引号等操作 。
- unicode 包:包含判别文字符号值特性(如是否为数字
IsDigit
、字母IsLetter
、大写IsUpper
、小写IsLower
)的函数,还有转换大小写(ToUpper
、ToLower
)的函数,遵循 Unicode 标准 。
函数示例
func main() {fmt.Println(basename2("a/b/c.go"))fmt.Println(basename2("c.d.go"))fmt.Println(basename2("abc"))
}func basename2(s string) string {slash := strings.LastIndex(s, "/")s = s[slash+1:]if dot := strings.LastIndex(s, "."); dot >= 0 {s = s[:dot]}return s
}func basename1(s string) string{for i := len(s) - 1; i >= 0; i-- {if s[i] == '/' {s = s[i + 1:]break;}}for i := len(s) - 1; i >= 0; i-- {if s[i] == '.'{s = s[:i]break}}return s
}
- basename 函数:有两种实现方式,初版独立完成工作,移除字符串中类似文件系统路径的前缀和文件类型后缀 ;简化版利用
strings.LastIndex
函数,更简洁高效 。
字符串与字节 slice 转换
- 字符串和字节 slice 可相互转换,
[]byte(s)
将字符串s
转换为字节 slice ,会分配新字节数组并拷贝内容;string(b)
将字节 sliceb
转换为字符串,会生成副本 。strings 包和 bytes 包有功能对应的实用函数,只是操作对象分别为字符串和字节 slice 。
func main() {fmt.Println(intsToString([]int{1, 2, 3}))
}func intsToString(values []int) string {var buf bytes.Bufferbuf.WriteByte('[')for i, v := range values {if i > 0 {buf.WriteString(", ")}fmt.Fprintf(&buf, "%d", v)}buf.WriteByte(']')return buf.String()
}
- intsToString 函数:将字节 slice 转化为 string。
字符串和数字的相互转换
整数转字符串
- 可使用
fmt.Sprintf
函数,通过格式化谓词(如%d
)将整数转换为字符串,例如x := 123; y := fmt.Sprintf("%d", x)
。 - 也可使用
strconv.Itoa
函数,它专门用于将整数转换为 ASCII 字符串 ,如strconv.Itoa(x)
。 strconv.FormatInt
和strconv.FormatUint
函数可按不同进位制格式化数字 ,如strconv.FormatInt(int64(x), 2)
可将整数按二进制格式输出 。相比之下,fmt.Printf
的%b
、%d
、%o
、%x
等谓词在包含额外信息时更方便 。
字符串转整数
strconv.Atoi
函数用于将表示整数的字符串转换为整型 ,如x, err := strconv.Atoi("123")
。strconv.ParseInt
函数可指定进制和结果匹配的整型大小(第三个参数 ),如y, err := strconv.ParseInt("123", 10, 64)
,结果类型为int64
,可再转换为较小类型 。strconv.ParseUint
用于无符号整数转换 。
输入处理
- 对于字符串和数字混合的单行输入,可尝试用
fmt.Scanf
解释 ,但它在处理不完整或不规则输入时灵活性欠佳 。
常量
- 定义:常量是在编译阶段就能计算出值的表达式,本质属于基本类型(布尔型、字符串、数字 ) 。其声明类似变量,但值恒定,防止程序运行中被意外修改,适合表示数学常量(如圆周率
pi
) 。 - 计算时机:许多常量计算在编译时完成,减少运行时工作量,利于编译器优化 。操作数为常量时,一些运行时才报错的情况(如整数除 0 )编译时就会报错 。常量参与的数学、逻辑、比较运算结果仍是常量,类型转换结果和部分内置函数(
len
、cap
等 )返回值也是常量 。
声明与使用
- 单个常量声明:用
const
关键字,如const pi = 3.14159
。 - 多个常量声明:可在一个
const
声明中定义一系列常量,适用于相关值,如const ( e = 2.71828… pi = 3.14159… )
。 - 在类型声明中使用:常量表达式可用于数组类型长度声明,如
const IPv4Len = 4; var p [IPv4Len]byte
。 - 类型推断:常量声明可指定类型和值,未显式指定类型时,根据右边表达式推断 ,如
const noDelay time.Duration = 0
。 - 复用表达式:同时声明一组常量时,除第一项外,其他项等号右侧表达式可省略,复用前面一项的表达式及其类型 。
常量生成器 iota
iota
是常量生成器,用于创建一系列相关值,在常量声明中,它从 0 开始取值,逐项加 1 。
应用示例
type Weekday int
const (Sunday Weekday = iotaMondayTuesdayWednesdayThursdayFridaySaturday
)
- 枚举类型定义:以
time
包中Weekday
类型定义为例,声明每周 7 天为Weekday
类型常量,从Sunday
开始,Sunday
值为 0 ,Monday
值为 1 ,依此类推 。
type Flags uint
cosnt (FlagUp Flags = 1 << itoaFlagBroadcastFlagLoopbackFlagPointToPointFlagMulticast
)func IsUp(v Flags) bool { return v&FlagUp == FlagUp }
func TurnDown(v *Flags) { *v &^= FlagUp }
func SetBroadcast(v *Flags) { *v |= FlagBroadcast }
func IsCast(v Flags) bool { return v&(FlagBroadcast|FlagMulticast) != 0}
- 位标志定义:借助
net
包代码,定义Flags
类型常量,如FlagUp = 1 << iota
,随着iota
递增,每个常量按1 << iota
赋值,等价于 2 的连续次幂,用于表示不同的位标志,如FlagUp
(向上 )、FlagBroadcast
(支持广播访问 )等 ,还有相关函数用于判定、设置或清除这些位标志 。
const (_ = 1 << (10 * itoa)KiBMiBGiBTiBPiBEiBZiBYiB
)
- 幂值定义:声明表示 1024 的幂的常量,如
const { _ = 1 << (10 * iota); KiB; MiB; … }
,iota
递增,对应值为 1024 的不同幂次 。
局限性
- 由于 Go 语言不存在指数运算符,
iota
无法直接生成 1000 的幂(如KB
、MB
) 。
无类型常量
- Go 语言中许多常量不从属具体类型,编译器将其表示为精度更高的值(算术精度可达 256 位 ),有 6 种无类型常量,即无类型布尔、无类型整数、无类型文字符号、无类型浮点数、无类型复数、无类型字符串 。
优势
-
可暂时维持高精度,能用于更多表达式且无需转换类型 。如计算中值过大无法用常规整型存储的常量,可参与运算;
math.Pi
作为无类型常量可用于需浮点值或复数的地方,若一开始确定类型(如float64
),会导致精度下降 。 -
类型推断:常量字面量类型由语法决定,如
0
(无类型整数 )、0.0
(无类型浮点数 )、0i
(无类型复数 )、'\u0000'
(无类型文字符号 ) 。常量除法中,操作数写法影响结果类型(如整数除法结果可能是整型,浮点数除法结果是浮点型 ) 。 -
类型转换:无类型常量声明为变量或给变量赋值时,会隐式转换成变量类型 。显式或隐式转换时,目标类型要能表示原值,实数和复数允许舍入取整 。变量声明未显式指定类型时,无类型常量会隐式转换为变量默认类型 。无类型整数可转成
int
,无类型浮点数和复数分别转成float64
和complex128
。要将变量转成不同类型,需显式转换无类型常量或在声明时指明类型 。
接口值与默认类型
- 将无类型常量转换为接口值时,其默认类型决定接口值动态类型,如
fmt.Printf("%T\n", 0)
输出int
,fmt.Printf("%T\n", 0.0)
输出float64
等 。
浮点型 ) 。
- 类型转换:无类型常量声明为变量或给变量赋值时,会隐式转换成变量类型 。显式或隐式转换时,目标类型要能表示原值,实数和复数允许舍入取整 。变量声明未显式指定类型时,无类型常量会隐式转换为变量默认类型 。无类型整数可转成
int
,无类型浮点数和复数分别转成float64
和complex128
。要将变量转成不同类型,需显式转换无类型常量或在声明时指明类型 。
接口值与默认类型
- 将无类型常量转换为接口值时,其默认类型决定接口值动态类型,如
fmt.Printf("%T\n", 0)
输出int
,fmt.Printf("%T\n", 0.0)
输出float64
等 。
参考资料:《Go程序设计语言》