Go ist Pass-by-Value: jede Funktion, jede Methode, jeder Channel-Send bekommt eine Kopie ihrer Argumente. Das ist die Default-Semantik, und sie ist absichtlich so gewählt — kein implizites Aliasing, kein „der Aufrufer weiß nicht, was passiert". Ein Pointer ist der Mechanismus, mit dem du diese Default-Kopie gezielt überspringst: Wer mutieren will, wer eine teure Kopie vermeiden will, wer „kein Wert vorhanden" über nil ausdrücken will — der nimmt einen Pointer. Dieser Artikel ordnet die drei Hauptgründe für Pointer-Verwendung, arbeitet das Receiver-Type-Idiom an Methoden gründlich durch und macht die Stellen explizit, an denen ein Pointer nicht die richtige Antwort ist.

Pass-by-Value — das formale Fundament

Bevor wir entscheiden, wann du einen Pointer brauchst, müssen wir die Default-Semantik fest verankern. Die Go-Spec ist im Abschnitt Calls unmissverständlich:

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.

Lies das langsam: passed by value. Jeder Parameter ist eine eigene Variable, die mit einer Kopie des Arguments initialisiert wird. Was die Funktion an ihren Parametern verändert, sieht der Aufrufer nicht — die Originale liegen woanders im Speicher.

Go pass-by-value.go
package main

import "fmt"

func tryToChange(x int) {
    x = 999 // schreibt nur in die lokale Kopie
}

func main() {
    n := 42
    tryToChange(n)
    fmt.Println(n) // 42 — unverändert
}
Output
42

Das gilt für alle Typen — int, string, bool, Struct, Array, Slice-Header, Map-Header, Channel-Handle, Pointer-Variable selbst. Bei Referenz-Typen (Slice, Map, Channel) wird der Header kopiert; die referenzierten Daten teilen sich Kopie und Original. Dazu gleich mehr — die Unterscheidung ist wichtig.

Grund 1 — Mutation

Wenn eine Funktion eine Variable des Aufrufers verändern soll, reicht Pass-by-Value nicht. Der klassische Fall ist ein „Setter" oder „Updater":

Go mutation.go
package main

import "fmt"

type Counter struct {
    N int
}

// Wert-Parameter: arbeitet auf einer Kopie. Aufrufer sieht nichts.
func incByValue(c Counter) {
    c.N++
}

// Pointer-Parameter: schreibt durch die Adresse in das Original.
func incByPointer(c *Counter) {
    c.N++
}

func main() {
    c := Counter{N: 0}

    incByValue(c)
    fmt.Println("nach Wert-Aufruf:   ", c.N) // 0

    incByPointer(&c)
    fmt.Println("nach Pointer-Aufruf:", c.N) // 1
}
Output
nach Wert-Aufruf:    0
nach Pointer-Aufruf: 1

Die Mechanik: incByPointer(&c) übergibt die Adresse von c als Kopie eines Pointers. Innerhalb der Funktion ist c (der Parameter) ein neuer Pointer — aber er zeigt auf dieselbe Variable wie das Original. Der Zugriff c.N++ wird zu (*c).N++ mit dem von Go automatisch eingefügten Stern-Operator — und schreibt in den Original-Speicher.

Faustregel: Wer mutieren will, nimmt einen Pointer. Es gibt keine andere Sprach-Mechanik dafür — keine inout-Parameter, keine Referenz-Typen wie in C++. Nur Pointer.

Grund 2 — große Kopien vermeiden

Auf 64-Bit-Systemen ist ein Pointer 8 Byte groß. Ein Struct mit zwanzig Feldern kann dagegen mehrere hundert Byte belegen. Bei jedem Funktions-Aufruf, der einen solchen Struct als Wert übergibt, wird der gesamte Inhalt kopiert — bei jedem Methodenaufruf, bei jedem Channel-Send, bei jeder Iteration einer Slice von Structs.

Go performance.go
package main

import (
    "fmt"
    "unsafe"
)

type Big struct {
    ID       int64
    Name     string
    Tags     [16]string
    Metadata [256]byte
    Flags    [32]bool
}

// Wert-Parameter: bei jedem Aufruf wird die volle Größe kopiert.
func sumByValue(b Big) int {
    return int(b.ID)
}

