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.
Globale Arrays und Structs tragen seit lyxc 1.0.17C (#1256, #1299). Auf Modulebene deklarierte Aggregate verhalten sich jetzt wie lokale:var q: [8]int64;nimmt einen schreibenden Indexzugriff an, ein Initialisierer (:= [1,2,3,4,5,6,7,8]) bleibt erhalten, und ein Feldzugriff auf einen globalen Struct (s.x := 3) wirkt.
Bis 1.0.16x stürzte der schreibende Zugriff ab, der Initialisierer wurde verworfen, und Struct-Felder blieben still auf0.
→ 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; // ohne Initialisierer
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 → panic, aber nur mit --runtime-checks
Die Laufzeitprüfung hängt am Schalter–runtime-checks. Ohne ihn liest ein Zugriff außerhalb der Grenzen kommentarlos fremden Speicher:a[9]auf einem[4]int64liefert einen beliebigen Wert statt eines Abbruchs. Mit dem Schalter meldet das Programmindex out of boundsund endet mit Exit 1. Für sicherheitskritische Builds gehört er damit zur Pflichtausstattung.
Mehrdimensionale Arrays tragen seit lyxc 1.0.17C (#1230).var m: [3][4]int64;lässt sich lesen und schreiben (m[1][2] := 7). Umgesetzt ist genau eine Verschachtelung: die Daten liegen flach (N*M Slots hinter einem{cap,len}-Kopf),m[i]liefert die Adresse der Zeile,m[i][j]liest von dort. Eine ganze Zeile zuzuweisen (m[i] := x) und eine dritte Dimension werden abgewiesen. Bis 1.0.16x endete jeder Zugriff mit Exit 139.
–runtime-checks (kontrollierter 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];
PrintLn(IntToStr(len(list))); // 3 — len() ist ein Builtin, keine Methode
PrintLn(IntToStr(list[0])); // 10
list[0] := list[0] + 1; // Lesen und Schreiben
list[2] := 999;
list.push(40); // Anhängen
var letzter: int64 := list.pop(); // Entnehmen — liefert 40
dispose list; // Zwingend — sonst Memory Leak
Eine blosse Deklaration ohne := legt seit lyxc 1.0.13 ein leeres Array an — push darauf funktioniert, und len() zählt korrekt mit:
var u: array<int64>; // ohne Initialisierer
u.push(5);
u.push(7);
PrintLn(IntToStr(len(u))); // 2
PrintLn(IntToStr(u[0])); // 5
Dasselbe gilt für ein Array aus einem Literal: len() folgt seit lyxc 1.0.15B auch dort dem push.
len() auf einem leeren Array-Literal liefert 0, ohne dass vorher ein push nötig wäre. Bis lyxc 1.1.2H legte := [] keinen Kopf an und len() las an Offset 0 eines Nullzeigers — Speicherzugriffsfehler in genau der Zeile, mit der jede einsammelnde Schleife beginnt (#1642).
import std.io;
fn main(): int64 {
var a: array := [];
PrintInt(len(a)); // 0
return 0;
}
var l := [1, 2, 3];
push(l, 4);
PrintLn(IntToStr(len(l))); // 4
Die Bereichsprüfung zur Compile-Zeit entfällt für eine solche Variable, sobald sie im selben Rumpf wächst — erkannt an push, append oder pop, in freier Form (push(a, x)) wie in Methodenform (a.push(x)). Nur bei einer Deklaration mit fester Größe ([N]T) bleibt die vermerkte Größe die Schranke.
Dynamische Arrays taugen seit lyxc 1.0.17C als Funktionsparameter (#1197).fn F(a: array<int64>): int64 { return a[0]; }liefert den Elementwert; bis 1.0.16x kam dort eine Adresse an, also eine wechselnde Zahl. Dasselbe galt für Feldzugriffe und Verkettungen — auch die liefern jetzt Werte statt Adressen.
An einem Array sind genau vier Feldzugriffe gültig:
| Zugriff | Bedeutung |
|---|---|
a.length, a.len | Anzahl der Elemente |
a.cap | Kapazität |
a.data | Zeiger auf das erste Element (mit peek64 lesbar) |
Jeder andere Feldname wird seit lyxc 1.0.15B mit unknown field abgewiesen — ein Tippfehler wie list.laenge ist damit nicht mehr von einem gültigen Feld zu unterscheiden, sondern ein Übersetzungsfehler. Dasselbe gilt an eingebauten Grundtypen: n.egal an einem int64 meldet unknown field.
list.len(),list.cap()undlist.clear()als Methoden gibt es weiterhin nicht — der Aufruf meldetundefined function. Die Länge liefert das Builtinlen(list)oder das Feldlist.len.
Verlässt eine Funktion über mehrere Pfade, muss dispose vor jedem return stehen:
fn LoadConfig(path: pchar): bool {
var cfg: Map<int64, 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, int64
Maps erlauben den Zugriff über einen Schlüssel. Lyx bietet native Literal-Syntax seit v0.5.0; Lesen und Schreiben über ganzzahlige Schlüssel sind seit #1152 umgesetzt und mit lyxc 1.1.11B nachgemessen (m[3] := 7 → m[3] ergibt 7). Eine Iteration über Schlüssel-Wert-Paare gibt es weiterhin nicht.
Nur ganzzahlige Schlüssel. Die Laufzeit vergleicht den Schlüssel als Zahl. Beipcharwäre das die Adresse — zwei gleich geschriebene Literale haben verschiedene Adressen,m["Alpha"]fände seinen eigenen Eintrag also nicht wieder.Map<pchar, V>wird deshalb bereits bei der Deklaration abgewiesen, statt still danebenzugreifen. Für Zeichenketten-Schlüssel den Schlüssel selbst hashen (std.hash) und den Hash als ganzzahligen Schlüssel führen. (Inhaltsvergleich ist als Issue #1291 aufgesetzt.)
import std.io;
var scores: Map<int64, int64> := {1: 100, 2: 250, 3: 175};
// Lesen — ein fehlender Schlüssel liefert 0
var s: int64 := scores[1]; // 100
// Schreiben / Einfügen
scores[4] := 300;
// Existenzprüfung mit in — immer vor Zugriff auf unbekannte Schlüssel
if (4 in scores) {
PrintLn(IntToStr(scores[4])); // 300
}
// Anzahl der Einträge
PrintLn(IntToStr(len(scores))); // 4
dispose scores;
Eine Deklaration ohne Initialisierer legt eine leere, sofort benutzbare Map an:
var m: Map<int64, int64>;
m[1] := 5;
PrintLn(IntToStr(len(m))); // 1
delete m[k]undfor key, val in m dogibt es nicht. Beide sind im Parser nicht umgesetzt;deletemeldetundefined symbol. Einen Eintrag „entfernt„ man ersatzweise durch Überschreiben mit einem Sentinel-Wert, und iteriert über die Schlüssel, indem man sie zusätzlich in einemarray<int64>mitführt.
(Issue #1152)
Die Laufzeit ist eine Hashtabelle mit offener Adressierung: Sondiert wird ab key & (cap-1). Zugriff damit O(1) amortisiert, unabhängig von der Anzahl der Einträge.
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<int64, 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 — nur ganzzahlige Schlüssel |
| for key, val in | Nein | Nein | Nein | Nein — for key, val in m endet mit Parse error: expected :=, got ', (mit 1.1.11B nachgemessen) |
Eine Iteration über die Elemente eines Arrays (for v in a do) gibt es nicht — der Versuch meldet for … in erwartet einen Bereich. Über ein Array läuft man mit Index:
for i := 0; i < len(a); i++ { PrintLn(IntToStr(a[i])); }
Für Zählschleifen stehen daneben for i := 0 to N, for i in range(N) und seit lyxc 1.0.14 auch for i in 0..N zur Verfügung — letzteres mit einschließender Obergrenze.
Letzte Aktualisierung: 2026-08-19 — len() auf dem leeren Array-Literal (#1642) ergänzt; Beispiel mit lyxc 1.1.3I übersetzt und ausgeführt.
Vorherige Aktualisierung: 2026-08-11
Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).