Go语言:new还是make?到底该如何选择?

简介: 根据传入的类型申请一块内存,然后返回指向这块内存的指针

new 函数

我们直接声明一个指针类型的变量 p ,然后对改变量的值进行修改,为“微客鸟窝”:

func main() {
  var p *string
  *p = "微客鸟窝"
  fmt.Println(*p)
}
复制代码

程序运行,会报错:

panic: runtime error: invalid memory address or nil pointer dereference
复制代码

这是因为指针类型的变量如果没有分配内存,默认值是零值 nil。它没有指向的内存,所以无法使用。

如果要使用,给它分配一块内存就可以了,可以使用 new函数:

func main() {
  var p *string
  p = new(string) //new函数进行内存分配
  *p = "微客鸟窝"
  fmt.Println(*p)
}
复制代码

上面示例便可以正常运行,内置函数 new 的作用是什么呢?源码分析:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
复制代码

作用:

  • 根据传入的类型申请一块内存,然后返回指向这块内存的指针
  • 指针指向的数据就是该类型的零值
  • 比如传入的类型是 string,那么返回的就是 string 指针,这个 string 指针指向的数据就是空字符串

变量初始化

声明完变量,对变量进行赋值,就是所谓的初始化。

string 类型变量初始化:

var s string = "微客鸟窝"
s1 := "微客鸟窝"
复制代码

指针变量初始化

我们可以定义一个函数来进行初始化指针变量

package main
import "fmt"
func main() {
  pp:=NewPerson("微客鸟窝",18)
  fmt.Println(pp) //&{微客鸟窝 18}
}
type person struct {
  name string
  age int
}
func NewPerson(name string,age int) *person{
  p := new(person)
  p.name = name
  p.age = age
  return p
}
复制代码

make 函数

上文我们已经了解到,在使用 make 函数创建 map 的时候,其实调用的是 makemap 函数:

// makemap implements Go map creation for make(map[k]v, hint).
func makemap(t *maptype, hint int, h *hmap) *hmap{
  //省略无关代码
}
复制代码

makemap 函数返回的是 *hmap 类型,而 hmap 是一个结构体,它的定义如下:

// A header for a Go map.
type hmap struct {
   // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
   // Make sure this stays in sync with the compiler's definition.
   count     int // # live cells == size of map.  Must be first (used by len() builtin)
   flags     uint8
   B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
   noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
   hash0     uint32 // hash seed
   buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
   oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
   nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)
   extra *mapextra // optional fields
}
复制代码

可以看到, map 关键字其实非常复杂,它包含 map 的大小 count、存储桶 buckets 等。要想使用 hmap ,只是通过简单的 new 函数来返回一个 *hmap 就可以实现的,还需要对其进行初始化,这就是 make 所发挥的作用:

m := make(map[string]int,10)
复制代码

我们发现 make 函数跟上面的自定义的 NewPerson 函数很像,其实 make 函数就是 map 类型的工厂函数,它可以根据传递给它的键值对类型,创建不同类型的 map ,同时可以初始化 map 的大小。

make 函数不只是 map 类型的工厂函数,还是 chan、slice 的工厂函数。它同时可以用于 slice、chan 和 map 这三种类型的初始化。

  • new 函数只用于分配内存,且把内存清零,不太常用。
  • make 函数只用于 slice、chan 和 map 这三种内置类型的创建和初始化,因为这三种类型结构比较复杂,比如 slice 要提前初始化好内部元素的类型,slice 的长度和容量等。


