搜索
bottom↓
回复: 39

请教一条语句:x=(x-1)%246 的结果 ......

[复制链接]

出0入0汤圆

发表于 2012-9-23 20:42:32 | 显示全部楼层 |阅读模式
在设计按键程序时,为了用一条语句既能完成减一,又能完成当前数为0时减一等于9(相信做过按键程序的都遇到过吧,就象9加一为0),于是想用如下公式来实现:
x=(x-1)%246;
按我想象,当 X=0 时,减一为255,255%249 正好等于9 ...... 可实际结果根本就不是9,而是 0x23 ,同时如果把这条语句拆成两条:
x=x-1;
x=x%246;
则结果正确 ......

哪位兄弟知道么,这两条语句合并为上面那一条,为啥结果就不同呢?

出0入0汤圆

发表于 2012-9-23 21:03:50 | 显示全部楼层
unsigned char x;
x = 9 - (++x)%10 ;

出0入0汤圆

 楼主| 发表于 2012-9-23 21:34:50 | 显示全部楼层
x定义为:unsigned char x;

不太理解三楼的说法:x-1 不是8位?

出0入0汤圆

 楼主| 发表于 2012-9-23 21:36:37 | 显示全部楼层
本帖最后由 Wxy8030 于 2012-9-23 21:39 编辑
GNMXD 发表于 2012-9-23 21:03
unsigned char x;
x = 9 - (++x)%10 ;


实际测试,以下语句可以:
x = (--x)%246 ;

还有就是,以前一直写:x=(x+1)%10 ; 今天才发现这条语句被编译器编译得其复杂无比,也需要更改为:
x = (++x)%10;

出0入0汤圆

 楼主| 发表于 2012-9-23 21:38:32 | 显示全部楼层
renpeng009 发表于 2012-9-23 21:13
因为x-1不是8位

这怎么理解呢?或者说,怎么写,才能让编译器明白这是8位?

出0入0汤圆

发表于 2012-9-23 21:45:25 | 显示全部楼层
因为x-1不是8位


right on.

出0入0汤圆

发表于 2012-9-23 21:46:08 | 显示全部楼层
这怎么理解呢?


google integer promotion.

出0入0汤圆

发表于 2012-9-23 21:49:24 | 显示全部楼层
Wxy8030 发表于 2012-9-23 21:36
实际测试,以下语句可以:
x = (--x)%246 ;

抱歉,我的程序不能满足要求!

你的这个问题是有点奇怪,贴个图上来看看

出0入0汤圆

 楼主| 发表于 2012-9-23 22:07:56 | 显示全部楼层
GNMXD 发表于 2012-9-23 21:49
抱歉,我的程序不能满足要求!

你的这个问题是有点奇怪,贴个图上来看看 ...

C:0x0005    1F       DEC      R7
C:0x0006    EF       MOV      A,R7
C:0x0007    75F0F6   MOV      B(0xF0),#0xF6
C:0x000A    84       DIV      AB
C:0x000B    AFF0     MOV      R7,B(0xF0)

以上是“        x=(--x)%(255-9);     ” 编译出来的结果,确实没问题。

出0入0汤圆

发表于 2012-9-24 08:22:22 | 显示全部楼层
本帖最后由 uc_c++ 于 2012-9-24 09:02 编辑

x-1

x是unsigned char,
C语言规定int以下不能直接参与运算,需要提升到int或者unsigned int 才能参与运算,这个转换由编译器自动帮你完成。
(换句话说char,unsigned char,signed char根本就没有算术运算和逻辑运算)

x-1  
相当于
(int)-1

出0入0汤圆

 楼主| 发表于 2012-9-24 09:03:38 | 显示全部楼层
我还是不太能理解,哪里有对这个的详细说明?

为什么以下语句:
x=x-25;
编译器就认为是字节操作,后面加了一个 %200 ;就认识为字操作了呢?

出0入0汤圆

 楼主| 发表于 2012-9-24 09:04:00 | 显示全部楼层
兄弟们能否推荐一本书,书上对这个有详细说明的?

出0入0汤圆

 楼主| 发表于 2012-9-24 09:04:39 | 显示全部楼层
uc_c++ 发表于 2012-9-24 08:22
x-1

x是unsigned char,

有哪里有书对此有明确的描述么?

出0入0汤圆

发表于 2012-9-24 09:08:40 | 显示全部楼层
Wxy8030 发表于 2012-9-24 09:04
有哪里有书对此有明确的描述么?

http://www.amobbs.com/forum.php? ... 7&highlight=C99

