Arreglos y slices de Zig

En el lenguaje de programación Zig, los arreglos y los slices son estructuras básicas utilizadas para almacenar y manipular un conjunto de datos del mismo tipo.

  • Arreglo: se utilizan para almacenar un conjunto de datos del mismo tipo de tamaño fijo. El tamaño se especifica al definirlo y se almacenan en la pila.
  • Slice: se utilizan para referenciar una parte de un arreglo u otra región de memoria contigua. Su tamaño se puede ajustar dinámicamente, son más flexibles y normalmente hacen referencia a memoria del montón.

Arreglo (Array):

  • Un arreglo es una secuencia de longitud fija cuyo tamaño ya se determina en tiempo de compilación.
  • El tipo de un arreglo es[T], dondeTes el tipo de los elementos del arreglo.
  • La memoria de los arreglos es contigua, lo que les brinda una gran ventaja en rendimiento, especialmente al manejar grandes cantidades de datos.

Slice:

  • Un slice es una secuencia de longitud dinámica que permite cambiar su tamaño en tiempo de ejecución.
  • El tipo de un slice es[]T, dondeTes el tipo de los elementos del slice.
  • Un slice es en realidad una referencia a un arreglo; contiene un puntero al arreglo y la longitud del slice.
  • Los slices pueden manejar datos de manera más flexible porque pueden compartirse y transferirse fácilmente entre diferentes arreglos.

Arreglos

Los arreglos son de tamaño fijo, se almacenan en la pila y los tipos de sus elementos deben ser iguales.

Se puede usar[]sintaxis para definir un arreglo y especificar su tamaño.

El tipo de un arreglo se define como[T; N], donde T es el tipo de los elementos del arreglo y N es la longitud del arreglo.

Los elementos de un arreglo se acceden mediante un índice; el índice comienza en0.

Formato de sintaxis

const arrayName: [size]ElementType = [size]ElementType{element1, element2, ...};

Descripción de parámetros:

  • arrayName: el nombre del arreglo.
  • size: el tamaño del arreglo (número de elementos), que es una constante en tiempo de compilación.
  • ElementType: el tipo de los elementos del arreglo.
  • element1, element2, ...: los elementos del arreglo.

Un arreglo es un bloque de memoria contiguo de tamaño fijo, cuyo tamaño ya se determina en tiempo de compilación:

var myArray: [10]u8 = [10]u8{0} ** 10; // 定义并初始化一个大小为 10 的 u8 类型数组

Ejemplo

const std = @import("std");

pub fn main() void {
    // Define un arreglo que contiene 5 elementos de tipo i32
    const arr: [5]i32 = [5]i32{1, 2, 3, 4, 5};

    // Accede a los elementos del arreglo mediante un índice
    std.debug.print("First element: {}\n", .{arr[0]});
    std.debug.print("Third element: {}\n", .{arr[2]});

    // El tamaño del arreglo es fijo
    const size: usize = arr.len;
    std.debug.print("Array size: {}\n", .{size});
}

El resultado de la compilación es:

First element: 1
Third element: 3
Array size: 5

Recorrer arreglos

Se puede usarforun bucle para recorrer los elementos de un arreglo.

Ejemplo

const std = @import("std");

pub fn main() void {
    const arr: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };

    var index: usize = 0;

    // Recorrer el arreglo
    for (arr) |item| {
        std.debug.print("Index: {}, Item: {}\n", .{ index, item });
        index += 1;
    }
}

El resultado de la compilación es:

Index: 0, Item: 1
Index: 1, Item: 2
Index: 2, Item: 3
Index: 3, Item: 4
Index: 4, Item: 5

Slice (segmento)

Un slice es una referencia a una parte de un arreglo u otra región de memoria contigua.

Un slice puede ajustar su tamaño dinámicamente y es más flexible que un arreglo, pero sus elementos se almacenan en el montón.

Definición e inicialización

Un slice es una referencia a una parte de un arreglo u otra región de memoria contigua.

Los slices son dinámicos, pueden cambiar su tamaño y generalmente se usan para representar una parte de un arreglo o un bloque de memoria asignado dinámicamente.

