void compute(int rows, int columns, double *data) {
double (*data2D)[columns] = (double (*)[columns]) data;
// do something with data2D
}
int main(void) {
double data[25] = {0};
compute(5, 5, data);
}
有时,将参数视为多维数组非常方便,但需要将其声明为指向平面数组的指针。将指针视为多维数组是否安全,如上例中compute
所做的那样?我很确定内存布局可以保证正常工作,但我不知道标准是否允许指针以这种方式进行转换。
这会破坏任何严格的别名规则吗?指针算法的规则怎么样;由于数据“实际上并不是”double[5][5]
,我们是否允许在data2D
上执行指针算法和索引,或者是否违反了指针算法不会偏离适当数组范围的要求? data2D
甚至可以保证指向正确的位置,还是只保证我们可以将其强制转换并恢复data
?标准报价将非常受欢迎。
答案 0 :(得分:1)
我提前道歉,因为有人说标准中的这些规则很难解释。
C11 6.3.2.3说
指向对象类型的指针可以转换为指向a的指针 不同的对象类型。如果结果指针不正确 对于引用的类型,行为是未定义的。
所以只要两个指针具有相同的对齐方式,实际的强制转换就可以了。
然后关于通过指针访问实际数据,C11 6.5为您提供了一个关于“别名”的乱码文本,这很难理解。我将试着引用我认为是这个具体案例的唯一相关部分:
“访问其存储值的对象的有效类型是 声明的对象类型,如果有的话。“/ - /
“对象的存储值只能由左值访问 具有以下类型之一的表达式:
- 类型兼容 与对象的有效类型,“
/ - /
- “包含其中一种上述类型的聚合或联合类型 成员“
(以上有时被称为“strict aliasing rule”,它不是正式的C语言术语,而是由编译器实现者组成的术语。)
在这种情况下,对象的有效类型是25个双精度数组。您正在尝试将其强制转换为指向5个双精度数组的数组指针。无论它是与有效类型兼容的类型,还是包含该类型的聚合,我都不确定。但我很确定这是两个有效案件中的任何一个。
据我所见,此代码不违反6.3.2.3和6.5。我相信代码可以保证工作正常,行为应该是明确的。
答案 1 :(得分:0)
在这种情况下最安全的方法是将元素保存在一个平面的1维数组中,但是以多维方式编写访问器方法以从该数组中读取和写入。
#include <stdio.h>
#include <string.h>
const int rowCount = 10;
const int columnCount = 10;
const int dataSize = rowCount*columnCount;
double data[dataSize];
void setValue( const int x, const int y, double value)
{
if ( x>=0 && x<columnCount && y>=0 && y<rowCount) {
data[x+y*columnCount] = value;
}
}
double getValue( const int x, const int y )
{
if ( x>=0 && x<columnCount && y>=0 && y<rowCount) {
return data[x+y*columnCount];
} else {
return 0.0;
}
}
int main()
{
memset(data, 0, sizeof(double)*dataSize);
// set a value
setValue(5, 2, 12.0);
// get a value
double value = getValue(2, 7);
return 0;
}
该示例使用仅用于简单的全局变量。您可以将数据数组作为附加参数传递给函数,也可以创建要使用的上下文。
在c ++中,您可以将数据容器包装到一个类中,并使用这两种方法作为访问方法。