c++中二维和一维数组的等价



众所周知,通过简单的坐标转换,二维数组和一维数组可以等价地使用。c++标准是否保证了这种等价性,或者它可能是组织数据的最方便的方式,但不是所有地方都必须遵守?例如,以下代码是否独立于编译器?

std::ofstream ofbStream;
ofbStream.open("File", std::ios::binary);
char Data[3][5];
for(int i=0; i<3; ++i)
for(int j=0; j<5; ++j)
{
    Data[i][j] = (char) 5*i+j;
}
ofbStream.write(&Data[0][0], 15);
ofbStream.close();

程序期望写出数字:0,1,2,…, 14到一个文件。

在实践中,这很好。任何不这样做的编译器都会对现有的代码产生无数的问题。

严格来说,需要的指针运算是未定义行为。

char Data[3][5];
char* p = &Data[0][0];
p + 7; // UB!

5.7/5(强调我的):

当对指针进行整型表达式的加减运算时,其结果具有指针操作数的类型。如果指针操作数指向数组对象的一个元素,并且数组足够大,则结果指向与原始元素的元素偏移量,使得结果与原始数组元素的下标之差等于整型表达式. ...如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则求值不会产生溢出;否则,行为是未定义的。

标准确实保证所有的数组元素在内存中是相邻的,并且按照特定的顺序,并且对具有正确地址的指针解引用(无论你如何获得它)指向该地址的对象,但它不能保证p + 7做任何可预测的事情,因为pp + 7不指向相同数组的元素或超过末端。

c++编程语言一书中,Bjarne Stroustrup提到(C.7.2;特别版, 2000)第838页:

…我们可以这样初始化ma:

void int_ma() {
    for(int i=0; i<3; i++)
        for(int j=0; j<5; j++) ma[i][j] = 10 * i + j; }

数组ma仅仅是15个int,我们把它当作3来访问5个 ints . 的数组,特别是内存中没有单个对象即矩阵ma——仅存储元素。维度3

(强调我的).

换句话说,符号[][]...[]是一个编译器结构;语法糖,如果你愿意。

为了方便使用,我编写了以下代码:

#include<cstdlib>
#include<iostream>
#include<iterator>
#include<algorithm>
int main() {
  double ma[5][3]; double *beg = &ma[0][0]; // case 1
  //double ma[3][5]; double *beg = &ma[0][0]; // case 2
  //double ma[15]; double *beg = &ma[0]; // case 3
  double *end = beg + 15;
  // fill array with random numbers
  std::generate(beg, end, std::rand);
  // display array contents
  std::copy(beg, end, std::ostream_iterator<double>(std::cout, " "));
  std::cout<<std::endl;  
  return 0;
}

并比较使用编译命令(GCC 4.7.2)为三种情况生成的程序集:

g++ test.cpp -O3 -S -oc1.s 

分别称为c1.sc2.sc3.sshasum *.s命令输出如下:

5360e2438aebea682d88277da69c88a3f4af10f3  c1.s
5360e2438aebea682d88277da69c88a3f4af10f3  c2.s
5360e2438aebea682d88277da69c88a3f4af10f3  c3.s

现在,我必须提到,最自然的结构似乎是对ma的一维声明,即:double ma[N],因为这样初始位置只是ma,最终位置只是ma + N(这与获取数组第一个元素的地址相反)。

我发现<algorithm> c++标准库头提供的算法在这种情况下更适合。

最后,如果可能的话,我必须鼓励您考虑使用std::arraystd::vector

欢呼。

c++将多维数组按行主序存储为通过内存扩展的一维数组。

正如其他评论者所指出的,二维数组将被映射到一维内存。你的假设是平台无关的吗?我希望如此,但你应该经常测试以确定。

#include <iostream>
#include <iterator>
#include <algorithm>
int main() {
   char Data[3][5];
   int count = 0;
   for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 5; ++j)
         Data[i][j] = count++;
   std::copy(&Data[0][0], &Data[0][0] + 15, std::ostream_iterator<int>(std::cout,", "));
}

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12日,13日,14日,

http://www.fredosaurus.com/notes-cpp/arrayptr/23two-dim-array-memory-layout.html

如何在内存中格式化多维数组?

C中2D数组的内存映射

Quote

由此可见,c++中的数组是按行存储的(最后一个下标变化最快)声明中的第一个下标有助于确定数组所消耗的存储空间下标计算中没有其他部分。

c++标准

最新更新