Ponteiro nulo - Null pointer

Na computação , um ponteiro nulo ou referência nula é um valor salvo para indicar que o ponteiro ou referência não se refere a um objeto válido . Os programas usam rotineiramente ponteiros nulos para representar condições como o final de uma lista de comprimento desconhecido ou a falha em realizar alguma ação; esse uso de ponteiros nulos pode ser comparado aos tipos anuláveis e ao valor Nothing em um tipo de opção .

Um ponteiro nulo não deve ser confundido com um ponteiro não inicializado : um ponteiro nulo garante uma comparação desigual com qualquer ponteiro que aponte para um objeto válido. No entanto, dependendo da linguagem e da implementação, um ponteiro não inicializado pode não ter essa garantia. Ele pode ser comparado igual a outros ponteiros válidos; ou pode ser igual a ponteiros nulos. Ele pode fazer as duas coisas em momentos diferentes; ou a comparação pode ser um comportamento indefinido .

C

Em C , dois ponteiros nulos de qualquer tipo têm garantia de comparação igual. A macro do pré-processador NULLé definida como uma constante de ponteiro nulo definida pela implementação, que em C99 pode ser expressa de forma portável, o ((void *)0)que significa que o valor inteiro 0convertido para o tipo void*(ponteiro para void ). O padrão C não diz que o ponteiro nulo é o mesmo que o ponteiro para o endereço de memória  0, embora esse possa ser o caso na prática. Desreferenciar um ponteiro nulo é um comportamento indefinido em C, e uma implementação em conformidade pode assumir que qualquer ponteiro desreferenciado não é nulo.

Na prática, desreferenciar um ponteiro nulo pode resultar em uma tentativa de leitura ou gravação da memória que não está mapeada, disparando uma falha de segmentação ou violação de acesso à memória. Isso pode se manifestar como um travamento do programa ou ser transformado em uma exceção de software que pode ser detectada pelo código do programa. Existem, no entanto, certas circunstâncias em que este não é o caso. Por exemplo, no modo real x86 , o endereço é legível e geralmente gravável, e desreferenciar um ponteiro para esse endereço é uma ação perfeitamente válida, mas normalmente indesejada, que pode levar a um comportamento indefinido, mas sem travamento, no aplicativo. Há ocasiões em que desreferenciar o ponteiro para endereçar zero é intencional e bem definido; por exemplo, o código BIOS escrito em C para dispositivos x86 de modo real de 16 bits pode escrever o IDT no endereço físico 0 da máquina desreferenciando um ponteiro nulo para escrita. Também é possível que o compilador otimize a desreferência do ponteiro nulo, evitando uma falha de segmentação, mas causando outro comportamento indesejado . 0000:0000

C ++

Em C ++, enquanto a NULLmacro foi herdada de C, o literal inteiro para zero tem sido tradicionalmente preferido para representar uma constante de ponteiro nulo. No entanto, o C ++ 11 introduziu a constante de ponteiro nulo explícita nullptrpara ser usada em seu lugar.

Outras línguas

Em alguns ambientes de linguagem de programação (pelo menos uma implementação Lisp proprietária, por exemplo), o valor usado como o ponteiro nulo (chamado nilem Lisp ) pode na verdade ser um ponteiro para um bloco de dados internos úteis para a implementação (mas não explicitamente acessível a partir de programas de usuário), permitindo assim que o mesmo registro seja usado como uma constante útil e uma maneira rápida de acessar os internos de implementação. Isso é conhecido como nilvetor .

Em linguagens com uma arquitetura marcada , um ponteiro possivelmente nulo pode ser substituído por uma união marcada que impõe o tratamento explícito do caso excepcional; na verdade, um ponteiro possivelmente nulo pode ser visto como um ponteiro marcado com uma marca computada.

