Capítulo de Condensação "Volume C Record" - Os Quatro Irmãos do Gerenciamento Dinâmico de Memória na Primeira Batalha

Antigamente não havia ninguém, e Gubiao Lingyundao era um amigo. A espada é minha vida, e é ao mesmo tempo louca e cavalheiresca.

 

contente

⭐1 . Os principais pontos deste capítulo

⭐2 . Quatro áreas de memória C/C++

⭐3 . Alocação dinâmica de memória

2.1 O que é alocação dinâmica de memória

2.2 Por que há alocação dinâmica de memória

⭐4 . Função de alocação de memória dinâmica

4,1 malloc

 4.2 grátis

4,3 calo

4.4realocar

⭐5 . Erros comuns de memória dinâmica

5.1 Desreferenciando um ponteiro NULL

5.2 Acesso fora dos limites a espaços desenvolvidos dinamicamente

5.3 Usando livre para memória não dinâmica

5.4 Use free para liberar parte de uma memória alocada dinamicamente

5.5 Libere o mesmo pedaço de memória dinâmica várias vezes

5.6 Abra a memória dinamicamente e esqueça de liberar (vazamento de memória)

⭐6 . Perguntas clássicas do teste escrito

6.1 Tópico 1

6.2 Tópico 2

6.3 Tópico 3

6.4 Tópico 4


⭐1. Os principais pontos deste capítulo

  1. Apresentando as quatro áreas da memória C/C++
  2. O que é alocação dinâmica de memória
  3. Por que a alocação dinâmica de memória existe
  4. Quatro irmãos da função de memória dinâmica: malloc, calloc, realloc, free
  5. Resumir erros comuns de memória dinâmica
  6. Análise de várias questões clássicas de exames escritos

⭐2. Quatro áreas de memória C/C++

Como todos sabemos, a memória é usada para armazenar os dados quando o programa está em execução. Para facilitar o gerenciamento desses dados, a memória é dividida em quatro áreas, a saber, a área de pilha , a área de heap , o segmento de dados , e o segmento de código .

 área da pilha:

Área de pilha (pilha): Ao executar uma função, as unidades de armazenamento de variáveis ​​locais na função podem ser criadas na pilha e a função é executada
Essas unidades de armazenamento são liberadas automaticamente no final. A operação de alocação de memória de pilha é incorporada ao conjunto de instruções do processador, o que é muito eficiente, mas
A capacidade de memória alocada é limitada. A área de pilha armazena principalmente variáveis ​​locais, parâmetros de função, dados de retorno,
endereço de retorno, etc
Recursos da área de pilha:
Geralmente, o ciclo de vida e o escopo das variáveis ​​de pilha são curtos, e só existem no quadro da pilha da função (corpo da função).Quando o quadro da pilha da função é destruído (a função retorna), o espaço ao qual a variável pertence também é destruído. Portanto, a função geralmente não retorna o endereço de sua variável local, pois esse espaço é destruído quando a função retorna, e acessá-lo novamente é um acesso ilegal à memória.
(Nota: O quadro de pilha de função é um espaço especialmente aberto pela função na área de pilha.)

Área da pilha:

Esta área é alocada e liberada pelo próprio programador, caso o espaço alocado não seja liberado, será liberado pelo sistema operacional ao final do programa.

Características da área de pilha:

 O espaço é alocado e liberado pelo programador. Os dados na área de heap só podem ser liberados pelo programador ou pelo sistema operacional quando o programa termina. Portanto, para alguns dados que você deseja salvar por mais tempo, geralmente é colocado na área de heap, e a capacidade da área de heap é maior que a da pilha. A área é maior.

Segmento de dados:

O segmento de dados (área estática) (estático) armazena variáveis ​​globais e dados estáticos. Liberado pelo sistema após o término do programa.

Recursos do segmento de dados:

Armazenar variáveis ​​globais e variáveis ​​estáticas, também conhecidas como área global/área estática. O ciclo de vida e o escopo das variáveis ​​globais geralmente são o programa inteiro. Uma variável estática é uma variável modificada por estática. Quando uma variável local é modificada por estática, ela é colocada no segmento de dados. Neste momento, seu ciclo de vida se torna mais longo , mas o escopo permanece inalterado. Estático Modificar a variável global limitará o escopo da variável global. Neste momento, seu escopo se torna o arquivo atual, mas o ciclo de vida permanece inalterado.

Fragmento de código:

Segmento de código: código binário que armazena constantes/corpos de função somente leitura (funções de membro de classe e funções globais).

Recursos do snippet de código:

O código binário do corpo da função é armazenado, e algumas constantes de caracteres também são armazenadas. Essa área fica próxima à área do segmento de dados.

Resumir:

