¿Por qué especifica el tamaño cuando usa malloc en C?

9 minutos de lectura

avatar de usuario
andreas grech

Toma el siguiente código:

int *p = malloc(2 * sizeof *p);

p[0] = 10;  //Using the two spaces I
p[1] = 20;  //allocated with malloc before.

p[2] = 30;  //Using another space that I didn't allocate for. 

printf("%d", *(p+1)); //Correctly prints 20
printf("%d", *(p+2)); //Also, correctly prints 30
                      //although I didn't allocate space for it

con la línea malloc(2 * sizeof *p) Estoy asignando espacio para dos enteros, ¿verdad? Pero si agrego un int a la tercera posición, todavía me asigna correctamente y es recuperable.

Entonces mi pregunta es, ¿Por qué especificas un tamaño cuando usas malloc ?

  • Parece que has elegido p[3] porque es más grande que 2 y, por lo tanto, “fuera de rango”. Tienes razón, pero recuerda que tienes que pensar de forma indexada en 0, por lo que, de hecho, incluso p[2] en sí mismo está fuera de rango. Si ha asignado espacio para dos enteros, puede obtener los enteros usando *p (o p[0]) y *(p+1) (o p[1]), no p[1] y P[2].

    – Tyler

    6 de agosto de 2009 a las 20:07

  • Además de todas las respuestas que apuntan al problema, tiene una falla de segmento garantizada en sus manos si malloc devolvió NULL, lo cual está permitido hacer. Siempre verifique el valor de retorno de malloc antes de usarlo. Si es NULL, el administrador de memoria se niega a asignarle más memoria (generalmente porque pidió demasiado o agotó todo lo que está dispuesto a darle).

    -Bob Somers

    6 de agosto de 2009 a las 20:39

  • No te preocupes por eso. No es una pregunta estúpida, es una pregunta de “Soy nuevo en los conceptos de programación de bajo nivel”. stepancheg necesita aprender a no morder a los novatos.

    – Tyler McHenry

    6 de agosto de 2009 a las 21:56

  • Parece que nadie se tomó el tiempo de explicar por qué malloc parece estar escondiendo este espacio extra para ti. Malloc necesita asignar el espacio solicitado + un poco más para almacenar sus propios metadatos sobre la memoria asignada. Un problema común con las escrituras salvajes en la memoria de malloc es aplastar los metadatos de malloc, lo que hace que fallen los free’s y malloc’s posteriores. Además, dependiendo de la implementación, malloc puede simplemente solicitar una gran parte de la memoria del sistema operativo simplemente para que no tenga que volver a preguntar al sistema operativo cuando llame a otro malloc (esto es demasiado simplificado, pero espero que lo explique).

    – Falaína

    6 de agosto de 2009 a las 22:09

  • @Falaina, eso no está del todo bien. Malloc no está necesariamente asignando el espacio extra en cuestión. Sí, malloc funciona así, pero puede poner metadatos antes de el puntero que te da, o en algún otro lugar por completo. solo porque pag[3] parece funcionar no significa que haya encontrado los metadatos de malloc. Puede ser el almacenamiento de otra variable o una porción completamente no utilizada del espacio de la memoria virtual.

    – Tyler McHenry

    6 de agosto de 2009 a las 23:44

avatar de usuario
Sinan Ünür

Lógica simple: si no estaciona en un espacio de estacionamiento legal, es posible que no pase nada, pero ocasionalmente su automóvil puede ser remolcado y puede recibir una multa enorme. Y, a veces, mientras trata de encontrar el camino a la perrera donde remolcaron su automóvil, es posible que lo atropelle un camión.

malloc le da tantos lugares de estacionamiento legales como pidió. Puede intentar estacionar en otro lugar, puede parecer que funciona, pero a veces no funciona.

Para preguntas como esta, el Sección de asignación de memoria de las preguntas frecuentes de C es una referencia útil para consultar. Ver 7.3b.

En una nota relacionada (humorística), véase también una lista de meteduras de pata por ARTE.

  • Metáfora perfecta. Puedes estacionar ilegalmente día tras día y tal vez no pase nada malo. Pero todavía existe la posibilidad de ser remolcado o multado, por lo que no debe hacerlo.

    – Tyler

    6 de agosto de 2009 a las 20:09

  • Proporcionas una buena explicación, obtienes votos a favor. Gracias por la explicación.

    –David Thornley

    6 de agosto de 2009 a las 21:37

  • La analogía es buena, pero no explicas lo que está pasando. Parece obvio para las personas que entienden el problema… pero si él entendiera el problema, no habría hecho la pregunta. El hecho de que el programa se bloquee o que la memoria que está usando se sobrescriba no es obvio. Explique qué está pasando detrás de esta analogía, y es un +1 fácil.

    – Beska

    6 de agosto de 2009 a las 21:38

  • +1. Excelente analogía. Y el tiempo desperdiciado^H^H^H^H^H gastado persiguiendo las corrupciones de la memoria es la multa por la ofensa.

    – Andrés Y.

    6 de agosto de 2009 a las 21:57

  • @AndrewY: Si tienes suerte. “Back to School, Mr. Bean” ofrece una visión más realista. Cuando Mr. Bean saca un auto de un espacio de estacionamiento acordonado y lo usa él mismo, no recibe una multa. En cambio, la demostración programada por el ejército de las habilidades de “pasar por encima de cualquier cosa” de un tanque usando el vehículo en ese espacio procede según lo planeado, usando el vehículo de Mr. Bean en lugar del que había desplazado.

    – Super gato

    20 de abril de 2012 a las 16:43

avatar de usuario
Igal serbio

C amablemente deja que te dispares en la cabeza. Acaba de usar memoria aleatoria en el montón. Con consecuencias imprevisibles.

