C语言:#define的值弄乱了8位乘法。为什么?
-
16-09-2019 - |
题
我有以下的C代码:
#define PRR_SCALE 255
...
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
printf("prr: %u\n", prr);
如果我编译该(使用MSP430平台的编译器,用于称为半自助旅游旅行社一个小的嵌入式OS )的结果为0,而我预期191。 (uint8_t被Typedef的为unsigned char)
如果我将其更改为:
uint8_t a = 3;
uint8_t b = 4;
uint8_t c = 255;
uint8_t prr;
prr = (c * a) / b;
printf("prr: %u\n", prr);
它的工作原理正确输出并打印191。
编译此“正常”使用gcc的简单版本的Ubuntu盒打印在这两种情况下的正确的值。
我不知道是什么原因,这是。我可以通过预先分配定义的值给变量绕过它,但我宁愿不这样做。
有谁知道这是为什么?也许有一个关于这个链接一些信息呢?
解决方案
答案很简单:你的编译器是马车。 (没有与溢出没有问题,正如其他建议。)
在这两种情况下,运算在int
,这是保证是至少16位长进行。在前者的片段,那是因为255
是int
,后者是因为积分的推广一>
如你所指出的,GCC正确处理这一点。
其他提示
255正在处理作为文字的整数,并使得整个表达式为int基于而不是无符号字符为主。第二种情况迫使类型是正确的。试着改变你的#define如下:
#define PRR_SCALE ((uint8_t) 255)
如果所讨论的编译器是MSPGCC,它应该与二进制/ hex文件熄已编译的程序的汇编程序列表一起。其他编译器可能需要额外的编译器标志来做到这一点。或者甚至一个单独的反汇编器上的二进制运行。
这是地方去寻找一个解释。 由于编译器优化,呈现给所述处理器的实际代码可能具有不多相似性的C原码(但通常做相同的工作)。
通过几个汇编指令步进表示故障代码应揭示问题的原因。
我的猜测是,编译器以某种方式优化了整个计算SICE定义的常量是在编译时已知部分。 255 * X可以优化为x << 8-X(其为更快,更小) 也许有人与优化的汇编代码走错了。
我把编译我的系统上两个版本的时间。与活性优化,MSPGCC产生以下代码:
#define PRR_SCALE 255
uint8_t a = 3;
uint8_t b = 4;
uint8_t prr;
prr = (PRR_SCALE * a) / b;
40ce: 3c 40 fd ff mov #-3, r12 ;#0xfffd
40d2: 2a 42 mov #4, r10 ;r2 As==10
40d4: b0 12 fa 6f call __divmodhi4 ;#0x6ffa
40d8: 0f 4c mov r12, r15 ;
printf("prr: %u\n", prr);
40da: 7f f3 and.b #-1, r15 ;r3 As==11
40dc: 0f 12 push r15 ;
40de: 30 12 c0 40 push #16576 ;#0x40c0
40e2: b0 12 9c 67 call printf ;#0x679c
40e6: 21 52 add #4, r1 ;r2 As==10
我们可以看到,编译器直接计算的255 * 3至-3(0xfffd)的结果。这里是问题。不知怎的,255被解释为-1签署的255 16位无符号的8位来代替。或者,它被解析以8位,然后再符号扩展到16位。或任何。
在这个话题的讨论已经在已经在MSPGCC邮件列表开始。
我不知道为什么规定是不行的,但你可能会运行与uint8_t
变量翻车。 255是用于uint8_t (2^8 - 1)
的最大值,因此,如果乘上3,您一定碰到一些细微的翻转问题。
编译器可能会被优化代码,并预先计算你的数学表达式的结果和在PRR顺手把结果(因为它适合,即使中间值不适合)。
检查,如果你打破了你的表情像这样的(这不会表现得像你想要的)会发生什么:
prr = c * a; // rollover!
prr = prr / b;
您可能需要只使用一个更大的数据类型。
一个区别我能想到的情况下-1是
在PRR_SCALE文字值可能会进入ROM或代码区。并且可以有用于说,在MUL操作码一些差异,
case-1: [register], [rom]
case -2: [register], [register]
有可能没有意义的。