[Lenguaje C] Explicación detallada de la gestión de memoria dinámica

Acerca de la asignación de memoria dinámica

Recuerde los métodos de asignación de memoria que aprendimos antes:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
    
    0};//在栈空间上开辟10个字节的连续空间

Cuando aprendemos lenguaje C, sabemos que las estructuras de datos suelen tener un tamaño fijo. Tome la matriz como ejemplo: una vez compilado el programa, se determina el tamaño de la matriz y el número de elementos . Entonces, el tamaño de la estructura de datos no se puede cambiar sin modificar el programa y compilarlo nuevamente. El resumen son las siguientes dos características:

  1. El tamaño de la apertura del espacio es fijo.
  2. Al declarar una matriz, se debe especificar la longitud de la matriz. Una vez que se determina el tamaño del espacio de la matriz, no se puede ajustar.

Pero la demanda de espacio no se limita sólo a las situaciones antes mencionadas. A veces, la cantidad de espacio que necesitamos solo se puede saber cuando el programa se está ejecutando, por lo que el método de crear espacio durante la compilación de la matriz no es suficiente.
Entonces, el lenguaje C introdujo la asignación de memoria dinámica, que permite a los programadores solicitar y liberar espacio ellos mismos . A continuación se presentará cómo abrir memoria dinámicamente.

Introducción a las funciones malloc y calloc

La siguiente es la definición de par cplusplusmalloc :

void* malloc (size_t size);

Esta función se aplica a un espacio continuo en la memoria y devuelve un puntero a este espacio. sizeEs decir, el tamaño del espacio de memoria que desea solicitar void*es la dirección del primer elemento de la memoria solicitada. Como no conoce el tipo, lo usa void*. También hay que tener en cuenta los siguientes puntos:

  • Si la asignación se realiza correctamente, se devuelve un puntero al espacio asignado.
  • Si la asignación falla, se devolverá un puntero NULL, por lo que se debe verificar el valor de retorno de malloc.
  • El tipo de valor de retorno es void*, por lo que la función malloc no sabe el tipo de espacio a abrir, el usuario debe decidir por sí mismo al utilizarlo.
  • Si el parámetro de tamaño es 0, el comportamiento de malloc no está definido por el estándar y depende del compilador.

Echemos un vistazo a la definición de par cpluspluscalloc :

void* calloc (size_t num, size_t size);

mallocDe hecho, calloces muy similar a. rallocEl parámetro en sizees el tamaño de cada tipo de datos que desea solicitar, que numes el número de tipos de datos que desea solicitar. El tamaño total de la solicitud es num*size, de hecho. , se puede representar mallocpor size. Las otras características son mallocsimilares, por lo que no las presentaré en detalle. Por supuesto, existen diferencias
entre los dos , como sigue:

  • malloc La única diferencia con la función es que cada bytecalloc del espacio solicitado se inicializa a 0 antes de devolver la dirección .

Entonces, si queremos inicializar el espacio de memoria aplicado dinámicamente a 0, es rallocmás conveniente usarlo.

Reciclaje de memoria dinámica ---- gratis

De hecho malloc, callocfunciones como la asignación dinámica de memoria en realidad asignan memoria en el área del montón. Dado que el sistema no recupera automáticamente el espacio de memoria solicitado por estas funciones , el uso de dichas funciones con demasiada frecuencia para abrir espacio provocará el agotamiento del montón. En este momento necesitamos liberar activamente el espacio abierto , por lo que introducimos freeuna función, cuyo prototipo de función es el siguiente:

void free (void* ptr);

El puntero aquí ptrapunta a nuestra primera dirección de memoria asignada dinámicamente . Solo apuntando a la primera dirección se puede liberar por completo el espacio de memoria asignado dinámicamente. Hay dos casos especiales con respecto a ptrlos punteros ;

  • Si ptrel espacio al que apunta el parámetro no se asigna dinámicamente, freeel comportamiento de la función no está definido.
  • Si el argumento ptr es NULLun puntero, la función no hace nada.

Dos cosas más a tener en cuenta :

  1. Después de liberar el espacio abierto, el puntero original que apunta a este espacio ptrtodavía almacena la dirección aquí. Para evitar asignar o desreferenciar accidentalmente este puntero más adelante, lo que causa un problema de puntero salvaje , después de liberar el espacio, también debemos Este puntero debe ser asignado NULL.
  2. Es mejor tener siempre un puntero a este espacio al escribir código. Si no hay un puntero a este espacio, entonces este espacio no será accesible ni liberado. Para los programas, el espacio inaccesible también se llama basura, y los programas que dejan basura tienen pérdidas de memoria .
    Insertar descripción de la imagen aquí

