Um livro em C ++ que tenho lido afirma que, quando um ponteiro é excluído usando o delete
operador, a memória no local para o qual está apontando é "liberada" e pode ser substituída. Ele também afirma que o ponteiro continuará apontando para o mesmo local até que seja reatribuído ou definido como NULL
.
No Visual Studio 2012, no entanto; esse não parece ser o caso!
Exemplo:
#include <iostream>
using namespace std;
int main()
{
int* ptr = new int;
cout << "ptr = " << ptr << endl;
delete ptr;
cout << "ptr = " << ptr << endl;
system("pause");
return 0;
}
Ao compilar e executar este programa, obtenho a seguinte saída:
ptr = 0050BC10
ptr = 00008123
Press any key to continue....
Claramente, o endereço que o ponteiro está apontando muda quando a exclusão é chamada!
Por que isso está acontecendo? Isso tem algo a ver com o Visual Studio especificamente?
E se delete pode alterar o endereço para o qual está apontando, por que não excluir automaticamente define o ponteiro para em NULL
vez de algum endereço aleatório?
fonte
Respostas:
Notei que o endereço armazenado
ptr
estava sempre sendo substituído por00008123
...Isso parecia estranho, então eu pesquisei um pouco e encontrei esta postagem no blog da Microsoft contendo uma seção discutindo "Desinfecção automática de ponteiros ao excluir objetos C ++".
Ele não apenas explica o que o Visual Studio faz com o ponteiro após a exclusão, mas também responde por que eles escolheram NÃO configurá-lo
NULL
automaticamente!Este "recurso" está ativado como parte da configuração "Verificações SDL". Para ativar / desativar, vá para: PROJETO -> Propriedades -> Propriedades de configuração -> C / C ++ -> Geral -> Verificações SDL
Para confirmar isso:
Alterar essa configuração e executar novamente o mesmo código produz a seguinte saída:
"feature" está entre aspas porque, em um caso em que você tem dois ponteiros para o mesmo local, chamar delete excluirá apenas UM deles. O outro será deixado apontando para o local inválido ...
ATUALIZAR:
Após mais 5 anos de experiência em programação C ++, percebo que todo esse problema é basicamente um ponto discutível. Se você é um programador C ++ e ainda está usando
new
edelete
gerenciando ponteiros brutos em vez de usar ponteiros inteligentes (que contornam todo esse problema), convém considerar uma mudança na carreira para se tornar um programador C. ;)fonte
0x8123
em vez de0
. O ponteiro ainda é inválido, mas causa uma exceção ao tentar desreferenciá-lo (bom) e não passa nas verificações NULL (também bom, porque é um erro não fazer isso). Onde é o lugar para maus hábitos? Realmente é apenas algo que ajuda você a depurar.Você vê os efeitos colaterais da
/sdl
opção de compilação. Ativado por padrão para projetos do VS2015, ele permite verificações de segurança adicionais além daquelas fornecidas por / gs. Use a configuração Projeto> Propriedades> C / C ++> Geral> Verificações SDL para alterá-la.Citando o artigo MSDN :
Lembre-se de que definir ponteiros excluídos para NULL é uma prática ruim quando você usa o MSVC. Isso anula a ajuda que você obtém da Debug Heap e da opção / sdl. Você não pode mais detectar chamadas gratuitas de exclusão / exclusão no seu programa.
fonte
delete
escolhe, o Visual Studio deixará o segundo ponteiro apontando para o local original, que agora é inválido.Essa é definitivamente uma informação enganosa.
Isso está claramente dentro das especificações de idioma.
ptr
não é válido após a chamada paradelete
. Usandoptr
apósdelete
d foi motivo de comportamento indefinido. Não faça isso. O ambiente de tempo de execução é livre para fazer o que quiserptr
após a chamadadelete
.Alterar o valor do ponteiro para qualquer valor antigo está dentro da especificação do idioma. Quanto a alterá-lo para NULL, eu diria que isso seria ruim. O programa se comportaria de maneira mais sensata se o valor do ponteiro fosse definido como NULL. No entanto, isso ocultará o problema. Quando o programa é compilado com diferentes configurações de otimização ou transportado para um ambiente diferente, o problema provavelmente aparecerá no momento mais inoportuno.
fonte
delete
duas vezes o ponteiro não causaria problemas. Definitivamente isso não é bom.NULL
mas também definitivamente fora do espaço de endereço do processo, exporá mais casos do que as duas alternativas. Deixá-lo pendurado não causará necessariamente um segfault se for usado após ser liberado; configurá-lo paraNULL
não causará um segfault se fordelete
d novamente.Em geral, mesmo os valores de leitura (como você fez acima, observe: isso é diferente da exclusão de referência) de ponteiros inválidos (o ponteiro se torna inválido, por exemplo, quando você o
delete
faz) como comportamento definido pela implementação. Isso foi introduzido no CWG # 1438 . Veja também aqui .Observe que antes que os valores de leitura de ponteiros inválidos fossem um comportamento indefinido, o que você tem acima seria um comportamento indefinido, o que significa que qualquer coisa poderia acontecer.
fonte
[basic.stc.dynamic.deallocation]
: "Se o argumento fornecido para uma função de desalocação na biblioteca padrão for um ponteiro que não seja o valor nulo do ponteiro, a função de desalocação deverá desalocar o armazenamento referenciado pelo ponteiro, invalidando todos os ponteiros referentes a qualquer parte do armazenamento desalocado "e a regra na[conv.lval]
(seção 4.1) que diz que ler (lvalue-> conversão de valor) qualquer valor de ponteiro inválido é um comportamento definido pela implementação.Acredito que você esteja executando algum tipo de modo de depuração, e o VS está tentando apontar o ponteiro para algum local conhecido, para que outras tentativas de desreferenciação possam ser rastreadas e relatadas. Tente compilar / executar o mesmo programa no modo de lançamento.
Normalmente, os ponteiros não são trocados
delete
por motivos de eficiência e para evitar dar uma falsa idéia de segurança. Definir o ponteiro de exclusão para um valor predefinido não será bom na maioria dos cenários complexos, pois o ponteiro que está sendo excluído provavelmente será apenas um dos vários que apontam para esse local.Por uma questão de fato, quanto mais eu penso sobre isso, mais eu acho que o VS é o culpado ao fazê-lo, como de costume. E se o ponteiro for const? Ainda vai mudar isso?
fonte
Após excluir o ponteiro, a memória para a qual ele aponta ainda pode ser válida. Para manifestar esse erro, o valor do ponteiro é definido como um valor óbvio. Isso realmente ajuda no processo de depuração. Se o valor tiver sido definido como
NULL
, ele pode nunca aparecer como um bug potencial no fluxo do programa. Portanto, pode ocultar um erro quando você testar mais tardeNULL
.Outro ponto é que algum otimizador de tempo de execução pode verificar esse valor e alterar seus resultados.
Em tempos anteriores, o MS definiu o valor como
0xcfffffff
.fonte