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

时间:2022-04-19 01:53:01

I'm trying to write a function that will print out the contents of a multidimensional array. I know the size of the columns, but not the size of the rows.

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

EDIT: Since I didn't make this clear, the arrays passed to this function are NOT dynamically allocated. The sizes are known at compile time.

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

I am testing it using a 3x2 array. Here is the function as it stands:

我正在用一个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];
        }
    }
}

Obviously, this only works if I know the size of "i" is 3 (it is in this case). Ideally, however, I would like the function to work no matter what the size of the first dimension.

显然,只有当我知道“I”的大小是3(在本例中是3)时,这个才有效。然而,理想情况下,我希望函数可以工作,无论第一个维度的大小如何。

I thought I would be able to do this using the sizeof() function, e.g.

我想我可以用sizeof()函数来做这个。

int size = sizeof(array1);

... and do some math from there.

…然后做一些数学运算。

Here's the odd part. If I use the sizeof() function inside the array, it returns a value of 4. I can use pointer notation to dereference the array:

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

int size = sizeof(*array1);

... but this actually returns a value of 8. This is odd, because the total size should be rows(which = 3) * columns(= 2) * sizeof(int)(= 4), or 24. And, indeed, this is the result, when I use sizeof(*array1) outside of the function.

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

Does anyone know what is going on here? More importantly, does anyone have a solution?

有人知道这是怎么回事吗?更重要的是,有人有解决方案吗?

6 个解决方案

#1


7  

The answer is that you can not do this. You must pass the number of rows as an argument to the function, or use an STL container such as std::vector or std::array.

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

sizeof is computed compile time; sizeof is never useful in determining dynamic size of objects in C/C++. You (yourself, the programmer) can always calculate sizeof(x) just from looking at code and header files since sizeof counts the number of bytes used to represent the object. sizeof(*array1) will always be 8 since array1[i] is an array of two ints and 4==sizeof(int). When you declare int array1[][2] this is equivalent to int *array1[2]. That is, array1 is a pointer to arrays of two integers. sizeof(array1) is therefore 4 bytes, since it takes 4 bytes on your machine to represent a pointer.

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

#2


4  

You can accomplish this, to some degree, by using templated functions. The caveats are:

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

  • You will need to include the function definition (not just declaration) anywhere it is used
  • 您需要将函数定义(不仅仅是声明)包含在它所使用的任何地方。
  • It will only work when array size is fixed at compile time
  • 只有在编译时固定数组大小时,它才能工作
  • You will generate a separate function for every call to the function, resulting in some code bloat
  • 您将为对该函数的每次调用生成一个单独的函数,从而导致一些代码膨胀

I am working form the code on this blog post by Kevin Heifner.

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

#3


3  

You can get the size of both arrays with some template magic:

你可以用一些模板魔法得到两个数组的大小:

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

This only works for arrays whose bounds are known at compile time and not for dynamically allocated arrays.

这只适用于在编译时知道界限的数组,而不适用于动态分配的数组。

In any case: You should use std::vector or boost::multiarray.

无论如何:您应该使用std::vector或boost:::multiarray。

#4


2  

The size is 8 outside the function because you're dereferencing the first array, which gives you the column size (2) times the size of an int (4). If you wanted 24, you'd do sizeof(array) (outside the function). The answer is 4 inside the function because it treats array like a pointer, the size of which is 4 bytes.

函数外的大小是8,因为要取消对第一个数组的引用,第一个数组的列大小(2)乘以int(4)的大小。答案在函数里面是4,因为它把数组当作指针,它的大小是4个字节。

However, to reliably get the size of arrays that have been passed to functions, you either have to pass the size or use something like vector.

但是,要可靠地获得传递给函数的数组的大小,您必须传递大小或使用向量之类的东西。

#5


1  

A very simple way to do it, without needing vectors, templates, classes, or passing the size of the array, is to have a last row of data that contains something unique such as the following example, where a -1 is used in the last row, first column:

一种非常简单的方法,不需要向量、模板、类或传递数组的大小,即拥有包含一些惟一数据的最后一行,例如下面的示例,在最后一行中使用-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);

Then just break out of the loop(s) when you reach that unique value:

然后当你达到那个唯一的值时就跳出这个循环:

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

#6


0  

Simply use better arrays!

简单地使用更好的数组!

