免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: yuxh
打印 上一主题 下一主题

printf为什么会这样? [复制链接]

论坛徽章:
0
101 [报告]
发表于 2004-12-30 13:52 |只看该作者

printf为什么会这样?

为什么从后开始计算?
如果从前也可以,但势必要用临时空间保存数值(比如printf有32个参数),然后再取出计算后的32个数值压站,这不得不偿失么?
从后,算一个压一个,非常自然。
所以做编译的人一定不会从前开始。

论坛徽章:
1
荣誉会员
日期:2011-11-23 16:44:17
102 [报告]
发表于 2004-12-30 13:55 |只看该作者

printf为什么会这样?

那assiss和win_hate的结果是怎么得出来的?
原帖由 "win_hate" 发表:
example 1: 2 2
example 2: 2 2
example 3: 2 2

论坛徽章:
0
103 [报告]
发表于 2004-12-30 14:01 |只看该作者

printf为什么会这样?

有的编译有问题。

  1. #include <stdio.h>;
  2. int* magic2(int n)
  3. {
  4.     static int a;
  5.     a=n;
  6.     return & a;
  7. }
  8. void main2()
  9. {
  10.     printf("example 2:  %d %d\n",*(magic2(1)),*(magic2(2)));
  11. }

  12. char* magic3(char a)
  13. {
  14.     static char s;
  15.     s=a;
  16.     return & s;
  17. }
  18. void main3()
  19. {
  20.     printf("example 3:  %c %c\n",*(magic3('1')),*(magic3('2')));
  21. }
  22. char* magic1(char *a)
  23. {
  24.     static char s[20];
  25.     sprintf(s,"%s",a);
  26.     return s;
  27. }
  28. int main()
  29. {
  30.     printf("example 1: %s %s\n",magic1("1"),magic1("2"));
  31.     main2();
  32.     main3();
  33.     return 0;
  34. }
复制代码

1 1
1 2
1 2
是正确结果

论坛徽章:
0
104 [报告]
发表于 2004-12-30 16:44 |只看该作者

printf为什么会这样?

gcc3.3.1+mingw+win2k
1 1
1 1
1 2

FreeBSD 5.3 Release+gcc3.4.2
1 1
1 2
1 2

lcc3.3(2004.dec.17)+win2k
2 2
2 2
2 2

古老的linux0.11+gcc1.40
1 1
1 2
1 2

我相信这与编译器相关,标准C并没有定义这个答案.
我用的gcc3.3.1是mingw提供的,可能有问题,不知道谁有LINUX/UNIX下的GCC老版本来试试,看看答案是否一样.
或者它与系统也相关.

论坛徽章:
0
105 [报告]
发表于 2004-12-30 17:10 |只看该作者

printf为什么会这样?

lcc和mingw是不怎么成熟的GNU windows编译,所以不行。
另外,可以用这个程序测试编译是否过关。
你可以再实验
VC,
TC,
LINUX GCC

不应该出问题。

论坛徽章:
0
106 [报告]
发表于 2004-12-30 21:39 |只看该作者

printf为什么会这样?

思一克最后给出的代码(前三贴)

[zxl@PPC src]$ gcc -v
Reading specs from /usr/lib/gcc-lib/ppc-yellowdog-linux/3.2.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=ppc-yellowdog-linux
Thread model: posix
gcc version 3.2.2 20030217 (Yellow Dog Linux 3.0 3.2.2-2a)
[zxl@PPC src]$ ./a.out
example 1: 2 2
example 2:  2 2
example 3:  1 2
[zxl@PPC src]$

论坛徽章:
0
107 [报告]
发表于 2004-12-30 22:56 |只看该作者

printf为什么会这样?

假定我们要实现一个 c 编译器,在处理函数调用时,从左到右处理参数是十分自然的,但我们也确实可以见到一些编译器,对参数的处理是从右到左的,比如 x86 上的编译器。我认为这个现象背后的原因之一是要支持参数个数可变的特性。以 printf 为例子,其第一个参数为格式串,printf 正是从这个串中知道参数个数的。显然格式串必须最后入栈,否则 printf 将无法定位格式串,从而失去其他参数的信息。但是且慢----格式串一定要入栈吗?