出0入0汤圆

发表于 2012-9-24 09:31:26 | 显示全部楼层
uc_c++ 发表于 2012-9-24 08:22
x-1

x是unsigned char,

K&R C中关于整型提升(integral promotion)的定义为:

"A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion."

出0入0汤圆

发表于 2012-9-24 11:11:05 | 显示全部楼层
x=(unsigned char)(x-1)%246;
这样应该就可以了!

出0入0汤圆

 楼主| 发表于 2012-9-24 15:02:40 | 显示全部楼层
本帖最后由 Wxy8030 于 2012-9-24 15:05 编辑

谢谢上面各位兄弟的回复,现在大概搞明白,总结如下,如有不对,请兄弟们指点:
1、C语言里对“字节”类运算,都要提升为“字”运算的;
2、实际上大部分“字节”运算升级为“字”运算后,跟按“字节”运算的结果并没有任何不同,所以绝大部分时候编译器不管是按“字节”或“字”运算,结果都一样;但“%”和“/”运算符例外!
3、写程序时,对字节的运算时,要注意“%”和“/”运算符前的运算数,该运算数为字和为字节时如果运算结果不一致,程序会按字运算!其他的运算符目前看来应该不用考虑!

出0入0汤圆

发表于 2012-9-24 16:05:42 | 显示全部楼层
什么编译器?IAR?

出0入0汤圆

发表于 2012-9-24 16:19:40 | 显示全部楼层
1.默认类型是编译器相关...即不安全代码...
2.% / 是C库函数实现...在函数参数类型中已进行强制转换...
3.请忘掉 字 字节 双字的概念...尝试用int8 int16 int32这种说法来说...
4.我编过的按键程序不能算少...但我真想不出来0-9循环数有什么用....望指教...
5./  %在有的平台上效率很低...尽量不要用...
6.x = (x == 0) ? 9 : x--;

出0入0汤圆

发表于 2012-9-24 17:49:52 | 显示全部楼层
本帖最后由 uc_c++ 于 2012-9-24 17:53 编辑

考题:

unsigned char x=0xff;
~x   //结果是多少?

unsigned char a=255;
unsigned char b=1;
int c=a+b;    //结果为多少?

unsigned int a=65535;
unsigned int b=1;
long int c=a+b;    //结果为多少?

int a,b;
unsigned char i=1;
signed char j=-1;
unsigned int k=1;
signed int l=-1;
a=(i>j);     //结果为多少?
b=(k>l);    //结果为多少?

出0入0汤圆

发表于 2012-9-24 18:29:29 | 显示全部楼层
2、实际上大部分“字节”运算升级为“字”运算后,跟按“字节”运算的结果并没有任何不同,所以绝大部分时候编译器不管是按“字节”或“字”运算,结果都一样;但“%”和“/”运算符例外!


that couldn't have been more wrong. for example, try to right shift a signed integer.

you really need to take a decent class in C.

出0入0汤圆

发表于 2012-9-24 18:30:32 | 显示全部楼层
mark、、、、

出0入0汤圆

发表于 2012-9-24 19:10:49 | 显示全部楼层
有条件   看看编译出来的代码就很清楚了

出0入0汤圆

发表于 2012-9-24 19:12:36 | 显示全部楼层
6.x = (x == 0) ? 9 : x--;
好专业啊!

出0入0汤圆

发表于 2012-9-24 19:44:51 | 显示全部楼层
uc_c++ 发表于 2012-9-24 17:49
考题:

unsigned char x=0xff;

我来尝试回答一下,要是错了请指正

unsigned char x=0xff;
~x   //结果是多少?
~x 应该是 0x00

unsigned char a=255;
unsigned char b=1;
int c=a+b;    //结果为多少?
进行计算时,提升为整型
所以c = 256


unsigned int a=65535;
unsigned int b=1;
long int c=a+b;    //结果为多少?
表达式a+b的值为0x0000
所以c = 0x00000000


int a,b;
unsigned char i=1;
signed char j=-1;
unsigned int k=1;
signed int l=-1;
a=(i>j);     //结果为多少?
提升为int,所以a = 1
b=(k>l);    //结果为多少?
提升为unsigned int 所以 b =  0

出0入0汤圆

 楼主| 发表于 2012-9-24 21:05:57 | 显示全部楼层
millwood0 发表于 2012-9-24 18:29
that couldn't have been more wrong. for example, try to right shift a signed integer.

you really  ...

兄弟我 C 确实学的不怎么样,属于半道出马!

