Kurz: const Punkt = struct { x: f64, y: f64 = 0, // Standardwert
Teil des Kurses Zig
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.00Structs 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| Allocator | Einsatz |
|---|---|
std.heap.page_allocator | direkt vom Betriebssystem, einfach |
GeneralPurposeAllocator | Standard mit Leck- und Doppelfreigabe-Erkennung (Debug) |
ArenaAllocator | alles auf einmal freigeben (Anfragen, Parser) |
FixedBufferAllocator | fester Stack-Puffer, kein Heap |
std.testing.allocator | in 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.OutOfMemoryMerke
structmit Feldern, Standardwerten und Methoden;enum, getaggteunion(enum)- Keine versteckten Konstruktoren/Destruktoren, keine Vererbung
- Dynamischer Speicher nur über Allocators:
alloc/free,create/destroy,defer ArrayList,StringHashMapaus 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
Wer gibt in Zig dynamischen Speicher frei?
- Der Programmierer (meist mit defer) (richtig)
- Ein Garbage Collector
- Der Compiler allein
- Das Betriebssystem sofort
Wofür ist ein ArenaAllocator gut?
- Viele Allokationen auf einmal freigeben (richtig)
- Besonders kleine Zahlen speichern
- Threads synchronisieren
- Dateien lesen
Was ist union(enum)?
- Eine getaggte Union: ein Wert aus mehreren Varianten mit Tag (richtig)
- Eine Aufzählung
- Ein Zeiger
- Ein Array
Weiter im Kurs
Zurück: Fehler, defer und Ressourcen
Weiter: comptime, Generics und Tests
Alle Kapitel: Zig im Überblick