定义变量
go
package main
import "fmt"
func main() {
// 1.先定义 后赋值
var num1 int
num1 = 11
fmt.Println("num1 value is: ", num1)
// 2.定义并赋值
var num2 int = 12
fmt.Println("num2 value is: ", num2)
// 3.自动推导类型并赋值(推荐)
num3 := 13
fmt.Println("num3 value is: ", num3)
// 4.同时定义多个变量和值
name, age, isMale := "张三", 11, false
fmt.Println("name:", name)
fmt.Println("age:", age)
fmt.Println("isMale:", isMale)
// 也可以这样一次定义多个变量和值
var (
name2 = "李四"
age2 = 12
isMale2 = true
)
fmt.Println("name2:", name2)
fmt.Println("age2:", age2)
fmt.Println("isMale2:", isMale2)
}数据类型
强烈推荐先阅读 这个笔记,这样才知道为什么需要有这么多不同的数据类型,为什么这些数据类型取值范围是这些数字

基本数据类型
[提示]
在 C 语言中, 无符号整数类型是这样定义的 unsigned int n = 10
所以,在 Go 语言中无符号整数被简写成了 uint
整型
| 长度 | 有符号类型 | 无符号类型 |
|---|---|---|
| 8 位 | int8 (-128 ~ 127) | uint8 (0 ~ 255) |
| 16 位 | int16 (-32768 ~ 32767) | uint16 (0 ~ 65535) |
| 32 位 | int32 (-2147483648 ~ 2147483647) | uint32 (0 ~ 4294967295) |
| 64 位 | int64 (-9223372036854775808 ~ 9223372036854775807) | uint64 (0 ~ 18446744073709551615) |
特殊的整型类型
| 类型 | 是否有符号 | 占用存储空间 | 取值范围 |
|---|---|---|---|
| int | 有符号 | 32位系统: 4字节, 64位系统: 8字节 | 32位: -2147483648~2147483647, 64位: -9223372036854775808~9223372036854775807 |
| uint | 无符号 | 32位系统: 4字节, 64位系统: 8字节 | 32位: 0~4294967295, 64位: 0~18446744073709551615 |
| uintptr | 无符号 | 与指针大小相同 | 与指针大小相同 |
| rune | 有符号 | 4字节 | 与int32相同 (-2147483648 ~ 2147483647) |
| byte | 无符号 | 1字节 | 与uint8相同 (0 ~ 255) |
由上表可知, int 类型是根据不同位数的操作系统的值而定的, 在32位操作系统上可以看作 int32 类型, 在64为操作系统上可以看作 int64
[如果使用类型自动推导,整型默认会是哪个具体类型呢?]
答案是 int 类型, 因为 int 类型明显更适合将代码编译到不同位数的操作系统然后运行
go
package main
// 导入多个包可以这写
import (
"fmt"
"unsafe"
)
func main() {
// 定义一个变量, 类型让编译器自动推导
n := 1
// Printf %d 占位符可以获取变量的类型字符串
// 会输出: n type is int
fmt.Printf("n type is: %T", n)
// 换行
fmt.Println()
// 使用 unsafe.sizeOf 可以获取一个变量的占用直接数
fmt.Printf("n size is: %d", unsafe.Sizeof(n))
// 强行赋值一个超出取值范围的数字就会报错
// 64位操作系统最大值: 9223372036854775807
// 硬是要赋一个超出取值范围的值就会报错
n = 9223372036854775808
// 编译就会报错:
// # command-line-arguments
// ./main.go:26:6: cannot use 9223372036854775808 (untyped int constant) as int value in assignment (overflows)
}浮点型
| 长度 | 类型 | 取值范围/精度 |
|---|---|---|
| 32 位 | float32 | ±3.4e38,约7位小数精度 |
| 64 位 | float64 | ±1.8e308,约15位小数精度 |
[提示]
在Go中,如果没有指定类型,浮点字面量默认为float64类型
字符类型
在 Go 语言中没有 char 这个类型,而是使用:
byte:uint8 的别名,适合存储 ASCII 字符rune:int32 的别名,适合存储 Unicode 字符
go
var b byte = 'A' // ASCII字符
var r rune = '中' // Unicode字符字符串类型
字符类型只能存储单个字符, 要存储多个字符, 就必须使用 string 类型
字符串的本质就是一个只读的 rune 类型的切片
go
package main
import "fmt"
func main() {
// 定义字符串类型的变量
str := "hello"
fmt.Printf("str type: %T, value:%v \n", str, str)
// 输出: str type: string, value:hello
var str2 string = "world"
fmt.Printf("str2 type: %T, value:%v \n", str2, str2)
// 输出: str2 type: string, value:world
}布尔类型
布尔类型(bool)只有两个取值:true 和 false, 用于条件判断和流程控制
go
isGreater := 10 > 5 // isGreater 为 true复杂数据类型
Go 语言的复杂数据类型包括:
- 数组(Array): 固定长度的有序元素序列
- 切片(Slice): 动态长度的数组视图
- 映射(Map): 键值对集合, 类似字典(python dict)或哈希表(js object)
- 结构体(Struct): 自定义复合类型, 组合多个字段, 和 C 语言类似
- 指针(Pointer): 存储变量内存地址的类型
- 函数(Function): 可作为值传递的代码块
- 接口(Interface): 定义行为契约的抽象类型
- 通道(Channel): 用于 Goroutine 间通信的管道
这些复杂数据类型将在后续章节中详细介绍
基本数据类型的默认值
在定义变量的时候, 可以使用 var aaa type 然后再 aaa = 111 这样两个步骤
那么假设我现在不给他赋值, 而直接去使用, 那它的值是多少呢?
go
package main
import "fmt"
func main() {
var i8 int8
var u8 uint8
var i16 int16
var u16 uint16
var i32 int32
var u32 uint32
var f32 float32
var f64 float64
// 默认值全都是 0
fmt.Printf("%T default value is %v\n", i8, i8)
fmt.Printf("%T default value is %v\n", u8, u8)
fmt.Printf("%T default value is %v\n", i16, i16)
fmt.Printf("%T default value is %v\n", u16, u16)
fmt.Printf("%T default value is %v\n", i32, i32)
fmt.Printf("%T default value is %v\n", u32, u32)
fmt.Printf("%T default value is %v\n", f32, f32)
fmt.Printf("%T default value is %v\n", f64, f64)
// 默认值是false
var flg bool
fmt.Printf("%T default value is %v\n", flg, flg) // false
}强制类型转换
数字相关类型转换
go
package main
import (
"fmt"
)
func main() {
// 小 -> 大: 是没有问题的
var x1 int8 = 111
var x2 int16 = int16(x1)
fmt.Printf("x1=%d, x2=%d \n", x1, x2)
// 大 -> 小: 编译不会报错, 但是会出现数值溢出现象
var n1 int16 = 333
var n2 int8 = int8(n1)
fmt.Printf("n1=%d, n2=%d \n", n1, n2)
// 输出: n1=%d, n2=%d 333 77
}其他基本类型转字符串
go
package main
import (
"fmt"
)
func main() {
n := 10
f := 3.14
b := false
c := 'a'
str1 := fmt.Sprintf("%d", n)
fmt.Printf("str1 is %T type, str1 value is %s", str1, str1)
fmt.Println()
// 输出: str1 is string type, str1 value is 10
str2 := fmt.Sprintf("%f", f)
fmt.Printf("str2 is %T type, str2 value is %s", str2, str2)
fmt.Println()
// 输出: str2 is string type, str2 value is 3.140000
str3 := fmt.Sprintf("%t", b)
fmt.Printf("str3 is %T type, str3 value is %s", str3, str3)
fmt.Println()
// 输出: str3 is string type, str3 value is false
str4 := fmt.Sprintf("%c", c)
fmt.Printf("str4 is %T type, str4 value is %s", str4, str4)
fmt.Println()
// 输出: str4 is string type, str4 value is a
}字符串转其他基本类型
go
package main
import (
"fmt"
"strconv"
)
func main() {
// str -> int
// strconv.ParseInt 这个函数返回2个值, 先不用管第二个值是什么
s1 := "11"
v1, _ := strconv.ParseInt(s1, 10, 32)
fmt.Printf("v1: type=%T, value=%d \n", v1, v1)
// 输出: v1: type=int64, value=11
// str -> float
s2 := "11.22"
v2, _ := strconv.ParseFloat(s2, 32)
fmt.Printf("v2: type=%T, value=%f \n", v2, v2)
// 输出: v2: type=float64, value=11.220000
// str -> byte
s3 := "false"
v3, _ := strconv.ParseBool(s3)
fmt.Printf("v3: type=%T, value=%t \n", v3, v3)
}指针类型
- 指针变量只能存储内存地址
- 指针变量的类型必须匹配内存地址对应空间存储的数据的值
- 指针变量可以解引用(直接通过保存的内存地址去修改对应内存空间的值)
- 基本数据类型(值类型), 都有对应的指针类型(如:
int 和 *int,float32 和 *float32)
go
package main
import (
"fmt"
)
func main() {
n := 10
// 与C语言中一样: & 是取地址符号
fmt.Println("获取 n 的内存地址:", &n)
// 取地址符号后面加上变量名就可以获取到变量的地址
// 这个地址就是一种特殊的变量类型, 指针类型
// 可以自动推断简写为 nptr := &n
// nptr: 变量名
// *int: 指针类型
// &n : 取普通变量 n 的内存地址并赋值给 nptr 变量
var nptr *int = &n
fmt.Println("获取 nptr 的内存地址:", nptr)
// 根据 nptr 指针变量的存储的内存地址获取内存中的值
// 使用 *nptr 就可以获取指针变量内存地址中的值
// 这个操作就叫做: 解引用
fmt.Println("nptr 指针变量对应内存地址中存储的值:", *nptr)
// 可以通过指针变量保存的地址来修改对应内存中的值
*nptr = 20
fmt.Println("再次通过 n 来访问对应内存空间的值:", n)
}类型别名与自定义类型
- 使用 type 关键字, 可以定义类型别名
- 注意: 虽然是类型别名, 但是编译器会认为是不同的类型, 需要手动转换
- 类型别名: 仅仅是给复杂的类型取个别名,让代码看起来更简洁一些
- 自定义类型: 类似其他变成语言中的对象,可以给这个类型绑定自有的方法
go
package main
import "fmt"
// 定义一些类型别名(绣化了🤣)
type (
isize = int
usize = uint
i8 = int8
u8 = uint8
i16 = int16
u16 = uint16
i32 = int32
u32 = uint32
i64 = int64
u64 = uint64
f32 = float32
f64 = float64
)
func main() {
var n1 u8 = 20
var n2 uint
// n2 = n1 // 直接赋值会报错, 编译器报错信息如下: 说类型不一样
// # command-line-arguments
// ./main.go:25:7: cannot use n1 (variable of uint8 type u8) as uint value in assignment
n2 = uint(n1) // 这里的 uint 可以接受 u8 类型的值, 因为它只是个别名, 使用的内存空间是一样的
fmt.Println("n1 = ", n1)
fmt.Println("n2 = ", n2)
}go
package main
import "fmt"
type uid uint32
func (id uid) findOne() {
// 查询数据库, uid 对应的资源
fmt.Println("findOne by id")
}
func main() {
// 将一个数字转换成 uid 类型
uid := uid(1001)
// 通过类型调用类型上的方法
uid.findOne()
}任意类型 any
如果学过 TypeScript, 这个就很好理解了
any表示任意类型(或者说:不限制数据类型)
go
package main
import "fmt"
// 输出一个变量的值和类型(推荐 any 而不是 interface{})
func dd(x any) {
fmt.Printf("值:%v \t 类型:%T \n", x, x)
}
// 注意: any 类型关键字是 Go1.18 版本引入的关键字
// 在 Go1.18 之前的版本需要使用 interface{}
func dd2(x interface{}) {
fmt.Printf("值:%v \t 类型:%T \n", x, x)
}
func main() {
dd(1)
dd2(1)
dd("1")
dd2("1")
dd(1.1)
dd2(1.1)
dd(true)
dd2(true)
dd([]int{1, 2, 3})
dd2([]int{1, 2, 3})
}定义常量
注意点:
- 定义常量字符全大写_分割多个单词
- 初始化常量, 必须赋值(
const DB_DSN string这样不赋值是不行的) - 常量也可以自动推导类型
go
package main
import "fmt"
// 使用 const 定义常量
const DB_URL string = "sqlite3:///example.db"
// 一次定义多个
const (
DB_TYPE = "mysql"
DB_HOST = "localhost"
DB_PORT = "3306"
DB_NAME = "test_db"
DB_USER = "root"
DB_PASS = "root123456"
)
func main() {
// 常量定义后, 不能修改, 只能读取
fmt.Println("DB_URL: ", DB_URL)
fmt.Println("DB_TYPE: ", DB_TYPE)
fmt.Println("DB_HOST: ", DB_HOST)
fmt.Println("DB_PORT: ", DB_PORT)
fmt.Println("DB_NAME: ", DB_NAME)
fmt.Println("DB_USER: ", DB_USER)
fmt.Println("DB_PASS: ", DB_PASS)
// 硬是修改, 就无法通过编译
// DB_URL = "hello";
// # command-line-arguments
// ./main.go:12:2: cannot assign to DB_URL (neither addressable nor a map index expression)
}printf 占位符参考
| 占位符 | 描述 | 接收类型 |
|---|---|---|
| %% | 输出百分号% | 任意类型 |
| %s | 输出string/[] byte值 | string,[] byte |
| %q | 格式化字符串,输出的字符串两端有双引号"" | string,[] byte |
| %d | 输出十进制整型值 | 整型类型 |
| %f | 输出浮点数 | 浮点类型 |
| %e | 输出科学计数法形式 ,也可以用于复数 | 浮点类型 |
| %E | 与%e相同 | 浮点类型 |
| %g | 根据实际情况判断输出%f或者%e,会去掉多余的0 | 浮点类型 |
| %b | 输出整型的二进制表现形式 | 数字类型 |
| %#b | 输出二进制完整的表现形式 | 数字类型 |
| %o | 输出整型的八进制表示 | 整型 |
| %#o | 输出整型的完整八进制表示 | 整型 |
| %x | 输出整型的小写十六进制表示 | 数字类型 |
| %#x | 输出整型的完整小写十六进制表示 | 数字类型 |
| %X | 输出整型的大写十六进制表示 | 数字类型 |
| %#X | 输出整型的完整大写十六进制表示 | 数字类型 |
| %v | 输出值原本的形式,多用于数据结构的输出 | 任意类型 |
| %+v | 输出结构体时将加上字段名 | 任意类型 |
| %#v | 输出完整Go语法格式的值 | 任意类型 |
| %t | 输出布尔值 | 布尔类型 |
| %T | 输出值对应的Go语言类型值 | 任意类型 |
| %c | 输出Unicode码对应的字符 | int32 |
| %U | 输出字符对应的Unicode码 | rune,byte |
| %p | 输出指针所指向的地址 | 指针类型 |