我如何最好地处理 C/C++ 中的动态多维数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/365782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I best handle dynamic multi-dimensional arrays in C/C++?
提问by Ari Ronen
What is the accepted/most commonly used way to manipulate dynamic (with all dimensions not known until runtime) multi-dimensional arrays in C and/or C++.
在 C 和/或 C++ 中操作动态(所有维度直到运行时才知道)多维数组的公认/最常用方法是什么。
I'm trying to find the cleanest way to accomplish what this Java code does:
我试图找到最干净的方法来完成这个 Java 代码的作用:
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int rows=sc.nextInt();
int cols=sc.nextInt();
int[][] data=new int[rows][cols];
manipulate(data);
}
public static void manipulate(int[][] data){
for(int i=0;i<data.length;i++)
for(int j=0;j<data[0].length.j++){
System.out.print(data[i][j]);
}
}
(reads from std_in just to clarify that dimensions aren't known until runtime).
(从 std_in 读取只是为了澄清尺寸直到运行时才知道)。
Edit:I noticed that this question is pretty popular even though it's pretty old. I don't actually agree with the top voted answer. I think the best choice for C is to use a single-dimensional array as Guge said below "You can alloc rowscolssizeof(int) and access it by table[row*cols+col].".
编辑:我注意到这个问题很受欢迎,尽管它已经很老了。我实际上并不同意最高投票的答案。我认为 C 的最佳选择是使用一维数组,正如 Guge 在下面所说的“您可以分配行colssizeof(int) 并通过 table[row*cols+col] 访问它。”。
There is a number of choices with C++, if you really like boost or stl then the answers below might be preferable, but the simplest and probably fastest choice is to use a single dimensional array as in C.
C++ 有很多选择,如果您真的喜欢 boost 或 stl,那么下面的答案可能更可取,但最简单且可能最快的选择是使用 C 中的一维数组。
Another viable choice in C and C++ if you want the [][] syntax is lillq's answer down at the bottom is manually building the array with lots of malloc's.
如果您想要 [][] 语法,C 和 C++ 中的另一个可行选择是底部的 lillq 答案是手动构建具有大量 malloc 的数组。
回答by Klaim
Use boost::multi_array.
As in your example, the only thing you need to know at compile time is the number of dimensions. Here is the first example in the documentation :
在您的示例中,您在编译时唯一需要知道的是维数。这是文档中的第一个示例:
#include "boost/multi_array.hpp"
#include <cassert>
int
main () {
// Create a 3D array that is 3 x 4 x 2
typedef boost::multi_array<double, 3> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][4][2]);
// Assign values to the elements
int values = 0;
for(index i = 0; i != 3; ++i)
for(index j = 0; j != 4; ++j)
for(index k = 0; k != 2; ++k)
A[i][j][k] = values++;
// Verify values
int verify = 0;
for(index i = 0; i != 3; ++i)
for(index j = 0; j != 4; ++j)
for(index k = 0; k != 2; ++k)
assert(A[i][j][k] == verify++);
return 0;
}
Edit: As suggested in the comments, here is a "simple" exampleapplication that let you define the multi-dimensional array size at runtime, asking from the console input. Here is an example output of this example application (compiled with the constant saying it's 3 dimensions) :
编辑:正如评论中所建议的,这是一个“简单”的示例应用程序,可让您在运行时定义多维数组大小,从控制台输入询问。这是此示例应用程序的示例输出(使用常量表示它是 3 维编译):
Multi-Array test!
Please enter the size of the dimension 0 : 4
Please enter the size of the dimension 1 : 6
Please enter the size of the dimension 2 : 2
Text matrix with 3 dimensions of size (4,6,2) have been created.
Ready!
Type 'help' for the command list.
>read 0.0.0
Text at (0,0,0) :
""
>write 0.0.0 "This is a nice test!"
Text "This is a nice test!" written at position (0,0,0)
>read 0.0.0
Text at (0,0,0) :
"This is a nice test!"
>write 0,0,1 "What a nice day!"
Text "What a nice day!" written at position (0,0,1)
>read 0.0.0
Text at (0,0,0) :
"This is a nice test!"
>read 0.0.1
Text at (0,0,1) :
"What a nice day!"
>write 3,5,1 "This is the last text!"
Text "This is the last text!" written at position (3,5,1)
>read 3,5,1
Text at (3,5,1) :
"This is the last text!"
>exit
The important parts in the code are the main function where we get the dimensions from the user and create the array with :
代码中的重要部分是 main 函数,我们从用户那里获取维度并使用以下命令创建数组:
const unsigned int DIMENSION_COUNT = 3; // dimension count for this test application, change it at will :)
// here is the type of the multi-dimensional (DIMENSION_COUNT dimensions here) array we want to use
// for this example, it own texts
typedef boost::multi_array< std::string , DIMENSION_COUNT > TextMatrix;
// this provide size/index based position for a TextMatrix entry.
typedef std::tr1::array<TextMatrix::index, DIMENSION_COUNT> Position; // note that it can be a boost::array or a simple array
/* This function will allow the user to manipulate the created array
by managing it's commands.
Returns true if the exit command have been called.
*/
bool process_command( const std::string& entry, TextMatrix& text_matrix );
/* Print the position values in the standard output. */
void display_position( const Position& position );
int main()
{
std::cout << "Multi-Array test!" << std::endl;
// get the dimension informations from the user
Position dimensions; // this array will hold the size of each dimension
for( int dimension_idx = 0; dimension_idx < DIMENSION_COUNT; ++dimension_idx )
{
std::cout << "Please enter the size of the dimension "<< dimension_idx <<" : ";
// note that here we should check the type of the entry, but it's a simple example so lets assume we take good numbers
std::cin >> dimensions[dimension_idx];
std::cout << std::endl;
}
// now create the multi-dimensional array with the previously collected informations
TextMatrix text_matrix( dimensions );
std::cout << "Text matrix with " << DIMENSION_COUNT << " dimensions of size ";
display_position( dimensions );
std::cout << " have been created."<< std::endl;
std::cout << std::endl;
std::cout << "Ready!" << std::endl;
std::cout << "Type 'help' for the command list." << std::endl;
std::cin.sync();
// we can now play with it as long as we want
bool wants_to_exit = false;
while( !wants_to_exit )
{
std::cout << std::endl << ">" ;
std::tr1::array< char, 256 > entry_buffer;
std::cin.getline(entry_buffer.data(), entry_buffer.size());
const std::string entry( entry_buffer.data() );
wants_to_exit = process_command( entry, text_matrix );
}
return 0;
}
And you can see that to accede an element in the array, it's really easy : you just use the operator() as in the following functions :
你可以看到,要加入数组中的一个元素,真的很简单:你只需要像下面的函数一样使用 operator() :
void write_in_text_matrix( TextMatrix& text_matrix, const Position& position, const std::string& text )
{
text_matrix( position ) = text;
std::cout << "Text \"" << text << "\" written at position ";
display_position( position );
std::cout << std::endl;
}
void read_from_text_matrix( const TextMatrix& text_matrix, const Position& position )
{
const std::string& text = text_matrix( position );
std::cout << "Text at ";
display_position(position);
std::cout << " : "<< std::endl;
std::cout << " \"" << text << "\"" << std::endl;
}
Note : I compiled this application in VC9 + SP1 - got just some forgettable warnings.
注意:我在 VC9 + SP1 中编译了这个应用程序 - 只是得到了一些容易忘记的警告。
回答by Vincent Robert
There are two ways to represent a 2-dimension array in C++. One being more flexible than the other.
在 C++ 中有两种表示二维数组的方法。一个比另一个更灵活。
Array of arrays
数组数组
First make an array of pointers, then initialize each pointer with another array.
首先创建一个指针数组,然后用另一个数组初始化每个指针。
// First dimension
int** array = new int*[3];
for( int i = 0; i < 3; ++i )
{
// Second dimension
array[i] = new int[4];
}
// You can then access your array data with
for( int i = 0; i < 3; ++i )
{
for( int j = 0; j < 4; ++j )
{
std::cout << array[i][j];
}
}
THe problem with this method is that your second dimension is allocated as many arrays, so does not ease the work of the memory allocator. Your memory is likely to be fragmented resulting in poorer performance. It provides more flexibility though since each array in the second dimension could have a different size.
这种方法的问题是你的第二维被分配了尽可能多的数组,所以不会减轻内存分配器的工作。您的内存可能会碎片化,从而导致性能下降。但它提供了更大的灵活性,因为第二维中的每个数组可能具有不同的大小。
Big array to hold all values
保存所有值的大数组
The trick here is to create a massive array to hold every data you need. The hard part is that you still need the first array of pointers if you want to be able to access the data using the array[i][j] syntax.
这里的技巧是创建一个庞大的数组来保存您需要的每个数据。困难的部分是,如果您希望能够使用 array[i][j] 语法访问数据,您仍然需要第一个指针数组。
int* buffer = new int[3*4];
int** array = new int*[3];
for( int i = 0; i < 3; ++i )
{
array[i] = array + i * 4;
}
The int* array is not mandatory as you could access your data directly in buffer by computing the index in the buffer from the 2-dimension coordinates of the value.
int* 数组不是强制性的,因为您可以通过从值的二维坐标计算缓冲区中的索引来直接访问缓冲区中的数据。
// You can then access your array data with
for( int i = 0; i < 3; ++i )
{
for( int j = 0; j < 4; ++j )
{
const int index = i * 4 + j;
std::cout << buffer[index];
}
}
The RULE to keep in mind
要记住的规则
Computer memory is linear and will still be for a long time. Keep in mind that 2-dimension arrays are not natively supported on a computer so the only way is to "linearize" the array into a 1-dimension array.
计算机内存是线性的,并且会持续很长时间。请记住,计算机本身并不支持二维数组,因此唯一的方法是将数组“线性化”为一维数组。
回答by Guge
You can alloc rowscolssizeof(int) and access it by table[row*cols+col].
您可以分配行colssizeof(int) 并通过 table[row*cols+col] 访问它。
回答by cmaster - reinstate monica
Here is the easy way to do this in C:
这是在 C 中执行此操作的简单方法:
void manipulate(int rows, int cols, int (*data)[cols]) {
for(int i=0; i < rows; i++) {
for(int j=0; j < cols; j++) {
printf("%d ", data[i][j]);
}
printf("\n");
}
}
int main() {
int rows = ...;
int cols = ...;
int (*data)[cols] = malloc(rows*sizeof(*data));
manipulate(rows, cols, data);
free(data);
}
This is perfectly valid since C99, however it is not C++ of any standard: C++ requires the sizes of array types to be compile times constants. In that respect, C++ is now fifteen years behind C. And this situation is not going to change any time soon (the variable length array proposal for C++17 does not come close to the functionality of C99 variable length arrays).
这从 C99 开始就完全有效,但它不是任何标准的 C++:C++ 要求数组类型的大小是编译时间常量。在这方面,C++ 现在比 C 晚了 15 年。而且这种情况不会很快改变(C++17 的变长数组提议与 C99 变长数组的功能不相近)。
回答by Johannes Schaub - litb
The standard way without using boost is to use std::vector :
不使用 boost 的标准方法是使用 std::vector :
std::vector< std::vector<int> > v;
v.resize(rows, std::vector<int>(cols, 42)); // init value is 42
v[row][col] = ...;
That will take care of new / delete the memory you need automatically. But it's rather slow, since std::vector
is not primarily designed for using it like that (nesting std::vector
into each other). For example, all the memory is not allocated in one block, but separate for each column. Also the rows don't have to be all of the same width. Faster is using a normal vector, and then doing index calculation like col_count * row + col
to get at a certain row and col:
这将自动处理新/删除您需要的内存。但它相当慢,因为它的std::vector
主要设计目的不是像那样使用它(相互嵌套std::vector
)。例如,所有内存不是在一个块中分配,而是为每一列分配。此外,行不必都具有相同的宽度。更快的是使用法线向量,然后进行索引计算,例如col_count * row + col
获取特定行和列:
std::vector<int> v(col_count * row_count, 42);
v[col_count * row + col) = ...;
But this will loose the capability to index the vector using [x][y]
. You also have to store the amount of rows and cols somewhere, while using the nested solution you can get the amount of rows using v.size()
and the amount of cols using v[0].size()
.
但这将失去使用 索引向量的能力[x][y]
。您还必须在某处存储行数和列数,而使用嵌套解决方案时,您可以获得使用的行数和列v.size()
数使用v[0].size()
.
Using boost, you can use boost::multi_array
, which does exactly what you want (see the other answer).
使用 boost,您可以使用boost::multi_array
,它完全符合您的要求(请参阅其他答案)。
There is also the raw way using native C++ arrays. This envolves quite some work and is in no way better than the nested vector solution:
还有使用原生 C++ 数组的原始方式。这涉及相当多的工作,并且丝毫不比嵌套向量解决方案好:
int ** rows = new int*[row_count];
for(std::size_t i = 0; i < row_count; i++) {
rows[i] = new int[cols_count];
std::fill(rows[i], rows[i] + cols_count, 42);
}
// use it... rows[row][col] then free it...
for(std::size_t i = 0; i < row_count; i++) {
delete[] rows[i];
}
delete[] rows;
You have to store the amount of columns and rows you created somewhere since you can't receive them from the pointer.
您必须将创建的列和行的数量存储在某处,因为您无法从指针接收它们。
回答by Mr.Ree
2D C-style arrays in C and C++ are a block of memory of size rows * columns * sizeof(datatype)
bytes.
C 和 C++ 中的 2D C 样式数组是大小rows * columns * sizeof(datatype)
字节的内存块。
The actual [row][column] dimensions exist only statically at compile time. There's nothing there dynamically at runtime!
实际的 [row][column] 维度仅在编译时静态存在。运行时没有动态!
So, as others have mentioned, you can implement
因此,正如其他人所提到的,您可以实施
int array [ rows ] [ columns ];
As:
作为:
int array [ rows * columns ]
Or as:
或作为:
int * array = malloc ( rows * columns * sizeof(int) );
Next: Declaring a variably sized array. In Cthis is possible:
Next: 声明一个可变大小的数组。 在 C 中,这是可能的:
int main( int argc, char ** argv )
{
assert( argc > 2 );
int rows = atoi( argv[1] );
int columns = atoi( argv[2] );
assert(rows > 0 && columns > 0);
int data [ rows ] [ columns ]; // Yes, legal!
memset( &data, 0, sizeof(data) );
print( rows, columns, data );
manipulate( rows, columns, data );
print( rows, columns, data );
}
In Cyou can just pass the variably-sized array around the same as a non-variably-sized array:
在 C 中,您可以像传递非可变大小的数组一样传递可变大小的数组:
void manipulate( int theRows, int theColumns, int theData[theRows][theColumns] )
{
for ( int r = 0; r < theRows; r ++ )
for ( int c = 0; c < theColumns; c ++ )
theData[r][c] = r*10 + c;
}
However, in C++that is not possible. You need to allocate the array using dynamic allocation, e.g.:
但是,在 C++ 中这是不可能的。您需要使用动态分配来分配数组,例如:
int *array = new int[rows * cols]();
or preferably (with automated memory management)
或者最好(使用自动内存管理)
std::vector<int> array(rows * cols);
Then the functions must be modified to accept 1-dimensional data:
然后必须修改函数以接受一维数据:
void manipulate( int theRows, int theColumns, int *theData )
{
for ( int r = 0; r < theRows; r ++ )
for ( int c = 0; c < theColumns; c ++ )
theData[r * theColumns + c] = r*10 + c;
}
回答by Norman Ramsey
If you're using C instead of C++ you might want to look at the Array_T abstraction in Dave Hanson's library C Interfaces and Implementations. It's exceptionally clean and well designed. I have my students do a two-dimensional version as an exercise. You could do that or simply write an additional function that does an index mapping, e.g.,
如果您使用 C 而不是 C++,您可能需要查看 Dave Hanson 的库C Interfaces and Implementations中的 Array_T 抽象。它非常干净,设计精良。我让我的学生做一个二维版本作为练习。您可以这样做或简单地编写一个执行索引映射的附加函数,例如,
void *Array_get_2d(Array_T a, int width, int height, int i, int j) {
return Array_get(a, j * width, i, j);
}
It is a bit cleaner to have a separate structure where you store the width, the height, and a pointer to the elements.
有一个单独的结构来存储宽度、高度和指向元素的指针会更简洁一些。
回答by user2880576
I recently came across a similar problem. I did not have Boost available. Vectors of vectors turned out to be pretty slow in comparison to plain arrays. Having an array of pointers makes the initialization a lot more laborious, because you have to iterate through every dimension and initialize the pointers, possibly having some pretty unwieldy, cascaded types in the process, possibly with lots of typedefs.
我最近遇到了类似的问题。我没有可用的 Boost。与普通数组相比,向量的向量变得非常慢。拥有一个指针数组会使初始化更加费力,因为您必须遍历每个维度并初始化指针,在此过程中可能会有一些非常笨拙的级联类型,可能有很多 typedef。
DISCLAIMER: I was not sure if I should post this as an answer, because it only answers part of your question. My apologies for the following:
免责声明:我不确定是否应该将此作为答案发布,因为它只回答了您问题的一部分。对于以下情况,我深表歉意:
- I did not cover how to read the dimensions from standard input, as other commentators had remarked.
- This is primarily for C++.
- I have only coded this solution for two dimensions.
- 正如其他评论员所说,我没有介绍如何从标准输入中读取尺寸。
- 这主要用于 C++。
- 我只为二维编码了这个解决方案。
I decided to post this anyway, because I see vectors of vectors brought up frequently in reply to questions about multi-dimensional arrays in C++, without anyone mentioning the performance aspects of it (if you care about it).
无论如何,我决定发布这个,因为我看到在回答有关 C++ 中多维数组的问题时经常提到向量的向量,但没有人提到它的性能方面(如果你关心的话)。
I also interpreted the core issue of this question to be about how to get dynamic multi-dimensional arrays that can be used with the same ease as the Java example from the question, i.e. without the hassle of having to calculate the indices with a pseudo-multi-dimensional one-dimensional array.
我还解释了这个问题的核心问题是关于如何获得动态多维数组,这些数组可以像问题中的 Java 示例一样轻松使用,即无需使用伪计算索引的麻烦多维一维数组。
I didn't see compiler extensions mentioned in the other answers, like the ones provided by GCC/G++ to declare multi-dimensional arrays with dynamic bounds the same way you do with static bounds. From what I understand, the question does not restrict the answers to standard C/C++. ISO C99 apparently does support them, but in C++ and prior versions of C they appear to be compiler-specific extensions. See this question: Dynamic arrays in C without malloc?
我没有看到其他答案中提到的编译器扩展,例如 GCC/G++ 提供的那些,以与静态边界相同的方式声明具有动态边界的多维数组。据我了解,该问题不限制标准 C/C++ 的答案。ISO C99 显然确实支持它们,但在 C++ 和以前版本的 C 中,它们似乎是特定于编译器的扩展。看到这个问题:Dynamic array in C without malloc?
I came up with a way that people might like for C++, because it's little code, has the ease of use of the built-in static multi-dimensional arrays, and is just as fast.
我想出了一种人们可能会喜欢 C++ 的方法,因为它的代码很少,易于使用内置的静态多维数组,而且速度也一样快。
template <typename T>
class Array2D {
private:
std::unique_ptr<T> managed_array_;
T* array_;
size_t x_, y_;
public:
Array2D(size_t x, size_t y) {
managed_array_.reset(new T[x * y]);
array_ = managed_array_.get();
y_ = y;
}
T* operator[](size_t x) const {
return &array_[x * y_];
}
};
You can use it like this. The dimensions do not
你可以像这样使用它。尺寸不
auto a = Array2D<int>(x, y);
a[xi][yi] = 42;
You can add an assertion, at least to all but the last dimension and extend the idea to to more than two dimensions. I have made a post on my blog about alternative ways to get multi-dimensional arrays. I am also much more specific on the relative performance and coding effort there.
您可以添加断言,至少添加到最后一个维度之外的所有维度,并将想法扩展到两个以上的维度。我在我的博客上发表了一篇关于获取多维数组的替代方法的文章。我也更具体地了解那里的相对性能和编码工作。
回答by lillq
You could use malloc to accomplish this and still have it accessible through normal array[][] mean, verses the array[rows * cols + cols] method.
您可以使用 malloc 来完成此操作,并且仍然可以通过普通的 array[][] 方法访问它,这与 array[rows * cols + cols] 方法相反。
main()
{
int i;
int rows;
int cols;
int **array = NULL;
array = malloc(sizeof(int*) * rows);
if (array == NULL)
return 0; // check for malloc fail
for (i = 0; i < rows; i++)
{
array[i] = malloc(sizeof(int) * cols)
if (array[i] == NULL)
return 0; // check for malloc fail
}
// and now you have a dynamically sized array
}
回答by Andy
There is no way to determine the length of a given array in C++. The best way would probably be to pass in the length of each dimension of the array, and use that instead of the .length property of the array itself.
在 C++ 中无法确定给定数组的长度。最好的方法可能是传入数组每个维度的长度,并使用它而不是数组本身的 .length 属性。