Específicamente, ¿qué tiene de peligroso convertir el resultado de malloc?
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:
- Preguntas frecuentes sobre C: ¿Qué hay de malo en convertir el valor de retorno de malloc?
- SO: ¿Debería emitir explícitamente el valor de retorno de malloc()?
- SO: Punteros innecesarios en C
- SO: ¿Lanzo el resultado de malloc?
Tanto las preguntas frecuentes de C como muchas respuestas a las preguntas anteriores citan un error misterioso que malloc
el 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 malloc
valor de retorno de casting, o es solo una leyenda urbana?
Editar He encontrado dos argumentos bien escritos sobre este tema:
- 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
- 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."})
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 malloc
es una función que regresa int
, por lo tanto, convierten el void*
puntero realmente devuelto por malloc
y int
luego a su tipo de puntero debido a la conversión explícita. En algunas plataformas, int
los 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 gcc
resultado que proporcionó: le advierte que la declaración implícitaint malloc(int)
( ) es incompatible con la declaración incorporada malloc
. Así que gcc
parece saberlo malloc
incluso 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.
Uno de los buenos argumentos de nivel superior contra la conversión del resultado de malloc
a 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 p
con el que se declare, sin requerir ninguna intervención de el usuario.