tipos - funciones y procedimientos en c++
¿Por qué se compila una función sin parámetros(en comparación con la definición de la función real)? (10)
Acabo de encontrar el código C de alguien que me confunde por qué se está compilando. Hay dos puntos que no entiendo.
Primero, la función prototipo no tiene parámetros en comparación con la definición real de la función. Segundo, el parámetro en la definición de la función no tiene un tipo.
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
¿Por qué funciona esto? Lo he probado en un par de compiladores, y funciona bien.
Como se indicó en @Krishnabhadra, todas las respuestas anteriores de otros usuarios tienen una interpretación correcta y solo quiero hacer un análisis más detallado de algunos puntos.
En el antiguo C como en ANSI-C, el " parámetro formal no tipificado ", tome la dimensión de su registro de trabajo o la capacidad de profundidad de la instrucción (registros sombra o ciclo acumulativo de instrucciones), en una MPU de 8 bits, será un int16, en un 16bit MPU y así será un int16 y así sucesivamente, en el caso de que las arquitecturas de 64 bits puedan elegir compilar opciones como: -m32.
Aunque parece una implementación más simple a alto nivel, para pasar múltiples parámetros, el trabajo del programador en el paso del tipo de datos de control de control se vuelve más exigente.
En otros casos, para algunas arquitecturas de microprocesadores, los compiladores ANSI personalizados, aprovecharon algunas de estas características antiguas para optimizar el uso del código, forzando la ubicación de estos "parámetros formales sin tipo" para que funcionen dentro o fuera del registro de trabajo. casi lo mismo con el uso de "volátil" y "registro".
Pero hay que señalar que los compiladores más modernos, no hacen ninguna distinción entre los dos tipos de declaración de parámetros.
Ejemplos de una compilación con gcc en linux:
En cualquier caso, la declaración del prototipo a nivel local no sirve de nada, ya que no existe una llamada sin parámetros de referencia a este prototipo. Si usa el sistema con "parámetro formal sin tipo", para una llamada externa, proceda a generar un tipo de datos de prototipo declarativo.
Me gusta esto:
int myfunc(int param);
En C func()
significa que puede pasar cualquier número de argumentos. Si no desea argumentos, debe declarar como func(void)
. El tipo que está pasando a su función, si no está especificado por defecto, es int
.
En cuanto al tipo de parámetro, ya hay respuestas correctas aquí, pero si desea escucharlo desde el compilador, puede intentar agregar algunas banderas (las banderas son casi siempre una buena idea).
compilando su programa usando gcc foo.c -Wextra
obtengo:
foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]
extrañamente, -Wextra
no capta esto por clang
(no reconoce -Wmissing-parameter-type
por alguna razón, quizás por los históricos mencionados anteriormente) pero -pedantic
sí:
foo.c:5:10: warning: parameter ''param'' was not declared,
defaulting to type ''int'' [-pedantic]
int func(param)
^
1 warning generated.
Y para el problema del prototipo, como se dijo nuevamente, int func()
refiere a parámetros arbitrarios a menos que lo defina exclusivamente como int func(void)
que luego le daría los errores como se esperaba:
foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here
o en clang
como
foo.c:5:5: error: conflicting types for ''func''
int func(param)
^
foo.c:3:5: note: previous declaration is here
int func(void);
^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
int bla = func(10);
~~~~ ^~
foo.c:3:1: note: ''func'' declared here
int func(void);
^
2 errors generated.
Es la declaración y definición de la función de estilo K&R . Desde el estándar C99 (ISO / IEC 9899: TC3)
Sección 6.7.5.3 Declaradores de funciones (incluidos los prototipos)
Una lista de identificadores declara solo los identificadores de los parámetros de la función. 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. 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. (Si ambos tipos de funciones son "estilo antiguo", los tipos de parámetros no se comparan.)
Sección 6.11.6 Declaradores de funciones.
El uso de los declaradores de función con paréntesis vacíos (no los declaradores de tipo de parámetro de formato de prototipo) es una característica obsoleta.
Sección 6.11.7 Definiciones de funciones
El uso de definiciones de funciones con identificador de parámetros y listas de declaración separadas (no tipo de parámetro de formato de prototipo y declaradores de identificadores) es una característica obsoleta.
Que el estilo antiguo significa estilo K&R.
Ejemplo:
Declaración: int old_style();
Definición:
int old_style(a, b)
int a;
int b;
{
/* something to do */
}
Es por eso que normalmente recomiendo a las personas que compilen su código con:
cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition
Estas banderas imponen un par de cosas:
- - Declaraciones de variable de envío: es imposible declarar una función no estática sin obtener primero un prototipo. Esto hace que sea más probable que un prototipo en un archivo de encabezado coincida con la definición real. Alternativamente, exige que agregue la palabra clave estática a las funciones que no necesitan ser visibles públicamente.
- -Wstrict-variable-declaraciones: El prototipo debe enumerar correctamente los argumentos.
- -Wold-style-definition: la propia definición de la función también debe enumerar correctamente los argumentos.
Estas banderas también se usan de forma predeterminada en muchos proyectos de código abierto. Por ejemplo, FreeBSD tiene estos indicadores habilitados al compilar con WARNS = 6 en tu Makefile.
Si la declaración de función no tiene parámetros, es decir, está vacía, se está tomando un número no especificado de argumentos. Si desea que no tome argumentos, cámbielo a:
int func(void);
Todas las otras respuestas son correctas, pero solo para completion
Una función se declara de la siguiente manera:
return-type function-name(parameter-list,...) { body... }
return-type es el tipo de variable que devuelve la función. Esto no puede ser un tipo de matriz o un tipo de función. Si no se da, entonces se asume int
nombre-función es el nombre de la función.
lista de parámetros es la lista de parámetros que la función toma separados por comas. Si no se proporcionan parámetros, la función no acepta ninguno y debe definirse con un conjunto de paréntesis vacío o con la palabra clave void. Si no hay ningún tipo de variable delante de una variable en la lista de parámetros, se asume que se usa int . Las matrices y las funciones no se pasan a las funciones, sino que se convierten automáticamente a punteros. Si la lista termina con puntos suspensivos (, ...), entonces no hay un número establecido de parámetros. Nota: el encabezado stdarg.h se puede usar para acceder a los argumentos cuando se usan puntos suspensivos.
Y otra vez por el bien de la integridad. De la especificación C11 6: 11: 6 (página: 179)
El uso de los declaradores de función con paréntesis vacíos (no los declaradores de tipo de parámetro de formato de prototipo) es una característica obsoleta .
int func();
es una declaración de función obsolescente de los días en que no había un estándar C, es decir, los días de K&R C (antes de 1989, el año en que se publicó el primer estándar "ANSI C").
Recuerde que no había prototipos en K&R C y que la palabra clave void
aún no se había inventado. Todo lo que podía hacer era decirle al compilador sobre el tipo de retorno de una función. La lista de parámetros vacía en K&R C significa un número de argumentos "no especificados pero fijos". Fijo significa que debe llamar a la función con el mismo número de argumentos cada vez (a diferencia de una función variable , como printf
, donde el número y el tipo pueden variar para cada llamada).
Muchos compiladores diagnosticarán este constructo; en particular gcc -Wstrict-prototypes
le dirá que "la declaración de función no es un prototipo", lo cual es acertado, porque parece un prototipo (especialmente si está envenenado por C ++!), pero no lo es. Es una declaración de tipo de retorno K&R C de estilo antiguo.
Regla de oro: Nunca deje una declaración de lista de parámetros vacía, use int func(void)
para ser específico. Esto convierte la declaración de tipo de retorno de K&R en un prototipo C89 adecuado. Los compiladores están contentos, los desarrolladores están contentos, los verificadores estáticos están contentos. Sin embargo, los engañados por ^ W ^ Wfond of C ++ pueden temblar, porque necesitan escribir caracteres adicionales cuando intentan ejercer sus habilidades en idiomas extranjeros :-)
C asume int
si no se proporciona ningún tipo en la función tipo de retorno y lista de parámetros . Solo para esta regla es posible seguir cosas raras.
Una definición de función se ve así.
int func(int param) { /* body */}
Si es un prototipo escribes
int func(int param);
En prototipo solo se puede especificar el tipo de parámetros. El nombre de los parámetros no es obligatorio. Asi que
int func(int);
Además, si no especifica el tipo de parámetro pero el nombre int
se asume como tipo.
int func(param);
Si vas más lejos, los siguientes trabajos también.
func();
El compilador asume int func()
cuando escribe func()
. Pero no pongas func()
dentro de un cuerpo de función. Eso será una llamada de función
- La lista de parámetros vacía significa "cualquier argumento", por lo que la definición no es incorrecta.
- Se asume que el tipo que falta es
int
.
Consideraría que cualquier compilación que pase esto carece de un nivel configurado de advertencia / error, sin embargo, no tiene sentido que esto permita el código real.