¿La función C con parámetro sin indicador de tipo todavía funciona?

1 minuto de lectura

¿La funcion C con parametro sin indicador de tipo todavia
Zaiping Bie

El código es el siguiente:

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

int main()
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

Compilar resultado:

zbie@ubuntu:~$ gcc -Wall -g -std=c99 -O2 zeroparam.c

zeroparam.c: In function ‘func’:

zeroparam.c:2: warning: type of ‘param111’ defaults to ‘int’

Ejecutar resultado:

zbie@ubuntu:~$ ./a.out

99

10

11111110

99, 10, 11111110

Sé que el código debería estar bien si la función tiene cero parámetros, como int func(), que aceptará ninguna entradas. Pero, ¿cómo se compila y ejecuta correctamente este código?

  • Depende del compilador; Apple LLVM 5.0 genera muchas advertencias sobre los parámetros adicionales.

    – troyano

    13 de agosto de 2013 a las 6:45


  • Una pregunta int implícita más y borro mi cuenta… >.<

    usuario529758

    13 de agosto de 2013 a las 6:51

  • Ah, y por cierto, definir una función sin argumentos y luego pasarle argumentos es un comportamiento indefinido según la especificación (6.9.1/8): “Si una función que acepta un número variable de argumentos se define sin una lista de tipos de parámetros que termina con la notación de puntos suspensivos, el comportamiento no está definido”. Entonces, para aceptar correctamente un número variable de argumentos, debe haber un argumento adecuado y puntos suspensivos (...).

    – Un tipo programador

    13 de agosto de 2013 a las 6:51

  • @ H2CO3, tal vez sea molesto pero alimenta el índice de Google. Una persona lo pregunta, varios serán ayudados a lo largo de los años.

    – Profesor Falken

    13 de agosto de 2013 a las 7:01


  • @JoachimPileborg, por cada persona que se toma el tiempo de escribir una pregunta en SO, no importa cuán ingenuo sea, apuesto a que hay más palabras clave aleatorias en Google.

    – Profesor Falken

    13 de agosto de 2013 a las 7:23


1647723734 863 ¿La funcion C con parametro sin indicador de tipo todavia
Profesor Falken

Este comportamiento es para proporcionar compatibilidad con versiones anteriores del idioma, la versión K&R del idioma. Cuando GCC encuentra una función de “estilo antiguo”, se ajusta al antiguo comportamiento de K&R C, lo que implica que no hay advertencias en esta situación.

De hecho, si cambia la función a: int func(int param111)usted hacer obtener las advertencias esperadas:

x.c: In function ‘main’:
x.c:11:5: error: too many arguments to function ‘func’
x.c:2:5: note: declared here
x.c:12:5: error: too many arguments to function ‘func’
x.c:2:5: note: declared here
x.c:14:1: warning: control reaches end of non-void function [-Wreturn-type]

(Probado con GCC 4.7.3 y “gcc -std=c99 -Wall xc && ./a.out”)

O para citar a JeremyP de los comentarios: “En K&R C estaba perfectamente bien llamar a una función con tantos argumentos como quisieras, porque la notación de puntos suspensivos no se inventó entonces”..

