Tipos de cadenas

Las cadenas en Zig son slices “sobre” arrays de caracteres. Ya sabemos, de Arrays y Slices, que los slices no son propietarios de los datos:  apuntan a los datos de un array.

Por lo tanto, las cadenas - igual que cualquier otro slice - son punteros. Para ser más precisos, en Zig todos los slice son un tipo de puntero llamado “puntero gordo” (fat pointer) - contienen la dirección del primer elemento y la cantidad de elementos que hay a partir de esa dirección. Este número de elementos lo accedemos en Zig a través de .len

Cadena tipo slice de bytes

Hace un momento hemos visto la cadena declarada de esta forma:

      const s_hello: []const u8 = "Hola a todos";

, es un slice de bytes. En memoria queda así:

0

1

2

3

4

5

6

7

8

9

10

11

12

.{

’H’

’o’

’l’

’a’

’a’

’t’

’o’

’d’

’o’

’s’

0

}

Tiene .len y Zig añade un null final (0) en la memoria después de terminar el slice. Ese null no forma parte del slice y no es accesible, ni siquiera si queremos comparar contra él en un while para ver donde termina la cadena.

string_literal.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    const s_a: []const u8 = "Hola a todos";

    print("\nLongitud: {}\n", .{s_a.len});

    var n_i: usize = 0;

    var c_x: u8 = s_a[n_i];

    // comparamos si el char es null

    while (c_x != 0) : ({

        n_i += 1;

        c_x = s_a[n_i];

    }) {

        print("{c}", .{c_x});

    }

    print("\n", .{});

}

$ zig run string_literal.zig

Longitud: 12

Hola a todosthread 705195 panic: index out of bounds: index 12, len 12

string_literal.zig:12:18: 0x113e93e in main (string_literal.zig)

        c_x = s_a[n_i];

                 ^

Cadena tipo slice de bytes terminado en marcador

Sin embargo, si definimos, una cadena de esta forma:

      const s_hello: [:0]const u8 = "Hola a todos";

Es un slice terminado en marcador o centinela (sentinel en inglés). Esta vez al definirlo Zig también añade un null al final y sí se puede comprobar con un while.

0

1

2

3

4

5

6

7

8

9

10

11

12

.{

’H’

’o’

’l’

’a’

’a’

’t’

’o’

’d’

’o’

’s’

0

}

Tiene .len y además podemos comparar si el byte es null.

string_literal_sentinel.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    const s_a: [:0]const u8 = "Hola a todos";

    print("\nLongitud: {}\n", .{s_a.len});

    var n_i: usize = 0;

    var c_x: u8 = s_a[n_i];

    // comparamos si el char es null

    while (c_x != 0) : ({

        n_i += 1;

        c_x = s_a[n_i];

    }) {

        print("{c}", .{c_x});

    }

    print("\n", .{});

}

$ zig run string_literal_sentinel.zig

Longitud: 12

Hola a todos

Cadena tipo puntero terminado en marcador

Si definimos, una cadena así:

      const s_hello: [*:0]const u8 = "Hola a todos";

Declaramos un puntero a bytes terminado en centinela. La única manera de detectar el final de la cadena es detectar dónde está el null. No hay .len en este caso. No es un slice típico de Zig. Este tipo de cadenas es útil para interactuar con las funciones del lenguaje C que esperan cadenas terminadas en null.

0

1

2

3

4

5

6

7

8

9

10

11

12

.{

’H’

’o’

’l’

’a’

’a’

’t’

’o’

’d’

’o’

’s’

0

}

string_ptr.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    const s_a: [*:0]const u8 = "Hola a todos";

    var n_i: usize = 0;

    var c_x: u8 = s_a[n_i];

    // comparamos si el char es null

    while (c_x != 0) : ({

        n_i += 1;

        c_x = s_a[n_i];

    }) {

        print("{c}", .{c_x});

    }

    // acceder .len da error de compilación

    // print("\nLongitud: {}\n", .{s_a.len});

    print("\nLongitud: {}\n", .{n_i});

}

$ zig run string_ptr.zig

Hola a todos

Longitud: 12

“Cadena” array

Un array, en cierto modo, también puede considerarse una cadena de texto, ya que es una secuencia de caracteres. Sin embargo, en Zig no se considera una cadena en sentido estricto, porque no está terminada en null, a menos que se lo añadas explícitamente.

      const s_hello: [_]u8{'H','o','l','a',' ','a',' ','t','o','d','o','s'};

0

1

2

3

4

5

6

7

8

9

10

11

.{

’H’

’o’

’l’

’a’

’a’

’t’

’o’

’d’

’o’

’s’

}

Tiene .len, no tiene un null al final. Pero puedes montar un slice sobre ese array, porque como ya sabemos un slice es como una ventana que abarca parte o la totalidad de un array.

Entonces ¿debajo de toda cadena hay un array?

Sí, en Zig cualquier cadena tiene un array debajo.

Como, las cadenas son slices (punteros con inicio y longitud),  o punteros acabados en null, tienen que apuntar sobre un array, aunque el programador solo esté declarando un slice:

      const s_hello:[*:0]const u8 = “Hola”;

 Zig en ese caso crea internamente un array como:

      const s_hello: [_]u8{ 'H', 'o', 'l', 'a', 0 };

Todas estas cadenas que hemos ido definiendo al ser constantes, Zig las conoce en  tiempo de compilación.  

Y si quieres definir una cadena “variable”?

Bueno pues no se puede exactamente hacer eso.

Si haces esto:

  var s_x = "Hola a todos";

Y no cambias nada, el compilador te va a dar un error diciendo que no estás modificando la variable declarada, y por lo tanto que la declares como una constante.

Si intentas hacer algo así:

  s_x[0] = ‘h’;

Obtendrás un error:

cannot assign to constant

    s_x[0] = 'h';

    ~~~^~~

Porque en realidad, como ya vimos en Arrays y Slices, el array subyacente no es variable: estás declarando el slice como variable, pero no su contenido

Lo que sí puedes hacer es apuntar a otro array con la misma variable tipo slice mientras respetemos la longitud inicial:

  s_x = "Hola a toPos";

Eso sí que puedes hacerlo.

Así que las cadenas en Zig son punteros que apuntan a arrays de caracteres y declaradas así, como literales, son conocidas en tiempo de compilación.

Punteros
Entrada por teclado
© 2025 Zen of Zig