Análise e resumo de cenários comuns de exceção de software C++

Com base na experiência e experiência de solução de problemas de exceções de software, um breve resumo dos cenários e causas de exceções de software é fornecido para referência.

1. Problema de ponteiro selvagem
Pode ser que o ponteiro seja usado sem inicialização. Também é possível que a memória apontada pelo ponteiro tenha sido liberada, mas o ponteiro não esteja definido como NULL, uma vez que tal ponteiro seja acessado, haverá problemas. Em muitos casos (incluindo o caso de acesso a um ponteiro nulo), a área de memória do ponteiro NULL dentro de 64 KB que é proibida pelo sistema pode ser acessada e o sistema encerrará diretamente o programa. Aqui, uma determinada variável não foi inicializada, e também é possível que uma determinada biblioteca dll não tenha sido inicializada, então a interface na biblioteca é chamada.

2. Problema de ponteiro nulo
É possível que a memória tenha sido liberada e o ponteiro tenha sido definido como NULL, mas o ponteiro nulo ainda seja acessado posteriormente. Por exemplo, depois que uma biblioteca foi desinicializada, a interface na biblioteca é chamada ou o código em execução na biblioteca acessa o ponteiro nulo. Também é possível que a interface de chamada tenha retornado um ponteiro nulo e o chamador não tenha adicionado se o ponteiro é nulo ou não.

3. Acesso à memória fora dos limites
Ao destruir o conteúdo da memória adjacente ou adulterar o conteúdo da memória adjacente, vários problemas inesperados ocorrerão. Por exemplo, ele acionará a exceção de acesso à área de memória do ponteiro NULL e a cópia de memória subsequente será adulterada por causa do comprimento, resultando em outra operação fora dos limites da memória. Se a memória heap está fora dos limites ou a memória da pilha está fora dos limites, isso pode causar uma exceção. A exceção mais imediata pode ser uma violação de acesso à memória quando os limites são excedidos.

4. A memória da pilha é liberada como memória heap.
Por exemplo, a memória do objeto da classe atual é liberada automaticamente na função da classe, ou seja, exclua isso. No entanto, ao usar o objeto definido pela classe, há um problema com o uso de delete para liberar a memória da pilha.

5. Vazamento de memória
A memória heap não é liberada.
Outra parte da memória é gerenciada quando o ponteiro é atribuído e a memória anterior não é liberada e gerenciada.
Alocadores de memória (C: malloc, calloc, realloc, free, C++: new, delete, new[], delete[]) são misturados e usados ​​indiscriminadamente.

6. O problema de acesso direto a objetos de classe sem inicialização
1) Por exemplo, objetos de seção crítica, se eles entrarem diretamente sem chamar a interface de inicialização, ocorrerá uma exceção. É possível pular uma exceção no código e pular o código de inicialização.
2) O construtor registra a função de membro para a função de retorno de chamada.

7. A memória heap é liberada duas vezes
, já foi liberada, mas é liberada uma vez chamando free ou delete, ou seja, é liberada duas vezes. Para ponteiros nulos, liberar várias vezes é bom.

8.
Resumo do cenário de falha do iterador STL de falha do iterador

9. A criação e liberação de objetos estáticos e globais possuem dependências sequenciais
, por exemplo: objetos estáticos A e B são criados pelo sistema, então a ordem de criação não pode ser determinada. Mas o objeto B é chamado no construtor de A.

10. Referência circular do ponteiro compartilhado (shared_ptr)
Referência: Uso do ponteiro inteligente de referência fraca C++ fraco_ptr (2)

11. Endereçamento secundário ao chamar funções virtuais
As funções virtuais de C++ são armazenadas na tabela de funções virtuais. Se você deseja chamar uma função virtual de um objeto de classe, precisa obter o primeiro endereço da tabela de função virtual por meio do primeiro endereço do objeto de classe e, em seguida, encontrar o endereço da função virtual na função virtual de acordo com o nome da função virtual chamada e, em seguida, chame esse endereço. No código assembly, você pode ver o processo detalhado de endereçamento secundário.

12. Vazamentos de memória de liberação de objeto durante a herança.Existem
funções virtuais nas funções de membro da classe base, e o destruidor deve declarar funções não virtuais. Para garantir que a memória do objeto seja liberada normalmente.

13. Estouro de pilha
1) Razão: Existem muitas variáveis ​​locais no processo recursivo e a profundidade da recursão é muito grande, o que é a causa do estouro da pilha do sistema, especialmente quando o loop de coluna recursiva definitivamente ocorrerá.
2) Método de processamento:
Otimização recursiva de cauda.
Objetos com grande memória são alterados de pilha para heap

14. Memória insuficiente do sistema
Solução:
1) Libere a memória não utilizada o mais cedo possível no programa e libere a memória temporariamente ociosa
2) Reduza cópias redundantes
3) Configure um método para lidar com memória insuficiente std::set_new_handler
4) Use pool de memória
5) Use virtual memória
6) Use um sistema de 64 bits para aumentar o tamanho da memória física

