官方QQ群收藏本站

百问linux嵌入式论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2103|回复: 1

关于c语言的函数入栈,以及printf函数的浅析(修改错误)

[复制链接]

10

主题

26

帖子

97

积分

注册会员

Rank: 2

积分
97
发表于 2012-7-6 10:58:00 | 显示全部楼层 |阅读模式
在看程序员面试宝典的时候p34页的例题2时,由此引发的问题。源代码如下

复制代码
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. unsigned int a = 0xfffffff7;
  5. unsigned int i = (unsigned char )a;
  6. char *b = (char *)&a;
  7. printf("%08x,%08x",i,*b);
  8. return 0;
  9. }
复制代码
问题是:输出多少?答案:000000f7,fffffff7
虽然书上已经讲过了,不过我觉得可能在这里我要更深究一下:
再深入理解之前,我们必须要知道以下两要点
要点1:c语言对char和int型的数据在内存中的存放(书上没有讲)。
  1. 这个地方理解错误,char不是存的4个字节,子所以有的时候是存的4个字节,完全是考虑处理器的位数对齐的原因。
复制代码
  1. eg:
  2. char a,b,c,d;//char就是占据1字节
  3. 但是
  4. eg:
  5. char a;
  6. int b;
  7. //这样的话,char就可能是占据4字节了,对齐
复制代码


要点2:c语言函数入栈问题
大家都知道c语言函数入栈是从右往左入栈。但是你可能对类似这样的函数入栈的时候占据的空间就不了了知了。eg:
原型:void func(char a,char b)
调用:
  char a,char b;
  func(a,b);
你会知道先入栈b,后入栈a。然而入栈后在栈里面怎么存放的呢?
如果你够聪明的话,通过要点1就能才出来。如果你的答案是都占据4字节,那么恭喜你的,你猜对了。你可以用自己写程序来测试一下。
下面就上面的程序以栈来分析:加入main函数当要如局部变量的时候的栈是sp那么其变量分布如下:


  1. sp--> ff :起始SP
  2. ff
  3. ff
  4. f7 <-----a变量
  5. 00
  6. 00
  7. 00//这三字节的0,主要是考虑到与处理器的位数对齐
  8. f7 <-----i变量,可用数据只有一字节。截取1字节,其余的为0
  9. xx
  10. xx
  11. xx
  12. xx <-----b变量。里面xx的值组合成的32bit的值是a变量对应内存的地址。运行时才晓得
  13. 当调用printf函数的时候:printf("%08x,%08x",i,*b);
  14. 入栈*b,我们知道b指向a变量的地址。由于入栈*b是一个char类型的,也会以4个字节处理,所以会从sp-3处开始一直入到sp处共4字节,再入栈变量i,同样
  15. 从i开始如4个字节,所以调用printf函数的时候起栈为:
  16. printf sp---> ff
  17.                    ff
  18.                    ff
  19.                   f7
  20.                   00
  21.                   00
  22.                   00
  23. f7 <-----new printf sp
  24. printf函数在执行的时候,需要%x的时候,表示要从栈中取出一个4字节的二进制数,并且以16进制显示出来。
  25. 所以输出:000000f7,fffffff7
  26. 不是f7000000,f7ffffff。注意大小端
  27. 以上就是根据这个问题引发的总结:希望各位笑纳,如有分析不足之处,请别见怪
  28. 如您有有c语言相关问题的疑惑可以给本人讨论,虽然本人也很菜:qq:411186312

  29. 补充:
  30. int main(void)
  31. {
  32. char a = 1;
  33. int b= 1;
  34. char c=1;
  35. printf("%p %p",&a,&c);
  36. }
  37. 用gcc编译后运行的话,a和c的两个地址会只差1.为什么会这样?
  38. 以前在linux内核设计实现上讲过内存屏障。我想这里应该就是这个原因了。因为没有用到int的b变量的地址,故不需要知道b的
  39. 栈,所以,编译器把上面的代码优化成了如下代码:
  40. char a = 1;
  41. char c = 1;
  42. int b =1;
  43. 这样的情况也不会破坏程序员的用意。
  44. 如果你在后面使用到了变量b的地址,那么就不会出现这样的情况了。如在printf后面在加一句打印变量b的地址。eg:
  45. printf("%p\n",&b);这样的话,就会出现a和c的地址相差8字节了,也就符合了内存的对齐理论了。
  46. 还有一种情况也可以起到内存屏障的作用就是将int b。修改为volatile int b;这样的话也会起到一个内存屏障的作用。
  47. 对于前几天发出的错误结论,再次在这里给大家伙道歉了。不好意思,没有理解透彻
复制代码


回复

使用道具 举报

56

主题

5186

帖子

7811

积分

超级版主

Rank: 8Rank: 8

积分
7811
QQ
发表于 2012-7-11 11:29:00 | 显示全部楼层
好贴!
回复 支持 反对

使用道具 举报

技术支持
在线咨询
咨询热线
0755-86200561
微信扫一扫
获取更多资讯!

Archiver|小黑屋|百问linux嵌入式论坛     

GMT+8, 2018-11-18 14:39 , Processed in 0.323847 second(s), 20 queries , File On.

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

快速回复 返回顶部 返回列表