但我的总结基本适合我的程序风格 —— 因为我不可能在程序里去右移一个有符号数,这样做有何意义?兄弟能否举出一个有意义的例子?

出0入0汤圆

 楼主| 发表于 2012-9-24 21:09:05 | 显示全部楼层
本帖最后由 Wxy8030 于 2012-9-24 21:11 编辑
adce 发表于 2012-9-24 16:19
1.默认类型是编译器相关...即不安全代码...
2.% / 是C库函数实现...在函数参数类型中已进行强制转换...
3. ...


我编过的按键程序不能算少...但我真想不出来0-9循环数有什么用....望指教...

比如也LCD上编写一个改变设备485地址的程序(设备地址为 0到255),目前有两种比较快的办法,一种是通过长按某键实现,一种就是我的方法,将该字节拆成三个数字,可以一个数字一个数字的设置 ....... 这个时候每个数字的改变就是0到9的循环

另:这里我觉得 x=(--x)%246 是最简洁的办法,在51下汇编出的语句也都是字节操作指令

出0入0汤圆

 楼主| 发表于 2012-9-24 21:21:06 | 显示全部楼层
uc_c++ 发表于 2012-9-24 17:49
考题:

unsigned char x=0xff;

本帖最后由 uc_c++ 于 2012-9-24 17:53 编辑


考题:

unsigned char x=0xff;
~x   //结果是多少?
答:0x00,跟KEIL模拟结果一样


unsigned char a=255;
unsigned char b=1;
int c=a+b;    //结果为多少?
答:0x100,跟KEIL模拟结果一样


unsigned int a=65535;
unsigned int b=1;
long int c=a+b;    //结果为多少?
答:0x10000,跟KEIL模拟结果不一样,KEIL里结果为 0x00000

int a,b;
unsigned char i=1;
signed char j=-1;
unsigned int k=1;
signed int l=-1;
a=(i>j);     //结果为多少?
b=(k>l);    //结果为多少?
这个没研究,因为我不会这么写程序

出0入0汤圆

发表于 2012-9-24 23:08:19 | 显示全部楼层
其实吧,看看反汇编,什么都明白了。。。。。
即搞不明白类型提升,又想把语句硬凑成一句,何必呢?难道觉得凑成一句会更快么?

现在感觉,很多时候写的尽量傻一点,清晰一点,让编译器更容易理解你想干什么,这才是正道。什么前++i后++i,不如i = i + 1;拆开写,加上注释,好维护,好理解,更好debug。不然让人家看你的程序,这条语句的作用要反应半天,还要拿纸算一下,何必呢?
写得太复杂,太技巧性的C语句,有时候总是适得其反。当然如果你是为参加下届混乱C编程大赛做练习,那另当别论。

出0入0汤圆

 楼主| 发表于 2012-9-25 08:34:20 | 显示全部楼层
jisaowang 发表于 2012-9-24 23:08
其实吧,看看反汇编,什么都明白了。。。。。
即搞不明白类型提升,又想把语句硬凑成一句,何必呢?难道觉 ...

呵呵,有道理啊

不过写程序有的时候确实有点强迫症的感觉,感觉写成几行就不舒服,写成一行就很舒服。

出0入0汤圆

发表于 2012-9-25 08:37:08 | 显示全部楼层
本帖最后由 uc_c++ 于 2012-9-25 09:20 编辑
Wxy8030 发表于 2012-9-24 21:21
本帖最后由 uc_c++ 于 2012-9-24 17:53 编辑


以下答题假定int是16位,char是8位,long是32位:

unsigned char x=0xff;
~x   //结果是多少?
答:0x00,跟KEIL模拟结果一样
-----------------------------------------------------------------------------------
正确结果是(int)0xFF00;
char没有~运算能力,运算先先会被提升至int.
~x相当于~((int)x)
KEIL C51这里并没有遵守C标准。
http://www.amobbs.com/thread-4582991-1-1.html

unsigned char a=255;
unsigned char b=1;
int c=a+b;    //结果为多少?
答:0x100,跟KEIL模拟结果一样
-----------------------------------------------------------------------------------
正确。
unsigned char没有+运算能力,运算之前先提升至int
a+b相当于((int)a+(int)b)
结果是(int)0x100

unsigned int a=65535;
unsigned int b=1;
long int c=a+b;    //结果为多少?
答:0x10000,跟KEIL模拟结果不一样,KEIL里结果为 0x00000
----------------------------------------------------------------------------------------
为什么结果为0,而是不是0x10000呢?
先看a+b,
a是unsigned int,b也是unsigned int,
unsigned int与unsigned int运算不需要提升,也不需要类型转换,结果仍然是unsigned int
a+b相当于(unsigned int)(a+b)
c=a+b
相当于c=(long)((unsigned int)(a+b))
结果当然为0。