What I mean by that is you can make your own array class which wraps an array, or use some common libraries with such classes (e.g. boost). This is much more reliable, and likely is easier to reason about that straight-up C++ arrays.

我的意思是,您可以创建自己的数组类来包装一个数组,或者使用一些公共库来包装这些类(例如boost)。这更可靠,而且很可能更容易对c++数组进行推理。

One reason for this is if your write the function

一个原因是如果你写函数

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

you don't actually have as many guarantees on the array as you might think. For example, it is not guaranteed that the second dimension of the array is two elements wide (I could be wrong about this point, as I don't have references on hand, but I'm pretty confident about it). This is because that actual signature for the function is

实际上,对数组的保证没有想象的那么多。例如,不能保证数组的第二个维度是两个元素宽的(我可能在这一点上错了,因为我手头没有引用,但我对此很有信心)。这是因为函数的实际签名是

void foo( int ** );

This is because arrays degenerate to pointers when used in function declarations (which is why you're sizeof(array) returns 4, since 4 bytes is the size of a pointer type on your machine). Also, you clearly have no guarantee on the size of the first dimension, so assuming it is going to be 3 is dangerous, and potentially the result of confusing bugs in the future.

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

This is where a custom array class would be great, especially if it were templated. For example, a two dimensional array class could be declared like

这是一个自定义数组类非常棒的地方,特别是当它被模板化的时候。例如,一个二维数组类可以声明为

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

Using such an approach allows you to maintain all the information about the array in any situation, e.g.

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

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

Now you can decide the sizes every dimension in the array.

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

An added benefit, is that you can add bounds checking to your Array2D class, which C++ array do not have. E.g. in a 3x2 array, you are able to access the fourth element in the first row, even though it's not conceptually valid. Such a common source of bugs can easily be eradicated by using an array class like Array2D.

一个附加的好处是,您可以向Array2D类添加边界检查,这是c++数组所没有的。例如,在3x2数组中,您可以访问第一行中的第四个元素,即使它在概念上是无效的。使用Array2D这样的数组类可以很容易地消除这种常见的bug源。

There are some drawbacks, which is normal when using templates. The big one is that, because of the way template are instantiated, you have to define any templated classes in your header files, not in separate source files (technically, you can use the "export" keyword to do split the class up as normal, but this has limited support on major compilers).

有一些缺点,在使用模板时是正常的。是一个大问题,因为模板实例化的方式,你必须定义任何模板类头文件,而不是单独的源文件(从技术上讲,您可以使用“导出”关键字做分割类是正常的,但这主要编译器)提供了有限的支持。

As a last note (if you're interested, as I am), the situation becomes even better in C++0x (comming soon!) with the advent of variadic templates:

最后提醒一下(如果你和我一样感兴趣的话),随着变量模板的出现,c++ 0x的情况会变得更好:

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

Now all array types can be defined by a single class template. Never been easier.

现在所有数组类型都可以由一个类模板定义。不太容易。

#1


7  

The answer is that you can not do this. You must pass the number of rows as an argument to the function, or use an STL container such as std::vector or std::array.

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

sizeof is computed compile time; sizeof is never useful in determining dynamic size of objects in C/C++. You (yourself, the programmer) can always calculate sizeof(x) just from looking at code and header files since sizeof counts the number of bytes used to represent the object. sizeof(*array1) will always be 8 since array1[i] is an array of two ints and 4==sizeof(int). When you declare int array1[][2] this is equivalent to int *array1[2]. That is, array1 is a pointer to arrays of two integers. sizeof(array1) is therefore 4 bytes, since it takes 4 bytes on your machine to represent a pointer.

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

#2


4  

You can accomplish this, to some degree, by using templated functions. The caveats are:

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

  • You will need to include the function definition (not just declaration) anywhere it is used
  • 您需要将函数定义(不仅仅是声明)包含在它所使用的任何地方。
  • It will only work when array size is fixed at compile time
  • 只有在编译时固定数组大小时,它才能工作
  • You will generate a separate function for every call to the function, resulting in some code bloat
  • 您将为对该函数的每次调用生成一个单独的函数,从而导致一些代码膨胀

I am working form the code on this blog post by Kevin Heifner.

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

#3


3  

You can get the size of both arrays with some template magic:

你可以用一些模板魔法得到两个数组的大小:

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

This only works for arrays whose bounds are known at compile time and not for dynamically allocated arrays.

这只适用于在编译时知道界限的数组,而不适用于动态分配的数组。

In any case: You should use std::vector or boost::multiarray.

无论如何:您应该使用std::vector或boost:::multiarray。

#4


2  

The size is 8 outside the function because you're dereferencing the first array, which gives you the column size (2) times the size of an int (4). If you wanted 24, you'd do sizeof(array) (outside the function). The answer is 4 inside the function because it treats array like a pointer, the size of which is 4 bytes.

函数外的大小是8,因为要取消对第一个数组的引用,第一个数组的列大小(2)乘以int(4)的大小。答案在函数里面是4,因为它把数组当作指针,它的大小是4个字节。

However, to reliably get the size of arrays that have been passed to functions, you either have to pass the size or use something like vector.

但是,要可靠地获得传递给函数的数组的大小,您必须传递大小或使用向量之类的东西。

#5


1  

A very simple way to do it, without needing vectors, templates, classes, or passing the size of the array, is to have a last row of data that contains something unique such as the following example, where a -1 is used in the last row, first column:

一种非常简单的方法,不需要向量、模板、类或传递数组的大小,即拥有包含一些惟一数据的最后一行,例如下面的示例,在最后一行中使用-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);

Then just break out of the loop(s) when you reach that unique value:

然后当你达到那个唯一的值时就跳出这个循环:

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

#6


0  

Simply use better arrays!

简单地使用更好的数组!

What I mean by that is you can make your own array class which wraps an array, or use some common libraries with such classes (e.g. boost). This is much more reliable, and likely is easier to reason about that straight-up C++ arrays.

我的意思是,您可以创建自己的数组类来包装一个数组,或者使用一些公共库来包装这些类(例如boost)。这更可靠,而且很可能更容易对c++数组进行推理。

One reason for this is if your write the function

一个原因是如果你写函数

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

you don't actually have as many guarantees on the array as you might think. For example, it is not guaranteed that the second dimension of the array is two elements wide (I could be wrong about this point, as I don't have references on hand, but I'm pretty confident about it). This is because that actual signature for the function is

实际上,对数组的保证没有想象的那么多。例如,不能保证数组的第二个维度是两个元素宽的(我可能在这一点上错了,因为我手头没有引用,但我对此很有信心)。这是因为函数的实际签名是

void foo( int ** );

This is because arrays degenerate to pointers when used in function declarations (which is why you're sizeof(array) returns 4, since 4 bytes is the size of a pointer type on your machine). Also, you clearly have no guarantee on the size of the first dimension, so assuming it is going to be 3 is dangerous, and potentially the result of confusing bugs in the future.

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

This is where a custom array class would be great, especially if it were templated. For example, a two dimensional array class could be declared like

这是一个自定义数组类非常棒的地方,特别是当它被模板化的时候。例如,一个二维数组类可以声明为

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

Using such an approach allows you to maintain all the information about the array in any situation, e.g.

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

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

Now you can decide the sizes every dimension in the array.

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

An added benefit, is that you can add bounds checking to your Array2D class, which C++ array do not have. E.g. in a 3x2 array, you are able to access the fourth element in the first row, even though it's not conceptually valid. Such a common source of bugs can easily be eradicated by using an array class like Array2D.

一个附加的好处是,您可以向Array2D类添加边界检查,这是c++数组所没有的。例如,在3x2数组中,您可以访问第一行中的第四个元素,即使它在概念上是无效的。使用Array2D这样的数组类可以很容易地消除这种常见的bug源。

There are some drawbacks, which is normal when using templates. The big one is that, because of the way template are instantiated, you have to define any templated classes in your header files, not in separate source files (technically, you can use the "export" keyword to do split the class up as normal, but this has limited support on major compilers).

有一些缺点,在使用模板时是正常的。是一个大问题,因为模板实例化的方式,你必须定义任何模板类头文件,而不是单独的源文件(从技术上讲,您可以使用“导出”关键字做分割类是正常的,但这主要编译器)提供了有限的支持。

As a last note (if you're interested, as I am), the situation becomes even better in C++0x (comming soon!) with the advent of variadic templates:

最后提醒一下(如果你和我一样感兴趣的话),随着变量模板的出现,c++ 0x的情况会变得更好:

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

Now all array types can be defined by a single class template. Never been easier.

现在所有数组类型都可以由一个类模板定义。不太容易。