As linguagens de programação usam literais diferentes para o ponteiro nulo . Em Python, por exemplo, um valor nulo é chamado None. Em Pascal e Swift , um ponteiro nulo é chamado nil. Em Eiffel , é chamado de voidreferência.

Desreferenciamento nulo

Como um ponteiro nulo não aponta para um objeto significativo, uma tentativa de desreferenciar (ou seja, acessar os dados armazenados naquele local da memória) um ponteiro nulo geralmente (mas nem sempre) causa um erro em tempo de execução ou um travamento imediato do programa.

  • Em C , desreferenciar um ponteiro nulo é um comportamento indefinido . Muitas implementações fazem com que tal código resulte na interrupção do programa com uma violação de acesso , porque a representação do ponteiro nulo é escolhida para ser um endereço que nunca é alocado pelo sistema para armazenar objetos. No entanto, esse comportamento não é universal. Também não é garantido, uma vez que os compiladores têm permissão para otimizar programas sob a suposição de que eles estão livres de comportamento indefinido.
  • Em Delphi e em muitas outras implementações Pascal, a constante nilrepresenta um ponteiro nulo para o primeiro endereço na memória que também é usado para inicializar variáveis ​​gerenciadas. Desreferenciá-lo gera uma exceção de sistema operacional externo que está sendo mapeada em uma instância de exceção Pascal EAccessViolation se a unidade System.SysUtils estiver vinculada na cláusula uses.
  • Em Java , o acesso a uma referência nula aciona um NullPointerException(NPE), que pode ser capturado pelo código de tratamento de erros, mas a prática preferida é garantir que tais exceções nunca ocorram.
  • No .NET , o acesso à referência nula dispara uma NullReferenceException a ser lançada. Embora capturar isso seja geralmente considerado uma prática ruim, esse tipo de exceção pode ser capturado e tratado pelo programa.
  • Em Objective-C , as mensagens podem ser enviadas para um nilobjeto (que é um ponteiro nulo) sem causar a interrupção do programa; a mensagem é simplesmente ignorada e o valor de retorno (se houver) é nilou 0, dependendo do tipo.
  • Antes da introdução do SMAP , um bug de desreferência de ponteiro nulo poderia ser explorado mapeando o pagezero no espaço de endereço do invasor e, portanto, fazendo com que o ponteiro nulo apontasse para aquela região. Isso pode levar à execução do código em alguns casos.

Mitigação

Existem técnicas para facilitar a depuração de desreferências de ponteiro nulo. Bond et al. sugerir modificar a JVM para controlar a propagação nula. A ideia do sistema Casper é usar a transformação do código-fonte para rastrear essa propagação, sem modificar a JVM. Em alguns casos, é possível gerar automaticamente um patch para corrigir exceções de ponteiro nulo.

História

Em 2009, Sir Tony Hoare afirmou que ele inventou a referência nula em 1965 como parte do ALGOL W idioma. Naquela referência de 2009, Hoare descreve sua invenção como um "erro de um bilhão de dólares":

Eu chamo isso de meu erro de um bilhão de dólares. Foi a invenção da referência nula em 1965. Naquela época, eu estava projetando o primeiro sistema de tipo abrangente para referências em uma linguagem orientada a objetos (ALGOL W). Meu objetivo era garantir que todo uso de referências fosse absolutamente seguro, com checagem realizada automaticamente pelo compilador. Mas não pude resistir à tentação de colocar uma referência nula, simplesmente porque era muito fácil de implementar. Isso levou a inúmeros erros, vulnerabilidades e travamentos do sistema, que provavelmente causaram um bilhão de dólares em dores e danos nos últimos quarenta anos.

Veja também

Referências

Citações

Fontes

  • Comitê Técnico Conjunto ISO / IEC JTC 1, Subcomitê SC 22, Grupo de Trabalho WG 14 (2007-09-08). Padrão Internacional ISO / IEC 9899 (PDF) (Rascunho do Comitê).CS1 maint: vários nomes: lista de autores ( link )