如何让 VC++ 以二维数组的形式访问指针
Posted
技术标签:
【中文标题】如何让 VC++ 以二维数组的形式访问指针【英文标题】:How to get VC++ to access a pointer as a 2D array 【发布时间】:2014-09-13 08:39:05 【问题描述】:我正在做一些图形编程,我有一个二维数组(在程序执行期间大小会发生变化),我使用 openGL 存储它。
所以当我去访问它时,我得到的只是一个void
pointer 返回。
为了使逻辑更简单,我希望编译器假装它是二维数组,并将其用作二维数组(因为arr[i][j]
比ptr[i * y + j]
更简洁且不易出错)。
我发现这种巧妙的转换方法在 GCC 中运行良好(在 uni 的 linux 机器上):
Vertex (&vertices)[tess][tess] = *reinterpret_cast<Vertex (*)[tess][tess]>(
glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)
);
这基本上将openGL给我的内存块指针转换为tess X tess
2D数组,并创建该类型的引用来指向它。
这让我可以像vertices[i][j]
一样访问内存。Vertex
只是一个 typedef
ed struct
包含 float
s
但是,在我的 Windows 机器上,VS'12 非常适合,抱怨它需要将 tess
写入的整数为 constant
(特别是 error C2057: expected constant expression
)。
我不知道为什么。
现在,我了解 VS doesn't support VLA's,但我不是在此处创建数组,我正在创建对我不知道的东西的 引用 '直到运行时的大小。 所以它不应该关心函数调用之间的大小是否发生变化,对吧?为什么不允许这样做?
不要被吓倒我尝试使用std::array
std::array<std::array<Vertex, tess>, tess>& vertices;
除了明显的references must be initialized
之外,这个测试对我没有帮助,因为它仍然抱怨expression must have a constant value
(特别是;error C2975: '_Size' : invalid template argument for 'std::array', expected compile-time constant expression
)
我不知道在这里尝试什么,我为reinterpret_cast
感到非常自豪,它使事情变得如此简单,并且确定我没有使用违反标准的方法。
我不想从指针创建一个std::vector
,然后在完成后将该动态数组中的数据复制回指针位置;当内存块已经坐在那里时,这似乎效率很低!
没有办法创建一个向量围绕一个预先存在的内存块,是吗? ..不,这听起来很傻。
我想看看能不能做到不放弃,直接用Vertex*
;想法?
有人能告诉我为什么它在 VS 中不起作用吗?
我可以做些什么来让它工作(对 VS 的扩展/更新)?
VS'13 是否增加了对此的支持?
我也收到了我无法解释的错误C2087: 'vertices' : missing subscript
。
除了这些似乎表明 VS 迫切希望 tess
保持不变的其他错误:error C2466: cannot allocate an array of constant size 0
error C2540: non-constant expression as array bound
error C2440: 'initializing' : cannot convert from 'Vertex [1][1]' to 'Vertex (&)[][1]'
【问题讨论】:
它应该关心语言标准是否禁止它。 至于解决方案,您可以考虑一个抽象数组索引数学的小型帮助器类。 @OliCharlesworth,我现在正在尝试。我猜它只是让它变得不那么性感。你能详细说明一下,什么是被禁止的吗? “禁止”可能太强了。 C++ 语言没有定义 VLA,因此编译器没有义务实现它们。 但这就是我的意思,我在这里看不到任何 VLA,这些都是引用和指针强制转换。没有分配新的内存;我有一些数据,我使用语法糖以不同方式解释(并因此访问)它 【参考方案1】:这很有趣;我实现了一个类来处理我想要的。 它不像我想要的那样类型安全,但我从中学到了很多 就像我在发现 jQuery 之前为 javascript 实现应该是规范的一部分、语法糖式的功能一样。
基本上,而不是能够做到这一点。
int (&array)[x][y] = *reinterpret_cast<int (*)[x][y]>(pointer);
你必须这样做
MDAI<int, 2> array = MDAI<int, 2>(pointer, x, y);
但除此之外,它完美无缺! :D 我最初只写了一个专门的 TwoDArray 类,但发现我实际上也有一些 3D 数组。 因此,我没有实现 3D 版本(当您向下钻取时返回 TwoDArray),而是做了一些更通用的东西,可以帮助您处理任意多维的数组。
#include <Windows.h>
#include <iostream>
/*MultiDimensional Array Interpretation
has the compiler use a flat pointer reference as if it were a faceted array
C++11/GCC VLA-supporting equivalent:
int (&array)[x][y] = *reinterpret_cast<int (*)[x][y]>(pointer);
using MDAI, <C++11 and MSVS compatible:
MDAI<int, 2> array = MDAI<int, 2>(pointer, x, y);
*/
template<class Type, unsigned int dimension>
class MDAI
private:
Type* array;
//+1 to guard against zero-length-array
unsigned int bounds[dimension + 1];
public:
//unfortunately I can't use `unsigned int &(dimensions)[dimension]` to make it safe
//because of how operator[]() tries to construct its return value
/*constructor*/
MDAI(Type* array, unsigned int* bounds)
: array(array)
std::copy(bounds, bounds + dimension, this->bounds);
/*programmer usable constructor for typing of the dimensions, instead of having to declare an array*/
MDAI(Type* array, ...)
: array(array)
va_list arguments;
va_start(arguments, array);
for (int index = 0; index < dimension; ++index)
bounds[index] = va_arg(arguments, unsigned int);
va_end(arguments);
/*drills down one level into the multi dimensional array*/
MDAI<Type, dimension - 1> operator[](unsigned index)
if (dimension < 1)
std::cerr << "MDAI is not an array.\n";
throw 1;
if (index < 0 || index >= bounds[0])
std::cerr << "Index out of bounds.\n";
throw 1;
//figure out how many addresses to jump
for (unsigned int index2 = 1; index2 < dimension; ++index2)
index *= bounds[index2];
return MDAI<Type, dimension - 1>(array + index, bounds + 1);
/*'dereferences' the array to get a reference to the stored value*/
Type& operator*()
if (dimension > 0)
std::cerr << "MDAI is an array.\n";
throw 1;
return *array;
/*allows the compiler to automagically 'convert' the MDAI into whatever the user thinks it is*/
operator Type&()
return **this;
/*makes assignment work automagically too!*/
MDAI<Type, dimension>& MDAI<Type, dimension>::operator=(Type value)
**this = value;
return *this;
;
测试边界 2-4-3 的三维数组:
void main(unsigned int argC, char** argV)
using namespace std;
int array[2][4][3] =
1, 2, 3,
4, 5, 6,
7, 8, 9,
10, 11, 12
,
13, 14, 15,
16, 17, 18,
19, 20, 21,
22, 23, 24
;
//cast array to pointer, then interpret
MDAI<int, 3> mdai((int*)array, 2, 4, 3);
//testing correct memory access
cout << 15 << ' ' << mdai[1][0][2] << endl;
//testing modifcations using mdai are in array
mdai[0][2][1] = -1;
cout << array[0][2][1] << ' ' << mdai[0][2][1] << endl;
//testing modifications in array show up in mdai
array[1][3][2] = -23;
cout << -23 << ' ' << mdai[1][3][2] << endl;
//testing automatic type casting
cout << -15.0 << ' ' << mdai[0][0][1] * -7.5 << endl;
如果我将它保留为数组引用,它会像它一样无缝。
为了编译时安全,我想将operator*()
重新声明为,特别是;Type& MDAI<Type, 0>::operator*()
所以你只能在 operator[]()
仅出现在大于 0 的尺寸
哦,运行时检查必须足够好
【讨论】:
您可以通过将那些if
语句替换为 static_assert
调用来进行编译时检查以上是关于如何让 VC++ 以二维数组的形式访问指针的主要内容,如果未能解决你的问题,请参考以下文章