web-dev-qa-db-ja.com

Cでは、sizeof演算子は2.5mを渡すと8バイトを返しますが、1.25mを渡すと4バイトを返します* 2

sizeof演算子が次の結果を生成する理由がわかりません。

sizeof( 2500000000 ) // => 8 (8 bytes).

... 8を返し、次のようにすると:

sizeof( 1250000000 * 2 ) // => 4 (4 bytes).

... 8ではなく4を返します(これは私が期待したものです)。誰かがsizeofが式(またはデータ型)のサイズを決定する方法と、私の特定のケースでこれが発生している理由を明確にできますか?

私の推測では、sizeof演算子はコンパイル時の演算子です。

Bounty Question:これらの式を評価し、期待される出力を(キャストせずに)生成できるランタイム演算子はありますか?

65
Jacob Pollack

2500000000intに収まらないため、コンパイラはそれをlong(またはlong long、または収まるタイプ)として正しく解釈します。 1250000000もそうですし、2もそうです。 sizeofへのパラメーターは評価されないので、コンパイラーは乗算がintに収まらないことを認識できない可能性があるため、 intのサイズ。

また、パラメーターが評価された場合でも、オーバーフロー(および未定義の動作)が発生する可能性がありますが、それでも4が発生する可能性があります。

ここに:

#include <iostream>
int main()
{
    long long x = 1250000000 * 2;
    std::cout << x;
}

出力を推測できますか? 2500000000だと思うなら、あなたは間違っているでしょう。式1250000000 * 2の型はintです。これは、オペランドがintintであり、乗算が適合しない場合、自動的に大きなデータ型に昇格されないためです。

http://ideone.com/4Adf97

したがって、ここで、gccはそれが-1794967296であると言いますが、これは未定義の動作であるため、任意の数になる可能性があります。この数はintに収まります。

さらに、オペランドの1つを期待される型にキャストすると(整数以外の結果を探している場合に除算するときに整数をキャストするのと同じように)、次のように機能することがわかります。

#include <iostream>
int main()
{
    long long x = (long long)1250000000 * 2;
    std::cout << x;
}

正しい2500000000を生成します。

123
Luchian Grigore

[編集:最初は、これがCとC++の両方として投稿されていることに気づきませんでした。私はCに関してのみ答えています。]

「実行時に式または変数に割り当てられるメモリの量を決定する方法はありますか?」というフォローアップの質問に答えます。正確ではありません。問題は、これがあまり整形式の質問ではないということです。

(特定の実装とは対照的に)C-the-languageの「式」は、実際にはanyメモリを使用しません。 (特定の実装では、CPUレジスタなどに収まる結果の数に応じて、計算を保持するためのコードやデータメモリが必要です。)式の結果が変数に隠されていない場合、それは単に消えます(そしてコンパイラは保存されていない結果を計算するために、ランタイムコードを省略することがよくあります)。この言語では、存在するとは想定されていないもの、つまり式の格納スペースについて質問する方法はありません。

一方、変数はストレージ(メモリ)を占有します。変数の宣言は、どれだけのストレージを確保するかをコンパイラーに指示します。ただし、C99の可変長配列を除いて、必要なストレージは、実行時ではなく、純粋にコンパイル時に決定されます。これが、sizeof xが一般に定数式である理由です。コンパイラーは、コンパイル時にsizeof xの値を決定できます(実際にはそうしなければなりません)。

C99のVLAは、ルールの特別な例外です。

void f(int n) {
    char buf[n];
    ...
}

bufに必要なストレージは、(一般に)コンパイラーがコンパイル時に見つけることができるものではないため、sizeof bufはコンパイル時定数ではありません。この場合、bufは実際には実行時に割り当てられ、そのサイズはそのときにのみ決定されます。したがって、sizeof bufisランタイムで計算された式。

ただし、ほとんどの場合、すべてがコンパイル時に事前にサイズ設定され、実行時に式がオーバーフローした場合、動作はタイプに応じて未定義、実装定義、または明確に定義されます。 INT_MAXが27億を少し超える場合、25億に2を掛けたような符号付き整数オーバーフローは、「未定義の動作」を引き起こします。符号なし整数はモジュラー算術を実行するため、GF(2で計算できます。k)。