Formato de sintaxis

const sliceName: []ElementType = array[start..end];

Descripción de parámetros:

  • sliceName: el nombre del slice.
  • ElementType: el tipo de los elementos del slice.
  • array[start..end]: a partir dearrayse extrae un subslice,startyendes el índice.

Un slice se puede crear a partir de un subconjunto de un arreglo, o también mediante un puntero y una longitud:

var myArray: [10]u8 = ...; // 假设已经初始化
var mySlice = myArray[2..7]; // 创建一个切片,包含索引2到6的元素

// 或者使用指针和长度
var mySlicePtr = myArray[2..]; // 创建一个切片,从索引2开始到数组末尾

Los slices proporcionan algunos métodos integrados para manipularlos, por ejemplo:

  • len: obtiene la longitud del slice.
  • ptr: obtiene el puntero del slice.
  • capacity: obtiene la capacidad del slice, es decir, la longitud máxima de la parte del arreglo que puede referenciar.

Ejemplo

const std = @import("std");

pub fn main() void {
    var arr: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };

    // Crear un slice a partir de un arreglo
    const slice: []i32 = arr[1..4];

    // Acceder a los elementos del slice mediante un índice
    std.debug.print("First element of slice: {}\n", .{slice[0]});
    std.debug.print("Second element of slice: {}\n", .{slice[1]});

    // La longitud del slice
    const length: usize = slice.len;
    std.debug.print("Slice length: {}\n", .{length});
}

El resultado de la compilación es:

First element of slice: 2
Second element of slice: 3
Slice length: 3

Recorrer slices

Similar a los arreglos, se puede usarforun bucle para recorrer slices.

Ejemplo

const std = @import("std");

pub fn main() void {
    var arr: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };
    const slice: []i32 = arr[1..4];
    var index: usize = 1;

    // Recorrer el slice
    for (slice) |item| {
        std.debug.print("Index: {}, Item: {}\n", .{ index, item });
        index += 1;
    }
}

El resultado de la compilación es:

Index: 1, Item: 2
Index: 2, Item: 3
Index: 3, Item: 4

Diferencias entre arreglos y slices

  • Tamaño: el tamaño de un arreglo es fijo y se determina al definirlo; el tamaño de un slice se puede ajustar dinámicamente.
  • Ubicación de almacenamiento: los arreglos normalmente se almacenan en la pila, mientras que la memoria referenciada por un slice puede estar en el montón.
  • Flexibilidad: los slices son más flexibles y pueden referenciar una parte de un arreglo o memoria asignada dinámicamente.
CaracterísticaArregloSlice
TamañoFijo, determinado en tiempo de compilaciónDinámico, puede cambiar de tamaño
Tipo de elementosIgualIgual
Ubicación en memoriaGeneralmente en la pila (variables locales)La memoria referenciada puede estar en el montón o en la pila
AccesoMediante un índiceMediante los índices de inicio y fin del slice
CreaciónDefinición directaSe crea a partir de un arreglo u otro slice

En el siguiente ejemplo, la función printArray acepta un arreglo de tamaño fijo como parámetro, mientras que la función printSlice acepta un slice como parámetro. A través de estas funciones, se pueden ver las diferencias entre arreglos y slices en cuanto a su paso y uso.

Ejemplo

const std = @import("std");

fn printArray(arr: [5]i32) void {
    for (arr) |item| {
        std.debug.print("Array item: {}\n", .{item});
    }
}

fn printSlice(slice: []const i32) void {
    for (slice) |item| {
        std.debug.print("Slice item: {}\n", .{item});
    }
}

pub fn main() void {
    const arr: [5]i32 = [5]i32{ 1, 2, 3, 4, 5 };
    const slice: []const i32 = arr[1..4];

    printArray(arr);
    printSlice(slice);
}

El resultado de la compilación es:

Array item: 1
Array item: 2
Array item: 3
Array item: 4
Array item: 5
Slice item: 2
Slice item: 3
Slice item: 4
Otras extensiones