简单来说,有一种ANSI-C方法可以使函数成为常量表达式吗?
constexpr
那样表现的东西,并且不会在运行时解决。背景
我需要在没有浮点的嵌入式处理器中实现大量的数学运算,所以我在我的应用程序中使用了固定点。
我不喜欢在我的头文件中看到神秘的常量。
我的硬件需要几个浮点常量(例如130.7 microseconds
,0.2503 mJ
),我真的希望能够读取(和更改)我的常量,因为列出了部件数据表值。
在给定的时刻,我的硬件需要使用这个常量,例如,填写一个定时器重载值,并且,由于值是常量,我想有类似的东西:
// Header file.
static const int values_table[] =
{
_Time( 123.45 ), // 123.45 microseconds.
// ...
};
然后:
// Application source file.
int conv_to_timer( x ) { /* my calculations - all const. */ }
// ...
void my_code( void )
{
// ...
timer_reload = conv_to_timer( values_table[ index ] );
一种方法是让我的_Time( x )
宏来完成计时器值所需的所有计算,但它不灵活(即不能与某些外部相比),既不是便携式(不同的硬件也需要不同的计算)。
对优雅方法有任何建议吗?
答案 0 :(得分:5)
TL; DR:使用宏。
标准C没有C ++ constexpr
的直接模拟。最接近的是宏和内联函数,在这两者中,宏可以在某些方面使用内联函数不能使用 - 特别是,在C需要常量表达式的情况下不能使用内联函数(嗯...听起来类似对于你知道的任何C ++关键字?),但另一方面,宏可以扩展为合适的表达式。
constexpr
被引入C ++主要是为了提供宏的替代方法,其中需要一个非平凡但可编译时可计算的表达式。我相信它们也有一些优点,例如它们可以导致在编译时计算的值的类型,但那些似乎与您的特定情况无关。在C中,从未引入constexpr
,宏仍然是标准的方法。
您对使用宏的主要反对意见似乎主要与代码风格有关。您观察到C ++ constexpr
函数的主体可以在与“调用”不同的转换单元中,这显然对您有吸引力。但请注意,如果这是您的选项,那么在使用它们的翻译单元中,您仍然需要至少声明这些函数,因此您实际上并没有节省太多清洁。此外,尽管每个翻译单元必须包含它使用的每个宏的主体,但您仍然可以将宏定义分隔为单独的标题。
总的来说,我没有看到避免使用宏 - 如果可以的话 - 会真正获得任何东西。我倾向于认为C ++社区对宏的普遍厌恶与constexpr
的任何功能性好处一样大。如果碰巧你受到特定厌恶的折磨,那么你真的需要克服它以便在标准C中有效地编程。
答案 1 :(得分:2)
相信你的优化器,然后验证。
C ++ constexpr
是基于编译器已经进行的优化而设计的。如果你写:
int fac( int n ) {
int r = 1;
for (int i = 2; i < n; ++i)
r*=i;
return r;
}
然后做
printf("%d", fac(10));
在适度优化设置下的编译器将评估对fac
的调用,并将调用替换为fac(10)
的常量返回值。
constexpr
主要用于允许fac(10)
在语义上恒定的位置使用,例如C ++中数组的大小。所以在C ++中你可以这样做:
char buffer[fac(10)];
当且仅当fac(10)
为constexpr
时。 (C具有可变大小的数组)。
您可以将_Time
编写为宏,也可以将其编写为inline
函数并告诉编译器进行优化。然后检查生成的二进制文件以查看它是否已使用最终值存储数组。
宏也可以使用,但是宏有通常的调试问题(我不知道编译器会在你搞砸的时候给你很好的逐步执行),这使得它们不太适合复杂的代码。
理解C ++没有保证在编译时实际评估constexpr
函数。它只是允许您在其他上下文中使用它,大多数编译器都会为您预先计算它。