在C中,sizeof运算符在传递2.5m时返回8个字节,在传递1.25m * 2时返回4个字节

时间:2013-05-30 04:34:27

标签: c sizeof

我不明白为什么sizeof运算符产生以下结果:

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

...它返回8,当我执行以下操作时:

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

...它返回4而不是8(这是我的预期)。有人可以澄清sizeof如何确定表达式(或数据类型)的大小以及为什么在我的特定情况下会发生这种情况?

我最好的猜测是sizeof运算符是编译时运算符。

Bounty问题:是否有运行时运算符可以评估这些表达式并生成我的预期输出(不进行转换)?

8 个答案:

答案 0 :(得分:123)

2500000000不适合int,因此编译器会正确地将其解释为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

另外,如果你将其中一个操作数强制转换为期望的类型(就像你在寻找非整数结果时分割整数一样),你会看到这个有效:

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

产生正确的2500000000

答案 1 :(得分:8)

[编辑:我最初没有注意到,这是作为C和C ++发布的。我只回答C。]

回答你的后续问题,“无论如何确定在运行时分配给表达式或变量的内存量?”:好吧,不完全是。问题是这不是一个非常好的问题。

“表达式”,在C语言中(与某些特定实现相对),实际上并不使用任何内存。 (具体实现需要一些代码和/或数据存储器来保存计算,具体取决于有多少结果适合CPU寄存器等等。)如果表达式结果没有隐藏在变量中,它就会消失(并且编译器可以经常省略运行时代码来计算从未保存的结果)。该语言没有给你一种方法来询问它不存在的东西,即表达式的存储空间。

另一方面,变量会占用存储空间(内存)。变量的声明告诉编译器要留出多少存储空间。但是,除了C99的可变长度数组之外,所需的存储仅在 compile 时确定,而不是在运行时确定。这就是为什么sizeof x通常是一个常量表达式:编译器可以(实际上必须)在编译时确定sizeof x的值。

C99的VLA是该规则的特殊例外:

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

buf所需的存储不是(通常)编译器在编译时可以找到的东西,因此sizeof buf不是编译时常量。在这种情况下,buf实际上是在运行时分配的,其大小仅在此时确定。所以sizeof buf 运行时计算的表达式。

但是,在大多数情况下,所有内容都在编译时预先设置,如果表达式在运行时溢出,则行为是未定义的,实现定义的,或者根据类型定义良好。有符号整数溢出,如25亿乘以2,当INT_MAX略高于27亿时,会导致“未定义的行为”。无符号整数执行模运算,因此允许您在GF中计算(2 k )。

如果你想确保一些计算不能溢出,那么你必须在运行时自己计算。这是使多精度库(如gmp)难以用C编写的一个重要部分 - 它通常更容易,更快,在汇编中编写大部分内容并利用CPU的已知属性(如溢出标志,或双倍宽的结果寄存器对。

答案 2 :(得分:6)

Luchian已经回答了。只是为了完成它..

C11标准状态(C ++标准具有类似的行),指定类型的整数文字的类型定义如下:

来自6.4.4常量( C11 draft ):

  

语义

     

4十进制常数的值以10为基数计算;一个   八进制常数,基数8;十六进制常数,基数为16   词法上第一个数字是最重要的。

     

5整数常量的类型是对应的第一个    可以表示其值的列表。

表格如下:

十进制常量

int
int long int 
long long int

八进制或十六进制常量

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

对于八进制和十六进制常量,甚至可以使用无符号类型。因此,根据您的平台,无论上面列表中的哪一个( int或long int或long long int ), first (按顺序)都将是整数文字的类型。< / p>

答案 3 :(得分:2)

另一种回答的方法是说与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关键字有相同的规则)。

答案 4 :(得分:2)

对于你的后续问题,没有“运算符”,表达式的“编译时间”大小与“运行时”大小没有区别。

如果您想知道某个特定类型是否能保存您正在寻找的结果,您可以尝试这样的事情:

#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

答案 5 :(得分:0)

是的,sizeof()不计算乘法结果所需的内存。

在第二种情况下,两个文字:12500000002都需要4 bytes内存,因此sizeof()返回4。如果其中一个值高于4294967295 (2^32 - 1),则您将获得8

但我不知道sizeof()如何为8返回2500000000。它在我的VS2012编译器上返回4

答案 6 :(得分:0)

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,它返回int,即4个字节(所以它返回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)。

答案 7 :(得分:0)

一行中最简单的答案是:

sizeof()是在COMPILE TIME评估的函数,其输入是c类型,其值完全被忽略

进一步详细说明:因此,当编译2500000000时,它必须存储为LONG,因为它太长而无法放入int中,因此该参数只是编译为'(type)long'。但是,1250000000和2都适合类型'int',因此传递给sizeof的类型,因为结果值永远不会存储,因为编译器只是对类型感兴趣,所以从不评估乘法。