Cの変数の「真の」サイジングはどの程度役に立ちますか?


9

Cのポジティブな機能(実際にはgcc、clangなどの実装の機能)として常に直感的に私を驚かせた1つのことは、実行時に独自の変数の隣に隠された情報を格納しないという事実です。つまり、たとえば「uint16_t」タイプの変数「x」が必要な場合、「x」が2バイトのスペースしか占有しないことを確認できます(そのタイプなどの非表示の情報は含まれません)。 。)。同様に、100個の整数の配列が必要な場合は、100個の整数と同じ大きさであることを確認できます。

しかし、より多くの私はそれが実際に持っている場合はより多くの私は疑問に思って、この機能のための具体的なユースケースを思い付くしようとしています任意のすべての実用的な利点を。私がこれまでに思いつくことができる唯一のことは明らかにそれがより少ないRAMを必要とするということです。AVRチップなどの限られた環境では、これは間違いなく大きなプラスですが、日常のデスクトップ/サーバーのユースケースでは、それはかなり無関係であるようです。私が考えている別の可能性は、ハードウェアへのアクセス、またはメモリ領域のマッピング(たとえば、VGA出力など)に役立つ/重要である可能性があることです... ...

私の質問:この機能なしでは実装できないか、非常に煩雑にしか実装できない具体的なドメインはありますか?

PSより良い名前があるかどうか教えてください!;)



@gnat私はあなたの問題が何であるかを理解していると思います。複数の答えがある可能性があるからですよね?まあ、私はこの質問がstackexchangeが機能する方法に適さないかもしれないことを理解しますが、正直にそれ以外の場合はどこに質問すればよいかわかりません...
Thomas Oltmann

1
@lxrec RTTIはvtableに格納され、オブジェクトはvtableへのポインタのみを格納します。さらに、型には、virtualメンバー関数があるために既にvtableがある場合にのみRTTIがあります。したがって、RTTIはオブジェクトのサイズを大きくすることはなく、定数によってバイナリを大きくするだけです。

3
@ThomasOltmann 仮想メソッドを持つすべてのオブジェクトには、vtableポインターが必要です。これがないと、仮想メソッドの機能を使用できません。さらに、仮想メソッド(したがって、vtable)を使用することを明示的に選択します。

1
@ThomasOltmannあなたはとても混乱しているようです。これは、vtableポインターを運ぶオブジェクトへのポインターではなく、オブジェクト自体です。T *つまり、常に同じサイズでありT、vtableを指す非表示フィールドが含まれる場合があります。また、C ++コンパイラは、 vtableを必要としないオブジェクトにvtableを挿入したことはありません。

回答:


5

いくつかの利点があります。明らかなのは、コンパイル時に、関数のパラメーターなどが渡される値と確実に一致するようにすることです。

しかし、私はあなたが実行時に何が起こっているかについて尋ねていると思います。

コンパイラーは、実行する操作にデータ型の知識を埋め込むランタイムを作成することに注意してください。メモリ内のデータの各チャンクは自己記述的ではない場合がありますが、コードは本質的にそのデータが何であるかを知っています(ジョブを正しく実行した場合)。

実行時には、あなたが考えるよりも少し異なります。

たとえば、uint16_tを宣言するときに2バイトのみが使用されると想定しないでください。プロセッサとワードアラインメントに応じて、スタック上で16、32、または64ビットを占有できます。ショーツの配列は、予想よりもはるかに多くのメモリを消費することがあります。

これは、特定のオフセットでデータを参照する必要がある特定の状況で問題になる可能性があります。これは、ワイヤレスリンクまたはファイルを介して、プロセッサアーキテクチャが異なる2つのシステム間で通信するときに発生します。

Cでは、ビットレベルの粒度で構造体を指定できます。

struct myMessage {
  uint8_t   first_bit: 1;
  uint8_t   second_bit: 1;
  uint8_t   padding:6;
  uint16_t  somethingUseful;
}

この構造は3バイト長で、shortは奇数オフセットで開始するように定義されています。また、定義したとおりになるようにパックする必要があります。それ以外の場合、コンパイラーはメンバーをワード位置合わせします。

コンパイラーはこのデータを抽出してレジスターにコピーするために舞台裏でコードを生成し、それを使用して便利なことを実行できます。

これで、私のプログラムがmyMessage構造体のメンバーにアクセスするたびに、それを抽出して操作する方法が正確にわかることがわかります。

これは、異なるバージョンのソフトウェアを使用する異なるシステム間で通信するときに問題が発生し、管理が困難になる可能性があります。システムとコードを慎重に設計して、両側でデータ型の定義がまったく同じになるようにする必要があります。これは、環境によっては非常に困難な場合があります。ここで、Googleのプロトコルバッファなどの自己記述型データを含むより良いプロトコルが必要になります。

最後に、デスクトップ/サーバー環境でこれがどれほど重要であるかを尋ねるのが良いでしょう。それは実際に使用する予定のメモリの量に依存します。画像処理などを実行している場合、大量のメモリを使用することになり、アプリケーションのパフォーマンスに影響を与える可能性があります。これは、メモリが制限されており、仮想メモリがない組み込み環境では、常に常に問題になります。


2
「ショーツの配列が予想よりもはるかに多くのメモリを消費する場合があります。」これはCでは間違っています。配列には、ギャップのない方法で要素が含まれることが保証されています。はい、単一のと同様に、配列を適切に配置する必要がありますshort。しかし、これは配列の開始のための1回限りの要件であり、残りは連続しているために自動的に正しく整列されます。
cmaster-モニカを2016年

また、パディングの構文が間違っていuint8_t padding: 6;ます。最初の2ビットと同じように、でなければなりません。または、より明確には、コメントのみ//6 bits of padding inserted by the compiler。あなたが書いたように、この構造のサイズは3バイトではなく、少なくとも9バイトです。
cmaster-モニカを2016年

