В 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 65

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 и умножение не автоматически продвигается к большему типу данных, если он не подходит.

http://ideone.com/4Adf97

так вот, 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, поскольку результирующее значение никогда не сохраняется как потому, что компилятор просто интересуется типом, умножение никогда не вычисляется.