golang内幕之数组

func Array_1() {
	var arr1 [3]int
	arr1[0] = 1
	arr1[1] = 2
	arr1[2] = 3
	fmt.Fprintf(os.Stdout, "%T - %v\n", arr1, arr1)

	arr2 := [3]int{
		1,
		2,
		3,
	}
	fmt.Fprintf(os.Stdout, "%T - %v\n", arr2, arr2)

	arr3 := [3]int {1,2,3}
	fmt.Fprintf(os.Stdout, "%T - %v\n", arr3, arr3)

	arr4 := [3]int {
		1,
		2,
		3}
	fmt.Fprintf(os.Stdout, "%T - %v\n", arr4, arr4)
}

数组定义如上,注意逗号的使用。

下面看看数组是如何分配内存的:

func Array_1() {
	arr2 := [3]int{
		1,
		2,
		3,
	}
	arr2[0] = 4
}
	0x0000 00000 (array.go:3)	TEXT	"".Array_1(SB), NOSPLIT|ABIInternal, $32-0
	0x0000 00000 (array.go:3)	SUBQ	$32, SP
	0x0004 00004 (array.go:3)	MOVQ	BP, 24(SP)
	0x0009 00009 (array.go:3)	LEAQ	24(SP), BP
    ...
	0x000e 00014 (array.go:4)	MOVQ	$0, "".arr2(SP)
	0x0016 00022 (array.go:4)	XORPS	X0, X0
	0x0019 00025 (array.go:4)	MOVUPS	X0, "".arr2+8(SP)
	0x001e 00030 (array.go:5)	MOVQ	$1, "".arr2(SP)
	0x0026 00038 (array.go:6)	MOVQ	$2, "".arr2+8(SP)
	0x002f 00047 (array.go:7)	MOVQ	$3, "".arr2+16(SP)
	0x0038 00056 (array.go:9)	MOVQ	$0, "".arr2(SP)
	0x0040 00064 (array.go:10)	MOVQ	24(SP), BP
	0x0045 00069 (array.go:10)	ADDQ	$32, SP
	0x0049 00073 (array.go:10)	RET

这里的array没涉及到动态分配内存,使用的只有栈空间,这个因为golang发现array只有在本函数内使用,不涉及传递。

func Array_1() {
	arr2 := [3]int{
		1,
		2,
		3,
	}
	Array_2(arr2)
}

func Array_2(arr [3]int) {
	fmt.Println(arr)
}
"".Array_1 STEXT size=128 args=0x0 locals=0x38
	0x0000 00000 (array.go:5)	TEXT	"".Array_1(SB), ABIInternal, $56-0
	0x0000 00000 (array.go:5)	MOVQ	TLS, CX
	0x0009 00009 (array.go:5)	MOVQ	(CX)(TLS*2), CX
	0x0010 00016 (array.go:5)	CMPQ	SP, 16(CX)
	0x0014 00020 (array.go:5)	JLS	121
	0x0016 00022 (array.go:5)	SUBQ	$56, SP
	0x001a 00026 (array.go:5)	MOVQ	BP, 48(SP)
	0x001f 00031 (array.go:5)	LEAQ	48(SP), BP
    ...
	0x0024 00036 (array.go:6)	PCDATA	$2, $0
	0x0024 00036 (array.go:6)	PCDATA	$0, $0
	0x0024 00036 (array.go:6)	MOVQ	$0, "".arr2+24(SP)
	0x002d 00045 (array.go:6)	XORPS	X0, X0
	0x0030 00048 (array.go:6)	MOVUPS	X0, "".arr2+32(SP)
	0x0035 00053 (array.go:7)	MOVQ	$1, "".arr2+24(SP)
	0x003e 00062 (array.go:8)	MOVQ	$2, "".arr2+32(SP)
	0x0047 00071 (array.go:9)	MOVQ	$3, "".arr2+40(SP)
	0x0050 00080 (array.go:11)	MOVQ	$1, (SP)
	0x0058 00088 (array.go:11)	MOVQ	$2, 8(SP)
	0x0061 00097 (array.go:11)	MOVQ	$3, 16(SP)
	0x006a 00106 (array.go:11)	CALL	"".Array_2(SB)
	0x006f 00111 (array.go:12)	MOVQ	48(SP), BP
	0x0074 00116 (array.go:12)	ADDQ	$56, SP
	0x0078 00120 (array.go:12)	RET

