Específicamente, ¿qué tiene de peligroso lanzar el resultado de malloc?

8 minutos de lectura

avatar de usuario
Roberto S. Barnes

Ahora, antes de que las personas comiencen a marcar esto como un dup, he leído todo lo siguiente, ninguno de los cuales proporciona la respuesta que estoy buscando:

  1. C Preguntas frecuentes: ¿Qué hay de malo en convertir el valor de retorno de malloc?
  2. SO: ¿Debería emitir explícitamente el valor de retorno de malloc()?
  3. SO: punteros innecesarios en C
  4. SO: ¿Lanzo el resultado de malloc?

Tanto las preguntas frecuentes de C como muchas respuestas a las preguntas anteriores citan un error misterioso que mallocEl valor de retorno de puede ocultar; sin embargo, ninguno de ellos da un ejemplo específico de tal error en la práctica. Ahora presta atención a lo que dije errorno advertencia.

Ahora dado el siguiente código:

#include <string.h>
#include <stdio.h>
// #include <stdlib.h>

int main(int argc, char** argv) {

    char * p = /*(char*)*/malloc(10);
    strcpy(p, "hello");
    printf("%s\n", p);

    return 0;
}

La compilación del código anterior con gcc 4.2, con y sin cast da las mismas advertencias, y el programa se ejecuta correctamente y proporciona los mismos resultados en ambos casos.

anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc 
hello

Entonces, ¿alguien puede dar un ejemplo de código específico de un error de compilación o tiempo de ejecución que podría ocurrir debido a la conversión? mallocvalor de retorno de, o es sólo una leyenda urbana?

Editar Me he encontrado con dos argumentos bien escritos con respecto a este tema:

  1. A favor de Casting: Asesoría CERT: Transmitir inmediatamente el resultado de una llamada de función de asignación de memoria en un puntero al tipo asignado
  2. Contra Casting (Error 404 a partir del 2012-02-14: use el Archivo de Internet Wayback Machine copia de 2010-01-27. {2016-03-18:”La página no se puede rastrear ni mostrar debido a robots.txt.”})

  • fundición void punteros permite compilar el código como C++; algunas personas dicen que es una característica, yo diría que es un error;)

    – Cristóbal

    14 de octubre de 2009 a las 17:29

  • también, lea los comentarios al primero de sus enlaces, ya que describe lo que debe hacer en lugar de enviar: codificación segura.cert.org/confluence/display/seccode/…

    – Cristóbal

    14 de octubre de 2009 a las 17:41

  • Seguiré el consejo de CERT para incluir al elenco. Además, nunca me olvidaré de incluir stdlib.h. 🙂

    – Abhinav

    20 de mayo de 2012 a las 18:29

  • Aquí hay un ejemplo SO de un error de tiempo de ejecución de compilación debido a la conversión mallocvalor de retorno de: conversión a int* en arco de 64 bits.

    – John_West

    28 de diciembre de 2015 a las 18:02


  • esta pregunta está etiquetada C no C++ (son dos idiomas diferentes) Entonces, cualquier discusión (como en algunas de las respuestas) no es relevante para esta pregunta.

    – usuario3629249

    9 de marzo de 2017 a las 18:15

avatar de usuario
Hormiga

Uno de los buenos argumentos de alto nivel en contra de lanzar el resultado de malloc a menudo no se menciona, aunque, en mi opinión, es más importante que los problemas conocidos de nivel inferior (como truncar el puntero cuando falta la declaración).

Una buena práctica de programación es escribir código, que sea lo más independiente posible del tipo. Esto significa, en particular, que los nombres de tipo deben mencionarse en el código lo menos posible o, mejor, no mencionarse en absoluto. Esto se aplica a las conversiones (evitar conversiones innecesarias), tipos como argumentos de sizeof (evite usar nombres de tipo en sizeof) y, en general, todas las demás referencias a nombres de tipos.

Los nombres de tipos pertenecen a las declaraciones. En la medida de lo posible, los nombres de tipo deben restringirse a declaraciones y solo a declaraciones.

Desde este punto de vista, este fragmento de código es malo.

int *p;
...
p = (int*) malloc(n * sizeof(int));

y esto es mucho mejor

int *p;
...
p = malloc(n * sizeof *p);

