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?
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?
NULL
. (probablemente por eso se introdujo C ++nullptr
: C ++ no permite ninguna conversión de puntero implícita)Respuestas:
No ; usted no emitir el resultado, ya que:
void *
se promociona de forma automática y segura a cualquier otro tipo de puntero en este caso.<stdlib.h>
. Esto puede causar bloqueos (o, lo que es peor, no causar un bloqueo hasta más tarde en una parte totalmente diferente del código). Considere lo que sucede si los punteros y los enteros tienen tamaños diferentes; entonces está ocultando una advertencia al emitir y puede perder partes de su dirección devuelta. Nota: a partir de C99, las funciones implícitas han desaparecido de C, y este punto ya no es relevante ya que no existe un supuesto automático de que las funciones no declaradas vuelvenint
.Como aclaración, tenga en cuenta que dije "usted no lanza", no "no necesita lanzar". En mi opinión, es un fracaso incluir el elenco, incluso si lo hiciste bien. Simplemente no hay beneficios al hacerlo, pero hay un montón de riesgos potenciales, e incluir el elenco 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 desreferenciar el puntero que se utiliza para almacenar el valor de retorno, para "bloquear" los dos juntos:Esto también mueve
length
al frente para una mayor visibilidad y elimina los paréntesis redundantes consizeof
; 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. Recuerde: ¡sizeof
no es una función! :)Si bien moverse
length
hacia el frente puede aumentar la visibilidad en algunos casos raros, también se debe prestar atención a que, en el caso general, debería ser mejor escribir la expresión como:Dado que mantener el
sizeof
primero, en este caso, asegura que la multiplicación se realice con al menossize_t
matemáticas.Comparar:
malloc(sizeof *sieve * length * width)
vs.malloc(length * width * sizeof *sieve)
el segundo puede desbordar ellength * width
cuándowidth
ylength
son tipos más pequeños quesize_t
.fuente
int x = (int) 12;
solo para aclarar las cosas?(int)12
no es comparable.12
es unint
, el elenco simplemente no hace nada. La retirada demalloc()
esvoid *
, no el tipo de puntero al que se ha lanzado. (Si no esvoid *
así, la analogía(int)12
sería(void*)malloc(…)
lo que nadie está discutiendo).En C, no necesita emitir el valor de retorno de
malloc
. El puntero a vacío devuelto pormalloc
se convierte automáticamente al tipo correcto. Sin embargo, si desea que su código se compile con un compilador de C ++, se necesita un reparto. Una alternativa preferida entre la comunidad es usar lo siguiente:lo que además te libera de tener que preocuparte por cambiar el lado derecho de la expresión si alguna vez cambias el tipo de
sieve
.Los moldes son malos, como la gente ha señalado. Especialmente moldes de puntero.
fuente
malloc(length * sizeof *sieve)
parece quesizeof
es una variable, así que creo quemalloc(length * sizeof(*sieve))
es más legible.malloc(length * (sizeof *sieve))
aún más legible. EN MI HUMILDE OPINIÓN.()
un lado el problema de @Michael Anderson , tenga en cuenta que su estilo sugerido cambió el orden. Tenga en cuenta cuándo se calcula el recuento de elementoslength*width
, mantener elsizeof
primero en este caso asegura que la multiplicación se realice con al menossize_t
matemáticas. Compararmalloc(sizeof( *ptr) * length * width)
vs.malloc(length * width * sizeof (*ptr))
- el segundo puede desbordar ellength*width
cuandowidth,length
hay tipos más pequeños quesize_t
.malloc(sizeof *sieve * length)
static_cast>()
(oreinterpret_cast<>()
) no es compatible con ningún dialecto de C.Lo haces porque:
type *
versustype **
.#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 REAL realmente importante para recordar!"malloc()
errores se detectan mucho más rápido cuando hay un elenco. Al igual que con las afirmaciones, las anotaciones que revelan la intención disminuyen los errores.fuente
.c
/.cpp
para compilar como ambos no es útil muy a menudo, pero un caso es agregarthrow
compatibilidad con C ++ cuando se compila con el compilador de C ++ (peroreturn -1;
cuando se compila con el compilador de C, o lo que sea).malloc
llamada:char **foo = malloc(3*sizeof(*foo));
si es bastante completa: 3 punteros a punteros de caracteres. luego bucle y hacerfoo[i] = calloc(101, sizeof(*(foo[i])));
. Asigne una matriz de 101 caracteres, perfectamente inicializados a ceros. No se necesita yeso. cambie la declaración aunsigned char
cualquier otro tipo, para el caso, y todavía está bienstruct Zebra *p; ... p=malloc(sizeof struct Zebra);
, el malloc no puede evitar duplicar información sobre el tipo de p, pero ni el compilador ni la inspección del código local detectarían ningún problema si un tipo cambiara pero el otro no. Cambie el código ap=(struct Zebra*)malloc(sizeof struct Zebra);
y el compilador emitirá un chillido si el tipo de lanzamiento no coincidep
, y la inspección local revelará ...Como otros han dicho, no es necesario para C, pero sí para C ++. Si cree que va a compilar su código C con un compilador de C ++, por cualquier motivo, puede usar una macro, como:
De esa manera, aún puede escribirlo de una manera muy compacta:
y compilará para C y C ++.
fuente
new
en la definición de C ++?new
debe usardelete
y si usamalloc()
debe hacerlofree()
. Nunca los mezcles.NEW
es probablemente una mala idea ya que el recurso nunca se devuelve usandodelete
(oDELETE
), por lo que está mezclando su vocabulario. En cambio, nombrarloMALLOC
, o más bienCALLOC
en este caso, tendría más sentido.De la Wikipedia :
Aunque malloc sin casting es el método preferido y los programadores más experimentados lo eligen , debe usar el que desee para conocer los problemas.
es decir: si necesita compilar el programa C como C ++ (aunque es un lenguaje separado), debe emitir el resultado del uso
malloc
.fuente
malloc()
llamada "? ¿Podrías dar un ejemplo?p = malloc(sizeof(*p) * count)
idioma recoge los cambios en el tipo automáticamente, por lo que no tiene que recibir advertencias e ir a cambiar nada. Así que esta no es una ventaja real frente a la mejor alternativa para no lanzar.En C, puede convertir implícitamente un
void
puntero a cualquier otro tipo de puntero, por lo que no es necesario un reparto. El uso de uno puede sugerir al observador casual que hay alguna razón por la que se necesita, lo que puede ser engañoso.fuente
No emites el resultado de malloc, porque hacerlo agrega desorden inútil a tu código.
La razón más común por la cual las personas emiten el resultado de malloc es porque no están seguros de cómo funciona el lenguaje C. Esa es una señal de advertencia: si no sabes cómo funciona un mecanismo de lenguaje en particular, entonces no adivines. Búscalo o pregunta en Stack Overflow.
Algunos comentarios:
Un puntero nulo 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 reparto habría sido correcto. Pero si programa en C ++, debería usarnew
y no malloc (). Y nunca debe compilar código C usando un compilador de C ++.Si necesita admitir C y C ++ con el mismo código fuente, use modificadores de compilación para marcar las diferencias. No intente saciar ambos estándares de idioma con el mismo código, ya que 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 al respecto. Entonces, si olvidó incluir
<stdlib.h>
eso no es gran cosa, no podrá construir su programa.En los 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 convirtieron implícitamente el tipo de retorno aint
. Lanzar el resultado de malloc explícitamente escondería este error.Pero eso no es realmente un problema. No estás usando una computadora de 25 años, entonces ¿por qué usarías un compilador de 25 años?
fuente
En C obtienes una conversión implícita de
void *
cualquier otro puntero (de datos).fuente
Transmitir el valor devuelto por
malloc()
Ahora no es necesario , 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 proporcione el
void *
tipo genérico de punteros,char *
es el tipo para dicho uso. En ese caso, el elenco puede cerrar las advertencias del compilador.Referencia: C Preguntas frecuentes
fuente
Solo agregando mi experiencia, estudiando ingeniería informática, veo que los dos o tres profesores que he visto escribir en C siempre emiten malloc, sin embargo, el que le pregunté (con un inmenso CV y comprensión de C) me dijo que es absolutamente innecesario, pero solía ser absolutamente específico y hacer que los estudiantes tuvieran la mentalidad de ser absolutamente específicos. Esencialmente, el lanzamiento no cambiará nada en su funcionamiento, hace exactamente lo que dice, asigna memoria y el lanzamiento no lo afecta, obtienes la misma memoria e incluso si lo lanzas a otra cosa por error (y de alguna manera evades el compilador errores) C accederá de la misma manera.
Editar: El casting tiene un cierto punto. Cuando utiliza la notación de matriz, el código generado tiene que saber cuántos lugares de memoria tiene que avanzar para alcanzar el comienzo del siguiente elemento, esto se logra mediante la conversión. De esta manera, usted sabe que para un doble va 8 bytes adelante, mientras que para un int va 4, y así sucesivamente. Por lo tanto, no tiene ningún efecto si utiliza la notación de puntero, en notación de matriz se hace necesario.
fuente
p = malloc(sizeof *p * n);
es tan simple y mejor.No es obligatorio emitir los resultados de
malloc
, ya que regresavoid*
, yvoid*
puede apuntar a cualquier tipo de datos.fuente
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 hay necesidad de convertirlo 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 realizar la conversión de texto, por lo que todo depende de la usabilidad.
fuente
malloc
y amigos en C ++ es una buena señal de advertencia de que merece atención especial (o reescribir en C).void *
, por lo tanto,void *
es insuficiente para almacenar bien un puntero de función.Esto es lo que dice el manual de referencia de la biblioteca GNU C :
Y, de hecho, el estándar ISO C11 (p347) lo dice así:
fuente
El tipo devuelto es nulo *, que se puede convertir al tipo deseado de puntero de datos para que no se pueda hacer referencia.
fuente
void*
se puede convertir al tipo deseado, pero no es necesario hacerlo, ya que se convertirá automáticamente. Por lo tanto, el reparto no es necesario y, de hecho, no es deseable por las razones mencionadas en las respuestas de alta puntuación.En el lenguaje C, se puede asignar un puntero nulo 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 en C:
Con estos en su lugar, simplemente puede decir
Para matrices no dinámicas, la tercera macro de función imprescindible es
lo que hace que los bucles de matriz sean más seguros y convenientes:
fuente
malloc()
uno.void*
puntero a / desde una función puede perder información, por lo que "un puntero vacío se puede asignar a cualquier puntero" es un problema en esos casos. Sin embargovoid*
, asignar un puntero desde,malloc()
a cualquier objeto no es un problema.do
comentario del bucle se relaciona con macros que involucran un bucle pero que se está alejando de la pregunta del título. Eliminando ese comentario. Tomará este abajo más tarde también.Depende del lenguaje de programación y el compilador. Si lo usa
malloc
en C, no es necesario escribir cast, ya que escribirá automáticamente cast. Sin embargo, si está utilizando C ++, debe escribir cast porquemalloc
devolverá unvoid*
tipo.fuente
Las personas acostumbradas a GCC y Clang están en mal estado. No es tan bueno por ahí.
A lo largo de los años, he estado bastante horrorizado por los compiladores asombrosamente viejos que me han requerido usar. A menudo, las empresas y los gerentes adoptan un enfoque ultraconservador para cambiar los compiladores y ni siquiera lo harán prueban si un nuevo compilador (con mejor cumplimiento de estándares y optimización de 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 puede aplicarse a tu código.
También sugeriría que muchas organizaciones apliquen un estándar de codificación propio y que debería ser el método que la gente siga si se define. En ausencia de una guía explícita, tiendo a ir con mayor probabilidad de compilar en todas partes, en lugar de una servil adhesión 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 regido exclusivamente por el estándar del día, sino por los aspectos prácticos de lo que me gusta llamar "el campo de la realidad de la gestión local". Y eso está doblado y retorcido más de lo que nunca estuvo el tiempo espacial. :-)
YMMV.
Tiendo a pensar en lanzar Malloc como una operación defensiva. No es bonito, no es perfecto, pero generalmente es seguro. (En serio, si no ha incluido stdlib.h entonces tienes manera más problemas que la fundición malloc!).
fuente
Puse el elenco simplemente para mostrar la desaprobación del agujero feo en el sistema de tipos, que permite que el código como el siguiente fragmento se compile sin diagnóstico, a pesar de que no se utilizan moldes para provocar la conversión incorrecta:
Desearía que no existiera (y no existe en C ++) y, por lo tanto, lancé. Representa mi gusto y mi política de programación. No solo estoy lanzando un puntero, sino efectivamente, votando y expulsando demonios de estupidez . Si no puedo realmente echar fuera la estupidez , a continuación, al menos permítanme expresar el deseo de hacerlo con un gesto de protesta.
De hecho, una buena práctica es envolver
malloc
(y amigos) con funciones que regresanunsigned char *
, y básicamente nunca usarlasvoid *
en su código. Si necesita un puntero genérico a cualquier objeto, use unchar *
ounsigned char *
, y tenga conversiones en ambas direcciones. La única relajación que puede permitirse, tal vez, es usar funciones comomemset
ymemcpy
sin yesos.Sobre el tema de la compatibilidad de conversión y C ++, si escribe su código para que se compile como C y C ++ (en cuyo caso debe emitir el valor de retorno
malloc
al asignarlo a otra cosa que no seavoid *
), puede hacer algo muy útil cosa para usted: puede usar macros para la conversión que se traducen en conversiones de estilo C ++ cuando se compila como C ++, pero se reducen a una conversión C cuando se compila como C:Si se adhiere a estas macros, una simple
grep
búsqueda en su base de código para estos identificadores le mostrará dónde están todos sus moldes, para que pueda revisar si alguno de ellos es incorrecto.Luego, en el futuro, si compila regularmente el código con C ++, impondrá el uso de un reparto apropiado. Por ejemplo, si usa
strip_qual
solo para eliminar aconst
ovolatile
, pero el programa cambia de tal manera que ahora se trata de 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 adherirse a estas macros, el compilador GNU C ++ (¡no C!) Tiene una hermosa característica: un diagnóstico opcional que se produce para todas las apariciones de modelos de estilo C.
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 aparecer en el código, y hacer un seguimiento de estos diagnósticos reemplazándolo con una elección adecuada entre las macros anteriores (o un combinación, si es necesario).Este tratamiento de conversiones es la justificación técnica independiente más grande para trabajar en una "C limpia": el dialecto combinado de C y C ++, que a su vez técnicamente justifica emitir el valor de retorno de
malloc
.fuente
Lo mejor que se puede hacer al programar en C siempre que sea posible:
-Wall
y corrija todos los errores y advertenciasauto
-Wall
y-std=c++11
. Solucione todos los errores y advertencias.Este procedimiento le permite aprovechar la estricta verificación de tipos de C ++, reduciendo así la cantidad de errores. En particular, este procedimiento te obliga a incluir
stdlib.h
o obtendrásy también te obliga a lanzar el resultado
malloc
o obtendráso cual sea tu tipo de objetivo.
Los únicos beneficios de escribir en C en lugar de C ++ que puedo encontrar son
Tenga en cuenta que los segundos inconvenientes deberían desaparecer en el caso ideal 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
fuente
gcc -c c_code.c
), el código C ++ como C ++ (por ejemplog++ -c cpp_code.cpp
), y luego vincularlos (por ejemplo,gcc c_code.o cpp_code.o
o viceversa, según las dependencias del proyecto). Ahora no debería haber ninguna razón para privarse de las características agradables de cualquiera de los idiomas ...p = malloc(sizeof(*p));
, lo que no necesita cambiar en primer lugar sip
cambia a un nombre de tipo diferente. La "ventaja" propuesta de la conversión es que obtienes un error de compilación sip
es del tipo incorrecto, pero es aún mejor si simplemente funciona.No, no emites el resultado de
malloc()
.En general, no lanzas hacia o desde
void *
.Una razón típica dada para no hacerlo es que el no
#include <stdlib.h>
poder pasar desapercibido. Esto ya no es un problema desde hace mucho tiempo ya que C99 hizo ilegales las declaraciones de funciones implícitas , por lo que si su compilador cumple al menos con C99, recibirá un mensaje de diagnóstico.Pero hay un razón mucho más fuerte para no introducir lanzamientos de puntero innecesarios:
En C, una conversión de puntero casi siempre es un error . Esto se debe a la siguiente regla ( §6.5 p7 en N1570, el último borrador para C11):
Esto también se conoce como la estricta regla de alias . Entonces el siguiente código es un comportamiento indefinido :
Y, a veces sorprendentemente, lo siguiente también es:
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 emitido en su código es un lugar donde debe verificar su validez . Por lo tanto, nunca escribe un molde de puntero innecesario.
tl; dr
En pocas palabras: debido a que en C, cualquier aparición de un puntero debe levantar una bandera roja para el código que requiere atención especial, nunca debe escribir innecesariamente puntero .
Notas al margen:
Hay casos en los que realmente necesita una conversión
void *
, por ejemplo, si desea imprimir un puntero:El reparto es necesario aquí, porque
printf()
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 hacia y desde
void *
es no implícita. C ++ tiene un conjunto completo de diferentes sabores de fundición.fuente
Prefiero hacer el reparto, pero no manualmente. Mi favorito es usar
g_new
yg_new0
macros de glib. Si no se usa glib, agregaría macros similares. Esas macros reducen la duplicación de código sin comprometer la seguridad del tipo. Si obtiene el tipo incorrecto, obtendría una conversión implícita entre punteros no vacíos, lo que provocaría una advertencia (error en C ++). Si olvida incluir el encabezado que defineg_new
yg_new0
, obtendrá un error.g_new
yg_new0
ambos toman los mismos argumentos, a diferencia demalloc
eso toma menos argumentos quecalloc
. Simplemente agregue0
para obtener memoria de inicialización cero. El código se puede compilar con un compilador de C ++ sin cambios.fuente
La transmisión es solo para C ++ no C. En caso de que esté utilizando un compilador de C ++, es mejor que lo cambie a compilador de C.
fuente
El concepto detrás del puntero void es que se puede convertir a cualquier tipo de datos, por eso malloc devuelve void. También debe tener en cuenta el encasillado automático. Por lo tanto, no es obligatorio lanzar el puntero, aunque debe hacerlo. Ayuda a mantener limpio el código y ayuda a depurar
fuente
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 hay necesidad de convertirlo 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 realizar la conversión de texto, por lo que todo depende de la usabilidad.
fuente
Como se indicó, no es necesario para C, sino para C ++.
La inclusión del elenco puede permitir que un programa o función C se compile como C ++.
En C es innecesario, ya que void * se promociona de forma automática y segura a cualquier otro tipo de puntero.
Pero si lanza, puede ocultar un error si olvidó incluir stdlib.h . Esto puede causar bloqueos (o, lo que es peor, no causar un bloqueo hasta más tarde en una parte totalmente diferente del código).
Porque stdlib.h contiene el prototipo para 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 reparto, esta advertencia no se produce, ocultando un error.
fuente
La conversión de malloc es innecesaria en C pero obligatoria en C ++.
La fundición es innecesaria en C debido a:
void *
se promociona de forma automática y segura a cualquier otro tipo de puntero en el caso de C.<stdlib.h>
. Esto puede causar accidentes.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 C se compile como C ++.
fuente
Para mí, la conclusión y la conclusión aquí es que el lanzamiento
malloc
en C NO es totalmente necesario, pero si lo hace, no afectará,malloc
yamalloc
que aún le asignará el espacio de memoria bendita solicitado. Otra razón para llevar a casa 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 llevarían a serios problemas tarde o temprano.
fuente
Puede, pero no necesita convertir en C. Debe emitir si ese código se compila como C ++.
fuente