找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2250|回复: 55
打印 上一主题 下一主题
收起左侧

表现奇怪的C语言0x80 正数?负数?bug?

  [复制链接]
跳转到指定楼层
楼主
ID:1043747 发表于 2022-9-12 08:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0x80在例子1表达式中为正数,在例子2表达式中为负数。
我不禁疑惑,0x80在表达式中什么时候被解释为正数,何时是负数?

测试设备信息
开发板:普中A2
单片机:stc89c52rc
晶振:11.0592MHz
IDE:keil 5

单片机源程序如下:
  1. #include <STC89C5xRC.H>

  2. sbit led1 = P2^1;
  3. sbit led2 = P2^2;

  4. void main(void) {
  5.         
  6.         signed char a;
  7.         signed char byte, i, t;
  8.         // example 1
  9.         a = 0x80;
  10.         if (a != 0x80) led1 = 0; // led亮,说明if判断中字面量0x80是正数
  11.         // example 2
  12.         byte = 0xa0;
  13.         i = 4;
  14.         t = byte & 0x80>>i;
  15.         if (t < 0) led2 = 0; // led亮,说明上一句中0x80被解释为负数
  16.         while (1);
  17. }
复制代码



表现奇怪的0x80.zip (17.09 KB, 下载次数: 19)
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:94031 发表于 2022-9-12 10:07 | 只看该作者
signed char 的范围:-128-127 对于有符号数:最高位代表符号位: 0代表正数 1代表负数 。
回复

使用道具 举报

板凳
ID:213173 发表于 2022-9-12 11:14 | 只看该作者
signed char 声明有符号型变量就得按C语言规定使用,数据范围-128~+127。不是随使用者意想而改变。觉得反复横跳的是自己理解的问题?
回复

使用道具 举报

地板
ID:624769 发表于 2022-9-12 11:49 来自手机 | 只看该作者
正负数,是人为划定的一个范围,你要判断正负,也是应该用if(a<0)来判断,0x80在作为一个常量的时候,永远是0x80,没有正负数只说。之所以0x80赋值给signed char 声明的变量后,变-128,是C语言,这么“认知”的。某种意义上其实就是“溢出”。
回复

使用道具 举报

5#
ID:1043747 发表于 2022-9-12 12:16 来自手机 | 只看该作者
xuyaqi 发表于 2022-9-12 10:07
signed char 的范围:-128-127 对于有符号数:最高位代表符号位: 0代表正数 1代表负数 。

我知道,但我说的是字面量0x80
回复

使用道具 举报

6#
ID:123289 发表于 2022-9-12 14:19 | 只看该作者
本帖最后由 yzwzfyz 于 2022-9-13 08:34 编辑

完全看人为的定义!
必须事先规定!!
这样才能没有争议。
回复

使用道具 举报