no simplemente porque “no arroja el resultado de malloc“, sino porque es independiente del tipo (o independiente del tipo, si lo prefiere), porque se ajusta automáticamente a cualquier tipo p se declara con, sin requerir ninguna intervención por parte del usuario.

  • Fwiw, creo que esta es más o menos la misma razón que esta: stackoverflow.com/questions/953112/… pero se centró en la independencia de tipo en lugar del bricolaje. Por supuesto, el primero se deriva del segundo (o viceversa), por lo que al menos se menciona algunas veces. 🙂

    – relajarse

    16 de octubre de 2009 a las 8:09

  • @unwind lo más probable es que te refieras a SECO en vez de bricolaje

    – Kratenko

    15 de junio de 2012 a las 17:03

  • Tengo esta creencia de que el compilador sabe más sobre el lenguaje que yo, así que si me advierte sobre algo, presto atención.

    – György Andrasek

    14 de octubre de 2009 a las 10:43

  • En muchos proyectos, el código C se compila como C++, donde es necesario convertir el void*.

    – laalto

    14 de octubre de 2009 a las 11:02

  • nit: “por defectose supone que las funciones sin prototipo devuelven int.” — ¿Quiere decir que es posible cambiar el tipo de devolución de funciones no prototipo?

    – pmg

    14/10/2009 a las 11:40

  • @laalto – Lo es, pero no debería serlo. C es C, no C++, y debe compilarse con un compilador de C, no con un compilador de C++. No hay excusa: GCC (uno de los mejores compiladores de C que existen) se ejecuta en casi todas las plataformas imaginables (y también genera código altamente optimizado). ¿Qué razones podría tener para compilar C con un compilador de C++, aparte de la pereza y los estándares flexibles?

    – Chris Lutz

    14/10/2009 a las 18:53

  • Ejemplo de código que quizás desee compilar como C y C++: #ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif. Ahora, realmente no sé por qué querrías llamar a malloc en una función estática en línea, pero los encabezados que funcionan en ambos son casi desconocidos.

    –Steve Jessop

    15 de octubre de 2009 a las 16:20

avatar de usuario
peter joot

Si hace esto al compilar en modo de 64 bits, su puntero devuelto se truncará a 32 bits.

EDITAR: Perdón por ser demasiado breve. Aquí hay un fragmento de código de ejemplo para fines de discusión.

main()
{
   char * c = (char *)malloc(2) ;
   printf("%p", c) ;
}

Suponga que el puntero del montón devuelto es algo más grande que lo que se puede representar en un int, digamos 0xAB00000000.

Si malloc no está prototipado para devolver un puntero, el valor int devuelto estará inicialmente en algún registro con todos los bits significativos establecidos. Ahora el compilador dice: “Está bien, ¿cómo convierto e int en un puntero?”. Será una extensión de signo o una extensión cero de los 32 bits de bajo orden que se le ha dicho a malloc que “devuelve” al omitir el prototipo. Dado que int está firmado, creo que la conversión será una extensión de signo, que en este caso convertirá el valor a cero. Con un valor de retorno de 0xABF0000000, obtendrá un puntero distinto de cero que también causará algo de diversión cuando intente desreferenciarlo.

  • ¿Podría explicar en detalle cómo ocurriría esto?

    – Robert S. Barnes

    14/10/2009 a las 12:00

  • Creo que Peeter Joot se dio cuenta de que “de forma predeterminada, se supone que las funciones sin prototipos devuelven int” sin incluir stdlib.h, y sizeof(int) es de 32 bits mientras que sizeof(ptr) es de 64.

    – Prueba

    14 de octubre de 2009 a las 12:32

avatar de usuario
Prueba

Una regla de software reutilizable:

En el caso de escribir una función en línea en la que se usó malloc(), para que también sea reutilizable para el código C++, realice una conversión de tipos explícita (por ejemplo, (char*)); de lo contrario, el compilador se quejará.

  • ¿Podría explicar en detalle cómo ocurriría esto?

    – Robert S. Barnes

    14/10/2009 a las 12:00

  • Creo que Peeter Joot se dio cuenta de que “de forma predeterminada, se supone que las funciones sin prototipos devuelven int” sin incluir stdlib.h, y sizeof(int) es de 32 bits mientras que sizeof(ptr) es de 64.

    – Prueba

    14 de octubre de 2009 a las 12:32

avatar de usuario
Yogeesh HT

Un puntero vacío en C se puede asignar a cualquier puntero sin una conversión explícita. El compilador dará una advertencia, pero puede ser reutilizable en C++ por tipo de fundición malloc() al tipo correspondiente. Sin tipo de fundición también se puede utilizar en Cporque C no es una verificación de tipo estricta. Pero C++ es estrictamente verificación de tipos por lo que es necesario escribir cast malloc() en C++.

  • Si usas malloc en C++, ¡es mejor que tengas una muy buena razón! ;pag

    – antred

    29 de noviembre de 2016 a las 12:13

¿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