// Pointer-Parameter: 8 Byte (Pointer-Größe), egal wie groß Big ist.
func sumByPointer(b *Big) int {
    return int(b.ID)
}

func main() {
    fmt.Println("sizeof(Big)    =", unsafe.Sizeof(Big{}))
    fmt.Println("sizeof(*Big)   =", unsafe.Sizeof(&Big{}))
}
Output
sizeof(Big)    = 624
sizeof(*Big)   = 8

Faustregel der Community: Ab etwa 64 Byte Struct-Größe könnte ein Pointer günstiger sein als die Kopie — verbindlich ist das aber nicht. Drei Punkte zur Einordnung:

  • Messen, nicht raten. Der Compiler ist gut darin, kleine Kopien zu vermeiden, Felder auf Register zu legen und Inlining zu betreiben. Erst mit go test -bench=... und pprof weißt du, ob die Kopie wirklich der Engpass ist.
  • Escape-Analyse. Ein Pointer-Parameter kann den Compiler dazu zwingen, den referenzierten Wert auf dem Heap zu allokieren statt auf dem Stack. Das ist teurer als eine kleine Kopie — manchmal kostet ein Pointer also mehr Performance, nicht weniger.
  • Cache-Lokalität. Ein Slice von kleinen Wert-Structs liegt im Speicher kontiguierlich; ein Slice von Pointern enthält 8-Byte-Indirektionen zu verstreuten Heap-Adressen. Für Iteration kann der Wert-Slice deutlich schneller sein, obwohl die Kopien „teurer" wirken.

Heißt: Performance ist ein möglicher, aber selten der dominante Grund. In der Praxis greift fast immer Grund 1 (Mutation) oder Grund 6 (Konsistenz mit Receiver-Type, siehe unten).

Grund 3 — optionale Werte über nil

Go kennt kein Option<T> oder Maybe<T>. Wer ausdrücken will, dass ein Wert vielleicht nicht da ist, hat zwei idiomatische Wege: ein (wert, ok)-Tupel für lokale Lookups oder einen Pointer, der nil sein darf. Das zweite Pattern siehst du überall in der Stdlib und in REST-API-Modellen:

Go optional.go
package main

import "fmt"

type UpdateUser struct {
    // Pflichtfeld — direkt der Wert
    ID int64

    // Optionale Felder — Pointer, weil ein nil-Wert „nicht ändern"
    // bedeutet, ein gesetzter Wert dagegen „auf diesen Wert setzen".
    Name  *string
    Email *string
    Age   *int
}

func applyUpdate(u UpdateUser) {
    fmt.Printf("ID=%d\n", u.ID)
    if u.Name != nil {
        fmt.Printf("  Name -> %q\n", *u.Name)
    }
    if u.Email != nil {
        fmt.Printf("  Email -> %q\n", *u.Email)
    }
    if u.Age != nil {
        fmt.Printf("  Age -> %d\n", *u.Age)
    }
}

func main() {
    newName := "Alice"
    newAge := 30

    // Nur Name und Age sollen aktualisiert werden — Email bleibt unverändert.
    applyUpdate(UpdateUser{ID: 1, Name: &newName, Age: &newAge})
}
Output
ID=1
  Name -> "Alice"
  Age -> 30

Bei primitiven Typen ist die Unterscheidung „nicht gesetzt" vs. „gesetzt auf Zero Value" sonst nicht möglich. Name string mit Wert "" kann sowohl „leerer Name explizit gewünscht" als auch „kein Update" bedeuten. Mit *string ist die Bedeutung eindeutig — nil heißt „nicht gesetzt", &amp;"" heißt „explizit leer".

Das Pattern hat einen Preis: jeder Zugriff braucht eine Nil-Prüfung, sonst paniciert die Dereferenzierung. Mehr dazu im nil-Pointer-Artikel.

Receiver-Typ — die praktisch wichtigste Entscheidung

Bei Methoden triffst du die Pointer-vs-Wert-Entscheidung an einer einzigen Stelle: dem Receiver. (t T) M() ist eine Value-Receiver-Methode, (t *T) M() ist eine Pointer-Receiver-Methode. Der Unterschied ist derselbe wie bei Funktions-Parametern:

Go receiver.go
package main

import "fmt"

type Counter struct {
    N int
}

