搜索
bottom↓
回复: 31

未初始化的无符号局部变量右移时最高位应该补1还是补0?

[复制链接]

出0入0汤圆

发表于 2014-6-17 08:56:29 | 显示全部楼层 |阅读模式
最近用MDK 4.72a时,发现很奇怪的问题,如下代码,我预期无论如何,以下代码应该dat应该最后为0,但经单步仿真调试,有时候最高位补1,有时候会补0。

  1. void example(void)
  2. {
  3.         uint8_t i,dat;
  4.        
  5.         for(i=0;i<8;i++)
  6.         {
  7.                 dat>>=1;               
  8.         }

  9. }
复制代码


大家有没有遇到过这种问题?

出5入14汤圆

发表于 2014-6-17 09:10:37 | 显示全部楼层
我遇到过,就算你定义为字节型,移位时也会按照 32BIT 来移,所以最高位补1还是补0就看上个字节原来是什么值了 —— 唯一的解决办法就是:初始化一下!

出0入0汤圆

 楼主| 发表于 2014-6-17 09:11:05 | 显示全部楼层
jswd0810 发表于 2014-6-17 09:01
dat初始化一下试试

怪就怪在只要初始化就行,而无论这个值是多少,0x00到0xFF都行,而不初始化就不行。

我现在搞不明白,即使dat初始化不也是0x00到0xFF中间的一个数吗?也没有改变其数据类型啊?

出0入0汤圆

 楼主| 发表于 2014-6-17 09:16:59 | 显示全部楼层
EMC菜鸟 发表于 2014-6-17 09:10
我遇到过,就算你定义为字节型,移位时也会按照 32BIT 来移,所以最高位补1还是补0就看上个字节原来是什么 ...



可这是为什么?你说的这个出自哪里?编译器的原因?

出0入4汤圆

发表于 2014-6-17 09:19:49 | 显示全部楼层
prince2010 发表于 2014-6-17 09:16
可这是为什么?你说的这个出自哪里?编译器的原因?

指令集 只有16位移位的指令  移好后截取8位给你

出5入14汤圆

发表于 2014-6-17 09:20:18 | 显示全部楼层
prince2010 发表于 2014-6-17 09:16
可这是为什么?你说的这个出自哪里?编译器的原因?

这个是因为 CPU 是32位的原因,请看下面这条语句的汇编结果(x定义的是字节型):

   493:                         x=x>>1;
