24

セグメンテーション違反でクラッシュするはずのコードのスニペットに出くわしましたが、それでも問題なく動作します。問題のコードと関連するデータ構造は次のとおりです (関連するコメントはすぐ上にあります)。

typedef struct {
  double length;
  unsigned char nPlaced;
  unsigned char path[0];
}


RouteDefinition* Alloc_RouteDefinition()
{
  // NB: The +nBags*sizeof.. trick "expands" the path[0] array in RouteDefinition
  // to the path[nBags] array
  RouteDefinition *def = NULL;
  return (RouteDefinition*) malloc(sizeof(RouteDefinition) + nBags * sizeof(def->path[0]));
}

なぜこれが機能するのですか?char * のサイズは、指定されたアーキテクチャのポインターのサイズに解決されると思いますが、ポインターを逆参照しているときにクラッシュして燃えてはいけませNULL?

4

3 に答える 3

19

なぜこれが機能するのですか?

これは、sizeofがコンパイル時の構成であるため機能しますが、可変長配列はまったく評価されません。C99 ドラフト標準セクションを見ると、6.5.3.4 sizeof 演算子の段落2には次のように書かれています (強調鉱山):

[...] サイズは、オペランドの型から決定されます。結果は整数です。オペランドの型が可変長配列型の場合、オペランドが評価されます。それ以外の場合、オペランドは評価されず、結果は整数定数になります。

これを確認するパラグラフ5の次の例も参照してください。

double *dp = alloc(sizeof *dp);
       ^^^                ^
                          |                                 
                          This is not the use of uninitialized pointer 

コンパイル時に、結果を計算するために式の型が決定されます。次の例でこれをさらに実証できます。

int x = 0 ;
printf("%zu\n", sizeof( x++ ));

これはインクリメントxしません。これはかなりきちんとしています。

アップデート

sizeof (x++) が x をインクリメントないのはなぜですか?コンパイル時の操作には例外がありsizeof、それはオペランドが可変長配列 ( VLA ) の場合です。私は以前にそれを指摘していませんでしたが、6.5.3.4上記の引用はこれを言っています.

ただし、C99 とは対照的に C11 ではsizeof、この場合に評価されるかどうかは指定されていません。

また、この質問の C++ バージョンがあることに注意してください: Sizeof が適用される式を評価しないと、C++ で sizeof 内の null または無効なポインターを逆参照することが合法になりますか? .

于 2013-11-05T10:28:36.210 に答える
10

sizeof演算子は純粋なコンパイル時の操作です。実行時には何も行われないため、正常に動作します。

ちなみに、pathメンバーは実際にはポインターではないため、技術的にはできませんNULL

于 2013-11-05T09:26:48.443 に答える
3

それsizeofが純粋にコンパイル時の構成であると述べることは(現在の既存の回答がそうであるように)、完全に正確ではありません。C99 以降sizeof、純粋にコンパイル時の構成ではありません。のオペランドはsizeof実行時に評価され、オペランド タイプは VLA です。これまでに投稿された回答は、その可能性を無視しているようです。

VLA が含まれていないため、コードは問題ありません。ただし、このようなものは別の話になる可能性があります

unsigned n = 10;
int (*a)[n] = NULL; // `a` is a pointer to a VLA 

unsigned i = 0;
sizeof a[i++];      // applying `sizeof` to a VLA

C99 標準によれば、 の引数はsizeof評価されることになっています (つまりi、インクリメントされることになっています。 https://ideone.com/9Fv6xCを参照してください)。a[0]ただし、ここでのヌルポイント逆参照が未定義の動作を生成することになっていることは完全にはわかりません。

于 2015-06-10T21:52:55.440 に答える