如何在 C++ 中解决非常大的二维数组

发布于 2024-07-04 10:24:29 字数 206 浏览 10 评论 0原文

我需要创建一个大小为 800x800 的 2D int 数组。 但这样做会造成堆栈溢出(哈哈)。

我是 C++ 新手,所以我应该做一些类似于向量的向量的事情吗? 然后将二维数组封装到一个类中?

具体来说,这个数组是我在图形程序中的 zbuffer。 我需要存储屏幕上每个像素的 az 值(因此尺寸为 800x800)。

谢谢!

I need to create a 2D int array of size 800x800. But doing so creates a stack overflow (ha ha).

I'm new to C++, so should I do something like a vector of vectors? And just encapsulate the 2d array into a class?

Specifically, this array is my zbuffer in a graphics program. I need to store a z value for every pixel on the screen (hence the large size of 800x800).

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

听风念你 2024-07-11 10:24:29

我可能会创建一个 800*800 的一维数组。 使用像这样的单个分配可能比分配 800 个单独的向量更有效。

int *ary=new int[800*800];

然后,可能将其封装在一个类似于二维数组的类中。

class _2DArray
{
  public:
  int *operator[](const size_t &idx)
  {
    return &ary[idx*800];
  }
  const int *operator[](const size_t &idx) const
  {
    return &ary[idx*800];
  }
};

这里显示的抽象有很多漏洞,例如,如果您访问“行”末尾之后会发生什么? 《Effective C++》一书对用 C++ 编写良好的多维数组进行了很好的讨论。

I might create a single dimension array of 800*800. It is probably more efficient to use a single allocation like this, rather than allocating 800 separate vectors.

int *ary=new int[800*800];

Then, probably encapsulate that in a class that acted like a 2D array.

class _2DArray
{
  public:
  int *operator[](const size_t &idx)
  {
    return &ary[idx*800];
  }
  const int *operator[](const size_t &idx) const
  {
    return &ary[idx*800];
  }
};

The abstraction shown here has a lot of holes, e.g, what happens if you access out past the end of a "row"? The book "Effective C++" has a pretty good discussion of writing good multi dimensional arrays in C++.

我很OK 2024-07-11 10:24:29

你可以做一个向量的向量,但这会产生一些开销。 对于 z 缓冲区,更典型的方法是创建一个大小为 800*800=640000 的数组。

const int width = 800;
const int height = 800;
unsigned int* z_buffer = new unsigned int[width*height];

然后按如下方式访问像素:

unsigned int z = z_buffer[y*width+x];

You could do a vector of vectors, but that would have some overhead. For a z-buffer the more typical method would be to create an array of size 800*800=640000.

const int width = 800;
const int height = 800;
unsigned int* z_buffer = new unsigned int[width*height];

Then access the pixels as follows:

unsigned int z = z_buffer[y*width+x];
最好是你 2024-07-11 10:24:29

好吧,在 Niall Ryan 的基础上,如果性能是一个问题,您可以通过优化数学并将其封装到一个类中来更进一步。

所以我们将从一些数学开始。 回想一下,800 可以写成 2 的幂:

800 = 512 + 256 + 32 = 2^5 + 2^8 + 2^9

因此,我们可以将寻址函数写为:

int index = y << 9 + y << 8 + y << 5 + x;

因此,如果我们将所有内容封装到一个漂亮的类中,我们会得到:

class ZBuffer
{
public:
    const int width = 800;
    const int height = 800;

    ZBuffer()
    {
        for(unsigned int i = 0, *pBuff = zbuff; i < width * height; i++, pBuff++)
            *pBuff = 0;
    }

    inline unsigned int getZAt(unsigned int x, unsigned int y)
    {
        return *(zbuff + y << 9 + y << 8 + y << 5 + x);
    }

    inline unsigned int setZAt(unsigned int x, unsigned int y, unsigned int z)
    {
        *(zbuff + y << 9 + y << 8 + y << 5 + x) = z;
    }
private:
    unsigned int zbuff[width * height];
};

Well, building on what Niall Ryan started, if performance is an issue, you can take this one step further by optimizing the math and encapsulating this into a class.

So we'll start with a bit of math. Recall that 800 can be written in powers of 2 as:

800 = 512 + 256 + 32 = 2^5 + 2^8 + 2^9

So we can write our addressing function as:

int index = y << 9 + y << 8 + y << 5 + x;

So if we encapsulate everything into a nice class we get:

class ZBuffer
{
public:
    const int width = 800;
    const int height = 800;

