Golang 数据类型和运算符
一、变量
1. 声明
同一作用域内不支持重复声明,且 Go 语言的变量声明后必须使用。
(1)单个或批量声明
// 单变量声明
var 变量名 类型
// 批量声明
var (
变量名 类型
变量名 类型
)
(2)短变量声明
- 仅能在函数内部使用,语法:
变量名 := 表达式
(3)匿名变量
- 用下划线(
_)表示,用于忽略不需要的返回值等场景。
2. 初始化
Go 会自动对变量对应的内存区域进行初始化,每个变量会被初始化为其类型的默认值。
(1)初始化语法
// 类型可省略,编译器会自动推导,可一次初始化多个变量
var 变量名 类型 = 表达式
(2)常用类型的默认值
- 整型、浮点型:0
- 字符串:“”
- bool:false
- 切片、函数、指针:nil
3. 示例
package main
import "fmt"
func getNameAndAge() (string, int) {
return "abcd", 30
}
func main() {
// 1. 批量声明
var (
name1 string
age1 int
isOk1 bool
)
fmt.Printf("name1: %s, age1: %d, isOk1: %t\n", name1, age1, isOk1)
// 2. 变量初始化
var name2 = "yingzai"
var site2 = "www.baidu.com"
var age2 = 30
fmt.Printf("name2: %s, site2: %s, age2: %d\n", name2, site2, age2)
// 3. 初始化多个变量(逗号分隔)
var name3, site3, age3 = "yingzai", "www.baidu.com", 30
fmt.Printf("name3: %s, site3: %s, age3: %d\n", name3, site3, age3)
// 4. 短变量声明(函数内部,用 := 声明和初始化)
name4 := "yingzai"
site4 := "www.baidu.com"
age4 := 30
fmt.Printf("name4: %s, site4: %s, age4: %d\n", name4, site4, age4)
// 5. 匿名变量(用不到的变量用 _ 表示)
name5, _ := getNameAndAge()
fmt.Printf("name: %s\n", name5)
}
二、常量
1. 普通常量
(1)语法
const constantName [type] = value
- 同时声明多个常量时,若省略值则表示与上一行值相同
- 可使用下划线(
_)跳过某些值
(2)示例
package main
import "fmt"
func main() {
// 1. 常量声明
const PI1 float64 = 3.14
const PI2 = 3.1415 // 可省略类型
const i, j = 1, 2 // 多重赋值
const a, b, c = 1, 2, "foo"
const (
width = 100
height = 200
a1 = 100
a2 // 与上一行值相同(100)
a3 // 与上一行值相同(100)
)
fmt.Printf("a1: %v, a2: %v, a3: %v\n", a1, a2, a3) // 100 100 100
}
2. iota
iota 是可被编译器修改的常量,代表 const 声明块的行索引(从 0 开始)。
(1)取值规则
- 单个 const 声明块中,iota 从 0 开始,每增加一行声明,值 +1
- 遇到 const 关键字时重置为 0
- 单行声明中多个 iota 取值相同
用法和常量类似
- 如果为常量指定了一个表达式,但后续的常量没有表达式,则继承上面的表达式
- iota 还可以声明中间插队
- 可以使用下划线(_)跳过某些值
(2)示例
package main
import "fmt"
func main() {
const (
a5 = iota // 0
a6 = iota // 1
a7 = iota // 2
)
fmt.Printf("a5: %v, a6: %v, a7: %v\n", a5, a6, a7) // 0 1 2
const (
a8 = iota
_ // 跳过值(1)
a9 = iota // 2
)
fmt.Printf("a8: %v, a9: %v\n", a8, a9) // 0 2
const (
a10 = iota // 0
a11 = 100 // 插队赋值
a12 = iota // 2(继续按行索引计数)
)
fmt.Printf("a10: %v, a11: %v, a12: %v\n", a10, a11, a12) // 0 100 2
// 实际应用示例
type Priority int
const (
LOG_EMERG Priority = iota // 0
LOG_ALERT // 1
LOG_CRIT // 2
LOG_ERR // 3
LOG_WARNING // 4
LOG_NOTICE // 5
LOG_INFO // 6
LOG_DEBUG // 7
)
const (
mutexLocked = 1 << iota // 1(1<<0)
mutexWoken // 2(1<<1)
mutexStarving // 4(1<<2)
mutexWaiterShift = iota // 3(行索引)
startvationThresholdNs = 1e6 // 1000000
)
const (
bit0, mask = 1 << iota, 1<<iota - 1 // 1, 0
bit1, mask1 // 2, 1
_, _ // 4, 3
bit3, mask3 // 8, 7
)
三、数据类型
数据类型用于区分数据所需的内存大小,提高内存利用率。
1. 布尔类型
- 取值:
true或false - 注意:不能用 0 或非 0 表示真假
2. 数值类型
(1)整型
- 大小与操作系统相关:
uint、int(32 位系统占 4 字节,64 位系统占 8 字节) - 大小固定(与系统无关):
- 无符号:
uint8、uint16、uint32、uint64 - 有符号:
int8、int16、int32、int64
- 无符号:
- 特殊类型:
uintptr(无符号整型,用于存放指针)
(2)浮点型
float32:IEEE-754 32 位浮点型,最大范围常量math.MaxFloat32float64:IEEE-754 64 位浮点型,最大范围常量math.MaxFloat64
(3)复数
complex64:32 位实数和虚数complex128:64 位实数和虚数
(4)其他
byte:类似uint8(用于表示 ASCII 字符)rune:类似int32(用于表示 UTF-8 字符,如中文、日文等)
3. 字符串类型
使用 UTF-8 编码,是任意字节的常量序列。
(1)声明
func test01() {
var s1 string
s1 = "hello world"
s2 := "yingzai"
s3 := s1 + s2 // 拼接
fmt.Printf("s1: %s, s2: %s, s3: %s\n", s1, s2, s3)
}
(2)字符串字面量
- 双引号(
"):可解析字符串,支持转义,不可多行(如"hello\nworld") - 反引号(
`):原生字符串,不支持转义,可多行(如`hello\nworld`输出hello\nworld)
(3)字符串拼接
- 加号(
+):s3 := s1 + s2;追加(+=):s1 += s2(性能较差,会产生临时字符串) fmt.Sprintf():内部用[]byte实现,性能一般strings.Join(a []string, sep string):先计算长度再申请内存,数组场景下效率高(sep 为分隔符)buffer.WriteString():可变字符操作,可预估长度用buffer.Grow()优化性能
(4)字符串转义(\)
常用转义符:\r(回车)、\n(换行)、\t(tab)、\'(单引号)、\"(双引号)、\\(反斜杠)
(5)字符串切片(截取)
str[n]:获取索引 n 的原始字节str[n:m]:获取索引 n 到 m-1 的字符str[n:m:max]:获取索引 n 到 m-1 的字符, 截取 max 个长度str[n:]:获取索引 n 到末尾的字符str[:m]:获取索引 0 到 m-1 的字符
(6)常用函数
- 长度:
len(str) - 切割:
strings.Split - 包含判断:
strings.Contains - 前后缀判断:
strings.HasPrefix()、strings.HasSuffix() - 子串位置:
strings.Index()、strings.LastIndex()
(7)注意事项
-
空字符串长度为 0,但不是
nil -
字符串与
[]byte转换会发生内存拷贝(有性能开销):func test02() { b1 := []byte{'h', 'e', 'l', 'l', 'o'} s1 := string(b1) // []byte 转 string s2 := "hello world" b2 := []byte(s2) // string 转 []byte fmt.Printf("s1: %s, b1: %v, s2: %s, b2: %v\n", s1, b1, s2, b2) } -
在使用 for-range 遍历字符串时,每次迭代返回的是 UTF-8 编码的首个字节的下标和字节值,这意味着下标可能不连续:
func test03() { s := "中国" for i, v := range s { fmt.Printf("index: %d, value: %c\n", i, v) // index: 0, value: 中;index: 3, value: 国 } } -
字符串不可通过下标修改(可重新赋值):
func test04() { s := "hello" // &s[0] = 'a' // 编译错误 s = "world" // 合法 fmt.Printf("s: %s\n", s) }
4. 派生类型
- 指针类型(Pointer)
- 数组类型(Array)
- 结构化类型(Struct)
- 通道类型(Channel)
- 函数类型(func)
- 切片类型(Slice)
- 接口类型(Interface)
- 哈希表(Map)
5. 指针:uintptr 和 unsafe.Pointer
(1)转换规则
- 任何类型指针 ↔
unsafe.Pointer uintptr↔unsafe.Pointer
(2)区别
| 类型 | 本质 | 用途 | 限制 |
|---|---|---|---|
unsafe.Pointer |
通用无类型指针(类似 C 的 void*) |
指针类型转换、与 uintptr 配合操作内存 |
不支持算术运算 |
uintptr |
无符号整数(存储指针地址) | 指针算术运算、系统调用交互 | 非指针类型,不能直接解引用 |
6. 示例
package main
import (
"fmt"
"math"
"unsafe"
)
// bool 类型
func test01() {
// 1.bool 类型
var b1 bool = true
var b2 bool = false
var b3 = true
var b4 = false
b5 := true
b6 := false
fmt.Printf("b1: %v, b2: %v, b3: %v, b4: %v, b5: %v, b6: %v\n",
b1, b2, b3, b4, b5, b6)
// 2.条件判断中
age := 18
ok := age >= 18
if ok {
fmt.Printf("ok: true") // ok: true
} else {
fmt.Printf("ok: false")
}
fmt.Printf("\n")
// 3.循环语句中
count := 10
for i := 0; i < count; i++ {
fmt.Printf("%v ", i)
}
fmt.Printf("\n")
// 4.逻辑表达式中
age4 := 20
gender := "man"
if age4 >= 18 && gender == "man" {
fmt.Printf("gender: %v, age4: %v\n", gender, age4)
}
// 5.注意:不能使用 0 和 非0 表示真假
/*
i := 1
if i {
// Error! 编译错误
}
*/
}
// 数值类型
func test02() {
// 1.整型
var i8 int8
var i16 int16
var i32 int32
var i64 int64
fmt.Printf("%T %dB %v~%v\n", i8, unsafe.Sizeof(i8), math.MinInt8, math.MaxInt8) // int8 1B -128~127
fmt.Printf("%T %dB %v~%v\n", i16, unsafe.Sizeof(i16), math.MinInt16, math.MaxInt16) // int16 2B -32768~32767
fmt.Printf("%T %dB %v~%v\n", i32, unsafe.Sizeof(i32), math.MinInt32, math.MaxInt32) // int32 4B -2147483648~2147483647
fmt.Printf("%T %dB %v~%v\n", i64, unsafe.Sizeof(i64), math.MinInt64, math.MaxInt64) // int64 8B -9223372036854775808~9223372036854775807
var ui8 uint8
var ui16 uint16
var ui32 uint32
var ui64 uint64
fmt.Printf("%T %dB %v~%v\n", i8, unsafe.Sizeof(ui8), 0, math.MaxUint8) // int8 1B 0~255
fmt.Printf("%T %dB %v~%v\n", ui16, unsafe.Sizeof(ui16), 0, math.MaxUint16) // uint16 2B 0~65535
fmt.Printf("%T %dB %v~%v\n", ui32, unsafe.Sizeof(ui32), 0, math.MaxUint32) // uint32 4B 0~4294967295
fmt.Printf("%T %dB %v~%v\n", ui64, unsafe.Sizeof(ui64), 0, uint64(math.MaxUint64)) // uint64 8B 0~18446744073709551615
// 2.浮点型
var f32 float32
var f64 float64
fmt.Printf("%T %dB %v~%v\n", f32, unsafe.Sizeof(f32), -math.MaxFloat32, math.MaxFloat32) // float32 4B -3.4028234663852886e+38~3.4028234663852886e+38
fmt.Printf("%T %dB %v~%v\n", f64, unsafe.Sizeof(f64), -math.MaxFloat64, math.MaxFloat64) // float64 8B -1.7976931348623157e+308~1.7976931348623157e+308
// 越界
var ui uint
ui = uint(math.MaxUint64) // +1 会导致 overfloat 错误
fmt.Printf("%T %dB %v~%v\n", ui, unsafe.Sizeof(ui), 0, ui) // uint 8B 0~18446744073709551615
var imax, imin int
imax = int(math.MaxInt64) // +1 会导致 overflows 错误
imin = int(math.MinInt64) // -1 会导致 overflows 错误
fmt.Printf("%T %dB %v~%v\n", imax, unsafe.Sizeof(imax), imin, imax) // int 8B -9223372036854775808~9223372036854775807
// 按进制输出
var a int = 10
fmt.Printf("%d \n", a) // 10
fmt.Printf("%b \n", a) // 1010 二进制
fmt.Printf("%o \n", a) // 12 八进制
fmt.Printf("%x \n", a) // a 十六进制
fmt.Printf("%X \n", a) // A 十六进制
// 浮点型输出
fmt.Printf("%f\n", math.Pi) // 3.141593
fmt.Printf("%.2f\n", math.Pi) // 3.14
// 复数
var c1 complex64
var c2 complex128
c1 = 1 + 2i
c2 = 2 + 3i
fmt.Println(c1) // (1+2i)
fmt.Println(c2) // (2+3i)
}
func main() {
//test01()
test02()
}
四、类型定义和类型别名(type)
用于定义自定义类型和函数类型。
1. 语法
// 类型定义(全新类型)
type NewType Type
// 类型别名(原类型的别名)
type NewType = Type
2. 区别
- 类型定义:创建全新类型,与原类型不兼容(需显式转换)
- 类型别名:仅为原类型的别名,编译后不存在,与原类型兼容(可直接使用原类型方法)
- 建议:优先使用类型别名,减少显式转换
五、运算符
1. 算术运算符
+、-、*、/、%(取余)++(自增)、--(自减):为单独语句,非运算符
2. 比较运算符
>、<、>=、<=、==、!=
3. 逻辑运算符
&&(与)、||(或)、!(非)
4. 位运算符
&(按位与)、|(按位或)、^(按位异或)<<(左移)、>>(右移)
5. 赋值运算符
=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
6. 示例
package main
import "fmt"
func main() {
a := 100
b := 10
// 1.算术运算符
fmt.Printf("a + b = %v\n", a+b)
fmt.Printf("a - b = %v\n", a-b)
fmt.Printf("a * b = %v\n", a*b)
fmt.Printf("a / b = %v\n", a/b)
a++
fmt.Printf("a++ = %v\n", a) // 101
a--
fmt.Printf("a-- = %v\n", a) // 100
fmt.Println("-----------------")
// 2.比较运算符
fmt.Printf("a b: %t\n", a b)
fmt.Printf("a >= b: %t\n", a >= b)
fmt.Printf("a < b: %t\n", a < b)
fmt.Printf("a <= b: %t\n", a <= b)
fmt.Printf("a == b: %t\n", a == b)
fmt.Printf("a != b: %t\n", a != b)
fmt.Println("-----------------")
// 3.逻辑运算符
c := true
d := false
fmt.Printf("c && d: %t\n", c && d)
fmt.Printf("c || d: %t\n", c || d)
fmt.Printf("!c: %t\n", !c)
fmt.Println("-----------------")
// 4.位运算符
e := 7 // 111
f := 10 // 1010
fmt.Printf("e & f = %v\n", (e & f)) // 2
fmt.Printf("e | f = %v\n", e|f) // 15
fmt.Printf("e ^ f = %v\n", e^f) // 13
fmt.Printf("f << 1 = %v\n", f<<1) // 20
fmt.Printf("f >1 = %v\n", f>>1) // 5
fmt.Println("-----------------")
// 5.赋值运算符
var g int
g = 10
fmt.Printf("g = %v\n", g)
g += 1
fmt.Printf("g += 1 = %v\n", g)
g -= 1
fmt.Printf("g -= 1 = %v\n", g)
g *= 2
fmt.Printf("g *= 2 = %v\n", g)
g /= 2
fmt.Printf("g /= 2 = %v\n", g)
fmt.Println("-----------------")
}
六、格式化输出
1. 常用占位符

2. %v、%+v、%#v 的区别
%v:仅输出值%+v:输出字段名 + 值(结构体)%#v:输出结构体名 + 字段名 + 值(结构体)
3. 示例
package main
import "fmt"
// 结构体
type Website struct {
Name string
}
// 定义结构体变量
var site = Website{Name: "www.baidu.com"}
type user struct {
name string
}
func main() {
u := user{"yingzai"}
fmt.Printf("%+v\n", u) // {name:yingzai}
fmt.Printf("%#v\n", u) // main.user{name:"yingzai"}
fmt.Printf("%T\n", u) // main.user
fmt.Println("---------------------------")
fmt.Printf("%t\n", true) // true
fmt.Printf("%b\n", 1024) // 10000000000
fmt.Printf("%c\n", 11111) // ⭧
fmt.Printf("%d, %o, %x\n", 10, 10, 10) // 10, 12, a
fmt.Printf("%q\n", 10) // '\n'
fmt.Printf("%U\n", 1233) // U+04D1
fmt.Printf("%b\n", 12.34) // 6946802425218990p-49
fmt.Printf("%e\n", 12.34) // 1.234000e+01
fmt.Printf("%f\n", 12.345) // 12.345000
fmt.Printf("%g\n", 12.345) // 12.345
fmt.Println("---------------------------")
fmt.Printf("%s\n", "abcde") // abcde
fmt.Printf("%q\n", "abcde") // "abcde"
fmt.Printf("%x\n", "abc") // 616263
fmt.Printf("%p\n", 0x123) // %!p(int=291)
}