Com relação às quatro áreas da memória, basta compreendê-las nesta fase, não é necessário ir muito fundo, é apenas para facilitar nosso entendimento e aprendizado, a distribuição real da memória não é assim.

⭐3. Alocação dinâmica de memória

2.1 O que é alocação dinâmica de memória

Enciclopédia Baidu : A chamada alocação dinâmica de memória (Dynamic Memory Allocation) refere-se ao método de alocação dinâmica ou recuperação de espaço de armazenamento no processo de execução do programa. A alocação dinâmica de memória não requer pré-alocação de espaço de armazenamento como arrays e outros métodos de alocação de memória estática, mas é alocada pelo sistema em tempo real de acordo com as necessidades do programa, e o tamanho da alocação é o tamanho exigido pelo programa.

2.2 Por que há alocação dinâmica de memória

Porque às vezes precisamos aumentar dinamicamente nossa capacidade de espaço. Por exemplo, quando escrevemos um catálogo de endereços, solicitamos 100 espaços consecutivos na área da pilha, então esse espaço não pode ser expandido ou reduzido, por isso também é chamado de memória estática. Alocação , e a alocação dinâmica de memória pode resolver esse problema, podemos primeiro solicitar 10 espaços na área de heap e, quando a função de detecção verificar se o espaço está cheio, a capacidade será aumentada automaticamente. A chamada memória dinâmica significa que a memória desta aplicação pode ser expandida ou reduzida, pelo que esta funcionalidade é normalmente utilizada na vertente de listas encadeadas.

⭐4. Função de alocação de memória dinâmica

4,1 malloc

Protótipo de função:
void* malloc ( size_t size );
Essa função solicita um espaço livre contíguo do heap e retorna um ponteiro para esse espaço.
  • Se a abertura for bem-sucedida, um ponteiro para o espaço aberto é retornado.
  • Se o desenvolvimento falhar, um ponteiro NULL é retornado, portanto, o valor de retorno de malloc deve ser verificado.
  • O tipo do valor de retorno é void*, então a função malloc não sabe o tipo do espaço a ser aberto, e o usuário decide é usado.
  • Se o tamanho do parâmetro for 0, o comportamento do malloc é indefinido pelo padrão e depende do compilador

 4.2 grátis

A linguagem C disponibiliza outra função gratuita, que é especialmente utilizada para liberação e recuperação de memória dinâmica.O protótipo da função é o seguinte:

Protótipo de função:

void livre ( void* ptr );

A função free é usada para liberar memória alocada dinamicamente.
  • Se o espaço apontado pelo parâmetro ptr não for alocado dinamicamente, o comportamento da função livre é indefinido.
  • Se o parâmetro ptr for um ponteiro NULL, a função não fará nada.

Tanto malloc quanto free são declarados no arquivo de cabeçalho stdlib.h.  

por exemplo:

#include <stdio.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(10 * sizeof(int));
	if (NULL != ptr)//判断ptr指针是否为空
	{
		//将堆区开辟的10个整形数据都置为0
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(ptr + i) = 0;
		}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;//是否有必要?
	return 0;
}

Quando livre (ptr), é recomendável definir ptr como NULL, caso contrário, ptr é um ponteiro selvagem e há um risco oculto.

4,3 calo

A linguagem C também fornece uma função chamada calloc , que também é usada para alocação dinâmica de memória.
Protótipo de função:
void* calloc ( size_t num , size_t size );
  •  A função da função é abrir um espaço para num elemento de tamanho size e inicializar cada byte do espaço para 0.

  • A única diferença da função malloc é que calloc inicializará cada byte do espaço solicitado para todos os 0s antes de retornar o endereço.

4.4realocar

Protótipo de função:

void* realloc ( void* ptr , size_t tamanho );

  • ptr é o endereço de memória para ajustar
  • novo tamanho após ajuste de tamanho
  • O valor de retorno é a posição inicial da memória ajustada.
  • Com base no ajuste do tamanho do espaço de memória original, esta função também moverá os dados da memória original para o novo espaço.
  • O surgimento da função realloc torna o gerenciamento dinâmico de memória mais flexível.
  • Às vezes, achamos que o espaço solicitado no passado é muito pequeno, e às vezes sentimos que o espaço solicitado é muito grande. Para usar a memória razoavelmente, devemos ajustar com flexibilidade o tamanho da memória. A função realloc pode ajustar o tamanho da memória alocada dinamicamente.

Existem duas situações em que a realloc ajusta o espaço de memória:

1. Expansão in situ

Adicione espaço diretamente da parte de trás da memória, os dados originais não mudam.

Diagrama:

 

2. Expansão remota