// Value-Receiver — c ist eine Kopie. Mutation hier verpufft.
func (c Counter) IncByValue() {
    c.N++
}

// Pointer-Receiver — c zeigt auf das Original. Mutation wirkt.
func (c *Counter) IncByPointer() {
    c.N++
}

func main() {
    c := Counter{N: 0}

    c.IncByValue()
    fmt.Println("nach Value-Receiver:  ", c.N) // 0

    c.IncByPointer() // Go fügt &amp; automatisch ein
    fmt.Println("nach Pointer-Receiver:", c.N) // 1
}
Output
nach Value-Receiver:   0
nach Pointer-Receiver: 1

Beachte: c.IncByPointer() funktioniert auch dann, wenn c ein Counter ist (kein *Counter). Effective Go beschreibt diese Komfort-Regel präzise:

When the value is addressable, the language takes care of the common case of invoking a pointer method on a value by inserting the address operator automatically.

Heißt: Der Compiler nimmt (&amp;c).IncByPointer() für dich, solange c adressierbar ist. Bei Werten in Maps (m[key].IncByPointer()) oder Return-Werten (f().IncByPointer()) geht das nicht — die sind nicht adressierbar. Die Mechanik steht detailliert im Adress-Operator-Artikel.

Die Spec-Regel zum Method-Set kommt zusätzlich ins Spiel, sobald Interfaces beteiligt sind:

The method set of a type T consists of all methods declared with receiver type T. The method set of a pointer type *T (where T is not a pointer or interface type) is the set of all methods declared with receiver *T or T.

Konsequenz: *T hat mehr Methoden als T. Wer ein Interface implementieren will, das eine Pointer-Receiver-Methode verlangt, muss den Pointer-Typ zuweisen — der Wert allein erfüllt das Interface nicht. Mehr dazu in den Pitfalls.

Konsistenz-Regel — alle Methoden eines Typs gleich

Die Go-Code-Review-Comments fassen die wichtigste Praxis-Regel in einem Satz zusammen:

Don't mix receiver types. Choose either pointers or struct types for all available methods.

Wer einen Typ mit zehn Methoden hat, sollte nicht fünf davon mit Value-Receiver und fünf mit Pointer-Receiver schreiben. Gründe:

  • Method-Set-Asymmetrie. Werte und Pointer auf den Typ haben dann unterschiedliche Method-Sets — der Wert kann nicht alle Methoden aufrufen. Das ist verwirrend und produziert subtile Interface-Bugs.
  • Lesbarkeit. Ein konsistenter Typ liest sich „dieser Typ wird per Pointer benutzt" oder „dieser Typ wird per Wert benutzt". Mischformen zwingen den Leser, bei jeder Methode neu zu überlegen.
  • Kopier-Semantik. Wenn auch nur eine Methode mutiert, muss der gesamte Typ als veränderbar gedacht werden — sonst werden die anderen Methoden auf veralteten Kopien aufgerufen.
Go konsistenz.go
package main

type Cache struct {
    data map[string]string
}

// Konsistent: alle Methoden Pointer-Receiver, weil Set mutiert.
func (c *Cache) Set(key, value string) { c.data[key] = value }
func (c *Cache) Get(key string) string { return c.data[key] }
func (c *Cache) Len() int              { return len(c.data) }

// ANTI-PATTERN — mischt Receiver-Typen:
// func (c *Cache) Set(...)       // Pointer für Mutation
// func (c Cache)  Get(...) string // Wert „weil es nur liest"
// ...
// Effekt: *Cache implementiert mehr Interfaces als Cache.
// Aufrufer müssen wissen, welche Methode welchen Receiver hat.

Faustregel: Sobald eine Methode des Typs Pointer-Receiver braucht, kriegen alle Pointer-Receiver. Die einzige Ausnahme sind die wenigen Typen, die bewusst als Value benutzt werden (time.Time, kleine Vec3-Strukturen aus Mathe-Libs) — dort sind alle Methoden Value-Receiver.

Pointer-Receiver ist Pflicht bei…

Drei Situationen lassen dir keine Wahl — Pointer-Receiver oder das Programm verhält sich falsch:

(1) Mutation. Wenn die Methode Felder des Receivers ändert, muss es ein Pointer-Receiver sein. Sonst arbeitet die Methode auf einer Kopie, und der Aufrufer sieht die Änderung nie.

