Cで配列のみを含む構造体を宣言するのはなぜですか?


131

私は以下を含むいくつかのコードに遭遇しました:

struct ABC {
    unsigned long array[MAX];
} abc;

このような宣言を使用するのはいつ意味がありますか?

回答:


184

配列を値によって関数に渡すか、関数から値によって配列を返すことができます。

これらのコンテキストでポインタに減衰する配列とは異なり、構造体は値で渡すことができます。


1
インラインではない関数の場合、16バイトまたは32バイト以上の配列でこれを行うことに注意してください。呼び出し先がすでに破棄できるtmpコピーが必要でない限り、const-referenceで渡す方が効率的です。呼び出し/戻りが最適化されない場合、中規模から大規模な配列(数千バイト)は、値で渡すのはひどいものです。
Peter Cordes

85

もう1つの利点は、サイズが抽象化されるため、[MAX]そのようなオブジェクトを宣言するときはどこでもコード全体を使用する必要がないことです。これは、

typedef char ABC[MAX];

しかし、あなたははるかに大きな問題を抱えています:あなたはそれABCが配列型であることを認識しなければなりません(タイプの変数を宣言するときにこれを見ることができないとしてもABC)さもなければ、ABC何か違うことを意味するという事実に悩まされるでしょう関数の引数リストと変数の宣言/定義の比較。

もう1つの利点は、構造体を使用すると、多くのコードを書き直さなくても、後で必要に応じて要素を追加できることです。


45

構造体をコピーして、関数から構造体を返すことができます。

配列でそれを行うことはできません-構造体の一部でない限り!


26

このようにコピーできます。

struct ABC a, b;
........
a = b;

配列の場合は、memcpy関数またはループを使用して各要素を割り当てる必要があります。


6
(そのため、よりクリーンなコードが可能です-速度などに違いはありません)
John Carter

3
それは便利です。残念ながら、(a == b)の場合はできません!?!それがいかに矛盾しているか。C ++の場合は、==演算子を探します。Cでは、「バイナリ==のオペランドが無効です。
Matt

11

structを使用して、stringのような新しいタイプのデータを作成できます。あなたが定義することができます:

struct String {
    char Char[MAX];
};

または、関数の引数で使用したり、メソッドで返すことができるデータのリストを作成できます。=のようないくつかの演算子をサポートでき、その中にいくつかのメソッドを定義できるため、構造体は配列よりも柔軟性があります。

それがあなたに役立つことを願っています:)


2
基本的に、これはCがクラスを作成するのに最も近いものです。それを指摘することに最も近いので、私はこの答えが好きです。
Nate CK

1
Cのメソッドのようなものはありません。Cの構造体は古いデータです。これは、(他の回答がこれを行うための理由であることを示し)、デフォルトでサポートされている=演算子を持っているが、これは誤解を招くと、主にC ++ではなく、Cに適用される
ジョナサン・スタンバーグ

3
@J Sternberg:「メソッド」は、サブルーチンが影響を与えるデータ「オブジェクト」に関連しているとサブルーチンを考える単なる方法です。Cで動作する「オブジェクト」と「メソッド」の「クラス」を作成することは確かに可能です。言語はそのようなことを正式に定義していません。Cでより優れた抽象化を作成したい場合は、通常、構造体に物を詰め込むことが最善の方法です。
Nate CK

さらに、Cでメソッドを本当に「作成」したい場合は、関数ポインタ(はい、はい、トリッキーな構文、データ保護なしなど)を使用して、関数を操作対象のデータに関連付けることができます。Cの関数内にこのポインターが自動的に作成されることはないため、最初の引数に「self」を渡す必要があります(必要に応じて「this」という名前を付けることもできます)。もちろん、すべての体操で、 C ++ではデフォルトでこのようになっていますが、ボーナスとしてオーバーヘッドが隠されている可能性があります...
BenPen

2

そのようなaを使用するもう1つの利点structは、そのようなa が使用される場合は常に保証structが適用されることです。特に、異なる目的で使用される同じサイズの配列で構成される2つの型がある場合、これらの型は、誤って配列を不適切に使用することを防ぐのに役立ちます。

配列をでラップしない場合structでも、そのtypedefためにを宣言できます。これには、次のような利点がありstructます。–•型が1回宣言される•サイズが自動的に正しい•コードの意図がより明確になる•コードはより保守しやすくなります–しかし、厳密な型安全性、◦型の値をコピーして返す機能、および残りのコードを壊さずに後でメンバーを追加する機能が失われます。typedef特定のタイプのベア配列の2 つのは、サイズが異なる場合にのみ異なるタイプを生成します。あなたが使用している場合また、typedefせずに*関数の引数で、それは同等ですchar *劇的型の安全性を減らすこと、。

要約すると

typedef struct A_s_s { char m[113]; } A_s_t; // Full type safey, assignable
typedef char   A_c_t[113];                   // Partial type-safety, not assignable

A_s_t          v_s(void);     // Allowed
A_c_t          v_c(void);     // Forbidden

void           s__v(A_s_t);     // Type-safe, pass by value
void           sP_v(A_s_t *);   // Type-safe
void           c__v(A_c_t);     // UNSAFE, just means char * (GRRR!)
void           cP_v(A_c_t *);   // SEMI-safe, accepts any array of 113

1

構造体には、OOPメモリ管理パラダイムのいくつかの利点をエミュレートする配列の初期化、コピー、およびfini関数を含めることができます。実際、この概念を拡張して汎用のメモリ管理ユーティリティを作成し(sizeof()構造体を使用して管理されているバイト数を正確に把握することにより)、ユーザー定義の構造体を管理することは非常に簡単です。Cで記述されたスマートプロダクションコードベースの多くはこれらを頻繁に使用し、そのスコープが非常にローカルでない限り、通常は配列を使用しません。

実際、構造に埋め込まれた配列の場合、この配列にアクセスしたいときはいつでも、境界チェックなどの他の「スマートなこと」を実行できます。繰り返しになりますが、配列のスコープが非常に限られている場合を除き、配列のスコープを使用してプログラム間で情報を渡すことはお勧めできません。遅かれ早かれ、夜に目を覚まし、週末を台無しにするバグに遭遇します。


これは、なぜ配列のみstructを含むを使用するのかという質問には答えません。
PJTraill 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.