首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《Go小技巧&易错点100例》第三十八篇

《Go小技巧&易错点100例》第三十八篇

作者头像
闫同学
修改2025-06-26 09:25:56
修改2025-06-26 09:25:56
1230
举报

本期分享:

1.new和make的区别

2.Go语言的string和byte


new make**的区别**

在 Go 语言中,newmake 是两个用于内存分配的关键字,但它们的用途和行为有本质区别:

核心区别

特性

new(T)

make(T, args)

适用类型

所有类型(值类型、引用类型)

仅限 slice、map、channel

返回值

*T(指向类型零值的指针)

T(已初始化的类型本身)

初始化行为

分配内存并置零(零值)

分配内存 + 初始化底层结构

详细对比

new(T):

  • 作用:为类型 T 分配一块内存,返回指向该内存的指针。
  • 特点
    • 内存被置零(如 int0string"",结构体字段为各自零值)。
    • 返回 *T 类型,需通过指针访问值。
  • 示例
代码语言:javascript
复制
p := new(int)   // p 是 *int 类型,指向值为 0 的内存
fmt.Println(*p) // 输出 0

make(T, args):

  • 作用:为 slicemapchannel 分配内存并初始化底层结构。
  • 特点
    • 返回类型 T 本身(而非指针)。
    • slice 会初始化底层数组和长度/容量。
    • map 会初始化哈希表。
    • channel 会初始化缓冲区(如果指定)。
  • 示例
代码语言:javascript
复制
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 调用类型特定的初始化函数(如 makeslicemakemap),完成底层数据结构的初始化。

理解这些区别可以避免内存操作错误,确保代码健壮性。

Go语言的string和byte

在 Go 语言中,string[]byte(字节切片)是紧密相关但功能不同的数据类型,它们的关系体现在底层存储、不可变性、编码规则和操作方式上。

底层本质:字符串是只读的字节序列**

string 的底层结构

Go 的 string 类型在底层是一个只读的字节切片struct { ptr *byte; len int }),直接指向一段连续的内存空间。例如:

代码语言:javascript
复制
s := "Hello"
// 底层表示为:ptr 指向字节数组 [72, 101, 108, 108, 111],len 为 5

[]byte 的底层结构

[]byte 是一个可变的字节切片,包含指向底层数组的指针、长度和容量。例如:

代码语言:javascript
复制
b := []byte{72, 101, 108, 108, 111}
// 底层表示为:ptr 指向字节数组,len 和 cap 均为 5
核心区别:不可变性 vs 可变性

string 的不可变性

Go 的字符串一旦创建,内容不可修改。任何修改操作(如拼接、替换)会生成新字符串:

代码语言:javascript
复制
s := "Hello"
// s[0] = 'h' // 编译错误:无法修改字符串
s = "h" + s[1:] // 合法:生成新字符串 "ello"

[]byte 的可变性

字节切片允许直接修改内容:

代码语言:javascript
复制
b := []byte{72, 101, 108, 108, 111}
b[0] = 104 // 修改后,b 变为 [104, 101, 108, 108, 111](即 "hello")
转换与性能影响

string[]byte

通过 []byte(s) 将字符串转为字节切片时,会复制底层数据(因为字符串不可变,需保证切片可独立修改):

代码语言:javascript
复制
s := "Hello"
b := []byte(s) // 复制 s 的字节到新切片
b[0] = 'h'     // 修改切片不影响原字符串

[]bytestring

通过 string(b) 将字节切片转为字符串时,也会复制数据(确保字符串不可变性):

代码语言:javascript
复制
b := []byte{72, 101, 108, 108, 111}
s := string(b) // 复制切片的字节到新字符串
编码与 Unicode 支持

默认 UTF-8 编码

Go 的 string 类型隐式支持 UTF-8,可直接存储多语言字符:

代码语言:javascript
复制
s := "你好" // UTF-8 编码为 3 字节/字符

[]byte 的原始字节

字节切片不关心编码,仅表示原始二进制数据。若需处理 Unicode 字符,需手动解码:

代码语言:javascript
复制
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 减少分配。

示例代码
代码语言:javascript
复制
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 的关系,有助于高效处理文本与二进制数据,避免编码陷阱和性能问题。

本篇结束~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 扯编程的淡 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • new 和 make**的区别**
    • 核心区别
    • 详细对比
    • 关键区别总结
    • 底层原理
  • Go语言的string和byte
    • 底层本质:字符串是只读的字节序列**
    • 核心区别:不可变性 vs 可变性
    • 转换与性能影响
    • 编码与 Unicode 支持
    • 常见使用场景
    • 关键总结
    • 示例代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档