这个 C 函数是否写得不好?

发布于 2024-08-13 15:13:48 字数 802 浏览 3 评论 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;
 }
}

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

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

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

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

问题在于,特定编译器的免费/评估版本会生成完美的汇编代码,该代码可以在性能受到影响的情况下工作,但是付费且据称更高级的编译器可以更有效地生成代码,但代价是无法引用我所有字节数组的地址用于驱动液晶显示器上的图形。

我知道使用专有编译器来构建非典型的架构是徒劳的,但我希望有人能提出一些建议。

谢谢。

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;
 }
}

The purpose of this function is to take an unsigned char value of 0 through 99 and return either it's ascii equivalent in the case it is 0-9 or manipulate a small global character array that can be referenced from the calling code following function completion.

I ask this question because two compilers from the same vendor interpret this code in different ways.

This code was written as a way to parse address bytes sent via RS485 into strings that can easily be passed to a send-lcd-string function.

This code is written for the PIC18 architecture (8 bit uC).

The problem is that the free/evaluation version of a particular compiler generates perfect assembly code that works while suffering a performance hit, but the paid and supposedly superior compiler generates code more efficiently at the expense of being able reference the addresses of all my byte arrays used to drive the graphics on my lcd display.

I know I'm putting lots of mud in the water by using a proprietary compiler for a less than typical architecture, but I hope someone out there has some suggestions.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

滿滿的愛 2024-08-20 15:13:48

我绝对会避免在 PIC 上使用浮点任何东西。我会 - 尽量不 - 使用任何划分。您有多少次看到向 LCD 发送非 ASCII 字符?你能把它保存到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 标签之后不久(大约)在顶部附近找到它(取决于您的代码)。

我的从第 223 行和带有 _ floatsisf 的内存地址 001BC 开始,继续通过几个附加标签(_fpack、_divsf3 等)并以 _funpack 结束,最后一行位于 535 和内存地址 0042C。如果您可以处理 (42C-1BC = 0x270 =) 624 字节丢失的程序空间,那就太好了,但有些芯片只有 2k 的空间,这不是一个选择。

如果可能的话,尝试使用以 2 为基数的定点算术,而不是浮点。

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

I would definitely avoid using floating point anything on a PIC. And I would -try not to- use any divisions. How many times do you see sending a non-ascii char to the LCD? Can you save it to the LCD's memory and then call it by it's memory position?

Here's what a divide by 10 looks like in my code, note the 17 cycles it needs to complete. Think about how long that will take, and make sure there is nothing else waiting on this.

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]

If you do a floating point anything in your code, look in the program memory after you've compiled it, on the Symbolic tab (so you can actually read it) and look for the floating point code that will need to be included. You'll find it up near the top (depending on your code), soon(ish) after the _reset label.

Mine starts at line number 223 and memory address of 001BC with _ floatsisf, continues through several additional labels (_fpack, _divsf3, etc) and ends in _funpack, last line at 535 and memory address 0042C. If you can handle (42C-1BC = 0x270 =) 624 bytes of lost program space, great, but some chips have just 2k of space and that's not an option.

Instead of floating point, if it's possible, try to use fixed point arithmetic, in base 2.

As far as not being able to reference all the byte arrays in your LCD, have you checked to make sure that you're not trying to send a null (which is a fine address) but it get's stopped by code checking for the end of an ascii string? (it's happened to me before).

望她远 2024-08-20 15:13:48

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

如果除法和取模两者都需要,则执行其中一项,然后通过乘法/差分得到另一项。

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

modulo and integer division can be very very expensive. I have do not know about your particular architecture, but my guess it is expensive there as well.

If you need both, division and modulo, do one of them and get the other one by multiplication/difference.

q =p/10;
r = p - q*10;
神回复 2024-08-20 15:13:48

我可能会写成:

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;
 }
}

I'd probably write that as:

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;
 }
}
夏末染殇 2024-08-20 15:13:48

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

如果完美的汇编代码意味着它可以正常工作,但比浮点和浮点转换还要慢,那么请使用整数并在 for 循环中休眠。

至于汇编代码不完善,问题出在哪里呢? “以能够引用我所有字节数组的地址为代价”?看起来 char* 类型正在您的代码中工作,因此您似乎可以按照 C 标准规定的方式对所有字节数组进行寻址。有什么问题吗?

Is it poor form to convert to floating, call fmod, and convert to integer, instead of just using the % operator? I would say yes. There are more readable ways to slow down a program to meet some timing requirement, for example sleeping in a for loop. No matter what compiler or what tweaking of assembly code or whatever else, this is a highly obfuscated way to control the execution speed of your program, and I call it poor form.

If perfect assembly code means that it works right but it's even slower than the conversions to floating point and back, then use integers and sleep in a for loop.

