Menu

C# vs. C++: Speicher, Performance, Spieleentwicklung und Lernkurve

C# ist eine verwaltete Sprache mit Garbage Collector; C++ kompiliert in nativen Code und gibt dir manuelle Kontrolle über den Speicher. Wie sich dieser Unterschied bei Sicherheit, Performance, Game Engines, Build-Modell und Lernaufwand zeigt.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

C# und C++ teilen sich einen Buchstaben und die Syntax mit geschweiften Klammern, aber sie arbeiten auf unterschiedlichen Ebenen. C++ (1985) kompiliert direkt in Maschinencode und lässt dich genau bestimmen, wo jedes Objekt liegt und wann es stirbt. C# (2002) kompiliert in eine Zwischensprache, die die .NET-Runtime zur Laufzeit in Maschinencode übersetzt, und ein Garbage Collector gibt Speicher für dich frei. Fast jeder Unterschied unten folgt aus dieser einen Entscheidung.

Auf einen Blick

C#C++
AusführungVerwaltet: IL, per JIT von der CLR kompiliertNativ: vorab in Maschinencode kompiliert
SpeicherGarbage CollectorManuell, RAII, Smart Pointer
ZeigerReferenzen; rohe Zeiger nur in unsafe-CodeRohe Zeiger und Referenzen überall
SicherheitArrays mit Grenzprüfung, keine hängenden ReferenzenUndefiniertes Verhalten bei Zugriff außerhalb der Grenzen, Use after free
Build-ModellProjekte und Assemblies, keine HeaderHeader, Präprozessor, Übersetzungseinheiten, Linker
GenericsGenerics, einmal geprüft, zur Laufzeit aufgelöstTemplates, zur Kompilierzeit instanziiert
StandardbibliothekGroß: Collections, HTTP, JSON, Dateien, ThreadsKleiner: Container, Algorithmen, Threads
KompiliergeschwindigkeitSchnellLangsam bei großen Codebasen
SpieleUnity, GodotUnreal, die meisten hauseigenen AAA-Engines
Weitere HaupteinsätzeWeb-Backends, Desktop, Cloud, ToolsEngines, Browser, Betriebssysteme, Embedded, Trading

Speicher: Garbage Collector vs. RAII

In C++ wird ein Objekt mit automatischer Speicherdauer zerstört, wenn es den Gültigkeitsbereich verlässt, und sein Destruktor läuft genau in diesem Moment. Heap-Objekte gehören Smart Pointern (std::unique_ptr, std::shared_ptr) oder werden von Hand mit new und delete verwaltet. Dieses Muster, Resource Acquisition Is Initialization (RAII), sorgt für deterministisches Aufräumen von Speicher und jeder anderen Ressource.

// C++
#include <memory>
#include <fstream>

void save() {
    std::ofstream file("log.txt");          // opened here
    auto buffer = std::make_unique<char[]>(4096);
    file << "saved\n";
}   // file closed and buffer freed here, in reverse order

In C# liegt jede Klasseninstanz auf dem verwalteten Heap, und der Garbage Collector gibt sie irgendwann später frei, sobald nichts mehr auf sie verweist. Du schreibst nie delete und kannst nie etwas freigeben, das noch benutzt wird. Speicher ist erledigt; was der GC nicht zeitnah erledigt, sind andere Ressourcen: Dateien, Sockets, Datenbankverbindungen. Dafür hat C# IDisposable und die using-Anweisung, die am Ende eines Blocks Dispose aufruft, das Nächste, was einem Destruktor entspricht:

Ausgabe:

open db
open cache
db <- SELECT 1
cache <- PING
close cache
close db
done

Der Unterschied: In C++ ist das Aufräumen für jedes lokale Objekt und jedes Objekt im Besitz eines Smart Pointers an den Gültigkeitsbereich gebunden, in C# ist es für Speicher automatisch und für alles andere optional (über using). Mehr dazu auf der Seite zur using-Anweisung.

Sicherheit: Exceptions statt undefiniertem Verhalten

