Xiaochat : este artículo es un resumen del
golang
primer , la comparación de características y la discusión basada en el aprendizaje y la comprensión básicos, ¿cuál es la diferencia entre las matrices de go? ¿Cuál es el beneficio de cortar? ¿Cómo distinguir su uso? Agregar algo de expansión del conocimiento ayudará a profundizar la comprensióngo
de yArray
, ¡léelo!Slice
Tabla de contenido
- 1. ir matriz matriz
- 2. ve a rebanar rebanar
- 3. La diferencia entre el uso de matrices y cortes
- 4. Ampliar
1. ir matriz matriz
1.1 Definición de go Array
- definición básica
var arr1 [3]int = [3]int{
1, 2, 3}
var arr2 = [3]int{
1, 2, 3} // 可省去前面[3]int,var会自动识别类型
arr3 := [3]int{
1, 2, 3} //局部定义(函数内)
arr4 := [...]int{
1, 2, 3} //初始化后会自动计算数组长度并给它,所以无区别
arr5 := [...]int{
1: 1, 5: 5} //指定索引序号初始化赋值,其它索引处是int的默认值0
arr6 := [...]struct {
name string
}{
{
name: "Alice"}} //结构体数组,也必须在初始化时赋予初始,可省略属性名name(注意,这是一个匿名结构体)
var arr7 *[3]int = &[3]int{
} // 数组指针
var arr8 [3]*int = [3]*int{
} // 指针数组
- Matrices multidimensionales
arr := [2][2]int{
{
1, 2}, {
3, 4}}
brr := [...][2]int{
{
1, 2}, {
3, 4}, {
5, 6}}
1.2 Cosas interesantes sobre go Array
(1) La matrizgo
generalmente se usa de la misma manera que otros lenguajes, pero hay una gran diferencia en la definición del tipo: la longitud de la inicialización de la matriz también forma parte del tipo de matriz. Array
Por ejemplo, var arr [2]int
y var brr [3]int
son de diferentes tipos.
package main
import "fmt"
func main() {
var arr [2]int = [2]int{
}
var brr [3]int = [3]int{
}
// 输出他们的类型
fmt.Printf("arr: %T\n", arr) // arr: [2]int
fmt.Printf("brr: %T\n", brr) // brr: [3]int
}
(2) go
Admite el uso de ==
y !=
para comparar dos matrices. Sabemos que go
una matriz es un tipo de valor y ya es una secuencia de datos de longitud fija cuando se inicializa y se almacena en la memoria. Por lo tanto, go
también Println
generar directamente el valor de toda la matriz sin atravesar. En este punto, es fácil de entender si se puede comparar directamente. Por supuesto, no se pueden comparar diferentes tipos y se informará un error al compilar, como var a [2]int
yvar d [3]int
package main
import "fmt"
func main() {
a := [2]int{
1, 2}
b := [2]int{
1, 2}
c := [2]int{
3, 4}
// d := [3]int{1, 2}
if a == b {
fmt.Println("a == b") // 输出:a == b
} else {
fmt.Println("a != b")
}
if a == c {
fmt.Println("a == c")
} else {
fmt.Println("a != c") // 输出:a != c
}
// 编译报错
// if a == d {
// fmt.Println("a == d")
// } else {
// fmt.Println("a != d")
// }
}
2. ve a rebanar rebanar
2.1 Definición de go Array
- definición básica
package main
import "fmt"
func main() {
// 创建切片方式
s1 := []int{
}
fmt.Printf("s1: %v, 类型: %T\n", s1, &s1) // s1: [], 类型: *[]int
var s2 []int = make([]int, 2)
fmt.Printf("s2: %v, len: %v, cap: %v\n", s2, len(s2), cap(s2)) // s2: [0 0], len: 2, cap: 2
var s3 []int = make([]int, 2, 4)
fmt.Printf("s3: %v, len: %v, cap: %v\n", s3, len(s3), cap(s3)) // s3: [0 0], len: 2, cap: 4
// 利用数组进行切片初始化
arr := [...]int{
0, 1, 2, 3, 4}
var s4 []int = arr[0:2]
fmt.Printf("s4: %v\n", s4) //s4: [0 1]
var s5 []int = arr[:2]
fmt.Printf("s5: %v\n", s5) //s5: [0 1]
var s6 []int = arr[2:]
fmt.Printf("s6: %v\n", s6) //s6: [2 3 4]
var s7 []int = arr[:]
fmt.Printf("s7: %v\n", s7) //s7: [0 1 2 3 4]
}
Operaciones que inicializan segmentos en una matriz:
funcionar | significado |
---|---|
var s int[] := arr[n] | artículo en el índice n en el segmento s |
var s int[] := arr[:] | El segmento obtenido de las posiciones de índice 0 a len(s)-1 del segmento s |
var s int[] := arr[bajo:] | El segmento obtenido desde la posición de índice inferior del segmento s hasta len(s)-1 |
var s int[] := arr[:alto] | Rebanada obtenida desde la posición de índice О hasta el alto de la rebanada s, len=alto |
var s int[] := arr[bajo:alto] | El segmento obtenido desde la posición del índice de menor a mayor del segmento s, len=alto-bajo |
var s int[] := arr[bajo:alto:max] | Segmento obtenido desde la posición de índice inferior a la superior del segmento s, len=high-low, cap=max-low |
- matriz de rebanadas
package main
import "fmt"
func main() {
s8 := make([][]int, 2, 4)
fmt.Printf("s8: %v, len: %v, cap: %v\n", s8, len(s8), cap(s8)) // s8: [[] []], len: 2, cap: 4
s9 := [][]int{
[]int{
1, 2},
[]int{
3, 4},
}
fmt.Printf("s9: %v, len: %v, cap: %v\n", s9, len(s9), cap(s9)) // s9: [[1 2] [3 4]], len: 2, cap: 2
}
2.2 Aspectos interesantes de go Slice
Debido a que slice es
go
una definición única , el contenido será más detallado. Primero lo presentaremos y luego hablaremos sobre cómo usarlo. Por cierto, hablaremos sobre el principio y su relación con las matrices.Todo el mundo debería tener una breve comprensión de la división, aquí hay un resumen.
Un segmento es similar a una matriz y se puede usar como una matriz, con la diferencia más obvia: es una matriz dinámica queappend
puede crecer automáticamente cuando se agregan elementos. Oye, algunos estudiantes dirán que esto es similar a la colección de Java y la lista de Python. De hecho, el lenguaje tiene puntos en común, pero aún tiene sus características.
- (1)
Slice
Asigne la longitud y la capacidad de acuerdo con la estrategia interna y luego implemente el esquema de longitud variable, de modo que la porción se pueda usar como una matriz variable.
s := []int{
1, 2}
fmt.Printf("s: %v, len: %v, cap: %v\n", s, len(s), cap(s)) // s: [1 2], len: 2, cap: 2
s = append(s, 3)
fmt.Printf("s: %v, len: %v, cap: %v\n", s, len(s), cap(s)) // s: [1 2 3], len: 3, cap: 4
- (2) Un segmento es un tipo de referencia, no un tipo de valor, es una referencia
Array
a . Esto es muy importante, sobre sus propias características y conexiónArray
con .
¿Qué es un tipo de referencia, que es similar a un puntero pero no un puntero.
c++
Es fácil , es decir, puede operar matrices de una manera similar a los punteros. ¿Por qué es una matriz? Porque la capa inferior es una matriz cuando se inicializa el segmento. Entonces, cuando definimos un segmento, podemos definir el tipo de segmento sobre la base de un arreglo, y la operación real de este segmento es ese arreglo, porque un segmento es un tipo de referencia, que se refiere a este arreglo. Si queremos modificar los elementos en este segmento en este momento, afectará el contenido de la matriz, porque sus direcciones físicas de memoria son las mismas. Por supuesto, el segmento que creamosmake
en el camino es una nueva matriz. Sin embargo, todavía existen restricciones sobre la inicialización de segmentos en matrices de longitud fija, que se presentarán más adelante.[Haga clic aquí: ¿Qué es un tipo de referencia]
package main
import "fmt"
func main() {
// 我们举个数组初始化的例子
arr := [3]int{
1, 1, 1}
fmt.Printf("初始数组arr : %v\n", arr) // 初始数组arr : [1 1 1]
s := arr[:]
fmt.Printf("初始化后的切片s2 : %v\n", s) // 初始化后的切片s2 : [1 1 1]
s[0] = 2
fmt.Printf("修改后的切片s2 : %v\n", arr) // 修改后的切片s2 : [2 1 1]
fmt.Printf("修改后的数组arr : %v\n", arr) // 修改后的数组arr : [2 1 1]
// 然而我们再来打印索引为0的地址确认一下:
fmt.Printf("数组arr索引为0的地址: %p\n", &arr[0]) // 数组arr索引为0的地址: 0xc000016150
fmt.Printf("切片s2索引为0的地址: %p\n", &s[0]) // 切片s2索引为0的地址: 0xc000016150
}
- Los parámetros de la definición de sector son
[]type
,len
ycap
. Por ejemplo:var s []int = make([]int, 2, 4)
.
Si no se
cap
establece , el valor predeterminadolen
es el mismo que el valor de . Con respecto a la relación entre la longitudlen
, la capacidadcap
y la estrategia de alargamiento del código fuente, en una oración: cuandoappend
la longitud del elemento de segmento excedecap
, la expansión se activará. El código fuente del mecanismo de expansión explica la situación normal: cuando la la capacidad total actual es inferior a 1024, se activa la expansión única. Aumente el tamaño del límite actual en 1. Cuando supere los 1024, la expansión desencadenante aumentará elcap
tamaño
package main
import "fmt"
func main() {
s := make([]int, 2)
fmt.Println("初始切片情况:")
fmt.Printf("val: %v\nlen(s): %v,cap(s): %v\n", s, len(s), cap(s)) // len(s): 2,cap(s): 2
fmt.Println("第一次扩容:")
s = append(s, 100, 200)
fmt.Printf("val: %v\nlen(s): %v,cap(s): %v\n", s, len(s), cap(s)) // len(s): 4,cap(s): 4
fmt.Println("第二次扩容:")
s = append(s, 300, 400)
fmt.Printf("val: %v\nlen(s): %v,cap(s): %v\n", s, len(s), cap(s)) // len(s): 6,cap(s): 8
fmt.Println("第三次扩容:")
s = append(s, 500, 600, 700)
fmt.Printf("val: %v\nlen(s): %v,cap(s): %v\n", s, len(s), cap(s)) // len(s): 9,cap(s): 16
}
- Al inicializar un segmento basado en una matriz, no se puede exceder el límite de longitud fija de la matriz original. Es decir
0 <= len(slice) <= len(array)
, ¿dóndearray
estáslice
la matriz a la que hace referencia .
Con el conocimiento anterior, ya sabemos que para inicializar el segmento sobre la base del arreglo, el segmento es la referencia del arreglo original y comparten los datos de la operación.
Entonces habrá una pregunta de este tipo: mi teoría de corte puede expandirse y crecer infinitamente, pero la matriz no es variable, entonces, ¿cuál es el rango de longitud al inicializar la matriz? ¿Los segmentos estarán limitados por la longitud fija de la matriz al agregar datos?
La respuesta es:len
la longitud máxima es la longitud de la matriz; la expansión del sector no estará limitada, y el área expandida es un espacio recién abierto, que es utilizado exclusivamente por el sector. La forma de usara := []int{}
oa := make([]int, 0)
es normal, porque crean una nueva matriz subyacente sin inicializar un límite de longitud fijo.
package main
import "fmt"
func main() {
arr := [4]int{
1, 2}
fmt.Printf("数组arr:val: %v,len(s): %v,cap(s): %v\n", arr, len(arr), cap(arr))
var s []int = arr[:]
fmt.Printf("切片s:val: %v,len(s): %v,cap(s): %v\n", s, len(s), cap(s))
s = append(s, 3, 4)
fmt.Printf("扩容后:切片s:val: %v,len(s): %v,cap(s): %v\n", s, len(s), cap(s))
fmt.Printf("扩容后:数组arr:val: %v,len(s): %v,cap(s): %v\n", arr, len(arr), cap(arr))
}
// 输出
数组arr:val: [1 2 0 0],len(s): 4,cap(s): 4
切片s:val: [1 2 0 0],len(s): 4,cap(s): 4
扩容后:切片s:val: [1 2 0 0 3 4],len(s): 6,cap(s): 8
扩容后:数组arr:val: [1 2 0 0],len(s): 4,cap(s): 4
3. La diferencia entre el uso de matrices y cortes
A través de la combinación de conocimientos anterior, cuando se trata de la diferencia, casi puede pensar y comprender las siguientes preguntas y ejemplos.
- La asignación entre matrices es para copiar los datos completos de la matriz, los sectores y matrices o sectores y sectores son referencias de copia, no copian los datos completos ;
Java
las asignaciones de matriz son referencias de copia
package main
import "fmt"
func main() {
arr := [3]int{
1, 2, 3}
brr := arr
fmt.Printf("arr[0]的地址: %p\n", &arr[0]) // arr[0]的地址: 0xc000016150
fmt.Printf("brr[0]的地址: %p\n", &brr[0]) // brr[0]的地址: 0xc000016168
slice1 := make([]int, 5)
slice2 := slice1
fmt.Printf("slice1[0]的地址: %p\n", &slice1[0]) // slice1[0]的地址: 0xc000010480
fmt.Printf("slice2[0]的地址: %p\n", &slice2[0]) // slice2[0]的地址: 0xc000010480
}
- De manera similar, cuando una función pasa parámetros, la matriz se pasa por valor y el segmento se pasa por referencia, por lo que en la mayoría de los casos, el uso de segmentos (por supuesto, los punteros también están bien) ahorrará mucho consumo de memoria.
package main
import "fmt"
func change1(arr [5]int) [5]int {
for i := 0; i < 5; i++ {
arr[i] = i
}
return arr
}
func change2(s []int) []int {
for i := 0; i < 5; i++ {
s[i] = i
}
return s
}
func main() {
arr := [5]int{
}
slice := make([]int, 5)
fmt.Printf("初始值:arr: %v\n", arr) // 初始值:arr: [0 0 0 0 0]
fmt.Printf("初始值:slice: %v\n", slice) // 初始值:slice: [0 0 0 0 0]
change1(arr)
change2(slice)
fmt.Printf("修改后值:arr: %v\n", arr) // 修改后值:arr: [0 0 0 0 0]
fmt.Printf("修改后值:slice: %v\n", slice) // 修改后值:slice: [0 1 2 3 4]
}
expandir
4.1 Comprensión simple de las referencias
Definición: un tipo de referencia es un tipo de datos representado por una referencia de valor real (similar a un puntero) del tipo. Si asigna a una variable un tipo de referencia), la variable se referirá (o "apuntará a") al valor original. No se crean copias. Los tipos de referencia incluyen clases, interfaces, delegados y tipos de valores en caja.
Debido a que el puntero en sí también es un tipo de referencia, el puntero original y la referencia se pueden combinar para la discusión. Sin embargo, debido a que la referencia protege los detalles de implementación, el programador no necesariamente conoce la operación de la referencia, qué parte de la función es específica y hay más situaciones inesperadas que deben señalarse que el puntero transparente.
Permítanme dar un ejemplo simple: "En una clase, cuando el maestro quiere que un alumno se levante y responda una pregunta, ¿cómo pueden todos saber qué compañero es? Uno: llamarlo por su nombre; dos: señalarlo con un puntero. Entonces
, todos los estudiantes son datos de memoria, el nombre aquí es la guía y el puntero es el puntero. La diferencia es que cuando quiero operar la memoria, la referencia es el nombre dado a la memoria de destino durante la inicialización. Es fijo, y el puntero apunta a la ubicación física de los datos de la memoria. Donde sea que apunte, significa los datos para ser accedido, pero el puntero puede moverse, apuntar a diferentes direcciones de memoria para obtener diferentes datos, pero las referencias no pueden, al igual que el nombre, se fija cuando se inicializa cuando nace. Pero sus funciones son similares, es decir, para encontrar los datos de destino. 【Haga clic aquí para volver atrás】
4.2 Paso de valor Go, paso de referencia y paso de puntero
Tome el paso de parámetros del método de llamada como ejemplo:
Transferencia de valor : se copia el valor y se modifica el valor del parámetro formal sin afectar el valor original.
Transferencia por referencia : la información referenciada se copia, pero los datos de la memoria subyacente no se copian y se operan los mismos datos.
Paso de puntero : se copia la dirección física a la que apunta el puntero y se manipula el valor de la misma dirección física.
Nota: dado que Go no permite la aritmética en los punteros, no hay posibilidad de cambiar accidentalmente un puntero. Y si se asigna un nuevo valor al puntero, las modificaciones posteriores, por supuesto, ya no afectarán el valor señalado por el valor anterior. Dado que el mecanismo del puntero es transparente, esto es fácil de entender.