As for the imperfect assembly code, what's the problem? "at the expense of being able reference the addresses of all my byte arrays"? It looks like type char* is working in your code, so it seems that you can address all your byte arrays the way the C standard says you can. What's the problem?

如痴如狂 2024-08-20 15:13:48

坦率地说,我会说是的..

如果您希望 b 成为余数,请使用 MOD 或自行推出:

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

与浮点数的转换永远不是做事的方法,除非您的价值观真的是花车。

此外,我强烈建议坚持将数据类型显式引用为“有符号”或“无符号”的约定,并保留裸露的“字符”,因为它实际上是一个字符(一个字符串)。您正在传递原始数据,我认为它应该是一个无符号字符(当然假设源无符号的!)。很容易忘记某些内容是否应该签名/未签名,并且使用裸字符,您会遇到各种翻转错误。

大多数 8 位微控制器需要很长时间才能执行乘法(而执行除法则需要更长的时间),因此请尝试尽量减少这些时间。

希望这有帮助..

Frankly, I would say yes..

If you wanted b to be the remainder, either use MOD or roll-your-own:

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

Conversion to/from floats is never the way to do things, unless your values really are floats.

Furthermore, I would strongly recommend to stick to the convention of explicitly referring to your datatypes as 'signed' or 'unsigned', and leave the bare 'char' for when it actually is a character (part of a string). You are passing in raw data, which I feel should be an unsigned char (assuming of course, that the source is unsigned!). It is easy to forget if something should be signed/unsigned, and with a bare char, you'll get all sorts of roll-over errors.

Most 8-bit micros take forever for a multiply (and more than forever for a divide), so try and minimise these.

Hope this helps..

獨角戲 2024-08-20 15:13:48

该代码似乎在做两件截然不同的事情,具体取决于它是否给出了 0-9 或 10-99 范围内的数字。因此,我想说这个函数的编写方式很糟糕:我会将您的函数分成两个函数。

The code seems to be doing two very different things, depending on whether it's given a number in the range 0-9 or 10-99. For that reason, I would say that this function is written in poor form: I would split your function into two functions.

冷月断魂刀 2024-08-20 15:13:48

因为我们在这里讨论除以 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);
}

干杯,
尼尔斯

Since we're discussing divisions by 10 here..

This is my take. It only simple operations and does not even need wide registers.

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);
}

Cheers,
Nils

回忆凄美了谁 2024-08-20 15:13:48

如果您仔细研究内部结构,优化器通常会不时地做一些不需要的事情。

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

It is typical for optimizers to do unwanted thingies from time to time if you poke around in the internals.

Is your converted_value a global value or otherwise assigned in such a fashion that the compiler knows not to touch it?

酷炫老祖宗 2024-08-20 15:13:48

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;
 }
}

PIC's don't like doing pointer arithmetic.

As Windows programmer points out, use the mod operator (see below.)

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;
 }
}
把昨日还给我 2024-08-20 15:13:48

是的,我相信你的功能:
<代码>
char byte_to_ascii(char value_to_convert, 易失性 char *converted_value) {

<代码>
if (要转换的值 < 10) {
    return (value_to_convert + 48);
} 否则{

char a = value_to_convert / 10;
双 x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*转换值 = a;
*(转换值+1) = b;
返回 0;

<代码> }
}

状态不佳:

不要对 ASCII 字符使用十进制数字,而使用字符,即“@”而不是 0x40。

无需使用 fmode 函数。

这是我的例子:
// 假设 8 位八位字节
<代码>
字符值;
字符大值;
值 = 要转换的值 / 100;
值 += '0';
转换值[0] = 值;
LargeValue = value_to_convert - 值 * 100;
值=大值/10;
值 += '0';
转换值[1] = 值;
大值 = 大值 - 值 * 10;
值 += '0';
转换值[2] = 值;
转换值[3] = '\0'; // 空终止符。

由于只有 3 位数字,我决定展开循环。没有分支可以中断指令的预取。没有浮点异常,只有整数运算。

如果您以空格而不是零开头,您可以尝试以下操作:
值=(值==0)? ' ' : 值 + '0';

Yes, I believe that your function:

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;

}
}

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.

If you leading spaces instead of zeros, you can try this:
value = (value == 0) ? ' ' : value + '0';

梦醒时光 2024-08-20 15:13:48

只是说个小白,但来自同一函数的多个 return 语句可能被认为是不好的形式 (MISRA)。

此外,上面的一些讨论是关于永久优化的限制。有些任务必须留给编译器。然而,在这样一个简约的嵌入式环境中,这些技巧可能仍然有效。

Just to be a nitwitt, but multiple return statements from the same function can be considered bad form (MISRA).

Also, some of the discussions above are on the limit of permature optimizations. Some tasks must be left to the compiler. However, in such a minimalistic embedded environment, these tricks may be valid still.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文