kuanglf 发表于 2019-2-19 11:53:36

三个8位无符号数拼成一个24位数,这样移位拼怎么会错

ulong k;
uchar j;

j=0x55;
k=j<<16;
j=0xAA;
k+=(j<<8);
j=0x33;
k+=j;       
得出的结果k为0xFFAA33

改成
j=0x55;
k=((ulong)j)<<16;
j=0xAA;
k+=(j<<8);
j=0x33;
k+=j;
得出的结果k为0x54AA33

用以下的方法就能得出正确结果k=0x55AA33

j=0x55;
k=j<<8;
j=0xAA;
k+=j;
k=k<<8;
j=0x33;
k+=j;

编译软件iccavr8。这是怎么回事?

mcucow 发表于 2019-2-19 11:58:12

uchar j;
k=j<<16;

定义小了, 舍不得内存开销,哪能抓野猪

68336016 发表于 2019-2-19 11:58:18

j是8位的,移那么多位可以吗?

mcucow 发表于 2019-2-19 11:59:15

小女子要嫁高富帅, 自己也要花点内存 打扮打扮

go2deathward 发表于 2019-2-19 12:11:57

移位出问题,基本都是数据类型转换中出的错误

wye11083 发表于 2019-2-19 12:12:48

自己查汇编吧。编译器出错的几率是存在的。通常约定编译器执行时以最大的数据类型为准。

wye11083 发表于 2019-2-19 12:14:40

对了,avr在gcc8.2.0下编译,当大于6kb时,加一堆编译指令禁用jumptable也经常给我生成不可理喻的结果。所以我已经不再用avr了。目前用riscv,没出过任何故障。

KongQuan 发表于 2019-2-19 12:27:57

定义联合,然后直接赋值更简单高效吧。

labtech 发表于 2019-2-19 13:04:06

我记得以前参加笔试的时候经常遇到类似的题目。

fnems 发表于 2019-2-19 13:22:02

wye11083 发表于 2019-2-19 12:14
对了,avr在gcc8.2.0下编译,当大于6kb时,加一堆编译指令禁用jumptable也经常给我生成不可理喻的结果。所 ...

求问RISC V有推荐的芯片吗,好像平常没见到过呢

wye11083 发表于 2019-2-19 13:49:56

fnems 发表于 2019-2-19 13:22
求问RISC V有推荐的芯片吗,好像平常没见到过呢

我是fpga软核。riscv目前还没有。你可以考虑stm32。arm,x86,riscv还是比较稳定的。mips有段时间编译器也犯傻,一个好好的switch case结果汇编里面丢了一个case。

1a2b3c 发表于 2019-2-19 13:52:11

遇到这种问题,一旦搞不明白,我都直接强制转换永远不会错

saccapanna 发表于 2019-2-19 13:54:04

j溢出先赋值再右移

t3486784401 发表于 2019-2-19 14:31:10

短类型直接移位,不出错都是运气。ICCAVR 还负责一点,给出了提示:



先强制类型转换,再进行移位才安全。以下内容是 VS 中 windef.h 的数据拼接宏:
#define MAKEWORD(a, b)      ((WORD)(((BYTE)((DWORD_PTR)(a) & 0xff)) | ((WORD)((BYTE)((DWORD_PTR)(b) & 0xff))) << 8))
#define MAKELONG(a, b)      ((LONG)(((WORD)((DWORD_PTR)(a) & 0xffff)) | ((DWORD)((WORD)((DWORD_PTR)(b) & 0xffff))) << 16))
#define LOWORD(l)         ((WORD)((DWORD_PTR)(l) & 0xffff))
#define HIWORD(l)         ((WORD)((DWORD_PTR)(l) >> 16))
#define LOBYTE(w)         ((BYTE)((DWORD_PTR)(w) & 0xff))
#define HIBYTE(w)         ((BYTE)((DWORD_PTR)(w) >> 8))

类型转换、括号顺序几乎写到了啰嗦的地步

ilikemcu 发表于 2019-2-19 14:42:26

k=j<<16;
你忽略了j中间运算中就溢出了,j的定义是uchar啊

zhugean 发表于 2019-2-19 15:06:58

如果没有自动整形提升的话,三种写法都是错误的,应为 j是8位变量不能移了

kuanglf 发表于 2019-2-19 16:41:08

zhugean 发表于 2019-2-19 15:06
如果没有自动整形提升的话,三种写法都是错误的,应为 j是8位变量不能移了 ...

肯定是自动提升了的,因为最后一种是正确的

kuanglf 发表于 2019-2-19 16:42:03

68336016 发表于 2019-2-19 11:58
j是8位的,移那么多位可以吗?

k=j<<8; 这句是正确执行了的

kuanglf 发表于 2019-2-19 16:43:59

ilikemcu 发表于 2019-2-19 14:42
k=j

依你的看法,k=j<<8; 会等于0,但事实不是你想的那样

kuanglf 发表于 2019-2-19 16:45:49

1a2b3c 发表于 2019-2-19 13:52
遇到这种问题,一旦搞不明白,我都直接强制转换永远不会错

我用了强制转换:k=((ulong)j)<<16;也不对

kuanglf 发表于 2019-2-19 16:48:53

t3486784401 发表于 2019-2-19 14:31
短类型直接移位,不出错都是运气。ICCAVR 还负责一点,给出了提示:




