Introdução
Arrays multidimensionais (matrizes) são fundamentais para representar tabelas, grades, imagens e dados científicos. Em Zig, matrizes são representadas como arrays de arrays com tamanho fixo em tempo de compilação, ou como slices alocados dinamicamente para tamanhos variáveis.
Nesta receita, você aprenderá a criar, acessar e manipular arrays multidimensionais em Zig.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Matriz 2D com Tamanho Fixo
Arrays de arrays em tempo de compilação:
const std = @import("std");
pub fn main() !void {
// Matriz 3x4 de inteiros
const matriz = [3][4]i32{
[_]i32{ 1, 2, 3, 4 },
[_]i32{ 5, 6, 7, 8 },
[_]i32{ 9, 10, 11, 12 },
};
// Acessar elemento [linha][coluna]
std.debug.print("Elemento [1][2]: {d}\n", .{matriz[1][2]}); // 7
// Imprimir toda a matriz
std.debug.print("\nMatriz 3x4:\n", .{});
for (matriz) |linha| {
for (linha) |elem| {
std.debug.print("{d:>4}", .{elem});
}
std.debug.print("\n", .{});
}
// Dimensões
std.debug.print("\nLinhas: {d}, Colunas: {d}\n", .{ matriz.len, matriz[0].len });
}
Saída esperada
Elemento [1][2]: 7
Matriz 3x4:
1 2 3 4
5 6 7 8
9 10 11 12
Linhas: 3, Colunas: 4
Matriz Mutável e Operações
const std = @import("std");
pub fn main() !void {
// Criar matriz de zeros
var grade: [5][5]u8 = .{.{0} ** 5} ** 5;
// Desenhar um X na grade
for (0..5) |i| {
grade[i][i] = 1;
grade[i][4 - i] = 1;
}
// Imprimir grade
std.debug.print("Grade 5x5 com X:\n", .{});
for (grade) |linha| {
for (linha) |cell| {
const ch: u8 = if (cell == 1) '#' else '.';
std.debug.print("{c} ", .{ch});
}
std.debug.print("\n", .{});
}
}
Saída esperada
Grade 5x5 com X:
# . . . #
. # . # .
. . # . .
. # . # .
# . . . #
Matriz Dinâmica com Alocador
Para matrizes com tamanho definido em tempo de execução:
const std = @import("std");
fn criarMatriz(allocator: std.mem.Allocator, linhas: usize, colunas: usize) ![][]f64 {
const matriz = try allocator.alloc([]f64, linhas);
errdefer allocator.free(matriz);
var inicializado: usize = 0;
errdefer {
for (matriz[0..inicializado]) |linha| allocator.free(linha);
}
for (matriz) |*linha| {
linha.* = try allocator.alloc(f64, colunas);
inicializado += 1;
// Inicializar com zeros
@memset(linha.*, 0.0);
}
return matriz;
}
fn liberarMatriz(allocator: std.mem.Allocator, matriz: [][]f64) void {
for (matriz) |linha| allocator.free(linha);
allocator.free(matriz);
}
fn imprimirMatriz(matriz: [][]f64) void {
for (matriz) |linha| {
for (linha) |elem| {
std.debug.print("{d:>8.2}", .{elem});
}
std.debug.print("\n", .{});
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const linhas: usize = 3;
const colunas: usize = 4;
var matriz = try criarMatriz(allocator, linhas, colunas);
defer liberarMatriz(allocator, matriz);
// Preencher com valores
for (matriz, 0..) |linha, i| {
for (linha, 0..) |_, j| {
matriz[i][j] = @as(f64, @floatFromInt(i * colunas + j + 1)) * 1.5;
}
}
std.debug.print("Matriz dinâmica {d}x{d}:\n", .{ linhas, colunas });
imprimirMatriz(matriz);
}
Multiplicação de Matrizes
const std = @import("std");
fn multiplicarMatrizes(
comptime N: usize,
comptime M: usize,
comptime P: usize,
a: [N][M]f64,
b: [M][P]f64,
) [N][P]f64 {
var resultado: [N][P]f64 = .{.{0.0} ** P} ** N;
for (0..N) |i| {
for (0..P) |j| {
for (0..M) |k| {
resultado[i][j] += a[i][k] * b[k][j];
}
}
}
return resultado;
}
pub fn main() !void {
const A = [2][3]f64{
.{ 1, 2, 3 },
.{ 4, 5, 6 },
};
const B = [3][2]f64{
.{ 7, 8 },
.{ 9, 10 },
.{ 11, 12 },
};
const C = multiplicarMatrizes(2, 3, 2, A, B);
std.debug.print("A (2x3) x B (3x2) = C (2x2):\n", .{});
for (C) |linha| {
for (linha) |elem| {
std.debug.print("{d:>8.1}", .{elem});
}
std.debug.print("\n", .{});
}
}
Saída esperada
A (2x3) x B (3x2) = C (2x2):
58.0 64.0
139.0 154.0
Transpor Matriz
const std = @import("std");
fn transpor(comptime N: usize, comptime M: usize, matriz: [N][M]i32) [M][N]i32 {
var resultado: [M][N]i32 = undefined;
for (0..N) |i| {
for (0..M) |j| {
resultado[j][i] = matriz[i][j];
}
}
return resultado;
}
pub fn main() !void {
const original = [2][4]i32{
.{ 1, 2, 3, 4 },
.{ 5, 6, 7, 8 },
};
std.debug.print("Original (2x4):\n", .{});
for (original) |linha| {
for (linha) |elem| std.debug.print("{d:>4}", .{elem});
std.debug.print("\n", .{});
}
const transposta = transpor(2, 4, original);
std.debug.print("\nTransposta (4x2):\n", .{});
for (transposta) |linha| {
for (linha) |elem| std.debug.print("{d:>4}", .{elem});
std.debug.print("\n", .{});
}
}
Dicas e Boas Práticas
Use arrays fixos quando possível: Arrays de tamanho fixo ficam na stack e são mais rápidos.
Cuidado com a ordem dos índices:
[linhas][colunas]– o primeiro índice é a linha, o segundo a coluna.errdeferpara matrizes dinâmicas: Ao alocar linhas individualmente, useerrdeferpara liberar em caso de falha parcial.Considere arrays contíguos: Para melhor uso de cache em matrizes grandes, use um array 1D com cálculo de índice (
i * colunas + j).
Receitas Relacionadas
- Ordenar Arrays e Slices - Ordenação
- Operações Matemáticas - Funções matemáticas
- Usando ArenaAllocator - Simplificar alocação
- Arrays Dinâmicos com ArrayList - Listas dinâmicas