Explicación detallada de la memoria dinámica del lenguaje C


prefacio

La memoria es el lugar más propenso a errores en nuestro programa, especialmente la memoria dinámica en este capítulo.La mayor desventaja del lenguaje C es que necesita administrar la memoria por sí mismo . No existe un mecanismo de reciclaje de memoria en otros lenguajes de alto nivel.La memoria dinámica aprendida en este capítulo requiere que prestemos atención a la memoria y evitemos pérdidas de memoria.

Primero, por qué hay una asignación de memoria dinámica

Entre los métodos de desarrollo de memoria que hemos dominado se encuentran variables y matrices:

	int num = 10;//在栈空间上开辟4个字节
	int sum[10] = {
    
     0 };//在栈空间上开辟40个连续的字节

En el espacio abierto anterior, el tamaño del espacio abierto es fijo o se debe especificar la longitud de la matriz al declarar la matriz para que el espacio se pueda asignar en tiempo de compilación . Pero a veces no sabemos el tamaño del espacio que necesitamos, como cuántos estudiantes hay en una clase, si habrá nuevos estudiantes que se unirán a la clase más tarde, el espacio abierto por matrices estáticas es muy limitado, lo que causar desperdicio de espacio o espacio insuficiente. Esta vez requiere asignación de memoria dinámica.
Primero entendamos la distribución de la memoria a través de una imagen:
inserte la descripción de la imagen aquí
las variables que creamos antes generalmente están ubicadas en la pila, y el espacio abierto dinámicamente está ubicado en el área del montón. La pila puede aumentar hacia abajo y el montón puede aumentar hacia arriba.
1. Área de pila (stack): cuando se ejecuta la función, la unidad de almacenamiento de las variables locales en la función se puede crear en la pila, y se liberará automáticamente cuando finalice la función. El área de pila almacena principalmente las variables locales asignado por la función en ejecución, los parámetros de la función y los datos de retorno, la dirección de retorno, etc.
2. Área de montón (heap): Generalmente asignada y liberada por el programador, si el programador no libera, puede ser reciclada por el sistema operativo al final del programa.
3. Segmento de código: almacena el código binario del cuerpo de la función.

2. Asignación de memoria dinámica

La función de asignación de memoria dinámica debe incluir el archivo de encabezado stdlib.h.

1. malloc

Prototipo de función:

void* malloc(size_t size);

Esta función puede solicitar un espacio disponible continuo de la memoria y devolver un puntero a este espacio .
Nota:
si la asignación se realiza correctamente , se devolverá un puntero a este espacio.
Si el desarrollo falla , se devuelve un puntero NULL. Se debe comprobar el valor de retorno de malloc .
Si el tamaño es 0, es un comportamiento indefinido y depende del compilador.
El valor de retorno de la función es tipo void*, que podemos convertir al tipo que usamos según sea necesario .
El código es el siguiente (ejemplo):