Go muss-pointer-mutation.go
type Stack struct {
    items []int
}

// PFLICHT Pointer-Receiver — append kann die items-Slice ersetzen.
func (s *Stack) Push(x int) {
    s.items = append(s.items, x)
}

(2) sync-Primitives als Felder. sync.Mutex, sync.WaitGroup, sync.Once, sync.Cond — alle dürfen nach erstem Gebrauch nicht kopiert werden. Die Dokumentation jeder dieser Typen sagt das explizit („A Mutex must not be copied after first use"). Ein Value-Receiver würde bei jedem Methoden-Aufruf eine Kopie machen — und auf der Kopie zu locken hat keine Wirkung auf das Original.

Go muss-pointer-mutex.go
import "sync"

type SafeCounter struct {
    mu sync.Mutex
    n  int
}

// PFLICHT Pointer-Receiver — sonst wird mu bei jedem Aufruf kopiert.
func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.n++
}

(3) Große Structs. Ab einer gewissen Größe ist die Pointer-Übergabe günstiger als die Kopie. „Gewisse Größe" ist Faustregel-Land — circa 64 Byte und aufwärts ist ein üblicher Schwellwert, aber gemessen schlägt geschätzt jedes Mal.

Value-Receiver ist OK bei…

Es gibt Typen, bei denen Value-Receiver die bessere Wahl ist:

  • Kleine, unveränderbare Wert-Typen. time.Time ist das kanonische Beispiel: 24 Byte groß, immutable per Konvention, Methoden geben neue Werte zurück statt zu mutieren. Alle time.Time-Methoden haben Value-Receiver.
  • Identitätslose Werte. Wenn zwei Instanzen mit gleichem Inhalt auch gleich sein sollen — ein Point{X, Y}, ein Color{R, G, B}, ein Money{Amount, Currency} — passen Value-Receiver. Die Mathematik dahinter rechnet mit Werten, nicht mit Referenzen.
  • Primitiv-Wrapper. Eigene Typen über int, string, float64 mit ein paar Methoden (Currency, UserID, Celsius) sind klein und werden meist nur gelesen — Value-Receiver ist hier idiomatisch.
Go value-receiver-ok.go
package main

import "fmt"

type Celsius float64

// Value-Receiver — Celsius ist 8 Byte, immutable.
func (c Celsius) Fahrenheit() float64 {
    return float64(c)*9/5 + 32
}

type Point struct {
    X, Y float64
}

// Value-Receiver — Point ist 16 Byte, identitätslos.
// Add gibt einen neuen Point zurück, mutiert nichts.
func (p Point) Add(q Point) Point {
    return Point{X: p.X + q.X, Y: p.Y + q.Y}
}

func main() {
    t := Celsius(20)
    fmt.Println(t.Fahrenheit())

    a := Point{1, 2}
    b := Point{3, 4}
    fmt.Println(a.Add(b))
}
Output
68
{4 6}

Wann KEIN Pointer

Die spiegelbildliche Frage: in welchen Fällen ist ein Pointer-Parameter oder -Receiver unnötig oder sogar falsch?

Strings. Ein String-Header ist 16 Byte (Pointer + Länge) und immutable. *string als Parameter ist kein Performance-Gewinn, weil die Kopie eines String-Headers ohnehin minimal ist — und immutability bedeutet, dass Mutation als Grund wegfällt. Ausnahme: optionale Strings (Grund 3).

Slices, Maps, Channels. Das sind bereits Referenz-Typen — der Wert enthält intern einen Pointer auf die eigentlichen Daten. Eine Funktion mit func f(s []int) kann die Elemente von s verändern, weil der Slice-Header eine Kopie ist, aber das zugrunde liegende Array geteilt wird:

Go slice-mutation.go
package main

import "fmt"

// Kein Pointer nötig — die Slice teilt sich das Backing-Array.
func zeroFirst(s []int) {
    if len(s) > 0 {
        s[0] = 0
    }
}

func main() {
    xs := []int{10, 20, 30}
    zeroFirst(xs)
    fmt.Println(xs) // [0 20 30] — Original wurde geändert
}
Output
[0 20 30]

