sirven que punteros puntero parametros para los funciones estructura declaracion datos como cadenas aritmetica apuntadores c++ c pointers

c++ - que - punteros como parametros de funciones en c



Punteros innecesarios en C (12)

Recibí un comentario sobre mi respuesta en este hilo:

¿Malloc dentro de una llamada a función parece liberarse a la vuelta?

En resumen, tenía un código como este:

int * somefunc (void) { int * temp = (int*) malloc (sizeof (int)); temp[0] = 0; return temp; }

Recibí este comentario:

¿Puedo decir, por favor, no arrojar el valor de retorno de malloc? No es obligatorio y puede ocultar errores.

Estoy de acuerdo con que el elenco no es obligatorio en C. Es obligatorio en C ++, así que generalmente los añado en caso de que tenga que portar el código en C ++ algún día.

Sin embargo, me pregunto cómo los moldes como este pueden ocultar errores. ¿Algunas ideas?

Editar:

Parece que hay argumentos muy buenos y válidos en ambos lados. Gracias por publicar, amigos.


Bueno, creo que es exactamente lo opuesto, siempre eléctelo directamente al tipo que necesite. ¡Lee aquí!


Creo que deberías poner el yeso. Considera que hay tres ubicaciones para los tipos:

T1 *p; p = (T2*) malloc(sizeof(T3));

Las dos líneas de código podrían estar ampliamente separadas. Por lo tanto, es bueno que el compilador haga cumplir que T1 == T2. Es más fácil verificar visualmente que T2 == T3.

Si te pierdes el reparto de T2, entonces tienes que esperar que T1 == T3.

Por otro lado, tiene el argumento stdlib.h que falta, pero creo que es menos probable que sea un problema.


En realidad, la única forma en que un elenco podría ocultar un error es si está convirtiendo de un tipo de datos a un tipo de datos más pequeño y pierde datos, o si está convirtiendo peras en manzanas. Toma el siguiente ejemplo:

int int_array[10]; /* initialize array */ int *p = &(int_array[3]); short *sp = (short *)p; short my_val = *sp;

en este caso, la conversión a short estaría soltando algunos datos del int. Y luego este caso:

struct { /* something */ } my_struct[100]; int my_int_array[100]; /* initialize array */ struct my_struct *p = &(my_int_array[99]);

en el que terminaría señalando el tipo incorrecto de datos, o incluso a la memoria no válida.

Pero en general, y si sabes lo que estás haciendo, está bien hacer el casting. Aún más cuando recibes memoria de malloc, que devuelve un puntero vacío que no puedes usar a menos que lo lances, y la mayoría de los compiladores te advertirán si estás transfiriendo a algo el valor l (el valor para el el lado izquierdo de la tarea) no puede tomar de todos modos.


Lanzar una función que devuelve (void *) en lugar de ser un (int *) es inofensivo: estás lanzando un tipo de puntero a otro.

Por lo general, es incorrecto crear una función que devuelva un entero en lugar de un puntero. El compilador habría marcado si no lo hubieras lanzado explícitamente.


Parece apropiado que publique una respuesta, ya que dejé el comentario: P

Básicamente, si olvida incluir stdlib.h el compilador asumirá que malloc devuelve un int . Sin lanzar, recibirás una advertencia. Con el casting no lo harás.

Así que al elegir no obtienes nada, y corres el riesgo de suprimir las advertencias legítimas.

Se ha escrito mucho sobre esto, una búsqueda rápida en Google le mostrará explicaciones más detalladas.

editar

Se ha argumentado que

TYPE * p; p = (TYPE *)malloc(n*sizeof(TYPE));

lo hace obvio cuando accidentalmente no asigna suficiente memoria porque, por ejemplo, creía que p era TYPE , y por lo tanto deberíamos lanzar malloc porque la ventaja de este método anula el menor costo de suprimir accidentalmente las advertencias del compilador.

Me gustaría señalar 2 cosas:

  1. debe escribir p = malloc(sizeof(*p)*n); para asegurarse siempre de malloc la cantidad correcta de espacio
  2. con el enfoque anterior, debe hacer cambios en 3 lugares si alguna vez cambia el tipo de p : una vez en la declaración, una vez en el malloc y una vez en el elenco.

