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

Resuelto Robert S. Barnes asked hace 14 años • 7 respuestas

Ahora, antes de que la gente empiece a marcar esto como una duplicación, leí todo lo siguiente, ninguno de los cuales proporciona la respuesta que estoy buscando:

  1. Preguntas frecuentes sobre C: ¿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 casting puede ocultar; sin embargo, ninguno de ellos da un ejemplo específico de tal error en la práctica. Ahora preste atención que dije error , no 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;
}

Al compilar el código anterior con gcc 4.2, con y sin cast, se obtienen 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 de tiempo de ejecución que podría ocurrir debido al mallocvalor de retorno de casting, o es solo una leyenda urbana?

Editar He encontrado dos argumentos bien escritos sobre este tema:

  1. A favor de la conversión: Aviso CERT: Convierta inmediatamente el resultado de una llamada a la función de asignación de memoria en un puntero al tipo asignado
  2. Contra la transmisión (error 404 a partir del 14 de febrero de 2012: use la copia de Internet Archive Wayback Machine del 27 de enero de 2010.{2016-03-18:"La página no se puede rastrear ni mostrar debido a robots.txt."})
Robert S. Barnes avatar Oct 14 '09 17:10 Robert S. Barnes
Aceptado

No obtendrá un error del compilador , sino una advertencia del compilador . Como dicen las fuentes que cita (especialmente la primera ), puede obtener un error de tiempo de ejecución impredecible al usar la conversión sin incluirstdlib.h .

Entonces, el error de tu lado no es el reparto, sino olvidar incluirlo stdlib.h. Los compiladores pueden asumir que malloces una función que regresa int, por lo tanto, convierten el void*puntero realmente devuelto por mallocy intluego a su tipo de puntero debido a la conversión explícita. En algunas plataformas, intlos punteros pueden ocupar diferentes números de bytes, por lo que las conversiones de tipos pueden provocar daños en los datos.

Afortunadamente, los compiladores modernos dan advertencias que señalan el error real. Vea el gccresultado que proporcionó: le advierte que la declaración implícitaint malloc(int) ( ) es incompatible con la declaración incorporada malloc. Así que gccparece saberlo mallocincluso sin él stdlib.h.

Dejar de lado el elenco para evitar este error es prácticamente el mismo razonamiento que escribir

if (0 == my_var)

en lugar de

if (my_var == 0)

ya que este último podría provocar un error grave si se confundiera =y ==, mientras que el primero provocaría un error de compilación. Personalmente prefiero este último estilo ya que refleja mejor mi intención y no tiendo a cometer este error.

Lo mismo ocurre con la conversión del valor devuelto por malloc: Prefiero ser explícito en la programación y generalmente vuelvo a verificar para incluir los archivos de encabezado de todas las funciones que uso.

Ferdinand Beyer avatar Oct 14 '2009 10:10 Ferdinand Beyer

Uno de los buenos argumentos de nivel superior contra la conversión del resultado de malloca menudo no se menciona, aunque, en mi opinión, es más importante que los problemas bien 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 los tipos deben mencionarse en el código lo menos posible o, mejor, no mencionarse en absoluto. Esto se aplica a conversiones (evite conversiones innecesarias), tipos como argumentos de sizeof(evite usar nombres de tipos en sizeof) y, en general, todas las demás referencias a nombres de tipos.

Los nombres de los tipos pertenecen a las declaraciones. En la medida de lo posible, los nombres de tipos deben restringirse a declaraciones y sólo 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 emite 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 pcon el que se declare, sin requerir ninguna intervención de el usuario.

AnT stands with Russia avatar Oct 14 '2009 17:10 AnT stands with Russia