Linguagem C avançada - gerenciamento dinâmico de memória

Quanto tempo sem ver. Hoje vamos aprender sobre gerenciamento dinâmico de memória em linguagem C. Este é um capítulo tão importante quanto os ponteiros, então você deve estudar bem este capítulo.

1. Por que existe a alocação dinâmica de memória?

Os métodos de desenvolvimento de memória que dominamos são:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
    
    0};//在栈空间上开辟大小为十个字节大小的内存,并且他们是连续的


No entanto, a forma de abertura do espaço acima mencionada tem duas características;

  1. O tamanho da alocação de espaço é fixo.
  2. Quando a matriz é declarada, o comprimento da matriz deve ser especificado e a memória necessária é alocada no tempo de compilação.
    Mas a demanda por espaço não é apenas a situação acima. Às vezes, o tamanho do espaço de que precisamos só pode ser conhecido quando o programa está em execução, e
    a maneira de abrir espaço quando o array é compilado não pode ser satisfeita.
    No momento, podemos apenas tentar o armazenamento dinâmico para liberar espaço.

2. Introdução às funções de memória dinâmica

malloc

insira a descrição da imagem aqui
void* malloc (size_t size);
Vamos primeiro entender o valor de retorno e os parâmetros desta função

Seu valor de retorno é o primeiro endereço para abrir o espaço de memória dinâmica. Se a abertura não for bem-sucedida, retornará um
ponteiro nulo. O parâmetro é o tamanho do byte. escreva 40 bytes para abrir

Esta função se aplica a um espaço contíguo disponível da memória e retorna um ponteiro para este espaço.

  • Se a alocação for bem-sucedida, um ponteiro para o espaço alocado será retornado.
  • Se a abertura falhar, um ponteiro NULL é retornado, então o valor de retorno de malloc deve ser verificado.
  • Se a abertura falhar, um ponteiro NULL é retornado, então o valor de retorno de malloc deve ser verificado.
    O tipo do valor de retorno é void*, então a função malloc não conhece o tipo do espaço aberto, e o usuário pode decidir por si mesmo quando usá-lo.
  • Se o tamanho do parâmetro for 0, o comportamento de malloc é indefinido pelo padrão e depende do compilador.

Da mesma forma, temos malloc para abrir espaço, então podemos usar free para liberar espaço

insira a descrição da imagem aqui

Essa função serve para liberar o espaço que abrimos com a função malloc para abrir espaço na memória, ou seja, eles devem ser usados ​​consecutivamente, caso contrário haverá um problema de vazamento de memória, então nossa memória será consumida pouco a pouco.

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

Então, vamos usar um exemplo para entender como usar essas duas funções.
O arquivo de cabeçalho é <stdlib.h>

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int arr[10] = {
    
    0};//这是在栈上开辟的空间
	int* ptr = (int*)malloc(40);//开辟十个整型
	//在内存上开辟
	if (ptr == NULL)
	{
    
    
		perror("malloc");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*(ptr + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(ptr + i));
	}
	//使用完之后释放
	free(ptr);
	ptr = NULL;
	//我们要把它变成空指针,要不然可能就会变成野指针
	return 0;
}

Quando nossa memória dinâmica é desenvolvida, ela é desenvolvida na área de heap, a área de pilha é desenvolvida para variáveis ​​locais e variáveis ​​temporárias, que serão destruídas quando forem retiradas da pilha, portanto não precisamos liberá-la. Há também nossa área estática, que armazena nossos dados globais, variáveis ​​e variáveis ​​estáticas.

Deixe-me dar um exemplo para ilustrar a importância do grátis. Este exemplo ajudará você a entender melhor
. , eles terminaram (grátis), mas o aluno é obcecado por isso. Ele é um cachorro lambendo. Ele ainda se lembra da colega de classe número de telefone e assediar a colega de vez em quando. Endereço) bateu na cabeça dele, baixou e esqueceu esse número de telefone, então ele não pode assediar outras colegas de classe.

calloc

insira a descrição da imagem aqui
A linguagem C também fornece uma função chamada calloc, que também é usada para alocação dinâmica de memória. O protótipo é o seguinte:

void* calloc (size_t num, size_t tamanho);

