C# - Criando código não seguro com unsafe


Hoje veremos como podemos criar código não seguro usando a palavra-chave unsafe.

A palavra-chave unsafe é usada para indicar uma seção de código que não é gerenciado pelo Common Languagem Runtime (CLR) da plataforma .NET.

Ela é usada na declaração de um tipo ou membro ou para especificar um bloco de código para especificar que um método ou o  contexto de todo o método é inseguro.

Para manter a segurança de tipo, o C# não suporta aritmética de ponteiro, por padrão. No entanto, usando a palavra-chave unsafe, podemos definir um contexto não seguro no qual os ponteiros podem ser usados.

O código não seguro pode criar problemas com estabilidade e segurança, devido à sua sintaxe complexa inerente e ao potencial de erros relacionados à memória, como excesso de pilha, acesso e substituição da memória do sistema. Para evitar esses erros o desenvolvedor vai ter que escrever código extra para evitar possíveis erros ou riscos à segurança.

Para poder usar a palavra-chave não segura em um projeto .Net, você deve marcar a opção "Permitir código não seguro" na janela de propriedades do projeto no item Build :

Como exemplo veja o código abaixo:

 class Program
  {
        static void Main(string[] args)
        {
           int ab = 32;
           int* p = &ab;
           Console.WriteLine("valor de a ab é {0}", *p);
           Console.ReadLine();
        }
}
 

A compilação deste código produz os seguintes erros:

Esses erros ocorrem porque executamos nosso programa no modo de segurança.

Mas como estamos usando ponteiros devemos executar nosso programa no modo Inseguro.

Mas afinal "O que se entende por Código Seguro e Não Seguro?"

Código gerenciado é aquele código que é executado sob a supervisão da CLR, e a CLR é responsável por várias tarefas como :

Por outro lado, o código não gerenciado é aquele executado fora do contexto da CLR, e neste contexto o programador vai ter que ficar responsável por tarefas como :

Assim para tratar com código inseguro devemos usar a palavra-chave unsafe (além de habilitar o código inseguro). A seguir temos um exemplo simples de utilização deste recurso:

   class Program
    {
        unsafe static void Main()
        {
            fixed (char* valor = "seguro")
            {
                char* ptr = valor;
                while (*ptr != '\0')
                {
                    Console.WriteLine(*ptr);
                    ++ptr;
                }
            }
        }
  }

Neste código o compilador não vai mais indicar erros devido ao uso dos ponteiros, pois declaramos o bloco de código referente ao método Main() como unsafe.

Assim para escrever código inseguro além da palavra-chave unsafe, também podemos usar a palavra-chave fixed,  onde:,

Para finalizar vejamos a seguir um exemplo onde vamos passar uma variável do tipo ponteiro para um método usando código inseguro com unsafe:

    class Program
    {
        static unsafe void Main(string[] args)
        {
            Program p = new Program();
            int var1 = 10;
            int var2 = 20;
            int* x = &var1;
            int* y = &var2;

            Console.WriteLine("Antes da Troca : var1:{0}, var2: {1}", var1, var2);

            p.swap(x, y);

            Console.WriteLine("Depois da Troca: var1:{0}, var2: {1}", var1, var2);
            Console.ReadKey();
        }

        public unsafe void swap(int* p, int* q)
        {
            int temp = *p;
            *p = *q;
            *q = temp;
        }
}

Executando o código teremos:

A seguir temos algumas das vantagens em usar o modo inseguro:

Mas nem tudo são flores, e temos as seguintes desvantagens:

Então basicamente usamos o modo inseguro quando formos trabalhar com ponteiros ou quando vamos interagir com código não gerenciado, ou ainda, se precisarmos acessar a memória bruta de forma direta.

E estamos conversados.

"O entendimento para aqueles que o possuem, é uma fonte de vida, mas a instrução dos tolos é a sua estultícia."
Provérbios 16:22

Referências:


José Carlos Macoratti