EMZETT.
Login

Kurz: const Punkt = struct { x: f64, y: f64 = 0, // Standardwert

Teil des Kurses Zig

Kapitel 5 von 8 im Kurs Zig. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Structs

const std = @import("std");
 
const Punkt = struct {
    x: f64,
    y: f64 = 0,                           // Standardwert
 
    fn abstand(self: Punkt) f64 {
        return @sqrt(self.x * self.x + self.y * self.y);
    }
 
    fn verschiebe(self: *Punkt, dx: f64, dy: f64) void {
        self.x += dx;
        self.y += dy;
    }
 
    const ursprung = Punkt{ .x = 0 };      // Konstante im Struct
 
    fn neu(x: f64, y: f64) Punkt {         // "Konstruktor": normale Funktion
        return .{ .x = x, .y = y };
    }
};
 
const Farbe = enum { rot, gruen, blau };
 
const Form = union(enum) {                 // getaggte Union
    kreis: f64,
    rechteck: struct { b: f64, h: f64 },
 
    fn flaeche(self: Form) f64 {
        return switch (self) {
            .kreis => |r| std.math.pi * r * r,
            .rechteck => |r| r.b * r.h,
        };
    }
};
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var p = Punkt.neu(3, 4);
    try out.print("{d} {d}\n", .{ p.abstand(), Punkt.ursprung.x });
    p.verschiebe(1, 1);
    try out.print("({d}, {d})\n", .{ p.x, p.y });
    try out.print("{any}\n", .{p});
 
    const f: Farbe = .gruen;
    try out.print("{s} {d} {any}\n", .{ @tagName(f), @intFromEnum(f), f == .gruen });
    switch (f) {
        .rot => try out.print("R\n", .{}),
        .gruen, .blau => try out.print("nicht rot\n", .{}),
    }
 
    const formen = [_]Form{ .{ .kreis = 1 }, .{ .rechteck = .{ .b = 3, .h = 4 } } };
    for (formen) |form| try out.print("{d:.2}\n", .{form.flaeche()});
}

Ausgabe:

5 0
(4, 5)
main.Punkt{ .x = 4e0, .y = 5e0 }
gruen 1 true
nicht rot
3.14
12.00

Structs haben keine Vererbung, keine versteckten Konstruktoren oder Destruktoren: Alles ist sichtbar. Methoden sind Funktionen im Struct, self ist der erste Parameter.

Speicher und Allocators

Zig versteckt keine Heap-Allokation. Wer dynamischen Speicher braucht, bekommt einen Allocator übergeben und gibt den Speicher selbst frei (meist mit defer).

const std = @import("std");
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};   // findet Speicherlecks
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();
 
    // Einzelner Wert und Slice
    const zahl = try alloc.create(i32);
    defer alloc.destroy(zahl);
    zahl.* = 42;
 
    const puffer = try alloc.alloc(u8, 5);
    defer alloc.free(puffer);
    @memset(puffer, 'x');
    try out.print("{d} {s}\n", .{ zahl.*, puffer });
 
    // Dynamische Liste
    var liste = std.ArrayList(i32).init(alloc);
    defer liste.deinit();
    for (1..6) |i| try liste.append(@intCast(i * i));
    try out.print("{any} {d}\n", .{ liste.items, liste.items.len });
    _ = liste.pop();
    try liste.insert(0, 99);
    _ = liste.orderedRemove(1);
    try out.print("{any}\n", .{liste.items});
 
    // Text zusammenbauen
    var text = std.ArrayList(u8).init(alloc);
    defer text.deinit();
    try text.appendSlice("Hallo");
    try text.writer().print(" {s} {d}", .{ "Zig", 2025 });
    try out.print("{s}\n", .{text.items});
 
    // Formatierter String auf dem Heap
    const s = try std.fmt.allocPrint(alloc, "{d}+{d}={d}", .{ 2, 3, 5 });
    defer alloc.free(s);
    try out.print("{s}\n", .{s});
 
    // Hashmap
    var zaehler = std.StringHashMap(u32).init(alloc);
    defer zaehler.deinit();
    for ([_][]const u8{ "a", "b", "a", "c", "a" }) |w| {
        const eintrag = try zaehler.getOrPut(w);
        if (!eintrag.found_existing) eintrag.value_ptr.* = 0;
        eintrag.value_ptr.* += 1;
    }
    try out.print("{d} {d} {d}\n", .{ zaehler.get("a").?, zaehler.get("b").?, zaehler.count() });
}

Ausgabe:

42 xxxxx
{ 1, 4, 9, 16, 25 } 5
{ 99, 4, 9, 16 }
Hallo Zig 2025
2+3=5
3 1 3
AllocatorEinsatz
std.heap.page_allocatordirekt vom Betriebssystem, einfach
GeneralPurposeAllocatorStandard mit Leck- und Doppelfreigabe-Erkennung (Debug)
ArenaAllocatoralles auf einmal freigeben (Anfragen, Parser)
FixedBufferAllocatorfester Stack-Puffer, kein Heap
std.testing.allocatorin Tests: meldet Lecks
const std = @import("std");
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();                          // gibt alles auf einmal frei
    const a = arena.allocator();
    const eins = try a.alloc(u32, 100);
    const zwei = try a.dupe(u8, "kopiert");
    try out.print("{d} {s}\n", .{ eins.len, zwei });
 
    var puffer: [64]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&puffer);
    const kleine = try fba.allocator().alloc(u8, 16);
    try out.print("{d}\n", .{kleine.len});
    const zu_gross = fba.allocator().alloc(u8, 100);
    try out.print("{any}\n", .{zu_gross});
}

Ausgabe:

100 kopiert
16
error.OutOfMemory

Merke

  • struct mit Feldern, Standardwerten und Methoden; enum, getaggte union(enum)
  • Keine versteckten Konstruktoren/Destruktoren, keine Vererbung
  • Dynamischer Speicher nur über Allocators: alloc/free, create/destroy, defer
  • ArrayList, StringHashMap aus der Standardbibliothek
  • GPA findet Lecks, Arena gibt alles auf einmal frei

Übungsaufgabe

Lies Wörter eines Textes in eine StringHashMap und gib die Häufigkeiten aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Fehler, defer und Ressourcen

Weiter: comptime, Generics und Tests

Alle Kapitel: Zig im Überblick