一部の計算がオーバーフローしないようにしたい場合は、実行時に自分で計算する必要があります。これは、多倍長ライブラリ(gmpなど)をCで記述しにくくする大きな部分です。通常、アセンブリでその大部分をコーディングし、CPUの既知のプロパティを利用する方がはるかに簡単で高速です。オーバーフローフラグ、または倍幅の結果レジスタペア)。

8
torek

ルチアンはすでにそれに答えました。それを完了するためだけに..

C11標準では(C++標準にも同様の行があります)、型を指定するための接尾辞のない整数リテラルの型は次のように決定されます。

6.4.4定数から(C11ドラフト):

セマンティクス

4 10進定数の値は、基数10で計算されます。 8進数定数の基数8。 16進数の定数、基数16のそれ。字句的に最初の桁が最も重要です。

5整数定数のタイプは、その値を表すことができる対応するリストの最初のものです。

そして、表は次のとおりです。

10進定数

int
int long int 
long long int

8進数または16進数の定数

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

8進数および16進数の定数の場合、符号なしの型も可能です。したがって、プラットフォームに応じて、上記のリスト(intまたはlongintまたはlonglong int)のいずれかがfirstに適合します(順序)は整数リテラルのタイプになります。

6
P.P.

別の言い方をすれば、sizeofに関連するのは式の値ではなく、型であるということです。 sizeofは、型または式として明示的に指定できる型のメモリサイズを返します。この場合、コンパイラーはコンパイル時にこの型を計算しますなし実際に式を計算します(既知の規則に従って、たとえば関数を呼び出すと、結果の型は戻り値の型になります)。

他のポスターが述べたように、可変長配列には例外があります(その型サイズは実行時にのみ知られています)。

言い換えると、通常はsizeof(type)sizeof expressionのようなものを記述します。ここで、式はL値です。式が複雑なコンピューティングになることはほとんどありません(上記の関数を呼び出す愚かな例のように):評価されないため、とにかく役に立たないでしょう。

#include <stdio.h>

int main(){
    struct Stype {
            int a;
    } svar;
    printf("size=%d\n", sizeof(struct Stype));
    printf("size=%d\n", sizeof svar);
    printf("size=%d\n", sizeof svar.a);
    printf("size=%d\n", sizeof(int));

}

また、sizeofは言語キーワードであるため、末尾の式の前に関数の括弧は必要ありません(returnキーワードにも同じ種類のルールがあります)。

2
kriss

フォローアップの質問については、「演算子」はなく、式の「コンパイル時」のサイズと「実行時」のサイズに違いはありません。

特定の型が探している結果を保持できるかどうかを知りたい場合は、いつでも次のようなことを試すことができます。

#include <stdio.h>
#include <limits.h>

int main(void) {
    int a = 1250000000;
    int b = 2;

    if ( (INT_MAX / (double) b) > a ) {
        printf("int is big enough for %d * %d\n", a, b);
    } else {
        printf("int is not big enough for %d * %d\n", a, b);
    }

    if ( (LONG_MAX / (double) b) > a ) {
        printf("long is big enough for %d * %d\n", a, b);
    } else {
        printf("long is not big enough for %d * %d\n", a, b);
    }

    return 0;
}

そして(少し)より一般的な解決策、ヒバリのためだけに:

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

/* 'gssim' is 'get size of signed integral multiplication */

size_t gssim(long long a, long long b);
int same_sign(long long a, long long b);