7#
ID:1043747 发表于 2022-9-12 21:35 来自手机 | 只看该作者
188610329 发表于 2022-9-12 11:49
正负数,是人为划定的一个范围,你要判断正负,也是应该用if(a

你说的对,0x80就是128,永远是正数。
在例子2中,字面量0x80被解释为负数,我认为是keil bug,你觉得呢?
回复

使用道具 举报

8#
ID:550613 发表于 2022-9-13 10:33 | 只看该作者
你是从哪里得出0x80被解释为负数的结论。
注释的意思和你理解的意思真的一样吗?
这个注释就一定是对的吗?
回复

使用道具 举报

9#
ID:1043747 发表于 2022-9-13 12:03 来自手机 | 只看该作者
vca 发表于 2022-9-13 10:33
你是从哪里得出0x80被解释为负数的结论。
注释的意思和你理解的意思真的一样吗?
这个注释就一定是对的吗 ...

如果我哪里不对,你可以提出来啊,不要一味指责
回复

使用道具 举报

10#
ID:94031 发表于 2022-9-13 14:28 | 只看该作者
censv 发表于 2022-9-12 12:16
我知道,但我说的是字面量0x80

字面量0x80 作为 signed char 应该是 -128
回复

使用道具 举报

11#
ID:624769 发表于 2022-9-13 17:51 | 只看该作者
censv 发表于 2022-9-12 21:35
你说的对,0x80就是128,永远是正数。
在例子2中,字面量0x80被解释为负数,我认为是keil bug,你觉得呢 ...

t = byte & 0x80>>i;
从 这句, 以及你的结论看, 你完全没有理解  变量和常量的关系, 以及 有符号数的运算规则。
你尝试 把 上面这句, 分步写出来。 然后尝试,每一步 写出注释,说明这一步的目的,以及你认为得到的结果。看看,和你后面的 if 是不是能 衔接起来?

回复

使用道具 举报

12#
ID:1043747 发表于 2022-9-13 18:55 来自手机 | 只看该作者
188610329 发表于 2022-9-13 17:51
t = byte & 0x80>>i;
从 这句, 以及你的结论看, 你完全没有理解  变量和常量的关系, 以及 有符号数的 ...

那我们再简化一下,
当shift和result变量声明为8位有符号类型
shift = 4;
result = 0x80 >> shift;
现在result是0xF8,如果字面量0x80被当做正数,左移4位后的结果是0x08。
结论:字面量0x80一定被当做了负数
回复

使用道具 举报

13#
ID:213173 发表于 2022-9-13 20:54 | 只看该作者
censv 发表于 2022-9-12 21:35
你说的对,0x80就是128,永远是正数。
在例子2中,字面量0x80被解释为负数,我认为是keil bug,你觉得呢 ...

    signed char a;
    a = 0x80;
    if (a != 0x80) led1 = 0;//这里if只判断真假,哪来的正负判断?自己理解错误,不要怀疑keil bug,在keil里走单步看输出窗口就一目了然了。
回复

使用道具 举报

14#
ID:1043747 发表于 2022-9-14 06:15 来自手机 | 只看该作者
wulin 发表于 2022-9-13 20:54
signed char a;
    a = 0x80;
    if (a != 0x80) led1 = 0;//这里if只判断真假,哪来的正负判断 ...

if当然是判断真假,a != x80,因为a表示为负数,不等于正数的字面量0x80,是对的。
但在第二个例子,0x80的左移,最高位补的是1啊,这就不正常了
回复

使用道具 举报

15#
ID:1043747 发表于 2022-9-14 06:18 来自手机 | 只看该作者
xuyaqi 发表于 2022-9-13 14:28
字面量0x80 作为 signed char 应该是 -128

0x80字面量应该是128,-0x80才是-128
回复

使用道具 举报

16#
ID:550613 发表于 2022-9-14 10:57 | 只看该作者
censv 发表于 2022-9-13 12:03
如果我哪里不对,你可以提出来啊,不要一味指责

我没有指责你的意思,我的意思是你要思考一下,你理解的意思和注释的意思是不是一样的,如果是一样的,你是通过什么方法验证的,是通过调试或者是其他方法验证,最终得出这个结论的
回复

使用道具 举报

17#
ID:624769 发表于 2022-9-14 11:05 | 只看该作者
censv 发表于 2022-9-13 18:55
那我们再简化一下,
当shift和result变量声明为8位有符号类型
shift = 4;

所以,我说你没明白,什么叫 常量, 什么叫变量.   所以我让你分步写, 有助你理解.
result = 0x80 >> shift;  你为什么不分步写?如果分步写了,根本就没有这样的疑惑!
0x80 是 常量, 它能移位么??   不能! 所以, 你分步写只能这样写:
result = 0x80;
result >>= shift;
看出来了么??  你的 result 是 有符号char  -128~127,  所以 0x80 赋值后,是-128, 明白了么??

提个小建议,你不如先 学 1礼拜 的汇编, 也不要深入, 了解一下 51的 运转原理,以及常量变量的 应用关系,再回头看这问题,就会发现根本不是问题。
回复

使用道具 举报

18#
ID:1043747 发表于 2022-9-14 12:40 来自手机 | 只看该作者
本帖最后由 censv 于 2022-9-16 10:22 编辑
188610329 发表于 2022-9-14 11:05
所以,我说你没明白,什么叫 常量, 什么叫变量.   所以我让你分步写, 有助你理解.
result = 0x80 >> shif ...
0x80是常量不假,但也是整数。
而且关于移位运算符两个要求操作数都必须为整数,并且遵循整形提升原则,结果的类型是提升后的左操作数的类型。
所以对0x80的移位没有任何问题。有问题的我仍然认为是keil的编译没有遵循c语言标准(没有进行整形提升)

我还有些intel 8086的汇编知识,你能告诉我怎么查看keil编译后的汇编代码吗
回复

使用道具 举报

19#
ID:1043747 发表于 2022-9-14 12:50 来自手机 | 只看该作者
188610329 发表于 2022-9-14 11:05
所以,我说你没明白,什么叫 常量, 什么叫变量.   所以我让你分步写, 有助你理解.
result = 0x80 >> shif ...

不是不分步,是我不懂你的分布是什么意思?
我以为你是嫌代码多懒得看,我只好把代码再精简一下
回复

使用道具 举报

20#
ID:624769 发表于 2022-9-14 13:20 | 只看该作者
censv 发表于 2022-9-14 12:40
关键点来了,0x80是常量不假,但也是整数。
而且关于移位运算符两个要求操作数都必须为整数,并且遵循整 ...

察看编译后的 汇编代码是  Ctrl + F5

我还是那句话, 常量是不能 做移位的,必须有个载体。
让你做分步的目的,就是让你 意识到这个 “载体”
你要分步的话, 肯定 不能  0x80 >>= result;  吧? 编译绝对通不过。
由于你自己没有分步, 所以,编译器只好 帮你来分步。那么,怎么分?
你的受体目标是 result  编译器 只好按 result 类型作为标装, 来做0x80 的位移,理解了么?
如果,你正而8经的 分步写:
ACC = 0x80;   
ACC >>= 4;
result = ACC;
自然,就不是你那个结果了。明白了么?
回复

使用道具 举报

21#
ID:526543 发表于 2022-9-14 13:28 | 只看该作者

signed char 声明有符号型变量就得按C语言规定使用,数据范围-128~+127
回复

使用道具 举报

22#
ID:161164 发表于 2022-9-14 15:21 | 只看该作者
试试if (a != (char)0x80) led1 = 0;
回复

使用道具 举报

23#
ID:879809 发表于 2022-9-14 15:35 | 只看该作者
censv 发表于 2022-9-14 12:50
不是不分步,是我不懂你的分布是什么意思?
我以为你是嫌代码多懒得看,我只好把代码再精简一下

你精简的结果已经很明显了,没有人能看懂你在说什么东西。

你要把每一步都仔细写出来,大家才能知道你哪里错了。
回复

使用道具 举报

24#
ID:1043747 发表于 2022-9-14 19:28 | 只看该作者
rundstedt 发表于 2022-9-14 15:35
你精简的结果已经很明显了,没有人能看懂你在说什么东西。

你要把每一步都仔细写出来,大家才能知道你 ...

不知道你从哪认为的我认为自己绝对正确?

一上来就颐指气使,你哪%出来的%!
你引用的那段也不是回复你的,和你有关吗?
问不问的更不需要你指手画脚

你既然看不懂,那说明你%,那就不要回复就好了。
如果你认为我态度不好,先看看你自己
回复

使用道具 举报

25#
ID:1043747 发表于 2022-9-14 19:52 | 只看该作者
188610329 发表于 2022-9-14 13:20
察看编译后的 汇编代码是  Ctrl + F5

我还是那句话, 常量是不能 做移位的,必须有个载体。

非常感谢,通过看了反汇编我明白了。就是keil没遵循c标准。

只是你说常量不能移位,我不认为是正确的,我上面中间回复的那句话是c语言标准上的。
移位运算并不要求左值(类似你说的「载体」),
0x80 >>= result; >>=不是移位运算符,这是赋值运算符,更准确的说是复合赋值运算符,赋值运算符要求左操作数是左值。0x80是常量是右值,不能作为左值使用。所以该语句非法。

result = 0x80 >> shift; 的执行顺序,应该是0x80 >> shift 先运算,而非先看result的类型。因为移位优先级高于赋值


回复

使用道具 举报

26#
ID:624769 发表于 2022-9-14 20:18 | 只看该作者
censv 发表于 2022-9-14 19:52
非常感谢,通过看了反汇编我明白了。就是keil没遵循c标准。

只是你说常量不能移位,我不认为是正确的 ...

唉……,好吧,你觉得常量可以移位,就可以移位吧。
0x80 >> shift;   是不能独立成语句的。
0x80 >>= shift; 是非法的。

result >> shift;  也是不能独立成语句的。
result >>= shift; 是合法的。

移位必须在  A 寄存器操作。
任何变量,做移位操作,都会把 类型 带入。
你是 singed char 就会 有 MOV C,ACC.7
你是 unsigned char 就会有 CLR C
如果你是 int 或者 Long  则还会有  XCH A,R7 这些操作。 选择哪个操作,都是根据变量类型来定的。
但是,常量是没有 类型的。 当然喜欢抬杠的会拿  22118400UL 这种来说事,我就不一一解释了,就泛泛来讲,我说的“载体” 就是 承载告知 编译器 "类型" 的变量。
算了,你觉得怎么好理解就怎么理解吧,总之问题不在 0x80,也不是KEIL 的bug, 而是,在你的写法。决定了最后的结果。
回复

使用道具 举报

27#
ID:1043747 发表于 2022-9-14 22:28 来自手机 | 只看该作者
本帖最后由 censv 于 2022-9-15 07:38 编辑
188610329 发表于 2022-9-14 20:18
唉……,好吧,你觉得常量可以移位,就可以移位吧。
0x80 >> shift;   是不能独立成语句的。
0x80 >>=  ...

常量当然有类型,字面量常量0x80永远是正的128。

咱们撇过以上分歧不谈,你认为下一行语句执行后result变量的结果是多少,result声明为signed char

result = 0x80 >> 4;
回复

使用道具 举报

28#
ID:1043747 发表于 2022-9-14 22:31 来自手机 | 只看该作者
188610329 发表于 2022-9-14 20:18
唉……,好吧,你觉得常量可以移位,就可以移位吧。
0x80 >> shift;   是不能独立成语句的。
0x80 >>=  ...

0x80 >> shift;   是不能独立成语句的。
result >> shift;  也是不能独立成语句的。

这两条都是合法的,编译不会报错
回复

使用道具 举报

29#
ID:879809 发表于 2022-9-14 23:04 | 只看该作者
censv 发表于 2022-9-14 22:28
常量当然有类型,字面量常量0x80永远是正的128。

咱们撇过以上分歧不谈,你认为下一行语句执行后resul ...

c语言的魅力就在于贴近机器,0x80赋值给signed char后不是正数
回复

使用道具 举报

30#
ID:1043747 发表于 2022-9-15 07:16 来自手机 | 只看该作者
发表于 2022-9-14 23:04
c语言的魅力就在于贴近机器

0x80赋值给signed char并不影响原值,0x80也不会变成0x81
回复

使用道具 举报

31#
ID:624769 发表于 2022-9-15 10:52 | 只看该作者
censv 发表于 2022-9-14 22:31
0x80 >> shift;   是不能独立成语句的。
result >> shift;  也是不能独立成语句的。

你确定, 这样写不会出 275 警告?
你 Ctril +F5 看过编译结果么?
你知不知道, 这两句, 压根出不了 汇编代码, 会被编译器直接抛弃?

再回答上面你说的问题,
还是那句话, 常量是不能移位的, 所以:
result = 0x80 >> 4;   你看汇编代码的话, 你只会 看到  MOV result,#08H  而完全看不到 移位的操作。明白了么?
C语言的优点,也是他的缺点, 当你不严谨的时候, 他会自己脑补。


回复

使用道具 举报

32#
ID:1043747 发表于 2022-9-15 16:57 来自手机 | 只看该作者
188610329 发表于 2022-9-15 10:52
你确定, 这样写不会出 275 警告?
你 Ctril +F5 看过编译结果么?
你知不知道, 这两句, 压根出不了 汇编 ...

它们只是没有产生副作用,出275警告恰恰说明它们是合法语句。至于不会产生实际机器码,那是编译器优化掉了。

通过result =0x80 >> 4;的对比就可发现,移位操作并不和result类型相关。根据你的分步而说,如相关result的结果只能是0xF8。至于看不到汇编代码,那是因为0x80>>4是常量表达式,编译时执行。

我发现使用反汇编来反推C语言,有些倒果为因了。
看汇编能解决为什么代码在该平台是这样执行的,但解决不了编译器为什么这样翻译,这还得回到C语言标准本身

有3点可以证明c语言常量移位是合法的
①编译器不会报错②c语言标准对移位运算符有明确描述,不存在类似a[ i]=i++;等关乎运算顺序的歧义③vc,gcc,clang等编译器对于result=0x80>>shift的执行结果都是0x08,而不是keil的0xF8。
回复

使用道具 举报

33#
ID:824490 发表于 2022-9-24 16:10 | 只看该作者
一瓶农夫山泉,在超市里买2块钱,同样一瓶水,同样的容量,当它在迪厅里,可是会被卖到20块钱。
场景地点很关键!
所以,同样的0x80,当它赋值给不同类型的变量时(signed char/unsigned char),其代表的含义什是不同的。
变量的类型一旦定义下来,在运行时是不会被改变的。
且绕开变量的类型去谈它的正负值是没有意义的。
回复

使用道具 举报

34#
ID:384109 发表于 2022-9-24 21:12 | 只看该作者
怎么觉得讨论的有点偏了,0x80的十进制是什么数,首先要定义0x80的数据类型,在编程里,十六进制数和二进制数不事先定义数据类型,数值就没有意义,定义位有符号数,0x80-0xff就都是负数,定义为无符号数0x80-0xff就都是正数,至于0x80>>4,只是代表以0x80为具体数值进行移位操作,所以是可以实现也是正确的,但是移位结果同样要看数据类型,无符号数移位是补零,有符号数移位是补1,不能因为移位而改变了数据的类型
回复

使用道具 举报

35#
ID:137736 发表于 2022-9-24 21:29 | 只看该作者
看你定义,是有符合还是无符号。 signed char 与un signed char
回复

使用道具 举报

36#
ID:1043747 发表于 2022-9-26 15:50 来自手机 | 只看该作者
名字不是重点 发表于 2022-9-24 16:10
一瓶农夫山泉,在超市里买2块钱,同样一瓶水,同样的容量,当它在迪厅里,可是会被卖到20块钱。
场景地点 ...

你的意思是常量没有类型,且谈论她的类型没有意义。对吗?
回复

使用道具 举报

37#
ID:1043747 发表于 2022-9-26 15:52 来自手机 | 只看该作者
人中狼 发表于 2022-9-24 21:12
怎么觉得讨论的有点偏了,0x80的十进制是什么数,首先要定义0x80的数据类型,在编程里,十六进制数和二进制 ...

那你认为0x80是什么类型?
回复

使用道具 举报

38#
ID:1043747 发表于 2022-9-26 15:54 来自手机 | 只看该作者
zmc419 发表于 2022-9-24 21:29
看你定义,是有符合还是无符号。 signed char 与un signed char

说的是常量0x80,不是变量,你可能没认真读
回复

使用道具 举报

39#
ID:384109 发表于 2022-9-26 16:08 | 只看该作者
censv 发表于 2022-9-26 15:52
那你认为0x80是什么类型?

你这个问法就真的偏了,要先定义类型,再讨论具体含义,而不是随意讨论,那样没结果的,这是很直接的因果关系,先因后果
回复

使用道具 举报

40#
ID:1043747 发表于 2022-9-26 18:00 来自手机 | 只看该作者
人中狼 发表于 2022-9-26 16:08
你这个问法就真的偏了,要先定义类型,再讨论具体含义,而不是随意讨论,那样没结果的,这是很直接的因果 ...

那你的意思是常量没有类型?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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