typedef固定長配列


210

24ビットのデータ型を定義する必要がありますchar[3]。型を表すために使用しています。することができます私のtypedef char[3]type24?コードサンプルで試してみました。typedef char[3] type24;ヘッダーファイルに入れました。コンパイラはそれについて文句を言わなかった。しかしvoid foo(type24 val) {}、Cファイルで関数を定義すると、文句を言われました。type24_to_int32(type24 val)ではなくのような関数を定義できるようにしたいと思いますtype24_to_int32(char value[3])

回答:


320

typedefは

typedef char type24[3];

ただし、結果の型は配列型であるため、これはおそらく非常に悪い考えですが、ユーザーは配列型であることを認識しません。関数の引数として使用すると、値ではなく参照によって渡され、sizeofため forは正しくありません。

より良い解決策は

typedef struct type24 { char x[3]; } type24;

後者には実装で定義された署名があるため、おそらくのunsigned char代わりに使用することもできますchar


10
typedefされた配列をパラメーターとして渡すことに関連するコーナーケースを説明する素晴らしいドキュメントはありますか?関数がパラメータを取る場合、例えばtype24 foo、何のサイズ、タイプ、および意味だろうfoo*foo**foo&foo、およびを&&foo?そのような表現の意味と合法性は長年にわたって変化しましたか?
スーパーキャット2012

4
24ビットのデータ型は、RGB画像データのように異なる定義のパッキングセマンティクスで何かにマップすることを目的としている可能性があるため、おそらく構造パッキングの警告に言及する価値があります。
sh1 2013年

2
@ sh1:私が認識しているすべての最新の実世界のABIで-誤って調整されたアクセスが非常に高価なものであっても-構造は、メンバーが構造を持たない場合よりも強い調整要件を取得しません。もちろん、OPやこのアプローチを使用する他のユーザーは、プログラムの動作や移植性に問題があるかどうか、私の主張を確認する必要があります。
R .. GitHub STOP HELPING ICE 2013

4
@R ..これの一部は誤解を招く-Cでは、配列は常に参照によって渡されます。つまり、関数に引数として渡された配列を変更すると、関数のコンテキストだけでなくグローバルに変更されます。これは、Cの配列は常に値によって渡されていると言うこともできます。これは、呼び出し先スタックのスタックにコピーされる最初の要素のアドレスを渡すだけだからです。ただし、どちらの場合も、答えは誤解を招くものです。
バイボ2014

1
@bobbogo:それはパッキングではなく、ただの不要なパディングの非挿入です。構造のアライメントがアライメント1.有する全てがそのメンバーのいずれかのちょうど最大のアラインメントである
Rを.. GitHubのSTOPはICE手助けする

49

あなたが欲しい

typedef char type24[3];

Cの型宣言はそのように奇妙です。その型の変数を宣言する場合、変数名が行く場所に型を正確に置きます。


33

R .. 'sの答え

ただし、結果の型は配列型であるため、これはおそらく非常に悪い考えですが、ユーザーは配列型であることを認識しません。関数の引数として使用すると、値ではなく参照によって渡されるため、そのためのsizeofは正しくありません。

それが配列だとわからないユーザーは、おそらく次のようなものを書きます(失敗します):

#include <stdio.h>

typedef int twoInts[2];

void print(twoInts *twoIntsPtr);
void intermediate (twoInts twoIntsAppearsByValue);

int main () {
    twoInts a;
    a[0] = 0;
    a[1] = 1;
    print(&a);
    intermediate(a);
    return 0;
}
void intermediate(twoInts b) {
    print(&b);
}

void print(twoInts *c){
    printf("%d\n%d\n", (*c)[0], (*c)[1]);
}

次の警告が表示されてコンパイルされます。

In function intermediate’:
warning: passing argument 1 of print from incompatible pointer type [enabled by default]
    print(&b);
     ^
note: expected int (*)[2]’ but argument is of type int **’
    void print(twoInts *twoIntsPtr);
         ^

そして、次の出力を生成します。

0
1
-453308976
32767

13

Cでは、配列を関数パラメーターとして値で渡すことはできません。

あなたは構造体に配列を置くことができます:

typedef struct type24 {
    char byte[3];
} type24;

そしてそれを値で渡しますが、もちろん使用するのはあまり便利ではありません:のx.byte[0]代わりにx[0]

関数はtype24_to_int32(char value[3])実際には値ではなくポインタで渡されます。これは、とまったく同じだtype24_to_int32(char *value)、と3無視されます。

ポインタを渡すことに満足している場合は、配列に固執して次のようにすることができます。

type24_to_int32(const type24 *value);

これは、最初の要素へのポインターではなく、配列へのポインターを渡すので、次のように使用します。

(*value)[0]

あなたが誤って書くvalue[1]と何か愚かなことが起こるので、私はそれが本当に利益であることを確信していません。


2
この答えは、decayどこかで用語を言及することで改善できると思います(おそらく、配列を返すと状況が悪化する-まったく機能しないことを指摘することで)。
Frerich Raabe、2015

9

配列型を関数の引数またはテンプレートパラメータとして適切に使用するには、typedefの代わりに構造体を作成し、次に構造体にを追加しoperator[]て、次のような機能のような配列を保持できるようにします。

typedef struct type24 {
  char& operator[](int i) { return byte[i]; }
  char byte[3];
} type24;

type24 x;
x[2] = 'r';
char c = x[2];

14
これはCの質問であり、C ++ではありません。また、Cにも存在するものはありませchar&operator[]
マイケルモリス

3

typedef配列が混乱して矛盾する理由の簡単な例を次に示します。他の回答は回避策を提供します。

#include <stdio.h>
typedef char type24[3];

int func(type24 a) {
        type24 b;
        printf("sizeof(a) is %zu\n",sizeof(a));
        printf("sizeof(b) is %zu\n",sizeof(b));
        return 0;
}

int main(void) {
        type24 a;
        return func(a);
}

これは出力を生成します

sizeof(a) is 8
sizeof(b) is 3

パラメータとしてのtype24はポインタだからです。(Cでは、配列は常にポインターとして渡されます。)gcc8コンパイラーは、ありがたいことに、デフォルトで警告を発行します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.