你试过 k=j<<8;吗?   怎么不会出错?

ilikemcu 发表于 2019-2-19 16:54:56

kuanglf 发表于 2019-2-19 16:43
依你的看法,k=j

我没有说会溢出就是0,只是知道这样做是错误的做法,至于出错的结果,不是我需要考虑的,都是错了,有什么区别呢?

就像不遵守交规的人,不一定都是完全一样的死法,没必要去纠结怎么死的,只要自己遵守交规就可以了嘛。

就此打住,要不然就成抬杠了,一点意思都没有。

takashiki 发表于 2019-2-19 19:11:36

kuanglf 发表于 2019-2-19 16:48
你试过 k=j

我来回答吧,有时候还是要较真一下,互相探讨为好,抬杠的确实没有必要。
C语言按规范的实际上只有int和double类型有运算操作,更长的肯定要扩展否则就算不出来了,比它们小的类型就只好类型提升了。单片机的因为受到资源制约往往都不标准,好多double和float是一样的都是编译器的实现了。
那么uchar移位就应该使用int移位,这才是标准的。请注意:uchar是无符号的,但是很显然它能够类型提升到int,因此不会隐式转换为uint。那么:您的 j << 16就溢出了,因为int装不下,变成0了。然后就是 j << 8的问题。您能看到的是,j是uchar型,类型提升为int,那么j << 8就是int型,请注意,这里是有符号的!0xAA 左移8位变成了 0xAA00这是没错的,但是这个是负数。然后你就在这里栽跟头了。请外面用(ulong)再进行强制转换,正规的应该是(ulong)(uint)(j << 8)。

BTW:就这么个拼接,用联合体或指针都要好得多

jianfengxixi 发表于 2019-2-19 19:31:14

这个看汇编比较直接

fnems 发表于 2019-2-19 22:26:04

wye11083 发表于 2019-2-19 13:49
我是fpga软核。riscv目前还没有。你可以考虑stm32。arm,x86,riscv还是比较稳定的。mips有段时间编译器 ...


MIPS我玩过AR9331路由器当开发板,感觉还行。不下沉到汇编层面的话体会不到ARM和MIPS架构不同而对应用带来的区别。
至于丢CASE,也太奇葩了…


RISC-V据说前景很好,我还以为已经有应用出来了呢…


wye11083 发表于 2019-2-20 00:27:04

fnems 发表于 2019-2-19 22:26
MIPS我玩过AR9331路由器当开发板,感觉还行。不下沉到汇编层面的话体会不到ARM和MIPS架构不同而对应用带 ...

有,国产好多小嵌入式芯片都是rv32imdc的方案。年前重庆有家公司来我们这推销低功耗三核rv32 ai处理器。我敢说,riscv将来会占据嵌入式半壁江山,然而它并不能撼动arm和x86。新的硬件集成商都会慢慢转向riscv,如西数都开源了自有soc。关键是riscv面积小,性能强,代码密度高,理念先进。

至于mips gcc丢case我也无语,x86不丢就mips丢。当时在板上死活跑不出来结果,我查汇编才查出来。后来我改成if else然后把顺序倒了半天才找回来(case在mips汇编下有值注释,可以数出来丢了)。

路由器处理器一般不算太强,ar9341可以,就是太老了,不支持1gbps。这也是老家伙的缺点吧。所以我前段时间把所有网络设备全部换成1gbps的了,9341正式淘汰。

ycheng2004 发表于 2019-2-20 10:54:29

takashiki 发表于 2019-2-19 19:11
我来回答吧,有时候还是要较真一下,互相探讨为好,抬杠的确实没有必要。
C语言按规范的实际上只有int和d ...

向各位牛人学习了,

1a2b3c 发表于 2019-2-20 12:03:30

kuanglf 发表于 2019-2-19 16:45
我用了强制转换:k=((ulong)j)

要是你按照我说的,肯定不会错,虽然我不懂C语言,都是简单自学的一点,所以我才会很笨的强制转换!
你第二种方法中,你只说了8-->32强制,那么你的8-->16强制了吗?没有,就是你那个k+=(j<<8);,,要你是按照我一样的不懂的C语言的方式去做,改成 k+=(((unsigned short)j)<<8);那么一切问题就解决了。。。

就像前面有网友说的,你说的那个直接移动是正确的,可能的确是有运气和巧合在里面啊

我上面的验证的是在keilC51下面验证的,直接copy你的代码运行,结果和你的一样,都是54AA33,但是你按照我说的试试呢,那么就是正确的答案55AA33了{:lol:} 祝好运

1a2b3c 发表于 2019-2-20 12:05:09

哦,原来23楼 takashiki 已经分析了,高

t3486784401 发表于 2019-2-20 13:33:48

kuanglf 发表于 2019-2-19 16:48
你试过 k=j

编译器的提示中已经提到了 int 类型了,这也如 23 楼解释的一样,uchar 移位时自动升级到有符号的 int 了。

强制类型转换诶,这种时候还是不要省

kuanglf 发表于 2019-2-20 17:58:27

takashiki 发表于 2019-2-19 19:11
我来回答吧,有时候还是要较真一下,互相探讨为好,抬杠的确实没有必要。
C语言按规范的实际上只有int和d ...

您的回复让我找到了迷踪,谢谢!
页: [1]
查看完整版本: 三个8位无符号数拼成一个24位数,这样移位拼怎么会错