13

私はマージソート関数を書いていますが、今はテストケース配列を使用しています (入力はありません - これは今のところ静的です)。配列を引数として渡す方法がわかりません。ここに私のコードがあります:

//merge sort first attempt

#include <iostream>

#include <algorithm>

#include <vector>

int mergeSort(int[]);
int main() {
    int originalarray[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
    mergeSort(originalarray[]);
}

int mergeSort(int[] originalarray) {
    int num = (sizeof(originalarray) / sizeof(int));
    std::vector < int > original(num);

    if (num > 2) {
        return num;
    }

    // Fill the array using the elements of originalarray
    // This is just for demonstration, normally original will be a parameter,
    // so you won't be filling it up with anything.
    std::copy(originalarray, originalarray + num, original.begin());

    // Create farray and sarray of the appropriate size
    std::vector < int > farray(num / 2);
    std::vector < int > sarray(num - farray.size());

    // Fill those using elements from original
    std::copy(original.begin(), original.begin() + farray.size(), farray.begin());
    std::copy(original.begin() + farray.size(), original.end(), sarray.begin());

    mergeSort(farray);
    mergeSort(sarray);
}

この mergeSort 関数は機能しないことに注意してください。それらをマージする方法がまだわかっていないためです (それが私の課題です)。それを処理する前に 2 つのベクトルを並べ替えたいのですが、引数として配列を渡す必要があるため、これをコンパイルできません。私はポインターを理解していないので、それが解決策である場合、私の言い訳は無知です。私は現在、C++ を第一言語としてプログラミングを学んでおり、言語の機能の基本的な理解しかできていません。助けてくれてありがとう。

4

7 に答える 7

33

これを少し拡張すると、C++ 配列は正確にC 配列であることを思い出してください。したがって、あなたが持っているのは、何かの配列であると主張する(保証なしで)メモリの一部のアドレスだけです。

アップデート

よし、もう少し拡大しよう。

C (したがって C++) には、実際には「配列」自体がありません。持っているのはアドレス、ポインターだけです。したがって、何かを「配列」にすると、実際に起こることは、変数がアドレスを表すことをコンパイラーに伝えることです。

C で宣言定義を区別すると便利です。宣言では、何かに名前と型を与えるだけです。定義では、実際にスペースを割り当てます。

したがって、次のような配列を定義することから始めると、

int ar[100];

つまり、100intのスペースが必要であり、すべてを 1 つのチャンクに割り当てて、その名前を使用することをコンパイラに伝えているということですar。演算子はsizeof、型またはオブジェクトによって使用されるバイト数を与えるため、配列arは 100×sizeof(int)バイトを使用します。ほとんどのマシンでは 400 バイトですが、マシンによって異なります。

変数を定義すると

int * ar_p;   // using '_p' as a reminder this is a pointer

アドレスを含む変数のスペースを定義しています。そのサイズは になりsizeof(int*)、通常は 4 または 8 になりますが、一部のマシンでは、すぐに遭遇する可能性が低いマシンでは 2 から 16 になる可能性があります。

配列の名前ar. コンパイラはその名前をアドレスに変換するので、そのアドレスを次のように保存できます

ar_p = ar ;     // THIS WORKS

arここで、便宜上、配列がたまたまメモリ内の 1000 番地から始まったとしましょう。

その名前にはスペースが割り当てられてarいません。それは定数、数値のようなものです。したがって、その割り当てを元に戻すことはできません

ar = ar_p ;     // THIS WON'T WORK

言えなかったのと同じ理由で

1000 = ar_p ;   // THIS WON'T WORK EITHER

つまり、1000 の値を変更することはできません。 「2」の値は 3 です。)

C の配列は常に 0 ベースです。つまり、最初のインデックスは常に 0 です。他のすべてのインデックスは、インデックスを使用して計算された単なるアドレスです。したがって、ar[0]はアドレス 1000 にオフセットの 0 バイトを加えたもの、 つまり1000ar[1]です。実際、これは C では常に当てはまります。int

これを配列参照と呼びます。

