2

これら 2 つのコード スニペットの違いを理解するのに苦労しています。

// out is of type char* of size N*D
// N, D are of type int


for (int i=0; i!=N; i++){
    if (i % 1000 == 0){
        std::cout << "i=" << i << std::endl;
    }
    for (int j=0; j!=D; j++) {
        out[i*D + j] = 5;
    }
}

このコードは、非常に大きなデータ セット (N=100000、D=30000) の場合でも問題なく実行されます。ポインター演算について私が理解していることから、これは同じ結果をもたらすはずです:

for (int i=0; i!=N; i++){
    if (i % 1000 == 0){
        std::cout << "i=" << i << std::endl;
    }
    char* out2 = &out[i*D];
    for (int j=0; j!=D; j++) {
        out2[j] = 5;
    }
}

ただし、後者は非常に大きなデータセットでは機能しません(インデックス143886でフリーズします-セグメンテーション違反だと思いますが、Windowsでの開発に慣れていないため、100%確信が持てません)。ポインター演算がどのように機能するかについて明らかな何かが欠けています。char* の進行に関連している可能性がありますか?

編集:問題がインデックスのオーバーフロー (つまり (i*D + j) >= 2^32) であることを確認したので、int32_t の代わりに uint64_t を使用すると問題が解決しました。私にはまだ不明なのは、上記の最初のケースが実行され、他のケースが segfault になる理由です。

4

3 に答える 3

1

配列のサイズとして N を使用する場合、なぜ int を使用するのですか? 配列の負の値には論理的な意味がありますか?

「うまくいかない」とはどういう意味ですか?

ポインターは、「オブジェクト」ではなく、メモリ内のアドレスと考えてください。

char* 
void*
int*

はすべてメモリ アドレスへのポインタであるため、定義されている場合や関数に渡されている場合はまったく同じです。

char * a;
int* b = (char*)a;
void* c = (void*)b;

a == b == c;

違いは、a、a[i] にアクセスする場合、取得される値はアドレス a から次の sizeof(*a) バイトであるということです。

++ を使用してポインターを進める場合、ポインターが設定されているアドレスは

sizeof(pointer_type) bytes.

例:

char* a = 1;
a++;

a は 2 になりました。

((int*)a)++;

a は 6 になりました。

別物:

char* a = 10;
char* b = a + 10;

&(a[10]) == b

結局だから

a[10] == *((char*)(a + 10))

2つの例は同じであるため、例の配列サイズに問題はありません。

編集

ここで、負のメモリ アドレスがないことに注意してください。したがって、符号付き負の値を持つ配列にアクセスすると、値が正に変換されます。

int a = -5;
char* data;
data[a] == data[MAX_INT - 5]

そのため、(符号値を配列サイズとして使用する場合!) 2 つの例で実際には同じ結果が得られない可能性があります。

于 2013-08-21T20:57:40.177 に答える
-1

バージョン 1

for (int i=0; i!=N; i++) // i starts at 0 and increments until N.  Note:  If you ever skip N, it will loop forever.  You should do < N or <= N instead
{
    if (i % 1000 == 0) // if i is a multiple of 1000
    {
        std::cout << "i=" << i << std::endl; // print i
    }

    for (int j=0; j!=D; j++) // same as with i, only j is going to D (same problem, should be < or <=)
    {
        out[i*D + j] = 5; // this is a way of faking a 2D array by making a large 1D array and doing the math yourself to offset the placement
    }
}

バージョン 2

for (int i=0; i!=N; i++) // same as before
{
    if (i % 1000 == 0) // same as before
    {
        std::cout << "i=" << i << std::endl; // same as before
    }

    char* out2 = &out[i*D]; // store the location of out[i*D]
    for (int j=0; j!=D; j++) 
    {
        out2[j] = 5; // set out[i*D+j] = 5;
    }
}

これらは同じことを行っていますが、 がout十分に大きくない場合、どちらも未定義の動作をします (おそらくクラッシュします)。

于 2013-08-21T20:37:13.107 に答える