Descargo de responsabilidad: mi última programación en C real se realizó hace unos 15 años.

  • No se necesita descargo de responsabilidad, esta respuesta es absolutamente correcta. Por lo general, la siguiente variable que asigna es la que se sobrescribe, pero si tiene mucha mala suerte, puede golpear el espacio variable de otro programa y arruinar algo al azar.

    – Raquitismo

    6 de agosto de 2009 a las 20:22

  • Con eso quise decir, si después de declarar p también declaraste int* q = malloc(sizeof(int)); (una matriz con un elemento), es probable (pero no garantizado) que p[2] == q[0]. Esto también introduce casos en los que su programa puede continuar, y puede no causar estragos, y de repente surge un caso en el que p[2] != q[0] y un error ocurre una vez… Esos errores impredecibles que van y vienen son extremadamente difíciles de depurar.

    – Raquitismo

    6 de agosto de 2009 a las 20:24

avatar de usuario
Lasse V. Karlsen

Déjame darte una analogía de por qué esto “funciona”.

Supongamos que necesita hacer un dibujo, por lo que toma una hoja de papel, la coloca sobre la mesa y comienza a dibujar.

Desafortunadamente, el papel no es lo suficientemente grande, pero tú, sin importarte o sin darte cuenta, simplemente continúas dibujando tu dibujo.

Cuando haya terminado, retroceda un paso y mire su dibujo, y se ve bien, exactamente como quería que fuera y exactamente de la manera en que lo dibujó.

Hasta que llega alguien y recoge el papel que dejó sobre la mesa antes de que llegaras.

Ahora falta una parte del dibujo. La pieza que dibujaste en el papel de esa otra persona.

Además, esa persona ahora tiene partes de tu dibujo en su papel, probablemente jugando con lo que quisiera tener en el papel.

Entonces, aunque parezca que su uso de memoria funciona, solo lo hace porque su programa finaliza. Deje un error de este tipo en un programa que se ejecuta durante un tiempo y puedo garantizarle que obtendrá resultados extraños, bloqueos y demás.

C está construido como una motosierra con esteroides. No hay casi nada que no puedas hacer. Esto también significa que necesita saber lo que está haciendo, de lo contrario, atravesará el árbol y se clavará en su pie antes de darse cuenta.

  • Esta es una analogía soberbia, ya que también responde algo del “por qué nada atrapa para eso”; uno podría construir un marco alrededor de la hoja de papel para que la pluma no pueda salir, pero eso ciertamente sería más trabajo que simplemente tomar una hoja de papel y hacer el dibujo. En algunos entornos, el costo adicional del marco se considera que vale la pena. En otros entornos, las personas con pendientes se consideran lo suficientemente confiables como para no tener que preocuparse por los marcos.

    – Super gato

    21 de noviembre de 2011 a las 1:06

Tuviste (des)suerte. Accediendo a p[3] no está definido, ya que no ha asignado esa memoria para usted. Leer/escribir el final de una matriz es una de las formas en que los programas C pueden bloquearse de formas misteriosas.

Por ejemplo, esto podría cambiar algún valor en alguna otra variable que se asignó a través de malloc. Eso significa que podría fallar más tarde y será muy difícil encontrar el código (no relacionado) que sobrescribió sus datos.

Peor aún, es posible que sobrescriba algunos otros datos y no se dé cuenta. Imagina que esto sobrescribe accidentalmente la cantidad de dinero que le debes a alguien 😉

De hecho, malloc no está asignando suficiente espacio para su tercer entero, pero tuvo “suerte” y su programa no falló. Solo puede estar seguro de que malloc ha asignado exactamente lo que solicitó, no más. En otras palabras, su programa escribió en una parte de la memoria que no se le asignó.

Entonces, malloc necesita saber el tamaño de la memoria que necesita porque no sabe qué terminará haciendo con la memoria, cuántos objetos planea escribir en la memoria, etc.

  • Yo diría que esto es realmente desafortunado 🙂

    – bdonlan

    6 de agosto de 2009 a las 19:53

  • Verá qué desafortunado es cuando el código se ejecuta en algún sistema donde el montón se comporta de manera diferente (pero dentro del estándar, por supuesto). “Se ejecutó en mi máquina” no es la frase que los clientes quieren escuchar.

    – diente filoso

    12 de agosto de 2009 a las 11:19

avatar de usuario
matt kellogg

Todo esto se remonta a C, lo que te permite dispararte en el pie. Solo porque puedas hacer esto, no significa que debas hacerlo. Definitivamente no se garantiza que el valor en p+3 sea el que puso allí a menos que lo haya asignado específicamente usando malloc.

  • Yo diría que esto es realmente desafortunado 🙂

    – bdonlan

    6 de agosto de 2009 a las 19:53

  • Verá qué desafortunado es cuando el código se ejecuta en algún sistema donde el montón se comporta de manera diferente (pero dentro del estándar, por supuesto). “Se ejecutó en mi máquina” no es la frase que los clientes quieren escuchar.

    – diente filoso

    12 de agosto de 2009 a las 11:19

avatar de usuario
usuario47559

Prueba esto:

int main ( int argc, char *argv[] ) {
  int *p = malloc(2 * sizeof *p);
  int *q = malloc(sizeof *q);
  *q = 100;

  p[0] = 10;    p[1] = 20;    p[2] = 30;    p[3] = 40;
  p[4] = 50;    p[5] = 60;    p[6] = 70;


  printf("%d\n", *q);

  return 0;
}

En mi máquina, imprime:

50

Esto se debe a que sobrescribió la memoria asignada para p y pisoteó q.

Tenga en cuenta que malloc puede no poner p y q en la memoria contigua debido a las restricciones de alineación.

¿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