C++ 在C+中实现二维数组+;内存不足

C++ 在C+中实现二维数组+;内存不足,c++,arrays,algorithm,C++,Arrays,Algorithm,我需要一个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]; }

我需要一个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];
    }
}
#包括
int main(){
字符**选项卡;
int x,y;
标准:cin>>x>>y;
tab=新字符*[x+2];
对于(int i=0;i
它是有效的。问题是它占用了太多的内存(示例数据需要16kb,我只能使用5kb),有没有一种简单(或不方便)的方法来实现这一点

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

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

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

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

如果使用“-Os”标记编译代码,它将优化内存

这不是很像C++-ish,但您可以使用宏访问和数组,就像矩阵一样:

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

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

编辑:我厌倦了看到太多不正确的答案,所以我做了一些实际的实验来证明我的观点的正确性,并重写了我的答案

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


问题 在此循环中:

for (int i = 0; i < x+2; i++) {
  tab[i] = new char [y+2];
}
现在,您不是要求
new
char
数组保留空间,而是要求它为char指针数组保留空间

在大多数现代体系结构中,指针需要64位。这些指针存储在堆中
tab
指向的内存地址中。i、 e.
tab[0]
保存指向char的第一个指针,
tab[1]
保存第二个指针,以此类推。。。这些指针保存已分配用于保存字符的额外内存的位置

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

tab = new char* [x+2];
tab = new char* [x+2];
tab[i] = new char [y+2];
(x+2)*(y+2)
此行的字符:

tab = new char* [x+2];
tab = new char* [x+2];
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      |   
看看每次
x
增加时内存使用量是如何增加8B的。这是存储指针数组的空间分配。请注意,对于
y=0
情况,根本没有存储字符……因此当
x=100
y=0
时,您使用800B的内存是完全没有意义的

仅供参考,massif还报告了由于最小分配规模、效率等原因系统代表您进行的额外分配。但我上面给出的数字是massif要求您提供的确切金额


如何修复它? 关键是要重新安排存储字符的方式和寻址方式。存储字符是为了创建一个大的字符数组,并找到一种不同的方法对其进行索引。除了字符本身之外,不需要堆分配


我还建议不要使用原始数组和指针,而是使用std::array或std::vector,但我假设您已被明确告知使用它们进行赋值…

您尚未指定输入的2D数组大小(测试中的x和y是什么)

请注意,“新”具有最小分配大小

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

如果可以一次分配所有内存(作为单个阵列分配),则可以将所需的内存量降至最低。
e、 g.新字符[x*y]

我怀疑开销是否可以减少到16kb可以容纳5KB的程度。我想你必须找到一种方法,在任何给定的时间只包含部分数据。你用字符来表示什么?根据你所表示的数据集,你可以用一个字符来保存多个值(例如,1x8位字符用于保存8个布尔值,而不是8x8位字符)。这要求我们了解正在使用的数据域。除非您在微控制器上编程(不太可能,因为您使用的是iostreams和
new
)在现代机器上,16KB的内存是非常小的。事实上,
newchar[]的底层内存分配器
本身可能会保留超过16 kB的内存以满足未来的内存请求。为什么16 kB是这样一个问题,为什么5 kB是您的限制?我怀疑这个练习的目的是一次只处理一小段数据,在这种情况下,我们无法回答这个问题。这是预处理器,对吗?如果我也有ite标签(4,6)?正确的方法