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

C++: getting the row size of a multidimensional array passed to a function

本文关键字:数组 函数 获取      更新时间:2023-10-16

我试图写一个函数,将打印出多维数组的内容。我知道列的大小,但不知道行的大小。

编辑:因为我没有说清楚,传递给这个函数的数组不是动态分配的。大小在编译时就知道了。

我正在使用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) 时,结果是这样的。

有人知道这里发生了什么吗?更重要的是,有人有解决办法吗?

答案是你不能这样做。您必须将行数作为参数传递给函数,或者使用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个字节来表示指针。

在某种程度上,可以通过使用模板化函数来实现这一点。注意事项如下:

  • 你需要包括函数定义(不只是声明)的任何地方使用
  • 只有当数组大小在编译时固定时才会起作用
  • 您将为每个函数的调用生成一个单独的函数,从而导致一些代码膨胀

我正在编写Kevin Heifner的这篇博文的代码。

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];
        }
    }
}

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

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 .

在函数外的大小是8,因为你正在解引用第一个数组,它给你的列大小(2)乘以int(4)的大小。如果你想要24,你会做sizeof(array)(在函数外)。答案是在函数内的4 ,因为它将array视为指针,其大小为4字节。

然而,要可靠地获得已传递给函数的数组的大小,您要么必须传递大小,要么使用vector之类的东西。

一个非常简单的方法来做到这一点,不需要向量,模板,类,或传递数组的大小,是有一个数据的最后一行包含一些唯一的东西,如下面的例子,其中-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];
        }
    }
}

使用更好的数组!

我的意思是你可以创建自己的数组类来包装数组,或者使用一些常见的类库(例如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这样的数组类,可以很容易地消除这种常见的bug来源。

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

最后一点说明(如果你和我一样感兴趣的话),随着可变模板的出现,c++ 0x(即将推出!)的情况变得更好了:

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

现在所有的数组类型都可以由一个类模板定义。再简单不过了