int main()
{
    
    
	//向内存申请10个连续的整形空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int i = 0;
		//为开辟的空间赋值
		for (i = 0; i < 10; i++)
		{
    
    
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

inserte la descripción de la imagen aquí

2.gratis

Prototipo de función:

void* free(void* ptr);

La función libre se utiliza para liberar la memoria asignada dinámicamente.
Si el espacio al que apunta el parámetro ptr no se abre dinámicamente, el comportamiento de la función libre no está definido .
Si el parámetro ptr es un puntero NULL, la función no hace nada. .

3.calloc

Prototipo de función:

void* calloc(size_t num,size_t size);

La función de esta función es abrir un espacio para num elementos de tamaño e inicializar cada byte del espacio a 0 .
En comparación con malloc, esta función inicializará el espacio solicitado antes de devolver la dirección .
El código es el siguiente (ejemplo):

int main()
{
    
    
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int i = 0;
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

inserte la descripción de la imagen aquí

4.realizar

La función realloc puede hacer que la gestión de la memoria dinámica sea más flexible.
Cuando el espacio de nuestra aplicación es demasiado pequeño o demasiado grande, para usar la memoria de manera razonable, podemos usar realloc para ajustar el tamaño.

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

ptr es la dirección de memoria a ajustar .
El tamaño es el tamaño ajustado .
El valor de retorno de la función es la posición inicial de la memoria ajustada .
Hay dos situaciones en las que realloc ajusta el espacio de memoria:
1. Cuando hay suficiente espacio después del espacio original, agregue espacio directamente después de ptr y luego regrese a ptr.
2. Si no hay suficiente espacio después del espacio original. Luego encuentra un nuevo espacio. Y copie los datos en la memoria original al nuevo espacio, libere automáticamente el espacio antiguo y devuelva la dirección del nuevo espacio .
El código es el siguiente (ejemplo):

int main()
{
    
    
	int* p = (int*)malloc(5 * sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int* ptr = realloc(p, 10 * sizeof(int));
		if (ptr != NULL)
		{
    
    
			p = ptr;
		}
		int i = 0;
		for (i = 0; i < 10; i++)
		{
    
    
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

inserte la descripción de la imagen aquí
Nota:
la realloc necesita usar el nuevo puntero para recibir primero. Si la realloc falla y se usa el puntero original para recibir, el puntero original se perderá.

3. Errores comunes de memoria

1. Desreferenciar el puntero NULL: esta situación generalmente existe cuando la memoria asignada dinámicamente se usa directamente sin juzgar si se abrió con éxito.
2. Acceso fuera de los límites al espacio asignado dinámicamente: esta situación generalmente existe cuando el espacio asignado es menor que el espacio al que se accede.
3. Use free para liberar la memoria del espacio abierto no dinámicamente
4. Use free para liberar parte de la memoria abierta dinámicamente: Esta situación generalmente existe cuando se cambia el puntero del espacio abierto, de modo que el puntero no apunta a la posición inicial, y se realiza la liberación libre parte se liberará.
5. Múltiples liberaciones de la misma pieza de memoria dinámica: Esta situación comúnmente existe en múltiples liberaciones de memoria dinámica. La solución es establecer el puntero de la memoria liberada en NULL. Incluso si hay múltiples liberaciones de la memoria modificada, no causar problemas.

4. Matriz flexible

El arreglo flexible existe en la estructura
Las características del arreglo flexible:
1. Debe haber al menos otro miembro antes del miembro del arreglo flexible de la estructura.
2. El tamaño de esta estructura devuelta por sizeof no incluye la memoria de la matriz flexible.
3. La estructura que contiene la matriz flexible utiliza malloc para la asignación de memoria, y la memoria asignada es mayor que el tamaño de la estructura para acomodar el tamaño esperado de la matriz flexible.
El código es el siguiente (ejemplo):

struct S
{
    
    
	int n;
	int arr[0];
};
int main()
{
    
    
	struct S* p = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
	p->n = 10;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		p->arr[i] = i;
	}
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d ", p->arr[i]);
	}
	free(p);
	p=NULL;
	return 0;
}

inserte la descripción de la imagen aquí

struct S
{
    
    
	int n;
	int* arr;
};
int main()
{
    
    
	struct S* p = (struct S*)malloc(sizeof(struct S));
	p->arr = malloc(5*sizeof(int));
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		p->arr[i] = i;
	}
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d ", p->arr[i]);
	}
	free(p->arr);
	p->arr = NULL;
	free(p);
	p = NULL;
	return 0;
}

inserte la descripción de la imagen aquí
Una de las formas anteriores también puede lograr un desarrollo dinámico, ¿por qué configurar una matriz flexible?
La primera ventaja de usar una matriz flexible es facilitar la liberación de memoria, que se puede comparar con los dos códigos anteriores. La segunda ventaja es que es beneficioso para la velocidad de acceso. La contigüidad de la memoria favorece la mejora de la velocidad de acceso y la reducción de la fragmentación de la memoria.

Resumir

La asignación de memoria dinámica mejora en gran medida la flexibilidad de nuestra programación, pero la mayoría de los errores en el lenguaje C también provienen de esto. La asignación de memoria dinámica requiere que prestemos atención a muchas cosas para reducir los errores de memoria.

Supongo que te gusta

Origin blog.csdn.net/2301_76986069/article/details/130744605
Recomendado
Clasificación