Parámetros y argumentos
Cuando hablamos de parámetros o argumentos de una función, se suelen usar ambos términos como si fueran sinónimos. Aunque son parecidos hay una diferencia sútil:
- Los parámetros se definen en la declaración de una función. Son parte de su definición.
|
// fmt y args son parámetros de print pub fn print(comptime fmt: []const u8, args: anytype) void { |
- Los argumentos son los valores concretos que se pasan en la llamada a una función.
|
// pasamos “Hola {s}.\n” y .{“Programador”} como argumentos print("Hola {s}.\n", .{"Programador"}); |
Es muy común usar ambos de forma intercambiable, pero saber esta diferencia puede ayudar a ser más precisos a la hora de escribir y explicar el código.
En Zig, por defecto, los parámetros de una función son inmutables: no se pueden modificar sin más. Cuando pasamos un dato a una función, ese valor se pasa por copia. Esto quiere decir que dentro del cuerpo de la función trabajamos con una copia del dato original. Los parámetros están “desvinculados” de las variables o constantes que usamos en la llamada.
En el caso de pasar estructuras grandes, el compilador, puede optimizar el paso usando una referencia interna a esos datos (un puntero) en lugar de copiar todo el contenido. Pero como programadores no sabemos si ha hecho una u otra cosa sin ver el código máquina compilado.
Lo más importante es recordar que el parámetro que recibimos en el cuerpo de la función siempre es una copia. Incluso aunque pases un puntero recibirás una copia de ese puntero. Si tomamos la dirección en memoria de esa copia, como hacemos a continuación, debemos de tener cuidado: es una dirección local al stack de la función y deja de ser válida al salir de ella.
inc_return_ref.zig |
|
const std = @import("std"); const print = std.debug.print; // incrementa un valor y devuelve la dirección de memoria // donde apunta el parámetro n_x fn inc(n_x: *u8) *const *u8 { // imprimimos la dirección en n_x print("Dirección que llega contenida en n_x = {}\n", .{n_x}); // imprimimos la dirección de n_x print("Dirección del n_x: {}\n", .{&n_x}); print("A qué apunta n_x: {}\n", .{&n_x.*}); // modificamos el dato apuntado por la dirección // contenida en n_x n_x.* += 1; return &n_x; } pub fn main() void { // variable var n_a: u8 = 41; print("Valor n_a = {}\n", .{n_a}); // imprimimos la dirección de la variable print("Dirección n_x en main {}\n", .{&n_a}); // incrementamos n_a const p_x = inc(&n_a); print("Recuperamos el puntero: {}\n", .{p_x}); print("Pero ya da comportamiento no definido, apunta a {}\n", .{p_x.*}); // imprimimos n_a incrementado print("Valor n_a = {}\n", .{n_a}); } |
|
$ zig run inc_return_ref.zig |
|
Valor n_a = 41 Dirección n_x en main u8@7ffdf073a808 Dirección que llega contenida en n_x = u8@7ffdf073a808 Dirección del n_x: *u8@7ffdf073a788 A qué apunta n_x: u8@7ffdf073a808 Recuperamos el puntero: *u8@7ffdf073a788 Pero ya da comportamiento no definido, apunta a u8@6570756365520000 Valor n_a = 42 |