2

これは非常にばかげた質問だと確信していますが、本当に不意を突かれてしまい、これを正しく考えていないだけかもしれません. OpenGL ES 2.0 を使用しているときに、次のコード行に遭遇しました。

/////////////////////////////////////////////////////////////////
// Forward declarations
static void SceneMeshInitIndices(
   GLushort meshIndices[NUM_MESH_INDICES]);
static void SceneMeshUpdateNormals(
   SceneMeshVertex mesh[NUM_MESH_COLUMNS][NUM_MESH_ROWS]);
static void SceneMeshUpdateMeshWithDefaultPositions(
   SceneMeshVertex mesh[NUM_MESH_COLUMNS][NUM_MESH_ROWS]);

さて、私が不意を突かれたのは、関数が入力を特定のサイズの 2D 配列として宣言しているという事実です。私はこれがこのように行われるのを見たことがなく、それが合法であるかどうか、または Objective-C 内でどのように合法であるかについて完全に確信が持てません。元の配列へのポインターを使用してそれを渡すように常に言われてきましたが、これは奇妙に思えます。誰かが私にこれを説明したり、私が間違っていることを指摘したりできますか?

4

3 に答える 3

7

配列を関数に渡すと、一連の規則に従ってポインターに分解されます。これらは:

1。インダイレクションの最初のレベル (つまり、配列の最初の次元/インデックス) はポインターに分解されるため、そのサイズを指定する必要はありません (また、使用する必要もありません)。

二。配列のそれ以降の次元は変更されず、ポインターに変化しません。これらは指定する必要があり、引数の型の一部です。

そのため、本質的に以下は同等で合法です。

void foo(int arr[]);
void foo(int arr[MAX_ARRAY_SIZE]);
void foo(int *arr);

ただし、以下は同等ではありませんが、合法です。

void foo(int arr[MAX_ROWS][MAX_COLUMNS]);
void foo(int **arr);

以下も同等であり、合法です。

void foo(int arr[MAX_ROWS][MAX_COLUMNS]);
void foo(int arr[][MAX_COLUMNS]);

(そして、これらは として解釈されint (*arr)[MAX_COLUMNS])ます。つまり、配列へのポインタとして解釈されます)。

また、以下は違法です。つまり、コンパイラ エラーが発生します。

void foo(int arr[MAX_ROWS][]);
于 2013-02-13T20:57:34.070 に答える
1

元の配列へのポインターを使用してそれを渡すように常に言われてきました。

元の配列の要素へのポインターを使用するように言われました。これは、配列を値で受け取る関数を宣言しようとすると、要素の型へのポインターを受け取るようにパラメーターの型を「調整」するように言語が指定するためです (事実上、配列のパラメーターの型がそのサイズを忘れてしまいます)。値ではなく参照によって渡されます)。したがって、基本的には、ソース コードが実際の真実を正確かつ明示的に表すように、手動で調整を行うように指示されています。

表示する宣言は、次のアドバイスを使用して宣言することもできます。

static void SceneMeshInitIndices(GLushort *meshIndices);
static void SceneMeshUpdateNormals(SceneMeshVertex (*mesh)[NUM_MESH_ROWS]);
static void SceneMeshUpdateMeshWithDefaultPositions(SceneMeshVertex (*mesh)[NUM_MESH_ROWS]);

int [X][Y]の型が「Y int の X 配列の配列」を意味することは明らかです。したがって、要素の型は「Y int の配列」です。したがって、調整された型は「int (*)[Y]」または「Y int の配列へのポインター」です。


パラメータの型を「調整」するルールは、未加工の配列がひどい理由の 1 つであり、回避できる場合は使用すべきではありません。残念ながら、C にはそれを行う良い方法がありませんが、C++ には がstd::arrayあり、配列が本来あるべき動作をします。つまり、値で渡すことができ、値で返すことができ、要素の型へのポインターに自動的に変換されることはありません (そのため、サイズを忘れることはありません) などです。


たぶん、より多くの例がそれをより明確にするでしょう。あなたが書いた場合:

void foo(int param[10]);

また、パラメーターの型を調整するルールを知らなかった場合は、次のコードでエラーが発生すると予想される場合があります。

int bar[5];
foo(bar); // error? an array of 5 ints is not the expected type.

そして、このコードの場合:

int baz[10];
foo(baz);

bar配列がパラメーターにコピーされ、パラメーターに対して行われた変更が元の配列に影響を与えないことが予想される場合があります。残念ながら、これらの完全に合理的な期待は間違っています。あなたが書いvoid foo(int param[10]);たとき、コンパイラは配列型のパラメータを見て、あなたが書いたのと同じになるようにそれを変更しますvoid foo(int *param);

したがって、あなたが書いfoo(bar)たとき、コンパイラは関数がint*(int[10]あなたが書いた ではなく) を取ることを見て、それが に変換できることを見てbarint *エラーを報告する代わりに、悪意を持って関数を呼び出すプログラムを生成します。記述した型が渡された変数と互換性がなくても、 の本体が次のfoo()ようなことを実行できてもparam[9] *= 2;、これが適切に定義されていることを保証する必要があります。

同様に、foo(baz)は配列を値渡しせず、関数内の配列パラメーターに対して行われることはすべて、実際の配列に対して直接行われます。これは、すべてが値/コピーによって渡される通常の C セマンティクスとはまったく異なり、「参照渡し」の唯一の方法は、別のオブジェクトへの参照として機能するポインター値を取得することです。この場合、コンパイラはユーザーに代わってポインタを自動的に取得し、参照によって配列を効果的に渡します。

void foo(int [10])さらに、書くかどうかvoid foo(int [100])は問題ではないことに注意してください。どちらの場合も、パラメーターの型が調整されint *ているため、それらの宣言は異なっているように見えますが、同じ関数を宣言しています。

于 2013-02-13T20:59:57.570 に答える