C2017/Типы данных в C
Целочисленные типы данных используются для хранения целых чисел (тип char также используется для хранения ASCII-символов). Все размеры диапазонов представленных ниже типов данных минимальны и на отдельно взятой платформе могут быть больше.
Стандарт не требует, чтобы два разных типа имели разный размер: sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long) . Таким образом, даже типы char и long могут иметь одинаковый размер, но такие платформы очень редки. Стандарт гарантирует, что sizeof(char) всегда равен 1.
Минимальный диапазон значений целых типов по стандарту определяется с [math]-(2^N-1)[/math] по [math]2^N-1[/math] , где [math]N[/math] — разрядность типа. Реализация компиляторов может расширять этот диапазон по своему усмотрению. Отметим, что такие ограничения позволяют использовать то или иное представление знаковых чисел:
- дополнительный код (two’s complement) — для отрицательного числа код можно получить инвертированием его двоичного модуля и прибавлением к инверсии единицы
- обратный код (ones’ complement) — двоичный код отрицательного целого числа состоит из одноразрядного кода знака (двоичной цифры 1), за которым следует ( [math]n−1[/math] )-разрядное двоичное число, представляющее собой инвертированное ( [math]n−1[/math] )-разрядное представление модуля числа
- прямой код (sign and magnitude method) — знаковый бит и затем модуль числа без инвертирования.
Отдельное внимание стоит уделить типу char . Формально это отдельный тип, но фактически char эквивалентен либо signed char , либо unsigned char , в зависимости от компилятора.
Для того, чтобы избежать путаницы между размерами типов стандарт C99 ввел новые типы данных, описанные в файле stdint.h. Среди них такие типы как: intN_t , int_leastN_t , int_fastN_t , где N = 8, 16, 32 или 64. Приставка least- обозначает минимальный тип, способный вместить N бит, приставка fast- обозначает тип размером не менее 16 бит, работа с которым наиболее быстрая на данной платформе. Типы без приставок обозначают типы с фиксированном размером, равным N бит.
Типы с приставками least- и fast- можно считать заменой типам int , short , long , с той лишь разницей, что первые дают программисту выбрать между скоростью и размером.
| Тип данных | Размер | Минимальный диапазон значений | Первое появление |
|---|---|---|---|
| signed char | минимум 8 бит | от -127 (= -(2 8 -1)) до 127 | K&R C |
| unsigned char | минимум 8 бит | от 0 до 255 (=2 8 -1) | K&R C |
| char | минимум 8 бит | от -127 до 127 или от 0 до 255 в зависимости от компилятора | K&R C |
| short int | минимум 16 бит | от -32,767 (= -(2 15 -1)) до 32,767 | K&R C |
| unsigned short int | минимум 16 бит | от 0 до 65,535 (= 2 16 -1) | K&R C |
| int | минимум 16 бит | от -32,767 до 32,767 | K&R C |
| unsigned int | минимум 16 бит | от 0 до 65,535 (= 2 16 -1) | K&R C |
| long int | минимум 32 бита | от -2,147,483,647 до 2,147,483,647 | K&R C |
| unsigned long int | минимум 32 бита | от 0 до 4,294,967,295 (= 2 32 -1) | K&R C |
| long long int | минимум 64 бита | от -9,223,372,036,854,775,807 до 9,223,372,036,854,775,807 | C99 |
| unsigned long long int | минимум 64 бита | от 0 до 18,446,744,073,709,551,615 (= 2 64 -1) | C99 |
| int8_t | 8 бит | от -127 до 127 | C99 |
| uint8_t | 8 бит | от 0 до 255 (=2 8 -1) | C99 |
| int16_t | 16 бит | от -32,767 до 32,767 | C99 |
| uint16_t | 16 бит | от 0 до 65,535 (= 2 16 -1) | C99 |
| int32_t | 32 бита | от -2,147,483,647 до 2,147,483,647 | C99 |
| uin32_t | 32 бита | от 0 до 4,294,967,295 (= 2 32 -1) | C99 |
| int64_t | 64 бита | от -9,223,372,036,854,775,807 до 9,223,372,036,854,775,807 | C99 |
| uint64_t | 64 бита | от 0 до 18,446,744,073,709,551,615 (= 2 64 -1) | C99 |
Ошибки
Memsize-типы
Для представления величин, связанных с памятью, в C есть typedef’ы size_t и ptrdiff_t. Размер определяется архитектурой процессора.
size_t — беззнаковый тип для представления размера любого объекта (включая массив). Он способен хранить в себе размер максимального массива, который может быть теоретически выделен в рамках данной архитектуры.
Оператор sizeof возвращает результат типа size_t. Для size_t гарантируется, что он как минимум 16-битный.
Тип ptrdiff_t знаковый, используется для хранения разности указателей. Стандартом допускается вычисление разности только для указателей одного типа, для разных типов — зависит от реализации.
Модели данных
Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках среды разработки на С.
Примеры приведены в таблице.
Вещественные типы
Стандартом гарантируется только, что sizeof(float) <= sizeof(double) <= sizeof(long double) .
На большинстве платформ применяются типы с плавающей точкой в соответствии со стандартом IEEE 754.
Логический тип
В языке C в стандарте C99 введён тип _Bool.
Массивы
Для каждого типа T, кроме void и типов функций, существует тип «массив из элементов N типа T». Массив — это коллекция значений одного типа, хранящихся последовательно в памяти. Массив размера N индексируется целым числом от 0 до N-1.
Массивы могут быть инициализированы с помощью списка инициализации, но не могут быть присвоены друг к другу.
В C99 добавлены динамические многомерные массивы:
Указатели
Для любого типа T существует тип «указатель на T».
Переменные могут быть объявлены как указатели на значения различных типов с помощью символа * . Для того чтобы определить тип переменной как указатель, нужно предварить её имя звёздочкой.
Поскольку указатель — тоже тип переменной, правило «для любого типа T» выполняется и для них: можно объявлять указатели на указатели. К примеру, можно пользоваться int*** :
Существуют также указатели на массивы и на функции. Указатели на массивы имеют следующий синтаксис:
pc — массив указателей, занимающий 10 * sizeof(char*) байт (на распространённых платформах — обычно 40 или 80 байт), а pa — это один указатель; занимает он обычно 4 или 8 байт, однако позволяет обращаться к массиву, занимающему 10 байт: sizeof(pa) == sizeof(int*) , но sizeof(*pa) == 10 * sizeof(char) . Указатели на массивы отличаются от указателей на первый элемент арифметикой. Например, если указатели pa указывает на адрес 2000, то указатель pa+1 будет указывать на адрес 2010.
Структуры
Композитный тип данных, инкапсулирующий без сокрытия набор значений различных типов.
При инициализации элементов структуры надо иметь в виду следующий момент:
If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
Важное отличие между С и C++.
В C++ можно писать
В C нужно писать
Так сделано специально. Названия структур находятся в отдельном пространстве имён. Их всего четыре вида:
- названия меток (для goto);
- теги (названия структур, объединений, перечислений);
- члены структур и объединений;
- все прочие идентификаторы.
Это позволяет, например, назвать переменную так же, как называется структура.
Или же можно сделать typedef:
Доступ к элементам структуры осуществляется с помощью оператора .. Для обращения к полям структуры по указателю существует оператор «стрелочка» ->, синонимичный предыдущей записи: (*p).x — то же самое, что и p->x.
Структуры могут содержать указатели на самих себя, что позволяет реализовывать многие структуры данных, основанных на связных списках. Такая возможность может показаться противоречивой, однако все указатели занимают одинаковое число байт, поэтому размер этого поля не изменится от числа полей структуры.
Структуры не всегда занимают число байт, равное сумме байт их элементов. Компилятор обычно выравнивает элементы. The Lost Art of C Structure Packing
Адрес памяти первого поля структуры равен адресу самой структуры.
Объединения
Доступ к одним и тем же данным разными способами:
Т. н. type punning (каламбур типизации)
Стандартом C99 это разрешается (в отличие от приведения указателей разных типов).
If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.
Name already in use
docs / docs / csharp / language-reference / builtin-types / integral-numeric-types.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Integral numeric types (C# reference)
The integral numeric types represent integer numbers. All integral numeric types are value types. They’re also simple types and can be initialized with literals. All integral numeric types support arithmetic, bitwise logical, comparison, and equality operators.
Characteristics of the integral types
C# supports the following predefined integral types:
| C# type/keyword | Range | Size | .NET type |
|---|---|---|---|
| sbyte | -128 to 127 | Signed 8-bit integer | xref:System.SByte?displayProperty=nameWithType |
| byte | 0 to 255 | Unsigned 8-bit integer | xref:System.Byte?displayProperty=nameWithType |
| short | -32,768 to 32,767 | Signed 16-bit integer | xref:System.Int16?displayProperty=nameWithType |
| ushort | 0 to 65,535 | Unsigned 16-bit integer | xref:System.UInt16?displayProperty=nameWithType |
| int | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer | xref:System.Int32?displayProperty=nameWithType |
| uint | 0 to 4,294,967,295 | Unsigned 32-bit integer | xref:System.UInt32?displayProperty=nameWithType |
| long | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | Signed 64-bit integer | xref:System.Int64?displayProperty=nameWithType |
| ulong | 0 to 18,446,744,073,709,551,615 | Unsigned 64-bit integer | xref:System.UInt64?displayProperty=nameWithType |
| nint | Depends on platform (computed at runtime) | Signed 32-bit or 64-bit integer | xref:System.IntPtr?displayProperty=nameWithType |
| nuint | Depends on platform (computed at runtime) | Unsigned 32-bit or 64-bit integer | xref:System.UIntPtr?displayProperty=nameWithType |
In all of the table rows except the last two, each C# type keyword from the leftmost column is an alias for the corresponding .NET type. The keyword and .NET type name are interchangeable. For example, the following declarations declare variables of the same type:
The nint and nuint types in the last two rows of the table are native-sized integers. Starting in C# 9.0, you can use the nint and nuint keywords to define native-sized integers. These are 32-bit integers when running in a 32-bit process, or 64-bit integers when running in a 64-bit process. They can be used for interop scenarios, low-level libraries, and to optimize performance in scenarios where integer math is used extensively.
The native-sized integer types are represented internally as the .NET types xref:System.IntPtr?displayProperty=nameWithType and xref:System.UIntPtr?displayProperty=nameWithType. Starting in C# 11, the nint and nuint types are aliases for the underlying types.
The default value of each integral type is zero, 0 .
Each of the integral types has MinValue and MaxValue properties that provide the minimum and maximum value of that type. These properties are compile-time constants except for the case of the native-sized types ( nint and nuint ). The MinValue and MaxValue properties are calculated at runtime for native-sized types. The sizes of those types depend on the process settings.
Use the xref:System.Numerics.BigInteger?displayProperty=nameWithType structure to represent a signed integer with no upper or lower bounds.
Integer literals can be
- decimal: without any prefix
- hexadecimal: with the 0x or 0X prefix
- binary: with the 0b or 0B prefix
The following code demonstrates an example of each:
The preceding example also shows the use of _ as a digit separator. You can use the digit separator with all kinds of numeric literals.
The type of an integer literal is determined by its suffix as follows:
If the literal has no suffix, its type is the first of the following types in which its value can be represented: int , uint , long , ulong .
[!NOTE] Literals are interpreted as positive values. For example, the literal 0xFF_FF_FF_FF represents the number 4294967295 of the uint type, though it has the same bit representation as the number -1 of the int type. If you need a value of a certain type, cast a literal to that type. Use the unchecked operator, if a literal value cannot be represented in the target type. For example, unchecked((int)0xFF_FF_FF_FF) produces -1 .
If the literal is suffixed by U or u , its type is the first of the following types in which its value can be represented: uint , ulong .
If the literal is suffixed by L or l , its type is the first of the following types in which its value can be represented: long , ulong .
[!NOTE] You can use the lowercase letter l as a suffix. However, this generates a compiler warning because the letter l can be confused with the digit 1 . Use L for clarity.
If the literal is suffixed by UL , Ul , uL , ul , LU , Lu , lU , or lu , its type is ulong .
If the value represented by an integer literal exceeds xref:System.UInt64.MaxValue?displayProperty=nameWithType, a compiler error CS1021 occurs.
If the determined type of an integer literal is int and the value represented by the literal is within the range of the destination type, the value can be implicitly converted to sbyte , byte , short , ushort , uint , ulong , nint or nuint :
As the preceding example shows, if the literal’s value isn’t within the range of the destination type, a compiler error CS0031 occurs.
You can also use a cast to convert the value represented by an integer literal to the type other than the determined type of the literal:
You can convert any integral numeric type to any other integral numeric type. If the destination type can store all values of the source type, the conversion is implicit. Otherwise, you need to use a cast expression to perform an explicit conversion. For more information, see Built-in numeric conversions.
Native sized integers
Native sized integer types have special behavior because the storage is determined by the natural integer size on the target machine.
To get the size of a native-sized integer at run time, you can use sizeof() . However, the code must be compiled in an unsafe context. For example:
. code language=»csharp» source=»snippets/shared/NativeIntegerTypes.cs» .
You can also get the equivalent value from the static xref:System.IntPtr.Size?displayProperty=nameWithType and xref:System.UIntPtr.Size?displayProperty=nameWithType properties.
To get the minimum and maximum values of native-sized integers at run time, use MinValue and MaxValue as static properties with the nint and nuint keywords, as in the following example:
. code language=»csharp» source=»snippets/shared/NativeIntegerTypes.cs» .
You can use constant values in the following ranges:
- For nint : xref:System.Int32.MinValue?displayProperty=nameWithType to xref:System.Int32.MaxValue?displayProperty=nameWithType.
- For nuint : xref:System.UInt32.MinValue?displayProperty=nameWithType to xref:System.UInt32.MaxValue?displayProperty=nameWithType.
The compiler provides implicit and explicit conversions to other numeric types. For more information, see Built-in numeric conversions.
There’s no direct syntax for native-sized integer literals. There’s no suffix to indicate that a literal is a native-sized integer, such as L to indicate a long . You can use implicit or explicit casts of other integer values instead. For example:
C# language specification
For more information, see the following sections of the C# language specification:
Свод правил по работе с целыми числами в C/C++

В основу статьи легли мои собственные выработанные нелегким путем знания о принципах работы и правильном использовании целых чисел в C/C++. Помимо самих правил, я решил привести список распространенных заблуждений и сделать небольшое сравнение системы целочисленных типов в нескольких передовых языках. Все изложение строилось вокруг баланса между краткостью и полноценностью, чтобы не усложнять восприятие и при этом отчетливо передать важные детали.
Всякий раз, когда я читаю или пишу код на C/C++, мне приходится вспоминать и применять эти правила в тех или иных ситуациях, например при выборе подходящего типа для локальной переменной/элемента массива/поля структуры, при преобразовании типов, а также в любых арифметических операциях или сравнениях. Обратите внимание, что типы чисел с плавающей запятой мы затрагивать не будем, так как это большей частью относится к анализу и обработке ошибок аппроксимации, вызванных округлением. В противоположность этому, математика целых чисел лежит в основе как программирования, так и компьютерной науки в целом, и в теории вычисления здесь всегда точны (не считая проблем реализации вроде переполнения).
Типы данных
Базовые целочисленные типы
Целочисленные типы устанавливаются с помощью допустимой последовательности ключевых слов, взятых из набора
Несмотря на то, что битовая ширина каждого базового целочисленного типа определяется реализацией (т.е. зависит от компилятора и платформы), стандартом закреплены следующие их свойства:
- char : минимум 8 бит в ширину;
- short : минимум 16 бит и при этом не меньше char ;
- int : минимум 16 бит и при этом не меньше short ;
- long : минимум 32 бит и при этом не меньше int ;
- long long : минимум 64 бит и при этом не меньше long .
Наличие знака
- Стандартный сhar может иметь знак или быть беззнаковым, что зависит от реализации.
- Стандартные short , int , long и long long идут со знаком. Беззнаковыми их можно сделать, добавив ключевое слово unsigned .
- Числа со знаком можно кодировать в двоичном формате в виде дополнительного кода, обратного или как величину со знаком. Это определяется реализацией. Заметьте, что обратный код и величина со знаком имеют различные шаблоны битов для отрицательного нуля и положительного, в то время как дополнительный код имеет уникальный нуль.
- Символьные литералы (в одинарных кавычках) имеют тип ( signed ) int в C, но ( signed или unsigned ) char в C++.
Дополнительные правила
- sizeof(char) всегда равен 1, независимо от битовой ширины char .
- Битовая ширина не обязательно должна отличаться. Например, допустимо использовать char , short и int , каждый шириной в 32 бита.
- Битовая ширина должна быть кратна 2. Например, int может иметь ширину 36 бит.
- Есть разные способы написания целочисленного типа. К примеру, в каждой следующей строке перечислен набор синонимов:
- int , signed , signed int , int signed ;
- short , short int , short signed , short signed int ;
- unsigned long long , long unsigned int long , int long long unsigned .
Типы из стандартных библиотек
- size_t (определен в stddef.h) является беззнаковым и содержит не менее 16 бит. При этом не гарантируется, что его ширина будет как минимум равна int .
- ptrdiff_t (определен в stddef.h) является целочисленным типом со знаком. Вычитание двух указателей будет давать этот тип. При этом не стоит ожидать, что вычитание двух указателей даст int .
- В stdint.h определена конкретная ширина типов: uint8_t , int8_t , 16 , 32 и 64 . Будьте внимательны к операциям, подразумевающим продвижение типов. Например, uint8_t + uint8_t даст int (со знаком и шириной не менее 16 бит), а не uint8_t , как можно было предположить.
Преобразования
Представим, что значение исходного целочисленного типа нужно преобразовать в значение целевого целочисленного типа. Такая ситуация может возникнуть при явном приведении, неявном приведении в процессе присваивания или при продвижении типов.
Как происходит преобразование?
Главный принцип в том, что, если целевой тип может содержать значение исходного типа, то это значение семантически сохраняется.
- Когда исходный тип расширяется до целевого типа с аналогичной знаковой характеристикой (например, signed char -> int или unsigned short -> unsigned long ), каждое исходное значение после преобразования сохраняется.
- Даже если исходный и целевой типы имеют разные диапазоны, все значения в их пересекающейся части будут сохранены. Например, int , содержащий значение в диапазоне [0, 255] , будет без потерь преобразован в unsigned char .
- При преобразовании в беззнаковый тип новое значение равняется старому значению по модулю 2 целевая ширина в битах . Объяснение:
- Если исходный тип беззнаковый и шире целевого, тогда старшие биты отбрасываются.
- Если исходный тип имеет знак, тогда в процессе преобразования берется исходное значение, и из него/к нему вычитается/прибавляется 2 целевая ширина в битах до тех пор, пока новое значение не впишется в диапазон целевого типа. Более того, если число со знаком представлено в дополнительном коде, то в процессе преобразования старшие биты отбрасываются, как и в случае с беззнаковыми числами.
- Если исходное значение вписывается в диапазон целевого типа, тогда процесс преобразования (например, расширение знака) производит целевое значение, семантически равное исходному.
- Если же оно не вписывается, тогда поведение будет определяться реализацией и может вызвать исключение (к примеру, прерывание из-за переполнения).
Арифметика
Продвижение/преобразование
-
Унарный арифметический оператор применяется только к одному операнду. Примеры: — ,
- В реализации присутствуют 16-битный short и 24-битный int . Если переменные x и y имеют тип unsigned short , то операция x & y продвигает оба операнда до signed int .
- В реализации присутствуют 32-битный char и 32-битный int . Если переменные x и y имеют тип unsigned char , то операция x – y продвигает оба операнда до unsigned int .
- (long) + (long) → (long) ;
- (unsigned int) * (int) → (unsigned int) ;
- (unsigned long) / (int) → (unsigned long) ;
- если int является 32-битным, а long 64-битным: (unsigned int) % (long) → (long) ;
- если int и long оба являются 32-битными: (unsigned int) % (long) → (unsigned long) .
Неопределенное поведение
Знаковое переполнение:
- При выполнении арифметических операций над целочисленным типом переполнение считается неопределенным поведением (UB). Такое поведение может вызывать верные, несогласованные и/или неверные действия как сразу, так и в дальнейшем.
- При выполнении арифметики над беззнаковым целым (после продвижений и преобразований) любое переполнение гарантированно вызовет оборот значения. Например, UINT_MAX + 1 == 0 .
- Выполнение арифметики над беззнаковыми целыми фиксированного размера может привести к едва уловимым ошибкам. Например:
- Пусть uint16_t = unsigned short , и int равен 32-битам. Тогда uint16_t x=0xFFFF , y=0xFFFF , z=x*y ; x и y будут продвинуты до int , и x * y приведет к переполнению int , вызвав неопределенное поведение.
- Пусть uint32_t = unsigned char , и int равен 33-битам. Тогда uint32_t x=0xFFFFFFFF , y=0xFFFFFFFF , z=x+y ; x и y будут продвинуты до int , и x + y приведет к переполнению int , то есть неопределенному поведению.
- Чтобы обеспечить безопасную арифметику с беззнаковыми целыми, нужно либо прибавить 0U , либо умножить на 1U в качестве пустой операции. Например: 0U + x + y или 1U * x * y . Это гарантирует, что операнды будут продвинуты как минимум до ранга int и при этом останутся без знаков.
- Деление на нуль и остаток с делителем нуля также относятся к неопределенному поведению.
- Беззнаковое деление/остаток не имеют других особых случаев.
- Деление со знаком может вызывать переполнение, например INT_MIN / -1 .
- Остаток со знаком при отрицательных операндах может вызывать сложности, так как некоторые части являются однообразными, в то время как другие определяются реализацией.
- Неопределенным поведением считается битовый сдвиг (< < и >>) на размер, который либо отрицателен, либо равен или больше битовой ширины.
- Левый сдвиг беззнакового операнда (после продвижения/преобразования) считается определенным правильно и отклонений в поведении не вызывает.
- Левый сдвиг операнда со знаком, содержащего неотрицательное значение, вследствие которого 1 бит переходит в знаковый бит, является неопределенным поведением.
- Левый сдвиг отрицательного значения относится к неопределенному поведению.
- Правый сдвиг неотрицательного значения (в типе операнда без знака или со знаком) считается определенным правильно и отклонений в поведении не вызывает.
- Правый сдвиг отрицательного значения определяется реализацией.
Счетчик цикла
Выбор типа
Предположим, что у нас есть массив, в котором нужно обработать каждый элемент последовательно. Длина массива хранится в переменной len типа T0 . Как нужно объявить переменную счетчика цикла i типа T1 ?
- Самым простым решением будет использовать тот же тип, что и у переменной длины. Например:
- Говоря обобщенно, переменная счетчика типа T1 будет работать верно, если диапазон T1 будет являться (не строго) надмножетсвом диапазона T0 . Например, если len имеет тип uint16_t , тогда отсчет с использованием signed long (не менее 32 бит) сработает.
- Говоря же более конкретно, счетчик цикла должен просто покрывать всю фактическую длину. Например, если len типа int гарантированно будет иметь значение в диапазоне [3,50] (обусловленное логикой приложения), тогда допустимо отсчитывать цикл, используя char без знака или со знаком (в котором однозначно можно представить диапазон [0,127] ).
- Нежелательно использовать переменную длины и переменную счетчика с разной знаковостью. В этом случае сравнение вызовет неявное сложное преобразование, сопровождаемое характерными для платформы проблемами. К примеру, не стоит писать такой код:
Отсчет вниз
Для циклов, ведущих отсчет вниз, более естественным будет использовать счетчик со знаком, потому что тогда можно написать:
При этом для беззнакового счетчика код будет таким:
Примечание: сравнение i >= 0 имеет смысл только, когда i является числом со знаком, но всегда будет давать true , если оно будет беззнаковым. Поэтому, когда это выражение встречается в беззнаковом контексте, значит, автор кода скорее всего допустил ошибку в логике.
Заблуждения
Все пункты приведенного ниже списка являются мифами. Не опирайтесь на эти ложные убеждения, если хотите писать корректный и портируемый код.
What is the difference between “int” and “uint” / “long” and “ulong”?
I know about int and long (32-bit and 64-bit numbers), but what are uint and ulong ?
6 Answers 6
The primitive data types prefixed with «u» are unsigned versions with the same bit sizes. Effectively, this means they cannot store negative numbers, but on the other hand they can store positive numbers twice as large as their signed counterparts. The signed counterparts do not have «u» prefixed.
The limits for int (32 bit) are:
And for long (64 bit):

uint and ulong are the unsigned versions of int and long . That means they can’t be negative. Instead they have a larger maximum value.
To write a literal unsigned int in your source code you can use the suffix u or U for example 123U .
You should not use uint and ulong in your public interface if you wish to be CLS-Compliant.
Read the documentation for more information:
By the way, there is also short and ushort and byte and sbyte.
The difference is that the uint and ulong are unsigned data types, meaning the range is different: They do not accept negative values:

u means unsigned , so ulong is a large number without sign. You can store a bigger value in ulong than long , but no negative numbers allowed.
A long value is stored in 64-bit,with its first digit to show if it’s a positive/negative number. while ulong is also 64-bit, with all 64 bit to store the number. so the maximum of ulong is 2(64)-1, while long is 2(63)-1.
Based on the other answers here and a little review you can understand it this way: unsigned is in reference to the assignment of a negative or positive explicit assignment (think the "-" in -1) and the inability to have negative versions of said numbers.
And because of this capacity on the negative end being removed as an option they instead allocated that capacity to the positive end hence the doubling of the positive valuation’s maximum value. So instead of the bit range being split along positive and negative valuations, they are instead for ushort, uint, along, etc allocated to the positive end of the valuation.
It’s been a while since I C++’d but these answers are off a bit.
As far as the size goes, ‘int’ isn’t anything. It’s a notional value of a standard integer; assumed to be fast for purposes of things like iteration. It doesn’t have a preset size.
So, the answers are correct with respect to the differences between int and uint, but are incorrect when they talk about "how large they are" or what their range is. That size is undefined, or more accurately, it will change with the compiler and platform.
It’s never polite to discuss the size of your bits in public.
When you compile a program, int does have a size, as you’ve taken the abstract C/C++ and turned it into concrete machine code.
So, TODAY, practically speaking with most common compilers, they are correct. But do not assume this.
Specifically: if you’re writing a 32 bit program, int will be one thing, 64 bit, it can be different, and 16 bit is different. I’ve gone through all three and briefly looked at 6502 shudder
use int if you really don’t care how large your bits are; it can change.
Use size_t and ssize_t if you want to know how large something is.
If you’re reading or writing binary data, don’t use int. Use a (usually platform/source dependent) specific keyword. WinSDK has plenty of good, maintainable examples of this. Other platforms do too.
I’ve spent a LOT of time going through code from people that "SMH" at the idea that this is all just academic/pedantic. These ate the people that write unmaintainable code. Sure, it’s easy to use type ‘int’ and use it without all the extra darn typing. It’s a lot of work to figure out what they really meant, and a bit mind-numbing.
It’s fragile coding when you mix int and assume sizes.
use int and uint when you just want a fast integer and don’t care about the range (other than signed/unsigned).