在 main 之前进行重要初始化的最优雅的方法是什么?

发布于 2024-12-11 15:36:24 字数 1212 浏览 0 评论 0原文

我不太喜欢 C 设计模式,所以我的疑问可能很简单(尽管有点具体)。 这个问题的实际应用比较难解释,所以让我简化一下。

假设我有一个数组,我想在其中存储素数。该数组包含的素数数量由 NUMBER_OF_PRIMES 定义,它是编译时定义的常量。

因此,我们有:

unsigned primes[NUMBER_OF_PRIMES];

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

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

但是如果 NUMBER_OF_PRIMES 大于 200,那就相当难看了。想要某种方式在运行时运行函数,但在 main() 之前,会将这些素数放在那里。我当然可以这样做:

unsigned* primes = make_primes(NUMBER_OF_PRIMES);

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

/* 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 */

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

我的问题是是否有任何优雅的方法可以被普遍接受/使用,或者如果这很荒谬,我应该要求 main() 调用 init() 函数以避免不必要的、晦涩的代码。

正如我所说,我不从事任何与素数相关的工作;这只是面临同样挑战的一个例子。

此致。

I'm not really into C design patterns, so my doubt is potentially simple (although a little specific).
The real application of this question is harder to explain, so let me simplify it.

Suppose I have an array, in which I want to store prime numbers. The number of primes this array contains is defined by NUMBER_OF_PRIMES, a constant defined at compile time.

Thus, we have:

unsigned primes[NUMBER_OF_PRIMES];

If the size was fixed, I could pre-compute the primes and initialize the array as usual:

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

But that would be rather ugly if NUMBER_OF_PRIMES were, let's say, greater than 200. I want some way to run a function at runtime, but before main(), that will put those primes numbers there. Of course I could do:

unsigned* primes = make_primes(NUMBER_OF_PRIMES);

which would allocate the necessary memory and run before main. The main problem is: this array would be in a header file, but it's value would contain something that's hidden inside the corresponding .c file. what I thought I could do is:

/* 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 */

Another way is obviously putting initialize_primes() in the header file, and asking the user to call it inside the main function (like some libraries' init() function). However, I'd like not to force main() to contain a call to this function.

My question is if there is any elegant way to do this that is universally accepted/used, or if this is ridiculous and I should ask main() to call an init() function to avoid unnecessary, obscure code.

As I said, I'm not working with anything related to primes; this is just an example with the same challenge.

Best regards.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

猫瑾少女 2024-12-18 15:36:24

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

Using an init function is the proper way to go. Don't forget to add a fini function, too, so people can release the memory if they want to (without knowing what your library does it might or might not be important but generally it's good to be able to free all memory - for example, when a library is used by a loadable module it usually doesn't want to leak memory when it's unloaded).

白芷 2024-12-18 15:36:24

您可以做的一个技巧是:

// 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 将看起来像任何其他变量。赢。

One trick that you can do is this:

// 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
      }
}

The user uses primes()[N] to get the desired element, and it's guaranteed to be initialized. Even if you store the pointer and use it later, it's guaranteed to work (unless someone cast away the const and changed your list when you weren't looking). If you still want variable-like syntax, use this:

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

To the end-user, PRIMES will look like any other variable. Win.

十二 2024-12-18 15:36:24

为什么需要它在 main 之前运行?我能想到的唯一原因是如果您不控制 main (即,您只是一个有人会调用的库)。

在这种情况下,坚持要求调用者调用您的初始化函数是完全可以的。 API 在他们的代码和您的代码之间形成了契约,如果他们违反了该契约,则不是您的责任。

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

如果您决定采用静态数组路线,并且不想输入所有素数(正如您所说,它们不是素数,但我假设它们是可计算的),那么您可以编写一个单独的程序作为您的构建工具的一部分(不与您的程序一起部署)。

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

这也将计算成本从用户身上移开,从而加快启动速度(假设成本不小)。

Why do you need it to run before main? The only reason I can think of is if you don't control main (ie, you're just a library that someone will call).

In that case, it's perfectly okay to insist that the caller call your initialisation function. The APIs form a contract between their code and yours and, if they break that contract, it's not your responsibility.

If you do control main then just call your own initialisation function right at the start. It doesn't need to happen any earlier.

If you decide to go down the static array route and you don't want to type in all the primes (as you state, they're not primes but I'm assuming they're computable), then you could write a separate program as part of your build tools (not deployed with your program).

It could pre-calculate the values based upon that constant and create a C file with the array that you could then compile and link to. That would at least remove the drudgery of typing them in.

That also moves the cost of calculation away from the user, resulting in faster startup (assuming the cost is non-trivial).

镜花水月 2024-12-18 15:36:24

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

There may be some nasty ways to do this, however i would suggest A) provide an init function to be called as required or B) if its a static array size, you could write a program offline to generate the array that is included. That way you can regenerate it later on if you need to change it. But A) is the more accepted way to do this

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文