C语言:#定义的值弄乱了8位乘法。为什么?

C语言:#定义的值弄乱了8位乘法。为什么?,c,embedded,overflow,multiplication,msp430,C,Embedded,Overflow,Multiplication,Msp430,我有以下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平台编译器,对于一个名为的小型嵌入式操作系统),结果是0,而我预期是191。 (uint8_t类型定义为无符号字符) 如果我将其更改为: uint8_t a = 3; uint8_t b = 4; uin

我有以下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平台编译器,对于一个名为的小型嵌入式操作系统),结果是0,而我预期是191。 (uint8_t类型定义为无符号字符)

如果我将其更改为:

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

在Ubuntu框上使用gcc编译这个“正常”的简单版本会在这两种情况下打印正确的值

我不太清楚这是为什么。我可以通过预先将定义的值赋给变量来绕过它,但我不希望这样做


有人知道这是为什么吗?也许通过链接可以了解更多信息?

255将作为整数文本处理,并导致整个表达式基于int而不是基于无符号字符。第二种情况强制类型正确。尝试更改您的#定义,如下所示:

 #define PRR_SCALE ((uint8_t) 255)

我不确定define为什么不起作用,但您可能会遇到带有
uint8\u t
变量的滚动。255是uint8_t(2^8-1)的最大值,因此如果将其乘以3,则必然会遇到一些微妙的滚动问题

编译器可能正在优化代码,预先计算数学表达式的结果,并将结果放入prr中(因为它适合,即使中间值不适合)

检查如果像这样分解表达式会发生什么(这不会像您想要的那样):


您可能需要使用更大的数据类型。

简单的回答是:您的编译器有缺陷。(正如其他人所建议的,溢出没有问题。)

在这两种情况下,算法都是在
int
中完成的,保证长度至少为16位。在前一段代码中,这是因为
255
是一个
int
,在后一段代码中,这是因为


正如您所指出的,gcc正确地处理了这个问题。

我认为在案例1中的一个区别是

PRR_刻度文字值可能进入ROM或代码区。比如说,MUL操作代码可能会有一些不同

case-1: [register], [rom]
case -2: [register], [register]

它可能根本没有意义。

如果所讨论的编译器是mspgcc,它应该将编译程序的汇编程序列表与二进制/十六进制文件放在一起。其他编译器可能需要其他编译器标志才能执行此操作。或者甚至可以在二进制文件上运行一个单独的反汇编程序

这是寻找解释的地方。 由于编译器的优化,呈现给处理器的实际代码可能与原始C代码没有太大的相似性(但通常做相同的工作)

单步执行表示错误代码的少数汇编指令应该可以揭示问题的原因

我的猜测是编译器以某种方式优化了整个计算,因为定义的常量在编译时是已知的部分。
255*x可以优化为xDoesn不起作用,我已经尝试过了(好吧,我直接在计算行中进行了转换。我按照你的建议重试了。没有区别)。我都编写了示例代码,算术是用int完成的。这是由于整数提升,请参见第6.3.1.1节第2段。从技术上讲,c*a不会溢出。C标准规定无符号整数类型不能发生溢出。酷,这是有道理的。我想“滚动”是一个更好的术语。我当然希望两者都能打印191。在第二种情况下,第一个c和a独立提升为int,因此它们的乘法不会溢出。在第一种情况下也会发生同样的情况(尽管在那里,PRR_比例已经是int-但这也不会改变a到int的提升)。框上的gcc运行正常。请检查是否包含标题stdio.h。我知道msp430的一个编译器确实允许隐式函数声明发生:如果是这种情况,对printf的调用将导致未定义的行为,因此将解释“0”结果。只要我的两分钱。不要认为这值得回答:)只是出于好奇:如果你将PRR_比例设置为255U会发生什么?@Jochen:打印的结果是255!?我不知道为什么(再次)…msp430的mul代码使用乘法仿真代码(没有硬件支持)iirc。因此,我怀疑很可能存在一些bug(尽管这是一个相当C前端的问题)。作为修复,尝试(+PRR_SCALE*+a)/+b;并添加“+”将手动升级它们的注释。也许这会有帮助?“这个算法是用int完成的,保证至少有16位长”——你能提供一个源代码吗?我质疑它的有效性…“算术是在int中完成的”:积分提升的结果。“[int]保证至少16位长”:间接保证,int_MAX必须至少为32767,int_MIN最多为-32767(请参阅标准中关于limits.h的部分)。+1,这是我调试一点后所期望的。您能否提供一个链接,指定如何将值转换回?或者它只是模运算?+1还有什么:)顺便说一下,如果一个二元运算符面对两个操作数,而这个家伙想将它们转换为一种类型,那么所做的转换称为“普通算术转换”。在该公司完成的转换中,您提到的是整体促销。在这两种情况下都会发生这种情况,事实上。JaredPar,如果转换的源类型或目标类型是有符号的,那么只有当值可以在目标类型中表示时,才会定义行为。对于无符号类型,使用模运算。
case-1: [register], [rom]
case -2: [register], [register]
#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