F2C 转换的代码中断,当由编译器优化时C++



我有一个 c++ 程序,其方法如下所示:

int myMethod(int* arr1, int* arr2, int* index)
{
    arr1--;        
    arr2--;
    int val = arr1[*index];
    int val2 = arr2[val];
    return doMoreThings(val);
}

启用优化 (/O2) 后,不会执行第一个指针递减的第一行。我正在并排调试优化和非优化构建,优化构建步骤递减,而非优化程序执行它。当它稍后使用 arr[*index] 访问数组时,这会产生可观察到的行为差异。

更新

正如@stefaanv所指出的,如果编译器改为递减的访问索引,编译器确实可以省略递减,它似乎确实这样做了。因此,省略的递减并不是导致行为差异的原因。相反,矩阵的使用中有一些东西导致了它。

进一步看,我已将其缩小到一个包含执行矩阵乘法的嵌套循环的方法。该方法的一部分如下所示:涉及 3 个数组:a、wa 和 t。 在方法的开头,f2c 转换器使用递减,因此在 fortran 中为 6 x 6 的数组在 c 中是平面double[36]。但是为了能够使用旧的索引,它会将数组指针移回矩阵中的列数。

通常,在这个 f2c 转换的程序中,平面数组作为&someArray[1]传递,方法首先将每个数组递减 1。 @Christoph指出这应该是有效的,因为数组永远不会递减超过其声明的范围。

在此方法的情况下,传入的数组不会作为指向元素的指针传递到数组&someArray[1]而是这里的数组是用固定大小声明的本地静态数组,例如 mat[36]并直接传递给乘法方法。

void test()
{
    double mat[36];
    ...
    mul(mat, .., ..)
}
void mul(double* a, double* t, double*wa, int M, int N, int K)
{
    // F2C array decrements.
    a -= (1+M); // E.g. decrement by seven for a[6x6]!
    t -= (1+N); 
    wa--;
    ...
    for (j = K; j <= M; ++j) {         
       for (i = 1; i <= N; ++i) {
          ii = K;
          wa[i] = 0.;          
          for (p = 1; p <= N; ++p) {
             wa[i] += t[p + i * t_dim1] * a[ii + j * a_dim1];
             ++ii;
          }
       }
       ii = K;      
       for (i = 1; i <= N; ++i) {
          a[ii + j * a_dim1] = wa[i];
          if (j > kn) {
             a[j + ii * a_dim1] = wa[i];
          }
          ++ii;
      }
    }
 }    

所以问题是:

这是否意味着行为是未定义的,并且当您执行 f2c 在此处执行的操作时,即从 double[36] 数组指针中减去 7,但随后在正确的位置访问数组中的所有项目(偏移量 7)时,行为可能会在优化下中断?

编辑:在C常见问题解答中找到了这一点,这适用于这里吗?

指针

算术仅在指针指向时定义 在相同的分配内存块内,或到虚构 "终止"元素一过去;否则,行为为 未定义,即使指针未取消引用。 .... 参考文献:K&R2 第 5.3 节,第 100 页,第 5.4 节,第 102-3 页,第 A7.7 节 第205-6页;ISO 第 6.3.6 节;理由 第3.2.2.3节。

更新 2:

如果我使用递减索引而不是递减指针重新编译多维数组,

#define a_ref(a_1,a_2) a[(a_2)*a_dim1 + a_1 - 1 - a_dim1]
a_ref(1,2);

然后,无论优化如何,该方法都会生成相同的(预期)输出。仅递减一的单维数组似乎不会产生任何问题。

我可以将程序中的所有多维数组更改为使用上述访问方法,但是单个暗淡数组太多而无法手动更改,因此理想情况下,我想要一个同时适用于两者的解决方案。

新问题:

  • f2c 是否有使用此数组访问方法而不是指针摆弄的选项?似乎这将是 f2c 中的简单更改,并生成定义良好的代码,因此您会认为它已经是一种选择。
  • 这个问题有没有其他解决方案(除了跳过优化并希望程序运行良好,尽管依赖于未定义的行为)。
  • 我可以在 c++ 编译器中做些什么吗?我使用 Microsoft C++ (2010) 编译,作为一个托管的 c++ 项目。

优化程序应仅确保没有可观察到的行为更改,因此它可以选择不执行递减并访问具有递减索引的数据(额外的偏移量可以是操作码的一部分),因为该函数使用指向数组的指针的副本。 您没有告诉我们数组实际上是如何访问的,以及优化器是否实际上引入了错误,所以我只能猜测这一点。

但正如 slartibartfast 已经说过的那样:这是未定义的行为,在检查 *index> 0 后,递减应该替换为 int val = arr1[*index-1];

指针移动到数组指定范围之外,然后通过它进行访问(尽管完整的表达式回退到范围中)是未定义的行为 AFAIK。然而,在几乎每个实现中,这段代码都应该按预期工作,所以问题是,你在看什么?也许汇编程序指令中有一个隐式的先置,从 arr1[] 中获取 int?仅仅因为您在调试器中看不到递减并不意味着它不存在。通过向该元素写入区分值来检查是否访问了正确的元素。

最新更新