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

时间:2015-01-22 16:08:40

标签: c gcc pre-compilation

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

第一次尝试是将表定位在一个单独的文件中,并使用任何其他脚本语言/程序/创建它。在这种情况下,为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,我将获得程序集中的数据。显然,所有信息都可以在编译时获得。

你能否告诉我是否有办法实现我的目的:编译器计算的值与离线值一样多?或者是使用外部脚本/程序/ ...计算表条目并将其添加到仅#include d的单独文件的最佳方式?

3 个答案:

答案 0 :(得分:4)

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

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

  

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

     

常量表达式不得包含赋值,增量,   递减,函数调用或逗号运算符,除非它们是   包含在未评估的子表达式中.15)

然而,正如@ ShafikYaghmour的评论一样,GCC将用其内置对应方替换sin函数调用(除非存在-fno-builtin选项),这可能被视为常量表达式。根据{{​​3}}:

  

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

答案 1 :(得分: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的代码,并粘贴输出。如果您的代码是自包含的,那么生成的输出只有在生成它的代码发生变化时才会发生变化。

答案 2 :(得分:2)

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

如果您仍想这样做,我不认为C预处理器可以直接执行,因为它没有迭代或递归功能。

最有效的方法是用C或其他语言编写程序来打印表的C源,然后使用预处理器将该文件包含在程序中。如果您使用make之类的工具,则可以创建规则来生成表文件,并使.c文件依赖于该文件。

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