这个C函数写得不好吗?

时间:2009-12-04 03:54:25

标签: c pointers embedded floating-point microcontroller

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;
  double x = fmod((double)value_to_convert, 10.0);
  char b = (char)x;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

此函数的目的是获取0到99的unsigned char值,并在0-9的情况下返回它的ascii等效值,或者操作一个小的全局字符数组,该数组可以从函数后面的调用代码中引用完成。

我问这个问题,因为来自同一供应商的两个编译器以不同的方式解释这个代码。

这段代码的编写方式是将通过RS485发送的地址字节解析成可以轻松传递给send-lcd-string函数的字符串。

此代码是为PIC18架构(8位uC)编写的。

问题在于特定编译器的免费/评估版本会生成完美的汇编代码,但在遭受性能损失的情况下仍能正常工作,但付费且假设优越的编译器会以更高效的方式生成代码,但代价是能够引用所有地址我的字节数组用来驱动我的液晶显示器上的图形。

我知道我通过使用专有编译器为一个不太典型的架构在水中投入大量泥浆,但我希望那里的人有一些建议。

感谢。

11 个答案:

答案 0 :(得分:7)

我绝对会避免在PIC上使用任何浮点数。而且我会 - 不要 - 使用任何分歧。你有多少次看到将非ascii字符发送到LCD?你可以将它保存到LCD的内存中,然后通过它的内存位置调用吗?

这是我的代码中除以10的结果,注意它需要完成的17个周期。想想需要多长时间,并确保没有其他等待。

61:                         q = d2 / 10;
 01520  90482E     mov.b [0x001c+10],0x0000
 01522  FB8000     ze 0x0000,0x0000
 01524  2000A2     mov.w #0xa,0x0004
 01526  090011     repeat #17
 01528  D88002     div.uw 0x0000,0x0004
 0152A  984F00     mov.b 0x0000,[0x001c+8]

如果在代码中执行任何浮点运算,请在编译后在符号选项卡上查看程序存储器(这样您就可以实际读取它)并查找需要的浮点代码包括在内。您会在_reset标签后很快(ish)找到它靠近顶部(取决于您的代码)。

我的行号为223,内存地址为001BC,_ floatsisf,继续通过几个附加标签(_fpack,_divsf3等),以_funpack结束,最后一行为535,内存地址为0042C。如果你可以处理(42C-1BC = 0x270 =)624字节的丢失程序空间,那很好,但有些芯片只有2k的空间而且这不是一个选择。

如果可能,请尝试在基数2中使用定点算法,而不是浮点数。

如果无法引用LCD中的所有字节数组,请检查以确保您没有尝试发送空(这是一个很好的地址),但是它会因代码检查而停止ascii字符串的结尾? (以前发生在我身上)。

答案 1 :(得分:4)

我可能会把它写成:

char byte_to_ascii(char value_to_convert, volatile char *converted_value)
{
 if (value_to_convert < 10) {
  return value_to_convert + '0';
 } else {
  converted_value[0] = (value_to_convert / 10) + '0';
  converted_value[1] = (value_to_convert % 10) + '0';
  return 0;
 }
}

答案 2 :(得分:4)

模数和整数除法可能非常昂贵。我不知道你的特定架构,但我猜它也很贵。

如果你需要,除法和模数,你可以做其中一个并通过乘法/差异得到另一个。

q =p/10;
r = p - q*10;

答案 3 :(得分:3)

转换为浮动,调用fmod和转换为整数,而不是仅仅使用%运算符,这是不好的形式?我会说是的。有更多可读方法来减慢程序以满足某些时序要求,例如在for循环中休眠。无论是什么编译器或汇编代码或其他任何调整,这是一种高度混淆的方式来控制程序的执行速度,我称之为糟糕的形式。

如果完美的汇编代码意味着它正常工作但它甚至比转换到浮点和返回更慢,那么使用整数并在for循环中休眠。

对于不完美的汇编代码,有什么问题? “以能够引用我所有字节数组的地址为代价”?看起来类型char *在您的代码中工作,所以您似乎可以按照C标准所说的方式处理所有字节数组。有什么问题?

答案 4 :(得分:2)

坦率地说,我会说是的。

如果您想要 b 作为剩余部分,请使用MOD或自己动手:

char a = value_to_convert / 10;
char b = value_to_convert - (10 * a);

转换为浮动或从浮动转换永远不是处理事情的方式,除非你的值确实是浮点数。

此外,我强烈建议坚持明确将您的数据类型称为“已签名”或“未签名”的惯例,并在实际 字符时保留裸'char' (字符串的一部分)。你传入原始数据,我觉得应该是一个unsigned char(当然,假设源 未签名!)。很容易忘记如果某些东西应该签名/未签名,并且使用裸字符,你将会遇到各种翻转错误。

大多数8位微处理器需要一个乘法(对于除法而言永远不会永远),所以尽量减少这些。

希望这会有所帮助..

答案 5 :(得分:1)

代码似乎做了两件非常不同的事情,这取决于它是否给出了0-9或10-99范围内的数字。出于这个原因,我会说这个函数写得很差:我会将你的函数分成两个函数。

答案 6 :(得分:1)

因为我们在这里讨论的是10分。

这是我的看法。它只是简单的操作,甚至不需要宽寄存器。

unsigned char divide_by_10 (unsigned char value)
{
  unsigned char q;
  q = (value>>1) + (value>>2);
  q += (q>>4);
  q >>= 3;
  value -= (q<<3)+q+q;
  return q+((value+6)>>4);
}

干杯,   尼尔斯

答案 7 :(得分:0)

如果您在内部进行搜索,优化者通常会不时地做不需要的事情。

您的converted_value是全局值还是以编译器知道不触摸它的方式分配?

答案 8 :(得分:0)

PIC不喜欢做指针运算。

正如Windows程序员指出的那样,使用mod运算符(见下文)。

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;  
  char b = value_TO_convert%10;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

答案 9 :(得分:0)

是的,我相信你的功能:
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

if (value_to_convert < 10) {
    return (value_to_convert + 48);
} else {

char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;
char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;

}
}
is in poor form:

Don't use decimal numbers for ASCII chars, use the character, i.e. '@' instead of 0x40.
There is no need for using the fmode function.

Here is my example:
// Assuming 8-bit octet
char value;
char largeValue;
value = value_to_convert / 100;
value += '0';
converted_value[0] = value;
largeValue = value_to_convert - value * 100;
value = largeValue / 10; value += '0';
converted_value[1] = value;
largeValue = largeValue - value * 10; value += '0';
converted_value[2] = value;
converted_value[3] = '\0'; // Null terminator.

Since there are only 3 digits, I decided to unroll the loop. There are no branches to interrupt the prefetching of instructions. No floating point exceptions, just integer arithmetic.


答案 10 :(得分:0)

只是成为一个nitwitt,但来自同一函数的多个return语句可被视为不良形式(MISRA)。

此外,上面的一些讨论是关于过早优化的限制。某些任务必须留给编译器。但是,在这种简约的嵌入式环境中,这些技巧仍然有效。