9

これが役立つ唯一の理由の1つである外部データ構造のマッピングにアクセスしました。これらには、メモリマップされたビデオバッファー、ハードウェアレジスタなどが 含まれますまた、SSL証明書、IPパケット、JPEG画像など、プログラムの外部でそのまま送信されるデータ、およびプログラムの外部で永続的な寿命を持つほとんどすべてのデータ構造が含まれます。


5

Cは低レベル言語であり、ほぼ移植可能なアセンブラーであるため、そのデータ構造と言語構造は金属に近い(データ構造には、ハードウェアとABIによって課されるパディング、配置、サイズの制約を除いて、隠れたコストはありません)。そのため、Cには、ネイティブの動的型付けはありません。ただし、必要な場合は、すべての値がいくつかの型情報(例:...)で始まる集約であるという規則を採用できます。使用-s及び(アレイのようなもののための)フレキシブル配列メンバに、アレイのサイズを含みます。enumunionstruct

(Cでプログラミングする場合、特に事前条件と事後条件および不変条件を定義、文書化、および従うのはユーザーの責任です。また、C動的メモリ割り当てではfree、ヒープmallocメモリーゾーンをだれにするかについて明示的な規則が必要です)

したがって、ボックス化された整数、文字列、または一種のSchemeのような記号、または値のベクトルである値を表すには、概念的には、タグ付きユニオン(ポインターのユニオンとして実装)を使用します。-例:

enum value_kind_en {V_NONE, V_INT, V_STRING, V_SYMBOL, V_VECTOR};
union value_en { // this union takes a word in memory
   const void* vptr; // generic pointer, e.g. to free it
   enum value_kind_en* vkind; // the value of *vkind decides which member to use
   struct intvalue_st* vint;
   struct strvalue_st* vstr;
   struct symbvalue_st* vsymb;
   struct vectvalue_st* vvect;
};
typedef union value_en value_t;
#define NULL_VALUE  ((value_t){NULL})
struct intvalue_st {
  enum value_kind_en kind; // always V_INT for intvalue_st
  int num;
};
struct strvalue_st {
  enum value_kind_en kind; // always V_STRING for strvalue_st
  const char*str;
};
struct symbvalue_st {
  enum value_kind_en kind; // V_SYMBOL
  struct strvalue_st* symbname;
  value_t symbvalue;
};
struct vectvalue_st {
  enum value_kind_en kind; // V_VECTOR;
  unsigned veclength;
  value_t veccomp[]; // flexible array of veclength components.
};

動的な型の値を取得するには

enum value_kind_en value_type(value_t v) {
  if (v.vptr != NULL) return *(v.vkind);
  else return V_NONE;
}

以下は、ベクトルへの「動的キャスト」です。

struct vectvalue_st* dyncast_vector (value_t v) {
   if (value_type(v) == V_VECTOR) return v->vvect;
   else return NULL;
}

ベクトル内の「安全なアクセサー」:

value_t vector_nth(value_t v, unsigned rk) {
   struct vectvalue_st* vecp = dyncast_vector(v);
   if (vecp && rk < vecp->veclength) return vecp->veccomp[rk];
   else return NULL_VALUE;
}

通常は、上記の短い関数のほとんどをstatic inlineヘッダーファイルのように定義します。

ところで、Boehmのガベージコレクターを使用できる場合は、いくつかの高レベル(ただし安全ではない)スタイルで非常に簡単にコーディングでき、いくつかのSchemeインタープリターがそのように行われます。可変個ベクトルコンストラクターは

value_t make_vector(unsigned size, ... /*value_t arguments*/) {
   struct vectvalue_st* vec = GC_MALLOC(sizeof(*vec)+size*sizeof(value));
   vec->kind = V_VECTOR;
   va_args args;
   va_start (args, size);
   for (unsigned ix=0; ix<size; ix++) 
     vec->veccomp[ix] = va_arg(args,value_t);
   va_end (args);
   return (value_t){vec};
}

3つの変数がある場合

value_t v1 = somevalue(), v2 = otherval(), v3 = NULL_VALUE;

あなたはそれらを使用してベクトルを構築することができます make_vector(3,v1,v2,v3)

Boehmのガベージコレクターを使用したくない場合(または独自のガベージコレクターを設計したくない場合)は、デストラクタを定義し、誰が、どのように、いつメモリをfree-dにするかをドキュメント化する際に十分注意する必要があります。この例を参照してください。したがってmallocGC_MALLOC上記の代わりに(ただし、その失敗に対してテストする)を使用できますが、デストラクタ関数を慎重に定義して使用する必要がありますvoid destroy_value(value_t)

Cの長所は、上記のようなコードを可能にし、独自の規則(特にソフトウェア)を定義するのに十分な低レベルであることです。


あなたは私の質問を誤解したと思います。Cで動的型付けをしたくありません。Cのこの特定のプロパティが実用的であるかどうか知りたいと思いました。
Thomas Oltmann、2016年

しかし、Cの正確なプロパティは何を参照していますか?Cデータ構造は金属に近いため、隠れたコストはありません(配置とサイズの制約を除く)
Basile Starynkevitch

まさにそれ:/
トーマス・オルトマン'17年

Cは低レベル言語として発明されましたが、最適化がオンになっている場合、gccなどのコンパイラーは低レベル構文を使用する言語を処理しますが、プラットフォームが提供する動作保証への低レベルアクセスを確実に提供しません。手の込んだアドレス計算のためのmallocやmemcpyのが、使用を使用するための一つのニーズはsizeofは、「現代」C.ではサポートされないかもしれません
supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.