Qual é a finalidade de alocar uma quantidade específica de memory para matrizes em C ++?

Eu sou um aluno fazendo uma aula sobre Estruturas de Dados em C ++ neste semestre e me deparei com algo que eu não entendo muito bem hoje à noite. Digamos que eu fosse criar um ponteiro para uma matriz no heap:

int* arrayPtr = new int [4]; 

Eu posso acessar esse array usando a syntax do ponteiro

 int value = *(arrayPtr + index); 

Mas se eu fosse adicionar outro valor à posição da memory imediatamente após o final do espaço alocado para o array, eu seria capaz de acessá-lo

 *(arrayPtr + 4) = 0; int nextPos = *(arrayPtr + 4); //the value of nextPos will be 0, or whatever value I previously filled that space with 

A posição na memory de * (arrayPtr + 4) está além do final do espaço alocado para a matriz. Mas, tanto quanto eu entendo, o acima ainda não causaria problemas. Então, além de ser um requisito do C ++, por que dar aos arrays um tamanho específico ao declará-los?

Quando você passa do final da memory alocada, na verdade você está acessando a memory de algum outro object (ou memory que está livre agora, mas isso pode mudar mais tarde). Então, isso vai lhe causar problemas. Especialmente se você tentar escrever algo para isso.

Eu posso acessar esse array usando a syntax do ponteiro

int valor = * (arrayPtr + índice);

Sim, mas não faça. Use arrayPtr[index]

A posição na memory de * (arrayPtr + 4) está além do final do espaço alocado para a matriz. Mas, tanto quanto eu entendo, o acima ainda não causaria problemas.

Você entende errado. Muito errado. Você está invocando um comportamento indefinido e o comportamento indefinido é indefinido. Pode funcionar por uma semana, então quebrar um dia na próxima semana e você vai ficar se perguntando por quê. Se você não souber o tamanho da coleção com antecedência, use algo dynamic como um vector vez de um array.

Sim, em C / C ++ você pode acessar a memory fora do espaço que alega ter alocado. As vezes. Isso é o que é chamado de comportamento indefinido .

Basicamente, você disse ao compilador e ao sistema de gerenciamento de memory que você quer espaço para armazenar quatro inteiros, e o sistema de gerenciamento de memory alocou espaço para você armazenar quatro inteiros. Deu-lhe um ponteiro para esse espaço. Na contabilidade interna do gerenciador de memory, esses bytes de ram agora estão ocupados, até que você chame delete[] arrayPtr; .

No entanto, o gerenciador de memory não alocou o próximo byte para você. Você não tem como saber, em geral, qual é o próximo byte ou a quem pertence.

Em um programa de exemplo simples como o seu exemplo, que apenas aloca alguns bytes e não aloca mais nada, é provável que o próximo byte pertença ao seu programa e não esteja ocupado. Se esse array é a única memory alocada dinamicamente em seu programa, então é provavelmente , talvez seguro rodar no final.

Mas em um programa mais complexo, com múltiplas alocações de memory dinâmica e desalocações, especialmente próximo às bordas das páginas de memory, você realmente não tem uma boa maneira de saber o que os bytes fora da memory que você pediu contêm. Então, quando você escreve em bytes fora da memory que você pediu em new você poderia estar escrevendo basicamente para qualquer coisa.

É aí que entra o comportamento indefinido . Como você não sabe o que está nesse espaço em que escreveu, não sabe o que acontecerá como resultado. Veja alguns exemplos de coisas que podem acontecer:

  • A memory não foi alocada quando você escreveu para ela. Nesse caso, os dados estão bem e nada de ruim parece acontecer. No entanto, se uma alocação de memory posterior usar esse espaço, tudo o que você tentou colocar lá será perdido.

  • A memory foi alocada quando você escreveu para ela. Nesse caso, parabéns, você acabou de replace alguns bytes randoms de alguma outra estrutura de dados em algum outro lugar de seu programa. Imagine replace uma variável em algum dos seus objects por dados randoms e considere o que isso significaria para o seu programa. Talvez uma lista em outro lugar agora tenha a contagem errada. Talvez uma string agora tenha alguns valores randoms para os primeiros caracteres, ou esteja vazia porque você substituiu esses caracteres por zeros.

  • A matriz foi alocada na borda de uma página, então os próximos bytes não pertencem ao seu programa. O endereço está fora da alocação do seu programa. Nesse caso, o sistema operacional detecta o access à memory aleatória que não é sua e encerra seu programa imediatamente com o SIGSEGV .

Basicamente, o comportamento indefinido significa que você está fazendo algo ilegal, mas como o C / C ++ foi projetado para ser rápido, os designers de linguagem não incluem uma verificação explícita para garantir que você não violará as regras, como outras linguagens (por exemplo, Java C #). Eles apenas listam o comportamento de quebrar as regras como indefinido, e então as pessoas que fazem os compiladores podem ter a saída mais simples, mais rápida, já que nenhuma verificação de limites é feita, e se você quebrar as regras, é problema seu.

Então, sim, isso às vezes funciona, mas nunca confie nele.

Não causaria nenhum problema em uma configuração puramente abstrata, onde você só se preocupa se a lógica do algoritmo é boa. Nesse caso, não há motivo para declarar o tamanho de um array. No entanto, o seu computador existe no mundo físico e possui apenas uma quantidade limitada de memory. Quando você está alocando memory, você está pedindo ao sistema operacional para permitir que você use parte da memory finita do computador. Se você for além disso, o sistema operacional deve pará-lo, geralmente matando seu processo / programa.

Sim, você deve escrevê-lo como arrayptr [index] porque a posição na memory de * (arrayptr + 4) está além do final do espaço que você alocou para o array. É a falha em C ++ que o tamanho da matriz não pode ser estendido uma vez alocado.