In C++ über das Ende eines Arrays hinaus zu lesen ist undefiniertes Verhalten: Das Programm gibt vielleicht Unsinn aus, stürzt ab oder läuft mit beschädigtem Speicher weiter, und das Ergebnis kann sich zwischen Builds ändern. Derselbe Fehler wirft in C# eine Exception genau in dieser Zeile.

// C++: compiles, and the behavior is undefined
int scores[3] = {90, 85, 77};
int x = scores[5];   // reads whatever is in memory there

Ausgabe:

Index 5 is outside an array of length 3
name was null

Die ganze Klasse der Speicherfehler (Pufferüberläufe, Use after free, doppeltes Freigeben, hängende Zeiger) existiert in sicherem C# nicht. Das ist ein großer Teil des Grundes, warum C#-Code schneller zu schreiben und zu reviewen ist.

Zeiger und unsafe-Code

C# hat durchaus Zeiger, aber nur in unsafe-Blöcken, und das Projekt muss das mit <AllowUnsafeBlocks>true</AllowUnsafeBlocks> erlauben. Objekte auf dem verwalteten Heap können sich bei der Garbage Collection verschieben, deshalb fixierst du sie mit fixed, bevor du ihre Adresse nimmst:

// C#, requires AllowUnsafeBlocks
unsafe
{
    int[] data = { 1, 2, 3 };
    fixed (int* p = data)
    {
        *(p + 1) = 20;   // data is now { 1, 20, 3 }
    }
}

Unsafe-Code wird für die Interoperabilität mit nativen Bibliotheken und für einige heiße Schleifen verwendet. Heute nutzt der meiste Low-Level-Code in C# stattdessen Span<T>, ref-Locals und stackalloc, die zeigerähnliche Performance bieten und die Grenzprüfungen behalten.

Performance

C++ gibt dem Compiler das ganze Programm vorab und fügt zur Laufzeit nichts hinzu: keinen Garbage Collector, kein JIT, keine Grenzprüfungen, außer du forderst sie an. Deshalb ist es die Wahl, wo jede Mikrosekunde oder jedes Byte zählt und wo Pausen nicht akzeptabel sind.

C# bezahlt seine Sicherheit mit einer Runtime, JIT-Aufwärmen beim Start und gelegentlichen GC-Pausen. Beim Durchsatz liegt es meist nur um einen kleinen Faktor hinter C++, und der Abstand ist mit jedem .NET-Release kleiner geworden: gestuftes JIT mit profilgesteuerter Optimierung, Structs und Span<T> zur Vermeidung von Allokationen, Hardware-Intrinsics für SIMD und Native AOT, das vorab in eine einzelne native ausführbare Datei kompiliert. Bei Web-APIs, Tools und Geschäftslogik dominieren Datenbank und Netzwerk, und der Unterschied der Sprache zeigt sich selten.

Spieleentwicklung: Unity vs. Unreal

Hier begegnen die meisten der Frage. Unity wird in C# geskriptet: Gameplay-Code, UI und Tools sind C#-Klassen, die an Spielobjekten hängen, während der Engine-Kern in C++ geschrieben ist. Unreal Engine ist in C++ geschrieben, und Gameplay entsteht in C++ plus dem visuellen Skriptsystem Blueprints. Godot unterstützt sowohl GDScript als auch C#.

C# mit Unity ist schneller zu lernen und erlaubt schnellere Iteration, deshalb ist es bei Indie- und Mobile-Spielen so verbreitet. Unreal und C++ sind in AAA-Studios Standard, und Engine-Programmierer arbeiten überall in C++. Ein häufiger Weg ist, mit Unity zu beginnen und dann C++ zu lernen, wenn man zu Unreal oder zur Engine-Arbeit wechselt.

Build-Modell

Ein C++-Programm ist in Header (.h, Deklarationen) und Quelldateien (.cpp, Definitionen) aufgeteilt. Der Präprozessor fügt Header in jede Quelldatei ein, jede Datei wird separat kompiliert, und der Linker fügt die Ergebnisse zusammen. Templates werden in jeder Datei instanziiert, die sie nutzt, einer der Gründe, warum große C++-Builds langsam sind.

