Una comprensión completa y breve de go array Array y slice Slice

Xiaochat : este artículo es un resumen del golangprimer , 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ón gode yArray , ¡léelo!Slice

Tabla de contenido



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. ArrayPor ejemplo, var arr [2]inty var brr [3]intson 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) goAdmite el uso de ==y !=para comparar dos matrices. Sabemos que gouna 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, gotambién Printlngenerar 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]intyvar 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 gouna 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 que appendpuede 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) SliceAsigne 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 Arraya . Esto es muy importante, sobre sus propias características y conexión Arraycon .

¿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 creamos makeen 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, leny cap. Por ejemplo: var s []int = make([]int, 2, 4).

Si no se capestablece , el valor predeterminado lenes el mismo que el valor de . Con respecto a la relación entre la longitud len, la capacidad capy la estrategia de alargamiento del código fuente, en una oración: cuando appendla longitud del elemento de segmento excede cap, 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á el captamañ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ónde arrayestá slicela 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: lenla 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 usar a := []int{}o a := 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): 4cap(s): 4
切片s:val: [1 2 0 0]len(s): 4cap(s): 4
扩容后:切片s:val: [1 2 0 0 3 4]len(s): 6cap(s): 8
扩容后:数组arr:val: [1 2 0 0]len(s): 4cap(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 ; Javalas 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.



ensayo

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/m0_48489737/article/details/127178400
Recomendado
Clasificación