类型
类型总览
分类 | 类型 | 长度 | 默认值 | 说明 |
---|---|---|---|---|
布尔型 | bool | 1 | false | true / false |
整型 | byte | 1 | 0 | 是 uint8 的别名,两者完全等价 |
rune | 4 | 0 | 是 int32 的别名,两者完全等价 | |
int, uint | 4 或 8 | 0 | 32位系统中是4字节,在64位系统中是8字节 | |
int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255 | |
int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 | |
int32, uint32 | 4 | 0 | -231 ~ 231-1, 0 ~ 2^32-1 | |
int64, uint64 | 8 | 0 | -263 ~ 263-1, 0 ~ 2^64-1 | |
浮点型 | float32 | 4 | 0.0 | 精确度较低,应优先使用 float64 |
float64 | 8 | 0.0 | 精确度更高的浮点型 | |
复数 | complex64 | 8 | 0+0i | 内部由float32实现 |
complex128 | 16 | 0+0i | 内部由float64实现 | |
其他 | uintptr | 4 或 8 | 0 | 足以存储指针的 uint32 或 uint64 整数 |
array | 数组,值类型,后面内容细讲 | |||
struct | 结构体,值类型,后面内容细讲 | |||
string | "" | UTF-8 字符串,值类型 | ||
slice | nil | 引用类型,后面内容细讲 | ||
map | nil | 引用类型,后面内容细讲 | ||
channel | nil | 引用类型,后面内容细讲 | ||
interface | nil | 接口 | ||
function | nil | 函数 |
数值支持八进制、十六进制,以及科学记数法。标准库 math
定义了各数字类型取值范围。
a, b, c, d := 071, 0x1F, 1e9, math.MinInt16
空指针值为nil
,而不是C/C++中的 NULL
引用类型
值类型与引用类型的区别
值类型:参数传递时传递的是内容,而非对象本身
引用类型:参数传递时传递的是地址,是对象本身
go 中的引用类型只有 pointer
、function
、slice
,map
, channel
五种,它们有复杂的内部结构,除了申请内存外,还需要初始化相关属性。
和来自其他编程语言的固有印象不同,go 中的数组、字符串都是值类型
-
内置函数
new
计算类型大小,为其分配零值内存,返回指针。 -
make
会被编译器翻译成具体的创建函数,由其分配内存和初始化成员结构,返回对象而非指针。
a := []int{0, 0, 0} // 提供初始化表达式。
a[1] = 10
b := make([]int, 3) // makeslice
b[1] = 10
c := new([]int)
c[1] = 10 // Error: invalid operation: c[1] (index of type *[]int) -> 不能给指针赋值
关于引用类型的其他细节,就说到这里,其他的后面再谈
类型转换
go不支持隐式类型转换,即便从窄转宽也不行
var b byte = 100
// var n int = b // Error: cannot use b (type byte) as type int in assignment
var n int = int(b) // 显式转换
var f float = 4.8
var i int = int(f) // 向下取整,i = 4
其他类型不能当作布尔型
使用(许多编程语言中将0视作false
,非0视作true
,go中不允许这么做)
a := 100
if a { // Error: non-bool a (type int) used as if condition
println("true")
}
字符串
字符串是不可变值类型,内部用指针指向 UTF-8 字节数组。
- 本质上是一个数组
- 默认值是空字符串 ""。
- 用索引号访问某字节,如
s[i]
。 - 不能用序号获取字节元素指针,
&s[i]
非法。 - 不可变类型,无法修改字节数组。
- 字节数组尾部不包含
NULL
。 - 在go中没有专门的字符类型
string的内部结构
struct String
{
byte* str;
intgo len;
};
单引号与双引号
在Go中,双引号是用来表示字符串string,其实质是一个byte类型的数组,单引号表示rune类型(字符)
package main
import "fmt"
func main() {
testString := "测试字符串"
bytes := []byte(testString) //将字符串转为byte数组,这也是string内部的的存储方式
runes := []rune(testString) //将字符串转为rune数组
fmt.Printf("%s:%d\n","string len",len(testString)) // 15,一个汉字被转义为3个byte,所以长度为15
fmt.Printf("%s:%d\n","bytes len",len(bytes)) // 15,原因同上
fmt.Printf("%s:%d\n","runes len",len(runes)) // 5
}
可见 rune
类型能做到一个值对应一个字符而byte
做不到(byte范围太小)
访问字符串中的字符
可以使用索引号来访问字符(byte)
s := "abc"
println(s[0] == '\x61', s[1] == 'b', s[2] == 0x63)// true true true
处理原始字符串
使用 "`" 定义不做转义处理的原始字符串,支持跨行。
s := `a
b\r\n\x00
c`
println(s)
输出
a
b\r\n\x00
c
字符串拼接
与常见语言不通,链接跨行字符串时,+
必须在上一行末尾,否则编译错误
s := "Hello, " +
"World!"
s2 := "Hello, "
+ "World!" // Error: invalid operation: + untyped string
获取子串
支持用两个索引号返回子串。子串依然指向原字节数组,仅修改了指针和长度属性。
s := "Hello, World!"
s1 := s[:5] // Hello
s2 := s[7:] // World!
s3 := s[1:5] // ello
字符表示
单引号字符常量表示 Unicode Code Point,支持 \uFFFF
、\U7FFFFFFF
、\xFF
格式。对应 rune
类型,UCS-4。
package main
import "fmt"
func main() {
//输出变量的类型
fmt.Printf("%T\n", 'a') //输出int32,也就是rune
var c1, c2 rune = '\u6211', '们'
fmt.Println(c1 == '我', string(c2) == "\xe4\xbb\xac") //true true
}
修改字符串
要修改字符串,可先将其转换成 []rune
或 []byte
,完成后再转换为 string
。无论哪种转换,都会重新分配内存,并复制字节数组。
由于这个原因,所以处理字符串我更建议转换为[]rune
处理
package main
import "fmt"
func main() {
s := "abcd"
bs := []byte(s)
bs[1] = 'B'
fmt.Println(string(bs)) // aBcd
u := "电脑"
us := []rune(u)
us[1] = '话' //因为字符串本质上是rune数组,所以此处是单引号,而不是双引号
fmt.Println(string(us)) // 此处会重新分配内存,输出:电话
}
遍历字符
用 for
循环遍历字符串中的字符时,也有 byte
和 rune
两种方式。
package main
import "fmt"
func main() {
s := "abc汉字"
// byte方式
for i := 0; i < len(s); i++ {
fmt.Printf("%c,", s[i])
}
// rune方式
// range 关键字专门用于遍历,返回当前遍历元素
for _, r := range s {
fmt.Printf("%c,", r)
}
}
其他资料
Q.E.D.