Вопрос

Учитывая следующий фрагмент:

#include <stdio.h>

typedef signed long long int64;
typedef signed int int32;
typedef signed char int8;

int main()
{
    printf("%i\n", sizeof(int8));
    printf("%i\n", sizeof(int32));
    printf("%i\n", sizeof(int64));

    int8 a = 100;
    int8 b = 100;
    int32 c = a * b;
    printf("%i\n", c);

    int32 d = 1000000000;
    int32 e = 1000000000;
    int64 f = d * e;
    printf("%I64d\n", f);
}

Вывод с Mingw GCC 3.4.5 составляет (-O0):

1
4
8
10000
-1486618624

Первое умножение отбрасывается в Int32 внутри (в соответствии с выходом ассемблера). Второе умножение не отличается. Я не уверен, отличаются ли результаты, потому что программа работала на IA32, или потому, что она определена где -то в стандарте C. Тем не менее мне интересно, если это точное поведение где -то определено (ISO/IEC 9899?), Потому что мне нравится лучше понять, почему и когда я бросаю вручную (у меня проблемы с переносом программы из другой архитектуры).

Это было полезно?

Решение

Стандарт C99 указывает, что двоичные операторы, такие как * Не работайте на целочисленных типах меньше, чем int. Анкет Выражения этих типов повышаются int Перед применением оператора. См. 6.3.1.4 Пункт 2 и многочисленные вхождения слов «Целочисленное акция». Но это несколько ортогонально для инструкций сборки, создаваемых компилятором, который работает на intS, потому что это быстрее, даже если компилятору будет разрешено вычислять более короткий результат (поскольку, например, результат немедленно хранится в L-значении короткого типа).

Касательно int64 f = d * e; куда d и e имеют тип int, умножение осуществляется как int В соответствии с теми же правилами продвижения по службе. Переполнение технически неопределенное поведение, Здесь вы получаете результат на два S-дополнения, но вы можете получить что-либо в соответствии со стандартом.

ПРИМЕЧАНИЕ. Правила продвижения различают подписанные и неподписанные типы при продвижении. Правило состоит в том, чтобы продвигать меньшие типы до int пока не int не может представлять все значения типа, в этом случае unsigned int используется.

Другие советы

Проблема заключается в том, что умножение INT32 * Int32, который выполняется как Int32, а затем результат назначен INT64. Вы получите почти такой же эффект с double d = 3 / 2;, который будет разделить 3 к 2 с использованием целочисленного деления и назначить 1,0 к d.

Вы должны обратить внимание на тип выражения или подсудимости всякий раз, когда это может иметь значение. Это требует убедиться, что соответствующая операция рассчитывается в качестве соответствующего типа, такого как отливка одного из мультипликаций на INT64 или (в моем примере) 3.0 / 2 или (float)3 / 2.

Читайте K & R (оригинал). Все целочисленные операции выполняются с помощью естественного целочисленного типа, если только это не включает переменные, которые (или капикуются) к чем -то большему. Операции на ChAR приведены на 32 бита, потому что это естественный размер целого числа в этой архитектуре. Умножение двух 32 -битных целых чисел выполняется в 32 битах, потому что ничто не отдает его ни на что больше (пока вы не назначите его с 64 -битной переменной, но это слишком поздно). Если вы хотите, чтобы операция произошла за 64 бита, разделите один или оба INT до 64 бит.

int64 f = (int64)d * e;

a * b рассчитывается как int, а затем отбрасывает к получает тип переменной (который просто происходит, чтобы быть int)

d * e рассчитывается как int, а затем отбрасывает к получению типа переменного (который просто происходит, чтобы быть int64)

Если бы какая -либо из типовых переменных была больше, чем int (или был плавающей запятой), то был бы этот тип. Но поскольку все типы, используемые в умножениях, были int или меньше, использовались INT.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top