用较少的内存在C++中实现2d数组



我需要一个2d数组,它是类的一个字段。x是宽度,y是高度。

我写了这样的代码:

#include <iostream>
int main(){
char ** tab;
int x, y;
std::cin >> x >> y;
tab = new char* [x+2];
for (int i = 0; i < x+2; i++) {
tab[i] = new char [y+2];
}
}

它是有效的。问题是它占用了太多内存(示例数据需要16kb,而我只能使用5kb)有没有一种简单(或不容易)的方法来实现这一点?

我能想到的唯一解决方案是处理tab[(x+2)*(y+2)],但我必须更改整个程序,并用简单的算法填充它来计算数组中的位置,但这需要重写大量代码,所以我想避免这种情况。

编辑:5kb是必需的,因为它是学校的项目:)该程序在96次测试中运行良好(满分100次),但在这次测试中由于内存问题而停止。edit2:如何在一个字符中存储多个valueas?会不会很复杂?

我认为最好的方法是将它封装到2d数组类中。它可以使用一维数组,但您可以通过所选索引的getter和setter来访问它。这是我能想到的最简单、最优雅的解决方案。

编辑:我厌倦了看到太多不正确的答案获得选票,所以我做了一些实际的实验来证明我的说法的有效性,并重写了我的答案。

tl;drtab是指向char的指针数组。这意味着tab不是存储char(每个取8位),而是存储x指针,每个指针(通常)取64位。您需要找到一种方法来使用选项卡作为指向单个字符数组的指针:char * tab


问题

在这个循环中:

for (int i = 0; i < x+2; i++) {
tab[i] = new char [y+2];
}

您正在运行newx+2次(顺便说一句,为什么是+2次??)。如您所知,new返回指针,而不是字符。它为请求的数据类型(即char)分配内存,然后返回指向该内存地址的指针。因此,对循环中的new的调用分配8位。由于new返回一个内存地址,所以您需要将其存储在某个地方。值得庆幸的是,你已经分配了空间来存储这行地址:

tab = new char* [x+2];

现在,您不是要求newchar的数组保留空间,而是要求它为指向char的指针的数组预留空间

在大多数现代体系结构上,指针需要64位。这些指针存储在堆上tab所指向的内存地址中。即tab[0]保存第一个指向字符的指针,tab[1]保存第二个指针,等等。这些指针保存已分配用于保存字符的其他内存的位置。

因此,总的来说,您使用以下行为x+2指针分配空间:

tab = new char* [x+2];

和CCD_ 15个字符,行为:

tab[i] = new char [y+2];

如果你计算一下,指针(x+2)*8B加上字符(x+2)*(y+2)*1B。从方程中可以看出,对于给定数量的字符,即对于任何x*y,如果x大于y,则会看到更多的内存使用。

为了测试这一点,我在你的代码上运行了valgrind massif工具(除了我去掉了+2,结果如下:

|  x | y |useful-heap(B)|
|----|---|--------------|
|  0 | 1 |       0      |
|  1 | 0 |       8      |
|  1 | 1 |       9      |
|  1 | 2 |      10      |
|  1 | 3 |      11      |
|  2 | 0 |      16      |
|  2 | 1 |      18      |
|  2 | 2 |      20      |
|  3 | 0 |      24      |
|  3 | 1 |      27      |
| 20 | 0 |     160      |
|100 | 0 |     800      |   

看看每次CCD_ 21增加时内存使用量是如何增加8B的。这是用于存储指针数组的空间分配。注意,对于y=0的情况,根本没有存储字符。。。因此,当x=100y=0时,您使用800B的内存是完全免费的。

fyi,massif还报告了由于最小分配大小、效率等原因,系统代表您进行的额外分配,但我上面给出的数字正是massif声称您要求的金额


如何修复

关键是重新安排如何存储字符以及如何寻址。您正在存储字符,这样您就可以制作一个大的字符数组,并找到不同的方法来索引它们。这将不需要除了字符本身之外的堆分配。

我还建议远离原始数组和指针,改用std::array或std::vector,但我想你已经被明确告知要在赋值时使用它们。。。

如果使用"-Os"标志编译代码,它将针对内存进行优化。

这不是很C++风格,但你可以使用宏来访问和数组,就像矩阵一样:

#define TAB(col,row) tab[col*column_length+row]

正确的方法是创建一个类来封装所有这些。

您还没有指定输入的2D数组大小(测试中的x和y是多少)。

请注意,"new"具有最小分配大小。

如果调用一组新的char[1]数组(比如x=1、y=10000),那么您正在分配最小malloc大小*10000;而不是1*10000。我相信最小大小大约是32或64字节。

如果可以一次分配所有内存(作为单个数组分配),则可以最大限度地减少所需的内存量。例如,新字符[x*y]

我相信你已经回答了自己的问题,我认为没有办法让它占用更少的空间,因为可变大小的字符会决定它。

最新更新