int a,b;
unsigned char i=1;
signed char j=-1;
unsigned int k=1;
signed int l=-1;
a=(i>j);     //结果为多少?
b=(k>l);    //结果为多少?
这个没研究,因为我不会这么写程序
-----------------------------------------------------------
这里要强调,char不仅没有+-*/ & | ~运算,< > >= <=也运算也没有。
要进行比较运算,也必须要首先提升。
(i>j)
i是unsigned char,j是signed char
比较前先提升至int
(i>j)相当于((int)i>(int)j,结果为真。

而(k<l)
k是unsigned intr,j是signed int
unsigned int与signed int进行运算,signed int首先转换成unsigned int
(k<l) 相当于 (k<(unsigned int)l),结果为假。



出0入0汤圆

发表于 2012-9-25 08:47:48 | 显示全部楼层
~x 应该是 0x00
---------------------------------
假定int是16位:
~x结果是(int)0xff00

假定int是32位:
~x结果是(int)0xffffff00

以上结果是按照标准C运算规则得到的结果,
大部分编译器都遵守这个规则。

也有少部分编译器例外(如KEIL C51),
http://www.amobbs.com/thread-4582991-1-1.html

出0入0汤圆

发表于 2012-9-25 08:54:52 | 显示全部楼层
题目不错!
貌似您是老师?
学习了。谢谢

出0入0汤圆

 楼主| 发表于 2012-9-25 15:01:48 | 显示全部楼层
uc_c++ 发表于 2012-9-25 08:47
~x 应该是 0x00
---------------------------------
假定int是16位:

~x 是 0x00 这个倒也不是 KEIL 没遵守这个规律,而是 x 是8位,就算整型提升后运算结果是 0xFF00,赋值给 x 之后还得削掉高8位,所以结果还是 0x00 !

这也是我上面总结的时候提到的,为什么 加、减、乘 不需要注意整型提升的原因,因为提升后的运算结果再变成字节后,跟纯字节计算并没有什么不同,惟独 % / 这两操作符,8位和16位计算会导致结果的不同!

出0入0汤圆

发表于 2012-9-25 16:56:20 | 显示全部楼层
Wxy8030 发表于 2012-9-25 15:01
~x 是 0x00 这个倒也不是 KEIL 没遵守这个规律,而是 x 是8位,就算整型提升后运算结果是 0xFF00,赋值给 ...

是~x

不是
x=~x;

编译器不要仅仅局限于KEIL C51.
你可以试试IAR8051,SDCC8051,AVRGCC,IARAVR,VC,C++Builder,Keil MDK等等。

在很多情况下,KEIL C51都是很不标准的C编译器。

出0入22汤圆

发表于 2012-9-25 17:00:00 | 显示全部楼层
x=(x==0)? (x-1):9;
这样不更简单?











出0入0汤圆

 楼主| 发表于 2012-9-25 19:54:12 | 显示全部楼层
uc_c++ 发表于 2012-9-25 16:56
是~x

不是

不明白,你光把 x 取反,赋值给谁呢?不赋值给一个变量,取反了有什么意义??

出0入0汤圆

发表于 2012-9-26 08:23:47 | 显示全部楼层
一个很简单的例子:


  unsigned char x=0xff;
  unsigned char y=0x01;
  
  if((~x)==0)
  {
     printf("(~x)==0 is true");
  }
  else
  {
     printf("(~x)==0 is flase");
  }
  
  if((x+y)==0)
  {
     printf("(x+y)==0 is true");
  }
  else
  {
     printf("(x+y)==0 is flase");
  }

出0入0汤圆

 楼主| 发表于 2012-9-26 08:29:13 | 显示全部楼层
uc_c++ 发表于 2012-9-26 08:23
一个很简单的例子:

你这个例子确实有一定的道理,写程序时需要注意!

出0入0汤圆

发表于 2012-9-27 18:53:59 | 显示全部楼层
uc_c++ 发表于 2012-9-25 08:37
以下答题假定int是16位,char是8位,long是32位:

unsigned char x=0xff;

这里要强调,char不仅没有+-*/ & | ~运算,< > >= <=也运算也没有。
要进行比较运算,也必须要首先提升。。。
请问一下:为什么单片机里用char在for循环里 比 用int在for循环里运行得快,用定时器比较过,在飞思卡尔单片机
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-5-5 13:42

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

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