Como o espaço a ser adicionado posteriormente é muito grande, a área de heap não é suficiente para continuar a abrir espaço posteriormente (o espaço pode ser ocupado por outras variáveis ​​posteriormente), então só podemos encontrar um novo espaço de memória grande, copie o original data para o novo espaço depois de encontrá-lo e retornar O endereço inicial do novo espaço.

Diagrama:

 

⭐5. Erros comuns de memória dinâmica

5.1 Desreferenciando um ponteiro NULL

void test()
{
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;//如果p的值是NULL,就会有问题
 free(p);
}

 Depois de abrir o espaço de heap, verifique se o valor de retorno está vazio, caso contrário, ele pode falhar ao abrir e retornar NULL causará o problema de desreferência de ponteiro nulo subsequente.

5.2 Acesso fora dos limites a espaços desenvolvidos dinamicamente

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}

 *(p + i) = i; // Acesso fora dos limites quando i é 10

5.3 Usando livre para memória não dinâmica

void test()
{
	int a = 10;
	int* p = &a;
	free(p);//ok?
}

Aqui a é uma variável na área de heap, que não pertence à memória aberta pela área de heap e não pode ser liberada usando free, caso contrário, um erro será relatado. 

5.4 Use free para liberar parte de uma memória alocada dinamicamente

void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);//p不再指向动态内存的起始位置
}

Um pedaço de espaço aberto ou não é liberado ou todo ele é liberado, não apenas uma parte dele

5.5 Libere o mesmo pedaço de memória dinâmica várias vezes

void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}

Esse problema geral de cópia profunda e cópia superficial em C++ é a liberação repetida de memória heap, que relatará um erro. 

5.6 Abra a memória dinamicamente e esqueça de liberar (vazamento de memória)

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

Depois de usar a memória heap, certifique-se de liberar o espaço, caso contrário, causará vazamento de memória, o que é um problema muito sério. Na empresa, isso é um acidente, e o bônus de final de ano pode ser cancelado. Algumas pessoas podem dizer: Depois que o programa for executado, o sistema operacional não recuperará automaticamente o espaço de heap? Mas e se esse programa funcionar 24 horas por dia? Isso é uma coisa muito séria.

⭐6. Perguntas clássicas do teste escrito

6.1 Tópico 1

void GetMemory(char* p) {
	p = (char*)malloc(100);
}
void Test(void) {
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
Qual é o resultado da execução da função Test ?
analisar:
  • Não imprimirá hello world, pois GetMemory(str) não alterará str, e str ainda é NULL, então ao chamar strcpy(), haverá um problema de dereference de NULL. Para isso, você precisa conhecer a implementação de simulação de strcpy , especificamente Você pode consultar meu artigo anterior.
  • Como o espaço solicitado pela área de heap não é liberado, ocorrerá um vazamento de memória.

6.2 Tópico 2

char* GetMemory(void) {
	char p[] = "hello world";
	return p;
}
void Test(void) {
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

analisar:

  • Irá imprimir hello world, após str=GetMemory(), str aponta para o espaço onde "hello world" está armazenado.
  • O que precisa ser analisado aqui é que o primeiro endereço da string passada por "hello world" é dado para p, e a string é uma string constante, que geralmente é colocada na área de constante de caracteres da memória.
  • No entanto, se o espaço na área de heap não for liberado, haverá um vazamento de memória.

6.3 Tópico 3

void GetMemory(char** p, int num) {
	*p = (char*)malloc(num);
}
void Test(void) {
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

analisar:

  • Irá imprimir hello, onde GetMeory() passa o endereço de str, o endereço do ponteiro secundário, *p é o próprio str, e então str aponta para o espaço de 100 caracteres da área de heap. Através da função strcpy(), hello pode ser copiado para o espaço de heap apontado por str.
  • O conhecimento de ponteiros secundários e o princípio de implementação do strcpy são necessários aqui.Para isso, você pode ver meu artigo anterior, onde há uma análise muito clara.
  • Da mesma forma, sem free(str), haverá vazamentos de memória.

6.4 Tópico 4

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

analisar:

  • O mundo não será impresso, str aponta para o espaço de 100 caracteres da área de heap, strcpy(str, "hello"), o espaço de heap é colocado em "hello", free(str), o espaço de heap é liberado e str ainda aponta para esse tempo Nesse espaço de heap, str é um ponteiro selvagem, o que é muito perigoso.
  • A próxima instrução de julgamento if, str não é igual a NULL, executa a instrução condicional e, em seguida, copia "world" para o espaço de heap, mas como o espaço de heap é liberado antecipadamente, haverá um conflito de acesso de leitura e gravação na memória .
  • Isso mostra que quando free(str), str=NULL é muito necessário.

Acho que você gosta

Origin blog.csdn.net/m0_62171658/article/details/123514730
Recomendado
Clasificación