    ZBuffer()
    {
        for(unsigned int i = 0, *pBuff = zbuff; i < width * height; i++, pBuff++)
            *pBuff = 0;
    }

    inline unsigned int getZAt(unsigned int x, unsigned int y)
    {
        return *(zbuff + y << 9 + y << 8 + y << 5 + x);
    }

    inline unsigned int setZAt(unsigned int x, unsigned int y, unsigned int z)
    {
        *(zbuff + y << 9 + y << 8 + y << 5 + x) = z;
    }
private:
    unsigned int zbuff[width * height];
};
栖迟 2024-07-11 10:24:29

如果您只需要一个实例,您可以在静态存储上分配数组(在文件范围内,或在函数范围内添加static 限定符)。

int array[800][800];

void fn()
{
    static int array[800][800];
}

这样它就不会进入堆栈,并且您不必处理动态内存。

You can allocate array on static storage (in file's scope, or add static qualifier in function scope), if you need only one instance.

int array[800][800];

void fn()
{
    static int array[800][800];
}

This way it will not go to the stack, and you not have to deal with dynamic memory.

心凉 2024-07-11 10:24:29

您需要大约 2.5 兆,因此仅使用堆就可以了。 除非需要调整矢量大小,否则不需要矢量。 有关使用“2D”堆的示例,请参阅 C++ FAQ Lite大批。

int *array = new int[800*800];

(完成后,不要忘记删除[]它。)

You need about 2.5 megs, so just using the heap should be fine. You don't need a vector unless you need to resize it. See C++ FAQ Lite for an example of using a "2D" heap array.

int *array = new int[800*800];

(Don't forget to delete[] it when you're done.)

一萌ing 2024-07-11 10:24:29

到目前为止,每一篇文章都将内存管理留给了程序员。 这是可以而且应该避免的。 ReaperUnreal 与我所做的非常接近,除了我使用向量而不是数组,并且还制作维度模板参数并更改访问函数 - 哦,只是 IMNSHO 清理一下:

template <class T, size_t W, size_t H>
class Array2D
{
public:
    const int width = W;
    const int height = H;
    typedef typename T type;

    Array2D()
        : buffer(width*height)
    {
    }

    inline type& at(unsigned int x, unsigned int y)
    {
        return buffer[y*width + x];
    }

    inline const type& at(unsigned int x, unsigned int y) const
    {
        return buffer[y*width + x];
    }

private:
    std::vector<T> buffer;
};

现在你可以分配堆栈上的这个二维数组很好:

void foo()
{
    Array2D<int, 800, 800> zbuffer;

    // Do something with zbuffer...
}

我希望这有帮助!

编辑:从 Array2D::buffer 中删除了数组规范。 感谢安德烈亚斯抓住了这一点!

Every post so far leaves the memory management for the programmer. This can and should be avoided. ReaperUnreal is darn close to what I'd do, except I'd use a vector rather than an array and also make the dimensions template parameters and change the access functions -- and oh just IMNSHO clean things up a bit:

template <class T, size_t W, size_t H>
class Array2D
{
public:
    const int width = W;
    const int height = H;
    typedef typename T type;

    Array2D()
        : buffer(width*height)
    {
    }

    inline type& at(unsigned int x, unsigned int y)
    {
        return buffer[y*width + x];
    }

    inline const type& at(unsigned int x, unsigned int y) const
    {
        return buffer[y*width + x];
    }

private:
    std::vector<T> buffer;
};

Now you can allocate this 2-D array on the stack just fine:

void foo()
{
    Array2D<int, 800, 800> zbuffer;

    // Do something with zbuffer...
}

I hope this helps!

EDIT: Removed array specification from Array2D::buffer. Thanks to Andreas for catching that!

最近可好 2024-07-11 10:24:29

您可以做的一件事是使用 VC 更改堆栈大小(如果您确实想要堆栈上的数组),执行此操作的标志是 [/F](http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.80).aspx)

但您可能想要的解决方案是将内存放在堆中而不是堆栈中,因为您应该使用向量向量

下面一行声明了一个包含 800 个元素的向量,每个元素都是一个包含 800 个 int 的向量,从而使您无需手动管理内存。

std::vector<std::vector<int> > arr(800, std::vector<int>(800));

请注意两个右尖括号 (> >) 之间的空格,这是为了消除它与右移运算符的歧义(在 C++0x)。

One thing you can do is change the stack size (if you really want the array on the stack) with VC the flag to do this is [/F](http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.80).aspx).

But the solution you probably want is to put the memory in the heap rather than on the stack, for that you should use a vector of vectors.

