- 论坛徽章:
- 0
|
printf为什么会这样?
假定我们要实现一个 c 编译器,在处理函数调用时,从左到右处理参数是十分自然的,但我们也确实可以见到一些编译器,对参数的处理是从右到左的,比如 x86 上的编译器。我认为这个现象背后的原因之一是要支持参数个数可变的特性。以 printf 为例子,其第一个参数为格式串,printf 正是从这个串中知道参数个数的。显然格式串必须最后入栈,否则 printf 将无法定位格式串,从而失去其他参数的信息。但是且慢----格式串一定要入栈吗?
x86 上,多数编译器完全通过栈来传递参数,但其它机器呢? 就我接触过的 x86_64, ppc32, saprc III 都是尽量通过寄存器来传递参数的。在通过寄存器传递参数的情况下,重新考虑 prinf 的例子。当编译器看到格式串时,把它放到``第一''个寄存器中,看到第二个参数时,把它放到另一个寄存器中。这样一来,就没有必要从右到左处理参数了,相反,可以采用更自然的方式,从左到右。
结论:从右到左的处理方式是在寄存器乏匮的机器上支持可变参数个数的一种不得以的方式。在通用寄存器丰富的机器上,一般不会采用这种方式。
以下是一些演示:
- #include <stdio.h>;
- int main(int argc, char **argv)
- {
- int i = 0;
- printf("%d %d %d\n", i++, i++, i++);
- return 0;
- }
复制代码
x86/linux/gcc:_________________2, 1, 0
x86_64/linux/gcc:______________2, 1, 0
ppc/linux/gcc:_________________0, 1, 2
sparc III/solaris/gcc:_________0, 1, 2
从中可以看到, ppc, sparc 上,参数处理是从左到右的。x86_64 尽管通过寄存器来传递参数,但参数的求值却是从右向左的,这是因为它背有与 x86 兼容的历史包袱。
在参数个数增多的时候,编译器处理参数的方式差异更大,其中以 x86_64 的方式最为古怪。当参数个数少于 7 时,它们全部通过寄存器传递,而当参数大于等于 7 时,前 5 个参数从寄存器传递,而后面的则压栈。
- #include <stdio.h>;
- int
- main (int argc, char **argv)
- {
- int i = 0;
- printf ("%d %d %d %d %d\n", i++, i++, i++, i++, i++);
- return 0;
- }
复制代码
原帖由 "x86_64" 发表:
4 3 2 1 0
- #include <stdio.h>;
- int
- main (int argc, char **argv)
- {
- long i = 0;
- printf ("%d %d %d %d %d %d %d %d %d %d\n", i++, i++, i++, i++, i++, i++, i++, i++, i++, i++);
- return 0;
- }
复制代码
原帖由 "x86_64" 发表:
4 3 2 1 0 9 8 7 6 5
在 x86_64 上,大体趋势是从左到右,局部从右到左。这种处理应该是不常见的。看一看 ppc 上的情况,r3-r12 用于参数传递。
- #include <stdio.h>;
- int main(int argc, char **argv)
- {
- int i = 0;
- printf("%d %d %d %d %d %d %d %d %d %d %d %d\n", i++, i++, i++, i++,
- i++, i++, i++, i++, i++, i++, i++, i++);
- return 0;
- }
复制代码
原帖由 "ppc32" 发表:
0 1 2 3 4 5 6 7 8 9 10 11
可见,在 ppc 上,参数处理始终如一地从左到右。
有 hp, compaq 的朋友帮忙式一试,我估计其上的 c 编译器也是从左到右处理参数的。
[注] 从我前面贴出来的 ANSI-C 标准可知,函数调用时对参数的求值次序是取决于实现的,因此任何求值次序都是合乎标准的。 |
|