← Volver al listado de tecnologías
Punteros y Memoria en Zig
Punteros y Memoria
Tipos de Punteros
const std = @import("std");
pub fn main() void {
var valor: i32 = 42;
// Puntero simple
const ptr: *i32 = &valor;
std.debug.print("Valor: {d}\n", .{ptr.*});
// Puntero constante
const ptr_const: *const i32 = &valor;
// ptr_const.* = 10; // Error: no se puede modificar
// Modificar a través del puntero
ptr.* = 100;
std.debug.print("Nuevo valor: {d}\n", .{valor});
// Puntero a muchos elementos
var arr = [_]i32{ 1, 2, 3, 4, 5 };
const many_ptr: [*]i32 = &arr;
std.debug.print("Tercer elemento: {d}\n", .{many_ptr[2]});
}
Punteros Opcionales
const std = @import("std");
fn buscarPuntero(arr: []i32, valor: i32) ?*i32 {
for (arr) |*elem| {
if (elem.* == valor) return elem;
}
return null;
}
pub fn main() void {
var numeros = [_]i32{ 10, 20, 30, 40 };
if (buscarPuntero(&numeros, 30)) |ptr| {
std.debug.print("Encontrado: {d}\n", .{ptr.*});
ptr.* = 300; // Modificar el valor original
}
std.debug.print("Array modificado: {any}\n", .{numeros});
}
Allocators
Zig no tiene garbage collector. La memoria se gestiona con allocators:
const std = @import("std");
pub fn main() !void {
// Page allocator (del OS)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Asignar memoria para un valor
const ptr = try allocator.create(i32);
defer allocator.destroy(ptr);
ptr.* = 42;
std.debug.print("Valor: {d}\n", .{ptr.*});
// Asignar array dinámico
const arr = try allocator.alloc(u8, 100);
defer allocator.free(arr);
@memset(arr, 0);
std.debug.print("Array de {d} bytes creado\n", .{arr.len});
}
Tipos de Allocators
const std = @import("std");
pub fn main() !void {
// 1. General Purpose Allocator (recomendado para desarrollo)
var gpa = std.heap.GeneralPurposeAllocator(.{
.safety = true,
.stack_trace_size = 8,
}){};
defer {
const check = gpa.deinit();
if (check == .leak) {
std.debug.print("Memory leak detectado!\n", .{});
}
}
// 2. Fixed Buffer Allocator (sin syscalls)
var buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const fba_alloc = fba.allocator();
const datos = try fba_alloc.alloc(u8, 100);
_ = datos;
// 3. Arena Allocator (libera todo al final)
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const arena_alloc = arena.allocator();
// No necesitas free individual, arena.deinit() libera todo
_ = try arena_alloc.alloc(u8, 500);
_ = try arena_alloc.alloc(u8, 500);
}
ArrayList Dinámico
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Crear ArrayList
var lista = std.ArrayList(i32).init(allocator);
defer lista.deinit();
// Agregar elementos
try lista.append(10);
try lista.append(20);
try lista.append(30);
// Agregar múltiples
try lista.appendSlice(&[_]i32{ 40, 50 });
// Iterar
for (lista.items) |item| {
std.debug.print("{d} ", .{item});
}
// Acceso por índice
std.debug.print("\nPrimero: {d}\n", .{lista.items[0]});
// Eliminar último
_ = lista.pop();
std.debug.print("Longitud: {d}\n", .{lista.items.len});
}
HashMap
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// HashMap string -> i32
var mapa = std.StringHashMap(i32).init(allocator);
defer mapa.deinit();
// Insertar
try mapa.put("uno", 1);
try mapa.put("dos", 2);
try mapa.put("tres", 3);
// Buscar
if (mapa.get("dos")) |valor| {
std.debug.print("dos = {d}\n", .{valor});
}
// Iterar
var iter = mapa.iterator();
while (iter.next()) |entry| {
std.debug.print("{s}: {d}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
// Eliminar
_ = mapa.remove("uno");
}
Defer y Errdefer
const std = @import("std");
fn abrirRecursos(allocator: std.mem.Allocator) ![]u8 {
const buffer1 = try allocator.alloc(u8, 100);
errdefer allocator.free(buffer1); // Solo si hay error después
const buffer2 = try allocator.alloc(u8, 100);
errdefer allocator.free(buffer2);
// Si esta línea falla, buffer1 y buffer2 se liberan
if (buffer1.len + buffer2.len > 150) {
return error.MuyGrande;
}
allocator.free(buffer2);
return buffer1;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const datos = try abrirRecursos(gpa.allocator());
defer gpa.allocator().free(datos);
std.debug.print("Recursos abiertos: {d} bytes\n", .{datos.len});
}
Testing de Memoria
const std = @import("std");
const testing = std.testing;
fn duplicarArray(allocator: std.mem.Allocator, arr: []const i32) ![]i32 {
const nuevo = try allocator.alloc(i32, arr.len);
@memcpy(nuevo, arr);
return nuevo;
}
fn crearLista(allocator: std.mem.Allocator) !std.ArrayList(i32) {
var lista = std.ArrayList(i32).init(allocator);
try lista.append(1);
try lista.append(2);
try lista.append(3);
return lista;
}
// Test allocator detecta memory leaks
test "duplicar array sin leaks" {
const arr = [_]i32{ 1, 2, 3, 4, 5 };
const copia = try duplicarArray(testing.allocator, &arr);
defer testing.allocator.free(copia);
try testing.expectEqual(@as(usize, 5), copia.len);
try testing.expectEqual(@as(i32, 1), copia[0]);
try testing.expectEqual(@as(i32, 5), copia[4]);
}
test "ArrayList con test allocator" {
var lista = try crearLista(testing.allocator);
defer lista.deinit();
try testing.expectEqual(@as(usize, 3), lista.items.len);
try testing.expectEqual(@as(i32, 1), lista.items[0]);
}
test "HashMap básico" {
var mapa = std.StringHashMap(i32).init(testing.allocator);
defer mapa.deinit();
try mapa.put("a", 1);
try mapa.put("b", 2);
try testing.expectEqual(@as(?i32, 1), mapa.get("a"));
try testing.expectEqual(@as(?i32, 2), mapa.get("b"));
try testing.expectEqual(@as(?i32, null), mapa.get("c"));
}
test "puntero opcional" {
var valor: i32 = 42;
var ptr: ?*i32 = &valor;
try testing.expect(ptr != null);
try testing.expectEqual(@as(i32, 42), ptr.?.*);
ptr = null;
try testing.expect(ptr == null);
}
test "fixed buffer allocator" {
var buffer: [256]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const arr = try allocator.alloc(u8, 100);
try testing.expectEqual(@as(usize, 100), arr.len);
// Verificar que no podemos asignar más de lo disponible
const resultado = allocator.alloc(u8, 200);
try testing.expectError(error.OutOfMemory, resultado);
}
Ejercicios
- Implementa una función que concatene dos slices usando un allocator
- Crea un struct
Stackgenérico con push/pop usando ArrayList - Implementa un cache simple con HashMap que limite el número de entradas
Resumen
- Punteros:
*T(uno),[*]T(muchos),?*T(opcional) - Allocators gestionan la memoria manualmente
defergarantiza limpieza,errdefersolo en errortesting.allocatordetecta memory leaks en tests- ArrayList y HashMap son las colecciones dinámicas principales