Promociones de argumentos predeterminados en llamadas a funciones de C

6 minutos de lectura

Configuración

Tengo algunas preguntas sobre las promociones de argumentos predeterminados cuando se llama a una función en C. Aquí está la sección 6.5.2.2 “Llamadas a funciones” Párrafos 6, 7 y 8 del Estándar C99 (pdf) (énfasis agregado y dividido en listas para facilitar la lectura):

Párrafo 6

  1. Si la expresión que denota la función llamada tiene un tipo que no incluye prototipolas promociones de enteros se realizan en cada argumento, y los argumentos que tienen tipo float son promovidos a double. Estos se llaman los promociones de argumento predeterminado.
  2. Si el número de argumentos no es igual al número de parámetros, el comportamiento no está definido.
  3. Si la función se define con un tipo que incluye un prototipoy el prototipo termina con puntos suspensivos (, ...) o los tipos de los argumentos después de la promoción no son compatibles con los tipos de los parámetros, el comportamiento no está definido.
  4. Si la función se define con un tipo que no incluye prototipoy los tipos de los argumentos después de la promoción no son compatibles con los de los parámetros después de la promoción, el comportamiento es indefinido, excepto en los siguientes casos:
    • un tipo promocionado es un tipo entero con signo, el otro tipo promocionado es el tipo entero sin signo correspondiente, y el valor se puede representar en ambos tipos;
    • ambos tipos son punteros a versiones cualificadas o no cualificadas de un tipo de carácter o void.

Párrafo 7

  1. Si la expresión que denota la función llamada tiene un tipo que incluye un prototipolos argumentos se convierten implícitamente, como por asignación, a los tipos de los parámetros correspondientes, tomando el tipo de cada parámetro como la versión no calificada de su tipo declarado.
  2. La notación de puntos suspensivos en un declarador de prototipo de función hace que la conversión de tipo de argumento se detenga después del último parámetro declarado. Las promociones de argumento predeterminadas se realizan en argumentos finales.

Párrafo 8

  1. Ninguna otra conversión se realiza implícitamente; en particular, el número y 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.

Lo que yo sé

  • Él promociones de argumento predeterminado son char y short para int/unsigned int y float para double
  • Los argumentos opcionales de las funciones variádicas (como printf) están sujetos a las promociones de argumentos predeterminados

Para que conste, mi comprensión de un prototipo de función Es esto:

void func(int a, char b, float c);  // Function prototype
void func(int a, char b, float c) { /* ... */ }  // Function definition

Pregunta

Me está costando mucho asimilar todo esto. Aquí hay algunas preguntas que tengo:

  • ¿Realmente difiere tanto el comportamiento de las funciones prototipadas y no prototipadas, como con respecto a las promociones predeterminadas y las conversiones implícitas?
  • ¿Cuándo ocurren las promociones de argumentos predeterminados? ¿Es siempre? ¿O es solo en casos especiales (como con funciones variádicas)? ¿Depende de si una función está prototipada?

Votó a favor la respuesta de AProgrammer: esos son los bienes reales.

Para aquellos de ustedes que se preguntan por qué las cosas son así: en la edad oscura antes de 1988, no existía un prototipo de función en el clásico “K&R” C, y las promociones de argumento predeterminado se instituyeron porque (a) eran esencialmente “gratis”, ya que no cuesta más poner un byte en un registro que poner una palabra en un registro, y (b) reducir los posibles errores en el paso de parámetros. Esa segunda razón nunca fue suficiente, por lo que la introducción de prototipos de funciones en ANSI C fue el cambio más importante en el lenguaje C.

En cuanto a cuándo se activan las promociones predeterminadas: Las promociones de argumento predeterminadas se usan exactamente cuando el tipo esperado del argumento es desconocido, es decir, cuando no hay prototipo o cuando el argumento es variado.

  • Gracias por aclararlo. Responder el “por qué” realmente me ayuda a aclarar esto.

    –Andrew Keeton

    10 de agosto de 2009 a las 17:46

avatar de usuario
Un programador

  • Los parámetros (no variadicos) de las funciones con un prototipo se convierten al tipo correspondiente, que puede ser char, short, float.

  • Los parámetros de las funciones sin prototipo y los parámetros variádicos están sujetos a promociones de argumentos predeterminados.

Si define una función con un prototipo y la usa sin el prototipo o viceversa y tiene parámetros de tipo char, short o float, probablemente tendrá un problema en tiempo de ejecución. Tendrá el mismo tipo de problemas con las funciones variádicas si el tipo promovido no coincide con el que se usa al leer la lista de argumentos.

Ejemplo 1: problema al definir una función con un prototipo y usarla sin él.

definición.c

void f(char c)
{
   printf("%c", c);
}

usar.c

void f();

int main()
{
   f('x');
}

puede fallar porque se pasará un int y la función espera un char.

Ejemplo 2: problema al definir una función sin prototipo y usarla con uno.

definición.c

void f(c)
   char c;
{
   printf("%c", c);
}

(Esta es una especie de definición que está muy pasada de moda)

usar.c

void f(char c);

int main()
{
   f('x');
}

puede fallar porque se espera un int pero se pasará un char.

Nota: observará que todas las funciones de la biblioteca estándar tienen tipos que resultan de promociones predeterminadas. Así que no causaron problemas durante la transición cuando se agregaron los prototipos.

  • ¿Qué quiere decir cuando dice “Si declara una función con un prototipo y la usa sin el prototipo…”?

    –Andrew Keeton

    10 de agosto de 2009 a las 17:48

  • Oh, solía declarar cuando quería decir definir. Se corrigió y agregó un ejemplo.

    – Un programador

    10 de agosto de 2009 a las 18:01

avatar de usuario
coste y flete

Su confusión se debe a un ligero malentendido de la terminología: tanto las declaraciones como las definiciones pueden incluir prototipos (o no):

void func(int a, char b, float c);

esa es una funcion declaración que incluye un prototipo.

void func(int a, char b, float c) { /* ... */ }

esa es una funcion definición que incluye un prototipo.

“Prototipado” y “sin prototipo” son solo atributos de una función tipoy tanto las declaraciones como las definiciones introducen el tipo de la función.

Entonces puedes tener una declaración sin un prototipo:

void func();

o puede tener una definición sin un prototipo (estilo K&R C):

void func(a, b, c)
    int a;
    char b;
    float c;
{ /* ... */ }

¿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