Go 之 语言基本类型

一、基本类型及其特性和用途

类型 长度(字节) 默认值 说明
bool 1 false 布尔值,表示真假
byte 1 0 别名为 uint8,用于表示ASCII字符或其他小整数值
rune 4 0 别名为 int32,表示一个Unicode码点
int, uint 4 或 8 0 有符号和无符号整数,具体长度取决于系统架构(32位或64位系统)
int8, uint8 1 0 有符号范围:-128 ~ 127;无符号范围:0 ~ 255,byteuint8 的别名
int16, uint16 2 0 有符号范围:-32,768 ~ 32,767;无符号范围:0 ~ 65,535
int32, uint32 4 0 有符号范围:-2,147,483,648 ~ 2,147,483,647;无符号范围:0 ~ 4,294,967,295,runeint32 的别名
int64, uint64 8 0 有符号范围:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807;无符号范围:0 ~ 18,446,744,073,709,551,615
float32 4 0.0 单精度浮点数
float64 8 0.0 双精度浮点数
complex64 8 复数,实部和虚部都是 float32
complex128 16 复数,实部和虚部都是 float64
uintptr 4 或 8 用于存储指针的整数类型,具体长度取决于系统架构
array - - 固定大小的数组,值类型
struct - - 结构体,包含多个字段的聚合类型,值类型
string - "" UTF-8 编码的字符串,值类型
slice - nil 动态大小的数组,引用类型
map - nil 键值对集合,引用类型
channel - nil 用于goroutine之间通信的通道,引用类型
interface - nil 接口类型,定义了一组方法集
function - nil 函数类型,可以作为参数传递或返回

二、byte 和 rune 区别

  • byte:适用于处理ASCII字符或单字节数据,通常用于处理纯英文文本或简单的二进制数据。
  • rune:适用于处理Unicode字符,特别是包含多字节字符的语言(如中文、日文等)。在处理复杂的字符串操作时,建议使用 rune 类型以确保字符的正确解析。

案例:

Go 复制代码
package main

import (
    "fmt"
)

func main() {
    str := "Hello你好"

    // 使用 byte 类型遍历字符串
    fmt.Println("Using byte:")
    for i := 0; i < len(str); i++ {
        fmt.Printf("Character: '%c', value: %d\n", str[i], str[i])
    }

    // 使用 rune 类型遍历字符串
    fmt.Println("\nUsing rune:")
    for _, r := range str {
        fmt.Printf("Character: '%c', value: %d\n", r, r)
    }
}

// 输出

Using byte:
Character: 'H', value: 72
Character: 'e', value: 101
Character: 'l', value: 108
Character: 'l', value: 108
Character: 'o', value: 111
Character: 'ä', value: 228
Character: '½', value: 189
Character: '?', value: 63

Using rune:
Character: 'H', value: 72
Character: 'e', value: 101
Character: 'l', value: 108
Character: 'l', value: 108
Character: 'o', value: 111
Character: '你', value: 20320
Character: '好', value: 22909

使用 byte 类型遍历时,由于字符串包含多字节字符(如"你好"),会出现乱码现象;而使用 rune 类型遍历时,则能正确解析每个字符。

三、new 与 make 的区别

特性 new(T) make(T, args...)
适用类型 任何类型,包括结构体、数组、基本类型 仅适用于切片、映射和通道
返回值 返回 *T(指向 T 的指针) 返回 T(类型本身,已初始化)
初始化方式 零值初始化 根据参数进行初始化
用途 分配内存并返回指针 创建并初始化切片、映射或通道
Go 复制代码
package main

import (
    "fmt"
)