int main(void) {
    printf("size required for 127 * 1 is %zu\n", gssim(127, 1));
    printf("size required for 128 * 1 is %zu\n", gssim(128, 1));
    printf("size required for 129 * 1 is %zu\n", gssim(129, 1));
    printf("size required for 127 * -1 is %zu\n", gssim(127, -1));
    printf("size required for 128 * -1 is %zu\n", gssim(128, -1));
    printf("size required for 129 * -1 is %zu\n", gssim(129, -1));
    printf("size required for 32766 * 1 is %zu\n", gssim(32766, 1));
    printf("size required for 32767 * 1 is %zu\n", gssim(32767, 1));
    printf("size required for 32768 * 1 is %zu\n", gssim(32768, 1));
    printf("size required for -32767 * 1 is %zu\n", gssim(-32767, 1));
    printf("size required for -32768 * 1 is %zu\n", gssim(-32768, 1));
    printf("size required for -32769 * 1 is %zu\n", gssim(-32769, 1));
    printf("size required for 1000000000 * 2 is %zu\n", gssim(1000000000, 2));
    printf("size required for 1250000000 * 2 is %zu\n", gssim(1250000000, 2));

    return 0;
}

size_t gssim(long long a, long long b) {
    size_t ret_size;
    if ( same_sign(a, b) ) {
        if ( (CHAR_MAX / (long double) b) >= a ) {
            ret_size = 1;
        } else if ( (SHRT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(short);
        } else if ( (INT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    } else {
        if ( (SCHAR_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = 1;
        } else if ( (SHRT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(short);
        } else if ( (INT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    }
    return ret_size;
}

int same_sign(long long a, long long b) {
    if ( (a >= 0 && b >= 0) || (a <= 0 && b <= 0) ) {
        return 1;
    } else {
        return 0;
    }
}

私のシステムでは、これは次のように出力します。

size required for 127 * 1 is 1
size required for 128 * 1 is 2
size required for 129 * 1 is 2
size required for 127 * -1 is 1
size required for 128 * -1 is 1
size required for 129 * -1 is 2
size required for 32766 * 1 is 2
size required for 32767 * 1 is 2
size required for 32768 * 1 is 4
size required for -32767 * 1 is 2
size required for -32768 * 1 is 2
size required for -32769 * 1 is 4
size required for 1000000000 * 2 is 4
size required for 1250000000 * 2 is 8
2
Paul Griffiths

C11ドラフトはここにあります: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf Cx0ドラフトはここにあります: http://c0x.coding-guidelines.com/6.5.3.4.html

どちらの場合も、セクション6.5.3.4があなたが探しているものです。基本的に、あなたの問題はこれに要約されます:

// Example 1:
long long x = 2500000000;
int size = sizeof(x); // returns 8

// Example 2:
int x = 1250000000;
int y = 2;
int size = sizeof(x * y); // returns 4

例1では、long long(8バイト)なので、8を返します。例2では、​​int * intは、4バイトのintを返します(したがって、4を返します)。

賞金の質問に答えるには:はい、いいえ。 sizeofは、実行しようとしている操作に必要なサイズを計算しませんが、適切なラベルを使用して操作を実行すると、結果のサイズがわかります。

long long x = 1250000000;
int y = 2;
int size = sizeof(x * y); // returns 8

// Alternatively
int size = sizeof(1250000000LL * 2); // returns 8

多数を処理していることを通知する必要があります。そうしないと、可能な最小の型(この場合はint)を処理していると見なされます。

0
Zac Howland

1行で最も簡単な答えは次のとおりです。

sizeof()は、COMPILE TIMEで評価される関数であり、入力はc型であり、その値は完全に無視されます

詳細:..したがって、2500000000がコンパイルされると、intに収まるには長すぎるため、LONGとして格納する必要があります。したがって、この引数は単に「(type)long」としてコンパイルされます。ただし、1250000000と2はどちらもタイプ 'int'に適合します。したがって、これはsizeofに渡されるタイプです。コンパイラが単にタイプに関心があるため、結果の値が格納されることはなく、乗算が評価されることはありません。

0
sbail95

はい、sizeof()は、その乗算の結果に必要なメモリを計算しません。

2番目のケースでは、両方のリテラル:_1250000000_と_2_はそれぞれ_4 bytes_のメモリを必要とするため、sizeof()は_4_を返します。値の1つが4294967295 (2^32 - 1)を上回っていた場合、_8_が得られます。

しかし、sizeof()が_8_に対して_2500000000_をどのように返したかはわかりません。 VS2012コンパイラで_4_を返します

0
Aravind