这是一个简单的函数,使用旧式语法来定义和定义:
#include <stdio.h>
void
error(message,a1,a2,a3,a4,a5,a6,a7)
char *message;
char *a1,*a2,*a3,*a4,*a5,*a6,*a7;
{
fprintf(stderr,message,a1,a2,a3,a4,a5,a6,a7);
}
int main ()
{
error("[ERROR %d]: %s.\n",110,"Connection timed out");
return 0;
}
可以编译并正确运行以打印:
[错误110]:连接超时。
我读到这个样式没有相关的原型,但是如何在运行时自动将int转换为char *,甚至提供的参数少于它声明的参数?
答案 0 :(得分:17)
基本上,它起作用,因为它太愚蠢,不能更好地了解。老式的K&amp; R C基本上不检查任何东西。你逃脱了,因为,
您正在使用的特定体系结构和编译器组合上发生了sizeof(int) == sizeof(char *)
。它并没有真正转换任何东西,它只是32位是32位。
当你把所有这些参数放在堆栈上时,它只是将它们推入。当printf使用它们时,它只是在需要时使用它们而剩下的就是其余部分;然后,当呼叫返回时,它们就会消失,而且没有人会更聪明。但是,如果您碰巧尝试打印七个值,只传递六个参数,它会在运行时爆炸,有时会以创造性和意想不到的方式爆炸。
答案 1 :(得分:9)
传递的参数太少,或者错误的类型(你们两者都做错了)会导致未定义的行为。这正是您不应该在新代码中使用旧样式语法的原因。如果您使用了新语法,您将从函数定义中获得“免费”原型。换句话说:
void
error(char * message,
char * a1, char * a2, char * a3, char * a4, char * a5, char * a6, char * a7)
{
}
也是原型。
使用旧语法,您必须提供自己的语法,而不是。这意味着编译器无法检查调用。
在实践中(在您的计算机上),error
正在从堆栈中将int
读取为char *
。然后,它将char *
传递给fprintf
。但是使用%d
说明符,因此fprintf
将其弹出为int
。这是更多未定义的行为。但它碰巧适用于您的机器; <{1}}和char *
的大小可能相同。
int
还会从堆栈中读取5个垃圾error
值。然后它将它们传递给它忽略的char *
,因为只有两个转换说明符。
答案 2 :(得分:-1)
实际上,如果在遇到函数error()
之前遇到函数110
的定义,编译器有效地确实在范围内有原型(这就是为什么老C程序员经常根据文件的使用顺序在文件中命令函数定义的原因)。因此,(char*)110
可以转换为sizeof(int) == sizeof(char*)
(而不是在sizeof(int) != sizeof(char*)
的机器上它很重要)。看看在{{1}}。
答案 3 :(得分:-2)
实际上有110转换为int,这个转换是由fprintf完成的,当fprint读取“%d”时,它会尝试将相应的参数转换为int。另一点是函数需要指针,即内存地址和指针是整数。如果传递了一个字符串而不是110,那么仍然会打印一个数字,即运行时字符串的地址。