¿Por qué anular el valor de retorno de free?

82

Estoy leyendo un libro ( Programación con hilos POSIX de Butenhof, 1997) que usa C, y me encontré con la siguiente línea:

(void)free(data);

Aquí, dataes solo un puntero a una estructura asignada,

data = malloc(sizeof(my_struct_t));

¿Por qué es el resultado de freeser lanzado a void?

Desde mi entendimiento de C, esto no parece tener sentido por dos razones:

  • La función libre ya regresa void
  • El código no usa el valor de retorno (ni siquiera se le asigna a una variable)

El libro fue escrito en 1997. ¿Es esto una especie de legado?

El autor menciona que los ejemplos se ejecutaron en Digital Unix 4.0d, pero todavía no puedo imaginar una razón para emitir el resultado de una función si no va a utilizar ese resultado.

Adam Johnston
fuente
Algunas explicaciones posibles se pueden encontrar aquí: stackoverflow.com/questions/689677/…
Timbo
3
Por curiosidad, ¿cuál es la fecha de publicación de su libro C? (¿Qué libro es?) Si es anterior a 1995, podría haber alguna justificación para ello: los compiladores C estándar no eran ubicuos antes de entonces. Si se publica después de eso y todavía contiene el elenco (y no se explica por qué), preocúpese por los otros malos hábitos que le está enseñando. ¡Obtén un libro más reciente!
Jonathan Leffler
10
Parece que la programación con hilos POSIX
Eugene Sh.
3
@JonathanLeffler como se mencionó en mi publicación original, el libro fue publicado en 1997 y estaba usando UNIX 4.0d. El libro es "Programación con hilos POSIX" de David R. Butenhof. Hasta ahora ha sido muy informativo y está escrito por uno de los colaboradores originales del estándar de hilos POSIX.
Adam Johnston
66
He estado usando mi copia de eso en la última semana, sí, todavía es útil. Fue escrito en la cúspide del 'estándar C omnipresente' (dije 'aproximadamente 1995'). El 'UNIX 4.0d' suena como Digital UNIX, ahí es donde trabajó Butenhof, y el prefacio lo menciona. Trate al elenco free()como una rareza en el libro que no necesita emular. Era semi-relevante hace mucho tiempo, pero ya no es relevante.
Jonathan Leffler

Respuestas:

100

Si estamos hablando de la freefunción estándar , entonces su prototipo es

void free(void *ptr);

Por lo tanto, el elenco es completamente inútil.
Ahora algo de especulación.

Es posible que el autor haya olvidado incluir el stdlib.hencabezado que declara este prototipo, por lo que el compilador asume el tipo de retorno como int. Ahora, durante el análisis estático de este código, el compilador advirtió sobre el valor de retorno no utilizado de lo que considera que no voidfunciona. Advertencias a tales suelen ser silenciados por la adición al elenco a void.

Eugene Sh.
fuente
50
Pero tenga en cuenta que si el elenco se introdujo por la razón especulada, entonces usarlo para silenciar la advertencia es incorrecto . En ese caso, el compilador atribuirá un tipo diferente al freeque realmente tiene, con el resultado de que la llamada tiene un comportamiento indefinido (suponiendo que la semántica C90, donde llamar a una función no declarada no exhibe inherentemente UB en todos los casos). En la práctica, es plausible que eso resulte en un mal comportamiento de buena fe en algunos sistemas. La solución correcta es proporcionar una declaración correcta para la función.
John Bollinger
11
En particular, los ejemplos en "Programación con hilos POSIX" repetidamente no incluyen los encabezados estándar relevantes. Tal vez esta fue una mala práctica del autor, podrían haber estado utilizando una configuración de compilador no estándar que incluía todas las bibliotecas estándar de forma predeterminada.
Lundin
74

¡Sería un legado!

Antes de que hubiera un estándar C, la free()función habría sido (implícitamente) de tipo int, porque todavía no había un tipo confiable voidpara que regresara. No hubo valor devuelto.

Cuando el código se modificó por primera vez para que funcione con compiladores C estándar, probablemente no lo incluyó <stdlib.h>(porque no existía antes del estándar). El código antiguo escribiría extern char *malloc();(tal vez sin el extern) para las funciones de asignación (de manera similar para calloc()y realloc()), y no necesitaba declarar free(). Y el código arrojaría el valor de retorno al tipo correcto, porque eso era necesario en al menos algunos sistemas (incluido el que aprendí en C).

