4. 在这问题上K&R C和ANSI C有没有区别?是什么区别
主要的几个地方:
1、可变参数列表,即va_arg(),这个是规定,因为编译器不知道函数原型,更不知道你的函数内部要的是什么类型,所以定义了这个规则。
2、算术运算,这个是小的应用
3、函数参数匹配,比如声明了void f(double),你调用f(1)是可以的,就是这个规则在起作用;f( "hello ")不行,也是这个规则。
4、进一步的,函数参数匹配也用在C++的函数overloading里,这时就可能会引起二义性。
我觉得你说的算术运算和第3点中的f(1)和这个va_arg()中遇到的default argument promotion规则还是有不同之处的。
算术运算以及调用f(1)时我认为不能算是一个default argument promotion, 常量的类型是在编译时根据函数原型指定好了的,这里并没有发生提升的情况。(仅仅是完成一次语义上的转化,一般的编译原理书上都有讲)
而用va_arg(arg, type)提取参数时,va_arg()事先并不知道以什么类型提取(因为是以变参中的...表示的),所以在这种情况下,比如在格式中遇到一个%c,它仍然是以va_arg(arg, int)方式来提取这个char类型的参数,而这种情况和上面的f(1)我觉得是有区别的。这里的区别问题在于,变参中的参数...是不是**总是**以提升后的类型压栈的?比如说,考虑下面这个函数:
void PrintMsg(char *errMsg , ...);
对这个函数有以下的调用:
PrintMsg( "%c %d %f\n ", 1, 2, 3);
这里的三个参数3,2,1在调用者压栈时分别是以什么类型压进去的呢?是不是参数1和2都是以int方式压栈,而3用double方式压栈?我认为编译器无法解决这个问题,它如何知道3在压栈时应该使用double类型呢?而同样的,如果不能确定栈上的参数类型,va_arg()在运行时又如何知道应该用float/double类型提取3这个参数呢?
PrintMsg( "%c %d %f\n ", 1, 2, 3);
的确全是以int(4字节)压栈的。而对于:
PrintMsg( "%c %d %f\n ", a, b, c);
其中a, b, c分别为char, int float类型的参数,
a和b分别以4字节压栈, c以float压栈(8字节)。
试验环境为32bit x86, FreeBSD/gcc2.95.3
va_arg()这个宏在处理变参...的时候,对于所有char, short类型的参数
都以int类型提取,对于float类型的参数都以double类型提取。
C语言允许不知道函数原型的情况下调用函数,所以,为了避免出现参数类型不一致引起的问题,在函数调用时,要进行type promotion,如short、int提升为int,float提升为double等。
这样,在进入函数时,要将promote后参数根据其类型进行处理。如果参数类型就是int、void*等不需要promote的类型,则其在栈里的位置不变。