2009-08-21 21 views
14

De Lenguaje de Programación C. 2ª Edición:C: conversión de tipo cuando se pasa un argumento en una llamada a la función

Desde un argumento de una llamada de función es una expresión, conversiones de tipos también tienen lugar cuando los argumentos se pasan a función. En ausencia de un prototipo de función, char y short se convierten en int, y float se convierte en double.

Al leer el texto, me da la impresión de que, a menos que especifique explícitamente el tipo de argumento utilizando el molde o el prototipo de función, los argumentos de función siempre se pasarán como int o double.

Con el fin de verificar mi hipótesis, que compiló el siguiente código:

#include <stdio.h> 

main() 
{ 
    unsigned char c = 'Z'; 
    float number = 3.14f; 
    function_call(c, number); 
} 

void function_call(char c, float f) 
{ 
} 

Después de la compilación consigo las siguientes advertencias:

typeconversion.c: 11: advertencia: Conflicto en los tipos para ' function_call '

typeconversion.c: 7: advertencia: la declaración implícita anterior de' function_call 'estaba aquí

Supongo que c y el número se convirtieron en int y en doble en la llamada a la función, y luego se convirtieron nuevamente en char y float. ¿Es esto lo que realmente sucedió?

+2

Esta es una buena pregunta - y también ilustra por qué es importante siempre ** ** tiene un prototipo en su alcance, con todos los parámetros declarados. –

Respuesta

17

Los moldes son irrelevantes, es el prototipo (posiblemente implícito) lo que importa.

void foo(short s) { 
    // do something 
} 

int main(void) { 
    signed char c = 'a'; 

    foo(c); // c is promoted to short by explicit prototype 
    bar(c); // c is promoted to int by implicit prototype 
} 

void bar(int i) { 
    // do something 
} 

Cuando el libro dice "un argumento de una llamada de función es una expresión" que significa que las mismas reglas se aplican tipo de promoción. Puede ser más fácil de entender si piensas en un argumento de función como una asignación implícita a la variable especificada en el prototipo de función. p.ej. en la llamada al foo() de arriba hay un implícito short s = c.

Es por esto que los moldes no importan.Considere el siguiente fragmento de código:

signed char c = 'a'; 
int i = (short) c; 

Aquí el valor de c es promovido primero en short (explícitamente) a continuación int (implícitamente). El valor de i siempre será un int.

cuanto a char y short convertirse int y float convertirse double que se refiere a los tipos predeterminados de prototipos de funciones implícitas. Cuando el compilador ve una llamada a una función antes de ver un prototipo o la definición de la función, genera un prototipo automáticamente. De forma predeterminada, es int para valores enteros y double para valores de punto flotante.

Si la declaración de función eventual no coincide con el prototipo implícito, recibirá advertencias.

+0

su publicación responde muchas preguntas que tenía antes de publicar esta pregunta. –

+0

Me alegra ayudar, Midnight Blue. –

+1

Esta respuesta mezcla "prototipo" con "declaración". Un prototipo es lo que declara el tipo de parámetros de función. Una declaración es lo que puede incluir un prototipo y cómo se llama todo ("void f();"). Entonces, en su lugar, quiere decir "declaración implícita" y "declaración explícita". –

15

Tiene una idea general de lo que está mal, pero no exactamente.

Lo que pasa es que cuando se escribió

function_call(c, number); 

El compilador vio que estuviera llamando a una función que no se había visto hasta ahora, por lo que tuvo que decidir cuál debería ser su firma. Sobre la base de la regla de la promoción que usted ha citado anteriormente, promovió char a int y flotar a duplicar y decidió que la firma es

function_call(int, double) 

Luego, cuando se ve

function_call(char c, float f) 

interpreta esto como una firma para una función diferente con el mismo nombre, que no está permitida en C. Es exactamente el mismo error que si prototipara una función de forma diferente a cómo la defines, solo que en este caso el prototipo es generado implícitamente por el compilador.

Por lo tanto, esta es la regla que está causando el problema, pero el error no tiene nada que ver con la conversión real de valores entre los tipos.

+0

La promoción también ocurre con declaraciones antiguas (K & R), así como con funciones varargs. –

+0

No creo que la pregunta fuera sobre el error per se, aunque los dos están ciertamente relacionados. –

+0

El único signo de interrogación que veo está adjunto a una oración que pregunta si su interpretación de la causa del error fue correcta. Pero sí, definitivamente hay varios puntos para responder en esta pregunta. –

2

El compilador se queja de que asumió que function_call es una función de retorno int indicada por el estándar, y luego le dice que es una función vacía. Al compilador no le interesarán los argumentos a menos que esté declarando explícitamente que son diferentes a la función real. No puede pasarle argumentos y no se quejará.

Siempre debe declarar sus funciones porque este error no se detectará si la función está en otros módulos. Si la función debe devolver cualquier tipo que sea más grande que int, como void * o long, la conversión a int en la función de llamada probablemente lo trunque, dejándote con un error extraño.

+1

El error del compilador no tiene nada que ver con los argumentos que está pasando. Supusiste que sí, me modulaste. Las otras respuestas hacen caso omiso de esto y hacen pensar al OP que su compilador detectará errores de argumento incorrectos cuando no lo haga. Lo que sea. – jbcreix

2

Todos han perdido una cosa. En ISO C un prototipo de sintaxis ISO anula la promoción de argumento predeterminada.

Y en ese caso el compilador se permite para generar código diferente (!) basado puramente en el estilo de definición. Esto le proporciona compatibilidad con K & R, pero no siempre puede llamar entre los niveles de idioma a menos que haya escrito el código ISO como K & R espere o modifique el código K & para ver los prototipos ISO.

Prueba con cc -S ... -O

extern float q; void f(float a) { q = a; } 
movl 8(%ebp), %eax 
movl %eax, q 

extern float q; void f(a) float a; { q = a; } // Not the same thing! 
fldl 8(%ebp) 
fstps q 
+0

Bueno, esto es interesante ... – Artelius

Cuestiones relacionadas