C 在一行中进行多步计算是否更好?

C 在一行中进行多步计算是否更好?,c,embedded,C,Embedded,这个问题是专门针对嵌入式系统的C编译器的,但我相信它可以应用于一般的编程 在一行中编写多步数学表达式,而不是将它们分解为等效的单个操作,或者反之亦然,有什么好处吗 例如: int convert(int in){ return (int)(((long)in * 727) / 12) + 9; } 好或坏于: int convert(int in){ long out = (long)in; out *= 727; out /= 12; out += 9; return

这个问题是专门针对嵌入式系统的C编译器的,但我相信它可以应用于一般的编程

在一行中编写多步数学表达式,而不是将它们分解为等效的单个操作,或者反之亦然,有什么好处吗

例如:

int convert(int in){
  return (int)(((long)in * 727) / 12) + 9;
}
好或坏于:

int convert(int in){
  long out = (long)in;
  out *= 727;
  out /= 12;
  out += 9;
  return (int)out;
}
一般来说


我的直觉是,一行表达式可能更容易让编译器进行优化,但我不编写编译器,所以我不确定。

对于编译器,这两种方法没有区别


不过,对于试图理解代码的读者或调试代码的开发人员来说,具有重要中间值的公式是很有帮助的。

如今,编译器的性能非常好,因此,进行这种微优化几乎毫无意义。算法应该优化,而不是代码安排。做对别人来说更容易阅读和理解的事情

使用GCC第一级优化编译这两个代码会得到相同的程序集输出,因此它们严格等效:

如果在启用优化的情况下编译,则生成的代码中没有差异。我更喜欢循序渐进,因为它不太容易出错,而且在几个月后回到项目中时更容易理解

int convert0(int in){
  return (int)(((long)in * 727) / 12) + 9;
}

int convert1(int in){
  long out = (long)in;
  out *= 727;
  out /= 12;
  out += 9;
  return (int)out;
}

int convert2(int in){
  long out1 = (long)in;
  long out2 = out1 * 727;
  long out3 = out2 / 12;
  long out4 = out3 + 9;
  return (int)out4;
}
最好的办法就是自己检查一下。只需查看发出的程序集代码:

在一行中编写多步数学表达式,而不是将它们分解为等效的单个操作,或者反之亦然,有什么好处吗

对。一个是,如果您使用字符或短类型编写操作,则它们将始终使用int算术执行,从而允许更大的中间结果。相反,如果每个中间结果都分配给一个字符或短变量,则每个中间结果都会以C标准为无符号类型和实现为有符号类型定义的方式缩减为目标类型。1在中间值中有更多的“净空”通常是一种好处,但作业或强制转换可用于有意减少

第二种是C标准,它允许以比标称类型更高的精度执行浮点运算。例如,两个浮点操作数的乘法和另一个浮点操作数的加法可以像两个操作数一样执行,甚至像乘法中有无限精度一样执行。相反,赋值和强制转换要求将值转换为标称类型。这可能是一个好处,因为在为精确的IEEE-754语义编程时,它会减少舍入错误或损害

第三个原因是,单个表达式通常为编译器组织操作提供了更大的灵活性。在多个语句上中断操作会在它们之间引入序列点,从而限制编译器的优化。和其他语句一样,使用一个表达式来允许编译器优化可能是一个好处,或者使用多个语句来引入必要的排序也可能是一个好处

从注释和其他答案来看,这些都没有在检查示例代码时表现出来,但它们一般都存在

请注意,所有这些都会影响语义:好处或坏处在于有效地告诉编译器它是否可以使用更广泛的类型,它是否可以使用额外的精度,以及它是否必须对某些事情进行排序。就优化而言,使用一条语句或单独的语句并不是帮助现代编译器优化的有用方法。优秀的现代编译器以相当全面的方式分析程序语义,因此这些表达式的简单重组对他们帮助不大

脚注
1这个答案只讨论int大于char和short的常见情况。虽然这在现代C实现中是典型的,但C标准并不要求这样做。

稍加修改。对于没有任何优化级别的编译器,有一个区别。在第二种情况下,它必须在堆栈上分配和存储中间值。然而,一旦应用了优化级别,生成的二进制文件的结果应该是相同的:在这种情况下,第一个对我来说可读性更好。双方都同意。正如OP所要求的一般答案一样,我认为假设是可以的,他使用现代编译器,不手动降低默认优化级别,只是想用这段代码来说明他的观点。“他的中间价值观在这里确实没有意义。”阿列克斯洛普。不启用优化时,什么更有效?这个问题毫无意义。@0\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?OP没有说明使用了哪种编译器和哪种优化级别。有时候这是有道理的。。。例如,在开发过程中,必须调试代码,但
在某些领域,它仍然需要高效的代码。OP特别用embedded标记了这个问题godbolt链接非常有用,但我确实注意到程序集在-O0优化级别上并不是严格等效的,而且我的经验中不是每一个编译器都像gccYou在你的问题中所说的那样聪明,特别是关于嵌入式系统的C编译器,除了gcc或clang,您会使用什么-氧气用于任何严重的场合anyway@ShellCode有很多编译器,几乎针对每个主板/cpu供应商,比如ARC cpu的Synopsis编译器等。我使用的编译器和代码库太旧了,必须在视频游戏模拟器中运行。我不希望这样的事情比gcc更聪明——好吧,我不知道。我更习惯于供应商提供他们自己的gcc工具链,但没什么太疯狂的。只要看看这两种工具的机器代码,你就会发现它们之间是否存在差异。浮点代码中存在差异。C标准允许实现在计算表达式时使用额外的精度,但不能在赋值或强制转换中使用。