¿Lanzo el resultado de malloc?
En esta pregunta , alguien sugirió en un comentario que yo no emitir el resultado malloc
, es decir,
int *sieve = malloc(sizeof(int) * length);
más bien que:
int *sieve = (int *) malloc(sizeof(int) * length);
Por qué sería este el caso?
Respuestas
No ; usted no emitir el resultado, ya que:
- No es necesario, ya que
void *
en este caso se promueve de forma automática y segura a cualquier otro tipo de puntero. - Agrega desorden al código, las transmisiones no son muy fáciles de leer (especialmente si el tipo de puntero es largo).
- Te hace repetirte, lo que generalmente es malo.
- Puede ocultar un error si olvidó incluirlo
<stdlib.h>
. Esto puede causar fallas (o, peor aún, no causar una falla hasta mucho más tarde en alguna parte totalmente diferente del código). Considere lo que sucede si los punteros y los números enteros tienen un tamaño diferente; entonces estás ocultando una advertencia al transmitir y podrías perder partes de tu dirección devuelta. Nota: a partir de C99, las funciones implícitas se han ido de C, y este punto ya no es relevante ya que no hay una suposición automática de que las funciones no declaradas regresenint
.
Como aclaración, tenga en cuenta que dije "no emitir", no "no es necesario emitir". En mi opinión, es un fracaso incluir al elenco, incluso si lo hiciste bien. Simplemente no hay beneficios al hacerlo, pero hay un montón de riesgos potenciales, e incluir el yeso indica que no conoce los riesgos.
También tenga en cuenta, como señalan los comentaristas, que lo anterior habla de C directo, no de C ++. Creo firmemente en C y C ++ como lenguajes separados.
Para agregar más, su código repite innecesariamente la información de tipo ( int
) que puede causar errores. Es mejor quitar la referencia al puntero que se usa para almacenar el valor de retorno, para "bloquear" los dos juntos:
int *sieve = malloc(length * sizeof *sieve);
Esto también mueve el length
al frente para una mayor visibilidad y elimina los paréntesis redundantes con sizeof
; que sólo son necesarios cuando el argumento es un nombre de tipo. Muchas personas parecen no saber (o ignorar) esto, lo que hace que su código sea más detallado. Recuerda: ¡ sizeof
no es una función! :)
Si bien moverse length
al frente puede aumentar la visibilidad en algunos casos raros, también se debe prestar atención a que, en el caso general, sería mejor escribir la expresión como:
int *sieve = malloc(sizeof *sieve * length);
Ya que mantener el sizeof
primero, en este caso, asegura que la multiplicación se haga con al menos size_t
matemáticas.
Compare: malloc(sizeof *sieve * length * width)
vs. malloc(length * width * sizeof *sieve)
el segundo puede desbordar el length * width
cuando width
y length
son tipos más pequeños que size_t
.
En C, no es necesario convertir el valor de retorno de malloc
. El puntero a void devuelto por malloc
se convierte automágicamente al tipo correcto. Sin embargo, si desea que su código se compile con un compilador de C ++, se necesita una conversión. Una alternativa preferida entre la comunidad es utilizar lo siguiente:
int *sieve = malloc(sizeof *sieve * length);
lo que, además, le libera de tener que preocuparse por cambiar el lado derecho de la expresión si alguna vez cambia el tipo de sieve
.
Los yesos son malos, como ha señalado la gente. Especialmente lanzamientos de punteros.
Usted no emitidos, debido a que:
- Hace que su código sea más portátil entre C y C ++ y, como muestra la experiencia de SO, muchos programadores afirman que están escribiendo en C cuando en realidad lo están haciendo en C ++ (o C más extensiones de compilador local).
- Si no lo hace, puede ocultar un error : observe todos los ejemplos de SO de confusión sobre cuándo escribir
type *
versustype **
. - La idea de que evita que se dé cuenta de que falló en
#include
un archivo de encabezado apropiado pierde el bosque para los árboles . Es lo mismo que decir "no te preocupes por el hecho de que no pudiste pedirle al compilador que se quejara por no ver prototipos - ¡ese molesto stdlib.h es lo REALMENTE importante que debes recordar!" - Obliga a una verificación cruzada cognitiva adicional . Pone el tipo (supuesto) deseado justo al lado de la aritmética que está haciendo para el tamaño sin formato de esa variable. Apuesto a que podría hacer un estudio SO que muestre que los
malloc()
insectos se detectan mucho más rápido cuando hay un yeso. Al igual que con las afirmaciones, las anotaciones que revelan la intención disminuyen los errores. - Repetirse de una manera que la máquina pueda verificar es a menudo una gran idea. De hecho, eso es lo que es una aserción, y este uso de cast es una aserción. Las afirmaciones siguen siendo la técnica más general que tenemos para corregir el código, ya que a Turing se le ocurrió la idea hace tantos años.
Como dijeron otros, no es necesario para C, pero sí para C ++. Si cree que va a compilar su código C con un compilador C ++, por cualquier motivo, puede usar una macro en su lugar, como:
#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif
De esa manera, aún puede escribirlo de una manera muy compacta:
int *sieve = NEW(int, 1);
y se compilará para C y C ++.
De la Wikipedia :
Ventajas del casting
La inclusión de la transmisión puede permitir que un programa o función en C se compile como C ++.
El elenco permite versiones de malloc anteriores a 1989 que originalmente devolvieron un char *.
La conversión puede ayudar al desarrollador a identificar inconsistencias en el tamaño del tipo si cambia el tipo de puntero de destino, particularmente si el puntero se declara lejos de la llamada malloc () (aunque los compiladores modernos y los analizadores estáticos pueden advertir sobre tal comportamiento sin requerir la conversión).
Desventajas del casting
Según el estándar ANSI C, el reparto es redundante.
Agregar el elenco puede enmascarar la falla al incluir el encabezado stdlib.h , en el que se encuentra el prototipo de malloc. En ausencia de un prototipo para malloc, el estándar requiere que el compilador de C asuma que malloc devuelve un int. Si no hay conversión, se emite una advertencia cuando este entero se asigna al puntero; sin embargo, con el elenco, esta advertencia no se produce, ocultando un error. En ciertas arquitecturas y modelos de datos (como LP64 en sistemas de 64 bits, donde long y pointers son de 64 bits e int es de 32 bits), este error puede dar como resultado un comportamiento indefinido, ya que el malloc declarado implícitamente devuelve un valor de 32 bits. valor de bit mientras que la función realmente definida devuelve un valor de 64 bits. Dependiendo de las convenciones de llamada y el diseño de la memoria, esto puede resultar en la destrucción de la pila. Es menos probable que este problema pase desapercibido en los compiladores modernos, ya que producen de manera uniforme advertencias de que se ha utilizado una función no declarada, por lo que seguirá apareciendo una advertencia. Por ejemplo, el comportamiento predeterminado de GCC es mostrar una advertencia que dice "declaración implícita incompatible de función incorporada" independientemente de si el elenco está presente o no.
Si el tipo de puntero se cambia en su declaración, es posible que también sea necesario cambiar todas las líneas donde se llama y se lanza malloc.
Aunque malloc sin conversión es el método preferido y los programadores más experimentados lo eligen , debe usar el que quiera tener al tanto de los problemas.
es decir: si necesita compilar un programa en C como C ++ (aunque es un lenguaje separado) debe emitir el resultado del uso malloc
.
En C, puede convertir implícitamente un void
puntero en cualquier otro tipo de puntero, por lo que no es necesario convertirlo. El uso de uno puede sugerir al observador casual que hay alguna razón por la que se necesita, lo cual puede inducir a error.
No echas el resultado de malloc
, porque hacerlo agrega un desorden inútil a tu código.
La razón más común por la que las personas emiten el resultado de malloc
es porque no están seguras de cómo funciona el lenguaje C. Esa es una señal de advertencia: si no sabe cómo funciona un mecanismo de lenguaje en particular, no adivine. Búscalo o pregunta en Stack Overflow.
Algunos comentarios:
Un puntero vacío se puede convertir a / desde cualquier otro tipo de puntero sin una conversión explícita (C11 6.3.2.3 y 6.5.16.1).
Sin embargo, C ++ no permitirá una conversión implícita entre
void*
y otro tipo de puntero. Entonces, en C ++, el elenco habría sido correcto. Pero si programa en C ++, debe usarnew
y nomalloc()
. Y nunca debe compilar código C usando un compilador C ++.Si necesita admitir tanto C como C ++ con el mismo código fuente, use modificadores del compilador para marcar las diferencias. No intente colocar ambos estándares de lenguaje con el mismo código, porque no son compatibles.
Si un compilador de C no puede encontrar una función porque olvidó incluir el encabezado, obtendrá un error de compilador / enlazador sobre eso. Entonces, si olvidó incluir
<stdlib.h>
eso no es gran cosa, no podrá construir su programa.En compiladores antiguos que siguen una versión del estándar que tiene más de 25 años, olvidarse de incluirlos
<stdlib.h>
resultaría en un comportamiento peligroso. Porque en ese antiguo estándar, las funciones sin un prototipo visible convertían implícitamente el tipo de retorno aint
. Lanzar el resultado demalloc
explícitamente ocultaría este error.Pero eso realmente no es un problema. No está usando una computadora de 25 años, entonces, ¿por qué usaría un compilador de 25 años?
En C, obtiene una conversión implícita de void *
a cualquier otro puntero (de datos).
malloc()
No es necesario transmitir el valor devuelto por ahora, pero me gustaría agregar un punto que parece que nadie ha señalado:
En la antigüedad, es decir, antes de que ANSI C proporcionara el void *
tipo genérico de punteros, char *
era el tipo para tal uso. En ese caso, la transmisión puede cerrar las advertencias del compilador.
Referencia: C FAQ
Solo sumando mi experiencia, estudiando ingeniería informática veo que los dos o tres profesores que he visto escribir en C siempre echaban malloc, sin embargo el que le pregunté (con un CV inmenso y comprensión de C) me dijo que es absolutamente innecesario pero solo solía ser absolutamente específico, y para que los estudiantes tuvieran la mentalidad de ser absolutamente específicos. Básicamente, la transmisión no cambiará nada en su funcionamiento, hace exactamente lo que dice, asigna memoria y la transmisión no la afecta, obtiene la misma memoria e incluso si la envía a otra cosa por error (y de alguna manera evade el compilador errores) C accederá a él de la misma manera.
Editar: El casting tiene cierto punto. Cuando usas la notación de matriz, el código generado tiene que saber cuántos lugares de memoria tiene que avanzar para llegar al comienzo del siguiente elemento, esto se logra mediante casting. De esta manera, sabrá que para un doble avanza 8 bytes mientras que para un int va 4, y así sucesivamente. Por lo tanto, no tiene ningún efecto si usa la notación de puntero, en la notación de matriz se vuelve necesario.
No es obligatorio emitir los resultados de malloc
, ya que devuelve void*
, y void*
se puede apuntar a cualquier tipo de datos.
Esto es lo que dice el manual de referencia de la biblioteca GNU C :
Puede almacenar el resultado de
malloc
en cualquier variable de puntero sin una conversión, porque ISO C convierte automáticamente el tipovoid *
a otro tipo de puntero cuando sea necesario. Pero la conversión es necesaria en contextos distintos a los operadores de asignación o si es posible que desee que su código se ejecute en C.
Y de hecho, el estándar ISO C11 (p347) lo dice:
El puntero devuelto si la asignación tiene éxito está alineado de manera adecuada para que pueda asignarse a un puntero a cualquier tipo de objeto con un requisito de alineación fundamental y luego usarse para acceder a dicho objeto o una matriz de tales objetos en el espacio asignado (hasta que el espacio está explícitamente desasignado)
Un puntero vacío es un puntero de objeto genérico y C admite la conversión implícita de un tipo de puntero vacío a otros tipos, por lo que no es necesario encasillarlo explícitamente.
Sin embargo, si desea que el mismo código funcione perfectamente compatible en una plataforma C ++, que no admite la conversión implícita, debe hacer el encasillado, por lo que todo depende de la usabilidad.
El tipo devuelto es void *, que se puede convertir al tipo deseado de puntero de datos para que no se pueda hacer referencia.
Depende del lenguaje de programación y del compilador. Si usa malloc
en C, no hay necesidad de escribir cast, ya que automáticamente escribirá cast. Sin embargo, si está utilizando C ++, debe escribir cast porque malloc
devolverá un void*
tipo.
En el lenguaje C, se puede asignar un puntero vacío a cualquier puntero, por lo que no debe usar un tipo de conversión. Si desea una asignación de "tipo seguro", puedo recomendar las siguientes funciones de macro, que siempre uso en mis proyectos C:
#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)
Con estos en su lugar, simplemente puede decir
NEW_ARRAY(sieve, length);
Para matrices no dinámicas, la tercera macro de función imprescindible es
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
lo que hace que los bucles de matriz sean más seguros y convenientes:
int i, a[100];
for (i = 0; i < LEN(a); i++) {
...
}
La gente acostumbrada a GCC y Clang es malcriada. No es tan bueno ahí fuera.
Me han horrorizado bastante a lo largo de los años los compiladores asombrosamente antiguos que he tenido que utilizar. A menudo, las empresas y los administradores adoptan un enfoque ultraconservador para cambiar los compiladores y ni siquiera probarán si un nuevo compilador (con un mejor cumplimiento de los estándares y optimización del código) funcionará en su sistema. La realidad práctica para los desarrolladores que trabajan es que cuando estás codificando necesitas cubrir tus bases y, desafortunadamente, lanzar mallocs es un buen hábito si no puedes controlar qué compilador se puede aplicar a tu código.
También sugeriría que muchas organizaciones apliquen un estándar de codificación propio y que ese debería ser el método que la gente siga si está definido. En ausencia de una guía explícita, tiendo a optar por la compilación más probable en todas partes, en lugar de la adherencia servil a un estándar.
El argumento de que no es necesario según los estándares actuales es bastante válido. Pero ese argumento omite los aspectos prácticos del mundo real. No codificamos en un mundo gobernado exclusivamente por el estándar del día, sino por los aspectos prácticos de lo que me gusta llamar "campo de realidad de la gestión local". Y eso está más doblado y retorcido que el espacio-tiempo. :-)
YMMV.
Tiendo a pensar en lanzar malloc como una operación defensiva. No es bonito, no es perfecto, pero en general es seguro. (En serio, si no ha incluido stdlib.h entonces tienes manera más problemas que la fundición malloc!).
No, no lanzas el resultado de malloc()
.
En general, no lanzas hacia o desdevoid *
.
Una razón típica que se da para no hacerlo es que no hacerlo #include <stdlib.h>
podría pasar desapercibido. Esto ya no es un problema desde hace mucho tiempo ya que C99 hizo que las declaraciones de funciones implícitas fueran ilegales, por lo que si su compilador se ajusta al menos a C99, recibirá un mensaje de diagnóstico.
Pero hay una razón mucho más sólida para no introducir conversiones de punteros innecesarios:
En C, la conversión de un puntero casi siempre es un error . Esto se debe a la siguiente regla ( §6.5 p7 en N1570, el último borrador para C11):
Se podrá acceder a un objeto a su valor almacenado únicamente mediante una expresión lvalue que tenga uno de los siguientes tipos:
- un tipo compatible con el tipo efectivo del objeto,
- una versión calificada de un tipo compatible con el tipo efectivo del objeto,
- un tipo que es el tipo firmado o no firmado correspondiente al tipo efectivo del objeto,
- un tipo que es el tipo firmado o no firmado correspondiente a una versión calificada del tipo efectivo del objeto,
- un tipo agregado o de unión que incluye uno de los tipos antes mencionados entre sus miembros (incluido, de forma recursiva, un miembro de una unión subagrupada o contenida), o
- un tipo de carácter.
Esto también se conoce como la regla de alias estricta . Entonces, el siguiente código es un comportamiento indefinido :
long x = 5;
double *p = (double *)&x;
double y = *p;
Y, a veces sorprendentemente, lo siguiente también es:
struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;
A veces, usted no necesita punteros elenco, pero teniendo en cuenta la regla de alias estricto , hay que tener mucho cuidado con él. Por lo tanto, cualquier aparición de un puntero en su código es un lugar en el que debe verificar su validez . Por lo tanto, nunca escribe una conversión de puntero innecesaria.
tl; dr
En pocas palabras: debido a que en C, cualquier ocurrencia de una conversión de puntero debería generar una bandera roja para el código que requiere atención especial, nunca debe escribir conversiones de puntero innecesarias .
Notas al margen:
Hay casos en los que realmente necesita una conversión
void *
, por ejemplo, si desea imprimir un puntero:int x = 5; printf("%p\n", (void *)&x);
La
printf()
conversión es necesaria aquí, porque es una función variada, por lo que las conversiones implícitas no funcionan.En C ++, la situación es diferente. La conversión de tipos de puntero es algo común (y correcto) cuando se trata de objetos de clases derivadas. Por lo tanto, tiene sentido que en C ++, la conversión ay desde no
void *
esté implícita. C ++ tiene un conjunto completo de diferentes sabores de casting.
Puse el elenco simplemente para mostrar la desaprobación del feo agujero en el sistema de tipos, que permite que el código como el siguiente fragmento se compile sin diagnósticos, aunque no se utilizan conversiones para provocar la mala conversión:
double d;
void *p = &d;
int *q = p;
Desearía que eso no existiera (y no existe en C ++) y entonces lancé. Representa mi gusto y mi política de programación. No solo estoy lanzando un puntero, sino efectivamente, emitiendo un voto y expulsando los demonios de la estupidez . Si realmente no puedo eliminar la estupidez , al menos déjeme expresar el deseo de hacerlo con un gesto de protesta.
De hecho, una buena práctica es envolver malloc
(y amigos) con funciones que regresan unsigned char *
, y básicamente nunca usarlas void *
en su código. Si necesita un puntero-a-cualquier-objeto-genérico, use una char *
o unsigned char *
, y tenga conversiones en ambas direcciones. La única relajación que se puede disfrutar, quizás, es usar funciones similares memset
y memcpy
sin yesos.
Sobre el tema de la conversión y la compatibilidad con C ++, si escribe su código para que se compile como C y C ++ (en cuyo caso, debe emitir el valor de retorno de malloc
cuando lo asigna a algo diferente a void *
), puede hacer una muy útil algo para usted mismo: puede usar macros para la conversión que se traducen a conversiones de estilo C ++ cuando se compila como C ++, pero se reducen a una conversión de C cuando se compila como C:
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
Si se adhiere a estas macros, una simple grep
búsqueda de su base de código para estos identificadores le mostrará dónde están todas sus transmisiones, para que pueda revisar si alguna de ellas es incorrecta.
Luego, en el futuro, si compila regularmente el código con C ++, hará cumplir el uso de una conversión adecuada. Por ejemplo, si usa strip_qual
solo para eliminar una const
o volatile
, pero el programa cambia de tal manera que ahora está involucrada una conversión de tipo, obtendrá un diagnóstico y tendrá que usar una combinación de conversiones para obtener la conversión deseada.
Para ayudarlo a cumplir con estas macros, el compilador GNU C ++ (¡no C!) Tiene una característica hermosa: un diagnóstico opcional que se produce para todas las ocurrencias de conversiones de estilo C.
-Wold-style-cast (solo C ++ y Objective-C ++) Advertir si se usa un molde de estilo antiguo (estilo C) a un tipo no vacío dentro de un programa C ++. Los elencos de nuevo estilo (dynamic_cast, static_cast, reinterpret_cast y const_cast) son menos vulnerables a efectos no deseados y mucho más fácil de buscar.
Si su código C se compila como C ++, puede usar esta -Wold-style-cast
opción para averiguar todas las apariciones de la (type)
sintaxis de conversión que pueden infiltrarse en el código y realizar un seguimiento de estos diagnósticos reemplazándolo con una opción adecuada de entre las macros anteriores (o una combinación, si es necesario).
Este tratamiento de las conversiones es la mayor justificación técnica independiente para trabajar en un "C limpio": el dialecto combinado de C y C ++, que a su vez justifica técnicamente la conversión del valor de retorno de malloc
.
Prefiero hacer el yeso, pero no manualmente. Mi favorito es el uso de macros g_new
y g_new0
de glib. Si no se usa glib, agregaría macros similares. Esas macros reducen la duplicación de código sin comprometer la seguridad de los tipos. Si obtiene el tipo incorrecto, obtendría una conversión implícita entre punteros no vacíos, lo que causaría una advertencia (error en C ++). Si olvida incluir el encabezado que define g_new
y g_new0
, obtendrá un error. g_new
y g_new0
ambos toman los mismos argumentos, al contrario malloc
que eso toma menos argumentos que calloc
. Solo agregue 0
para obtener memoria inicializada en cero. El código se puede compilar con un compilador de C ++ sin cambios.
Lo mejor que se puede hacer al programar en C siempre que sea posible:
- Haga que su programa se compile a través de un compilador de C con todas las advertencias activadas
-Wall
y corrija todos los errores y advertencias - Asegúrese de que no haya variables declaradas como
auto
- Luego compílelo usando un compilador C ++ con
-Wall
y-std=c++11
. Corrija todos los errores y advertencias. - Ahora compile usando el compilador de C nuevamente. Su programa ahora debería compilarse sin ninguna advertencia y contener menos errores.
Este procedimiento le permite aprovechar la comprobación de tipos estricta de C ++, reduciendo así el número de errores. En particular, este procedimiento le obliga a incluir stdlib.h
o obtendrá
malloc
no fue declarado dentro de este alcance
y también te obliga a lanzar el resultado de malloc
o obtendrás
conversión no válida de
void*
aT*
o cualquiera que sea su tipo de objetivo.
Los únicos beneficios de escribir en C en lugar de C ++ que puedo encontrar son
- C tiene un ABI bien especificado
- C ++ puede generar más código [excepciones, RTTI, plantillas, polimorfismo en tiempo de ejecución ]
Observe que, en el caso ideal, las segundas desventajas deberían desaparecer cuando se usa el subconjunto común a C junto con la característica polimórfica estática .
Para aquellos que encuentran inconvenientes las reglas estrictas de C ++, podemos usar la función C ++ 11 con tipo inferido
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
La conversión es solo para C ++, no para C. En caso de que esté utilizando un compilador de C ++, es mejor que lo cambie a un compilador de C.
La conversión de malloc es innecesaria en C pero obligatoria en C ++.
El casting es innecesario en C debido a:
void *
se promueve de forma automática y segura a cualquier otro tipo de puntero en el caso de C.- Puede ocultar un error si olvidó incluirlo
<stdlib.h>
. Esto puede provocar bloqueos. - Si los punteros y los números enteros tienen un tamaño diferente, entonces estás ocultando una advertencia al lanzar y podrías perder partes de tu dirección devuelta.
- Si el tipo de puntero se cambia en su declaración, es posible que también sea necesario cambiar todas las líneas donde
malloc
se llama y se lanza.
Por otro lado, la transmisión puede aumentar la portabilidad de su programa. es decir, permite que un programa o función en C se compile como C ++.
El concepto detrás del puntero void es que se puede convertir en cualquier tipo de datos, por eso malloc devuelve void. También debes tener en cuenta el encasillamiento automático. Por lo tanto, no es obligatorio lanzar el puntero, aunque debe hacerlo. Ayuda a mantener el código limpio y ayuda a depurar
Un puntero vacío es un puntero genérico y C admite la conversión implícita de un tipo de puntero vacío a otros tipos, por lo que no es necesario encasillarlo explícitamente.
Sin embargo, si desea que el mismo código funcione perfectamente compatible en una plataforma C ++, que no admite la conversión implícita, debe hacer el encasillado, por lo que todo depende de la usabilidad.
Como se ha dicho, no es necesario para C, sino para C ++.
La inclusión de la transmisión puede permitir que un programa o función en C se compile como C ++.
En C no es necesario, ya que void * se promueve de forma automática y segura a cualquier otro tipo de puntero.
Pero si lanza entonces, puede ocultar un error si olvidó incluir stdlib.h . Esto puede causar fallas (o, peor aún, no causar una falla hasta mucho más tarde en alguna parte totalmente diferente del código).
Debido a que stdlib.h contiene el prototipo de malloc, se encuentra. En ausencia de un prototipo para malloc, el estándar requiere que el compilador de C asuma que malloc devuelve un int. Si no hay conversión, se emite una advertencia cuando este entero se asigna al puntero; sin embargo, con el elenco, esta advertencia no se produce, ocultando un error.
Esta pregunta está sujeta a abusos basados en opiniones.
A veces noto comentarios como ese:
No eches el resultado de malloc
o
Por qué no lanzas el resultado de malloc
en preguntas donde OP usa casting. Los comentarios en sí contienen un hipervínculo a esta pregunta.
Eso es de cualquier manera inapropiada e incorrecta también. No hay bien ni mal cuando realmente se trata del propio estilo de codificación.
¿Por qué está pasando esto?
Se basa en dos razones:
De hecho, esta pregunta se basa en opiniones. Técnicamente, la cuestión debería haberse cerrado como basada en opiniones hace años. Una pregunta del tipo " Sí " o " No " o equivalente " ¿Debería? " O " No debería ", simplemente no puede responder con enfoque sin una actitud de la propia opinión. Una de las razones para cerrar una pregunta es porque "podría dar lugar a respuestas basadas en opiniones", como bien se muestra aquí.
Muchas de las respuestas (incluyendo la más evidente y aceptada respuesta de @unwind ) son completamente o casi en su totalidad a base de opinión (Fe un misterioso "desorden" que se añade a su código si colada o repetir siempre lo mismo sería malo) y el espectáculo una tendencia clara y enfocada a omitir el elenco. Discuten sobre la redundancia del elenco en un lado, pero también, y lo que es peor, argumentan para resolver un error causado por un error / falla de la programación en sí, no
#include <stdlib.h>
si se quiere usarmalloc()
.
Quiero aportar una visión real de algunos puntos discutidos, con menos de mi opinión personal. Es necesario señalar algunos puntos especialmente:
Una pregunta tan susceptible de caer en la propia opinión necesita una respuesta con pros y contras neutrales. No solo pros y contras.
En esta respuesta se enumera una buena descripción general de los pros y los contras:
https://stackoverflow.com/a/33047365/12139179
(Personalmente considero esto por esa razón la mejor respuesta, hasta ahora).
Una razón que se encuentra a lo sumo para razonar la omisión del elenco es que el elenco podría ocultar un error.
Si alguien usa un implícito declarado
malloc()
que devuelveint
(las funciones implícitas se han ido del estándar desde C99) ysizeof(int) != sizeof(int*)
, como se muestra en esta pregunta¿Por qué este código es predeterminado en la arquitectura de 64 bits pero funciona bien en 32 bits?
el elenco escondería un error.
Si bien esto es cierto, solo muestra la mitad de la historia, ya que la omisión del elenco solo sería una solución para un error aún mayor, sin incluir el
stdlib.h
usomalloc()
.Esto nunca será un problema serio, si usted,
Utilice un compilador compatible con C99 o superior (que se recomienda y debería ser obligatorio), y
No están tan ausentes como para olvidarse de incluir
stdlib.h
, cuando desea usarlomalloc()
en su código, que es un gran error en sí mismo.
Algunas personas discuten sobre el cumplimiento de C ++ del código C, ya que el reparto está obligado en C ++.
En primer lugar, para decir en general: compilar código C con un compilador C ++ no es una buena práctica.
C y C ++ son de hecho dos lenguajes completamente diferentes con semántica diferente.
Pero si realmente desea / necesita hacer que el código C sea compatible con C ++ y viceversa, use modificadores del compilador en lugar de cualquier conversión.
Dado que el elenco es con tendencia declarado como redundante o incluso dañino, quiero centrarme en estas preguntas, que dan buenas razones por las que el elenco puede ser útil o incluso necesario:
https://stackoverflow.com/a/34094068/12139179
https://stackoverflow.com/a/36297486/12139179
https://stackoverflow.com/a/33044300/12139179
- El reparto puede no ser beneficioso cuando cambia su código, respectivamente el tipo de puntero asignado (y con eso el tipo de reparto), aunque en la mayoría de los casos esto es poco probable. Entonces necesitaría mantener / cambiar todas las conversiones también y si tiene miles de llamadas a funciones de administración de memoria en su código, esto realmente puede resumir y disminuir la eficiencia del mantenimiento.
Resumen:
El hecho es que el reparto es redundante según el estándar C (ya desde ANSI-C (C89 / C90)) si el puntero asignado apunta a un objeto de requisito de alineación fundamental (que incluye la mayoría de los objetos).
No es necesario que realice la conversión, ya que el puntero se alinea automáticamente en este caso:
"El orden y la contigüidad del almacenamiento asignado por llamadas sucesivas a las funciones align_alloc, calloc, malloc y realloc no están especificados. El puntero devuelto si la asignación tiene éxito está alineado de manera adecuada para que pueda asignarse a un puntero a cualquier tipo de objeto con un requisito de alineación fundamental y luego se usa para acceder a tal objeto o una matriz de tales objetos en el espacio asignado (hasta que el espacio se desasigne explícitamente) ".
Fuente: C18, §7.22.3 / 1
"Una alineación fundamental es una alineación válida menor o igual a
_Alignof (max_align_t)
. Las alineaciones fundamentales serán respaldadas por la implementación para objetos de todas las duraciones de almacenamiento. Los requisitos de alineación de los siguientes tipos serán alineaciones fundamentales:- todos los tipos básicos atómicos, calificados o no calificados;
- todos los tipos enumerados atómicos, calificados o no calificados;
- todos los tipos de punteros atómicos, calificados o no calificados;
- todos los tipos de matrices cuyo tipo de elemento tiene un requisito de alineación fundamental; 57)
- todos los tipos especificados en la cláusula 7 como tipos de objetos completos;
- todos los tipos de estructura o unión cuyos elementos tienen tipos con requisitos de alineación fundamentales y ninguno de cuyos elementos tiene un especificador de alineación que especifique una alineación que no sea una alineación fundamental.
- Como se especifica en 6.2.1, la declaración posterior puede ocultar la declaración anterior. "
Fuente: C18, §6.2.8 / 2
Sin embargo, si asigna memoria para un objeto definido por la implementación con un requisito de alineación extendido, la conversión sería necesaria.
Una alineación extendida está representada por una alineación mayor que
_Alignof (max_align_t)
. Está definido por la implementación si se admiten alineaciones extendidas y las duraciones de almacenamiento para las que se admiten. Un tipo que tiene un requisito de alineación extendido es un tipo sobrealineado. 58)Fuente. C18, §6.2.8 / 3
Todo lo demás depende del caso de uso específico y de la propia opinión.
Tenga cuidado con la forma en que se educa.
Le recomiendo que lea cuidadosamente todas las respuestas hechas hasta ahora (así como sus comentarios que pueden apuntar a una falla) y luego construya su propia opinión si usted o si no arroja el resultado malloc()
a un caso específico.
Tenga en cuenta:
No hay una respuesta correcta o incorrecta a esa pregunta. Es una cuestión de estilo y tú mismo decides qué camino eliges (si no te obliga la educación o el trabajo, por supuesto). Por favor, tenlo en cuenta y no dejes que te engañe .
Última nota: últimamente he votado a favor de cerrar esta cuestión por considerarla basada en opiniones, lo que de hecho es necesario desde hace años. Si tiene el privilegio de cerrar / reabrir, me gustaría invitarlo a hacerlo también.
Para mí, la moraleja y la conclusión aquí es que la conversión malloc
en C NO es totalmente necesaria, pero si, sin embargo, la lanza, no afectará, malloc
ya malloc
que aún le asignará el espacio de memoria bendita solicitado. Otra conclusión es la razón o una de las razones por las que las personas hacen casting y esto es para permitirles compilar el mismo programa en C o C ++.
Puede haber otras razones, pero otras razones, casi con certeza, lo meterían en serios problemas tarde o temprano.
Puedes, pero no es necesario que lo conviertas en C. Tienes que hacerlo si ese código está compilado como C ++.