Algún tiempo después, el (void)elenco se agregó para decirle al compilador (o, más probablemente lint) que "el valor de retorno free()se ignora deliberadamente" para evitar una queja. Pero hubiera sido mejor agregar <stdlib.h>y dejar que su declaración le extern void free(void *vp);dijera lintal compilador que no había valor que ignorar.

JFTR: A mediados de los '80, el ICL Perq estaba originalmente en una arquitectura orientada a palabras y la char *dirección para una ubicación de memoria era un número muy diferente del 'apuntador_superior' a la misma ubicación. Era crucial declarar de char *malloc()alguna manera; Era crucial transmitir el resultado a cualquier otro tipo de puntero. El elenco en realidad cambió el número utilizado por la CPU. (También hubo mucho regocijo cuando la memoria principal en nuestros sistemas se actualizó de 1 MiB a 2 MiB, ya que el núcleo usaba aproximadamente 3/4 MiB, significaba que los programas de usuario podían usar 1 1/4 MiB antes de la paginación, etc.)

Jonathan Leffler
fuente
99
Acabo de abrir una copia de K&R, primera edición, que contiene una implementación de la free()p. 177 que regresa implícitamente int.
ex nihilo
99
Por supuesto, voidse agregó a algunos sistemas (Unix System III, tal vez) antes del lanzamiento del estándar, pero eso no era parte de C cuando se escribió K&R 1st Edn (1978). Una función que no devolvió un valor se declaró sin un tipo de retorno (lo que significa que se devolvió int), y siempre que no haya utilizado el valor que no se devolvió, no hubo ningún problema. El estándar C90 tuvo que tratar ese tipo de código como válido: habría fallado lamentablemente como un estándar si no lo hubiera hecho. Pero C99 eliminó las intreglas 'implícita ' y 'declaración de función implícita'. No todo el código del mundo se ha puesto al día.
Jonathan Leffler
55
Op afirma que el libro fue escrito en 1997. De lo que estás hablando aquí es de un muy temprano pre-estándar "K&R C" y parece poco probable que alguien escriba un libro sobre eso. El único libro que existió, que yo sepa, fue K&R 1st edition.
Lundin
Era una práctica frecuente (aunque quijotesca) no incluir encabezados si creía que podía salirse con la suposición implícita, porque la gente pensaba que reduciría los tiempos de compilación.
Spencer
¿Alguien usa un (void)yeso para printf()?
Luis Colorado
11

Este yeso no es necesario. Probablemente no hubiera sido en ese momento, ya que C se había estandarizado en forma de C89.

Si lo hubiera sido, habría sido debido a una declaración implícita . Esto generalmente significaba que la persona que escribía el código se olvidó #include <stdlib.h>y que se estaba utilizando un analizador estático. Esta no es la mejor solución y una idea mucho mejor hubiera sido simplemente #include <stdlib.h>hacerlo. Aquí hay algunas palabras de C89 sobre la declaración implícita:

Si la expresión que precede a la lista de argumentos entre paréntesis en una llamada a la función consiste únicamente en un identificador, y si no hay una declaración visible para este identificador, el identificador se declara implícitamente exactamente como si, en el bloque más interno que contiene la llamada a la función, la declaración

extern int identifier();

apareció.

Pero eso es extraño porque no están emitiendo el resultado de mallocninguno de los dos, mallocy freeestán en el mismo archivo de encabezado.

También es posible que esto sea solo un error o alguna forma de decirle al lector que freeno devuelve ningún resultado.

SS Anne
fuente
55
El hecho de que un idioma se estandarice no significa que todos actualicen instantáneamente su cadena de herramientas y código para ajustarse a él. Es plausible que el antiguo "K&R" C se haya quedado en otros 8 años. Aún así, estoy de acuerdo en que es extraño que una herramienta de análisis estático requiera un yeso para, freepero no para malloc.
dan04
44
@ dan04 Usualmente usas el resultado de malloc;) No soy fanático de escribir cosas como (nulo) printf (...) para detener el compilador que escupe advertencias pero "debe compilar sin advertencias, incluso las estúpidas" es algo que sucede en muchos proyectos.
Richard