旧式C函数声明

时间:2011-01-03 01:56:46

标签: c function coding-style runtime

这是一个简单的函数,使用旧式语法来定义和定义:

#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 *,甚至提供的参数少于它声明的参数?

4 个答案:

答案 0 :(得分:17)

基本上,它起作用,因为它太愚蠢,不能更好地了解。老式的K&amp; R C基本上不检查任何东西。你逃脱了,因为,

  1. 您正在使用的特定体系结构和编译器组合上发生了sizeof(int) == sizeof(char *)。它并没有真正转换任何东西,它只是32位是32位。

  2. 当你把所有这些参数放在堆栈上时,它只是将它们推入。当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,那么仍然会打印一个数字,即运行时字符串的地址。