4

内部で 3x3 行列を使用する関数があるが、API の制限により 9 つの要素の連続した配列を渡す必要がある場合、基本的に配列を 2 次元配列にキャストする方法はありますか?

次のアプローチは機能しませんが、うまくいけば、私がやろうとしていることを伝えることができます.

Eigen のようなパッケージがこれを些細なものにしていることは承知していますが、残念ながら、この場合のオプションではありません。

void transpose_3x3( double x[3][3] ) {
    ...
}

void foo( double x[9], double y[3][3] ) {

    transpose_3x3(y)  // OK    

    double *my_x[3][3] = &x[0];

    transpose_3x3(&my_x);  // How?

}

編集

キャストの正しいターゲットを示してくれた Daniel に感謝し、C++ の reinterpret_cast 関数を使用してこれを達成することができました。ユニオンとmemcpyのソリューションは機能しますが、単一の明示的なキャストが最も簡単なソリューションのようです.IMHO.

#include <iostream>

void print_matrix( double x[3][3] ) {

    for(unsigned int i=0; i< 3; i++) {
        for(unsigned int j=0; j<3; j++) {
            std::cout << x[i][j] << "  ";
        }
        std::cout << std::endl;
    }
}

int main() {

    double a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};    
    double (*b)[3] = reinterpret_cast<double (*)[3]>(a);  

    print_matrix(b);
    return 0;
}
4

5 に答える 5

7

明示的なキャストを行う方法が思いつきません。

ただし、me​​mcpy することもできます。memcpy の呼び出しは、あなたが思っているほど愚かではありません。コンパイラは、多くの場合、2 ビットのデータが実際には同じものを表し、memcpy が固定サイズであり、実際のコピーを最適化します。

あなたがやっているマトリックスほど大きなもので試したことは一度もないと言わなければなりませんが、なぜうまくいかないのかわかりません。

編集:実際、試してみようと思いました。次のコードを書きました。

void transpose_3x3( double (*x)[3][3] ) 
{
    const double t01    = (*x)[0][1];
    const double t02    = (*x)[0][2];
    const double t12    = (*x)[1][2];
    (*x)[0][1] = (*x)[1][0];
    (*x)[0][2] = (*x)[2][0];
    (*x)[1][0] = t01;
    (*x)[1][2] = (*x)[2][1];
    (*x)[2][0] = t02;
    (*x)[2][1] = t12;
}

void foo() 
{
    double x[9] = { 1.0f, 2.0f, 3.0f,
                    4.0f, 5.0f, 6.0f,
                    7.0f, 8.0f, 9.0f };

    double y[3][3];
    memcpy( y, x, sizeof( double ) * 9 );

    transpose_3x3( &y );

    printf( "%f, %f, %f\n", y[0][0], y[0][1], y[0][2] );
    printf( "%f, %f, %f\n", y[1][0], y[1][1], y[1][2] );
    printf( "%f, %f, %f\n", y[2][0], y[2][1], y[2][2] );
}

VS2010でリリースモードでビルドしました。

結果のアセンブリは次のとおりです。

