Когда использовать разные целые типы?

Языки программирования (например, c, С++ и java) обычно имеют несколько типов для целочисленной арифметики:

  • signed и unsigned типы
  • типы разного размера: short, int, long, long long
  • типы гарантированного и не гарантированного (т.е. зависимого от реализации) размера:
    например int32_t vs int (и я знаю, что int32_t не является частью языка)

Как вы суммируете, когда нужно использовать каждый из них?

Ответ 1

Интегральный тип по умолчанию (int) получает привилегированное лечение "первым среди равных" на почти всех языках. Таким образом, мы можем использовать это по умолчанию, если нет причин предпочитать другой тип.

Такими причинами могут быть:

  • Использование большего типа, если вы знаете, что вам нужен дополнительный диапазон или меньший тип, если вы хотите сохранить память и не возражаете против меньшего диапазона.
  • Использование неподписанного типа, чтобы убедиться, что вы не получаете никаких "лишних" 1s в своем целочисленном представлении, если вы намерены использовать смещение битов (<< и >>).
  • Если язык не гарантирует минимальный (или даже фиксированный) размер для типа (например, C/С++ vs С#/Java), и вы заботитесь о его свойствах, вам следует предпочесть некоторый механизм генерации типа с гарантированным размером (например, int32_t) - если ваша программа предназначена для переносимости и, как ожидается, будет скомпилирована с разными компиляторами, это станет более важным.

Обновить (расширение по типам гарантированного размера)

Мое личное мнение заключается в том, что типы без гарантированного фиксированного размера больше проблем, чем стоит сегодня. Я не буду вдаваться в исторические причины, порождающие их (кратко: переносимость исходного кода), но реальность такова, что в 2011 году мало кто из них, если таковой имеется, будет им полезен.

С другой стороны, есть много вещей, которые могут испортиться при использовании таких типов:

  • Тип оказывается не имеющим нужного диапазона
  • Вы получаете доступ к базовой памяти для переменной (возможно, ее сериализуете), но из-за конечности процессора и нефиксированного размера типа, в результате чего вы вводите ошибку

По этим причинам (и, вероятно, есть и другие), использование таких типов теоретически является главной болью. Кроме того, если не требуется экстремальная переносимость, вы не сможете вообще компенсировать это. И действительно, вся цель typedefs, как int32_t, заключается в том, чтобы полностью исключить использование типов с ограниченным размером.

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

Ответ 2

Один за другим на ваши вопросы:

  • signed и unsigned: зависит от того, что вам нужно. Если вы уверены, что номер будет неподписанным - используйте unsigned. Это даст вам возможность использовать большие числа. Например, a signed char (1B) имеет диапазон [-128: 127], но если он unsigned - максимальное значение удваивается (у вас есть еще один бит для использования - знаковый бит, поэтому unsigned char может быть 255 (все биты равны 1)

  • short, int, long, long long - это довольно ясно, не так ли? Наименьшее целое число (кроме char) равно short, следующее - int и т.д. Но эти функции зависят от платформы - int может быть 2B (давно давно: D), 4B (обычно). long может быть 4B (на 32-битной платформе) или 8B (на 64-битной платформе) и т.д. long long не является стандартным типом в С++ (он будет в С++ 0x), но обычно это typedef для int64_t.

  • int32_t vs int - int32_t, а другие типы, подобные этому, гарантируют их размер. Например, int32_t гарантированно будет 32 бит, тогда как, как я уже сказал, размер int зависит от платформы.

Ответ 3

используйте более короткое время для сохранения памяти, дольше, чтобы иметь возможность представлять большие числа. Если у вас нет таких требований, подумайте о том, какие API-интерфейсы вы будете использовать для обмена данными и настройте себя, поэтому вам не нужно слишком много делать или конвертировать.

Ответ 4

В целом вы должны использовать тот тип, который соответствует требованиям вашей программы и максимально повышает удобочитаемость и удобство в будущем.

Сказав это, как указывает Крис, люди используют шорты против ints для экономии памяти. Подумайте о следующем сценарии: у вас есть 1000 000 (довольно небольшое число) ints (обычно 32 байта) против коротких сообщений (обычно 16 байт). Если вы знаете, что вам больше не нужно будет представлять число больше 32 767, вы можете использовать короткий. Или вы можете использовать unsigned short, если знаете, что вам не нужно будет представлять число, большее 65 535. Это сэкономит: ((32 - 16) х 1,000,000) = 16k памяти.

Ответ 5

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

Библиотеки, такие как GMP и OpenSSL, предоставляют "большие числа", которые поддерживают сколь угодно большие результаты - это, как правило, самая безопасная вещь, если вы не можете доказать, что результаты ваших вычислений находятся в пределах границ.

Кроме того, многие языки по умолчанию имеют неограниченные целые типы, потому что они более безопасны.

Ответ 6

Потребность в различных типах целочисленного размера возникает из-за двух основных проблем: размер шрифта в значительной степени должен быть известен на низком уровне, а в старые времена ограничений памяти это важно в повседневном использовании, поскольку у вас мало играть с. Если вы делаете базовую арифметику, то почти неважно, какой тип вы используете. Например, при циклировании от 1 до 100 не важно, используете ли вы uint16_t или uint64_t, если ваши типы подходят, и вы используете правильный тип, если вам нужно/не нужно подписываться.

Однако, это важно, когда вы хотите масштабировать/нуждаться в оптимизации. Если вы хотите выделить 1 000 000 000 таких целых чисел, то сказать: "каждый из них будет 64-битным на всякий случай" или "Я буду использовать встроенный тип" не собирается его сокращать - это 8 000 000 000 байт, что составляет около 7,5 ГБ данных. Теперь 1 миллиард 64-битных целых чисел не представляется возможным, но это могут быть точки в трехмерном массиве (1000 ^ 3), и в этом случае вы также получили размеры указателей и для контента. Например, вы могли бы пойти намного дальше с 8-битными целыми числами для ваших значений. Сколько вы можете выделить, затем проинформирует ваши алгоритмы - если вы не можете выделить столько данных сразу, вы можете рассмотреть mmap или обрабатывать части данных сразу (обработка a.k.a.), свопинг самостоятельно). Если вам не нужны значения определенного размера, это когда вы начинаете использовать более ограниченные типы. Аналогично, вы получаете дополнительную мощность 2, используя неподписанные типы. Очень полезно.

Если вы когда-либо пишете сборку, чтобы идти вместе с вашим C/С++, то знание размера типа данных в значительной степени имеет решающее значение, особенно когда речь идет о распределении локального пространства стека или чтении переменных из адресов памяти (в отличие от уже в регистре). Подумайте об этом как о программировании, используя void*. Таким образом, я обычно использую stdint.h определенные типы по привычке, так что я всегда знаю о своих размерах и форматах, когда смешиваю их.

Ответ 7

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

Ответ 8

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

Естественно, что фактическая причина, по которой вы бы выбрали один тип или другой, на мой взгляд, связан с другими факторами. Например, большой оператор shift.

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    int i;

    //unsigned long long x;
    //int x;
    short x;

    x = 2;
    for (i=2; i<15; ++i)
    {
        x=pow(x,2);
        cout << x << endl;
    }
    return 0;
}