sizeof(x ++)がxを増分しないのはなぜですか?


505

以下は、dev c ++ウィンドウでコンパイルされたコードです。

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d and ", sizeof(x++)); // note 1
    printf("%d\n", x); // note 2
    return 0;
}

私は期待してx実行した後、6であることを注記1を。ただし、出力は次のとおりです。

4 and 5

注1のx後、なぜ増加しないのか、誰か説明できますか?


37
DevC ++は非常に古い古いコンパイラーを使用しているので、Codeblocks EclipseやVisual Studioなどの新しいIDEにアップグレードすることをお勧めします
Tom J Nowell

4
++ xは、x ++、cygwinおよびgcc 3.4.4と同じ結果を生成します。
イエナン

6
@Tim Pletzcker最初の値は変数のサイズであり、変数自体ではありません。
Surfbutler、

これがマージされたため、私の回答が追加されました。私の回答はVLAs、他のどれも実行していない場合に、ランタイム評価を取得する方法を実際に示しています。
Shafik Yaghmour 2014

2
この種の書き込みは、望ましくない副作用を引き起こす可能性があるため、避けてください。あなたの質問はすでにそれらの1つです。
user666412 2016

回答:


537

C99標準から(強調は私のものです)

6.5.3.4/2

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


51
「オペランドの型が可変長配列型の場合、オペランドが評価されます」wow!気づかなかった
コス

6
可変長配列型とはどういう意味ですか?それは、オペランドが配列であることを意味しますか?この場合のコードは配列ではありません。整理してくれますか?
Neigyl R. Noval

37
可変長配列とは、たとえばNstdinから読み取ってmakeを実行した場合など、コンパイル時にサイズが不明な値で宣言された配列ですint array[N]。これはC99機能の1つであり、C ++では使用できません。
Kos、

21
@LegendofCage、特にこれはsizeof(int[++x])、(とにかく本当に本当に悪い考え)の++ようなもので評価できることを意味します。
Jens Gustedt、

3
@Joe Wreschnig:それはによって評価されgccclang及びでideone.com/Pf7iF
JFS

190

sizeofコンパイル時の演算子であるため、コンパイル時にsizeofそのオペランドが結果値に置き換えられます。オペランドはされ評価されないすべてで(それが可変長配列である場合を除きます)。結果のタイプのみが重要です。

short func(short x) {  // this function never gets called !!
   printf("%d", x);    // this print never happens
   return x;
}

int main() {
   printf("%d", sizeof(func(3))); // all that matters to sizeof is the 
                                  // return type of the function.
   return 0;
}

出力:

2

short私のマシンでは2バイトを占めています。

関数の戻り値の型を次のように変更しますdouble

