
本期分享:
1.new和make的区别
2.Go语言的string和byte
new 和 make**的区别**在 Go 语言中,new 和 make 是两个用于内存分配的关键字,但它们的用途和行为有本质区别:
特性 | new(T) | make(T, args) |
|---|---|---|
适用类型 | 所有类型(值类型、引用类型) | 仅限 slice、map、channel |
返回值 | *T(指向类型零值的指针) | T(已初始化的类型本身) |
初始化行为 | 分配内存并置零(零值) | 分配内存 + 初始化底层结构 |
new(T):
T 分配一块内存,返回指向该内存的指针。int 为 0,string 为 "",结构体字段为各自零值)。*T 类型,需通过指针访问值。p := new(int) // p 是 *int 类型,指向值为 0 的内存
fmt.Println(*p) // 输出 0make(T, args):
slice、map、channel 分配内存并初始化底层结构。T 本身(而非指针)。slice 会初始化底层数组和长度/容量。map 会初始化哈希表。channel 会初始化缓冲区(如果指定)。s := make([]int, 5) // 创建长度为 5 的切片,底层数组已分配
m := make(map[string]int) // 创建空 map(已初始化哈希表)
ch := make(chan int, 10) // 创建带缓冲的 channel内存分配 vs 初始化:
new 仅分配内存并置零,不初始化复杂结构。make 分配内存并完成类型特定的初始化(如切片底层数组、map 的哈希桶)。返回值类型:
new 返回指针,需通过 * 解引用。make 返回类型本身,可直接使用。适用场景:
new 用于简单值类型或需要指针的场景。make 专门用于 slice/map/channel 的初始化。new 调用 runtime.newobject 分配内存并置零。make 调用类型特定的初始化函数(如 makeslice、makemap),完成底层数据结构的初始化。理解这些区别可以避免内存操作错误,确保代码健壮性。
在 Go 语言中,string 和 []byte(字节切片)是紧密相关但功能不同的数据类型,它们的关系体现在底层存储、不可变性、编码规则和操作方式上。
string 的底层结构:
Go 的 string 类型在底层是一个只读的字节切片(struct { ptr *byte; len int }),直接指向一段连续的内存空间。例如:
s := "Hello"
// 底层表示为:ptr 指向字节数组 [72, 101, 108, 108, 111],len 为 5[]byte 的底层结构:
[]byte 是一个可变的字节切片,包含指向底层数组的指针、长度和容量。例如:
b := []byte{72, 101, 108, 108, 111}
// 底层表示为:ptr 指向字节数组,len 和 cap 均为 5string 的不可变性:
Go 的字符串一旦创建,内容不可修改。任何修改操作(如拼接、替换)会生成新字符串:
s := "Hello"
// s[0] = 'h' // 编译错误:无法修改字符串
s = "h" + s[1:] // 合法:生成新字符串 "ello"[]byte 的可变性:
字节切片允许直接修改内容:
b := []byte{72, 101, 108, 108, 111}
b[0] = 104 // 修改后,b 变为 [104, 101, 108, 108, 111](即 "hello")string → []byte:
通过 []byte(s) 将字符串转为字节切片时,会复制底层数据(因为字符串不可变,需保证切片可独立修改):
s := "Hello"
b := []byte(s) // 复制 s 的字节到新切片
b[0] = 'h' // 修改切片不影响原字符串[]byte → string:
通过 string(b) 将字节切片转为字符串时,也会复制数据(确保字符串不可变性):
b := []byte{72, 101, 108, 108, 111}
s := string(b) // 复制切片的字节到新字符串默认 UTF-8 编码:
Go 的 string 类型隐式支持 UTF-8,可直接存储多语言字符:
s := "你好" // UTF-8 编码为 3 字节/字符[]byte 的原始字节:
字节切片不关心编码,仅表示原始二进制数据。若需处理 Unicode 字符,需手动解码:
b := []byte{228, 189, 160, 229, 165, 189} // UTF-8 编码的 "你好"
s := string(b) // 正确还原为 "你好"场景 | 使用 string | 使用 []byte |
|---|---|---|
文本处理 | 字符串拼接、正则表达式、格式化 | 修改字符内容(需转 []byte) |
网络/文件 I/O | 发送/接收文本数据 | 读写二进制协议(如 Protobuf) |
加密/哈希 | 极少直接使用 | 操作原始字节数据 |
JSON 解析 | 解析为 string 字段 | 解析为 []byte 字段(需反序列化) |
底层共享:string 和 []byte 均基于字节,但 string 是只读的,[]byte 是可变的。
转换代价:两者转换会复制数据,需避免高频操作。
编码处理:string 默认 UTF-8,[]byte 是原始字节,需显式处理编码。
性能优化:对字符串的修改需通过 []byte 完成,或使用 strings.Builder 减少分配。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// string → []byte
s := "Hello, 世界"
b := []byte(s)
b[7] = 0xE4// 修改 "世" 的 UTF-8 字节(需谨慎处理编码)
fmt.Println(string(b)) // 输出可能乱码(需正确处理 Unicode)
// 处理 Unicode 字符
r := []rune(s) // 将字符串转为 Unicode 码点切片
r[7] = '新' // 修改字符
fmt.Println(string(r)) // 输出 "Hello, 新界"
}理解 string 和 []byte 的关系,有助于高效处理文本与二进制数据,避免编码陷阱和性能问题。
本篇结束~