65

我不明白为什么sizeof运营商会产生以下结果:

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

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

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

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

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

赏金问题:是否有一个运行时运算符可以评估这些表达式并产生我的预期输出(无需强制转换)?

4

8 回答 8

123

2500000000不适合 a int,因此编译器正确地将其解释为 a long(或long long,或适合的类型)。1250000000确实如此,也是如此2。to 的参数sizeof 未评估,因此编译器不可能知道乘法不适合 an int,因此返回 an 的大小int

此外,即使对参数进行了评估,您也可能会出现溢出(和未定义的行为),但可能仍会导致4.

这里:

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

你能猜出输出吗?如果你认为是2500000000,那你就错了。表达式的类型1250000000 * 2int,因为操作数是intandint并且如果不合适,乘法不会自动提升为更大的数据类型。

http://ideone.com/4Adf97

所以在这里,gcc 说它是-1794967296,但它是未定义的行为,所以它可以是任何数字。这个数字确实适合int.

此外,如果您将其中一个操作数转换为预期的类型(就像您在寻找非整数结果时在除法时转换整数一样),您会看到这样的工作:

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

产生正确的2500000000.

于 2013-05-30T04:37:32.283 回答
8

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

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

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

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

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

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

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

但是,在大多数情况下,一切都是在编译时预先确定的,如果表达式在运行时溢出,则行为是未定义的、实现定义的或明确定义的,具体取决于类型。有符号整数溢出,如 25 亿乘以 2,当INT_MAX27 亿多一点时,会导致“未定义的行为”。无符号整数进行模运算,因此允许您在 GF(2 k ) 中进行计算。

如果你想确保某些计算不会溢出,那是你必须在运行时自己计算的东西。这是导致多精度库(如 gmp)难以用 C 编写的重要原因——在汇编中编写大部分代码并利用 CPU 的已知属性(如溢出标志或双宽结果寄存器对)。

于 2013-05-30T06:09:37.683 回答
6

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

C11 标准规定(C++ 标准有类似的行)没有后缀来指定类型的整数文字的类型确定如下:

从 6.4.4 常量(C11 草案)开始:

语义

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 )首先适合(按顺序)将是整数文字的类型。

于 2013-05-30T06:59:53.780 回答
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 关键字有相同的规则)。

于 2013-05-30T09:21:14.150 回答
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
于 2013-08-14T19:42:26.427 回答
0

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

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

但我不知道 sizeof() 如何返回8. 25000000004在我的 VS2012 编译器上返回

于 2013-05-30T04:50:55.847 回答
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返回 anint的 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)。

于 2013-08-14T19:43:46.910 回答
0

一行中最简单的答案是:

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

更多细节:..因此,当 2500000000 被编译时,它必须存储为 LONG,因为它太长而无法放入 int 中,因此这个参数被简单地编译为 '(type) long'。但是,1250000000 和 2 都适合类型“int”,因此这是传递给 sizeof 的类型,因为从不存储结果值,因为编译器只是对类型感兴趣,因此从不计算乘法。

于 2016-10-26T18:36:21.623 回答