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

时间:2013-05-24 16:27:53

标签: c++ multidimensional-array standards-compliance

众所周知,通过简单的坐标转换,可以等效地使用二维和一维阵列。 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写入文件。

5 个答案:

答案 0 :(得分:4)

在他的书The C++ Programming Language中,Bjarne Stroustrup提及(C.7.2; 特别版的第838页,2000年):

  

...我们可以像这样初始化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 s,我们可以访问它,就像它是3一样   5 int s的数组。 特别是,内存中没有单个对象   即矩阵ma - 仅存储元素。尺寸3   和5仅存在于编译器源中。

(强调我的)。

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

出于娱乐目的,我写了以下代码:

#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.s。命令shasum *.s的输出是:

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

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

我发现在这种情况下,<algorithm> C ++标准库标题提供的算法更加贴合。

最后,我们建议您尽可能考虑使用std::arraystd::vector

干杯。

答案 1 :(得分:4)

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

但严格地说,所需的指针算法是未定义的行为。

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

5.7 / 5(强调我的):

  

当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素并且数组足够大,则结果指向偏离原始元素的元素,使得结果和原始的下标的差异数组元素等于整数表达式。 ...如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。

标准确保所有数组元素在内存中都是相邻的并且按照特定的顺序排列,并且取消引用具有正确地址的指针(无论你如何获得它)引用该地址处的对象,但它不保证p + 7做任何可预测的事情,因为pp + 7不指向同一个数组或过去的结尾。 (相反,它们指向同一数组元素的元素。)

答案 2 :(得分:1)

C ++以row major顺序存储多维数组,作为通过内存扩展的一维数组。

答案 3 :(得分:0)

正如其他评论者指出的那样,二维数组将被映射到一维存储器。您的假设平台是否独立?我希望如此,但你应该经常测试它。

#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

How are multi-dimensional arrays formatted in memory?

Memory map for a 2D array in C

答案 4 :(得分:0)

引用

  

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

C++ ISO standard