相关文章
|
1天前
|
安全 Java Go
探索Go语言在高并发环境中的优势
在当今的技术环境中,高并发处理能力成为评估编程语言性能的关键因素之一。Go语言(Golang),作为Google开发的一种编程语言,以其独特的并发处理模型和高效的性能赢得了广泛关注。本文将深入探讨Go语言在高并发环境中的优势,尤其是其goroutine和channel机制如何简化并发编程,提升系统的响应速度和稳定性。通过具体的案例分析和性能对比,本文揭示了Go语言在实际应用中的高效性,并为开发者在选择合适技术栈时提供参考。
|
5天前
|
运维 Kubernetes Go
"解锁K8s二开新姿势!client-go:你不可不知的Go语言神器,让Kubernetes集群管理如虎添翼,秒变运维大神!"
【8月更文挑战第14天】随着云原生技术的发展,Kubernetes (K8s) 成为容器编排的首选。client-go作为K8s的官方Go语言客户端库,通过封装RESTful API,使开发者能便捷地管理集群资源,如Pods和服务。本文介绍client-go基本概念、使用方法及自定义操作。涵盖ClientSet、DynamicClient等客户端实现,以及lister、informer等组件,通过示例展示如何列出集群中的所有Pods。client-go的强大功能助力高效开发和运维。
22 1
|
5天前
|
SQL 关系型数据库 MySQL
Go语言中使用 sqlx 来操作 MySQL
Go语言因其高效的性能和简洁的语法而受到开发者们的欢迎。在开发过程中,数据库操作不可或缺。虽然Go的标准库提供了`database/sql`包支持数据库操作,但使用起来稍显复杂。为此,`sqlx`应运而生,作为`database/sql`的扩展库,它简化了许多常见的数据库任务。本文介绍如何使用`sqlx`包操作MySQL数据库,包括安装所需的包、连接数据库、创建表、插入/查询/更新/删除数据等操作,并展示了如何利用命名参数来进一步简化代码。通过`sqlx`,开发者可以更加高效且简洁地完成数据库交互任务。
13 1
|
5天前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
6天前
|
JSON 缓存 监控
go语言后端开发学习(五)——如何在项目中使用Viper来配置环境
Viper 是一个强大的 Go 语言配置管理库,适用于各类应用,包括 Twelve-Factor Apps。相比仅支持 `.ini` 格式的 `go-ini`,Viper 支持更多配置格式如 JSON、TOML、YAML
go语言后端开发学习(五)——如何在项目中使用Viper来配置环境
|
3天前
|
NoSQL Go Redis
Go语言中如何扫描Redis中大量的key
在Redis中,遍历大量键时直接使用`KEYS`命令会导致性能瓶颈,因为它会一次性返回所有匹配的键,可能阻塞Redis并影响服务稳定性。为解决此问题,Redis提供了`SCAN`命令来分批迭代键,避免一次性加载过多数据。本文通过两个Go语言示例演示如何使用`SCAN`命令:第一个示例展示了基本的手动迭代方式;第二个示例则利用`Iterator`简化迭代过程。这两种方法均有效地避免了`KEYS`命令的性能问题,并提高了遍历Redis键的效率。
9 0
|
4天前
|
监控 Serverless Go
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
|
4天前
|
关系型数据库 MySQL 数据库连接
Go语言中使用sqlx来操作事务
在应用中,数据库事务保证操作的ACID特性至关重要。`github.com/jmoiron/sqlx`简化了数据库操作。首先安装SQLX和MySQL驱动:`go get github.com/jmoiron/sqlx`和`go get github.com/go-sql-driver/mysql`。导入所需的包后,创建数据库连接并使用`Beginx()`方法开始事务。通过`tx.Commit()`提交或`tx.Rollback()`回滚事务以确保数据一致性和完整性。
8 0
|
6天前
|
SQL 安全 关系型数据库
Go 语言中的 MySQL 事务操作
在现代应用中,确保数据完整与一致至关重要。MySQL的事务机制提供了可靠保障。本文首先解释了事务的概念及其ACID特性,随后介绍了如何在Go语言中使用`database/sql`包进行MySQL事务操作。通过一个银行转账的例子,演示了如何通过Go开启事务、执行操作并在必要时回滚或提交,确保数据一致性。最后,还讨论了不同事务隔离级别的含义及如何在Go中设置这些级别。通过本文的学习,开发者能更好地掌握MySQL事务的应用。
11 0
|
7天前
|
SQL 关系型数据库 MySQL
Go语言中进行MySQL预处理和SQL注入防护
在现代Web应用开发中,安全性至关重要。SQL注入是一种常见的攻击方式,攻击者可通过构造特殊SQL查询来非法访问或修改数据库数据。本文介绍如何利用Go语言中的预处理SQL语句来防范此类攻击。预处理不仅能提升安全性,还能提高性能并简化代码。通过使用`?`作为占位符,Go自动处理参数转义,有效避免SQL注入。此外,文章还提供了连接MySQL数据库、执行预处理查询以及最佳实践的示例代码。务必遵循这些指导原则,确保应用程序的安全性。
16 0