x86 上,多数编译器完全通过栈来传递参数,但其它机器呢? 就我接触过的 x86_64, ppc32,  saprc III 都是尽量通过寄存器来传递参数的。在通过寄存器传递参数的情况下,重新考虑 prinf 的例子。当编译器看到格式串时,把它放到``第一''个寄存器中,看到第二个参数时,把它放到另一个寄存器中。这样一来,就没有必要从右到左处理参数了,相反,可以采用更自然的方式,从左到右。

结论:从右到左的处理方式是在寄存器乏匮的机器上支持可变参数个数的一种不得以的方式。在通用寄存器丰富的机器上,一般不会采用这种方式。

以下是一些演示:


  1. #include <stdio.h>;

  2. int main(int argc, char **argv)
  3. {
  4.     int i = 0;

  5.     printf("%d %d %d\n", i++, i++, i++);
  6.     return 0;
  7. }
复制代码

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 个参数从寄存器传递,而后面的则压栈。


  1. #include <stdio.h>;

  2. int
  3. main (int argc, char **argv)
  4. {
  5.           int i = 0;

  6.           printf ("%d %d %d %d %d\n", i++, i++, i++, i++, i++);
  7.           return 0;
  8. }
复制代码

原帖由 "x86_64" 发表:

4 3 2 1 0


  1. #include <stdio.h>;

  2. int
  3. main (int argc, char **argv)
  4. {
  5.           long i = 0;

  6.           printf ("%d %d %d %d %d %d %d %d %d %d\n", i++, i++, i++, i++, i++, i++, i++, i++, i++, i++);
  7.           return 0;
  8. }
复制代码

原帖由 "x86_64" 发表:

4 3 2 1 0 9 8 7 6 5


在 x86_64 上,大体趋势是从左到右,局部从右到左。这种处理应该是不常见的。看一看 ppc 上的情况,r3-r12 用于参数传递。

  1. #include <stdio.h>;

  2. int main(int argc, char **argv)
  3. {
  4.     int i = 0;

  5.     printf("%d %d %d %d %d %d %d %d %d %d %d %d\n", i++, i++, i++, i++,
  6.            i++, i++, i++, i++, i++, i++, i++, i++);
  7.     return 0;
  8. }
复制代码

原帖由 "ppc32" 发表:

0 1 2 3 4 5 6 7 8 9 10 11


可见,在 ppc 上,参数处理始终如一地从左到右。

有 hp, compaq 的朋友帮忙式一试,我估计其上的 c 编译器也是从左到右处理参数的。

[注] 从我前面贴出来的 ANSI-C 标准可知,函数调用时对参数的求值次序是取决于实现的,因此任何求值次序都是合乎标准的。

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
108 [报告]
发表于 2004-12-30 23:14 |只看该作者

printf为什么会这样?

看了这贴。只觉得自己脸上发烧。不认真,不求甚解,甚至连标准也不看一下。先自责一个。偶觉得win_hate这贴说的非常好,基本可以结贴了。

论坛徽章:
0
109 [报告]
发表于 2004-12-31 08:47 |只看该作者

printf为什么会这样?

win_hate  的总结很及时啊,这帖子可以加精了:)

论坛徽章:
0
110 [报告]
发表于 2004-12-31 12:12 |只看该作者

printf为什么会这样?

To win_hate,

先预祝新年好.

这个问题其实转化为更简单的问题了,就是FUNCTION CALL参数计算(=压栈)次序问题. 可以测试

main()
{
int i = 0;
    printf("%d, %d, %d\n", i++, i++, i++);
}

结果在unix/C上正确的应该是2, 1, 0. 从右向左(R2L)

你将你的编译时的参数去掉,让编译默认地做,看是何结果, 我也想知道.

我前面帖说过, 可以控制编译使它反过来, 比如用function修饰符作用单个函数,或用编译OPTION使全部函数
都反过来.

linux, freebsd(前边有朋友证实过), MS-VC, Turbo-C是对的.
根据你给出的情况, 有的系统上不是这样, 但最好用default编译选项, 关掉寄存器优化看看. 也可能有的系统编译default
就是L2R, 但应该有编译参数可以更改为R2L.

R2L的确UNIX/C的convention, 至于是否是"标准"那还要考察. 多数系统C都要和UNIX/C兼容,所以我觉得
一定会有方法调整过来.

UNIX的许多方面(包括UNIX最早的KERNEL-PDP/11上的被当作教科书的10000行程序)都依赖与R2L.

如果一个C编译是L2R, 又无法调整过来,那应是有问题的.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP