在main之前进行非平凡初始化的最优雅方法是什么?

时间:2011-10-23 22:42:54

标签: c initialization runtime

我并不是真正的C设计模式,所以我的疑问可能很简单(虽然有点具体)。 这个问题的实际应用难以解释,所以让我简化一下。

假设我有一个数组,我想在其中存储素数。此数组包含的素数由NUMBER_OF_PRIMES定义,unsigned primes[NUMBER_OF_PRIMES]; 是在编译时定义的常量。

因此,我们有:

unsigned primes[NUMBER_OF_PRIMES] = { 2, 3, 5, 7, ... };

如果大小是固定的,我可以预先计算素数并像往常一样初始化数组:

NUMBER_OF_PRIMES

但如果unsigned* primes = make_primes(NUMBER_OF_PRIMES); 大于200,那将是相当丑陋的。我想在运行时运行一个函数,但在main()之前,这将把那些素数放在那里。我当然可以这样做:

/* Header file */
unsigned primes[NUMBER_OF_PRIMES];

/* C file */
int nothing = initialize_primes(); /* This function would write the 
values to the array, using things that are not available in the
header (it is in the .c, so it can reference them), and return anything */

将分配必要的内存并在main之前运行。主要问题是:此数组将位于头文件中,但它的值将包含隐藏在相应.c文件中的内容。我认为我能做的是:

initialize_primes()

另一种方法显然是将init()放在头文件中,并要求用户在main函数内调用它(就像一些库'main()函数)。但是,我不想强​​制main()包含对此函数的调用。

我的问题是,是否有任何优雅的方式来做到普遍接受/使用,或者如果这是荒谬的,我应该要求init()调用{{1}}函数以避免不必要的,模糊的代码。

正如我所说,我不会处理与素数相关的任何事情;这只是一个同样挑战的例子。

最好的问候。

4 个答案:

答案 0 :(得分:7)

使用 init 功能是正确的方法。不要忘记添加 fini 功能,这样人们可以根据需要释放内存(不知道你的库有什么可能或者可能不重要但通常能够很好释放所有内存 - 例如,当可加载模块使用库时,它通常不希望在卸载时泄漏内存。)

答案 1 :(得分:5)

为什么需要它在main之前运行?我能想到的唯一原因是你不控制main(即,你只是一个有人会打电话的图书馆)。

在这种情况下,坚持让调用者调用您的初始化函数是完全可以的。这些API在他们的代码与您的代码之间形成契约,如果他们违反了合同,那么这不是您的责任。

如果您执行控制main,那么只需在开始时调用您自己的初始化函数。它不会需要更早发生。

如果您决定沿着静态数组路径走下去并且您不想输入所有素数(正如您所说,它们不是素数,但我假设它们是'可计算的),然后您可以编写一个单独的程序作为 build 工具的一部分(未与您的程序一起部署)。

它可以根据该常量预先计算值,并使用您可以编译和链接的数组创建一个C文件。这至少可以消除输入它们的苦差事。

这也会使计算成本远离用户,从而加快启动速度(假设成本非常重要)。

答案 2 :(得分:5)

你可以做的一件事是:

// primes.c
static unsigned *p = NULL;

const unsigned *primes(void)
{
    if(p == NULL)
      {
        p = malloc((PRIMES_COUNT + 1) * sizeof *p);
        *p++ = 0; // if user accidentally frees p now, it will segfault
        // populate p
        atexit(primes_cleanup);
      }
    return p;
}

void primes_cleanup(void)
{
    if(p)
      {
        free(p - 1); // correct way to free the pointer
        p = NULL;    // now calling _cleanup twice is fine
      }
}

用户使用primes()[N]获取所需元素,并保证初始化。即使您存储指针并在以后使用它,它也能保证正常工作(除非有人抛弃了const并在您不看时更改了列表)。如果您仍想要类似于变量的语法,请使用:

// primes.h
const unsigned *primes(void);
void primes_cleanup(void);
#define PRIMES (primes())

对最终用户来说,PRIMES看起来像任何其他变量。取胜。

答案 3 :(得分:1)

可能有一些讨厌的方法可以做到这一点,但我建议A)提供一个init函数,以便根据需要调用或B)如果它是一个静态数组大小,你可以离线编写一个程序来生成一个数组包括在内。这样,如果您需要更改它,您可以稍后重新生成它。但A)是更容易接受的方式