構文を使用する*ar_pと、コンパイラに に含まれるアドレスの AT を取得するように指示しますar_p。`。

これは、ポインターの逆参照と呼ばれます。

私たちが言うなら

ar_p = ar;

次に、同じことを参照してください*ar_par[0]

ar[0]コンパイラに から 0 バイトのアドレスにあるものが欲しいと伝えていると言うときarar[1]からのアドレス 1intまたは 4 バイトarです。と同じものを*(ar_p+3)指しar[3]ます。(最初にアドレスに 3 を追加してから内容を確認するため、かっこが必要です。 最初に*ar_p+3が指す内容を取得しap_p、次にそれらに 3 を追加します。

問題は、C は配列が実際にどれだけ大きいかを知らない、またはあまり気にしないということです。私がやってきて を実行するar[365]と、コンパイラはセル 1000+(365× sizeof(int)) を調べるコードを喜んで生成します。それが配列にある場合は問題ありませんが、それが単なるランダム メモリである場合も問題ありません。C 気にしない。

(C は電話会社から来ていることを思い出してください。「私たちは気にしません。気にする必要はありません。私たちは電話会社です。」)

これで、いくつかのルールがわかったので、ここに移動しました。「≡」は「と同等」または「と同じ」と読み替えてください。

頼れるもの:

  • foo(TYPE t[])foo(TYPE * t)

C はポインターと配列の違いを認識しないため、どちらを宣言してもかまいません。関数を定義するときは、次のように記述できます。

void foo(int[] ar){

また

void foo(int* ar){

まったく同じ効果が得られます。

  • t[i]*(t+i)

これは上でした。どこに書いar[i]ても、 に置き換えることができます*(ar+i)。(実際には、これを破る奇妙な副次的なケースがありますが、初心者として遭遇することはありません。)

  • どこでTYPE *t、プラス(t+i)のアドレスに等しくなりますti*sizeof(TYPE)

これについても上で説明しました。のように配列にインデックスを付ける場合ar[42]、開始アドレスから 42 番目が必要であることを意味します。したがって、 を使用している場合は、intどんなに幅が広いとしても 42 倍以上移動する必要があります。つまり、 です。intsizeof(int)

ここまでが C です。C++ は C の「種類」として定義されているため、C++ についてもすべて当てはまります。を除外する

  • untilは、 andTYPEをオーバーロードするユーザー定義型です。operator[]operator*

C++ では、他の型と同じように機能する新しい型を定義することを決定できますが、言語が特定のことを行う方法を変更することができます。したがって、プログラマーは、配列参照およびポインター逆参照演算子のデフォルトの動作を独自の工夫で「オーバーロード」、つまり置換することを決定できます。初心者として、すぐに直面するべきではありませんが、認識しておく必要があります。

于 2009-04-18T18:24:11.530 に答える
19

そんな使い方はいけませんsizeof(originalarray)/sizeof(int)。静的に宣言された配列に対してのみ機能します (サイズはコンパイル時に認識されます)。一緒にサイズを渡す必要があります。vector配列から a を作成して、代わりに渡してみませんか?

補足:経験則として、常にsizeofコンパイル時に変換されることに注意してください。したがって、引数として渡された配列のサイズを知る方法はありません。

于 2009-04-18T18:04:15.633 に答える
4

が含まれているよう<vector>です。配列のすべての使用をやめて、vectorクラスのみを使用することをお勧めします。vector こちらなどの STL コンテナーの使用例を見ることができます。

于 2009-04-18T18:07:02.990 に答える
2
  • 配列を関数に渡すと、表記に関係なく、配列の最初の要素へのポインターに減衰します。したがって、sizeof期待どおりに動作しません。

  • 配列を渡すときは、どこで停止するかがわかるように、配列サイズを渡すのが最善です。追加パラメーターとして追加します。

于 2009-04-18T18:06:03.930 に答える
0

上記のすべての回答に加えて、c-faq.comからアレイに関するQ&Aを確認することもできます:http://c-faq.com/aryptr/index.html

于 2009-04-18T19:42:24.837 に答える
0

残念ながら、C や C++ でやりたいことを正確に行うのは非常に困難です。次のように、固定サイズの配列を渡すことができます。

int mergeSort(int originalarray[20])
{
    // do something
}

ただし、配列のサイズは数値では定義されず、初期化リストの要素数によって定義されます。

あなたの場合にやるべきことは(実際には間違ったことですが)、2つのステップでそれを行うことです:

int originalarray[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
const size_t arraySize = sizeof originalarray / sizeof originalarray[0];
int mergeSort(int array[arraySize])
{
    // do something
}

残念なことに、必要な処理が行われません。このような関数に配列を渡すと、配列のコピーが作成され、並べ替えのポイントは元の配列を変更することになります。

実は、「ポインター」の概念を理解しないと、先に進むことはできません。

実際に開発する必要がある関数は次のようになります。

int originalarray[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
const size_t arraySize = sizeof originalarray / sizeof originalarray[0];

int mergeSort(int *array, const size_t size)
{
    // do something
}

mergeSort(&(originalArray[0]), arraySize);

つまり、最初の要素へのポインターと要素の数を渡します。

または、ベクトルを扱うこともできます。ベクターは、同じ 2 つのもの (最初の要素へのポインターとサイズ) を「オブジェクト」と呼ばれる 1 つのエンティティにカプセル化します。さらに、メモリを管理するため、必要に応じて要素の数を拡張できます。これが C++ のやり方です。残念ながら、配列のように {...} でベクトルを初期化することはできません。

于 2009-04-18T20:35:05.570 に答える
0

std::vector を使用するだけで十分だと思うのに、動的に割り当てられた配列とベクトルの両方を使用しているようです。

まず、入力配列を std::vector に変更し、入力データを入力します。

int main()
{
   std::vector<int> originalarray;
   for (int data = 1; data <= 10; data++)
   {
      originalarray.push_back(data);
   }
   mergeSort(originaldata);
}

ここで、std::vector への参照を取得するために、mergesort 関数を宣言することが重要です。

int mergeSort(std::vector<int>& originalarray)
{
   // The rest of your code, note that now you are passing 
   // in your array for sorting, so you can continue with your code to split
   // the vector into farray and sarray

   // then call sort on your halves.
   mergeSort(farray);
   mergeSort(sarray);

   // I'm guessing at this point you'd write code to combine your farray sarray, and
   // put it back into originalarray...don't forget to clear original array first!
}

インプレースソートを行っていないように見えるので、大量のデータをコピーしているため、ソートに時間がかかることを期待してください。

于 2009-04-19T03:36:40.440 に答える