En resumen, todavía creo personalmente que no hay necesidad de emitir el valor de retorno de malloc y ciertamente no es la mejor práctica.


Por otro lado, si alguna vez necesita portar el código a C ++, es mucho mejor usar el operador ''nuevo''.


Un posible error podría (dependiendo de esto es si lo que realmente quiere o no) ser mallocing con una escala de tamaño y asignarle un puntero de un tipo diferente. P.ej,

int *temp = (int *)malloc(sizeof(double));

Puede haber casos en los que desee hacer esto, pero sospecho que son raros.


Esta pregunta está etiquetada tanto para C como para C ++, por lo que tiene al menos dos respuestas, en mi humilde opinión:

do

Ejem ... Haz lo que quieras.

Creo que la razón dada anteriormente "Si no incluye" stdlib ", entonces no recibirá una advertencia" no es válida porque uno no debe confiar en este tipo de ataques para no olvidar incluir un encabezado.

La razón real que podría hacer que no escribas el elenco es que el compilador de C ya silenciosamente arroja un void * en el tipo de puntero que quieras, por lo que hacerlo tú mismo es excesivo e inútil.

Si desea tener seguridad tipográfica, puede cambiar a C ++ o escribir su propia función contenedora, como:

int * malloc_Int(size_t p_iSize) /* number of ints wanted */ { return malloc(sizeof(int) * p_iSize) ; }

C ++

A veces, incluso en C ++, debe obtener beneficios de las utilidades malloc / realloc / free. Entonces tendrás que lanzar. Pero tu ya lo sabías. Usar static_cast <> () será mejor, como siempre, que C-style cast.

Y en C, puede anular malloc (y realloc, etc.) a través de plantillas para lograr seguridad de tipos:

template <typename T> T * myMalloc(const size_t p_iSize) { return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ; }

Que se usaría como:

int * p = myMalloc<int>(25) ; free(p) ; MyStruct * p2 = myMalloc<MyStruct>(12) ; free(p2) ;

y el siguiente código:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization short * p = myMalloc<int>(25) ; free(p) ;

no compilará, entonces, no hay problema .

Con todo, en C ++ puro, ahora no tienes excusa si alguien encuentra más de un C malloc dentro de tu código ... :-)

Crossover C + C ++

A veces, quieres producir código que compilará tanto en C como en C ++ (por las razones que sean ... ¿No es el objetivo del bloque C ++ extern "C" {} ?). En este caso, C ++ exige el molde, pero C no entenderá la palabra clave static_cast, por lo que la solución es el molde de estilo C (que sigue siendo legal en C ++ por exactamente este tipo de razones).

Tenga en cuenta que incluso al escribir código C puro, compilarlo con un compilador C ++ le proporcionará muchas más advertencias y errores (por ejemplo, intentar utilizar una función sin declararlo primero no se compilará, a diferencia del error mencionado anteriormente).

Entonces, para estar seguros, escriba código que se compile limpiamente en C ++, estudie y corrija las advertencias, y luego use el compilador C para producir el binario final. Esto significa, una vez más, escribir el elenco, en un molde de estilo C.


#if CPLUSPLUS #define MALLOC_CAST(T) (T) #else #define MALLOC_CAST(T) #endif ... int * p; p = MALLOC_CAST(int *) malloc(sizeof(int) * n);

o, alternativamente

#if CPLUSPLUS #define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N)) #else #define MYMALLOC(T, N) malloc(sizeof(T) * N) #endif ... int * p; p = MYMALLOC(int, n);


La gente ya ha citado las razones por las que suelo repetir: el viejo argumento (que ya no se aplica a la mayoría de los compiladores) sobre no incluir stdlib.h y usar sizeof *p para asegurarse de que los tipos y tamaños siempre coinciden independientemente de las actualizaciones posteriores. Quiero señalar otro argumento en contra del casting. Es pequeño, pero creo que se aplica.

