【问题标题】:How to reset static variables within a function如何在函数中重置静态变量
【发布时间】:2011-03-30 03:38:34
【问题描述】:

有没有办法重置函数中声明为静态的变量?目标是确保不会使用来自不相关调用的延迟值调用该函数。例如,我有一个对矩阵列进行操作的函数。

int foo(matrix *A, int colnum, int rownum){
static int whichColumn;
static int *v; //vector of length A->nrows 
   if (column != whichColumn){
    memset(v,0,size);
    whichColumn = which;
   } 
   //do other things
}

函数被调用 n 次,每列调用一次。这是“重新设置”静态变量的正确方法吗?是否有其他通用的重置静态变量的万无一失的方法?例如,我想确保如果调用是使用可能具有不同维度的新矩阵进行的,则向量 v 会被调整大小并归零等。似乎最简单的方法可能是使用 NULL 指针调用函数:

int foo(matrix *A, int colnum, int rownum){
static int whichColumn;
static int *v; //vector of length A->nrows 
   if (A == NULL){
    FREE(v);
    whichColumn = 0;
   } 
   //do other things
}

【问题讨论】:

  • 不要使用静态/全局变量。而是向函数传递一个指向整数的指针,调用者维护该整数以在调用之间保持状态。
  • 我刚刚遇到了类似的问题,我不得不重置我的静态变量,但只能在我的测试代码中。我的技巧是将输入指针设置为 NULL,在 func 中检查它,然后根据需要重置变量 - 有效地使用现有参数中的 NULL 值作为标志。有点臭,但很管用。
  • 具有静态成员变量的类可能是您理想的解决方案,但对于 C(无 OOP),静态结构可能是最接近的优雅解决方案。
  • 抱歉,更仔细地重新阅读您的初始帖子 - 我已经有效地提出了完全相同的解决方案,快速而肮脏 - 想不出更好的方法可以快速实施跨度>

标签: c static initialization reset


【解决方案1】:

你可以这样构建你的函数,如果你用零参数调用它,那么它将重置它的内部静态变量

这是一个例子:

int foo(matrix *A = NULL, int colnum = 0, int rownum = 0)
{
   static int whichColumn;
   static int *v; //vector of length A->nrows 

   if (A == NULL){
    FREE(v);
    whichColumn = 0;
   } 
   //do other things
}

你实际上只需要像这样调用函数来重置:

foo();  // internal values would then be reset

确保函数的所有参数都具有默认值,例如,如果您传递一个可选参数,则确保它具有 = boost::none 作为默认值

【讨论】:

    【解决方案2】:

    如果一个人需要一个“reset”方法来处理未知数量的函数或模块,一种方法有时会很有帮助,那就是有一个全局计数器来记录该 reset 方法被调用的次数,然后让每个函数或模块包含如下代码:

    extern unsigned long global_reset_count; void do_something(int 不管) { 静态的......这个,那个,另一个,等等......; 静态无符号长 my_reset_count; if (my_reset_count != global_reset_count) { my_reset_count = global_reset_count; ...初始化这个,那个,另一个,等等...... } }

    在一些多线程上下文中,如果静态变量的初始化可能依赖于一些全局变量,不妨将“if”替换为“while”;在这种情况下;在这种情况下也可能需要内存屏障,但具体要求会因操作环境而异。

    另外,在嵌入式系统中可能有用的另一种模式是有一个 modules_initialized 全局变量,该变量通过全局重置方法设置为 0,然后让每个模块以以下内容开头:

    if (!atomic_bit_test_and_set32(&modules_initialized, FOOBOZZ_MODULE_ID)) { ... 初始化模块 FOOBOZZ ... }

    这需要不超过 32 个模块 ID,并且需要以某种方式唯一分配它们,但有些系统可以很好地处理这个问题。例如,链接器可以允许定义从地址空间的地址 0-31 独立于任何其他地址的“数据段”;如果每个模块在该地址空间内声明一个单字节变量,则链接器可以为这些变量生成适当的地址。

    【讨论】:

      【解决方案3】:

      在将 C 模块导入 C++ 时,我看到使用的一种方法是用类包装器包围整个模块,并将函数内的所有静态变量替换为函数外唯一命名的“全局”变量。对于涉及多个源文件的项目,我不知道有什么好的方法可以达到类似的效果,但我想知道是否存在。我在 C 中有一些嵌入式系统代码,我通过在 VS2005 中添加一些 C++ 包装器来模拟这些代码。例如,我定义了 I/O 寄存器,以便像 TX1CON = 0x5C;会转化为 IOMAP(0x251).P = 0x5C; IOMAP 是一个属性,它将“将 0x5C 写入地址 0x251”发送到硬件模拟程序。这种方法效果很好,但我无法进行干净的重置。有什么想法吗?

      【讨论】:

      • 问题是你最终会得到一大堆更多的功能 == 代码膨胀。另外,您只是将责任推上阶梯,最终导致您进入“上帝”功能反模式。对于非 OOP 语言,我认为没有明显的“干净”解决方案
      • @Hiett:在我用 VS2005 中的 C++ 包装器描述的特定场景中,代码膨胀只发生在模拟上下文中。由于真实系统有 128K ROM 和 4K RAM,而仿真环境有千兆内存,而且由于真实系统运行“原始代码”的速度可能不到仿真环境的 1%,所以我担心的是在这样的环境中编写代码方式在真实系统上是有效的,而不考虑在模拟环境中是否有效。其他情况会有其他要求。
      【解决方案4】:

      我建议将其转换为结构并编写一个小型辅助函数来管理您正在尝试执行的操作的语义。如果请求适合其大小,它可以返回缓冲区,或者根据需要创建一个新缓冲区(并释放旧缓冲区)。

      【讨论】:

      • 哎呀,结构+辅助函数怎么样?
      • 谢谢 Jesse,正如我对 Borealid 的评论所示,我使用了 teo 方法的混合。 “重置”仍然由隐藏细节的功能管理。我不懂C++,所以包装类是ootq。
      【解决方案5】:

      改用幂等初始化函数和全局变量。

      例如:

      int foo;
      int *m = NULL;
      
      static void InitVars() {
          foo = 0;
          if (m != NULL) {
              free(m);
          }
          m = malloc(sizeof(int)*5);
          memset(m, 0, sizeof(int)*5);
      }
      

      如果你的初始化器真的是幂等的,你可以再次调用它来重置变量。

      如果您需要自动调用它,请像这样使用__attribute__((constructor))(对于 GCC):

      static void InitVars __attribute__((constructor)) ();
      

      但是,您应该注意,如果您需要这样做,您应该重新考虑使用函数内static 变量,而是使用传入的新变量,这些变量返回/写入并传递给后续相关调用。

      【讨论】:

      • 您好,感谢您的回答。我决定听从建议,不使用 *v 作为静态变量,而是只使用 whichColumn 作为统计变量。 v 现在是下面 Jesse 推荐的结构的一部分。通过 (column = -1) 重置统计变量并重新初始化工作向量。然后每次都会适当地更新它(列!= whichColumn)。您的其他建议听起来很有用,我将了解它。再次感谢。 sm
      • hmm - 这看起来有点冒险 - 不确定它是否是最强大的解决方案,这些全局变量到处存在
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多