func main() {
    // 使用 new 创建映射指针
    mNew := new(map[string]int)
    fmt.Printf("%v, %T\n", mNew, mNew) // 输出: &map[], *map[string]int
    fmt.Println(*mNew)                 // 输出: map[] (实际上是 nil)

    // 尝试直接对 nil 映射进行赋值会导致 panic
    // (*mNew)["key"] = 10 // 这行代码会引发 panic

    // 方法1:使用 make 初始化映射,并将其赋值给指针指向的映射
    *mNew = make(map[string]int)
    (*mNew)["key"] = 10
    fmt.Println(*mNew) // 输出: map[key:10]

    // 方法2:使用字面量初始化映射,并将其赋值给指针指向的映射
    *mNew = map[string]int{"anotherKey": 20}
    fmt.Println(*mNew) // 输出: map[anotherKey:20]

    // 使用 make 创建映射
    mMake := make(map[string]int)
    fmt.Printf("%v, %T\n", mMake, mMake) // 输出: map[], map[string]int
    mMake["key"] = 10
    fmt.Println(mMake) // 输出: map[key:10]

    // 也可以将 mMake 赋值给 mNew 指向的映射
    *mNew = mMake
    (*mNew)["key"] = 30
    fmt.Println(*mNew) // 输出: map[key:30]
}

综上像 slice、map、channel 这三种还是用make比较合适。

四、array 数组

1、初始化数组

Go 复制代码
// 初始化一个包含5个整数的数组
arr := [5]int{1, 2, 3, 4, 5} 

// 初始化前两个元素,其余为0 // 结果: [1, 2, 0, 0, 0]
arr := [5]int{1, 2} 

// 编译器会自动推断数组长度为5
arr := [...]int{1, 2, 3, 4, 5} 

// 赋值3和4位置得值 ["", "", "", "hello world", "tom"]
var str = [5]string{3: "hello world", 4: "tom"} 

2、多维数组

Go 复制代码
// 声明一个二维数组
matrix := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

