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

Ahora, antes de que la gente empiece a marcar este doble, 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 lanzar el valor de retorno de malloc?
  2. SO: ¿Debería lanzar explícitamente el valor de retorno de malloc ()?
  3. SO: Punteros innecesarios en C
  4. SO: ¿Lanzo el resultado de malloc?

Tanto la Pregunta frecuente C como muchas respuestas a las preguntas anteriores citan un misterioso error que puede ocultar el valor de retorno de la conversión de malloc ; sin embargo, ninguno de ellos da un ejemplo específico de tal error en la práctica. Ahora preste atención a que dije error , no advertencia .

Ahora dado el siguiente código:

 #include  #include  // #include  int main(int argc, char** argv) { char * p = /*(char*)*/malloc(10); strcpy(p, "hello"); printf("%s\n", p); return 0; } 

Comstackr el código anterior con gcc 4.2, con y sin el molde proporciona las mismas advertencias, y el progtwig 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 comstackción o tiempo de ejecución que podría ocurrir debido a la conversión del valor de retorno de malloc , o es solo una leyenda urbana?

Editar He encontrado dos argumentos bien escritos sobre este tema:

  1. A favor de la emisión: CERT Advisory: Inmediatamente arroje el resultado de una llamada de función de asignación de memoria a un puntero al tipo asignado.
  2. Contra Casting (error 404 a partir del 2012-02-14: utilice la copia de Internet Archive Wayback Machine de 2010-01-27. {2016-03-18: “No se puede rastrear o mostrar la página debido a robots.txt.”})

No obtendrá un error de comstackción , sino una advertencia del comstackdor . Como dicen las fonts que citan (especialmente la primera ), puede obtener un error de tiempo de ejecución impredecible al usar el elenco sin incluir stdlib.h .

Entonces el error de tu lado no es el elenco, sino que stdlib.h incluir stdlib.h . Los comstackdores pueden suponer que malloc es una función que retorna a int , por lo tanto, convierte el puntero void* devuelto por malloc a int y luego a su tipo de puntero debido a la conversión explícita. En algunas plataformas, int y punteros pueden ocupar diferentes números de bytes, por lo que las conversiones de tipo pueden provocar daños en los datos.

Afortunadamente, los comstackdores modernos dan advertencias que apuntan a su error real. Vea la salida de gcc que suministró: le advierte que la statement implícita ( int malloc(int) ) es incompatible con el malloc incorporado. Entonces gcc parece conocer malloc incluso sin stdlib.h .

Salir del elenco para evitar este error es básicamente el mismo razonamiento que escribir

 if (0 == my_var) 

en lugar de

 if (my_var == 0) 

ya que este último podría conducir a un error grave si uno confundiría = y == , mientras que el primero daría lugar a un error de comstackción. Personalmente prefiero este último estilo, ya que refleja mejor mi intención y no tiendo a cometer este error.

Lo mismo se aplica a la conversión del valor devuelto por malloc : prefiero ser explícito en la progtwigción y, en general, hago doble comprobación para incluir los archivos de encabezado para todas las funciones que uso.

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

Una buena práctica de progtwigción es escribir código, que es tan independiente del tipo como sea posible. Esto significa, en particular, que los nombres de tipos deben mencionarse en el código lo menos posible o mejor no mencionarse en absoluto. Esto se aplica a los moldes (evite moldes innecesarios), tipos como argumentos de sizeof (evite el uso de nombres de tipos 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 estar restringidos a declaraciones y solo a declaraciones.

Desde este punto de vista, este bit 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 más bien porque es independiente del tipo (o tipo-agnosítico, si lo prefiere), ya que se ajusta automáticamente al tipo con el que se declara p , sin requerir ningún intervención del usuario.

Se supone que las funciones sin prototipo devuelven int .

Entonces estás lanzando un int a un puntero. Si los punteros son más anchos que int en su plataforma, este es un comportamiento altamente arriesgado.

Además, por supuesto, algunas personas consideran que las advertencias son errores, es decir, el código debe comstackrse sin ellas.

Personalmente, creo que el hecho de que no necesites emitir void * a otro tipo de puntero es una característica en C, y consideras que el código se rompe.

Si hace esto al comstackr 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.

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

Supongamos 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 tiene prototipos para devolver un puntero, el valor int devuelto estará inicialmente en algún registro con todos los bits significativos establecidos. Ahora el comstackdor dice, “está bien, cómo convierto e int a un puntero”. Eso va a ser una extensión de signo o una extensión cero de los 32 bits de orden inferior que se le ha dicho a malloc que “devuelve” al omitir el prototipo. Como int está firmado, creo que la conversión será una extensión de signo, que en este caso convertirá el valor en 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.

Una regla de software reutilizable:

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

Un puntero void en C se puede asignar a cualquier puntero sin un molde explícito. El comstackdor dará una advertencia, pero puede ser reutilizable en C ++ mediante el tipo de conversión malloc() al tipo correspondiente. Sin la conversión de tipo también se puede usar en C , porque C no es una verificación de tipo estricta . Pero C ++ es estrictamente comprobación de tipos, por lo que es necesario escribir cast malloc() en C ++.