本期分享:
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) // 输出 0
make(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
在 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 均为 5
核心区别:不可变性 vs 可变性
string
的不可变性:
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) // 复制切片的字节到新字符串
编码与 Unicode 支持
默认 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
的关系,有助于高效处理文本与二进制数据,避免编码陷阱和性能问题。
本篇结束~