EMZETT.
Login

Kurz: fn addiere(a: i32, b: i32) i32 { return a + b; }

Teil des Kurses Zig

Kapitel 3 von 8 im Kurs Zig (Abschnitt „Daten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Funktionen

const std = @import("std");
 
fn addiere(a: i32, b: i32) i32 {
    return a + b;
}
 
fn fakultaet(n: u64) u64 {
    return if (n <= 1) 1 else n * fakultaet(n - 1);
}
 
fn minmax(liste: []const i32) struct { min: i32, max: i32 } {      // anonymes Struct als Rückgabe
    var lo = liste[0];
    var hi = liste[0];
    for (liste) |x| {
        if (x < lo) lo = x;
        if (x > hi) hi = x;
    }
    return .{ .min = lo, .max = hi };
}
 
fn vertausche(a: *i32, b: *i32) void {         // Zeiger als Parameter
    const t = a.*;
    a.* = b.*;
    b.* = t;
}
 
inline fn quadrat(x: anytype) @TypeOf(x) {       // anytype: generischer Parameter
    return x * x;
}
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    try out.print("{d} {d}\n", .{ addiere(2, 3), fakultaet(15) });
    const mm = minmax(&[_]i32{ 4, 9, -2, 7 });
    try out.print("{d} {d}\n", .{ mm.min, mm.max });
    var x: i32 = 1;
    var y: i32 = 2;
    vertausche(&x, &y);
    try out.print("{d} {d}\n", .{ x, y });
    try out.print("{d} {d:.2}\n", .{ quadrat(@as(i32, 7)), quadrat(@as(f64, 1.5)) });
}

Ausgabe:

5 1307674368000
-2 9
2 1
49 2.25

Parameter sind in Zig unveränderlich. Wer etwas ändern will, übergibt einen Zeiger (*T). Die Syntax a.* dereferenziert, &x bildet einen Zeiger.

Arrays und Slices

Ein Array hat eine feste Länge, die zum Typ gehört ([5]i32). Ein Slice ([]T) ist eine Ansicht auf Speicher: Zeiger plus Länge.

const std = @import("std");
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var zahlen = [_]i32{ 5, 3, 9, 1, 7 };         // Länge wird erschlossen: [5]i32
    const nuller = [_]u8{0} ** 4;                  // Wiederholung
    const verkettet = zahlen ++ [_]i32{ 100, 200 };    // ++ verbindet zur Übersetzungszeit
    try out.print("{any} {d} {any} {d}\n", .{ zahlen, zahlen.len, nuller, verkettet.len });
 
    zahlen[0] = 50;
    const teil = zahlen[1..4];                     // Slice: Index 1 bis 3
    try out.print("{any} {d} {d}\n", .{ teil, teil.len, teil[0] });
 
    std.mem.sort(i32, &zahlen, {}, std.sort.asc(i32));
    try out.print("{any}\n", .{zahlen});
    std.mem.reverse(i32, &zahlen);
    try out.print("{any}\n", .{zahlen});
 
    var summe: i32 = 0;
    for (zahlen) |z| summe += z;
    try out.print("{d} {d}\n", .{ summe, std.mem.indexOfScalar(i32, &zahlen, 7).? });
 
    var matrix: [2][3]i32 = undefined;             // zweidimensional
    for (&matrix, 0..) |*zeile, i| {
        for (zeile, 0..) |*zelle, j| zelle.* = @intCast((i + 1) * (j + 1));
    }
    try out.print("{any}\n", .{matrix});
 
    for (&zahlen) |*z| z.* *= 2;                   // Elemente per Zeiger ändern
    try out.print("{any}\n", .{zahlen});
}

Ausgabe:

{ 5, 3, 9, 1, 7 } 5 { 0, 0, 0, 0 } 7
{ 3, 9, 1 } 3 3
{ 1, 3, 7, 9, 50 }
{ 50, 9, 7, 3, 1 }
70 2
{ { 1, 2, 3 }, { 2, 4, 6 } }
{ 100, 18, 14, 6, 2 }

Zugriffe außerhalb der Grenzen sind im Debug-Modus ein Panic (index out of bounds), nie stilles undefiniertes Verhalten.

Strings

Zig kennt keinen eigenen String-Typ: Ein Text ist ein Slice von Bytes ([]const u8), Literale sind *const [N:0]u8 (null-terminiert) und werden zu Slices.

const std = @import("std");
const mem = std.mem;
 
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const s = "Hallo Welt";
    try out.print("{s} {d} {c} {s}\n", .{ s, s.len, s[0], s[0..5] });
    try out.print("{any} {any}\n", .{ mem.eql(u8, s, "Hallo Welt"), mem.startsWith(u8, s, "Hal") });
    try out.print("{any} {any}\n", .{ mem.indexOf(u8, s, "Welt"), mem.count(u8, s, "l") });
 
    var it = mem.splitScalar(u8, "a,b,,c", ',');
    while (it.next()) |teil| try out.print("[{s}]", .{teil});
    try out.print("\n", .{});
 
    var tok = mem.tokenizeScalar(u8, "  viele   Wörter hier ", ' ');
    while (tok.next()) |w| try out.print("<{s}>", .{w});
    try out.print("\n", .{});
 
    var puffer: [32]u8 = undefined;
    const gross = std.ascii.upperString(&puffer, "klein");
    try out.print("{s} {s}\n", .{ gross, mem.trim(u8, "  getrimmt  ", " ") });
 
    const zahl = try std.fmt.parseInt(i32, "1234", 10);
    const kommazahl = try std.fmt.parseFloat(f64, "2.5");
    try out.print("{d} {d:.1}\n", .{ zahl + 1, kommazahl * 2 });
 
    const text = try std.fmt.bufPrint(&puffer, "{d}-{s}", .{ 7, "x" });
    try out.print("{s} {d}\n", .{ text, text.len });
 
    var n: usize = 0;
    for (s) |c| {
        if (std.ascii.isUpper(c)) n += 1;
    }
    try out.print("{d}\n", .{n});
 
    const umlaut = "Grüße";
    try out.print("{d} Bytes, {d} Zeichen\n", .{ umlaut.len, try std.unicode.utf8CountCodepoints(umlaut) });
    const mehrzeilig =
        \\Zeile 1
        \\  Zeile 2 mit "Anführungszeichen"
    ;
    try out.print("{s}\n", .{mehrzeilig});
}

Ausgabe:

Hallo Welt 10 H Hallo
true true
6 3
[a][b][][c]
<viele><Wörter><hier>
KLEIN getrimmt
1235 5.0
7-x 3
2
7 Bytes, 5 Zeichen
Zeile 1
  Zeile 2 mit "Anführungszeichen"

Merke

  • Funktionen: fn name(a: T) R; Parameter sind unveränderlich, Zeiger *T und .*
  • Arrays [N]T (feste Länge im Typ), Slices []T (Zeiger + Länge), ++ und ** zur Übersetzungszeit
  • Strings sind []const u8; std.mem bietet eql, indexOf, splitScalar, trim
  • std.fmt.parseInt, bufPrint für Umwandlungen
  • Zugriffe außerhalb der Grenzen führen im Debug-Modus zu einem Panic

Übungsaufgabe

Schreibe eine Funktion, die ein Slice von i32 umdreht, ohne std.mem.reverse zu nutzen.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Variablen, Typen und Kontrollfluss

Weiter: Fehler, defer und Ressourcen

Alle Kapitel: Zig im Überblick