The following line declares a vector of 800 elements, each element is a vector of 800 ints and saves you from managing the memory manually.

std::vector<std::vector<int> > arr(800, std::vector<int>(800));

Note the space between the two closing angle brackets (> >) which is required in order disambiguate it from the shift right operator (which will no longer be needed in C++0x).

一口甜 2024-07-11 10:24:29

不过,凯文的例子很好:

std::vector;   缓冲区[宽度*高度]; 
  

应该

std::vector<T> buffer;

稍微扩展一下,您当然可以添加运算符重载而不是 at() 函数:

const T &operator()(int x, int y) const
{
  return buffer[y * width + x];
}

T &operator()(int x, int y)
{
  return buffer[y * width + x];
}

示例:

int main()
{
  Array2D<int, 800, 800> a;
  a(10, 10) = 50;
  std::cout << "A(10, 10)=" << a(10, 10) << std::endl;
  return 0;
}

Kevin's example is good, however:

std::vector<T> buffer[width * height];

Should be

std::vector<T> buffer;

Expanding it a bit you could of course add operator-overloads instead of the at()-functions:

const T &operator()(int x, int y) const
{
  return buffer[y * width + x];
}

and

T &operator()(int x, int y)
{
  return buffer[y * width + x];
}

Example:

int main()
{
  Array2D<int, 800, 800> a;
  a(10, 10) = 50;
  std::cout << "A(10, 10)=" << a(10, 10) << std::endl;
  return 0;
}
謸气贵蔟 2024-07-11 10:24:29

有一种类似 C 的做法:

const int xwidth = 800;
const int ywidth = 800;
int* array = (int*) new int[xwidth * ywidth];
// Check array is not NULL here and handle the allocation error if it is
// Then do stuff with the array, such as zero initialize it
for(int x = 0; x < xwidth; ++x)
{
    for(int y = 0; y < ywidth; ++y)
    {
         array[y * xwidth + x] = 0;
    }
}
// Just use array[y * xwidth + x] when you want to access your class.

// When you're done with it, free the memory you allocated with
delete[] array;

您可以使用简单的 get 和 set 方法将 y * xwidth + x 封装在类中(可能会重载 [] 运算符 if您想开始学习更高级的 C++)。 如果您刚刚开始使用 C++ 并且没有开始为 n 维数组创建可重用的完整类模板,我建议您慢慢地进行此操作,这只会让您在开始时感到困惑。

一旦您开始图形工作,您可能会发现额外的类调用的开销可能会减慢您的代码。 不过,不必担心这一点,除非您的应用程序速度不够快,并且您可以对其进行分析以显示时间损失的位置,而不是使其在开始时更加难以使用并可能造成不必要的复杂性。

我发现 C++ lite FAQ 非常适合提供此类信息。 特别是您的问题的答案是:

http:// www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16

There's the C like way of doing:

const int xwidth = 800;
const int ywidth = 800;
int* array = (int*) new int[xwidth * ywidth];
// Check array is not NULL here and handle the allocation error if it is
// Then do stuff with the array, such as zero initialize it
for(int x = 0; x < xwidth; ++x)
{
    for(int y = 0; y < ywidth; ++y)
    {
         array[y * xwidth + x] = 0;
    }
}
// Just use array[y * xwidth + x] when you want to access your class.

// When you're done with it, free the memory you allocated with
delete[] array;

You could encapsulate the y * xwidth + x inside a class with an easy get and set method (possibly with overloading the [] operator if you want to start getting into more advanced C++). I'd recommend getting to this slowly though if you're just starting with C++ and not start creating re-usable fully class templates for n-dimension arrays which will just confuse you when you're starting off.

As soon as you get into graphics work you might find that the overhead of having extra class calls might slow down your code. However don't worry about this until your application isn't fast enough and you can profile it to show where the time is lost, rather than making it more difficult to use at the start with possible unnecessary complexity.

I found that the C++ lite FAQ was great for information such as this. In particular your question is answered by:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16

﹂绝世的画 2024-07-11 10:24:29

或者你可以尝试这样的事情:

boost::shared_array<int> zbuffer(new int[width*height]);

你应该仍然能够这样做:

++zbuffer[0];

不再担心管理内存,没有自定义类需要照顾,而且很容易扔掉。

Or you could try something like:

boost::shared_array<int> zbuffer(new int[width*height]);

You should still be able to do this too:

++zbuffer[0];

No more worries about managing the memory, no custom classes to take care of, and it's easy to throw around.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文