C# hat nichts davon. Ein Projekt ist eine Menge von .cs-Dateien, die gemeinsam zu einer Assembly (.dll) kompiliert werden; die Reihenfolge der Deklarationen und der Dateien spielt keine Rolle, und ein Typ in einer Datei kann einen Typ in einer anderen ohne Include verwenden. Bibliotheken kommen als NuGet-Pakete. Die using-Direktive importiert einen Namespace, keine Datei.

Syntaxunterschiede, die dir auffallen

  • Objekte. auto p = std::make_unique<Player>(); und p->Jump(); in C++; var p = new Player(); und p.Jump(); in C#. C# verwendet für alles ..
  • Strings. std::string ist ein veränderlicher Wert; string in C# ist ein unveränderlicher Referenztyp.
  • Mehrfachvererbung. C++ erlaubt einer Klasse, von mehreren Klassen zu erben; C# erlaubt eine Basisklasse plus beliebig viele Interfaces.
  • Templates vs. Generics. C++-Templates sind Codegenerierung zur Kompilierzeit und erlauben Metaprogrammierung; C#-Generics werden einmal typgeprüft, mit expliziten Constraints (where T : IComparable<T>).
  • Standardbibliothek. Die von C# enthält HTTP, JSON, reguläre Ausdrücke, Datei-IO, Kompression und Kryptografie; in C++ kommt vieles davon aus Bibliotheken von Drittanbietern.

Was du lernen solltest

Lerne C#, wenn du Anwendungen, Web-Backends, Tools oder Unity-Spiele bauen und schnell Ergebnisse sehen willst. Lerne C++, wenn du auf Game Engines, Grafik, Embedded-Systeme, Betriebssysteme, Browser oder alles zielst, bei dem Kontrolle auf Hardware-Ebene und vorhersagbare Latenz der Kern sind. C# ist die sanftere erste Sprache; C++ lohnt sich als zweite, weil es dir zeigt, was die C#-Runtime für dich erledigt.

Häufig gestellte Fragen

Was ist der Hauptunterschied zwischen C# und C++?

C# ist verwaltet: Es kompiliert in eine Zwischensprache, die die .NET-Runtime per JIT kompiliert, und ein Garbage Collector gibt Speicher frei. C++ kompiliert direkt in Maschinencode, und du bestimmst, wann Objekte erzeugt und zerstört werden. Dieser Tausch gibt C++ mehr Kontrolle und Vorhersagbarkeit und C# mehr Sicherheit und schnellere Entwicklung.

Ist C# einfacher als C++?

Ja, für die meisten. C# hat keine manuelle Speicherverwaltung, keine Header-Dateien, kein undefiniertes Verhalten in sicherem Code und klarere Compilerfehler. C++ ist eine größere Sprache mit mehr Möglichkeiten für subtile Fehler, etwa hängende Zeiger, Pufferüberläufe und Use after free, die der Compiler nicht erkennt.

Ist C++ schneller als C#?

Gut geschriebenes C++ ist meist schneller und vor allem vorhersagbarer, weil es keine Pausen durch den Garbage Collector und kein JIT-Aufwärmen gibt. Modernes C# verkleinert den Abstand mit Structs, Span<T>, SIMD und Ahead-of-time-Kompilierung, und bei Geschäftsanwendungen ist der Unterschied selten der Engpass. Für Engines, Treiber und Hochfrequenzhandel bleibt C++ der Standard.

Soll ich für die Spieleentwicklung C# oder C++ lernen?

Für deine ersten Spiele kommst du mit C# und Unity (oder Godot) schneller zu einem fertigen Spiel. Unreal Engine nutzt C++ (plus Blueprints), und AAA-Studios, die Engine-Code schreiben, stellen C++-Programmierer ein. Viele Entwickler beginnen mit Unity und C# und lernen C++, wenn sie auf Engine-Ebene arbeiten müssen.

Hat C# Zeiger?

Ja, innerhalb von unsafe-Codeblöcken, die mit der Projekteinstellung AllowUnsafeBlocks aktiviert werden müssen. Normales C# verwendet Referenzen, die der Garbage Collector verfolgt und die nie auf freigegebenen Speicher zeigen können. ref, Span<T> und stackalloc decken die meisten Fälle ab, in denen du zu einem Zeiger greifen würdest.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S