在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
,因此编译器将其正确地解释为long
( long long
或它适合的types)。 1250000000
,而且2
。 sizeof
的参数不计算 ,所以编译器不可能知道乘法不适合int
,所以返回int
的大小。
此外,即使参数被评估,你可能会得到一个溢出(和未定义的行为),但可能仍然导致4
。
这里:
#include <iostream> int main() { long long x = 1250000000 * 2; std::cout << x; }
你能猜出输出吗? 如果你认为这是2500000000
,你会错的。 expression式1250000000 * 2
的types是int
,因为操作数是int
和int
,如果不合适,乘法不会自动提升为更大的数据types。
所以在这里,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()不计算该乘法结果所需的内存。
在第二种情况下,文字: 1250000000
和2
每个都需要4 bytes
的内存,因此sizeof()返回4
。 如果其中一个值在4294967295 (2^32 - 1)
,那么你将得到8
。
但我不知道sizeof()返回8
为2500000000
。 它在我的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感兴趣,所以乘法从不计算。