C++ 我可以将二维数组视为连续的一维数组吗?

C++ 我可以将二维数组视为连续的一维数组吗?,c++,arrays,pointers,multidimensional-array,undefined-behavior,C++,Arrays,Pointers,Multidimensional Array,Undefined Behavior,考虑以下代码: int a[25][80]; a[0][1234] = 56; int* p = &a[0][0]; p[1234] = 56; 第二行是否调用未定义的行为?第四行怎么样?是的,你可以(不,它不是UB),它是由标准间接保证的。方法如下:二维数组是数组的数组。一个数组保证有连续的内存,并且sizeof(array)是sizeof(elem)乘以元素数。由此可知,您所做的一切都是完全合法的。您可以随意重新解释内存。只要倍数不超过线性内存。你甚至可以移动一个12, 40,使用

考虑以下代码:

int a[25][80];
a[0][1234] = 56;
int* p = &a[0][0];
p[1234] = 56;

第二行是否调用未定义的行为?第四行怎么样?

是的,你可以(不,它不是UB),它是由标准间接保证的。方法如下:二维数组是数组的数组。一个数组保证有连续的内存,并且
sizeof(array)
sizeof(elem)
乘以元素数。由此可知,您所做的一切都是完全合法的。

您可以随意重新解释内存。只要倍数不超过线性内存。你甚至可以移动一个12, 40,使用否定的索引。< /P> < P>你的编译器会因为下标超出范围(线2)和不兼容类型(3行)而引发一系列警告/错误,但是只要实际变量(在这种情况下是int)是固有的基本类型之一,这是在C和C++中保存的。 (如果变量是类/结构,它可能仍然在C中工作,但是在C++中所有的注记都是关闭的) 你为什么要这么做。。。。 对于第一种变体:如果您的代码依赖于这种混乱,那么从长远来看,它将很容易出错,并且很难维护

我可以看到第二个变量在通过在数据空间上运行1D指针来替换2D数组上的性能优化循环时有一定的用处,但是一个好的优化编译器通常会自己完成这项工作。
如果循环体太大/太复杂,编译器无法通过自己的1D运行来优化/替换循环,那么手动执行的性能提升也很可能不显著。

这取决于解释。虽然数组的连续性要求在如何布局多维数组方面没有留下太多的想象空间(这在前面已经指出),但请注意,当您执行
p[1234]
时,您正在索引第0行中只有80列的1234个元素。有些人将唯一有效的索引解释为0..79(
&p[80]
是一个特例)


从USENET中收集的关于C的相关信息(我认为C和C++在这一点上不一样,这是非常相关的)。< /P> < P>由<代码> A > /代码>引用的内存都是<代码> int [25 ] [80 ] < /C>和<代码> int [2000 ] < /C>。标准3.8p2如是说:

[注:阵列对象的生存期从获得具有适当大小和对齐方式的存储时开始,其生存期在阵列占用的存储被重用或释放时结束。12.6.2描述了基本和成员子对象的生存期。-结束注]

a
有一个特定类型,它是
int[25][80]
类型的左值。但是
p
只是
int*
。它不是“
int*
指向
int[80]
”或类似的东西。因此,事实上,所指的
int
int[25][80]
的一个元素,名为
a
,也是占据相同空间的
int[2000]
的一个元素

由于
p
p+1234
都是同一
int[2000]
对象的元素,指针算法定义良好。由于
p[1234]
的意思是
*(p+1234)
,所以它也有很好的定义

此规则对数组生存期的影响是,您可以自由使用指针算法在整个对象中移动


由于评论中提到了
std::array


如果有
std::数组a不存在
std::array
。确实存在一个
int[2000]
。我正在寻找任何需要
sizeof(std::array)==sizeof(T[N])
(和
==N*sizeof(T)
)的东西。如果没有这一点,您必须假设可能存在一些间隙,这些间隙会扰乱嵌套
std::array

的遍历,这两行都会导致未定义的行为

下标被解释为指针加和后接间接寻址,即,
a[0][1234]
/
p[1234]
相当于
*(a[0]+1234)
/
*(p+1234)
。根据(这里我引用了最新的草案,而关于OP的提出,你可以参考,结论是一样的):

如果表达式
p
指向具有n个元素的数组对象x的元素x[i],则表达式
p+J
J+p
(其中
J
具有值J)如果为0,则指向(可能是假设的)元素x[i+J]≤i+j≤N否则,行为是未定义的

由于
a[0]
(衰减为指向
a[0][0]
的指针)/
p
指向
a[0]
的元素(作为数组),并且
a[0]
只有大小80,因此行为未定义


作为语言律师,以下程序


当这些未定义的行为出现在常量表达式中时,编译器检测到这些行为。

在编写标准的语言中,调用以下函数不会有问题:

void print_array(double *d, int rows, int cols)
{
  int r,c;
  for (r = 0; r < rows; r++)
  {
    printf("%4d: ", r);
    for (c = 0; c < cols; c++)
      printf("%10.4f ", d[r*cols+c]);
    printf("\n");
  }
}
允许他们生成假定
d[1][0]
在执行
返回时仍保持1.0的代码,或者允许他们生成在
i
大于10时将陷阱的代码,如果使用
i==10
调用,则允许它们更适合于某些用途,而不是要求它们以静默方式返回
2.0

标准中没有对这些场景进行任何区分。虽然本标准可能包含这样的规则,即如果
i>=10
,则第二个示例调用UB,而不影响第一个示例(例如,对数组应用
[N]
,不会导致数组衰减为指针,而是生成该数组中必须存在的第N个元素),相反,该标准依赖于允许实现这一事实
void print_array(double *d, int rows, int cols)
{
  int r,c;
  for (r = 0; r < rows; r++)
  {
    printf("%4d: ", r);
    for (c = 0; c < cols; c++)
      printf("%10.4f ", d[r*cols+c]);
    printf("\n");
  }
}
double d[10][10];
double test(int i)
{
  d[1][0] = 1.0;
  d[0][i] = 2.0;
  return d[1][0]; 
}