首页 文章

通过计算预先计算变量与初始化的定义

提问于
浏览
4

例如,假设我想使用函数初始化我的变量:

int x[10];
void init_x(){
    for(int i=0; i<10; ++i){
        x[i]=i*i;
    }
}

它不一定是这个确切的函数,它可能更复杂并且在更大的数组或不同的int类型上完成,但无论如何,重点是结果是确定性的 . 我的问题是:它会更好(例如,我的程序每次都会更快地初始化),只是事先计算出它的结果并直接定义它吗?

int x[10]={0, 1, 4, 9, etc...}

这样,我只运行一次初始化函数(例如运行函数,然后将结果粘贴到数组定义并注释掉代码),而不是每次打开程序时都反复 . (至少这就是我的假设)

这样做有什么不利之处吗?

5 回答

  • 2

    在其他条件相同的情况下,人力工作比cpu时间或磁盘空间更昂贵 . 做任何事情都需要最少的前期和持续的人力资源 . 制作复杂的多阶段构建过程可能会节省一点cpu或磁盘,但这会花费很多精力 .

  • 2

    正如其他人所提到的,这取决于生成相关变量所需的时间 . 如果需要大量的运行时间,那么预先计算它是有意义的 .

    以下是如何完成此操作的示例:

    genx.c(预先计算数组的程序):

    #include <stdio.h>
    
    int main()
    {
        int i;
        printf("int x[] = {");
        for(i=0; i<10; ++i){
            if (i) printf(",");
            printf(" %d", i*i);
        }
        printf(" };\n");
        return 0;
    }
    

    运行时,输出:

    int x[] = { 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 };
    

    makefile:

    CFLAGS=-Wall -Wextra
    
    app: app.c x.c
            gcc $(CFLAGS) -o app app.c
    
    x.c: genx
            ./genx > x.c
    
    genx: genx.c
            gcc $(CFLAGS) -o genx genx.c
    
    clean:
            rm -f app genx x.c
    

    app.c(应用程序文件):

    #include <stdio.h>
    
    #include "x.c"
    
    int main()
    {
        int i;
        for (i=0;i<10;i++) {
            printf("x[%d]=%d\n",i,x[i]);
        }
        return 0;
    }
    

    当您运行 make app 时,它会看到 x.c 是一个依赖项,并首先运行该目标 . x.c 目标是通过运行 genx 构建的, genx 本身由 genx 目标编译 .

    假设 genx.c 没有改变, x.c 只生成一次,并且在必要时包含其内容 .

    输出 app

    x[0]=0
    x[1]=1
    x[2]=4
    x[3]=9
    x[4]=16
    x[5]=25
    x[6]=36
    x[7]=49
    x[8]=64
    x[9]=81
    
  • 1

    我的问题是:它会更好(例如,我的程序每次都会更快地初始化),只是事先计算出这个结果并直接定义它吗?

    肯定会更快 . 它是否足够重要,取决于计算的复杂程度以及初始化变量的频率 .

    如果您决定使用硬编码数字初始化数组,我强烈建议您在评论中添加注释或代码,以解释您如何获得数字 . 否则,这将是一个维护头痛 .

    这样做有什么不利之处吗?

    我不会对这些数字进行严格的编码,除非有支持证据证明通过这种方式可以节省大量资金 .

  • 0

    初始化的数组必须存储在可执行文件中并从磁盘加载 . 如果计算很简单,那么处理器可以比从磁盘读取数据更快地进行计算 . 这意味着将初始化数据放在可执行文件中可能会导致膨胀的可执行文件开始变慢,这是一种双输的情况 .

  • 3

    如果我正确地理解了你的问题,你会问你是否可以在编译时而不是在运行时进行计算,是否有警告?

    答案取决于计算的复杂程度 . 如果它们很简单(如你所说的那样确定),你通常可以成功地做到这一点 . 需要注意的是,执行计算的代码可能不容易阅读,并且可以大大增加编译时间 .

    这种技术的泛化称为meta-programming,您可以在通常的代码 - >二进制转换之前添加一个额外级别的代码转换(编译) .

    您可以使用预处理器进行有限形式的操作 . GCC还支持一些静态评估的表达式 . 其他技术包括使用X-Macros基本上实现C中的参数模板 .

    有些库能够使用预处理器(P99 for instance)在编译时执行图灵完备计算 . 通常,语法是多毛的,有许多惯例和许多成语,在 生产环境 之前要学习 .

    与复杂的元编程相比,我在使用例如代码生成代码时,通过维护代码的同事实现了更高的代码清晰度和赞赏度 . 一个Perl或Python脚本,比我和预处理器一起攻击的东西 .

    EDIT:

    为了回答你的问题,我会告诉你,我专门为微控制器编写了大量的C代码,具有4-16kb RAM和16-128kb闪存代码空间 . 大多数应用程序至少存在十年,并且需要运行更新和功能添加 . 这意味着我必须小心不要浪费资源,所以如果可以在编译时而不是在运行时计算某些东西,我总是更喜欢 . 这节省了代码空间,但代价是增加了构建系统的复杂性 . 如果数据是恒定的,这也意味着我可以将它放在只读闪存中并节省宝贵的RAM .

    另一个例子是aes-min project,这是一个小的实现AES128 . 我认为有一个构建选择,所以算法中的一个组件(S-box?)得到预先计算并放入ROM而不是RAM . 其他对称加密算法需要从密钥计算一些数据,如果密钥是静态的,则可以有效地使用这种预计算技术 .

相关问题