Die einzige Ausnahme: wenn die Funktion den Slice-Header selbst ersetzen will (append kann reallozieren, Länge ändert sich), braucht der Aufrufer entweder einen Pointer-Parameter oder muss den neuen Slice als Rückgabewert annehmen. Idiomatisch ist nicht *[]int, sondern func f(s []int) []int mit Rückgabe — siehe Stdlib append.

Kleine Structs ohne Mutation. Ein Point{X, Y float64} ist 16 Byte — die Kopie kostet praktisch nichts, dafür entfällt die Nil-Prüfung, und Werte sind in Maps und als Map-Keys nutzbar.

Praxis-Beispiele — drei typische Muster

Builder-Pattern. Eine Kette von Methoden, die einen Wert schrittweise konfiguriert. Pointer-Receiver, damit jede .With...()-Methode tatsächlich am selben Objekt arbeitet:

Go builder.go
package main

import "fmt"

type RequestBuilder struct {
    method  string
    url     string
    headers map[string]string
}

func NewRequest(url string) *RequestBuilder {
    return &RequestBuilder{
        method:  "GET",
        url:     url,
        headers: map[string]string{},
    }
}

// Alle With-Methoden mutieren und geben *self zurück → Chaining.
func (r *RequestBuilder) WithMethod(m string) *RequestBuilder {
    r.method = m
    return r
}

func (r *RequestBuilder) WithHeader(k, v string) *RequestBuilder {
    r.headers[k] = v
    return r
}

func main() {
    req := NewRequest("https://example.com").
        WithMethod("POST").
        WithHeader("Content-Type", "application/json")

    fmt.Printf("%s %s %v\n", req.method, req.url, req.headers)
}
Output
POST https://example.com map[Content-Type:application/json]

Config-Override mit Default-Werten. Ein Pointer-Parameter erlaubt nil als „nimm Defaults":

Go config-override.go
type Options struct {
    Timeout int
    Retries int
}

func defaults() Options {
    return Options{Timeout: 30, Retries: 3}
}

// nil-Pointer = nimm alles vom Default. Sonst überschreibt opts.
func openConn(addr string, opts *Options) {
    cfg := defaults()
    if opts != nil {
        cfg = *opts
    }
    _ = cfg
}

Immutable-Wrapper mit Value-Receiver. Methoden geben neue Werte zurück, das Original bleibt unangetastet. Das ist die time.Time-Strategie:

Go immutable.go
package main

import "fmt"

type Money struct {
    Amount   int64
    Currency string
}

// Value-Receiver — Add baut einen neuen Money-Wert.
func (m Money) Add(other Money) Money {
    if m.Currency != other.Currency {
        panic("Währungs-Mismatch")
    }
    return Money{Amount: m.Amount + other.Amount, Currency: m.Currency}
}

func main() {
    a := Money{Amount: 100, Currency: "EUR"}
    b := Money{Amount: 250, Currency: "EUR"}
    c := a.Add(b)
    fmt.Printf("a=%+v b=%+v c=%+v\n", a, b, c)
}
Output
a={Amount:100 Currency:EUR} b={Amount:250 Currency:EUR} c={Amount:350 Currency:EUR}

a und b bleiben nach a.Add(b) unverändert — die Methode konstruiert ein neues Money. Das ist robuste, leicht zu testende Code-Form, weil keine Aliasing-Effekte zwischen Aufrufern entstehen.

Entscheidungs-Matrix

Die Code-Review-Comments fassen die Wahl in einer praxistauglichen Liste zusammen. Hier in Tabellen-Form:

SituationPointer oder Wert
Methode soll Receiver mutierenPointer
Receiver enthält sync.Mutex oder ähnlichesPointer
Receiver ist großer Struct/Array (Faustregel ab ~64 Byte)Pointer
Receiver ist Map, Func, Channel, Slice (ohne Reslicing)Wert
Receiver ist kleiner, immutable Wert (z. B. time.Time, Point)Wert
Receiver ist primitiver Wrapper (eigener int/string-Typ)Wert
Gemischter Typ mit teils Mutation, teils nichtPointer (Konsistenz)
Im ZweifelPointer (Effective-Go-Empfehlung)