15. Problema de loop infinito
O loop infinito geralmente leva a um maior uso da CPU do sistema. Se for o thread principal da interface do usuário, é mais fácil de lidar, ou seja, thread 0, alterne para o thread 0 e vá várias vezes para verificar se a pilha é a mesma. Se for o mesmo, você pode usar bp para definir vários pontos de interrupção na pilha para ver em qual função ocorre o loop infinito inferior. Geralmente, é causado por um grande número de ciclos no corpo do loop ou há um problema com a configuração da condição do loop, que é sempre TRUE. Você pode olhar os valores das variáveis ​​locais relacionadas à condição do loop na pilha para localizar o problema. Se o valor transmitido da extremidade remota for usado como o número de ciclos, uma proteção de limite superior pode ser adicionada para evitar que valores anormais sejam transmitidos. Se for o thread subjacente, você pode usar o Process Explorer para verificar a pilha, localizar o id e a pilha do thread, alternar para o thread de destino em windbg e definir um ponto de interrupção para determinar se há um loop infinito. Você também pode usar o comando !runaway diretamente no windbg para verificar qual thread ocupa mais CPU.Se necessário, você também pode precisar verificar se o modo kernel ocupa mais ou o modo de usuário ocupa mais.

16. Erro de julgamento condicional
A lógica de julgamento condicional está ausente e a situação de else e default não é considerada

17. Conversão de tipo
A conversão de tipo não considera falha de conversão e condições anormais.

18. Diferentes compiladores têm diferentes implementações de STL.
Por exemplo, VS e MinGW são incompatíveis em std::string\std::map.

19. O problema do desequilíbrio da pilha causado por convenções de chamada de função inconsistentes.
Por exemplo, ao definir uma função de retorno de chamada na biblioteca dll, a função de retorno de chamada não especifica uma convenção de chamada e a chamada C padrão do VS é usada. Mas quando a dll é chamada por C#, o arquivo de cabeçalho da dll é incluído e a chamada padrão é usada por padrão em C#. A função de retorno de chamada é definida como uma chamada padrão quando implementada na camada C#. A função é responsável por limpar a pilha dentro da função. No entanto, quando a função de retorno de chamada é chamada na dll, a dll pensa que a função é uma Chamada C. Quando a função de retorno de chamada é chamada na dll, a pilha será limpa fora da função. Depois que a função de retorno de chamada é chamada, a pilha é limpa mais uma vez, portanto, a pilha fica desbalanceada.

20. O problema do uso misto de bibliotecas de depuração e liberação.É
possível que a memória heap aplicada pela interface de exportação de dll dentro da dll precise ser liberada externamente pelo chamador. Se a dll estiver na versão de lançamento e o chamador estiver na versão de depuração, ocorrerá uma exceção quando o chamador liberar a memória. Porque o gerenciamento de memória em depuração e liberação é diferente. Debug contém informações de depuração e a memória solicitada é relativamente grande.

21. Problemas causados ​​por violações de uso de tipo de dados
Por exemplo, se você não entender as restrições de um determinado tipo, pode usar sua interface à vontade, resultando em violações de uso e disparando exceções.

22. O problema é que parte do código é ignorada após uma exceção ser lançada.
Quando uma exceção é encontrada na classe e uma exceção é lançada, o programa ainda está em execução, mas algum código é ignorado, resultando em uma exceção no código lógica. Por exemplo, ao inicializar o objeto CTime, um valor anormal é passado e uma exceção é relatada. Outro exemplo é que um recém-chegado executa uma operação memset em objetos como vetctor em stl, que destrói os dados estruturais dentro de stl, causando uma exceção dentro de stl. Claro, há outro cenário em que a operação memset é executada diretamente no CString, que destrói os dados da estrutura de manutenção da classe e geralmente aciona uma exceção. Geralmente, o memset opera na estrutura, mas se a estrutura contiver tipos de dados não básicos, como objetos de classe, você deve prestar atenção. Ele só pode ser inicializado no construtor e você não pode memset diretamente. Portanto, ao usar memset, você deve prestar atenção aos objetos de classe.

23. Há competição de dados em multi-threading e não há problema de sincronização.
Bloqueios multi-threading, o design de conhecimento é muito profundo e será desenvolvido aqui.

24. Uma thread mantém o valor da variável e a outra thread a lê por meio de um loop
1) Problema:
Hot access (sem atraso) faz com que os recursos da CPU sejam ocupados. Adicionar um
atraso a cada loop facilita a perda do processo de mudança de variável , e a eficiência da troca frequente de tarefas Baixa
2) Método de processamento:
variável de condição, mecanismo de evento

25. Solução de problemas de impasse
A ferramenta de janela "Pilha Paralela" do VS pode analisar diretamente
logs e outras ferramentas.
Atualmente, é relativamente simples usar o windbg para solucionar o impasse de códigos-chave, porque os segmentos de código-chave são objetos no modo de usuário.
Se for um bloqueio de um objeto kernel, a depuração no modo kernel é necessária, mas a depuração no modo kernel é mais complicada.

26. Thread hot running
1) O loop no thread continua em execução, não sai por um longo tempo e ocupa muito da CPU.
2) O bloqueio de rotação compete por um longo tempo ou vários encadeamentos estão na competição de bloqueios auto-selecionados.
Referência: quantidade atômica C++11 implementa bloqueio de rotação

27. O cache causa erros de dados não sincronizados.
Por exemplo, os valores de objetos na memória e registros são inconsistentes.

Se houver algum erro ou deficiência, seja bem-vindo para comentar e apontar! A criação não é fácil, indique a fonte para reimpressão. Se for útil, lembre-se de curtir e seguir (⊙o⊙)
Para mais conteúdo, siga meu blog pessoal: https://blog.csdn.net/qq_43148810

Acho que você gosta

Origin blog.csdn.net/qq_43148810/article/details/128937797
Recomendado
Clasificación