void foo() 
{
00E11000  push        ebp  
00E11001  mov         ebp,esp  
00E11003  and         esp,0FFFFFFC0h  
00E11006  sub         esp,0B8h  
    double x[9] = { 1.0f, 2.0f, 3.0f,
00E1100C  fld1  
00E1100E  push        esi  
00E1100F  fstp        qword ptr [esp+2Ch]  
00E11013  push        edi  
00E11014  fld         qword ptr [__real@4000000000000000 (0E12138h)]  
                    4.0f, 5.0f, 6.0f,
                    7.0f, 8.0f, 9.0f };

    double y[3][3];
    memcpy( y, x, sizeof( double ) * 9 );

    transpose_3x3( &y );

    printf( "%f, %f, %f\n", y[0][0], y[0][1], y[0][2] );
00E1101A  sub         esp,18h  
00E1101D  fstp        qword ptr [esp+50h]  
00E11021  mov         ecx,12h  
00E11026  fld         qword ptr [__real@4008000000000000 (0E12130h)]  
00E1102C  lea         esi,[esp+48h]  
00E11030  fstp        qword ptr [esp+58h]  
00E11034  lea         edi,[esp+90h]  
00E1103B  fld         qword ptr [__real@4010000000000000 (0E12128h)]  
00E11041  fst         qword ptr [esp+60h]  
00E11045  fld         qword ptr [__real@4014000000000000 (0E12120h)]  
00E1104B  fstp        qword ptr [esp+68h]  
00E1104F  fld         qword ptr [__real@4018000000000000 (0E12118h)]  
00E11055  fstp        qword ptr [esp+70h]  
00E11059  fld         qword ptr [__real@401c000000000000 (0E12110h)]  
00E1105F  fst         qword ptr [esp+78h]  
00E11063  fld         qword ptr [__real@4020000000000000 (0E12108h)]  
00E11069  fstp        qword ptr [esp+80h]  
00E11070  fld         qword ptr [__real@4022000000000000 (0E12100h)]  
00E11076  fstp        qword ptr [esp+88h]  
00E1107D  rep movs    dword ptr es:[edi],dword ptr [esi]  
00E1107F  fstp        qword ptr [esp+10h]  
00E11083  fstp        qword ptr [esp+8]  
00E11087  fld         qword ptr [esp+90h]  
00E1108E  fstp        qword ptr [esp]  
00E11091  mov         esi,dword ptr [__imp__printf (0E120A0h)]  
00E11097  push        offset string "%f, %f, %f\n" (0E120F4h)  
00E1109C  call        esi  
    printf( "%f, %f, %f\n", y[1][0], y[1][1], y[1][2] );
00E1109E  add         esp,4  
00E110A1  fld         qword ptr [esp+0C8h]  
00E110A8  fstp        qword ptr [esp+10h]  
00E110AC  fld         qword ptr [esp+0B0h]  
00E110B3  fstp        qword ptr [esp+8]  
00E110B7  fld         qword ptr [__real@4000000000000000 (0E12138h)]  
00E110BD  fstp        qword ptr [esp]  
00E110C0  push        offset string "%f, %f, %f\n" (0E120F4h)  
00E110C5  call        esi  
    printf( "%f, %f, %f\n", y[2][0], y[2][1], y[2][2] );
00E110C7  fld         qword ptr [esp+0D4h]  
00E110CE  add         esp,4  
00E110D1  fstp        qword ptr [esp+10h]  
00E110D5  fld         qword ptr [__real@4018000000000000 (0E12118h)]  
00E110DB  fstp        qword ptr [esp+8]  
00E110DF  fld         qword ptr [__real@4008000000000000 (0E12130h)]  
00E110E5  fstp        qword ptr [esp]  
00E110E8  push        offset string "%f, %f, %f\n" (0E120F4h)  
00E110ED  call        esi  
00E110EF  add         esp,1Ch  
}

memcpy がないことに注意してください。実際には、行列を x から y に手動でコピーし、それを転置して出力するだけです。基本的に、コンパイラが物事を最適化するために何をするかを見るのは興味深い...

編集2:もちろん、水田の優れた反応を見た後、もう少し考えてみると、物事を直接ケースに入れることができると思います

transpose_3x3( (double (*)[3][3])&x );

memcpy またはユニオンなしで動作するもの:D

于 2013-01-09T22:41:33.600 に答える
6

まあ、これは少し汚れていますが、unionwith キャストを使用できます。誰かがこれについて私に告げるつもりですが、とにかく投稿します =)

#include <iostream>

using namespace std;

typedef union {
    double linear[9];
    double square[3][3];
} Matrix;

void transpose_3x3( double x[3][3] )
{
    for( int i = 0; i < 3; i++ ) {
        for( int j = 0; j < 3; j++ ) {
            cout << i << "," << j << ": " << x[i][j] << endl;
        }
    }
}

void foo( double x[9], double y[3][3] )
{
    transpose_3x3( y );
    transpose_3x3( ((Matrix*)x)->square );
}

int main()
{
    double x[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    double y[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
    foo( x, y );
    return 0;
}
于 2013-01-09T22:54:35.320 に答える
3
void transpose_3x3( double x[3][3] ) {
    ...
}

最初の次元3は無視されることに注意してください。のタイプtranspose_3x3は本当に

void transpose_3x3(double (*)[3])

3 つの の配列へのポインターであるにキャストxする必要があります。double (*)[3]double

void foo(double x[9], double y[3][3]) {
    // Note that the actual types of the parameters are
    // double *x
    // double (*y)[3]

    transpose_3x3(y);
    transpose_3x3((double(*)[3])x);
}

もちろん、x渡された要素が 9 要素より短い場合、キャストを渡すと大混乱が生じますが、短すぎるx場合も同様です。y

于 2013-01-10T01:12:15.420 に答える
1

次のように、マトリックスへのポインターを定義できます。

int (*matrix)[M][N];

配列 M*N がある場合、基本的にオブジェクト M*N へのポインターを作成できます。C でサポートされているネイティブ マトリックスは多次元ではありません。

int array[M*N]
int (*matrix)[M][N] = (int (*)[M][N]) array;

マトリックスから要素にアクセスする場合は、反復作業を簡素化する関数を作成できます。

int get(int (*matrix)[M][N], int i, int j) { return (*matrix)[i][j]; }
于 2013-08-21T00:15:55.150 に答える
0

C++ のタグを追加したので、{x, y} パラメーターと {contiguous x} パラメーターを使用してデータにアクセスするためのヘルパー関数を提供することで、これを行う独自のマトリックス クラスを作成することをお勧めします。

あなたが書くコードは、C++ で書かれたプログラムで通常見られるものではありません。同じロジックをよりクリーンで安全に実現できるからです。

于 2013-01-09T22:42:52.433 に答える