如何让 GCC 在编译时评估函数?

How to make GCC evaluate functions at compile time?

我正在考虑以下问题:我想用一个使用某种查找 tables.

的程序对微控制器(比如说 AVR mega 类型)进行编程

第一次尝试是在单独的文件中找到 table 并使用任何其他脚本 language/program/.... 创建它。在这种情况下,创建C 的必要源文件。

我现在的想法是使用预处理器和编译器来处理事情。我试图用正弦值的 table 来实现这个(仅作为示例):

#include <avr/io.h>
#include <math.h>

#define S1(i,n) ((uint8_t) sin(M_PI*(i)/n*255))
#define S4(i,n) S1(i,n), S1(i+1,n), S1(i+2,n), S1(i+3,n)

uint8_t lut[] = {S4(0,4)};

void main()
{
    uint8_t val, i;

    for(i=0; i<4; i++)
    {
        val = lut[i];
    }
}

如果我编译此代码,我会收到有关 sin 函数的警告。在程序集中,.data 部分中没有任何内容。如果我只是删除第三行中的 sin,我会在程序集中获取数据。显然所有信息在编译时都是可用的。

你能告诉我是否有办法实现我的意图:编译器计算尽可能多的离线值?或者是使用外部 script/program/... 计算 table 条目并将它们添加到单独的文件中的最佳方法,该文件将只是 #included?

您尝试的不是 C 语言的一部分。在这种情况下,我按照这种模式编写了代码:

#if GENERATE_SOURCECODE
int main (void)
{
    ... Code that uses printf to write C code to stdout
}
#else
    // Source code generated by the code above
    ... Here I paste in what the code above generated

    // The rest of the program
#endif

每次需要更改时,您 运行 定义了 GENERATE_SOURCECODE 的代码,然后粘贴到输出中。如果您的代码是自包含的并且生成的输出仅在生成它的代码发生变化时才发生变化,则效果很好。

这里的一般问题是 sin 调用使这个初始化实际上是非法的,根据 C 语言的规则,因为它不是 constant expression 本身而且你'正在初始化 静态存储持续时间 的数组,这需要这样做。这也解释了为什么您的数组不在 .data 部分中。

C11 (N1570) §6.6/2,3 常量表达式(强调我的)

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.115)

然而,正如@ShafikYaghmour 的评论所述,GCC 将用其内置的对应函数替换 sin 函数调用(除非存在 -fno-builtin 选项),这很可能被视为常量表达式。根据6.57 Other Built-in Functions Provided by GCC:

GCC includes built-in versions of many of the functions in the standard C library. The versions prefixed with __builtin_ are always treated as having the same meaning as the C library function even if you specify the -fno-builtin option.

首先,不用说,您应该评估(可能通过实验)这是否值得做。您的查找 table 会增加您的数据大小和程序员的工作量,但可能会也可能不会提供您需要的运行时速度提升。

如果你还想做,我认为 C 预处理器不能直接做,因为它没有迭代或递归的便利。

解决此问题的最可靠方法是用 C 或其他语言编写程序来打印 table 的 C 源代码,然后使用预处理器将该文件包含在您的程序中。如果您使用像 make 这样的工具,您可以创建一个规则来生成 table 文件并让您的 .c 文件依赖于该文件。

另一方面,如果您确定永远不会更改此 table,您可以编写一个程序来生成它一次并粘贴进去。