O primeiro parâmetro na função é abrir vários elementos, e o segundo parâmetro é o tamanho de cada elemento. Na verdade, é semelhante ao malloc, mas quando usamos calloc, o conteúdo que abrimos será inicializado com 0.

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		perror("CALLOC");
		return 1;
	}
	free(p);
	p = NULL;
	return 0;
}

insira a descrição da imagem aqui

  • A função da função é abrir um espaço para num elementos cujo tamanho é size e inicializar cada byte do espaço como 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.
#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		perror("Calloc");
		return 1;
	}
	int i = 0;
	
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(p+i));
	}
	free(p);
	p = NULL;
	return 0;
}

insira a descrição da imagem aqui
realloc

O surgimento da função realloc torna o gerenciamento dinâmico de memória mais flexível

Às vezes, descobrimos que o espaço que solicitamos no passado é muito pequeno e, às vezes, sentimos que o espaço que solicitamos é muito grande. Para tornar a memória razoável, devemos fazer ajustes flexíveis no tamanho da memória. Então a função realloc pode ajustar o tamanho da memória alocada dinamicamente

Função protótipo
void* realloc (void* ptr, size_t size);

insira a descrição da imagem aqui

ptr é o
tamanho do endereço de memória a ser ajustado, o novo tamanho após o ajuste
e o valor de retorno é a posição inicial da memória após o ajuste.
Com base no ajuste do tamanho do espaço de memória original, esta função também moverá os dados na memória original para o novo espaço.

Mas quando nosso desenvolvimento não for bem-sucedido, o valor de retorno será diferente das duas situações diferentes acima

  • Caso 1: Há espaço suficiente após o espaço original
  • Caso 2: Não há espaço suficiente após o espaço original
  • Caso 3: retornar um ponteiro nulo, este não é aberto com sucesso

Aqui falamos principalmente da situação 2, porque realloc é aumentar o espaço de memória, por exemplo, nosso malloc abre dez inteiros, mas agora precisamos de 20, então podemos escrever como, mas haverá problemas ao abrir, se (int*)realloc(p,80)houver não há espaço suficiente atrás dele, ele não pode ser aberto, então precisamos encontrar um novo local para abrir e continuar procurando um local na área de heap para abrir. Neste momento, o endereço que retornamos não é o original, mas um novo endereço.

insira a descrição da imagem aqui

Então vamos olhar o código para ver como ele o usa


#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
    
    
		perror("Malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*(p + i) = i;
	}
	
	int* tmp = (int*)realloc(p, 80);
	if (tmp == NULL)
	{
    
    
		perror("Realloc");
		return 1;
	}
	else
	{
    
    
		p = tmp;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		*(p + i) = i;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	free(tmp);
	p = NULL;
	return 0;
}

3.1 Operação de desreferenciação no ponteiro NULL

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

Por exemplo, aqui temos que julgar p para ver se é um ponteiro nulo.Se for um ponteiro nulo, haverá um problema, que é um acesso ilegal.

Solução: faça um julgamento não nulo sobre p

3.2 Acesso fora dos limites a espaços alocados dinamicamente

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

Portanto, temos que julgar se nosso alcance de acesso está fora dos limites depois de escrever

3.3 Use free para liberar memória não dinâmica

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

Desenvolvemos isso na pilha, não precisamos liberá-lo, o sistema operacional irá destruí-lo automaticamente

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

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

Este tipo de problema é muito comum. Se mudarmos a posição inicial, não podemos liberá-la completamente, então devemos liberá-la da posição inicial criamos esta memória dinâmica. 3.5 Várias liberações da mesma memória
dinâmica

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

Portanto, temos que alterá-lo para um ponteiro nulo após cada lançamento

3.6 Abra a memória dinamicamente e esqueça de liberá-la (vazamento de memória)

Esquecemos de liberar quando terminamos de escrever o código, o que levará a vazamentos de memória

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

Solução, livre (liberação) e definida como um ponteiro nulo

Isso é tudo para o compartilhamento de hoje. Na próxima edição, compartilharemos algumas perguntas do teste escrito

Acho que você gosta

Origin blog.csdn.net/2301_76895050/article/details/131867050
Recomendado
Clasificación