10

配列を型定義することは可能ですか?

3 つの float の配列である float へのポインターをすべて受け入れるベクトル関数のセットがあります。float* vec3_t を typedef することはできますが、括弧内の配列と等しく設定するだけではオブジェクトを作成できません。

typedef float* vec3_t;

vec3_t a = {1,1,1}; // Does not work
vec3_t b = (float[]){1,1,1}; // Works
float c[] = {1,1,1}; // Works

void f(vec3_t x);

f({1,1,1}); // Error
f((float[]){1,1,1}; // OK

誰かがなぜこれがこのように機能するのか説明してもらえますか?

4

3 に答える 3

7

ポインタと配列は同じものではありません。多くの場合、これらは同じように動作しますが、大きな違いがあり、そのうちの 1 つを発見しました。

コードが実際に行うこと

説明を明確にするために、typedefed 型を real 型に置き換えました。

float c[] = {1,1,1};

配列を作成して初期化しただけです

f({1,1,1});

上記のコードは左辺値でも右辺値でもありません。{val1,...,valn}構文はイニシャライザにすぎず、他の場所では使用できません。

float* b = (float[]){1,1,1};

ここでは、配列を作成して初期化し、その位置をポインターに格納しました

f((float[]){1,1,1};

このケースは上記のケースと同じですが、ポインターを格納する代わりに、引数として関数に渡します。

float* a = {1,1,1};

まだ割り当てられていないメモリ位置に 3 つの変数を書き込もうとしています。一般に{valn,...,valn}、 は初期化子です。この時点では、初期化するものは何もありません。したがって、この構文は無効です。まだ製造されていないキャニスターにガスを注入しようとしています。

あなたが達成したいことは理解していますが、メモリとポインターの概念全体を誤解しているようです。次のコードを想像してみてください。これは (いくつかの汚いロジックでは) 実行しようとしているものと同等です。

float* a = NULL;
a[0] = 1;
a[1] = 1;
a[2] = 1;

このコードを実行するとどうなりますか?

これで、コンパイラが禁止する理由がわかりました。

于 2013-06-10T13:31:59.520 に答える
2

コードにさまざまな機能を積み上げなければならないため、「なぜこれがこのように機能するのか」という意味が明確ではありません。「これ」って具体的に何?

とにかく、「配列を型定義する」ためには、ポインターではなく配列を型定義する必要があります

typedef float vec3_t[3];

その後、あなたはできるようになります

vec3_t a = { 1, 1, 1 };

コードの残りの部分は、配列の型定義とは関係ありません。複合リテラル構文を発見しただけです。これ(non-scalar-type) { initializers }は、指定された型の名前のない一時オブジェクトを作成します。この(non-scalar-type)部分は、複合リテラル構文の重要な部分です。省略できません。

だから、代わりに

vec3_t a = { 1, 1, 1 };
f(a);

名前付き配列オブジェクトを気にしない場合はa、単純に行うことができます

f((vec3_t) { 1, 1, 1 });

また

f((float [3]) { 1, 1, 1 });

また

f((float []) { 1, 1, 1 });

同じ効果で。

于 2013-06-11T06:42:33.573 に答える
1

配列はポインターではありません。配列はベース ポインターがあります。とにかく、達成しようとしていることに最も適したデータ構造は次のようになります。

struct{
    float x;
    float y;
    float z;
} myFloat3;

配列は可変長の構造体などには便利ですが、3 つの浮動小数点数があり、それを利用できることがわかっているため、最も効率的な選択ではありません。コンパイラーが大量の構造体を効率的にパックし、必要なものだけをヒープから割り当てます。

于 2013-06-10T14:13:17.240 に答える