5

我正在考虑以下问题:我想用一个使用某种查找表的程序对一个微控制器(比如说一个 AVR 巨型类型)进行编程。

第一次尝试是在一个单独的文件中找到该表并使用任何其他脚本语言/程序/....创建它。在这种情况下,为 C 创建必要的源文件需要付出相当大的努力。

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

#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第三行中的,我会在程序集中获得数据。显然,所有信息在编译时都可用。

你能告诉我是否有办法实现我的意图:编译器计算尽可能多的离线值?还是使用外部脚本/程序/...来计算表条目并将它们添加到一个单独的文件中的最佳方法是#included?

4

3 回答 3

5

这里的一般问题是sin,根据 C 语言的规则,调用使得这个初始化事实上是非法的,因为它本身不是常量表达式,并且你正在初始化静态存储持续时间的数组,这需要它。这也解释了为什么您的阵列不在.data部分中。

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

常量表达式可以在翻译期间而不是运行时求值,因此可以在常量可能存在的任何地方使用。

常量表达式不应包含赋值、递增、递减、函数调用或逗号运算符,除非它们包含在未计算的子表达式中。115)

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

GCC 包括标准 C 库中许多函数的内置版本。即使您指定了选项,带有前缀的版本__builtin_也始终被视为与 C 库函数具有相同的含义。-fno-builtin

于 2015-01-22T16:24:18.623 回答
2

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

如果您仍然想这样做,我认为 C 预处理器不能直接完成,因为它没有迭代或递归的设施。

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

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

于 2015-01-22T16:34:57.013 回答
2

您正在尝试的不是 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 的代码,然后粘贴到输出中。如果您的代码是自包含的并且生成的输出仅在生成它的代码发生更改时才会更改,则效果很好。

于 2015-01-22T16:20:22.597 回答