基础数据类型之值类型
# 基础数据类型 值类型
值类型通常是存储在内存栈空间中的键值对,将变量的实际值存储在栈中随取随用,但在赋值或拷贝时产生的是独立的键值对副本,修改新数据旧值不受影响。
类型 | 长度/Byte | 默认值 | 说明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | uint8 |
rune | 4 | 0 | Unicode Code Point, int32 |
int, uint | 4/8 | 0 | 32 或 64 位 |
int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255,byte是uint8 的别名 |
int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 4 | 0 | -21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名 |
int64, uint64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
string | UTF-8 字符串 | ||
array | 固定长度的数组 |
# 01 布尔类型
和C++一样,Golang中也使用bool
保留字声明布尔类型的数据,这种数据只有true
和false
两种值,分别表示“真”和“假”。
布尔类型有如下特点:
布尔类型的默认值是
false
布尔类型无法参与任何形式数值运算
布尔类型无法与其他任何类型进行转换
func BoolExp() {
var flag bool
if !flag {
fmt.Println("flag is false")
}
}
# 02 字符类型
组成每个字符串的元素叫做“字符”,在C/C++这种类型被称为字符类型用保留字char
表示,在Golang中用两种方式声明字符类型:
byte
类型:和C/C++中的char
相似只能表示 ASCII 码表中的字符,是uint8
的别名类型rune
类型:则是Golang中特有的字符类型能够表示 UTF-8 中的任一字符, 是int32
的别名类型
byte 类型虽然表示范围较小但在处理字符串性能上更优;而 rune 类型则在处理中文、日文等其他Unicode
复合字符串时更加方便。Golang通过这两种字符类型让其在处理字符串时性能和扩展性都有照顾。
func CharExp() {
var a byte = 'A'
var b rune = 'A'
fmt.Printf("sizeof a is %d bytes\n", unsafe.Sizeof(a))
fmt.Printf("sizeof b is %d bytes\n", unsafe.Sizeof(b))
}
# 03 整型
Golang中对数据类型进行更明确的命名,其中国整型分为以下两个大类:
有符号整型:
int8
、int16
、int32
、int64
无符号整型:
uint8
、uint16
、uint32
、uint64
其中uint8
是byte
类型及C/C++中的char
类型;int16
对应C/C++中的short
类型;int32
对应C/C++中的int
类型;int64
对应C/C++中的long
类型。
func IntExp() {
var a int8 = 1
var b int16 = 1
var c int32 = 1
var d int64 = 1
var e int = 1
fmt.Printf("size of a is %d bytes", unsafe.Sizeof(a))
fmt.Printf("size of b is %d bytes", unsafe.Sizeof(b))
fmt.Printf("size of c is %d bytes", unsafe.Sizeof(c))
fmt.Printf("size of d is %d bytes", unsafe.Sizeof(d))
fmt.Printf("size of e is %d bytes", unsafe.Sizeof(e))
}
# 04 浮点类型
Golang支持两种浮点型数:float32
和float64
分别对应C/C++中的float
和double
两种浮点类型。
float32
的浮点数的最大范围约为3.4e38
,可以使用常量定义:math.MaxFloat32
。 float64
的浮点数的最大范围约为 1.8e308
,可以使用一个常量定义:math.MaxFloat64
。
# 05 复数类型
Golang中增加了对复数表示的支持,针对复数实部和虚部float32
和float64
两种不同的精度提供了两种复数类型:complex64
和complex128
。
复数类型可以通过real
和 imag
两个内置函数分别获取complex的实部和虚部
var a complex128 = complex(1, 2)
var b complex128 = complex(3, 4)
fmt.Println(a*b)
fmt.Println(real(a*b))
fmt.Println(imag(a*b))
# 06 字符串类型
不同于C/C++中字符串以第三方库出现,Golang中将字符串作为原生数据类型,并实现基于UTF-8
编码,极大简化了字符串的处理。
# 6.1 定义字符串
字符串类型的保留字是string
,可以直接使用双引号""
来定义字符串。
func StringExp() {
var str1 string = "hello, world"
var str2 = "golang \\"
str3 := "go"
fmt.Println(str1, "printed by", str2, str3)
}
上面的例子中 str2 的定义中使用到反斜杠的转义符\\
,除此之外字符串中常见的转义符包含回车、换行、单双引号、制表符等。
转义 | 含义 |
---|---|
\r | 回车符(返回行首) |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符(tab键) |
' | 单引号 |
" | 双引号 |
\ | 反斜杠 |
定义多行字符串:Golang中可以使用反引号定义一个多行字符串,但反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
func MultiRows() {
var rows = `hello \n
world!
\t golang
`
fmt.Println(rows)
}
# 6.2 字符串操作
方法 | 介绍 |
---|---|
len(str) | 求长度 |
+ 或 fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.Contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
strings.Join(a[]string, sep string) | join操作 |
(1)获取字符串长度
Golang可以使用内置函数len()
来获取字符串的长度,也可以使用RuneCountInString()
来获取 Unicode 编码的字符串长度。
len()
获取的是字符串的 ASCII 字符个数或者整个字符串所占空间的字节长度,如下例子中,str1
是纯ASCII字符串其字符个数为12,则len(str1)
的返回值是12;str2
则是Unicode编码的字符串,"你好"字符串以 UTF-8 格式保存,每个中文字符占用 3 个字节,因此len(str2)
的返回值是 6。
func StringLen() {
var str1 string = "hello, world"
str2 := "你好"
fmt.Printf("the length of str1 is %d, str2 is %d\n", len(str1), len(str2))
fmt.Printf("the length of str1 is %d, str2 is %d\n", len(str1), utf8.RuneCountInString(str2))
}
如果想要获取 Unicode 字符串的字符个数,则需要使用 UTF-8 包中的 RuneCountInString()
方法,统计字符个数,及一般意义上的字符串长度。
(2)遍历字符串
Golang中遍历字符串可以通过标准索引法来获取,即用方括号运算符[]
找到对应索引位置的字符,字符串索引从 0 开始计数。
func StringTraversal() {
str1 := "hello, world"
str2 := "hello, 世界"
for i := 0; i < len(str1); i++ {
fmt.Printf(" %c:%d", str1[i], str1[i])
}
fmt.Println()
for i := 0; i < len(str2); i++ {
fmt.Printf(" %c:%d", str2[i], str2[i])
}
fmt.Println()
for _, ch := range str2 {
fmt.Printf(" %c:%d", ch, ch)
}
fmt.Println()
}
比较str1
和str2
的标准索引遍历方式,后者中Unicode编码字符出现乱码情况,可以得出该方式仅适用于纯 ASCII 码字符串 。Unicode 字符串遍历使用 for range
。
字符串底层原理:这是由于字符串底层是一个byte数组,所以可以和[]byte
类型相互转换。字符串是由byte字节组成,所以字符串的长度是byte字节的长度。 rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成,一个中文汉字由3~4
个字节组成,所以不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现乱码情况。
(3)截取子字符串
Golang字符串的截取可以使用标准索引法,使用索引[m:n]
表示截取索引大于等于m小于n的子串即str[m,n)
,如果字符串是 Unicode 编码则需要把包含复杂字符的字节数全都包含在截取区间内否则会出现乱码情况。
func StringSubStr() {
str := "hello, 世界"
fmt.Printf("str[0:5]:%s\n", str[0:5])
fmt.Printf("str[0:8]:%s\n", str[0:8])
fmt.Printf("str[0:10]:%s\n", str[0:10])
fmt.Printf("str[7:]:%s\n", str[7:])
}
(4)拼接字符串
Golang中字符串的拼接同样可以直接使用加号运算发+
将其右侧字符串拼接到其左侧字符串末尾生成一个新的字符串。也可以使用+=
运算符在当前字符串后直接添加一个字符串。
func StringAppend() {
str1 := "hello, "
str2 := "world!"
str := str1 + str2
fmt.Printf("%s + %s = %s\n", str1, str2, str)
str += "你好,世界!"
fmt.Printf("new string: %s\n", str)
}
(5)修改字符串
Golang中要修改字符串,需要先将其转换成[]rune
或[]byte
,完成后再转换为string
。无论哪种转换,都会重新分配内存,并复制字节数组。
func StringChange() {
str1 := "hello, world"
byteStr1 := []byte(str1)
byteStr1[1] = 'E'
fmt.Println("change str1: ", string(byteStr1))
str2 := "hello, 世界"
runeStr2 := []rune(str2)
runeStr2[7] = '国'
fmt.Println("change str2: ", string(runeStr2))
}
更多字符串操作参考标准库strings
strings package - strings - Go Packages (opens new window)
//判断两个字符串是否相同,忽略大小写。
func EqualFold(s, t string) bool
//判断字符串p的前缀是否为prefix
func HasPrefix(s, prefix string) bool
//判断字符串的结尾是否为suffix
func HasSuffix(s, suffix string) bool
//判断字符串s是否包含substr
func Contains(s, substr string) bool
//判断字符串中是否包括rune字符
func ContainsRune(s string, r rune) bool
//字符串s中是否包含chars任何一个字符
func ContainsAny(s, chars string) bool
//统计s中有多少sep个字符串
func Count(s, sep string) int
//返回在s中sep的首字母位置
func Index(s, sep string) int
//返回字符c在s中的位置
func IndexByte(s string, c byte) int
//返回字符r在s中的位置
func IndexRune(s string, r rune) int
//返回字符串chars中在s中出现最早的位置
func IndexAny(s, chars string) int
//返回符合函数规范的索引
func IndexFunc(s string, f func(rune) bool) int
//任意的字符chars在s中最后一个字符的索引
func LastIndexAny(s, chars string) int
//转换成小写字母
func ToLower(s string) string
//转换成大写字母
func ToUpper(s string) string
//字符串中的指定字符,最后一个参数为替换的个数,如果为-1,则全部替换
func Replace(s, old, new string, n int) string
//将所有包含sutset的开头和结尾的字符全部删除
func Trim(s string, cutset string) string
//删除全部空白
func TrimSpace(s string) string
//删除根据函数进行字符串开头和结尾的删除
func TrimFunc(s string, f func(rune) bool) string
//将字符串左侧的指定字符删除
func TrimLeft(s string, cutset string) string
//将字符串左侧符合函数的字符进行删除
func TrimLeftFunc(s string, f func(rune) bool) string
//将字符串左侧的prefix进行删除
func TrimPrefix(s, prefix string) string
//按字符串之间的空白进行分割,返回数组
func Fields(s string) []string
//按字符串中符合函数的字符进行分割
func FieldsFunc(s string, f func(rune) bool) []string
//将字符串中的sep作为分割符,进行分割,返回数组
func Split(s, sep string) []string
//按照sep进行分割,并且可以指定分割的次数
func SplitN(s, sep string, n int) []string
//前些个分割字符串是将分割符进行删除,此函数,将在分割符后进行分割。
func SplitAfter(s, sep string) []string
//不删除分割符,并指定分割次数
func SplitAfterN(s, sep string, n int) []string
//将字符串型数组,按照指定连接符进行连接
func Join(a []string, sep string) string
# 07 定长数组
# 7.1 定义数组
Golang中数组 Array 是同一种数据类型的固定长度的序列,所以定义数组时必须指定存放元素的类型和容量,其格式如下所示:
var 变量名称 [数组容量]数据类型
// example:
var arr0 [5]int
var arr1 [3]bool
如果在定义数组时不对其进行初始化,则使用默认值填充,整型默认值是0、浮点型默认值是0.0、布尔类型默认值是false、字符串默认值则是空串。
使用大括号初始化数组:即在大括号中定义好和数组指定的数据类型与数组容量一致的值
var arr0 = [5]int{1, 2, 3, 4, 5}
var arr1 [3]bool
fmt.Println("arr0:", arr0, "\narr1:", arr1)
局部初始化:初始化元素个数小于指明的容量时,数组在初始化过程中会用默认值填充补齐;如果数组声明时未指明容量或者使用...
设置数组容量,则数组在初始化过程中会根据初始值自动判断数组的容量。
var arr2 = [7]int{1, 2, 3, 4, 5, 6, 7}
var arr3 = [7]int{1, 2, 3}
var arr4 = []int{1, 2, 3}
var arr5 = [...]int{1, 2, 3}
fmt.Println("arr2:", arr2, "\narr3:", arr3, "\narr4:", arr4, "\narr5:", arr5)
通过指定索引对应的值初始化数组:在大括号中指定索引对应的值,未指定索引的值会用默认值填充。
arr6 := [5]int{0: 1, 3: 4}
arr7 := []int{0: 1, 4: 5, 8: 9}
fmt.Println("arr6:", arr6, "\narr7:", arr7)
# 7.2 定义多维数组
Golang中可以通过增加容量维度来定义多维数组,声明语法格式如下所示:
var 变量名称 [一维容量][二维容量]...[N维容量]数据类型
// example:
var arr8 [3][3]int
var arr9 [3][3][3]bool
多维数组的初始化与一维一致,满足嵌套结构
arr8 := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{0: 7, 2: 9},
}
fmt.Println("arr8:", arr8)
多维数组容量声明时仅能省略第一维度,其他维度不可省略
arr10 := [...][3]int{
{1, 2, 3},
{4, 5, 6},
{0: 7, 2: 9},
}
arr11 := [][3]int{
{1, 2, 3},
{0: 7, 2: 9},
}
fmt.Println("arr10:", arr10, "\narr11:", arr11)
# 7.3 遍历数组
使用标准索引法遍历数组:这种方式需要使用len()
内置函数获取数组长度,然后使用[]
中括号运算符获取索引元素。
func ArrayTraversal() {
var arr0 = [5]int{1, 2, 3, 4, 5}
for i := 0; i < len(arr0); i++ {
fmt.Printf("%d:%d ", i, arr0[i])
}
arr8 := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{0: 7, 2: 9},
}
for i := 0; i < len(arr8); i++ {
for j := 0; j < len(arr8[0]); j++ {
fmt.Printf("(%d,%d):%d ", i, j, arr8[i][j])
}
}
}
使用for range
遍历数组:
func ArrayTraversal() {
var arr0 = [5]int{1, 2, 3, 4, 5}
for _, e := range arr0 {
fmt.Printf("%d", e)
}
arr8 := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{0: 7, 2: 9},
}
for k1, row := range arr8 {
for k2, elem := range row {
fmt.Printf("(%d,%d):%d ", k1, k2, elem)
}
}
}
# 参考资料
基本类型 · Go语言中文文档 (opens new window)