double func(short x) {
// rest all same

8出力として与えます。


12
たまに-可能であればコンパイル時です。
Martin Beckett、

10
-1。これは受け入れられた(そして正しい)回答と矛盾し、標準を引用していないためです。
sam hocevar

1
文字列を操作するときに、sizeof()演算子のコンパイル時の解決には素晴らしい利点があります。strlen()を使用する代わりに、引用符付き文字列として初期化された文字列があり、文字列を構成する文字配列が実行時にnullターミネーターをスキャンする必要がある場合、sizeof(quoted_string)はコンパイル時にわかります。したがって、実行時。それは小さなことですが、引用符で囲まれた文字列をループで何百万回も何百万回も使用すると、パフォーマンスに大きな違いが生じます。
user2548100 2014年

ループで実際に何百万回も使用する場合は、ループから長さ計算を因数分解する方がはるかに賢明ではないでしょうか。何百万もの異なるハードコードされた定数がコードに含まれていないことを願っています。:-o
Veky

47

sizeof(foo) コンパイル時に式のサイズを見つけるために本当に一生懸命試みます:

6.5.3.4:

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

つまり、可変長配列、実行時に実行されます。(注:可変長配列は特定の機能であり、で割り当てられた配列ではありませんmalloc(3)。)それ以外の場合は、式のタイプとコンパイル時のタイプのみが計算されます。


33

sizeofコンパイル時の組み込み演算子であり、関数ではありません。これは、括弧なしで使用できる場合に非常に明確になります。

(sizeof x)  //this also works

1
しかし、これはどのように質問への答えですか?
セバスチャンマッハ

5
@phresnel:これは、sizeofが「奇妙」であり、通常の関数の規則の対象ではないことを明確にするためです。とにかく投稿を編集して、(+)や(-)などの通常のランタイムオペレーターとの混乱の可能性を排除しました
hugomg

sizeofオペレーターはコンパイル時のオペレーターではなく、これを理解するためにVLAを与えるだけです。
paxdiablo

21

注意

この回答は、遅れた日付を説明する複製からマージされました。

元の

可変長配列を 除いて、sizeofは引数を評価しません。これは、ドラフトC99標準セクション6.5.3.4 のsizeofオペレーターの段落2から確認できます。

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

コメント(現在は削除されています)は、このようなものが実行時に評価されるかどうかを尋ねました:

sizeof( char[x++]  ) ;

そして、確かにそれは、このようなものも機能します(両方をライブで見る):

sizeof( char[func()]  ) ;

どちらも可変長配列だからです。とはいえ、どちらにも実用的な使い方はあまり見られません。

可変長配列は、C99標準ドラフトセクションの6.7.5.2 配列宣言子のパラグラフ4で説明されています。

[...]サイズが整数定数式で、要素の型が既知の定数サイズである場合、配列型は可変長配列型ではありません。それ以外の場合、配列型は可変長配列型です。

更新

C11では、VLAケースの答えが変わります。特定のケースでは、サイズ式が評価されるかどうかは指定されていません。6.7.6.2 配列宣言子のセクションから:

[...]サイズ式がsizeof演算子のオペランドの一部であり、サイズ式の値を変更しても演算子の結果に影響がない場合、サイズ式が評価されるかどうかは不定です。

たとえば、次のような場合(ライブで見る):

sizeof( int (*)[x++] )

1
ここで覚えておくべき重要なことは、ほとんどの場合、sizeof事実上マクロであるということです。これはコードを作成するのではなく、期待値を事前に計算し、それをコードに直接デポジットします。VBAが存在しなかったため、これがC99までの唯一の動作であったことに注意してください(この答えが出るまで実際に聞いたことがない、信じられない
かもしれ

式の値との新しい値を決定する以外のsizeof (char[x++]);目的で、の値をどのように使用しますか。どちらもその演算子では正常です。xx++x
スーパーキャット2014

@alk ...ええ、ええ、もちろん「VLA」を意味しました:) Shafik-なぜそれらは実行時に評価されるのですか?私が言ったように、私はVLAを見たことがありませんが、それらのタイプはどちらもコンパイル時にわかっていますね。
Corley Brigman、2014

@CorleyBrigmanの標準的な答えはb / cですが、理由はコンパイル時に配列サイズがわからないため、実行時に式を評価する必要があるためです。VLAは興味深いトピックですここここに、2つの投稿があります
Shafik Yaghmour 2014

@Shafik-ああ、そうか、char[x++]VLAだと気づかなかったのは私の混乱だと思う。それはchar*私の知らない人の目には効果的に似ています。
Corley Brigman、2014

11

sizeof演算子のオペランドは評価されないので、これを行うことができます:

int f(); //no definition, which means we cannot call it

int main(void) {
        printf("%d", sizeof(f()) );  //no linker error
        return 0;
}

オンラインデモ:http : //ideone.com/S8e2Y

つまり、関数のみをf使用する場合は、関数を定義する必要はありませんsizeof。この手法は、C ++でもオペランドがsizeof評価されないため、C ++テンプレートのメタプログラミングで主に使用されます。

なぜこれが機能するのですか?これは、sizeof演算子がvalueではなく式のタイプを操作するため機能します。ですから、あなたが書くときsizeof(f())、それは式の型に作用しますf()、そしてそれは関数の戻り型に他なりませんf。関数が実際に実行した場合に関数が返す値に関係なく、戻り値の型は常に同じです。

C ++では、これを行うこともできます。

struct A
{
  A(); //no definition, which means we cannot create instance!
  int f(); //no definition, which means we cannot call it
};

int main() {
        std::cout << sizeof(A().f())<< std::endl;
        return 0;
}

しかし、ではsizeof、最初にをA書き込んA()でのインスタンスを作成し、次にをf書き込んA().f()でインスタンスの関数を呼び出しているように見えますが、そのようなことは起こりません。

デモ:http : //ideone.com/egPMi

これは、のその他の興味深いプロパティを説明する別のトピックですsizeof


10

コンパイル中は実行できません。したがって、++i/ i++は起こりません。またsizeof(foo())、関数は実行されませんが、正しいタイプが返されます。


2
コンパイル中に実行することはできません。」どういう意味ですか?
curiousguy

1
コンパイルではオブジェクトコードのみが作成されます...オブジェクトコードは、ユーザーがバイナリを実行したときにのみ実行されます。コンパイル時にsizeofが発生するため、i ++がインクリメントすると仮定すると間違っています。
rakesh

sizeofはコンパイル時に発生します」とsizeofは、「コンパイル時の定数式と同様に」という意味ですか。
curiousguy

"#define"が前処理中に発生するように、同様にsizeofはコンパイル時に発生します。コンパイル中にすべての型情報が利用できるので、sizeofがその場で評価され、コンパイル中にそこで値が置き換えられます。すでに@pmgで「C99標準から」と述べたように。
ラケッシュ

1
sizeofはコンパイル時に発生します」可変長配列ではないもの
curiousguy

0

sizeofコンパイル時にx++実行されますが、実行時にのみ評価できます。これを解決するために、C ++標準でsizeofは、オペランドが評価されないように規定されています。C標準は言う:

オペランドの型[of sizeof]が可変長配列型の場合、オペランドが評価されます。そうでない場合、オペランドは評価されず、結果は整数定数になります。


C ++にはVLAはありません。
LF

-2

sizeof() 演算子はデータ型のサイズのみを指定し、内部要素は評価しません。


これは誤りであり、sizeof()演算子は再帰的に動作し、コンテナのすべての要素、クラスまたは構造体のメンバーなどのバイト単位のサイズを取得します。いくつかのメンバーを含む単純なクラスを作成することで、これを非常に簡単に証明できます。それを求めsizeof()ています。(ただし、そこにあるポインターはすべて、サイズを確認できません-ポインターのサイズのみです。)これは、他のコメンターが述べたように、コンパイル時にすべて発生します。内部の式sizeof()は評価されません。
タイラーシェルバーグ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.