C ++:获取传递给函数的多维数组的行大小

时间:2011-08-04 00:09:47

标签: c++ multidimensional-array sizeof

我正在尝试编写一个打印出多维数组内容的函数。我知道列的大小,但不知道行的大​​小。

编辑:由于我没有说清楚,传递给这个函数的数组不是动态分配的。这些大小在编译时是已知的。

我正在使用3x2阵列进行测试。这是现在的功能:

void printArrays(int array1[][2], int array2[][2]) {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 2; j++) {
            cout << "\narray1[" << i << "][" << j << "] = "
                 << setfill('0') << setw(2) << array1[i][j]
                 << "\tarray2[" << i << "][" << j << "] = "
                 << setfill('0') << setw(2) << array2[i][j];
        }
    }
}

显然,这只有在我知道“i”的大小为3(在这种情况下)时才有效。然而,理想情况下,无论第一维的大小如何,我都希望该函数能够正常工作。

我以为我可以使用sizeof()函数来做到这一点,例如

int size = sizeof(array1);

......然后从那里做一些数学。

这是奇怪的部分。如果我在数组中使用sizeof()函数,它返回值4.我可以使用指针表示法取消引用数组:

int size = sizeof(*array1);

...但这实际上返回值8.这很奇怪,因为总大小应该是行(= 3)*列(= 2)* sizeof(int)(= 4)或24。事实上,当我在函数外使用sizeof(* array1) 时,这就是结果。

有谁知道这里发生了什么?更重要的是,有没有人有解决方案?

6 个答案:

答案 0 :(得分:8)

答案是你不能这样做。您必须将行数作为参数传递给函数,或使用STL容器,例如std::vectorstd::array

sizeof是计算编译时间; sizeof在确定C / C ++中对象的动态大小时永远不会有用。你(你自己,程序员)总是可以通过查看代码和头文件来计算sizeof(x),因为sizeof计算用于表示对象的字节数。 sizeof(*array1)始终为8,因为array1[i]是两个ints4==sizeof(int)的数组。当您声明int array1[][2]时,这相当于int *array1[2]。也就是说,array1是指向两个整数数组的指针。因此,sizeof(array1)是4个字节,因为在您的机器上需要4个字节来表示指针。

答案 1 :(得分:4)

您可以通过使用模板化函数在某种程度上完成此操作。警告是:

  • 您需要在任何使用它的地方都包含函数定义(而不仅仅是声明)
  • 仅在编译时修复数组大小时才会起作用
  • 您将为每次调用该函数生成一个单独的函数,从而导致某些代码膨胀

我是Kevin Heifner的working form the code on this blog post

template <typename T>
struct array_info
{
};

template <typename T, size_t N, size_t M>
struct array_info<T[N][M]>
{
  typedef T type;
  enum { size1 = N, size2 = M };
};

template <typename A1>
void printArrays(A1 array1, A1 array2) {
    size_t A1_i = array_info<A1>::size1;
    size_t A1_j = array_info<A1>::size2;
    for (size_t i = 0; i < A1_i; i++) {
        for (size_t j = 0; j < A1_j; j++) {
            cout << "\narray1[" << i << "][" << j << "] = "
                 << setfill('0') << setw(2) << array1[i][j]
                 << "\tarray2[" << i << "][" << j << "] = "
                 << setfill('0') << setw(2) << array2[i][j];
        }
    }
}

答案 2 :(得分:3)

你可以通过一些模板魔法获得两个数组的大小:

template< typename T, std::size_t n, std::size_t m >
void foo( T(&)[n][m] ) {
  std::cout << n << " " << m << std::endl;
}     

int main() {
  int a[3][3];
  int b[2][5];

  foo(a); foo(b);
}

这仅适用于在编译时已知边界的数组,而不适用于动态分配的数组。

无论如何:您应该使用std::vectorboost::multiarray

答案 3 :(得分:2)

函数外部的大小为8,因为您取消引用了第一个数组,这使您的列大小(2)倍于int(4)的大小。如果你想要24,你可以sizeof(array)(在函数之外)。答案是4 inside 函数,因为它将array视为一个指针,其大小为4个字节。