// 使用省略号自动推断长度,注意 第 2 纬度不能用 "..."
matrix := [...][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

3、遍历数组

Go 复制代码
arr := [5]int{1, 2, 3, 4, 5}


// for 模式
for i := 0; i < len(arr); i++ {
    fmt.Printf("arr[%d] = %d\n", i, arr[i])
}

// range 模式
for i, v := range arr {
    fmt.Printf("arr[%d] = %d\n", i, v)
}

五、slice 切片

切片是对底层数组的一个引用,因此多个切片可以共享同一个底层数组。长度(length)表示切片中当前元素的数量。 容量(capacity)表示切片底层数组中从切片起始位置到数组末尾的元素数量。

1、切片的创建、初始化、遍历、修改以及常见操作

Go 复制代码
package main

import (
    "fmt"
)

func main() {
    // 使用 make 创建切片
    s1 := make([]int, 5, 10)
    fmt.Println("s1:", s1) // 输出: [0 0 0 0 0]

    // 直接初始化切片
    s2 := []int{1, 2, 3, 4, 5}
    fmt.Println("s2:", s2) // 输出: [1 2 3 4 5]

    // 从数组创建切片
    arr := [6]int{1, 2, 3, 4, 5, 6}
    s3 := arr[1:4]
    fmt.Println("s3:", s3) // 输出: [2 3 4]

    // 遍历切片
    for i, v := range s2 {
        fmt.Printf("s2[%d] = %d\n", i, v)
    }

    // 修改切片元素
    s2[0] = 10
    fmt.Println("After modification:", s2) // 输出: [10 2 3 4 5]

    // 添加元素
    s2 = append(s2, 6, 7, 8)
    fmt.Println("After append:", s2) // 输出: [10 2 3 4 5 6 7 8]

    // 删除元素
    s2 = append(s2[:2], s2[3:]...)
    fmt.Println("After removing element at index 2:", s2) // 输出: [10 2 4 5 6 7 8]

    // 复制切片
    dst := make([]int, 3)
    n := copy(dst, s2)
    fmt.Println("Copied slice:", dst) // 输出: [10 2 4]
    fmt.Println("Number of elements copied:", n) // 输出: 3

    // 切片的切片
    subSlice := s2[1:4]
    fmt.Println("Sub-slice:", subSlice) // 输出: [2 4 5]
}
  • 创建 :可以使用 make 函数创建切片,或者直接初始化切片。
  • 初始化:可以直接在声明时初始化切片元素,或者从现有数组中创建切片。
  • 访问和修改:通过索引访问和修改切片中的元素。
  • 遍历 :可以使用 for 循环或 range 来遍历切片。
  • 添加元素 :使用 append 函数向切片添加元素。
  • 删除元素:可以通过切片操作符删除元素。
  • 复制切片 :使用 copy 函数将一个切片的内容复制到另一个切片中。
  • 切片的切片:可以从一个切片中创建另一个切片。

2、切片得常规操作

操作 含义
s[n] 访问切片 s 中索引位置为 n 的项。
s[:] 从切片 s 的索引位置 0len(s)-1 处所获得的切片(即整个切片)。
s[low:] 从切片 s 的索引位置 lowlen(s)-1 处所获得的切片。
s[:high] 从切片 s 的索引位置 0high 处所获得的切片,长度为 high
s[low:high] 从切片 s 的索引位置 lowhigh 处所获得的切片,长度为 high - low
s[low:high:max] 从切片 s 的索引位置 lowhigh 处所获得的切片,长度为 high - low,容量为 max - low
len(s) 返回切片 s 的长度,表示当前包含的元素数量,总是小于等于 cap(s)
cap(s) 返回切片 s 的容量,表示底层数组中从切片起始位置到数组末尾的元素数量,总是大于等于 len(s)
Go 复制代码
package main

import (
    "fmt"
)

func main() {
    s := []int{1, 2, 3, 4, 5}

    // 访问切片s中索引位置为2的项
    fmt.Println("s[2]:", s[2]) // 输出: 3

    // 获取整个切片
    fmt.Println("s[:]:", s[:]) // 输出: [1 2 3 4 5]

    // 获取从索引位置1到末尾的切片
    fmt.Println("s[1:]:", s[1:]) // 输出: [2 3 4 5]

    // 获取从索引位置0到3的切片
    fmt.Println("s[:3]:", s[:3]) // 输出: [1 2 3]

    // 获取从索引位置1到3的切片
    fmt.Println("s[1:3]:", s[1:3]) // 输出: [2 3]

    // 获取从索引位置1到3且最大容量为4的切片
    fmt.Println("s[1:3:4]:", s[1:3:4]) // 输出: [2 3]

    
    s := make([]int, 3, 5) // 创建一个长度为3,容量为5的切片
    s = append(s, 4, 5)    // 添加两个元素

    fmt.Println("s:", s)           // 输出: [0 0 0 4 5]
    fmt.Println("len(s):", len(s)) // 输出: 5
    fmt.Println("cap(s):", cap(s)) // 输出: 5

    // 获取从索引位置1到3的切片
    // 子切片的容量由底层数组的容量减去子切片的起始索引决定,所以 cap 容量 5-1
    subSlice := s[1:3]
    fmt.Println("subSlice:", subSlice)          // 输出: [0 0]
    fmt.Println("len(subSlice):", len(subSlice)) // 输出: 2
    fmt.Println("cap(subSlice):", cap(subSlice)) // 输出: 4
}
相关推荐
调试人生的显微镜3 分钟前
深入剖析 iOS 26 系统流畅度,多工具协同监控与性能优化实践
后端
蹦跑的蜗牛4 分钟前
Spring Boot使用Redis实现消息队列
spring boot·redis·后端
非凡ghost13 分钟前
HWiNFO(专业系统信息检测工具)
前端·javascript·后端
非凡ghost15 分钟前
FireAlpaca(免费数字绘图软件)
前端·javascript·后端
非凡ghost21 分钟前
Sucrose Wallpaper Engine(动态壁纸管理工具)
前端·javascript·后端
间彧25 分钟前
从零到一搭建Spring Cloud Alibbaba项目
后端
楼田莉子26 分钟前
C++学习:C++11关于类型的处理
开发语言·c++·后端·学习
LSTM9730 分钟前
使用 Java 对 PDF 添加水印:提升文档安全与版权保护
后端
该用户已不存在30 分钟前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程
用户2986985301433 分钟前
Spire.Doc 实践指南:将Word 文档转换为 XML
后端·.net