这会还没看到动态分配内存,但我么发现,调用Array_2也因此没有传递数组arr2的地址过去,而是把arr2的值传递过去了。

从这个例子可以看出,数组作为函数参数传递,是按照值传递方式传递的。

"".Array_2 STEXT size=273 args=0x18 locals=0x90
	0x0000 00000 (array.go:14)	TEXT	"".Array_2(SB), ABIInternal, $144-24
	0x0000 00000 (array.go:14)	MOVQ	TLS, CX
	0x0009 00009 (array.go:14)	MOVQ	(CX)(TLS*2), CX
	0x0010 00016 (array.go:14)	LEAQ	-16(SP), AX
	0x0015 00021 (array.go:14)	CMPQ	AX, 16(CX)
	0x0019 00025 (array.go:14)	JLS	263
	0x001f 00031 (array.go:14)	SUBQ	$144, SP
	0x0026 00038 (array.go:14)	MOVQ	BP, 136(SP)
	0x002e 00046 (array.go:14)	LEAQ	136(SP), BP
    ...
	0x0036 00054 (array.go:15)	MOVQ	"".arr+152(SP), AX
	0x003e 00062 (array.go:15)	MOVQ	AX, ""..autotmp_2+48(SP)
	0x0043 00067 (array.go:15)	MOVUPS	"".arr+160(SP), X0
	0x004b 00075 (array.go:15)	MOVUPS	X0, ""..autotmp_2+56(SP)
	0x0050 00080 (array.go:15)	PCDATA	$0, $1
	0x0050 00080 (array.go:15)	XORPS	X0, X0
	0x0053 00083 (array.go:15)	MOVUPS	X0, ""..autotmp_1+96(SP)
	0x0058 00088 (array.go:15)	PCDATA	$2, $1
	0x0058 00088 (array.go:15)	PCDATA	$0, $0
	0x0058 00088 (array.go:15)	LEAQ	""..autotmp_1+96(SP), AX
	0x005d 00093 (array.go:15)	PCDATA	$2, $0
	0x005d 00093 (array.go:15)	PCDATA	$0, $2
	0x005d 00093 (array.go:15)	MOVQ	AX, ""..autotmp_4+72(SP)
	0x0062 00098 (array.go:15)	PCDATA	$2, $1
	0x0062 00098 (array.go:15)	LEAQ	type.[3]int(SB), AX
	0x0069 00105 (array.go:15)	PCDATA	$2, $0
	0x0069 00105 (array.go:15)	MOVQ	AX, (SP)
	0x006d 00109 (array.go:15)	PCDATA	$2, $1
	0x006d 00109 (array.go:15)	LEAQ	""..autotmp_2+48(SP), AX
	0x0072 00114 (array.go:15)	PCDATA	$2, $0
	0x0072 00114 (array.go:15)	MOVQ	AX, 8(SP)
	0x0077 00119 (array.go:15)	CALL	runtime.convT2Enoptr(SB)
	0x007c 00124 (array.go:15)	MOVQ	16(SP), AX
	0x0081 00129 (array.go:15)	PCDATA	$2, $2
	0x0081 00129 (array.go:15)	MOVQ	24(SP), CX
	0x0086 00134 (array.go:15)	MOVQ	AX, ""..autotmp_5+80(SP)
	0x008b 00139 (array.go:15)	MOVQ	CX, ""..autotmp_5+88(SP)
	0x0090 00144 (array.go:15)	PCDATA	$2, $3
	0x0090 00144 (array.go:15)	MOVQ	""..autotmp_4+72(SP), DX
	0x0095 00149 (array.go:15)	TESTB	AL, (DX)
	0x0097 00151 (array.go:15)	MOVQ	AX, (DX)
	0x009a 00154 (array.go:15)	PCDATA	$2, $4
	0x009a 00154 (array.go:15)	LEAQ	8(DX), DI
	...
	0x009e 00158 (array.go:15)	CMPL	runtime.writeBarrier(SB), $0
	0x00a5 00165 (array.go:15)	JEQ	169
	0x00a7 00167 (array.go:15)	JMP	253
	0x00a9 00169 (array.go:15)	MOVQ	CX, 8(DX)
	0x00ad 00173 (array.go:15)	JMP	175
	0x00af 00175 (array.go:15)	PCDATA	$2, $1
	0x00af 00175 (array.go:15)	PCDATA	$0, $0
	0x00af 00175 (array.go:15)	MOVQ	""..autotmp_4+72(SP), AX
	0x00b4 00180 (array.go:15)	TESTB	AL, (AX)
	0x00b6 00182 (array.go:15)	JMP	184
	0x00b8 00184 (array.go:15)	MOVQ	AX, ""..autotmp_3+112(SP)
	0x00bd 00189 (array.go:15)	MOVQ	$1, ""..autotmp_3+120(SP)
	0x00c6 00198 (array.go:15)	MOVQ	$1, ""..autotmp_3+128(SP)
	0x00d2 00210 (array.go:15)	PCDATA	$2, $0
	0x00d2 00210 (array.go:15)	MOVQ	AX, (SP)
	0x00d6 00214 (array.go:15)	MOVQ	$1, 8(SP)
	0x00df 00223 (array.go:15)	MOVQ	$1, 16(SP)
	0x00e8 00232 (array.go:15)	CALL	fmt.Println(SB)
	0x00ed 00237 (array.go:16)	MOVQ	136(SP), BP
	0x00f5 00245 (array.go:16)	ADDQ	$144, SP
	0x00fc 00252 (array.go:16)	RET
