Si free() conoce la longitud de mi matriz, ¿por qué no puedo solicitarla en mi propio código?

8 minutos de lectura

avatar de usuario
Chris Cooper

Sé que es una convención común pasar la longitud de las matrices asignadas dinámicamente a las funciones que las manipulan:

void initializeAndFree(int* anArray, size_t length);

int main(){
    size_t arrayLength = 0;
    scanf("%d", &arrayLength);
    int* myArray = (int*)malloc(sizeof(int)*arrayLength);

    initializeAndFree(myArray, arrayLength);
}

void initializeAndFree(int* anArray, size_t length){
    int i = 0;
    for (i = 0; i < length; i++) {
        anArray[i] = 0;
    }
    free(anArray);
}

pero si no hay forma de obtener la longitud de la memoria asignada desde un puntero, ¿cómo free() ¿”automágicamente” saber qué desasignar cuando todo lo que estoy dando es el mismo puntero? ¿Por qué no puedo entrar en la magia, como programador de C?

Donde hace free() obtener su conocimiento libre (har-har) de?

  • También tenga en cuenta que int length Está Mal. Las longitudes de matriz y las compensaciones y los tamaños de tipo, y otras cosas similares son del tipo size_tque se define en el stddef.h, stdio.h, stdlib.hy string.h encabezados La principal diferencia entre size_t y int es eso int está firmado y size_t no está firmado, pero en algunas plataformas (por ejemplo, 64 bits) también pueden tener diferentes tamaños. Siempre debes usar size_t.

    – Chris Lutz

    16 de abril de 2010 a las 6:21

  • @Chris Lutz: Gracias. Haré ese cambio. He visto mucho el sufijo “_t”. ¿Qué significa? “Escribe”? Como en “size_type?” ¿Qué otros ejemplos hay?

    – Chris Cooper

    16 de abril de 2010 a las 10:52

  • Sí, significa tipo. Hay muchos otros ejemplos, incluyendo int32_t, regex_t, time_t, wchar_tetc

    – Mateo Flaschen

    16 de abril de 2010 a las 11:52

  • un tipo con _t sufijo es un tipo que no es un tipo fundamental de la lengua. Lo que significa que size_t es unsigned int, unsigned long, o algo así.

    – u0b34a0f6ae

    15 mayo 2010 a las 17:19

avatar de usuario
Mateo Flaschen

Además del punto correcto de Klatchko de que el estándar no lo proporciona, las implementaciones reales de malloc/free a menudo asignan más espacio del que solicita. Por ejemplo, si solicita 12 bytes, puede proporcionar 16 (ver Un asignador de memoria, que señala que 16 es un tamaño común). Por lo tanto, no necesita saber que solicitó 12 bytes, solo que te dio un fragmento de 16 bytes.

  • Pero, ¿qué pasa con C++? En C++, el tiempo de ejecución conoce el tamaño real cuando se asigna con new type[n] ya que llama a n constructores para delete []?

    – Victor Sehr

    16 de abril de 2010 a las 11:25

  • @Viktor No necesita almacenar el tamaño para tipos primitivos o tipos con un destructor vacío.

    – Yacoby

    16 de abril de 2010 a las 11:29


  • @Yacoby: cierto, todavía no responde mi pregunta

    – Victor Sehr

    16 de abril de 2010 a las 12:06

  • Viktor, entonces tu pregunta es ¿por qué C++ no proporciona una forma de obtener la cantidad de elementos en una matriz, cuando todos los elementos tienen destructores no vacíos? Probablemente porque sería algo confuso (debe conocer los detalles de implementación de una clase para saber si la función es segura) y no es una característica necesaria. Pero esto merece su propia pregunta (que puede que ya exista).

    – Mateo Flaschen

    16 de abril de 2010 a las 12:28


  • @ViktorSehr: si una implementación asigna más almacenamiento del solicitado y puede determinar por cualquier medio que llamar a un destructor sobre ese almacenamiento no tendrá efectos secundarios visibles, no es necesario realizar un seguimiento de la asignación real. Tener una función para solicitar el tamaño real de una asignación pero no hacer que funcione en los casos en que un compilador determinó que no es necesario mantener esa información sería muy confuso.

    – Super gato

    14/10/2016 a las 18:20

No puede obtenerlo porque el comité C no lo exigió en el estándar.

Si está dispuesto a escribir algún código no portátil, puede tener suerte con:

*((size_t *)ptr - 1)

o tal vez:

*((size_t *)ptr - 2)