0x08014D90 980E      LDR      r0,[sp,#0x38]
0x08014D92 1040      ASRS     r0,r0,#1
0x08014D94 900E      STR      r0,[sp,#0x38]

出0入0汤圆

 楼主| 发表于 2014-6-17 09:30:27 | 显示全部楼层
本帖最后由 prince2010 于 2014-6-17 10:17 编辑
EMC菜鸟 发表于 2014-6-17 09:20
这个是因为 CPU 是32位的原因,请看下面这条语句的汇编结果(x定义的是字节型):

   493:              ...


我倒,C标准有这样规定没有?

那是不是所有的局部变量都要初始化才行啊?

——————————————————————————————————————————————

补充:我其实是问除了移位运算的时候,还有哪些操作时需要注意,要赋初始值?



出5入14汤圆

发表于 2014-6-17 10:23:37 | 显示全部楼层
prince2010 发表于 2014-6-17 09:30
我倒,C标准有这样规定没有?

那是不是所有的局部变量都要初始化才行啊?

C怎么规定的我不知道,但是 —— 初始化是个好习惯哈!

出0入0汤圆

 楼主| 发表于 2014-6-17 10:27:42 | 显示全部楼层
EMC菜鸟 发表于 2014-6-17 10:23
C怎么规定的我不知道,但是 —— 初始化是个好习惯哈!

出0入4汤圆

发表于 2014-6-17 10:58:26 | 显示全部楼层
prince2010 发表于 2014-6-17 09:30
我倒,C标准有这样规定没有?

那是不是所有的局部变量都要初始化才行啊?

作为左值时,都需要赋初值。

出0入0汤圆

 楼主| 发表于 2014-6-17 11:18:12 | 显示全部楼层
EMC菜鸟 发表于 2014-6-17 09:10
我遇到过,就算你定义为字节型,移位时也会按照 32BIT 来移,所以最高位补1还是补0就看上个字节原来是什么 ...

你是说初始化一下能够达到初始化32位数据的效果?

出0入0汤圆

发表于 2014-6-17 11:21:27 | 显示全部楼层
现象还跟优化等级有关,具体还是看汇编吧。

出0入0汤圆

发表于 2014-6-17 11:27:45 | 显示全部楼层
本帖最后由 twitter 于 2014-6-17 11:28 编辑

楼主以前是不是写VB之类的高级语言?问个基础的问题,楼主知道临时变量是存放在哪里的么?

出0入224汤圆

发表于 2014-6-17 11:28:26 | 显示全部楼层
laujc 发表于 2014-6-17 10:58
作为左值时,都需要赋初值。

右值吧.

出0入0汤圆

 楼主| 发表于 2014-6-17 11:29:59 | 显示全部楼层
twitter 发表于 2014-6-17 11:27
楼主知道临时变量是存放在哪里的么?

应该是堆栈,只知道自动分配,其余不知

出0入264汤圆

发表于 2014-6-17 11:32:23 | 显示全部楼层
搜索 逻辑右移,与算术右移的区别。

出0入0汤圆

 楼主| 发表于 2014-6-17 11:33:37 | 显示全部楼层
mcu_lover 发表于 2014-6-17 11:32
搜索 逻辑右移,与算术右移的区别。

这个貌似针对有符号数而言的。

出0入0汤圆

发表于 2014-6-17 11:43:55 | 显示全部楼层
本帖最后由 twitter 于 2014-6-17 11:45 编辑
prince2010 发表于 2014-6-17 11:29
应该是堆栈,只知道自动分配,其余不知


这个……,看来楼主没学过汇编,在单片机上的压栈和出栈的“分配”指令都是编译时已经编译好的,静态的,但函数占用的具体的栈地址,一般却是会跟着程序的运行状态动态变化的,即使前后地址不变,由于有中断函数打断和函数自身修改临时变量,失效的栈内存空间里的值也不会固定的,等到下次再用到这个空间,值当然不一定会是你期望的。

出0入0汤圆

 楼主| 发表于 2014-6-17 12:10:19 | 显示全部楼层
twitter 发表于 2014-6-17 11:43
这个……,看来楼主没学过汇编,在单片机上的压栈和出栈的“分配”指令都是编译时已经编译好的,静态的, ...

汇编确实不太会,呵呵。

有个疑问:比如uint8_t dat=0;这样初始化一下,汇编代码能对几个字节清零?(我试了一下,好像是4个)那么这个个数是谁决定的?如果是4个,而我们实际只需要1个字节,那么我们怎么节省空间?

出0入0汤圆

发表于 2014-6-17 12:39:26 | 显示全部楼层
prince2010 发表于 2014-6-17 12:10
汇编确实不太会,呵呵。

有个疑问:比如uint8_t dat=0;这样初始化一下,汇编代码能对几个 ...

节省的空间指的是存放数据的空间,不是运算时的空间

出0入0汤圆

 楼主| 发表于 2014-6-17 12:48:04 | 显示全部楼层
ywhbn 发表于 2014-6-17 12:39
节省的空间指的是存放数据的空间,不是运算时的空间

谢谢。我问的就是运算时候是不是不满4字节的整数都要按4字节分配堆栈空间?

出0入0汤圆

发表于 2014-6-17 12:48:19 | 显示全部楼层
C标准里有一个规定叫integer promotions,指的是执行运算之前,要先把参与运算的运算对象提升为int类型,以提高运行效率。
虽然是uint8_t的右移位,实际上先是4字节的右移位,再把结果赋值给uint8_t类型的变量,因为没有初始化uint8_t,4字节的值是随机的,导致有时是1有时是0。楼上的汇编是很好的说明

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf P84 6.5.7 Bitwise shift operators

出0入0汤圆

 楼主| 发表于 2014-6-17 13:35:27 | 显示全部楼层
ywhbn 发表于 2014-6-17 12:48
C标准里有一个规定叫integer promotions,指的是执行运算之前,要先把参与运算的运算对象提升为int类型,以 ...

兄弟牛X啊,这个都能找到

出0入4汤圆

发表于 2014-6-17 13:48:50 | 显示全部楼层

对的,是作为右值前一般应赋值

出0入0汤圆

发表于 2014-6-17 13:55:13 | 显示全部楼层
ywhbn 发表于 2014-6-17 12:48
C标准里有一个规定叫integer promotions,指的是执行运算之前,要先把参与运算的运算对象提升为int类型,以 ...

衹有此樓是正解。

出0入0汤圆

发表于 2014-6-17 14:43:01 | 显示全部楼层
qlb1234 发表于 2014-6-17 13:55
衹有此樓是正解。

这是第2个原因,首先是没有初始化,然后才是整形提升的结果。

出0入0汤圆

 楼主| 发表于 2014-6-17 14:59:13 | 显示全部楼层
twitter 发表于 2014-6-17 14:43
这是第2个原因,首先是没有初始化,然后才是整形提升的结果。

其实先前我发了一个帖子——

http://www.amobbs.com/thread-5584497-1-1.html

如果没有整形提升,即使不初始化,那8位也是全部被顶掉的。

出0入0汤圆

发表于 2014-6-17 15:26:56 | 显示全部楼层
prince2010 发表于 2014-6-17 14:59
其实先前我发了一个帖子——

http://www.amobbs.com/thread-5584497-1-1.html

但如果你初始化为0了,整形提升也是是没有影响的。

出0入0汤圆

 楼主| 发表于 2014-6-17 17:04:21 | 显示全部楼层
twitter 发表于 2014-6-17 15:26
但如果你初始化为0了,整形提升也是是没有影响的。



我想了一下你的回复内容,是不是应该这么理解:正是初始化赋值操作(其实赋值不一定要在初始化的时候),才消除了整型提升带来的副作用。

出0入0汤圆

发表于 2014-6-17 17:37:23 | 显示全部楼层
prince2010 发表于 2014-6-17 17:04
我想了一下你的回复内容,是不是应该这么理解:正是初始化赋值操作(其实赋值不一定要在 ...

就你一楼的例子来说,一旦初始化了,就不受整形提升的影响,因为你的uint8_t应该是无符号的,所以提升时,高字节都是填充了0的,但没有初始化的话,因为ARM的左右移动是在整个通用寄存器上操作的(通用寄存器是4字节的),导致高3字节的数据被移到低位上。

出0入0汤圆

 楼主| 发表于 2014-6-17 17:48:46 | 显示全部楼层
twitter 发表于 2014-6-17 17:37
就你一楼的例子来说,一旦初始化了,就不受整形提升的影响,因为你的uint8_t应该是无符号的,所以提升时 ...

呵呵,我汇编不好,我以为CPU可以8位数据操作的
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-3 20:22

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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