LEAQ    type.[3]int(SB), AX
runtime.convT2Enoptr(SB)
func convT2Enoptr(t *_type, elem unsafe.Pointer) (e eface) {
    ...
	x := mallocgc(t.size, t, false)
	memmove(x, elem, t.size)
	e._type = t
	e.data = x
	return
}

这里我们终于看到涉及到动态内存分配了,也可以看到动态分配内存的array的底层数据结构是eface。

我们写段代码验证一下数组是值传递的:

func Array_1() {
	arr2 := [3]int{
		1,
		2,
		3,
	}
	fmt.Println(arr2)
	Array_2(arr2)
	fmt.Println(arr2)
}

func Array_2(arr [3]int) {
	arr[0] =  4
	arr[1] =  5
	arr[2] =  6
}
=== RUN   TestArray_1
[1 2 3]
[1 2 3]
--- PASS: TestArray_1 (0.00s)
PASS

确实,在Array_2中改变Array_1传入的数组的值,但其实没改,说明数组是值传递。

如果我们确实想修改arr2的值呢?

func Array_1() {
	arr2 := [3]int{
		1,
		2,
		3,
	}
	fmt.Println(arr2)
	Array_2(&arr2)
	fmt.Println(arr2)
}

func Array_2(arr *[3]int) {
	arr[0] =  4
	arr[1] =  5
	arr[2] =  6
}
=== RUN   TestArray_1
[1 2 3]
[4 5 6]
--- PASS: TestArray_1 (0.00s)
PASS

按照指针传递,果然修改成功了。

再看看len, cap:

func Array_1() {
	arr2 := [3]int{
		1,
		2,
		3,
	}
	fmt.Println(arr2, len(arr2), cap(arr2))
}
=== RUN   TestArray_1
[1 2 3] 3 3
--- PASS: TestArray_1 (0.00s)
PASS

数组是编译期间就确定大小的,因此cap和len都是编译时的固定值。

 

 

你可能感兴趣的:(golang内幕)