Effective Go schließt mit der Trumpf-Regel: „Finally, when in doubt, use a pointer receiver." Das ist keine Aufforderung, alles per Pointer zu machen — es ist eine Aufforderung, im Grenzfall die Mutations-fähige Variante zu wählen, damit spätere Erweiterungen die Signatur nicht brechen.

Häufige Stolperfallen

Pointer auf Slice, Map oder Channel ist meistens Code-Smell.

Diese Typen tragen intern bereits einen Header, der auf die Daten zeigt — *[]int, *map[string]int, *chan int sind in 99 % der Fälle unnötig. Wer Elemente mutiert, braucht keinen Pointer (das Backing-Array wird geteilt). Eine echte Ausnahme: wenn die Funktion den Slice-Header selbst tauschen will, ist die idiomatische Antwort meistens func f(s []int) []int mit Rückgabe, nicht *[]int.

Receiver-Typen niemals mischen.

Wenn auch nur eine Methode des Typs einen Pointer-Receiver hat, sollten alle anderen Methoden ebenfalls Pointer-Receiver bekommen. Sonst stimmt das Method-Set von T und *T nicht überein — Interfaces verhalten sich asymmetrisch, und Reviewer müssen bei jeder Methode neu nachschlagen.

sync.Mutex mit Value-Receiver ist ein versteckter Bug.

Ein sync.Mutex darf nach erstem Gebrauch nicht kopiert werden — das steht in der Doku. Ein Value-Receiver kopiert den Mutex bei jedem Methoden-Aufruf, das Lock() greift auf die Kopie, das Original bleibt frei. go vet warnt mit passes lock by value — die Warnung sollte man niemals ignorieren.

Großer Struct als Value-Receiver kopiert pro Aufruf.

Wer einen Struct mit 500 Byte hat und alle Methoden mit Value-Receiver schreibt, kopiert bei jedem Aufruf 500 Byte — selbst bei reinen Read-Methoden. Bei einem Hot-Path mit Millionen Aufrufen pro Sekunde wird das messbar. Lösung: Pointer-Receiver, sobald der Struct nennenswert groß wird.

Defensive Kopie — wer Pointer entgegennimmt und mutiert, muss das dokumentieren.

Ein Funktions-Parameter func update(c *Config) darf den übergebenen Config-Wert ändern — der Aufrufer sieht das. Wenn der Aufrufer das nicht erwartet (er hat den Pointer nur für Performance übergeben), entsteht ein subtiler Bug. API-Konvention: entweder im Doc-Kommentar klar sagen „mutiert den Parameter", oder defensiv eine Kopie ziehen, bevor du schreibst.

*io.Reader ist fast immer falsch — Interfaces nimmt man per Wert.

Interface-Werte enthalten intern bereits einen Pointer auf den konkreten Wert. *io.Reader als Parameter-Typ doppelt die Indirektion und macht die Nutzung umständlich. Der Standard ist func f(r io.Reader) — wer das Interface nullable braucht, prüft r == nil.

Zero-Value-Useful — bytes.Buffer & Co. brauchen Pointer-Receiver, sind aber als Value initialisierbar.

var buf bytes.Buffer ist sofort einsatzbereit — kein New-Aufruf nötig. Trotzdem haben alle Buffer-Methoden Pointer-Receiver, weil sie intern mutieren. Die Kombination ist idiomatisch: man deklariert per Wert, ruft Methoden auf, Go fügt &amp; automatisch ein. Wer den Buffer kopiert (buf2 := buf), kriegt einen leeren neuen Buffer — also nicht kopieren.

Interface-Implementation — *T und T haben unterschiedliche Method-Sets.

Wenn ein Typ eine Pointer-Receiver-Methode hat, implementiert nur *T das Interface, das diese Methode verlangt — nicht T. var w io.Writer = myBuf funktioniert nicht, wenn Write Pointer-Receiver hat; var w io.Writer = &amp;myBuf schon. Klassischer Stolperer beim ersten Interface-Bug.

Bei Generics — Type-Parameter T ist Wert, *T baut man explizit.

Ein generischer Container type Box[T any] struct { v T } speichert T per Wert. Wer einen Pointer-Container will, schreibt Box[*MyType] — der Type-Parameter wird zum Pointer-Typ. new(T) in generischem Code gibt *T zurück und ist eines der seltenen sinnvollen Einsatzgebiete des new-Built-ins.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Pointer

Zur Übersicht