Kurz: fn addiere(a: i32, b: i32) i32 { return a + b; }
Teil des Kurses Zig
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.25Parameter 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*Tund.* - Arrays
[N]T(feste Länge im Typ), Slices[]T(Zeiger + Länge),++und**zur Übersetzungszeit - Strings sind
[]const u8;std.membieteteql,indexOf,splitScalar,trim std.fmt.parseInt,bufPrintfü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
Was ist ein Slice?
- Zeiger plus Länge auf einen Speicherbereich (richtig)
- Ein Array fester Größe
- Ein String-Objekt
- Eine Liste
Wie ändert eine Funktion einen Wert des Aufrufers?
- *Über einen Zeiger (T) (richtig)
- Parameter sind veränderlich
- Mit global
- Gar nicht
Wie ist ein String in Zig typisiert?
- []const u8 (richtig)
- string
- String-Klasse
- char*
Weiter im Kurs
Zurück: Variablen, Typen und Kontrollfluss
Weiter: Fehler, defer und Ressourcen
Alle Kapitel: Zig im Überblick