que - En C, ¿por qué algunas personas lanzan el puntero antes de liberarlo?
puntero malloc (6)
Estoy trabajando en una base de código antigua y casi todas las invocaciones de free () usan un reparto en su argumento. Por ejemplo,
free((float *)velocity);
free((float *)acceleration);
free((char *)label);
donde cada puntero es del tipo correspondiente (y coincidente). No veo ningún punto en hacer esto en absoluto. Es un código muy antiguo, así que me pregunto si es algo de K&R. Si es así, en realidad deseo admitir los compiladores antiguos que pueden haber requerido esto, por lo que no quiero eliminarlos.
¿Hay alguna razón técnica para usar estos moldes? Ni siquiera veo una gran razón pragmática para usarlos. ¿Cuál es el punto de recordarnos el tipo de datos justo antes de liberarlo?
EDITAR: Esta pregunta no es un duplicado de la otra pregunta. La otra pregunta es un caso especial de esta pregunta, que creo que es obvio si los votantes cercanos leen todas las respuestas.
Colofón: le doy a la "respuesta constante" la marca de verificación porque es una verdadera razón genuina por la que esto podría ser necesario; sin embargo, la respuesta sobre que es una costumbre C anterior a ANSI (al menos entre algunos programadores) parece ser la razón por la que se usó en mi caso. Un montón de buenos puntos por muchas personas aquí. Gracias por sus aportaciones.
Aquí hay otra hipótesis alternativa.
Se nos dice que el programa fue escrito antes de C89, lo que significa que no puede estar trabajando en algún tipo de desajuste con el prototipo de
free
, porque no solo no había tal cosa ni
void *
antes de C89, había No existe un
prototipo de función
anterior a C89.
stdlib.h
sí fue un invento del comité.
Si los encabezados del sistema se hubieran molestado en declarar
free
, lo habrían hecho así:
extern free(); /* no `void` return type either! */
Ahora, el punto clave aquí es que la ausencia de prototipos de funciones significa que el compilador no realizó ninguna comprobación de tipo de argumento . Aplicó las promociones de argumento por defecto (las mismas que todavía se aplican a las llamadas a funciones variadas) y eso fue todo. La responsabilidad de hacer que los argumentos en cada sitio de llamadas se alineen con las expectativas del destinatario recae completamente en el programador.
Sin embargo, esto todavía no significa que era necesario lanzar el argumento de forma
free
en la mayoría de los compiladores de K&R.
Una función como
free_stuff(a, b, c)
float *a;
char *b;
int *c;
{
free(a);
free(b);
free(c);
}
debería haber sido compilado correctamente.
Así que creo que lo que tenemos aquí es un programa escrito para hacer frente a un compilador con errores para un entorno inusual: por ejemplo, un entorno donde
sizeof(float *) > sizeof(int)
y el compilador
no
usarían las llamadas apropiadas convención para punteros a menos que los lance en el punto de la llamada.
No conozco ningún entorno de ese tipo, pero eso no significa que no haya ninguno. Los candidatos más probables que vienen a la mente son los compiladores "C minúsculos" reducidos para micros de 8 y 16 bits a principios de la década de 1980. Tampoco me sorprendería saber que Crays temprano tuvo problemas como este.
Aquí hay un ejemplo en el que free fallaría sin un elenco:
volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p); // fail: warning C4090: ''function'' : different ''volatile'' qualifiers
free((int*)p); // success :)
free((void*)p); // success :)
En C puede obtener una advertencia (obtuvo una en VS2012). En C ++ obtendrá un error.
Dejando a un lado casos raros, el casting simplemente hincha el código ...
Editar:
lancé para
void*
no
int*
para demostrar la falla.
Funcionará igual que
int*
se convertirá a
void*
implícitamente.
Código
int*
agregado.
El C preestándar no tenía
void*
sino solo
char*
, por lo que tenía que emitir todos los parámetros pasados.
Si se encuentra con el antiguo código C, por lo tanto, puede encontrar dichos modelos.
Pregunta similar con referencias .
Cuando se lanzó el primer estándar C, los prototipos para malloc y free cambiaron de tener
char*
al
void*
que todavía tienen hoy.
Y, por supuesto, en el estándar C, tales modelos son superfluos y solo dañan la legibilidad.
Razón anterior: 1. Al usar
free((sometype*) ptr)
, el código es explícito sobre el tipo de puntero que debe considerarse como parte de la llamada
free()
.
El reparto explícito es útil cuando
free()
se reemplaza con un (hágalo usted mismo)
DIY_free()
.
#define free(ptr) DIY_free(ptr, sizeof (*ptr))
Un
DIY_free()
era (es) una forma, especialmente en modo de depuración, de hacer un análisis en tiempo de ejecución del puntero que se está liberando.
Esto a menudo se combina con un
DIY_malloc()
para agregar sentencias, recuentos de uso de memoria global, etc. Mi grupo usó esta técnica durante años antes de que aparecieran herramientas más modernas.
Obligó a que el elemento que se liberara fuera lanzado al tipo que se asignó originalmente.
- Dadas las muchas horas dedicadas a rastrear problemas de memoria, etc., pequeños trucos como lanzar el tipo free''d ayudarían a buscar y reducir la depuración.
Moderno: evitar advertencias
const
y
volatile
según lo abordado por
Manos Nikolaidis @
y
@egur
.
Pensé que notaría los efectos de los 3
calificadores
:
const
,
volatile
y
restrict
.
[editar] Se agregó
char * restrict *rp2
por
@R .. comentario
void free_test(const char *cp, volatile char *vp, char * restrict rp,
char * restrict *rp2) {
free(cp); // warning
free(vp); // warning
free(rp); // OK
free(rp2); // warning
}
int main(void) {
free_test(0,0,0,0);
return 0;
}
Se puede requerir el envío para resolver las advertencias del compilador si los punteros son
const
.
Aquí hay un ejemplo de código que causa una advertencia sin lanzar el argumento de free:
const float* velocity = malloc(2*sizeof(float));
free(velocity);
Y el compilador (gcc 4.8.3) dice:
main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
free(velocity);
^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
extern void free (void *__ptr) __THROW;
Si usa
free((float*) velocity);
El compilador deja de quejarse.
las tomas libres solo en punteros no constantes como parámetro. Entonces, en caso de punteros constantes, se requiere una conversión explícita a un puntero no constante.