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

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

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

…它返回8,当我做到以下几点:

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

…它返回4,而不是8(这是我所期望的)。 有人可以澄清sizeof如何确定expression式(或数据types)的大小,以及为什么在我的具体情况下这是发生?

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

赏金问题:是否有一个运行时操作符可以评估这些expression式,并产生我的预期输出(不铸造)?

2500000000不适合int ,因此编译器将其正确地解释为longlong long或它适合的types)。 1250000000 ,而且2sizeof的参数不计算 ,所以编译器不可能知道乘法不适合int ,所以返回int的大小。

此外,即使参数被评估,你可能会得到一个溢出(和未定义的行为),但可能仍然导致4

这里:

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

你能猜出输出吗? 如果你认为这是2500000000 ,你会错的。 expression式1250000000 * 2的types是int ,因为操作数是intint ,如果不合适,乘法不会自动提升为更大的数据types。

http://ideone.com/4Adf97

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

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

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

产生正确的2500000000

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

回答你的后续问题,“无论如何确定在运行时分配给一个expression式或variables的内存量?”:呃,不完全是这样。 问题是这不是一个很好的问题。

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

另一方面,variables占用存储空间(内存)。 variables的声明告诉编译器需要多less存储空间。 不过,除了C99的可变长度数组外,所需的存储空间纯粹是在编译时确定的,而不是在运行时确定的。 这就是为什么sizeof x通常是一个常量expression式:编译器可以(并且实际上必须)在编译时确定sizeof x的值。

C99的VLA是这个规则的特例:

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

buf所需的存储空间通常不是编译器在编译时可以find的东西,所以sizeof buf不是编译时常量。 在这种情况下, buf实际上是在运行时分配的,并且其大小只是在那时确定的。 所以sizeof buf 一个运行时计算expression式。

但是,在大多数情况下,在编译时,所有内容都是事先确定好的,如果expression式在运行时溢出,则根据types的不同,行为可能是未定义的,实现定义的或定义明确的。 有符号的整数溢出,如25亿乘以2,当INT_MAX 27亿时,导致“未定义的行为”。 无符号整数执行模运算,从而允许您在GF(2 k )中进行计算。

如果你想确保一些计算不能溢出,那么在运行时你必须计算一下自己。 这是使得多精度库(如gmp)很难在C​​中编写的重要组成部分 – 通常更容易和更快速地对程序集中的大部分进行编码,并利用CPU的已知属性(如溢出标志或双宽结果寄存器对)。

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

C11标准状态(C ++标准具有类似的行),指定types的没有后缀的整型文字的types如下所示:

从6.4.4常量( C11草案 ):

语义

4十进制常量的值以10为基础计算; 一个八进制常数,基数为8; 那是一个hex常量,基数为16.词法上的第一位是最重要的。

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

表格如下:

十进制常量

 int int long int long long int 

八进制或hex常量

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

对于八进制和hex常量,甚至可以使用无符号types。 因此,取决于你的平台上面列出的任何一个( int或long int或long long int适配(按顺序)将是整型文字的types。

另一种解答的方法是说sizeof相关不是expression式的价值,而是types。 sizeof返回一个types的内存大小,它可以明确地作为一个types或者一个expression式来提供。 在这种情况下,编译器将在编译时计算这种types, 而不实际计算expression式(例如,如果调用一个函数,结果types是返回值的types,则遵循已知规则)。

正如其他海报所述,可变长度数组(仅在运行时才知道其types大小)是一个例外。

换句话说,你通常写的东西,如sizeof(type)sizeof expression ,其中expression式是一个L值。 expression式几乎从来不是一个复杂的计算(就像上面调用一个函数的愚蠢例子):无论如何,它是没有用的,因为它没有被评估。

 #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是一个语言关键字,所以在尾随expression式(我们对return关键字有相同types的规则)之前不需要函数括号。

对于后续问题,没有“操作符”,expression式的“编译时”大小和“运行时间”大小没有区别。

如果你想知道一个给定的types是否可以保存你正在寻找的结果,你总是可以尝试这样的事情:

 #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; } 

和一个(略)更一般的解决scheme,只是为了百灵:

 #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 

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

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

但我不知道sizeof()返回82500000000 。 它在我的VS2012编译器上返回4

C11草案在这里: http : //www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf你可以在这里findCx0草案: 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 

你必须告诉它,你正在处理一个很大的数字,或者它会认为它处理的是最小的types(在本例中是int )。

一行中最简单的答案是:

sizeof()是在COMPILE TIME评估的一个函数,他input的是actypes,其值完全被忽略

进一步的细节:..因此,2500000000编译它将不得不被存储为一个长,因为它太长,以适应一个int,因此这个参数是简单编译为“(型)长”。 然而,1250000000和2两者都符合“int”types,因此这是传递给sizeof的types,因为结果值永远不会被存储,因为编译器只是对types感兴趣,所以乘法从不计算。