GO语言-new()分配与构造和初始化结构

本文深入探讨了Go语言中new()函数的用途与结构体的初始化方式,通过五个示例详细解释了new()函数如何分配存储空间、初始化结构体成员以及返回地址的过程。结合具体代码实例,帮助开发者清晰理解Go语言中结构体初始化的多种方法。

GO语言-new()分配与构造和初始化结构

学习笔记

new()和make()他们做不同的事情,并适用于不同类型,(初学时很容易能会造成混淆)不好理解啊

new()它是个内部函数,本质上和其它语言的同类一样;

new(T)分配一块清零的存储空间给类型 T 的新项,并返回其地址,值类型为T

从字面上理解起来还不是很爽

下面写了五个例子,分别写了注释

/**
 * Created with IntelliJ IDEA.
 * User: liaojie
 * Date: 12-9-7
 * Time: 下午10:45
 * To change this template use File | Settings | File Templates.
 * Name:New()
 */
package main

import (
	"fmt"

)

//定义类型 Test为一个结构
//这个结构为0值
//这里相当如一个new(Test)分配一块清零的存储空间给类型Test
//他的作用将接收一个类型 *Test的值
type Test struct {
	fd      int
	name    string
	nepipe  int
}

//这里将返回一个值*Test(暂理解为指针值吧)
func NewFile(fd int, name string) *Test {
	if fd < 0 {
		return nil
	}
	f := Test{fd, name, 2}
	return &f
}


//有时零值是不够好的,初始化构造函数是必要的
//这个时候我们就在里面新构
func NewFile2(fd int, name string) *Test {
	if fd < 0 {
		return nil
	}
	f := new(Test)
	f.fd = fd
	f.name = name
	f.nepipe = 0
	return f
}

//注意:以上例子中很多注模。它是个每次求值即生成新实例的表达式。
//如NewFile中 f := Test{fd, name, 2} 和return &f 这样就产生了两次新实例(暂理解为使用了两个内存空间吧)
//变量对应的存储空间在函数返回后仍然存在。每取一个组合字面的地址求值时都生成一个新实例,因此我们可以把最后两行合起来。
func NewFile3(fd int, name string) *Test {
	if fd < 0 {
		return nil
	}
	//这样我们就只产生了一次新实例
	//组合字面的Key必须按顺序给出并全部出现
	return &Test{fd, name,3}
}

func NewFile4(fd int, name string) *Test {
	if fd < 0 {
		return nil
	}

	//如果明确使用   Key:value 对应元素,初始化可用任意顺序,未出现的Key对应着零值或空
	//此例中把name放前面了,也没有定义nepipe
	return &Test{name:name,fd:fd}
}

func NewFile5(fd int, name string) *Test {
	if fd < 0 {
		return nil
	}

	//注意,如果一个组合字面一个Key也没有,此类型将为零值。
	//他的结果 &Test{} 和new(Test)是一样的。
	return &Test{}
}

func main() {
	//调用NewFile 看上面的注释应该清楚了得到的结果因该就是最开始定义的Test
	//结果会是fd,name,nepipe
	newfile:= NewFile(1,"Fuck you Test")
	fmt.Printf("示例五:fd>%d | name>%s | nepipe>%d \n",newfile.fd,newfile.name,newfile.nepipe)

	//调用NewFile2
	newfile2:= NewFile2(2,"Fuck you Test2")
	fmt.Printf("示例五:fd>%d | name>%s | nepipe>%d \n",newfile2.fd,newfile2.name,newfile2.nepipe)

	//调用NewFile3
	newfile3:= NewFile3(3,"Fuck you Test3")
	fmt.Printf("示例五:fd>%d | name>%s | nepipe>%d \n",newfile3.fd,newfile3.name,newfile3.nepipe)

	//调用NewFile4
	newfile4:= NewFile4(4,"Fuck you Test4")
	fmt.Printf("示例五:fd>%d | name>%s | nepipe>%d \n",newfile4.fd,newfile4.name,newfile4.nepipe)

	//调用NewFile5
	newfile5:= NewFile5(5,"Fuck you Test5")
	fmt.Printf("示例五:fd>%d | name>%s | nepipe>%d \n",newfile5.fd,newfile5.name,newfile5.nepipe)
}

 

 

 

转载于:https://my.oschina.net/golang/blog/77538

