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;
- O tamanho da alocação de espaço é fixo.
- 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
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
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
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;
}
- 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;
}
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);
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.
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