¿Es mejor usar argumentos C void “void foo(void)” o no “void foo()”? [duplicate]

11 minutos de lectura

avatar de usuario
Zifre

Qué es mejor: void foo() o void foo(void)? Con void se ve feo e inconsistente, pero me han dicho que es bueno. ¿Es esto cierto?

Editar: sé que algunos compiladores antiguos hacen cosas raras, pero si estoy usando solo GCC, es void foo() ¿De acuerdo? Será foo(bar); entonces ser aceptado?

void foo(void);

Esa es la forma correcta de decir “sin parámetros” en C, y también funciona en C++.

Pero:

void foo();

¡Significa cosas diferentes en C y C++! En C significa “podría tomar cualquier número de parámetros de tipos desconocidos”, y en C++ significa lo mismo que foo(void).

Las funciones de lista de argumentos variables son intrínsecamente no seguras y deben evitarse siempre que sea posible.

  • Tenga en cuenta que void foo(); no significa una función varargs; simplemente significa que no se le ha dicho al compilador cuáles son sus argumentos. Una función varargs debe (según el estándar) declararse con puntos suspensivos. Además, void foo() { … } – definición de función en lugar de declaración – está bien (aunque la consistencia sugiere usar void foo(void) { … } aquí también).

    –Jonathan Leffler

    20 de junio de 2009 a las 14:05

  • Usted dice “al compilador no se le ha dicho cuáles son sus argumentos”, yo digo “podría tomar cualquier cantidad de parámetros de tipos desconocidos”. ¿Cual es la diferencia?

    – Daniel Earwicker

    20 de junio de 2009 a las 15:41

  • Dice que al compilador no se le ha dicho sobre el número/tipo de los argumentos. Pero usted dice que la función podría tomar cualquier número de parámetros de tipos desconocidos. pero eso es lo que ... es para (función varargs). Esto suena un poco confuso, pero creo que querías decir “toma en frío un cierto número de argumentos, pero este número no se conoce”. Es así: tienes una olla, y no sé cuántos litros puede tomar. Pero eso no significa que la olla pueda contener cualquier cantidad de litros. En cierta cantidad, simplemente se desborda 🙂 Así que para void foo(): Para algunas cantidades/tipos, solo provoca un comportamiento indefinido

    – Johannes Schaub – litb

    22 de julio de 2009 a las 10:35

  • () se declara una característica obsoleta en C99. Creo que la única confusión aquí surgió porque mencioné que las listas vararg no son seguras inmediatamente después de discutir las listas de parámetros vacías. Esto fue simplemente porque me imagino a alguien leyendo la explicación y luego pensando “Oh, me pregunto qué cosas increíbles puedo lograr con esta función”. Luego, lo siguiente que descubrirán es usar ... para escribir funciones como printf, y quería desalentar eso de inmediato. Lejos de sugerir que () es la forma de hacer varargs, digo que es mejor evitar los varargs por completo.

    – Daniel Earwicker

    22 de julio de 2009 a las 11:52

  • Me parece que void foo(void); podría confundirse fácilmente con void foo(void*); , mientras que foo() probablemente no se confundiría con foo(void*);

    – El arte de la guerra

    8 oct 2012 a las 20:16

avatar de usuario
Johannes Schaub – litb

Hay dos formas de especificar parámetros en C. Una es usar una lista de identificadores y la otra es usar una lista de tipos de parámetros. La lista de identificadores se puede omitir, pero la lista de tipos no. Entonces, para decir que una función no toma argumentos en una definición de función, lo hace con una lista de identificadores (omitida)

void f() {
    /* do something ... */
}

Y esto con una lista de tipos de parámetros:

void f(void) {
    /* do something ... */
}

Si en una lista de tipos de parámetros el único tipo de parámetro es nulo (entonces no debe tener nombre), eso significa que la función no toma argumentos. Pero esas dos formas de definir una función tienen una diferencia en cuanto a lo que declaran.

Listas de identificadores

El primero define que la función toma un número específico de argumentos, pero no se comunica el conteo ni los tipos de lo que se necesita, como ocurre con todas las declaraciones de funciones que usan listas de identificadores. Entonces, la persona que llama debe conocer los tipos y el conteo con precisión de antemano. Entonces, si la persona que llama llama a la función y le da algún argumento, el comportamiento no está definido. La pila podría corromperse, por ejemplo, porque la función llamada espera un diseño diferente cuando obtiene el control.

El uso de listas de identificadores en parámetros de función está en desuso. Se usó en los viejos tiempos y todavía está presente en muchos códigos de producción. Pueden causar un grave peligro debido a esas promociones de argumentos (si el tipo de argumento promocionado no coincide con el tipo de parámetro de la definición de la función, ¡el comportamiento tampoco está definido!) y, por supuesto, son mucho menos seguros. Así que siempre usa el void cosa para funciones sin parámetros, tanto en declaraciones únicas como en definiciones de funciones.

Lista de tipos de parámetros

El segundo define que la función toma cero argumentos y también lo comunica, como en todos los casos en los que la función se declara mediante una lista de tipos de parámetros, que se denomina prototype. Si la persona que llama llama a la función y le da algún argumento, eso es un error y el compilador arroja un error apropiado.

La segunda forma de declarar una función tiene muchos beneficios. Uno, por supuesto, es que se verifican la cantidad y los tipos de parámetros. Otra diferencia es que debido a que el compilador conoce los tipos de parámetros, puede aplicar conversiones implícitas de los argumentos al tipo de los parámetros. Si no hay una lista de tipos de parámetros, eso no se puede hacer y los argumentos se convierten en tipos promocionados (lo que se denomina promoción de argumento predeterminada). char se convertirá intpor ejemplo, mientras float se convertirá double.

Tipo compuesto para funciones

Por cierto, si un archivo contiene tanto una lista de identificadores omitidos como una lista de tipos de parámetros, la lista de tipos de parámetros “gana”. El tipo de la función al final contiene un prototipo:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

Eso es porque ambas declaraciones no dicen nada contradictorio. El segundo, sin embargo, tenía algo que decir además. Que es que se acepta un argumento. Lo mismo se puede hacer al revés.

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

El primero define una función usando una lista de identificadores, mientras que el segundo proporciona un prototipo para ella, usando una declaración que contiene una lista de tipos de parámetros.

  • ¿Cómo puedo cambiar todas mis funciones en el programa que no toman el parámetro de aceptación para anular mi función (anular) usando herramientas de formateo de código como Astyle? ¿Hay alguna opción que pueda dar con un estilo para cambiarlo para todas las funciones?

    – usuario7375520

    4 de febrero de 2017 a las 19:41

  • @user7375520 usa gcc con -Wstrict-prototypes

    – ljrk

    3 de noviembre de 2017 a las 20:52

avatar de usuario
uri

void foo(void) es mejor porque dice explícitamente: no se permiten parámetros.

void foo() significa que podría (bajo algunos compiladores) enviar parámetros, al menos si esta es la declaración de su función en lugar de su definición.

avatar de usuario
Ciro Santilli Путлер Капут 六四事

Cotizaciones C99

Esta respuesta tiene como objetivo citar y explicar las partes relevantes de la Borrador estándar C99 N1256.

Definición de declarante

El término declarante surgirá mucho, así que entendámoslo.

De la gramática del lenguaje, encontramos que los siguientes caracteres subrayados son declaradores:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Los declaradores son parte tanto de las declaraciones como de las definiciones de funciones.

Hay 2 tipos de declarantes:

  • lista de tipos de parámetros
  • lista de identificadores

Lista de tipos de parámetros

Las declaraciones se ven como:

int f(int x, int y);

Las definiciones se parecen a:

int f(int x, int y) { return x + y; }

Se llama lista de tipos de parámetros porque debemos dar el tipo de cada parámetro.

Lista de identificadores

Las definiciones se parecen a:

int f(x, y)
    int x;
    int y;
{ return x + y; }

Las declaraciones se ven como:

int g();

No podemos declarar una función con una lista de identificadores no vacía:

int g(x, y);

porque 6.7.5.3 “Declaradores de funciones (incluidos los prototipos)” dice:

3 Una lista de identificadores en un declarador de función que no sea parte de una definición de esa función deberá estar vacía.

Se llama lista de identificadores porque solo damos los identificadores x y y sobre f(x, y)los tipos vienen después.

Este es un método más antiguo y ya no debe usarse. 6.11.6 Declaradores de funciones dice:

1 El uso de declaradores de función con paréntesis vacíos (no declaradores de tipo de parámetro en formato prototipo) es una característica obsoleta.

y el Introducción explica que es un característica obsoleta:

Ciertas características son obsoletas, lo que significa que se puede considerar su retiro en futuras revisiones de esta Norma Internacional. Se conservan debido a su uso generalizado, pero su uso en nuevas implementaciones (para características de implementación) o nuevos programas (para lenguaje [6.11] o características de la biblioteca [7.26]) se desaconseja

f() vs f(void) para declaraciones

Cuando escribes solo:

void f();

es necesariamente una declaración de lista de identificadores, porque 6.7.5 “Declarantes” dice define la gramática como:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

por lo que solo la versión de la lista de identificadores puede estar vacía porque es opcional (_opt).

direct-declarator es el único nodo gramatical que define el paréntesis (...) parte del declarante.

Entonces, ¿cómo eliminamos la ambigüedad y usamos la mejor lista de tipos de parámetros sin parámetros? 6.7.5.3 Declaradores de funciones (incluidos los prototipos) dice:

10 El caso especial de un parámetro sin nombre de tipo void como el único elemento de la lista especifica que la función no tiene parámetros.

Asi que:

void f(void);

es el camino.

Esta es una sintaxis mágica explícitamente permitida, ya que no podemos usar un void escriba el argumento de cualquier otra manera:

void f(void v);
void f(int i, void);
void f(void, int);

¿Qué puede pasar si uso una declaración f()?

Tal vez el código se compilará bien: 6.7.5.3 Declaradores de funciones (incluidos los prototipos):

14 La lista vacía en un declarador de función que no forma parte de una definición de esa función especifica que no se proporciona información sobre el número o tipos de parámetros.

Así que puedes salirte con la tuya:

void f();
void f(int x) {}

Otras veces, UB puede aparecer sigilosamente (y si tiene suerte, el compilador se lo dirá), y tendrá dificultades para averiguar por qué:

void f();
void f(float x) {}

Ver: ¿Por qué una declaración vacía funciona para definiciones con argumentos int pero no para argumentos flotantes?

f() y f(void) para definiciones

f() {}

contra

f(void) {}

son similares, pero no idénticos.

6.7.5.3 Declaradores de funciones (incluidos los prototipos) dice:

14 Una lista vacía en un declarador de función que forma parte de una definición de esa función especifica que la función no tiene parámetros.

que se parece a la descripción de f(void).

Pero quieto… parece que:

int f() { return 0; }
int main(void) { f(1); }

se ajusta a un comportamiento indefinido, mientras que:

int f(void) { return 0; }
int main(void) { f(1); }

no es conforme como se discutió en: ¿Por qué gcc permite que se pasen argumentos a una función definida para que no tenga argumentos?

TODO entender exactamente por qué. Tiene que ver con ser un prototipo o no. Definir prototipo.

avatar de usuario
maja

Además de las diferencias sintácticas, muchas personas también prefieren usar void function(void) por razones prácticas:

Si está utilizando la función de búsqueda y desea encontrar la implementación de la función, puede buscar function(void)y devolverá el prototipo así como la implementación.

Si omite el voidtienes que buscar function() y, por lo tanto, también encontrará todas las llamadas a funciones, lo que dificultará encontrar la implementación real.

avatar de usuario
Pedro Mortensen

En C++, hay no diferencia en main() y main(void).

Pero Cª, main() será llamado con ninguna número de parámetros.

Ejemplo:

main (){
    main(10, "abc", 12.28);
    // Works fine!
    // It won't give the error. The code will compile successfully.
    // (May cause a segmentation fault when run)
}

main(void) sera llamado sin cualquier parámetro. Si tratamos de pasarlo, esto termina conduciendo a un error de compilación.

Ejemplo:

main (void) {
     main(10, "abc", 12.13);
     // This throws "error: too many arguments to function ‘main’ "
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad