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

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

Я решил проверить, действительно ли это имеет значение для меня, в моей системе (intel core i5/64-bit 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 длинным?

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

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

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

Ответ 1

Язык гарантирует, что 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 в первую очередь.

Ответ 2

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

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

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

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

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

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

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