但是,要可靠地获取已传递给函数的数组的大小,您必须传递大小或使用类似vector的内容。

答案 4 :(得分:1)

一个非常简单的方法,不需要向量,模板,类或传递数组的大小,就是让最后一行数据包含一些独特的东西,例如下面的例子,其中-1是用在最后一行,第一列:

#define DATA_WIDTH 7

const float block10g[][DATA_WIDTH] = {
  {0, 15, 25, 50, 75, 100, 125},
  {2.12, 0, 1.000269, 3.000807, 4.24114056, 5.28142032, 6.001614},
  {6.36, 0, 1.2003228, 3.84103296, 6.24167856, 8.16219504, 10.08271152},
  {10.6, 0, 1.2003228, 4.4011836, 7.2019368, 9.2024748, 11.8031742},
  {21.2, 0, 2.000538, 6.001614, 8.002152, 10.4027976, 14.4038736},
  { -1}
};


const float block10g[][DATA_WIDTH] = {
  {0, 20, 50, 100, 150, 200, 250},
  {2.12, 0, 2.88077472, 5.04135576, 5.84157096, 6.08163552, 5.84157096},
  {6.36, 0, 3.84103296, 7.92213048, 11.52309888, 13.56364764, 14.4038736},
  {10.6, 0, 3.8010222, 8.8023672, 13.003497, 16.4044116, 18.4049496},
  {21.2, 0, 4.4011836, 9.2024748, 14.003766, 18.4049496, 22.4060256},
  { -1}
};

printArrays(block10g,block20g);

然后在达到唯一值时突然退出循环:

void printArrays(float array1[][DATA_WIDTH], float array2[][DATA_WIDTH]) {

    for (int i = 0; array1[i][0]!=-1 && array2[i][0]!=-1 ; i++) {
        for (int j = 0; j < DATA_WIDTH; j++) {

            cout << "\narray1[" << i << "][" << j << "] = "
                 << array1[i][j]
                 << "\tarray2[" << i << "][" << j << "] = "
                  << array2[i][j];
        }
    }
}

答案 5 :(得分:0)

只需使用更好的阵列!

我的意思是你可以创建自己的包装数组的数组类,或者使用一些带有这些类的公共库(例如boost)。这样更可靠,并且可能更容易推理出直接的C ++数组。

这样做的一个原因是你写了函数

void foo( int a[][2] )
{
    // etc.
}

你实际上并没有像你想象的那样在数组上有多少保证。例如,保证数组的第二个维度是两个元素的宽度(关于这一点我可能是错的,因为我手边没有参考,但我非常自信关于它)。这是因为该函数的实际签名是

void foo( int ** );

这是因为当在函数声明中使用时,数组会退化为指针(这就是为什么你sizeof(array)返回4,因为4个字节是你机器上指针类型的大小)。此外,您显然无法保证第一维的大小,因此假设它将是3是危险的,并且可能是将来混淆错误的结果。

这是自定义array类很棒的地方,特别是如果它是模板化的。例如,二维数组类可以声明为

template<typename T, int length1, int length2>
class Array2D
{
    // Array2D's gutsy stuff to make it act kind of like T[length1][length2]
};

使用这种方法可以在任何情况下维护所有有关数组的信息,例如

void foo( Array2D<int, 3, 2> &array ) {}

现在您可以决定数组中每个维度的大小。

另一个好处是,您可以将边界检查添加到Array2D类,而C ++数组没有。例如。在3x2数组中,您可以访问第一行中的第四个元素,即使它在概念上没有效果。通过使用像Array2D这样的数组类,可以很容易地消除这种常见的错误源。

有一些缺点,使用模板时很正常。最重要的是,由于模板的实例化方式,你必须在头文件中定义任何模板化的类,而不是在单独的源文件中定义(从技术上讲,你可以使用“export”关键字将类拆分为正常,但这对主要编译器的支持有限。)

作为最后一点(如果你感兴趣,就像我一样),随着可变参数模板的出现,C ++ 0x(即将推出!)的情况会变得更好:

template<typenameT, int... lengths>
class Array
{
    // etc...
};

现在所有数组类型都可以由单个类模板定义。从未如此简单。