Tenga en cuenta que un compilador puede mostrar tantas advertencias adicionales como desee y aún cumplir con el estándar. Por ejemplo, el compilador de Apple advierte sobre este código.

  • El OP pregunta por qué no recibe ni un error ni una advertencia en func(10, 99);

    – Pascal Cuoq

    13 de agosto de 2013 a las 6:40

  • He votado negativo porque esto ni siquiera intenta responder la pregunta.

    – ENP

    13 de agosto de 2013 a las 6:42

  • @NPE, una cosa a la vez, solo puedo escribir tan rápido… 🙂 “como int func() que aceptará cualquier entrada” también podría significar “¿por qué acepta un int y más argumentos”

    – Profesor Falken

    13 de agosto de 2013 a las 6:45


  • Son muchos votos negativos para una respuesta correcta. La definición de función es una definición de estilo K&R (no hay un especificador de tipo para el parámetro. En K&R C estaba perfectamente bien llamar a una función con tantos argumentos como quisiera, porque la notación de puntos suspensivos no se inventó entonces.

    – JeremyP

    13 de agosto de 2013 a las 6:56

  • @nneonneo: El estándar Nunca dice que un compilador no debe emitir una advertencia. Requiere diagnósticos (que pueden ser errores fatales o advertencias) en algunos casos, pero los compiladores siempre pueden imprimir advertencias adicionales.

    –Keith Thompson

    13 de agosto de 2013 a las 15:04

¿La funcion C con parametro sin indicador de tipo todavia
neoneo

La declaración de función se interpreta como una declaración de función de estilo K&R porque carece de tipos. En estándar, esto se llama una declaración de función con un lista de identificadoresa diferencia de un lista de tipos de parámetros como en la declaración habitual.

De acuerdo con la especificación C99, 6.9.1/7, solo las definiciones de función con un lista de tipos de parámetros se consideran prototipos de funciones. El estilo K&R usa una lista de identificadores en su lugar, por lo que no se considera que tenga un prototipo.

Las llamadas de funciones a funciones sin prototipos no se verifican en cuanto a recuentos o tipos de parámetros (según 6.5.2.2/8, “el número y los tipos de argumentos no se comparan con los de los parámetros en una definición de función que no incluye un declarador de prototipo de función” ). Por lo tanto, es legal llamar a una función declarada en el estilo K&R con cualquier número y tipo de argumentos, pero según 6.5.2.2/9, una llamada con tipos no válidos producirá un comportamiento indefinido.

Como ilustración, lo siguiente se compilará sin ninguna advertencias (en gcc -Wall -Wextra -pedantic -std=c99 -O):

#include <stdio.h>

void *func(param111)
char *param111;
{
    printf("%s\n", param111);
    return param111;
}

int main()
{
    void *bla0 = func();
    void *bla1 = func(99);
    void *bla2 = func(11111110,99);
    printf("%p, %p, %p\n", bla0, bla1, bla2);
    return 0;
}

a pesar de tener obviamente tipos de parámetros y recuentos incorrectos.

  • +1 para el estándar. Supongo que esto significa que GCC realmente cumple con C99 en este caso.

    – Profesor Falken

    13 de agosto de 2013 a las 7:20


  • Sí. También lo hace Clang, pero Clang elige advertir sobre los tipos incorrectos y el recuento de argumentos (a menos que especifique -Werrorsin embargo, Clang aún compilará el programa).

    – nneonne

    13 de agosto de 2013 a las 7:24

  • Gracias, interpreté tu respuesta como tal, pero no estaba 100% seguro. Gracias también por mencionar las advertencias de Clangs, también me preguntaba sobre eso. Supongo que un compilador es libre de advertir sobre lo que quiera.

    – Profesor Falken

    13 de agosto de 2013 a las 7:25

Se interpreta como K&R C, como han explicado otros. Vale la pena señalar que es un comportamiento indefinido en ANSI C:

C11 6.9.1 Definiciones de funciones Sección 9

Si una función que acepta un número variable de argumentos se define sin una lista de tipos de parámetros que termina con la notación de puntos suspensivos, el comportamiento no está definido.

Entonces, una función de argumentos de número variable tiene que terminar con ... como parámetro, como printf:

int printf( const char *format ,...);

Puedo explicar por qué esto funciona, pero no por qué el compilador no advierte al respecto.

Hay algunos convenciones de llamadas, que especifican cómo se ordenan los argumentos y dónde se colocan. La convención de llamadas de C permite pasar parámetros adicionales sin efectos secundarios, porque la persona que llama los limpia, no la función llamada, y todos se pasan a la pila:

Para su caso con func(10, 99), “main” empuja los valores a la pila en el siguiente orden (de derecha a izquierda):

99
10

“func” solo conoce un valor, y los toma desde el final, por lo que param111 == 10.

Luego, “main”, sabiendo que se enviaron dos argumentos, los recupera, limpiando así la pila.

¿La funcion C con parametro sin indicador de tipo todavia
x zhang

los func función en su código solo tiene una definición de función pero no un declarador de función. En C99 6.5.2.2 (Llamadas a funciones), dice:

“Ninguna otra conversión se realiza implícitamente; en particular, el número y los tipos de argumentos no se comparan con los de los parámetros en una definición de función que no incluye un declarador de prototipo de función”.

Cuándo func(10,99) y func(11111110, 99, 10001) se llama, el compilador no comparará el número y los tipos de argumentos con los parámetros en la definición de la función. Incluso puedes llamar a través de func("abc"). Sin embargo, si agrega la siguiente declaración de función de func en tu código:

int func(int);

(int fun(int) se declara porque el estándar C99 promoverá implícitamente para111 para int tipo), el compilador enviaría los siguientes errores:

zeroparam.c: En la función ‘principal’:
zeroparam.c:15:13: error: demasiados argumentos para la función ‘func’
zeroparam.c:6:5: nota: declarado aquí
zeroparam.c:16:17: error: demasiados argumentos para la función ‘func’

Por cierto: no creo que este sea un problema del “programa K&R”, ya que especifica explícitamente “-std = c99” en su comando.

  • Tiene un declarador de funciones; es parte de la definición de la función.

    –Keith Thompson

    13 de agosto de 2013 a las 15:03

Si no recibe ninguna advertencia para ese código, es porque su compilador no está aplicando las reglas C99 (llamando printf, o cualquier función, sin una declaración visible es una violación de restricción). Probablemente pueda obtener al menos algunas advertencias al pasar las opciones correctas a su compilador. Si estás usando gcc, prueba gcc -std=c99 -pedantic -Wall -Wextra.

El llamado K&R C, el lenguaje descrito por la primera edición de 1978 del libro clásico de Kernighan y Ritchie El lenguaje de programación C, no tenía prototipos de funciones. (Un prototipo es una declaración de función que especifica los tipos de sus parámetros). Una función definición todavía tenía que definir sus parámetros (quizás implícitamente), pero un declaración no lo hizo, y los compiladores típicos no verificaron la coincidencia correcta de los argumentos (en una llamada de función) con los parámetros (en una definición de función).

No estaba del todo claro qué sucede si llama a una función con el número y/o tipos de argumentos incorrectos. En términos modernos, era un comportamiento indefinido, pero los compiladores más antiguos generalmente te permiten jugar trucos.

El estándar ANSI C de 1989 (republicado como el estándar ISO C de 1990) introdujo prototipos (tomados de los primeros C++), pero no los requería. Pero sí declaró explícitamente que llamar a una función con el número o tipos de argumentos incorrectos causa comportamiento indefinido; el compilador no está obligado a advertirle al respecto, pero el programa puede hacer literalmente cualquier cosa cuando lo ejecuta.

El estándar ISO C de 1999 eliminó la regla “int implícita” y la hizo ilegal (una violación de restricción) para llamar a una función sin una declaración visible, pero aún permitía declaraciones y definiciones de funciones de estilo antiguo. Entonces, según las reglas K&R1 y C89/C90, la definición de su función:

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

es válido, y param111 es de tipo int. Según las reglas de C99, no es válido, pero esto:

int func(param111)
int param111;
{
    printf("%d\n", param111);
    return param111;
}

sigue siendo legal (y sigue siendo legal incluso bajo el estándar de 2011).

A partir de C99 y C11, si llama a una función cuya declaración visible no es un prototipo, depende completamente de usted obtener los argumentos correctos; el compilador no está obligado a advertirle si se equivoca.

Por eso deberías siempre use prototipos para todas las declaraciones y definiciones de funciones. La necesidad de escribir código que compile con compiladores anteriores a ANSI es prácticamente inexistente en estos días; es difícil encontrar un compilador que no admita al menos C89/C90.

Ah, y necesitas agregar

#include <stdio.h>

en la parte superior del archivo de origen porque estás llamando printf. Bajo las reglas C89/C90, llamando printf sin declaración visible tiene un comportamiento indefinido (porque printf toma un número variable de argumentos). En C99 y versiones posteriores, es una infracción de restricción que requiere un diagnóstico en tiempo de compilación.

He estado quisquilloso con la declaración de parámetros faltantes. Una variante ligeramente alterada de su programa:

#include <stdio.h> /* add this line */

int func(param111)
int param111;      /* add this line */
{
    printf("%d\n", param111);
    return param111;
}

int main(void)     /* add "void" */
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

no viola ninguna regla que requiera un diagnóstico en tiempo de compilación en C90, C99 o C11, pero la segunda y tercera llamadas a func tienen un comportamiento indefinido.

Tenga en cuenta que el compilador en realidad tiene suficiente información para advertirle que sus llamadas a func son incorrectos. Se acaba de ver la definición de funcy debe saber que cualquier llamada que no pase exactamente 1 argumento de un tipo que sea implícitamente convertible a int es inválido. No se requiere ninguna advertencia, pero los compiladores siempre pueden imprimir las advertencias adicionales que deseen. Aparentemente, los autores de gcc (y de cualquier compilador que esté usando) sintieron que no valía la pena el esfuerzo de advertir sobre llamadas a funciones que no coinciden con declaraciones y/o definiciones de estilo antiguo.

  • Tiene un declarador de funciones; es parte de la definición de la función.

    –Keith Thompson

    13 de agosto de 2013 a las 15:03

1647723736 595 ¿La funcion C con parametro sin indicador de tipo todavia
Un tipo programador

Si revisa las advertencias al compilar, verá este mensaje:

zeroparam.c:2: warning: type of ‘param111’ defaults to ‘int’

Eso le dice que un argumento sin un tipo por defecto será un número entero. Al igual que definir una función sin tipo de retorno, por defecto será int así como.

  • El OP pregunta por qué no recibe ni un error ni una advertencia en func(10, 99);

    – Pascal Cuoq

    13 de agosto de 2013 a las 6:41

  • La razón está en C99: “el número y los tipos de argumentos no se comparan con los de los parámetros en una definición de función que no incluye un declarador de prototipo de función”. La información adicional se incluye en mi publicación de respuesta.

    – X Zhang

    13 de agosto de 2013 a las 8:05


¿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