我有一个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乘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 FAQ中找到了这个,这适用于此吗?
只要指针指向,就会定义指针运算 在相同分配的内存块内,或虚构内 “终止”元素一个过去;否则,行为是 undefined,即使指针未被解除引用。 .... 参考文献:K&amp; R2 Sec。 5.3 p。 100,Sec。 5.4 pp.102-3,Sec。 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);
然后,无论优化如何,该方法都会产生相同的(预期的)输出。仅减1的单维数组似乎不会产生任何问题。
我可以使用上面的访问方法更改程序中的所有多维数组,但是单个dim数组太多而无法手动更改,所以理想情况下我想要一个适合两者的解决方案。
新问题:
答案 0 :(得分:5)
优化器应该只确保没有可观察到的行为更改,因此它可以选择不执行递减并使用递减的索引访问数据(额外的偏移量可以是操作码的一部分),因为函数使用指向数组的指针的副本。您没有告诉我们实际访问数组的方式以及优化器是否实际引入了错误,所以我只能猜到这一点。
但是,正如slartibartfast已经说过的那样:它是未定义的行为,在检查* index&gt;之后,应该用int val = arr1[*index-1];
替换该减量。 0
答案 1 :(得分:1)
将指向数组的指针移出数组指定的范围,然后通过它进行访问(尽管完整表达式回落到范围内)是未定义的行为AFAIK。然而,在几乎每一个实现中,这段代码应该像预期的那样工作,所以问题是,你在看什么?也许在从arr1 []获取int的汇编指令中有一个隐式的预定义?只是因为你没有看到调试器中的减量并不意味着它不存在。通过为其写入区别值来检查是否访问了正确的元素。