C está bastante débilmente tipado. La mayoría de las conversiones de tipo seguro ocurren automáticamente, y la mayoría de las inseguras requieren un lanzamiento. Considerar:

int from_f(float f) { return *(int *)&f; }

Ese es un código peligroso. Es un comportamiento técnicamente indefinido, aunque en la práctica va a hacer lo mismo en casi todas las plataformas en las que lo ejecuta. Y el elenco ayuda a decirte "Este código es un truco terrible".

Considerar:

int *p = (int *)malloc(sizeof(int) * 10);

Veo un yeso y me pregunto: "¿Por qué es necesario? ¿Dónde está el truco?" Me pone los pelos de punta al cuello de que algo malo está pasando, cuando en realidad el código es completamente inofensivo.

Siempre que usemos C, los moldes (especialmente los moldes de puntero) son una forma de decir "Aquí hay algo malo y fácilmente frágil". Pueden lograr lo que necesita lograr, pero le indican a usted y a los futuros mantenedores que los niños no están bien.

El uso de moldes en cada malloc disminuye la indicación de "corte" del lanzamiento del puntero. Hace menos desagradable ver cosas como *(int *)&f; .

Nota: C y C ++ son idiomas diferentes. C está débilmente tipado, C ++ es más fuertemente tipado. Los moldes son necesarios en C ++, a pesar de que no indican un hack en absoluto, debido a (en mi humilde opinión) el sistema de tipo C ++ innecesariamente fuerte. (Realmente, este caso particular es el único lugar en el que creo que el sistema de tipo C ++ es "demasiado fuerte", pero no puedo pensar en ningún lugar donde sea "demasiado débil", lo que lo hace en general demasiado fuerte para mi gusto).

Si te preocupa la compatibilidad con C ++, no lo hagas. Si está escribiendo C, use un compilador de C. Hay muchos realmente buenos disponibles para cada plataforma. Si, por alguna extraña razón, tiene que escribir el código C que compila limpiamente como C ++, realmente no está escribiendo C. Si necesita pasar de C a C ++, debe hacer muchos cambios para que su código C sea más idiomático. C ++.

Si no puedes hacer nada de eso, tu código no será bonito sin importar lo que hagas, por lo que realmente no importa cómo decidas lanzarlo en ese punto. Me gusta la idea de usar plantillas para crear un nuevo asignador que devuelva el tipo correcto, aunque eso básicamente consiste en reinventar la new palabra clave.


Un posible error que puede introducir es si está compilando en un sistema de 64 bits que usa C (no C ++).

Básicamente, si olvida incluir stdlib.h , se aplicará la regla int predeterminada. Por lo tanto, el compilador asumirá felizmente que malloc tiene el prototipo de int malloc(); En muchos sistemas de 64 bits, un int es de 32 bits y un puntero de 64 bits.

Oh, oh, el valor se trunca y solo obtienes los 32 bits más bajos del puntero. Ahora si lanzas el valor de retorno de malloc , este error queda oculto por el reparto. Pero si no lo hace, obtendrá un error (algo de la naturaleza de "no se puede convertir int a T *").

Esto no se aplica a C ++, por supuesto, por 2 razones. En primer lugar, no tiene una regla int predeterminada, en segundo lugar requiere el molde.

Sin embargo, todo lo que debes hacer es actualizar el código de C ++ :-P.


El argumento "olvidó stdlib.h" es un hombre de paja. Los compiladores modernos detectarán y advertirán sobre el problema (gcc -Wall).

Siempre deberías lanzar el resultado de malloc inmediatamente. No hacerlo debe considerarse un error, y no solo porque fallará como C ++. Si está apuntando a una arquitectura de máquina con diferentes tipos de indicadores, por ejemplo, podría terminar con un error muy complicado si no lo incluye.

Editar : El comentarista Evan Teran está en lo cierto. Mi error fue pensar que el compilador no tenía que hacer ningún trabajo en un puntero de vacío en ningún contexto. Me asusto cuando pienso en errores de puntero LEJOS, así que mi intuición es arrojar todo. Gracias Evan!