Что происходит, когда я назначаю long int для int в C?

В недавнем домашнем задании мне сказали использовать long переменная для хранения результата, так как это может быть большое число.

Я решил проверить, действительно ли это важно для меня, в моей системе (Intel Core i5/64-битный Windows 7/ GNU GCC компилятор) и обнаружил, что следующий код:

printf("sizeof(char) => %d\n", sizeof(char));
printf("sizeof(short) => %d\n", sizeof(short));
printf("sizeof(short int) => %d\n", sizeof(short int));
printf("sizeof(int) => %d\n", sizeof(int));
printf("sizeof(long) => %d\n", sizeof(long));
printf("sizeof(long int) => %d\n", sizeof(long int));
printf("sizeof(long long) => %d\n", sizeof(long long));
printf("sizeof(long long int) => %d\n", sizeof(long long int));

производит следующий вывод:

sizeof(char) => 1
sizeof(short) => 2
sizeof(short int) => 2
sizeof(int) => 4
sizeof(long) => 4
sizeof(long int) => 4
sizeof(long long) => 8
sizeof(long long int) => 8

Другими словами, в моей системе int а также long одинаковы, и все, что будет слишком большим для int держать, будет слишком большим для long держать также.

Само домашнее задание здесь не является проблемой. Интересно, как в системе, где int < long, я должен назначить int долго?

Я осознаю тот факт, что существует множество тесно связанных между собой вопросов по этому вопросу, но я чувствую, что ответы в них не дают мне полного понимания того, что произойдет или может произойти в процессе.

В основном я пытаюсь выяснить следующее:

  1. Должен ли я бросить long в int до назначения, или поскольку long это не другой тип данных, а просто модификатор, будет считаться вредным назначать напрямую?
  2. Что происходит в системах, где long > int? Будет ли результат неопределенным (или непредсказуемым) или это приведет к тому, что лишние части переменной будут опущены?
  3. Как происходит кастинг из long в int работает в C?
  4. Как происходит назначение от long в int работает в C, когда я не использую кастинг?

2 ответа

Решение

Язык гарантирует, что int не менее 16 бит, long не менее 32 бит, и long может представлять как минимум все значения, которые int может представлять.

Если вы назначите long значение для int объект, он будет неявно преобразован. Там нет необходимости явного приведения; это просто указывает на то же преобразование, которое произойдет в любом случае.

В вашей системе, где int а также long бывает одинакового размера и диапазона, преобразование тривиально; это просто копирует значение.

В системе, где long шире чем int, если значение не помещается в int, тогда результат преобразования определяется реализацией. (Или, начиная с C99, он может генерировать сигнал, определяемый реализацией, но я не знаю ни одного компилятора, который на самом деле это делает.) Обычно происходит то, что старшие биты отбрасываются, но вы не должны зависеть на что. (Правила для типов без знака различны; результат преобразования целого со знаком или без знака в тип без знака хорошо определен.)

Если вам нужно безопасно назначить long значение для int объект, вы можете проверить, что он будет соответствовать перед выполнением назначения:

#include <limits.h> /* for INT_MIN, INT_MAX */

/* ... */

int i;
long li = /* whatever */

if (li >= INT_MIN && li <= INT_MAX) {
    i = li;
}
else {
    /* do something else? */
}

Детали "чего-то еще" будут зависеть от того, что вы хотите сделать.

Одна поправка: int а также long всегда разные типы, даже если они имеют одинаковый размер и представление. Арифметические типы свободно конвертируемы, поэтому часто это не имеет значения, но, например, int* а также long* являются различными и несовместимыми типами; Вы не можете назначить long* для int*или наоборот, без явного (и потенциально опасного) приведения.

И если вам нужно преобразовать long значение для intПервое, что вы должны сделать, это пересмотреть дизайн вашего кода. Иногда такие преобразования необходимы, но чаще они являются признаком того, что int которому вы назначаете, должен был быть определен как long на первом месте.

long всегда может представлять все значения int, Если имеющееся значение может быть представлено типом переменной, которую вы присваиваете, то значение сохраняется.

Если он не может быть представлен, то для типа назначения со знаком результат формально не указан, в то время как для типа назначения без знака он указывается как исходное значение по модулю 2 n, где n - количество битов в представлении значения (которое не является обязательно все биты в пункте назначения).

На практике на современных машинах вы получаете упаковку также для подписанных типов.

Это связано с тем, что современные машины используют форму дополнения до двух для представления целых чисел со знаком, без каких-либо битов, используемых для обозначения "недопустимого значения" и т. Д., Т.е. всех битов, используемых для представления значения.

С представлением значения n битов любое целочисленное значение x отображается в x + K * 2 n с целочисленной константой K, выбранной так, что результат находится в диапазоне, где половина возможных значений является отрицательной.

Так, например, с 32-битным int значение -7 представляется как номер битового паттерна -7 + 2 32 = 2 32 -7, так что если вы отобразите число, которое битовый паттерн обозначает как целое число без знака, вы получите довольно большое число.

Причина, по которой это называется дополнением к двум, заключается в том, что это имеет смысл для двоичной системы счисления, системы счисления двух основ. Для двоичной системы счисления также есть дополнение к единице (обратите внимание на расположение апострофа). Аналогично, для десятичной системы счисления есть дополнение к десяти и дополнение к нулям. С представлением дополнения из десяти цифр десятого вы бы представляли -7 как 10000-7 = 9993. Вот и все, на самом деле.

Другие вопросы по тегам