简体   繁体   English

将指针解释为二维数组

[英]Interpret pointer as a two-dimensional array

I have an array transpose function that mirrors arrays like so: 我有一个数组转置函数,可以像这样镜像数组:

[1][2][3]            [1][4][7]
[4][5][6]    ===>    [2][5][8]
[7][8][9]            [3][6][9]

Here is the concept of the algorithm I came up: 这是我提出的算法的概念:

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);
  }
}

When proceeding through the algorithm by hand, all works as expected. 手动执行算法时,所有操作均按预期进行。 I would like to implement the function such that it takes two pointers to two-dimensional arrays, one with the source array and the second to a memory buffer that will hold the transposed array. 我想实现该函数,以便它使用两个指向二维数组的指针,一个指向源数组,第二个指向将保存转置数组的内存缓冲区。 When I try to implement the algorithm in C++ in a function I get the following error: 当我尝试在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); out [v] [s] = *(row + v); invalid types 'float[int]' for array subscript 数组下标的无效类型'float [int]'

Is this because the compiler does not know that it should treat the second float * out as a 2-dimensional array? 这是因为编译器不知道应将第二个float * out视为二维数组吗? If so, what's the fix? 如果是这样,解决办法是什么?

Well, your out variable is a pointer to a float , so derefencing it in out[v] yields a float value. 好了,您的out变量是一个指向float的指针,因此在out[v]对其进行out[v]引用会产生一个float值。 And you can't subscript a float value. 而且您不能下标浮点值。

What you need to do, is to calculate the array index for the out 2D array the same way you calculated it for the in 2D array: 您需要执行的操作是,以与计算in 2D数组相同的方式来计算out 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];
    }
  }
}

Note: 注意:
It's a pity that you are apparently writing this code in C++, and not in C. Because in C, you can do this in a quite nice way: 遗憾的是您显然是用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];
    }
  }
}

The trick here is that the in and out arguments are declared to be pointers to line arrays, which allows the language to invoke the same pointer arithmetic magic that it uses when you declare an array with float myArray[numvars][numsegs]; 这里的窍门是,将inout参数声明为线阵列的指针,这使该语言可以调用与使用float myArray[numvars][numsegs];声明数组时使用的指针相同的算术魔术float myArray[numvars][numsegs]; . This pointer arithmetic boils down to do the same thing implicitly which v*numsegs + s does explicitly. 该指针算法归结为隐式地执行v*numsegs + s显式执行的相同操作。

The advantage of C is, that it allows for array types with run time sizes , something C++ does not do. C的优点是,它允许具有运行时大小的数组类型,而C ++却没有。 Of course, if numvars and numsegs are compile time constants, you can do the equivalent in C++. 当然,如果numvarsnumsegs是编译时间常数,则可以在C ++中执行等效操作。

Here is how you can write your function if you want to use the same function signature as the one you gave in the question: 如果要使用与问题中给出的签名相同的签名,可以使用以下方法编写函数:

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];
    }
  }
}

The problem is already solved but I wanted to post a C++-ish solution to your 2D array handling problem. 该问题已解决,但我想为您的2D数组处理问题发布C ++类的解决方案。 If you want to treat a pointer (basically a 1 dimensional array) as a 2D array whose size is known only at runtime then you could employ one of the following helper templates. 如果要将指针(基本上是一维数组)视为2D数组,其大小仅在运行时才知道,则可以使用以下帮助程序模板之一。 They not only make your code look much nicer but they also help you to catch wrong out-of-range indexes in debug mode and in release they compile basically to the same code as your hand-written harder-to-read code. 它们不仅使您的代码看起来更好,而且还可以帮助您在调试模式下捕获错误的超出范围的索引,并且在发行时,它们基本上可以编译为与手写的难以阅读的代码相同的代码。 Today's C++ compilers are extremely good at optimizing away such simple methods/functions: 当今的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;
}

The compiler doesn't know the dimensions of the array. 编译器不知道数组的尺寸。

So if you keep your simple pointers then you need to do the same addressing arithmetic for out as you currently do for in . 因此,如果保留简单的指针,则需要对out与当前in相同的寻址算法。

Namely, calculate a row pointer, use an offset into that row, instead of out[v][s] . 即,计算一个行指针,在该行中使用偏移量,而不要使用out[v][s]


Technically, 从技术上讲,

  • the expression out[v] produces a reference to a float , and 表达式out[v]生成对float的引用,并且

  • if the float is denoted f , the expression f [s] is then just invalid: you can't index a float value. 如果float表示为f ,则表达式f [s]就是无效的:您无法索引float值。


As general advice, unless you're using some framework that uses float everywhere, such as SFML, then just use double . 作为一般建议,除非您使用某个在各处使用float框架,例如SFML,否则只需使用double That's the default floating point type in C and C++. 这是C和C ++中的默认浮点类型。 Eg, the literal 3.14 is of type double , not float . 例如,文字3.14的类型为double ,而不是float

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM