我有一个数组转置函数,可以像这样镜像数组:
[1][2][3] [1][4][7]
[4][5][6] ===> [2][5][8]
[7][8][9] [3][6][9]
以下是我提出的算法的概念:
size_t numvars = variables.size(), numsegs = segments.size();
for (int v = 0; v < numvars; ++v) {
for (int s = 0; s < numsegs; ++s) {
float * row = in + (s * numvars);
out[v][s] = *(row + v);
}
}
当手动完成算法时,所有工作都按预期进行。我想实现这个函数,它需要两个指向二维数组的指针,一个用于源数组,第二个指向一个用于保存转置数组的内存缓冲区。当我尝试在函数中用C ++实现算法时,我得到以下错误:
void transposeArray(float * in, float * out) throw()
{
size_t numvars = variables.size(), numsegs = segments.size();
for (int v = 0; v < numvars; ++v) {
for (int s = 0; s < numsegs; ++s) {
float * row = in + (s * numvars);
out[v][s] = *(row + v);
}
}
}
out [v] [s] = *(row + v); 数组下标的无效类型'float [int]'
这是因为编译器不知道它应该将第二个float * out
视为二维数组吗?如果是这样,有什么问题?
答案 0 :(得分:2)
嗯,您的out
变量是指向float
的指针,因此在out[v]
中对其进行解反,会产生浮点值。并且您不能下标浮点值。
您需要做的是计算out
2D数组的数组索引,方法与计算in
2D数组的方法相同:
void transposeArray(float * in, float * out) throw() {
size_t numvars = variables.size(), numsegs = segments.size();
for (int v = 0; v < numvars; ++v) {
for (int s = 0; s < numsegs; ++s) {
out[v*numsegs + s] = in[s*numvars + v];
}
}
}
注意:
遗憾的是,你显然是用C ++编写这段代码,而不是用C语言编写代码。因为在C语言中,你可以用一种非常好的方式来编写代码:
void transposeArray(int numvars, int numsegs, float (*in)[numvars], float (*out)[numsegs]) {
for (int v = 0; v < numvars; ++v) {
for (int s = 0; s < numsegs; ++s) {
out[v][s] = in[s][v];
}
}
}
这里的诀窍是in
和out
参数被声明为指向行数组的指针,这允许语言调用它在声明数组时使用的相同指针算术魔法float myArray[numvars][numsegs];
。这个指针算术归结为隐式执行v*numsegs + s
明确执行的操作。
C的优点是,它允许使用运行时大小的数组类型,这是C ++不能做到的。当然,如果numvars
和numsegs
是编译时常量,则可以在C ++中执行等效操作。
如果你想使用与你在问题中给出的函数签名相同的函数签名,可以使用以下函数编写函数:
void transposeArray(float * in, float * out) throw() {
size_t numvars = variables.size(), numsegs = segments.size();
float (*in2D)[numvars] = (void*)in;
float (*out2D)[numsegs] = (void*)out;
for (int v = 0; v < numvars; ++v) {
for (int s = 0; s < numsegs; ++s) {
out2D[v][s] = in2D[s][v];
}
}
}
答案 1 :(得分:1)
问题已经解决,但我想发布一个C ++ - ish解决方案来处理你的2D数组处理问题。如果要将指针(基本上是一维数组)视为仅在运行时已知大小的2D数组,则可以使用以下辅助模板之一。它们不仅使您的代码看起来更好,而且它们还可以帮助您在调试模式下捕获错误的超出范围的索引,并且在发布时它们基本上编译为与您手写的难以读取的代码相同的代码。今天的C ++编译器非常擅长优化这些简单的方法/函数:
#include <assert.h>
#include <stdio.h>
// An implementation that performs range checking on both dimension and
// has nice array subscript syntax. This has some performance overhead
// in debug mode but in release the compiler does the optimization magic.
template <typename T>
class PtrArray2D
{
public:
class SubDim
{
public:
SubDim(T* p, int d1) : m_Ptr(p), m_D1(d1) {}
T& operator[](int d1)
{
assert(d1>=0 && d1<m_D1);
return m_Ptr[d1];
}
const T& operator[](int d1) const
{
assert(d1>=0 && d1<m_D1);
return m_Ptr[d1];
}
private:
T* m_Ptr;
int m_D1;
};
PtrArray2D(T* p, int d0, int d1) : m_Ptr(p), m_D0(d0), m_D1(d1) {}
SubDim operator[](int d0)
{
assert(d0>=0 && d0<m_D0);
return SubDim(m_Ptr + m_D1*d0, m_D1);
}
const SubDim operator[](int d0) const
{
assert(d0>=0 && d0<m_D0);
return SubDim(m_Ptr + m_D1*d0, m_D1);
}
int GetD0() const { return m_D0; }
int GetD1() const { return m_D1; }
private:
T* m_Ptr;
int m_D0;
int m_D1;
};
template <typename T>
inline PtrArray2D<T> MakePtrArray2D(T* p, int d0, int d1)
{
return PtrArray2D<T>(p, d0, d1);
}
template <typename T>
void Transpose(const PtrArray2D<T>& src, PtrArray2D<T>& dest)
{
assert(src.GetD0() == dest.GetD1() && src.GetD1() == dest.GetD0());
for (int i=0,i_e=src.GetD0(); i<i_e; ++i)
{
for (int j=0,j_e=src.GetD1(); j<j_e; ++j)
{
dest[j][i] = src[i][j];
}
}
}
int test()
{
const int DIMENSION0 = 5;
const int DIMENSION1 = 2;
const int ARRAY_SIZE = DIMENSION0*DIMENSION1;
float* p = new float[ARRAY_SIZE];
for (int i=0; i<ARRAY_SIZE; ++i)
p[i] = (float)i;
PtrArray2D<float> arr0(p, DIMENSION0, DIMENSION1);
printf("%f, %f, %f\n", arr0[0][0], arr0[0][1], arr0[1][0]);
arr0[1][0] = 8;
// The statement below will cause an assert as the second dimension is out of range.
//arr0[0][2];
float* q = new float[ARRAY_SIZE];
PtrArray2D<float> arr1(q, DIMENSION1, DIMENSION0);
Transpose(arr0, arr1);
// OR if you want to create helper array object on-the fly only for the time of execution of Transpose():
//Transpose(MakePtrArray2D(p, DIMENSION0, DIMENSION1), MakePtrArray2D(q, DIMENSION1, DIMENSION0));
printf("%f, %f, %f\n", arr1[0][0], arr1[0][1], arr1[1][0]);
return 0;
}
答案 2 :(得分:0)
编译器不知道数组的尺寸。
因此,如果您保留简单的指针,则需要对out
执行与in
相同的寻址算法。
即,计算行指针,使用该行的偏移量,而不是out[v][s]
。
从技术上讲,
表达式out[v]
生成对float
的引用,
如果浮点数表示为 f ,则表达式 f [s]
则无效:您无法索引{{1} } value。
作为一般建议,除非您使用的是一些在任何地方使用float
的框架,例如SFML,否则只需使用float
。这是C和C ++中的默认浮点类型。例如,文字double
的类型为3.14
,而不是double
。