Lyx unterscheidet vier Container-Formen. Die Wahl zwischen ihnen ist keine Stilfrage, sondern eine Sicherheits- und Architekturentscheidung: Stack-Arrays sind deterministisch und DO-178C-tauglich, Heap-Arrays sind flexibel aber nicht echtzeitfähig, SIMD-Arrays für numerische Hochleistungsberechnungen, Maps für Schlüssel-Wert-Zugriff.
→ Datentypen · Memory Management · DO-178C
Statische Arrays liegen auf dem Stack. Größe und Typ stehen zur Compile-Zeit fest und sind unveränderlich. Das ist die bevorzugte Form für sicherheitskritischen und echtzeitfähigen Code.
// [Größe]Typ — Typ-zuerst-Notation (wie Go/Zig, NICHT wie C)
var buffer: [256]uint8 := []; // 256 Bytes, nullinitialisiert
var coords: [3]f64 := [0.0, 0.0, 0.0];
var ids: [16]int64 := [];
// let — unveränderliche Bindung (Werte im Array selbst bleiben änderbar)
let defaults: [4]int64 := [0, 1, 2, 3];
Wichtig: Die Syntax ist[N]T— Größe in eckigen Klammern vor dem Typ.T[N](C-Stil) ist in Lyx kein gültiger Typ.
var coords: [3]f64 := [0.0, 0.0, 0.0];
coords[0] := 10.5; // Schreiben
coords[1] := coords[0]; // Lesen und Schreiben
var z: f64 := coords[2]; // Lesen
// Bounds-Checking:
// — Konstanter Index außerhalb der Grenzen → Compile-Fehler
// — Variabler Index außerhalb der Grenzen → kontrolliertes panic zur Laufzeit
// 8×8-Matrix — flaches Layout im Speicher (64 × 8 Bytes = 512 Bytes)
var grid: [8][8]int64 := [];
grid[3][4] := 99;
var val: int64 := grid[3][4]; // 99
panic)@stack_limitdispose nötig — Stack-Frame wird automatisch freigegeben
// DO-178C DAL-A: Waypoint-Liste als statisches Array
@dal(A)
@flight_crit
@stack_limit(2048)
@wcet(100)
fn FindNearest(wps: [32]Waypoint, n: int64, pos: GeoPoint): int64 {
var best: int64 := 0;
var best_d: f64 := 1.0e18;
for i := 0 to n - 1 do {
var d: f64 := GeoDistance(pos, wps[i].pos);
if (d < best_d) { best_d := d; best := i; }
}
return best;
}
Dynamische Arrays liegen auf dem Heap und wachsen automatisch. Intern speichern sie drei Werte (Fat-Pointer): Speicheradresse, aktuelle Länge (len) und reservierte Kapazität (cap).
import std.io;
var list: array<int64> := [10, 20, 30];
list.push(40); // Anhängen
list.push(50);
PrintInt(list.len()); // 5
PrintInt(list.cap()); // Reservierte Kapazität (≥ len)
PrintInt(list[0]); // 10
list[0] := list[0] + 1; // Lesen und Schreiben (kein ++ Operator in Lyx)
list[2] := 999;
list.clear(); // len := 0, Speicher bleibt reserviert
PrintInt(list.len()); // 0
dispose list; // Zwingend — sonst Memory Leak
Verlässt eine Funktion über mehrere Pfade, muss dispose vor jedem return stehen:
fn LoadConfig(path: pchar): bool {
var cfg: Map<pchar, int64> := {};
if (ParseFile(path, cfg) = false) {
dispose cfg; // Auch im Fehlerfall freigeben
return false;
}
ApplyConfig(cfg);
dispose cfg;
return true;
}
Lyx hat kein automatisches RAII/Destruktor-System für Heap-Typen. Bei mehrerenreturn-Pfaden statische Arrays[N]Tbevorzugen — keindispose, kein Leak-Risiko.
In Funktionen mit@flight_critwarnt der Compiler bei Heap-Allokation. Ausschließlich[N]Tverwenden.
Parallele Arrays sind Heap-Arrays mit garantiertem SIMD-Alignment (16 Byte für SSE/NEON, 32 Byte für AVX). Batch-Operationen auf diesen Arrays übersetzt der Compiler direkt in Vektor-Instruktionen.
var data: parallel Array<f64> := parallel Array<f64>(1024);
data[0] := 1.0;
data[1] := 2.0;
// Batch-Skalierung — compiler übersetzt in SSE/AVX/NEON
// data := data * 2.0;
dispose data;
f32, f64, int32, int64Maps erlauben den Zugriff über einen Schlüssel. Lyx bietet native Literal-Syntax seit v0.5.0.
import std.io;
var scores: Map<pchar, int64> := {"Alpha": 100, "Bravo": 250, "Charlie": 175};
// Lesen
var s: int64 := scores["Alpha"]; // 100
// Schreiben / Einfügen
scores["Delta"] := 300;
// Existenzprüfung mit in — immer vor Zugriff auf unbekannte Schlüssel
if ("Delta" in scores) {
PrintInt(scores["Delta"]); // 300
PrintStr("\n");
}
// Löschen eines Eintrags
delete scores["Charlie"];
dispose scores;
var config: Map<pchar, int64> := {
"timeout_ms": 5000,
"retry_count": 3,
"log_level": 2
};
for key, val in config do {
PrintStr(key); PrintStr(": "); PrintInt(val); PrintStr("\n");
}
// Ausgabe (Reihenfolge implementierungsabhängig):
// timeout_ms: 5000
// retry_count: 3
// log_level: 2
dispose config;
var headers: Map<pchar, pchar> := {
"Content-Type": "application/json",
"Authorization": "Bearer abc123"
};
if ("Content-Type" in headers) {
PrintStr(headers["Content-Type"]); // application/json
PrintStr("\n");
}
dispose headers;
Alle Heap-Container (array<T>, parallel Array<T>, Map<K,V>) müssen explizit freigegeben werden:
fn ProcessData(): void {
var buf: array<uint8> := [];
var data: parallel Array<f64> := parallel Array<f64>(256);
var meta: Map<pchar, int64> := {};
// ... Verarbeitung ...
dispose buf;
dispose data;
dispose meta;
}
Wird dispose vergessen, entsteht ein Memory Leak — besonders in Langzeit-Systemen (Avionik, Server) kritisch.
| Typ | Syntax | Ort | Timing | DO-178C DAL-A | dispose |
|---|---|---|---|---|---|
| Statisches Array | [N]T | Stack | Deterministisch | ✅ | Nein |
| Dynamisches Array | array<T> | Heap | Nicht-deterministisch | ✗ (nur Init) | Ja |
| Paralleles Array | parallel Array<T> | Heap (SIMD) | Nicht-deterministisch | ✗ | Ja |
| Map | Map<K, V> | Heap | Nicht-deterministisch | ✗ | Ja |
| Eigenschaft | [N]T | array<T> | parallel Array<T> | Map<K,V> |
|---|---|---|---|---|
| Größe zur Compile-Zeit | Ja | Nein | Nein | Nein |
| Bounds-Checking | Compile + Runtime | Runtime | Runtime | Runtime |
| @stack_limit-kompatibel | Ja | Nein | Nein | Nein |
| SIMD-Vektorisierung | Nein | Nein | Ja | Nein |
| Schlüssel-Zugriff | Nein | Nein | Nein | Ja |
| for key, val in | Nein | Nein | Nein | Ja |
Letzte Aktualisierung: 2026-05-22