Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用整数表达式边界编写编译器绑定的检查数组(最佳实践) 我是用C++编写的一个编译器(C++ 98),我在寻找反馈/想法,如何处理下面的情况(参见语法的数组的标题):< /P>_C++_Arrays_Compiler Construction_Compiler Warnings - Fatal编程技术网

使用整数表达式边界编写编译器绑定的检查数组(最佳实践) 我是用C++编写的一个编译器(C++ 98),我在寻找反馈/想法,如何处理下面的情况(参见语法的数组的标题):< /P>

使用整数表达式边界编写编译器绑定的检查数组(最佳实践) 我是用C++编写的一个编译器(C++ 98),我在寻找反馈/想法,如何处理下面的情况(参见语法的数组的标题):< /P>,c++,arrays,compiler-construction,compiler-warnings,C++,Arrays,Compiler Construction,Compiler Warnings,绑定检查、运行时堆栈扩展(以及离开作用域时的收缩)等都可以在我的框架中很好地处理 我的问题涉及在使用未初始化变量的情况下发出警告,如上文(1)所示。如果a是一个在初始化前使用的整数变量,我允许它,但会发出警告消息。因为我想让编译器准备好以后可能有一个链接器,向后计算b和m并实际检查a的索引是否已初始化是不可能的(例如,在最常见的情况下,可以在不同的文件中定义一个变量)。如前所述,我知道如何发出代码在运行时执行绑定检查;但是 当一个变量未初始化使用到形式为a[(expr)]的情况时,发出警告的最佳

绑定检查、运行时堆栈扩展(以及离开作用域时的收缩)等都可以在我的框架中很好地处理

我的问题涉及在使用未初始化变量的情况下发出警告,如上文(1)所示。如果a是一个在初始化前使用的整数变量,我允许它,但会发出警告消息。因为我想让编译器准备好以后可能有一个链接器,向后计算b和m并实际检查a的索引是否已初始化是不可能的(例如,在最常见的情况下,可以在不同的文件中定义一个变量)。如前所述,我知道如何发出代码在运行时执行绑定检查;但是

当一个变量未初始化使用到形式为a[(expr)]的情况时,发出警告的最佳实践方式是什么?由于(expr)不必是整数值(仅仅是一个数字;它只需要是整数类型),如果不计算(expr)(我不想像上面所说的那样进行计算),我就不能保留(比如)带有标记为initialized的项的阴影数组。在(gcc)C中,这种情况被简单地忽略:发出一个警告,表明b和m都未初始化,但没有一个警告与使用未初始化的变量a[b*m]有关。这显然符合C对数组的看法,在C中(但不是我正在研究的语言,它没有指针的概念),表达式定义得很好,除了b和m没有初始化,并且访问(可能)越界(即将发生的堆栈溢出)


最好的做法是不检查[(expr)]在使用前是否已初始化;发射代码;并等待运行时错误(如果有)?或者…?

我认为尝试初始化检查一切会导致痛苦

考虑当
a[b*m]
的元素被有条件地初始化时会发生什么,即
a
的元素被初始化取决于输入参数。您不仅要跟踪“影子数组副本”中的初始化位,还要跟踪整个条件执行图,以确保覆盖所有执行路径。最终,这在图灵机器上是一个无法确定的问题,甚至;要解决这个问题,您必须决定(判断执行图的任何特定子图是否完成执行)


您可以使用一些启发式方法仅对未初始化的情况的某些子集发出警告,但这只意味着您的编译器有时会发出警告,而不是其他时候。作为一个程序员,你应该知道这种看似不确定的行为是多么令人恼火。

几乎不可能在每种情况下都能说出,例如,考虑一下:

void foo(int &x, int y)
{
   switch(y)
   {
      case 1:
         x = 11;
         break;
      case 2:
         x = 42;
         break;
      ...  // numbers 3-9 elided for brevity
      case 10:
         x = 97;
         break;
   }
}

int bar(int z)
{
    int a;
    foo(a, z);
}
a
是否已初始化?这取决于
z
的值。如果您有所有可用的代码,至少在理论上,您可以按照所有路径查看
z
将有哪些可能的值(当然,假设,
z
不是来自外部源的输入——在这种情况下,如果不进行范围检查,则可能是写得不好的代码,但很多代码都会欣然接受输入为“ok”)

因此,您必须选择两条路线中的一条:

  • 选择假设“似乎已初始化”的变量确实已初始化,并且仅在确定某些内容未初始化时发出警告

  • 选择假设“合理初始化”的变量确实没有初始化,并给出警告

  • GCC确实,至少有时会在您可以的情况下发出警告,因为人类推断它不可能未初始化(因为它总是采用多条路径中的一条)。我们有时会发现这一点,在这种情况下,某段代码将在一个配置中编译得很好(优化级别较低)并且在更高的优化级别上失败,并且考虑到我们使用-Werror,构建失败。在这种特殊情况下,使用额外的初始化不会有太多开销,但有时也会变得烦人/低效。你永远不会取悦所有人(但你可能会允许选择“格外偏执”并随时警告它似乎未初始化)

    当然,如果是您自己的语言,并且您不太关心性能(可能是“启用检查时”),您可以为每个变量添加一个额外的元素,以指示它是否已初始化,并在表达式求值过程中确定它是否已初始化。但是,每次使用变量时都需要更多的指令来检查布尔值[或者在第一次使用时,如果您可以确定的话-但请记住,可能会有分支!]

    或者总是初始化未初始化为“疯狂值”的变量(例如0xdeaddead或类似的值)-这几乎总是会导致在数组中使用时崩溃


    当然,在编译阶段尽可能多地捕捉信息总是更好的——这只是一个你是否能够可靠地做到这一点(以及需要多少努力/时间)的问题。在代码编译后的测试中,你检测到的任何东西都会“花费”更多的时间来查找和修复。

    你的标题与你的问题文本不匹配

    假设您的问题是:“如何检测未定义变量的使用”,如果您的语言不打算达到极高的性能,您可以始终为表示“未定义”的值定义一个位模式(对于32位有符号整数,-2^31非常好),并生成代码,在获取时检查未定义的值。这非常简单

    如果您的问题是“如何检测越界数组访问”,尤其是考虑到您的语言没有
    void foo(int &x, int y)
    {
       switch(y)
       {
          case 1:
             x = 11;
             break;
          case 2:
             x = 42;
             break;
          ...  // numbers 3-9 elided for brevity
          case 10:
             x = 97;
             break;
       }
    }
    
    int bar(int z)
    {
        int a;
        foo(a, z);
    }