В C оператор sizeof возвращает 8 байт при передаче 2,5 м, но 4 байта при передаче 1,25 м * 2
Я не понимаю, почему sizeof
оператор выдает следующие результаты:
sizeof( 2500000000 ) // => 8 (8 bytes).
... он возвращает 8, и когда я делаю следующее:
sizeof( 1250000000 * 2 ) // => 4 (4 bytes).
... он возвращает 4, а не 8 (что я ожидал). Может кто-нибудь прояснить, как sizeof
определяет размер выражения (или тип данных) и почему в моем конкретном случае это происходит?
мое лучшее предположение, что sizeof
оператор-это время компиляции оператор.
Баунти Вопрос: есть ли оператор времени выполнения, который может оценить эти выражения и произвести мой ожидаемый результат (без приведения)?
8 ответов:
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
, потому что операндыint
иint
и умножение не автоматически продвигается к большему типу данных, если он не подходит.так вот, gcc говорит, что это
-1794967296
, но это неопределенное поведение, так что может быть любое количество. Это число вписывается вint
.#include <iostream> int main() { long long x = (long long)1250000000 * 2; std::cout << x; }
дает правильный
2500000000
.
[Edit: изначально я не заметил, что это было опубликовано как C, так и c++. Я отвечаю только в отношении С.]
отвечая на ваш последующий вопрос, "есть в любом случае, чтобы определить объем памяти, выделенной для выражения или переменной во время выполнения?- ну, не совсем так. Проблема в том, что это не очень хорошо сформулированный вопрос.
"выражения", в C-языке (в отличие от некоторой конкретной реализации), на самом деле не используют любой память. (Конкретные реализации требуют некоторого кода и / или памяти данных для проведения вычислений, в зависимости от того, сколько результатов будет вписываться в регистры ЦП и так далее.) Если результат выражения не спрятан в переменной, он просто исчезает (и компилятор часто может опустить код времени выполнения для вычисления никогда не сохраненного результата). Язык не дает вам возможности спросить о чем-то, что он не предполагает, т. е. пространство для хранения выражения.
переменные, с другой стороны, занимают хранения (памяти). Объявление переменной указывает компилятору, какой объем памяти следует выделить. Однако, за исключением массивов переменной длины C99, требуемое хранилище определяется исключительно в compile времени, а не во время выполнения. Вот почему
sizeof x
обычно является константным выражением: компилятор может (и фактически должен) определить значениеsizeof x
во время компиляции.Влас К99 особенный исключение из правила:
void f(int n) { char buf[n]; ... }
хранение, необходимое для
buf
это не (в общем) то, что компилятор может найти во время компиляции, так чтоsizeof buf
не является константой времени компиляции. В этом случаеbuf
фактически выделяется во время выполнения, и его размер определяется только тогда. Так чтоsizeof buf
и вычисленное во время выполнения выражение.в большинстве случаев, однако, все определяется заранее, во время компиляции, и если выражение переполняется во время выполнения, то поведение не определено, определено реализацией или четко определено в зависимости от типа. Переполнение целого числа со знаком, как в 2.5 миллиардах умноженное на 2, когда
INT_MAX
это всего лишь чуть более 2,7 миллиарда, что приводит к"неопределенному поведению". Целые числа без знака выполняют модульную арифметику и, таким образом, позволяют вычислять в GF(2k).если вы хотите убедиться, что некоторые вычисления не могут переполниться, это то, что вы должны вычислить сами, во время выполнения. Это большая часть того, что делает библиотеки вычислений с повышенной точностью (например GMP) трудно писать в C-это, как правило, намного легче, а также быстрее, чтобы закодировать большие части, которые в сборе и воспользоваться известными свойствами этого процессора (например, переполнение флаги, или двойной ширины результата-Регистрация-пар).
Лукиан уже ответил на него. Просто для завершения его..
стандартные состояния C11 (стандарт C++ имеет аналогичные строки) , что тип целочисленного литерала без суффикса для обозначения типа dertermined следующим образом:
Из 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) подходит первый (в порядке) будет тип целочисленного литерала.
еще один ответ, чтобы сказать, что то, что относится к
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));
}
также обратите внимание, что as sizeof является ключевым словом языка, не скобки функции не нужны перед конечным выражением (у нас есть такое же правило для ключевого слова return).
для вашего последующего вопроса нет "оператора", и нет никакой разницы между размером" времени компиляции "выражения и размером" времени выполнения".
Если вы хотите знать, если данный тип может содержать результат, который вы ищете, то вы всегда можете попробовать что-то вроде этого:
#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
да, sizeof () не вычисляет память, необходимую для результата этого умножения.
во втором случае оба литерала:
1250000000
и2
для каждого4 bytes
памяти, следовательно sizeof () возвращает4
. Если одно из значений выше4294967295 (2^32 - 1)
вы получили бы8
.но я не знаю, как sizeof (), то возвращается
8
на2500000000
. Он возвращает4
на моем компиляторе VS2012
проект 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
).
самый простой ответ в одной строке:
sizeof () - это функция, вычисляемая во время компиляции, вход которой является типом c, значение которого полностью игнорируется
БОЛЕЕ ПОДРОБНАЯ ИНФОРМАЦИЯ: ..поэтому, поскольку 2500000000 компилируется, он должен храниться как длинный, поскольку он слишком длинный, чтобы поместиться в int, поэтому этот аргумент просто компилируется как "(тип) long". Однако 1250000000 и 2 оба вписываются в тип "int", поэтому это тип, переданный sizeof, поскольку результирующее значение никогда не сохраняется как потому, что компилятор просто интересуется типом, умножение никогда не вычисляется.