Menu

C# vs C++: memória, desempenho, desenvolvimento de jogos e curva de aprendizado

C# é uma linguagem gerenciada com garbage collector; C++ compila para código nativo e dá controle manual sobre a memória. Como essa diferença aparece em segurança, desempenho, motores de jogos, modelo de build e na dificuldade de aprender cada uma.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

C# e C++ compartilham uma letra e uma sintaxe com chaves, mas ficam em níveis diferentes. C++ (1985) compila diretamente para código de máquina e permite controlar exatamente onde cada objeto vive e quando ele morre. C# (2002) compila para uma linguagem intermediária que o runtime do .NET transforma em código de máquina em tempo de execução, e um garbage collector recupera a memória para você. Quase todas as diferenças abaixo vêm dessa única escolha.

Visão geral

C#C++
ExecuçãoGerenciada: IL, compilada com JIT pelo CLRNativa: compilada antecipadamente para código de máquina
MemóriaGarbage collectorManual, RAII, smart pointers
PonteirosReferências; ponteiros brutos só em código unsafePonteiros brutos e referências em todo lugar
SegurançaArrays com verificação de limites, sem referências pendentesComportamento indefinido fora dos limites, uso depois de liberar
Modelo de buildProjetos e assemblies, sem cabeçalhosCabeçalhos, pré-processador, unidades de tradução, linker
GenéricosGenéricos, verificados uma vez, resolvidos em tempo de execuçãoTemplates, instanciados em tempo de compilação
Biblioteca padrãoGrande: coleções, HTTP, JSON, arquivos, threadsMenor: containers, algoritmos, threads
Velocidade de compilaçãoRápidaLenta em bases de código grandes
JogosUnity, GodotUnreal, a maioria dos motores AAA próprios
Outros usos principaisBackends web, desktop, nuvem, ferramentasMotores, navegadores, sistemas operacionais, embarcados, trading

Memória: garbage collector vs RAII

Em C++, um objeto com armazenamento automático é destruído quando sai de escopo, e o destrutor executa nesse exato momento. Objetos no heap pertencem a smart pointers (std::unique_ptr, std::shared_ptr) ou são gerenciados à mão com new e delete. Esse padrão, Resource Acquisition Is Initialization (RAII), dá uma limpeza determinística da memória e de todos os outros recursos.

// 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

Em C#, toda instância de classe vive no heap gerenciado e o garbage collector a libera em algum momento posterior, quando nada mais a referencia. Você nunca escreve delete e nunca consegue liberar algo que ainda está em uso. A memória está resolvida; o que o GC não trata na hora são os outros recursos: arquivos, sockets, conexões de banco de dados. Para esses, C# tem IDisposable e a instrução using, que chama Dispose no fim de um bloco, o mais perto de um destrutor:

Saída:

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

A diferença é que em C++ a limpeza está ligada ao escopo para todo objeto local e todo objeto que pertence a um smart pointer, enquanto em C# ela é automática para a memória e opcional (via using) para todo o resto. Mais sobre isso na página da instrução using.

Segurança: exceções em vez de comportamento indefinido

Ler além do fim de um array em C++ é comportamento indefinido: o programa pode imprimir lixo, quebrar ou continuar executando com a memória corrompida, e o resultado pode mudar entre builds. O mesmo erro em C# lança uma exceção na linha exata.

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

Saída:

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

Toda a classe de bugs de corrupção de memória (buffer overflows, uso depois de liberar, liberação dupla, ponteiros pendentes) não existe em C# seguro. Isso explica boa parte de por que código C# é mais rápido de escrever e de revisar.

Ponteiros e código unsafe

C# tem ponteiros, mas só dentro de blocos unsafe, e o projeto precisa habilitá-los com <AllowUnsafeBlocks>true</AllowUnsafeBlocks>. Objetos no heap gerenciado podem se mover durante a coleta de lixo, então você os fixa com fixed antes de pegar o endereço:

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

Código unsafe é usado para interoperabilidade com bibliotecas nativas e em alguns laços críticos. A maior parte do C# de baixo nível hoje usa Span<T>, variáveis locais ref e stackalloc, que dão desempenho parecido com o de ponteiros mantendo a verificação de limites.

Desempenho

C++ entrega ao compilador o programa inteiro antecipadamente e não acrescenta nada em tempo de execução: sem garbage collector, sem JIT, sem verificação de limites a menos que você peça. Isso o torna a escolha onde cada microssegundo ou cada byte conta, e onde pausas são inaceitáveis.

C# paga pela segurança com um runtime, o aquecimento do JIT na inicialização e pausas ocasionais do GC. Em throughput, ele normalmente fica a um fator pequeno do C++, e a distância diminuiu a cada versão do .NET: JIT em camadas com otimização guiada por perfil, structs e Span<T> para evitar alocações, intrínsecos de hardware para SIMD e Native AOT para compilar antecipadamente em um único executável nativo. Em APIs web, ferramentas e lógica de negócio, o banco de dados e a rede dominam e a diferença entre as linguagens raramente aparece.

