2

fread()Cでファイルからメモリと配列データを割り当てる最も健全な方法は何だろうか.

まず、説明:

int32_t longBuffer;

ここで、longBuffer を freading すると、コードは次のようになります。

fread(&longBuffer, sizeof(longBuffer), 1, fd); //version 1
fread(&longBuffer, sizeof(int32_t), 1, fd); //version 2

2 つのうち、バージョン 1 はよりバグセーフであると言えます。なぜなら、 の型がlongBuffer変更された場合 (たとえば に)、を新しい型で更新するのint16_tを忘れることを心配する必要がないからです。fread()sizeof()

ここで、データの配列の場合、コードは次のように記述できます。

//listing 1
int8_t *charpBuffer=NULL; //line 1
charpBuffer = calloc(len, sizeof(int8_t)); //line 2
fread(charpBuffer, sizeof(int8_t), len, fd); //line 3

sizeof(<type>)ただし、これは最初の例で明らかになった問題を示しています。の型を変更するときに命令を同期することを忘れないように注意する必要がありcharpBufferます (たとえば、 からint8_t*int16_t*)。

したがって、次のように書こうとするかもしれません。

fread(charpBuffer, sizeof(charpBuffer[0]), len, fd); //line 3a

よりバグセーフなバージョンとして。2行目の割り当ての後、書き込みcharpBuffer[0]は完全に有効になるため、これは機能するはずです。

また、次のように書くこともできます。

fread(charpBuffer, sizeof(*charpBuffer), len, fd); //line 3b

ただし、次のようなメモリ割り当てに対して同じことを試みます。

charpBuffer = calloc(len, sizeof(charpBuffer[0])); //line 2a

構文は優れていますが、未定義の動作を示します。これは、この段階でcharpBuffer[0]結果を書き込むと、NULL ポインターが逆参照されるためです。また、次のように書いています。

charpBuffer = calloc(len, sizeof(*charpBuffer)); //line 2b

は同じ問題を示します。

だから、今の質問:

  1. コードの「2b行目」と「3b行目」は正しいですか(この質問の未定義の動作は無視してください)、または「2a行目/3a行目」や「2行目」 /3"?

  2. 「リスト 1」のコードを記述し、未定義の動作を回避する最もバグの少ない方法は何でしょうか?

編集(いくつかの側面を明確にするため):

議論は間違った方向に進みました。コンパイル時間と実行時間の問題は 1 つの問題です (これについても標準的な保証が必要ですが、それはトピックではありません)。また、 sizeof(NULL dereferencing) の未定義の動作の問題は別のものです。コンパイル時であっても、これが標準によってUBにならないように保証されているとは確信していません。規格は何らかの保証を提供していますか?

4

2 に答える 2

3

sizeofあなたはオペレーターについて間違った考えを持っているようです。この演算子はコンパイル時に評価されるため、プログラムの実行中に渡された式が評価される可能性はありません。

sizeof演算子のコンテキストでは*charBuffercharBuffer[0]対応するメモリが使用可能になる前または後に使用されたかどうかに関係なく、両方とも安全です。型の名前を入力するのを避ける方法にすぎないため、重複が少なくなります。

編集

以下にコメントされているように、コンパイル時に評価されるルールには注目すべき例外がありsizeofます (ただし、質問に投稿されたコードには関係ありません)。C および C++ では可変長配列を自動変数として使用できるため、sizeofこれらを適用すると、実際には実行時のオーバーヘッドが発生する場合があります。

未定義の動作に対するあなたの恐れに関しては、次の理由から、その根拠はないと思います。

int vla[n]; // declare a variable-length array of length n

/* The compiler will produce code using the value of n prior to
   declaring the array to compute its size. */
x = sizeof(vla);

/* The space for the array is already available, so the expression 
   *vla is not UB anywhere (except if n is 0). Furthermore, n is 
   not involved in the computation and the operator can be evaluated at 
   compile-time. */
y = sizeof(*vla);

z = sizeof(vla[0]); // same thing
于 2013-02-28T09:05:19.257 に答える
2

C99 6.5.4.3.2 から (強調鉱山):

このsizeof演算子は、そのオペランドのサイズ (バイト単位) を返します。これは、式または括弧で囲まれた型の名前の場合があります。サイズは、オペランドの型から決定されます。結果は整数です。 オペランドの型が可変長配列型の場合、オペランドが評価されます。それ以外の場合、オペランドは評価されず、結果は整数定数になります。

オペランドが「評価されない」ということは、sizeof(charBuffer[0])orにアクセスしても完全に安全であることを意味しsizeof(*charBuffer)ます。これらの式はその型に対してのみ使用されるためです。同じページの例 3 ではsizeof array / sizeof array[0]、配列内の要素数を計算するためのイディオムを明示的に文書化していますが、空の配列には有効ではないという言及や暗示はありません。

于 2013-02-28T10:21:13.243 に答える