<think>好的,我需要回答用户关于Go语言中makenew的区别的问题。首先,用户已经提供了一些站内引用,我需要参考这些资料,同时结合之前的回答来组织内容。 用户之前的问题是关于rune类型,现在转向了makenew的区别,所以我需要确保回答结构清晰,符合用户之前设定的格式要求,比如使用中文、正确LaTeX格式、相关问题生成等。 首先,我得明确makenew的基本区别。根据引用[2]引用[5],make用于slice、map、channel的初始化分配内存,返回的是类型本身;而new用于任何类型的内存分配,返回指针,且初始化为零值。需要详细说明两者的使用场景区别。 接下来,我需要整理两者的对比表格,类似之前回答中的runebyte的对比。同时,要给出代码示例,比如引用[3]引用[4]中的例子,说明newmake的不同用法。 还需要注意性能或使用时的注意事项,比如多次使用newmake时的效率问题,或者错误使用的例子。比如引用[3]中提到使用new创建切片后再make的情况,可能不如直接使用make高效。 此外,用户之前的回答中有技术特性、核心用途、使用场景建议等部分,我需要将makenew的区别按照类似的逻辑进行分点说明,比如定义用途、返回值、适用类型、初始化行为等。 最后,生成相关问题,确保问题覆盖用户可能关心的其他方面,比如如何选择使用哪个函数,或者常见错误等。同时,确保引用标记正确,根据提供的引用来源标注[^1]到[^5]。 需要检查LaTeX语法是否正确,比如行内公式用$...$,独立公式用$$...$$。例如,在讨论内存分配时,可以提到make初始化结构,而new返回指针,这里可能需要数学表达式来强调内存分配的不同。 可能还需要提及类型安全或错误处理的情况,比如错误使用new代替make会导致什么问题,如引用[3]中的例子,var p *[]int = new([]int)后*p是nil,需要再make才能使用,这在实际中不推荐。 总结回答结构,先概述,再分点详细对比,代码示例,使用场景,注意事项,最后相关问题。确保每个部分都有引用支持,并正确标注来源。</think>### Go语言 `make` `new` 的区别深度解析 --- #### 一、核心定义作用 1. **`new` 函数** - **功能**:为**任意类型**分配内存,返回**类型指针**(`*T`) - **行为特点**: -分配内存,不初始化(填充零值)[^2][^3] - 适用于值类型(如结构体、基本类型)引用类型(但需二次初始化- 语法示例: ```go p := new(int) // *int 类型指针,值为0 s := new([]int) // *[]int 类型指针,底层切片为nil ``` 2. **`make` 函数** - **功能**:为**特定引用类型**(`slice`、`map`、`channel`)分配初始化内存,返回**类型实例**(`T`) - **行为特点**: - 同时完成内存分配数据结构初始化[^2][^5] - 仅适用于三种内置引用类型 - 语法示例: ```go sl := make([]int, 5) // 初始化长度为5的切片 m := make(map[string]int) // 初始化空的map ch := make(chan int) // 初始化无缓冲通道 ``` --- #### 二、关键差异对比 | 特性 | `new` | `make` | |---------------------|--------------------------------|---------------------------------| | **返回值类型** | 指针(`*T`) | 实例(`T`) | | **适用类型** | 所有类型 | 仅 `slice`、`map`、`channel` | | **初始化行为** | 零值填充(如 `nil`、`0`) | 完整初始化数据结构 | | **内存分配位置** | 堆内存 | 堆内存(但通过运行时优化管理) | | **典型使用场景** | 创建值类型指针 | 创建引用类型实例 | --- #### 三、代码实例解析 1. **`new` 的陷阱正确用法** ```go // 错误示例:直接使用new创建切片 p := new([]int) *p = append(*p, 1) // 运行时panic(底层数组未分配) // 正确用法:new+显式初始化 p := new([]int) *p = make([]int, 0, 10) // 需要二次初始化[^3] ``` 2. **`make` 的初始化优势** ```go // 直接创建可用的切片 s := make([]int, 3, 5) // len=3, cap=5, 元素初始化为0 m := make(map[string]int) // 空map可立即操作 m["key"] = 1 // 安全操作 ``` 3. **内存布局验证** ```go // new返回指针的验证 p := new(int) fmt.Printf("%T, %v\n", p, *p) // 输出: *int, 0 // make返回实例的验证 s := make([]int, 2) fmt.Printf("%T, %v\n", s, s) // 输出: []int, [0 0] ``` --- #### 四、底层实现差异 1. **`new` 的编译过程** - 调用 `runtime.newobject` 函数分配内存 - 返回指针的数学表达: $$ \text{指针地址} = \text{堆内存分配}( \text{sizeof}(T) ) $$ 2. **`make` 的运行时逻辑** - 对不同类型的特殊处理(以切片为例): ```go // 伪代码表示切片make的底层操作 func makeslice(et *_type, len, cap int) unsafe.Pointer { mem := mallocgc(et.size*uintptr(cap), et, true) return mem } ``` - 直接构造完整数据结构(如map的哈希表桶、channel的环形缓冲区) --- #### 五、使用场景建议 | 场景类型 | 推荐方案 | 示例 | |-------------------------|-----------------|-------------------------------| | 创建值类型指针 | `new` | 结构体指针传递 | | 初始化切片/映射/通道 | `make` | 并发编程中的channel创建 | | 零值可用的类型 | `new` | `sync.Mutex` 指针初始化 | | 需要预设容量的数据结构 | `make` | 高性能预分配切片 | --- #### 六、常见错误优化 1. **错误混用示例** ```go // 尝试用new创建map(不可直接操作) m := new(map[string]int) (*m)["key"] = 1 // panic: assignment to nil map // 正确方式 m := make(map[string]int) m["key"] = 1 ``` 2. **性能优化技巧** - **预分配容量**减少扩容开销: ```go // 使用make预设切片容量 s := make([]int, 0, 1000) // 避免append时多次扩容 ``` - **避免不必要的指针解引用**: ```go // 低效写法 p := new(struct{ x int }) (*p).x = 10 // 高效写法(直接使用值类型) var v struct{ x int } v.x = 10 ``` --- ### 关键结论 1. `new` 返回指针且不初始化数据结构,适合值类型指针创建[^2][^4] 2. `make` 直接返回初始化后的引用类型实例,是 `slice/map/channel` 的标准创建方式[^3][^5] 3. 混用两者会导致冗余操作(如 `new` 后接 `make`),应优先使用类型匹配的函数[^3] --- ### 相关问题 1. 为什么 `make` 不能用于创建结构体实例? 2. 如何选择 `new` 直接变量声明(如 `var v T`)? 3. `make` 初始化切片时,`len` `cap` 参数有什么作用? 4. 在并发编程中,`new` `make` 的使用有哪些注意事项? [^1]: 基础内存分配概念 [^2]: 核心区别总结 [^3]: 实际应用示例 [^4]: 类型返回值验证 : 特定类型初始化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值