4

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

4

5 回答 5

4

Bjarne Stroustrup在他的《C++ 编程语言》一书中提到(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只是int我们访问的 15 s,就好像它是 3 个 5 ints 的数组一样。特别是,内存中没有单个对象是矩阵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::arrayorstd::vector如果可能的话。

干杯。

于 2013-05-24T16:58:05.280 回答
4

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

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

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

5.7/5(强调我的):

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

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

于 2013-05-24T17:26:49.467 回答
1

C++ 以行主要顺序将多维数组存储为通过内存扩展的一维数组。

于 2013-05-24T16:58:17.877 回答
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

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

C中二维数组的内存映射

于 2013-05-24T16:46:41.310 回答
0

引用

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

C++ ISO 标准

于 2013-05-24T17:03:42.793 回答