【问题标题】:static initialization in cc中的静态初始化
【发布时间】:2009-12-31 20:22:38
【问题描述】:

我有一个函数,它传递一个整数列表,直到一个值为“-1”并计算最小值。

如果函数被多次调用,它应该返回所有调用之间的最小值。

所以我写了这样的东西:

int min_call(int num, ...)
{
    va_list argptr;
    int number;

    va_start(argptr, num);

    //static int min = va_arg(argptr, int); //////// the questioned line

    static int all_min = -1;

    int min = va_arg(argptr, int);

    if (min != -1)
    {
        while ((number = va_arg(argptr, int)) != -1)
        {
            if (number < min)
            {
                min = number;
            }

        }
    }

    if (min < all_min || all_min == -1)
    {
        all_min = min;
    }

    return all_min;
}

我想知道有关标记线的一些信息...为什么我不能调用它 - 编译器说因为用于初始化静态 int 的表达式不是常量。

出于某种原因,我记得我可以初始化一个静态变量,并且知道初始化语句只会被调用一次(第一次),它是用 C++ 编写的。 如果该行可用,它将为我节省几个变量。

在这件事上C和C++有区别吗?

【问题讨论】:

  • 另一件事。我建议不要将 all_min 初始化为 -1,而是包含 并将 all_min 初始化为 INT_MIN。
  • 你的意思是INT_MAX,也请看我的回答。

标签: c++ c static


【解决方案1】:

是的,C++ 允许在运行时延迟初始化静态变量。有效地 C++ 将静态初始化变成这样:

static int XX_first_time = 1;
if (XX_first_time)
{
    // run the initializer
    XX_first_time = 0;
}

虽然这很方便,但它不是线程安全的。标准不要求这是线程安全的,尽管一些编译器已经这样做了(gcc 4.x 进行线程安全初始化,除非明确要求不要使用 -fno-threadsafe-statics)。

C 要求静态变量在编译时配置其值。是的,这更受限制,但更符合 C 在背后为你做的很少的工作(C 可以被认为是可移植的汇编)。

【讨论】:

  • 那不是“static int XX_first_time = 1;”吗? :-)(笑话)
  • @Martin - 哦!固定(不是开玩笑)。
  • “直到最近 C++ 编译器才开始以线程安全的方式处理这个问题”——事实上,他们甚至没有义务这样做(因为标准通常没有说明线程安全,并明确表示在其中的静态初始化程序仍在运行时再次调用相同的静态函数)。例如。 VC++ 不做线程安全的本地静态初始化。
  • 另外,样本扩展有点不正确。该标准要求,如果本地静态初始化程序抛出异常,则将该变量视为未初始化 - 即,在下一次进入函数时,应再次运行初始化程序。所以正确的展开应该是if (XX_first_time) { /*run initializer*/; XX_first_time=0; }。
  • @Pavel - 感谢您对细微错误的解释。我已经更新了我的示例。
【解决方案2】:

C 静态初始化必须使用常量值完成,即在编译时已知的东西。因为您对va_arg() 的调用要到运行时才能解决,所以您不能将其用作静态初始化程序。您可以在初始化时为 min 分配一个已知错误值,然后在变量初始化后简单地调用 va_arg()(John Dibling 的示例代码就是这样做的)。

另外,重要的是要注意静态初始化每个作用域(在本例中为您的函数)仅发生一次,因此即使您注释掉的行有效,它也只会执行一次,而不是每次调用函数时(正如我想你所期待的那样)。

【讨论】:

    【解决方案3】:

    正如其他人所说,在 C 中,static 变量必须使用 compile-time 常量进行初始化。如果省略初始值,则取为0。

    现在,关于您的函数中的逻辑。我觉得这个函数写得更清楚了(我没有改变函数的形式):

    #include <limits.h>
    
    int min_call(int num, ...)
    {
        va_list argptr;
        int number;
        static int min = INT_MAX;
    
        va_start(argptr, num);
    
        while ((number = va_arg(argptr, int)) != -1)
            if (number < min)
                min = number;
    
        va_end(argptr);
        return min;
    }
    

    如果num 也应包含在最小值的计算中,那么您将需要按照if (num &lt; min) min = num; 的行进行额外检查。

    我也很难理解if (min &lt; all_min || all_min == -1 ) 行。由于all_min 一开始就被初始化为-1,然后就再也没有被触及过,所以if 条件将永远为真。

    注意min 的初始化是INT_MAX 而不是INT_MIN,正如其中一位cmets 所说,我们必须调用va_end()。

    【讨论】:

    • 注意总是,只是第一次调用该函数。但我想我会使用 INT_MAX ...谢谢!
    • 我不知道您所说的“始终注意”是什么意思。假设您的意思是“不总是”,那指的是什么?
    • 啊,我明白了。我认为您根本不需要all_min,但看起来您无论如何都知道发生了什么。
    【解决方案4】:

    你不能做你想做的事,因为静态在程序开始运行之前就被初始化了——上下文中还不存在 argptr。你可以试试这样的:

    va_start( argptr, num );
    
    static bool  init = 0;
    static int min = -1;
    if( !init )
    {
       min = va_arg( argptr, int );
       init = true;
    }
    
    static int all_min = -1;
    

    【讨论】:

      【解决方案5】:

      静态不仅仅意味着它只在您调用它时第一次设置。这意味着它在编译时设置。编译器不知道如何在编译时调用您的 va_arg 函数。 (在您处于运行时并运行程序并传递参数之前,它没有任何意义。)

      (edit - 在 C 中。有人指出 C++ 是不同的。)

      【讨论】:

        【解决方案6】:

        这并不能完全回答您的问题,但其他答案已经做得很好了。

        但是,我认为您从错误的方向处理这个非常简单的问题。如果您希望您的函数具有可移植性和可重用性,您应该尽量减少它们的副作用。

        例如,您应该将状态数据保存在使用它的作用域的本地变量(或结构)中,而不是将状态数据保存在您的函数(邪恶)中。像这样:

        {
            int min_so_far;
        
            min_so_far = min_call(NULL, 4, 7, 2, -6, 3, -12);
            min_so_far = min_call(&min_so_far, -11, 5, 3, 8, -3);
        
            printf("%d\n", min_so_far); //should output -12
        }
        

        min_call 的第一个参数是int*,如果它作为NULL 传入,那么函数将忽略它。否则,每个参数都将与传入的 int 进行比较。

        这不是很方便,但肯定更安全,而且您可以在应用程序的多个地方使用此功能。考虑代码的可重用性是一个好习惯。

        【讨论】:

        • 您可能应该将 min_so_far 声明为 int* 或者在进行函数调用时传递 min_so_far 的地址。事实上,它可能会编译,但有严重的逻辑错误(和/或分段错误)。
        • 你说得对,对不起,我的工作让我在 PHP 领域漂浮了一段时间,我忘记了如何正确使用指针
        • 安全是指线程安全吗?为什么在我的函数中保持状态不好?
        猜你喜欢
        • 2023-03-31
        • 1970-01-01
        • 2012-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-12
        相关资源
        最近更新 更多