Como se muestra en la figura anterior, el puntero original papunta al primer bloque de memoria y, después de la operación, papunta al segundo bloque de memoria. Entonces, dado que no hay un puntero que apunte al primer bloque de memoria, este bloque de memoria ya no se puede usar, lo cual es la basura mencionada anteriormente, lo que provoca una pérdida de memoria .

introducción a la función de reasignación

A veces encontramos que el espacio que solicitamos en el pasado es demasiado pequeño y otras veces sentimos que el espacio que solicitamos es demasiado grande. Para solicitar memoria de manera razonable, debemos hacer ajustes flexibles en el tamaño de la memoria. En este momento, reallocla función puede ajustar el tamaño de la memoria asignada dinámicamente .
El prototipo de función es el siguiente:

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

El puntero ptrapunta a la dirección de memoria que se ajustará sizey al tamaño de memoria ajustado. El valor de retorno es la dirección inicial de la memoria ajustada.
Caso 1: Hay suficiente espacio después del espacio original
. En el caso 1, si desea expandir la memoria, simplemente agregue espacio directamente después de la memoria original. Los datos en el espacio original no cambiarán.
Insertar descripción de la imagen aquí

Caso 2: No hay suficiente espacio después del espacio original
. Cuando es el caso 2 y no hay suficiente espacio después del espacio original, el método de expansión es: encontrar otro espacio continuo de tamaño apropiado en el espacio del montón para usar. De esta forma, la función devuelve una nueva dirección de memoria. En este caso, para los datos que ya están en la memoria original, esta función copiará esos datos a la nueva memoria y se liberará la memoria original .
Insertar descripción de la imagen aquí
También existe el problema de no poder encontrar el espacio de memoria original reallocdebido a que el puntero se asigna cuando no se puede abrir el espacio de memoria dinámica . NULLGeneralmente creamos un nuevo puntero para recibir la dirección y NULLluego lo asignamos al puntero original después de juzgar que no es correcto, de la siguiente manera:

int main()
{
    
    
     int* ptr=(int*)malloc(5*sizeof(int));
     int* p=(int*)realloc(10*sizeof(int));
     if(p != NULL)
          ptr = p;
     //......(代码)
     free(ptr);
     ptr = NULL;
     return 0;
}

Errores comunes de memoria dinámica

  1. Operación de desreferenciación en puntero NULL:
 void test()
 {
    
    
      int *p = (int *)malloc(INT_MAX/4);
      *p = 20;
      free(p);
 }

Si el valor en este código pes NULL, habrá un problema.

  1. Acceso fuera de límites al espacio asignado dinámicamente:
 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);
 }
  1. Utilice la versión gratuita para memoria asignada de forma no dinámica:
void test()
{
    
    
     int a = 10;
     int *p = &a;
     free(p);
}

El de aquí ase abre en el área de la pila. Si utiliza freeel sistema de liberación, se informará un error.

  1. Libere la misma memoria dinámica varias veces:
void test()
 {
    
    
      int *p = (int *)malloc(100);
      free(p);
      free(p);//重复释放
 }

mallocEl espacio de memoria dinámica abierto aquí se libera repetidamente y el sistema también informará un error.

  1. Utilice gratis para liberar una parte de la memoria asignada dinámicamente:
 void test()
 {
    
    
      int *p = (int *)malloc(100);
      p++;
      free(p);
 }

Debido a que ++el símbolo cambiará el valor de la variable, ya pno apunta a la posición inicial de la memoria dinámica. En este momento, el uso de freela liberación no liberará toda la memoria dinámica.

  1. Memoria asignada dinámicamente y olvídese de liberarla (pérdida de memoria):
 void test()
 {
    
    
      int *p = (int *)malloc(100);
      if(NULL != p)
     {
    
    
          *p = 20;
     }
 }
 int main()
 {
    
    
      test();
      while(1);
 }

Después de llamar test()a la función, el espacio de memoria abierto no se libera activamente. El mismo espacio de memoria en el área de la pila se reciclará después de llamar a esta función, por lo que int* pno se puede encontrarmalloc el espacio abierto. Esta es la basura mencionada anteriormente, y la basura que queda El programa tiene una pérdida de memoria . ¡ Recuerde que la memoria abierta dinámicamente debe liberarse!

Supongo que te gusta

Origin blog.csdn.net/2301_77404033/article/details/133247008
Recomendado
Clasificación