====== Von C nach Lyx ======
Lyx liegt näher an C als an allem anderen: statisch, nativ, ohne Laufzeitsystem, Speicher von Hand. Wer C kann, schreibt nach einer Stunde brauchbares Lyx. Diese Seite zeigt, wo die Gewohnheiten tragen — und wo nicht.
→ [[lyx_-_programmiersprache:sprache|Sprachreferenz]] · [[lyx_-_programmiersprache:sprache:ffi|FFI]] · [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]] · [[lyx_-_programmiersprache:guides:speicher-praxis|Speicher in der Praxis]]
----
===== Gegenüberstellung =====
^ C ^ Lyx ^
| ''long x = 1;'' | ''var x: int64 := 1;'' |
| ''const int N = 8;'' | ''con N: int64 := 8;'' |
| ''static'' (Datei-lokal) | kein ''pub'' — Sichtbarkeit ist die Vorgabe |
| ''#include "foo.h"'' | ''import foo;'' — keine Header, keine Deklaration im Voraus |
| ''struct'' | ''struct'' |
| ''typedef struct {…} T;'' | ''type T = struct { … };'' |
| ''void f(void)'' | ''fn f(): void'' |
| ''int main(int argc, char** argv)'' | ''fn main(): int64'' + ''ArgvGet'' aus ''std.os'' |
| ''long *p;'' | ''var p: int64;'' — Zeiger sind Adressen als ''int64'' |
| ''*p'' / ''p[i]'' | ''peek64(p)'' / ''peek64(p + i * 8)'' |
| ''*p = v'' / ''p[i] = v'' | ''poke64(p, v)'' / ''poke64(p + i * 8, v)'' |
| ''malloc(n)'' / ''free(p)'' | ''alloc(n)'' / ''free(p, n)'' — **Größe auch beim Freigeben** |
| ''calloc(n, s)'' | ''allocZeroed(n * s)'' |
| ''char *s = "x";'' | ''var s: pchar := "x";'' |
| ''strlen(s)'' | ''StrLen(s)'' aus ''std.string'' |
| ''errno'' | Fehlercode als Rückgabewert |
| ''goto cleanup;'' | ''defer'' |
| ''switch'' | ''match'' (mit Vollständigkeitsprüfung) |
| ''#ifdef'' | keine Präprozessor-Ebene; Auswahl über Include-Pfad oder Attribute |
----
===== Ein Programm, das die Muster zeigt =====
unit Main;
import std.io;
import std.alloc;
import std.string;
// C: struct Punkt { long x, y; };
type TPunkt = struct { X: int64; Y: int64; };
// C: static long verdopple(long x) { return 2*x; } -> ohne pub = intern
fn Verdopple(x: int64): int64 { return x * 2; }
// C: extern int strcmp(const char*, const char*);
@cap(system.unsafe.ffi)
extern fn strlen(s: pchar): int64 link "libc.so.6";
// C: long summe(const long *a, size_t n)
fn Summe(a: int64, n: int64): int64 {
var s: int64 := 0;
var i: int64 := 0;
while (i < n) {
s := s + peek64(a + i * 8);
i := i + 1;
}
return s;
}
// C gibt oft -1 zurueck und setzt errno; Lyx gibt den Fehlercode direkt
fn Teile(a: int64, b: int64, out_erg: int64): int64 {
if (b == 0) { return 0 - 22; } // -EINVAL
poke64(out_erg, a / b);
return 0;
}
fn main(): int64 {
// Zeiger sind int64, Zugriff ueber peek/poke statt *p und p[i]
var puffer: int64 := allocZeroed(3 * 8);
poke64(puffer + 0, 10);
poke64(puffer + 8, 20);
poke64(puffer + 16, 12);
PrintLn(IntToStr(Summe(puffer, 3)));
free(puffer, 3 * 8);
// Struct auf dem Stapel, kein malloc noetig
var p: TPunkt;
p.X := 3; p.Y := 4;
PrintLn(IntToStr(Verdopple(p.X + p.Y)));
// C-Funktion ueber FFI
PrintLn(IntToStr(strlen("abcde")));
// Fehlercode statt errno
var erg: int64 := alloc(8);
var rc: int64 := Teile(10, 0, erg);
if (rc != 0) { PrintLn(StrConcat("Fehler ", IntToStr(rc))); }
free(erg, 8);
return 0;
}
42
14
5
Fehler -22
Übersetzt und ausgeführt mit ''lyxc 1.1.3I''.
----
===== Was anders ist =====
==== Kein Präprozessor ====
Kein ''#define'', kein ''#ifdef'', keine Header. Konstanten sind ''con'', Auswahl zwischen Plattformen läuft über den **Include-Pfad** (dasselbe Verfahren, mit dem [[lyx_-_programmiersprache:vega:guides:portabel|Vega sein Backend wählt]]), und Sichtbarkeit regelt ''pub''.
Der Vorteil ist derselbe wie bei Modulen anderswo: Es gibt keine zweite Wahrheit, die man synchron halten muss.
==== Zeiger sind int64, nicht typisiert ====
''peek8/16/32/64'', ''poke8/16/32/64'' und ''peekf64''/''pokef64'' sind der Zugriff. Es gibt keine Zeigerarithmetik mit implizitem Elementmaß: ''p + i * 8'' schreibt man aus. Das ist mühsamer und dafür immer sichtbar — die Sorte Fehler, die in C aus ''sizeof'' entsteht, gibt es hier nicht.
Für Puffer mit Struktur nimmt man Structs und Felder statt Offsets von Hand.
==== free braucht die Größe ====
''free(p, n)'' — dieselbe Zahl wie beim ''alloc''. Es liegt kein Kopf vor dem Block, deshalb weiß die Freigabe nichts von sich aus. Wer das nicht mag, nimmt ''malloc''/''free_mem'' aus ''std.alloc''.
==== Keine impliziten Umwandlungen ====
Keine „integer promotion", kein stillschweigendes ''int → double'', kein ''void*'' das überallhin passt. Jede Umwandlung steht als ''as'' im Quelltext. Das fängt genau die Fehler ab, für die C ''-Wconversion'' braucht.
==== Fehler kommen zurück, nicht über errno ====
Die Standardbibliothek liefert ''0'' bei Erfolg und einen negativen Code sonst — oft die vertrauten ''-EINVAL''-Zahlen. Ergebnisse gehen über ''out_buf''-Parameter, nicht über zurückgegebene Puffer; siehe [[lyx_-_programmiersprache:guides:speicher-praxis|Speicher in der Praxis]].
==== C-Bibliotheken einbinden ====
@cap(system.unsafe.ffi)
extern fn strlen(s: pchar): int64 link "libc.so.6";
Die Deklaration nennt die Bibliothek direkt; ein Linker-Aufruf entfällt. Einzelheiten samt Argumentgrenzen (acht Gleitkommawerte, Stapelargumente ab dem siebten) in [[lyx_-_programmiersprache:sprache:ffi|FFI]].
==== Ein Übersetzungsschritt ====
''lyxc app.lyx -o app'' erzeugt ein statisch gelinktes Binary — kein Makefile nötig, keine Objektdateien, kein Linkerskript. Units lassen sich mit ''--compile-unit'' vorübersetzen (''.lyu''), müssen es aber nicht.
----
===== Was Lyx zusätzlich mitbringt =====
* **Klassen mit Vererbung und virtuellen Methoden** — siehe [[lyx_-_programmiersprache:sprache:oop|OOP]]
* **''match'' mit Vollständigkeitsprüfung** statt ''switch'' mit vergessenem ''case''
* **''defer''** statt ''goto cleanup''
* **Range-Typen** mit Prüfung zur Übersetzungs- und Laufzeit
* **''@capabilities''** — daraus entsteht ein seccomp-Filter; was nicht deklariert ist, geht nicht
* **Tupel-Rückgaben** statt Ausgabeparameter, wo zwei Werte zusammengehören
----
===== Fallen für C-Gewohnheiten =====
* **''free(p)'' ohne Größe** — übersetzt nicht; ''free(p, n)'' oder ''free_mem(p)''.
* **''p[i]''** gibt es nicht für rohe Zeiger; nur für Arrays und dynamische Arrays.
* **''0'' ist nicht ''null''** bei Klassentypen — dort steht ''null''.
* **Kein ''sizeof''.** Größen stehen als Zahl da (''8'' für ''int64'') oder kommen aus ''size_of_int64()''.
* **Groß-/Kleinschreibung zählt**, und ''Set'', ''Map'', ''to'', ''layout'' sind reserviert.
* **Uninitialisierter Heap** ist auch hier Müll: ''allocZeroed'' nehmen, wenn gelesen wird, bevor alles geschrieben ist.
Letzte Aktualisierung: 2026-08-19 — Beispiel mit ''lyxc 1.1.3I'' übersetzt und ausgeführt.