Desenvolvimento de jogos: Unity vs Unreal

É aqui que a maioria das pessoas encontra a pergunta. A Unity é programada em C#: código de gameplay, interface e ferramentas são classes C# ligadas aos objetos do jogo, enquanto o núcleo do motor é em C++. A Unreal Engine é escrita em C++, e o gameplay é C++ mais o sistema de script visual Blueprints. O Godot suporta GDScript e C#.

C# com Unity é mais rápido de aprender e de iterar, e é por isso que é tão comum em jogos indie e mobile. Unreal e C++ são o padrão em estúdios AAA, e programadores de motores em todo lugar trabalham em C++. Um caminho comum é começar na Unity e aprender C++ se você for para a Unreal ou para trabalho de motor.

Modelo de build

Um programa C++ é dividido em cabeçalhos (.h, declarações) e arquivos-fonte (.cpp, definições). O pré-processador cola os cabeçalhos em cada arquivo-fonte, cada arquivo é compilado separadamente e o linker junta os resultados. Os templates são instanciados em cada arquivo que os usa, um dos motivos de builds grandes em C++ serem lentos.

C# não tem nada disso. Um projeto é um conjunto de arquivos .cs compilados juntos em um assembly (.dll); a ordem das declarações e dos arquivos não importa, e um tipo em um arquivo pode usar um tipo em outro sem include. Bibliotecas vêm como pacotes NuGet. A diretiva using importa um namespace, não um arquivo.

Diferenças de sintaxe que você vai notar

  • Objetos. auto p = std::make_unique<Player>(); e p->Jump(); em C++; var p = new Player(); e p.Jump(); em C#. C# usa . para tudo.
  • Strings. std::string é um valor mutável; a string de C# é um tipo de referência imutável.
  • Herança múltipla. C++ permite que uma classe herde de várias classes; C# permite uma classe base mais qualquer número de interfaces.
  • Templates vs genéricos. Templates de C++ são geração de código em tempo de compilação e permitem metaprogramação; genéricos de C# são verificados uma vez, com restrições explícitas (where T : IComparable<T>).
  • Biblioteca padrão. A de C# inclui HTTP, JSON, expressões regulares, I/O de arquivos, compressão e criptografia; em C++ muito disso vem de bibliotecas de terceiros.

Qual aprender

Aprenda C# se você quer construir aplicações, backends web, ferramentas ou jogos na Unity e ver resultados rápido. Aprenda C++ se o seu alvo são motores de jogos, gráficos, sistemas embarcados, sistemas operacionais, navegadores ou qualquer coisa em que o controle no nível do hardware e a latência previsível são o objetivo. C# é a primeira linguagem mais tranquila; C++ vale a pena como segunda, porque ensina o que o runtime de C# está fazendo por você.

Perguntas frequentes

Qual é a principal diferença entre C# e C++?

C# é gerenciada: compila para uma linguagem intermediária que o runtime do .NET compila com JIT, e um garbage collector libera a memória. C++ compila direto para código de máquina, e você controla quando os objetos são criados e destruídos. Essa troca dá ao C++ mais controle e previsibilidade, e ao C# mais segurança e desenvolvimento mais rápido.

C# é mais fácil que C++?

Sim, para a maioria das pessoas. C# não tem gerenciamento manual de memória, nem arquivos de cabeçalho, nem comportamento indefinido em código seguro, e tem erros de compilação mais claros. C++ é uma linguagem maior, com mais formas de cometer erros sutis, como ponteiros pendentes, buffer overflows e uso de memória depois de liberada, que o compilador não pega.

C++ é mais rápido que C#?

C++ bem escrito normalmente é mais rápido e, mais importante, mais previsível, porque não há pausa de garbage collector nem aquecimento do JIT. O C# moderno diminui a distância com structs, Span<T>, SIMD e compilação antecipada, e em aplicações de negócio a diferença raramente é o gargalo. Para motores, drivers e trading de alta frequência, C++ continua sendo o padrão.

Devo aprender C# ou C++ para desenvolvimento de jogos?

Nos seus primeiros jogos, C# com Unity (ou Godot) permite construir e publicar mais rápido. A Unreal Engine usa C++ (mais Blueprints), e estúdios AAA que escrevem código de motor contratam programadores C++. Muitos desenvolvedores começam com Unity e C# e aprendem C++ quando precisam de trabalho no nível do motor.

C# tem ponteiros?

Sim, dentro de blocos de código unsafe, que precisam ser habilitados com a configuração de projeto AllowUnsafeBlocks. O C# comum usa referências, que o garbage collector acompanha e que não podem apontar para memória liberada. ref, Span<T> e stackalloc cobrem a maioria dos casos em que você recorreria a um ponteiro.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR