O que significa fixed na prática
A palavra fixed aparece o tempo todo em contextos técnicos, mas o significado muda dependendo do que você está olhando. Em programação, fixed geralmente se refere a um modificador que trava uma variável no lugar na memória, impedindo que o coletor de lixo a mova. Isso é comum em Cquando você trabalha com ponteiros ou chamadas P/Invoke. A ideia é simples: se você passa um bloco de memória para código nativo e esse bloco se move durante uma garbage collection, seu programa vai falhar de forma intermitente e difícil de diagnosticar. Eu já passei meses caçando bugs que desapareciam quando o código era recompilado com otimizações diferentes. O problema era exatamente esse: uma estrutura fixa sendo movida pelo GC em threads concorrentes. A solução não eraa, mas achar a raiz foi. Você precisa marcar a variável com o modificador fixed dentro de um bloco unsafe e depois gerenciar o Scope manualmente. Sem isso, o compilador nem reclama, só o runtime quando decide que não é mais sua sorte.
Fixed em contextos diferentes
Fora do C#, fixed tem outros usos. Em SQL Server, FIXED DATA TYPE se refere a colunas com tamanho predefinido, como char(n) ou binary(n), onde o espaço é alocado mesmo que o conteúdo seja menor. Isso é diferente de variable-length types como varchar. A diferença prática é que fixed types nunca causam problemas de fragmentação de linha da mesma forma, mas podem desperdiçar espaço significativo se você usar char(255) para armazenar nomes que raramente passam de 50 caracteres. Em bancos com bilhões de linhas, isso se traduz em gigabytes desnecessários e queries mais lentas por causa do maior volume de dados lidos por operação. No contexto de finanças, fixed pode aparecer em fixed income, que são títulos de renda fixa. Aqui o significado é literal: o retorno é predefinido, não flutua com o mercado da mesma forma que ações. Não é que renda fixa seja livre de risco, é que o risco tem um perfil diferente. Inflação, risk of default, e reinvestment risk existem mesmo em títulos prefixados. Eu já vi gente entrar em títulos prefixados de longo prazo achando que estava protegida, e levar um susto quando a taxa real virou negativa por vários trimestres consecutivos.
Pegadinhas que todo mundo encontra
Uma das armadilhas mais comuns com fixed em Cé achar que o bloco fixed protege a memória para sempre. Ele não protege. Ele só garante que enquanto o bloco está ativo, o GC não move aquele objeto. Se você guardar um ponteiro para uso posterior, ele vai apontar para o nada assim que o bloco terminar e o GC fizer seu trabalho. O código compila, mas na execução você tem um access violation randômico. A workaround que eu uso é limitar o escopo do fixed ao estritamente necessário e nunca, sob nenhuma circunstância, armazenar o ponteiro fixado em uma variável de instância ou campo estático. Se você precisa de uma garantia de estabilidade de memória por mais tempo, o caminho é Marshal.AllocHGlobal ou um buffer gerenciado com GCHandle.Alloc e Affix. Outro erro frequente é confundir fixed com const. Fixed não é imutabilidade. Fixed é estabilidade de endereço. const é imutabilidade no compile time. Eles não são sinônimos e usar um quando o outro é necessário gera confusão que se reflete em bugs difíceis de rastrear. Uma variável fixed pode ter seu conteúdo alterado normalmente. Uma constante const não pode ser reassigned de jeito nenhum.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Quando fixed não é a resposta
Tem situações em que fixed é overkill ou simplesmente errado. Se você está apenas passando dados para uma API que faz cópia imediata, fixar a memória é perda de performance sem ganho real. O overhead de entrar num bloco unsafe, alinhar memória e gerenciar o scope pode ser maior do que o custo da cópia. Eu vi código onde fixed era usado em loops internos de processamento de imagem, e a remoção daqueles blocos fixos acelerou o programa em cerca de 30%, porque o GC podia trabalhar de forma mais eficiente e não havia chamado P/Invoke justifying a fixação. Renda fixa também não é para todo mundo. Se o seu horizonte é curto e você precisa de liquidez imediata, os títulos prefixados de longo prazo podem ter descontos agressivos no secondary market se os juros subirem. Você entra achando que está travando uma taxa boa e acaba precisando vender com perda significativa. O fixed aqui é ilusório, porque o valor de mercado flutua mesmo que o cupom seja fixo. Para esse caso, CDs ou LCIs com vencimento compatível com seu horizonte temporal costumam ser mais adequados, mesmo que o rendimento nominal seja um pouco menor.
Detalhes que fazem diferença
Em SQL, a escolha entre fixed e variable length afeta até índices. Colunas fixed permitem row locking mais previsível porque o tamanho da linha não muda após insert. Isso reduz fragmentação de índice e melhora performance em workloads de write heavy. O trade-off é o espaço. Se 80% das suas linhas têm valores muito abaixo do tamanho fixed declarado, você está pagando por espaço que não usa. A regra prática que eu sigo é: use fixed apenas quando o valor varia pouco em torno de um tamanho esperado, ou quando a consistência de tamanho é importante para integridade referencial e performance de join. Para textos de tamanho variável, varchar com size adequado é quase sempre a escolha certa. No C#, se você precisa fixar múltiplos objetos ao mesmo tempo, pode fazer isso em um único bloco fixed, listando cada um. Isso é mais eficiente do que múltiplos blocos aninhados, porque reduz a quantidade de vezes que o GC precisa ser consultado. Mas há um limite: você não pode fixar mais do que 128 objetos no mesmo bloco. Passar disso gera um CompileError. Essa limitação existe por um motivo interno de como o runtime gerencia pinned objects, e não adianta tentar burlar com reflection. O workaround é dividir em múltiplos blocos, preferencialmente agrupando objetos que vivem no mesmo ciclo de vida.
A parte mais subestimada de fixed é o impacto no debugging. Quando um crash acontece em código unsafe com pinned memory, o stack trace muitas vezes não mostra onde o ponteiro foi inválidoado. Você precisa olhar o dump de memória, rastrear o GCHandle, e verificar se algum thread escapou do bloco fixed. Ferramentas como dotMemory e WinDbg com extensão SOS ajudam, mas o tempo gasto nessa investigação costuma ser de horas, não minutos. Prevenir é muito mais barato do que debuggar.