构造方法中参数顺序_深入理解printf函数中的参数处理顺序

在阅读文章前,请先思考下面的代码执行结果是什么:

#include <stdio.h>
int main(){
  struct{
    int x; int y;
  }s[2] = {{2, 0}, {1, 3}}, *p = s;
  printf("%d, %d", ++p->x, (++p)->x);
  return 0;
}

如果你的答案为 3, 1 ,那你就大错特错了。

实际上,这道题的答案为 2, 1 ,那么这是为什么呢?

printf函数在处理参数的时候是从右向左处理的,其参数从右向左依次压入栈中,存放在栈中从高到低的地址里面,然后再格式化输出,输出时从低地址到高地址输出。即整个操作可以看做两部分:数据的处理(压栈)和格式化的输出(出栈)。

因此函数会先处理 (++p)->x ,因此第二个 %d 对应着 1 。然后再处理 ++p->x ,根据运算符优先级( -> 优先于 ++ ,因此 ++p->x 相当于 ++(p->x) )知第一个 %d 对应着 2


后来我发现事情又没有那么简单,请看下面的代码:

#include <stdio.h>

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

按照我们上述理论,总是从右向左解析,那么预期输出应为:

3 2 2 1

然而事实上这段代码跑下来实际结果为:

3 2 3 3
压栈顺序仍然是从右向左的,只是在底层 i++++i 的实现原理不一样。对于 i++ 的结果,是由ebp寻址函数栈空间来记录中间结果的,在最后给printf压栈的时候,再从栈中把中间结果取出来。而对于 ++i 的结果,则直接压寄存器变量,寄存器经过了所有的自增操作。 —— Go to Dessembly

这段代码的实际处理操作为:

  1. i 的地址入栈;
  2. i=i+1 ,压 i 的地址入栈;
  3. i 的值入栈, i=i+1
  4. i 的地址入栈;
  5. 出栈,为 i 的地址,解析为 3
  6. 出栈,为 2
  7. 出栈,为 i 的地址,解析为 3
  8. 出栈,为 i 的地址,解析为 3

因此结果为 3 2 3 3


参考文献

张春玲.可变参数函数printf调用过程的分析[J].电子制作,2014,000(002):058-058

注:文中所指的printf函数位于C语言标准库stdio.h中。结果均使用MinGW编译器验证,不同编译器结果可能不同。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include<stdio.h> #include<malloc.h> #define OK 1 #define ERROR 0 #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 #define ElemType int typedef struct { int *elem; int length; int listsize; }SqList; int InitList_Sq(SqList &L) { // 算法2.3,构造一个空的线性表L,该线性表预定义大小为LIST_INIT_SIZE // 请补全代码 } int Load_Sq(SqList &L) { // 输出顺序的所有元素 int i; if(_________________________) printf("The List is empty!"); // 请填空 else { printf("The List is: "); for(_________________________) printf("%d ",_________________________); // 请填空 } printf("\n"); return OK; } int ListInsert_Sq(SqList &L,int i,int e) { // 算法2.4,在顺序线性表L第i个位置之前插入新的元素e // i的合法值为1≤i≤L.length +1 // 请补全代码 } int ListDelete_Sq(SqList &L,int i, int &e) { // 算法2.5,在顺序线性表L删除第i个位置的元素,并用e返回其值 // i的合法值为1≤i≤L.length // 请补全代码 } int main() { SqList T; int a, i; ElemType e, x; if(_________________________) // 判断顺序表是否创建成功 { printf("A Sequence List Has Created.\n"); } while(1) { printf("1:Insert element\n2:Delete element\n3:Load all elements\n0:Exit\nPlease choose:\n"); scanf("%d",&a); switch(a) { case 1: scanf("%d%d",&i,&x); if(_________________________) printf("Insert Error!\n"); // 执行插入函数,根据返回值判断i值是否合法 else printf("The Element %d is Successfully Inserted!\n", x); break; case 2: scanf("%d",&i); if(_________________________) printf("Delete Error!\n"); // 执行删除函数,根据返回值判断i值是否合法 else printf("The Element %d is Successfully Deleted!\n", e); break; case 3: Load_Sq(T); break; case 0: return 1; } } }
最新发布
05-16

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值