一行 make([][][][]int, 3),Go 到底造出了什么?
在 Go 里创建一个 3 行 4 列的二维切片,标准写法是这样的:
1 | rows, cols := 3, 4 |
最让人困惑的往往是第一行:make([][]int, rows) 到底造了什么?为什么不能一步到位,非要再写个循环?
问一:[][]int 应该怎么读?
把它拆成两半:
1 | [] []int |
前面的 [] 表示“这是一个切片”,后面的 []int 是“每个元素的类型”。
所以 [][]int 读作元素为 []int 的切片。Go 里其实没有真正的“二维切片”,只有“切片套切片”。
问二:make([][]int, 3) 的结果是什么?
先看熟悉的写法。make([]T, n) 会造一个长度为 n 的切片,每个元素都是 T 类型的零值。
| 写法 | 元素类型 | 零值 | 结果 |
|---|---|---|---|
make([]int, 3) |
int |
0 |
[0 0 0] |
make([]string, 3) |
string |
"" |
["" "" ""] |
make([]bool, 3) |
bool |
false |
[false false false] |
make([][]int, 3) |
[]int |
nil |
[nil nil nil] |
最后一行和前三行的规则一样,只是 T 换成了 []int,而 []int 的零值是 nil。执行完这一句,内存里是这样的:

可以验证一下:
1 | grid := make([][]int, 3) |
也就是说,这一句只定下了“有 3 行”,没说每行有几列。
问三:后面的循环在做什么?
给每一行单独分配 4 个 int,再把这一行挂到 grid[i] 上。
i = 0 时:

i = 1 时:

i = 2 时:

有两点要注意:
- 外层格子里存的是指向那一行的引用(指针、长度、容量),数据本身在那一行里。
- 每一行都是单独 make 出来的,是各自独立的内存,互不影响。
问四:如果漏掉循环会怎样?
访问 grid[1][2] 时要走两跳:

如果没有循环,grid[1] 还是 nil,长度为 0,第二跳就会直接 panic:
1 | panic: runtime error: index out of range [2] with length 0 |

打个比方:make([][]int, 3) 只搭好了 3 层空书架,循环里的 make([]int, 4) 才是往每一层放进一个装得下 4 本书的盒子。只搭了隔板就去拿“第 1 层第 2 本书”,当然拿不到。
问五:每一行必须一样长吗?
不必。每一行都是单独 make 出来的切片,长度可以不同,内存也不一定连续。这种结构通常叫“锯齿数组”,杨辉三角就很适合用它来存:
1 | tri := make([][]int, 4) |
1 | tri[0] ──▶ [1] |
一句话总结
make([][]int, 3) 只造出了 3 个空的行引用,循环里的 make([]int, 4) 才真正给每一行分配了空间。
切片只是指向底层数组的窗口,二维切片就是一排这样的窗口,每个窗口通向自己的一行。