Pero si eso funciona dependerá exactamente de dónde almacena esos datos la implementación de malloc que está utilizando.

  • simplificaría a ((size_t *)ptr)[-1] personalmente.

    – Chris Lutz

    16 de abril de 2010 a las 6:18

  • @Chris Lutz: ¿Qué significa [-1] ¿indicar?

    – Chris Cooper

    16 de abril de 2010 a las 10:57

  • Pretende que el puntero es una matriz e indexa el elemento uno antes del comienzo.

    – Simón Buchan

    16 de abril de 2010 a las 11:23

  • @Simon: Ups. DUH. Gracias. Pensé que estaba declarando un tipo. Creo que mis ojos se pusieron vidriosos cuando vi todos los * y (‘s. =P

    – Chris Cooper

    16 de abril de 2010 a las 12:10

  • ¿Puede explicar en qué plataformas se espera que funcione?

    – einpoklum

    12 de diciembre de 2013 a las 10:18

avatar de usuario
norte 1.1

Después de leer la respuesta de Klatchko, yo mismo lo probé y ptr[-1] de hecho almacena el real memoria (generalmente más que la memoria que solicitamos, probablemente para ahorrar contra fallas de segmentación).

{
  char *a = malloc(1);
  printf("%u\n", ((size_t *)a)[-1]);   //prints 17
  free(a);
  exit(0);
}

Probando con diferentes tamaños, GCC asigna la memoria de la siguiente manera:

Inicialmente, la memoria asignada es de 17 bytes.
La memoria asignada es al menos 5 bytes más que el tamaño solicitado, si se solicita más, asigna 8 bytes más.

  • si el tamaño es [0,12]la memoria asignada es 17.
  • si el tamaño es [13]la memoria asignada es 25.
  • si el tamaño es [20]la memoria asignada es 25.
  • si el tamaño es [21]la memoria asignada es 33.

  • ¡Frio! Muchas gracias por la explicación.

    – Chris Cooper

    16 de abril de 2010 a las 12:09

  • Tenga en cuenta que un asignador diferente podría almacenar el tamaño en otro lugar. Tal vez todo un grupo de asignaciones comparta un campo de 64 KB con el tamaño almacenado en el primer bloque del campo.

    – Zan Lince

    20 de marzo de 2012 a las 21:48

  • Esta es una respuesta completamente engañosa. GCC no asigna nada. Su biblioteca C hace eso (o incluso el sistema operativo). Ingeniería inversa una implementación como esta es programación por experimentaciónque está destinado a fallar a la izquierda, a la derecha y al centro.

    – Jens

    7 mayo 2014 a las 13:27

avatar de usuario
clifford

Si bien es posible obtener los metadatos que el asignador de memoria coloca antes del bloque asignado, esto solo funcionaría si el puntero es realmente un puntero a un bloque asignado dinámicamente. Esto afectaría seriamente la utilidad de la función que requiere que todos los argumentos pasados ​​sean punteros a dichos bloques en lugar de decir una simple matriz automática o estática.

El punto es que no hay una forma portátil desde la inspección del puntero para saber a qué tipo de memoria apunta. Entonces, si bien es una idea interesante, no es una propuesta particularmente segura.

Un método seguro y portátil sería reservar la primera palabra de la asignación para mantener la longitud. GCC (y quizás algunos otros compiladores) admite un método no portátil para implementar esto utilizando una estructura con una matriz de longitud cero que simplifica un poco el código en comparación con una solución portátil:

typedef struct
{
    size_t length ;
    char alloc[0] ;   // Compiler specific extension!!!
} tSizedAlloc ;

// Allocating a sized block
tSizedAlloc* blk = malloc( sizeof(tSizedAlloc) + length ) ;
blk->length = length ;

// Accessing the size and data information of the block
size_t blk_length = blk->length ;
char*  data = blk->alloc ;

Sé que este hilo es un poco viejo, pero todavía tengo algo que decir. Hay una función (o una macro, aún no he revisado la biblioteca) malloc_usable_size(): obtiene el tamaño del bloque de memoria asignado del montón. La página de manual indica que es solo para depuración, ya que no muestra el número que solicitó, sino el número que ha asignado, que es un poco más grande. Note que es una extensión GNU.

Por otro lado, es posible que ni siquiera sea necesario, porque creo que para liberar fragmentos de memoria no es necesario conocer su tamaño. Simplemente elimine el identificador/descriptor/estructura que está a cargo del fragmento.

avatar de usuario
diente filoso

Una forma no estándar es usar _msize(). El uso de esta función hará que su código no sea portátil. Además, la documentación no es muy clara sobre si devolverá el número pasado malloc() o el tamaño real del bloque (puede ser mayor).

depende del malloc implementador cómo almacenar estos datos. La mayoría de las veces, la longitud se almacena directamente frente a la memoria asignada (es decir, si desea asignar 7 bytes, en realidad se asignan 7+x bytes donde los x bytes adicionales se usan para almacenar los metadatos). A veces, los metadatos se almacenan tanto antes como después de la memoria asignada para comprobar si hay daños en el montón. Pero el implementador también puede optar por utilizar una estructura de datos adicional para almacenar los metadatos.

  • Creo que la longitud debe almacenarse en la parte delantera. Necesita saber el tamaño del búfer para saber dónde encontrar los metadatos finales. Si el tamaño está en los metadatos finales, tiene un problema de huevo o gallina para obtener esos datos.

    – R. Samuel Klatchko

    16 de abril de 2010 a las 6:05

  • No ‘debe’. Es concebible que un asignador pueda tener una tabla hash almacenada por separado que asigna punteros a tamaños de forma gratuita. Alternativa: tenga una gran cantidad de arenas con varios tamaños de cubo, el tamaño de la asignación se puede determinar en función de si el puntero se encuentra dentro de la región de esa arena

    – Demur Rumed

    10/10/2016 a las 20:47


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad