qué - void main definicion
¿Es mejor usar C void argumentos "void foo(void)" o no "void foo()"? (6)
Esta pregunta ya tiene una respuesta aquí:
¿Qué es mejor: void foo()
o void foo(void)
? Con el vacío se ve feo e inconsistente, pero me han dicho que es bueno. ¿Es esto cierto?
Edit: Sé que algunos compiladores antiguos hacen cosas raras, pero si estoy usando solo GCC, ¿es void foo()
Ok? Will foo(bar);
entonces ser aceptado?
Además de las diferencias sintácticas, muchas personas prefieren usar la void function(void)
por razones prácticas:
Si está utilizando la función de búsqueda y desea buscar la implementación de la función, puede buscar la function(void)
, y devolverá el prototipo y la implementación.
Si omite el segundo void
, debe buscar function()
y, por lo tanto, también encontrará todas las llamadas a funciones, lo que dificultará la búsqueda de la implementación real.
En C ++, no hay diferencia en main()
y main(void)
.
Pero en C, main()
se llamará con cualquier 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 Segmentation fault when run)
}
main(void)
será llamado sin ningún parámetro. Si intentamos pasar, esto terminará en un error de compilación.
Ejemplo:
main (void) {
main(10,"abc",12.13);
//This throws "error: too many arguments to function ‘main’ "
}
Hay dos formas de especificar parámetros en C. Una usa una lista de identificadores y la otra usa una lista de tipos de parámetros. La lista de identificadores se puede omitir, pero la lista de tipos no se puede omitir. Entonces, para decir que una función no toma argumentos en una definición de función, se hace esto 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 (no debe tener ningún nombre en ese momento), entonces la función no acepta argumentos. Pero esas dos formas de definir una función tienen una diferencia con respecto 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 función que usan listas de identificadores. Así que la persona que llama tiene que saber los tipos y el recuento con precisión de antemano. Entonces, si la persona que llama llama a la función dándole algún argumento, el comportamiento es indefinido. La pila podría dañarse, por ejemplo, porque la función llamada espera un diseño diferente cuando gana el control.
El uso de listas de identificadores en los parámetros de funciones 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 promovido no coincide con el tipo de parámetro de la definición de la función, el comportamiento tampoco está definido) y es mucho menos seguro, por supuesto. Por lo tanto, siempre use void
thingy 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 comunica eso, al igual que en todos los casos en 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, es un error y el compilador emite un error apropiado.
La segunda forma de declarar una función tiene muchos beneficios. Por supuesto, uno es la cantidad y los tipos de parámetros que se verifican. 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 parámetros. Si no hay una lista de tipos de parámetros, no se puede hacer y los argumentos se convierten en tipos promocionados (que se denomina promoción de argumentos predeterminada). char
se convertirá en int
, por ejemplo, mientras que float
se convertirá en double
.
Tipo compuesto para funciones
Por cierto, si un archivo contiene una lista de identificadores omitidos y una lista de tipos de parámetros, la lista de tipos de parámetros "gana". El tipo de 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 un argumento es aceptado. Lo mismo se puede hacer a la inversa.
void f(a)
int a;
{
printf("%d", a);
}
void f(int);
La primera define una función utilizando una lista de identificadores, mientras que la segunda le proporciona un prototipo, utilizando una declaración que contiene una lista de tipos de parámetros.
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.
Citas de C99
Esta respuesta tiene como objetivo citar y explicar las partes relevantes del borrador estándar de C99 N1256 .
Definición de declarador
El término declarador aparecerá mucho, así que entendámoslo.
A partir de la gramática del lenguaje, encontramos que los siguientes caracteres de subrayado 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 de las declaraciones de funciones y definiciones.
Hay 2 tipos de declaradores:
- lista de tipos de parámetros
- lista de identificadores
Lista de tipos de parámetros
Las declaraciones parecen:
int f(int x, int y);
Las definiciones parecen:
int f(int x, int y) { return x + y; }
Se llama lista de tipos de parámetros porque debemos indicar el tipo de cada parámetro.
Lista de identificadores
Las definiciones parecen:
int f(x, y)
int x;
int y;
{ return x + y; }
Las declaraciones parecen:
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 función (incluidos los prototipos)" dice:
3 Una lista de identificadores en un declarador de función que no es parte de una definición de esa función estará vacía.
Se llama lista de identificadores porque solo damos los identificadores x
e y
en f(x, y)
, los tipos vienen después.
Este es un método más antiguo, y no debería usarse más. 6.11.6 Los declaradores de función dicen:
1 El uso de declaradores de función con paréntesis vacíos (no declaradores de tipo de parámetro de formato de prototipo) es una característica obsoleta.
y la Introducción explica qué es una característica obsolescente :
Ciertas características son obsoletas, lo que significa que pueden ser consideradas para retirarse en futuras revisiones de esta Norma Internacional. Se conservan debido a su uso generalizado, pero se desaconseja su uso en nuevas implementaciones (para funciones de implementación) o nuevos programas (para lenguaje [6.11] o funciones de biblioteca [7.26])
f () vs f (void) para declaraciones
Cuando solo escribes:
void f();
es necesariamente una declaración de lista de identificadores, porque 6.7.5 "Declaradores" dice que 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 declarador.
Entonces, ¿cómo desambiguamos y usamos la mejor lista de tipos de parámetros sin parámetros? 6.7.5.3 Los declaradores de funciones (incluidos los prototipos) dicen:
10 El caso especial de un parámetro sin nombre de tipo void como el único elemento en 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 permitida explícitamente, ya que no podemos usar un argumento de tipo void
de ninguna 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 compile 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 los tipos de parámetros.
Así que puedes salirte con:
void f();
void f(int x) {}
Otras veces, UB puede aumentar (y si tienes suerte, el compilador te lo dirá), y te será difícil descubrir por qué:
void f();
void f(float x) {}
f () yf (void) para definiciones
f() {}
vs
f(void) {}
Son similares, pero no idénticos.
6.7.5.3 Los declaradores de funciones (incluidos los prototipos) dicen:
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 aún así ... parece que:
int f() { return 0; }
int main(void) { f(1); }
está conformando un comportamiento indefinido, mientras que:
int f(void) { return 0; }
int main(void) { f(1); }
no es conforme como se explica en: ¿Por qué gcc permite que los argumentos se pasen a una función definida sin argumentos?
TODO entender exactamente por qué. Tiene que ver con ser un prototipo o no. Definir prototipo.
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 la lista de argumentos variables son intrínsecamente no tipográficas y deben evitarse siempre que sea posible.