[英]Interpret pointer as a two-dimensional array
我有一個數組轉置函數,可以像這樣鏡像數組:
[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
視為二維數組嗎? 如果是這樣,解決辦法是什么?
好了,您的out
變量是一個指向float
的指針,因此在out[v]
對其進行out[v]
引用會產生一個float值。 而且您不能下標浮點值。
您需要執行的操作是,以與計算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];
}
}
}
注意:
遺憾的是您顯然是用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];
聲明數組時使用的指針相同的算術魔術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];
}
}
}
該問題已解決,但我想為您的2D數組處理問題發布C ++類的解決方案。 如果要將指針(基本上是一維數組)視為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;
}
編譯器不知道數組的尺寸。
因此,如果保留簡單的指針,則需要對out
與當前in
相同的尋址算法。
即,計算一個行指針,在該行中使用偏移量,而不要使用out[v][s]
。
從技術上講,
表達式out[v]
生成對float
的引用,並且
如果float表示為f ,則表達式f [s]
就是無效的:您無法索引float
值。
作為一般建議,除非您使用某個在各處使用float
框架,例如SFML,否則只需使用double
。 這是C和C ++中的默認浮點類型。 例如,文字3.14
的類型為double
,而不是float
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.