Integer Types in C
In this guide on the C language, we have, until now, used only two basic (built-in) data types: int and float.
Now it is time to deepen the catalog of basic data types of the C language. In this lesson, in particular, we will begin to study integer numeric types.
Integer Types
The C language supports two different groups of numeric types: integer types and floating point types also called floating point types.
Integer type values represent, precisely, integer numbers without fractional part. Floating point types, instead, allow to represent numbers with fractional part as well.
In turn, integer types are divided into two categories: signed integer types and unsigned integer types called, respectively, signed integers and unsigned integers.
Integer Types
In C Language, integer types are used to represent integer numbers without fractional part. Integer types are divided into two categories: signed integer types and unsigned integer types.
Signed and Unsigned Integers
To fully understand the difference between signed and unsigned integer types, it is necessary to understand how numbers are represented in memory.
Without going too much into detail, it is enough to know that signed integer numbers are represented by reserving the most significant bit, that is the leftmost bit (leftmost bit) for the sign. This means that a signed integer number can assume both positive and negative values.
For example, if we use 16 bits to represent an integer, the leftmost bit will be reserved for the sign, while the remaining 15 bits will be used to represent the numeric value.
If we take the following 16-bit binary number:
This number represents the decimal value 32767, that is
Similarly, if we had used 8 bits to represent a signed integer number, the maximum representable value would be 127, that is
We do not enter, here, into the merit of how numbers are represented in binary system, but it is important to understand that signed integer numbers can assume both positive and negative values.
When, instead, we deal with unsigned integer numbers, the leftmost bit is not reserved for the sign, but is used to represent the numeric value together with the remaining bits. This means that unsigned integer numbers can assume only positive values, including zero.
For example, if we take the 16-bit number composed of only 1s:
This number represents the decimal value 65535, that is
Conversely, the minimum unsigned number we can represent with 16 bits is, rightly, 0:
Signed and Unsigned Integers
In C Language, integer types are divided into two categories: signed integer types and unsigned integer types.
- Signed integer types (signed) can represent positive and negative values. The leftmost bit is reserved for the sign.
- Unsigned integer types (unsigned) can represent only positive values, zero included. The leftmost bit is used to represent the numeric value.
In general, the formula to determine the representation range of integer numbers can be expressed as follows:
- If we have
bits available, the representation range of signed integer numbers will be:
- While the representation range of unsigned integer numbers will be:
By default, the C language considers all integers as signed integers. To declare an unsigned integer, it is necessary to use the unsigned qualifier.
The syntax to specify an unsigned integer consists of prepending the keyword unsigned to the data type. So, for example, we can write:
unsigned int x;
Similarly, to specify that it is a signed integer, the signed qualifier is used. However, since all integers are considered by default as signed integers, the use of signed is superfluous.
signed and unsigned
In C Language, to specify that an integer is unsigned, the unsigned qualifier is used. To specify that an integer is signed, the signed qualifier can be used, but it is superfluous since all integers are considered by default as signed integers.
The syntax is as follows:
/* Unsigned integer */
unsigned int x;
/* Signed integer */
signed int x;
Both signed and unsigned are reserved keywords of the C language and cannot be used as identifiers.
For example, the following two writings are equivalent:
/* Equivalent */
signed int y;
int y;
Unsigned integer types are often used to work at low level, that is to interact with the machine hardware or to write system programs. Other types of application can be, for example, the representation of values that cannot be negative, such as the age of a person or the number of elements in an array.
Gradually we will see, during this guide, when it is convenient to use one data type rather than another.
Size of Integer Types
The C language provides different integer types with different sizes. Typically, the int type is a 32-bit integer, but this is not always true.
The truth is that the size of int depends on two factors:
- The architecture of the machine on which the program is executed;
- The compiler used to compile the program.
Typically, on 32-bit processors, the integer type int is 32 bits, while on 64-bit processors, int is 64 bits. However, it is not guaranteed that this is true in all cases.
The int type is not the only integer type. The C language also provides the long type which allows to store very large numbers, and the short type which can be used when we need to save space, that is when we need to store a number in a lower number of bits.
To construct an integer of the desired size, these qualifiers can be combined with the int type obtaining, in fact, six possible combinations:
/* Signed Short Integer */
short int a;
/* Unsigned Short Integer */
unsigned short int b;
/* Signed Integer */
int c;
/* Unsigned Integer */
unsigned int d;
/* Signed Long Integer */
long int e;
/* Unsigned Long Integer */
unsigned long int f;
The C syntax also allows omitting the keyword int when declaring an integer. So we can also write:
/* Equivalent */
short a;
unsigned short b;
int c;
unsigned d;
long e;
unsigned long f;
This practice of omitting int is very common in programs written in C. It is so common that in languages with syntax derived from C, such as Java and C#, to declare a short or long integer, int must be omitted.
For this reason, in this guide we will adopt the practice of omitting int when we declare a short, long or unsigned integer.
As we said, the size of an integer type depends on the machine architecture and the compiler. However, there is a series of rules that all C compilers must respect:
-
Although integer types can have different sizes, the compiler must guarantee that they have a minimum size and a maximum size:
- A
shortinteger must have at least 16 bits; - An
intinteger must have at least 16 bits; - A
longinteger must have at least 32 bits.
- A
-
The size of a
shortinteger must be less than or equal to that of anintinteger, which in turn must be less than or equal to that of alonginteger.The consequence is that an
intcould have the same size as ashort, and alongcould have the same size as anint. But it must never happen that ashorthas a size greater than anint, or that aninthas a size greater than along:
Size of Integers in C Language
The C language standard does not impose a specific size for integer types. The size of an integer type depends on the machine architecture and the compiler used.
However, the standard requires that:
- A
shortinteger must have at least 16 bits; - An
intinteger must have at least 16 bits; - A
longinteger must have at least 32 bits.
Furthermore, the size of a short integer must be less than or equal to that of an int integer, which in turn must be less than or equal to that of a long integer.
In general, typical sizes can be expected depending on the machine architecture.
In the following table, we show the typical sizes of integers for a 16-bit processor:
| Type | Size | Minimum Value | Maximum Value |
|---|---|---|---|
short |
16 bit | ||
unsigned short |
16 bit | ||
int |
16 bit | ||
unsigned int |
16 bit | ||
long |
32 bit | ||
unsigned long |
32 bit |
The next table shows, instead, the typical sizes of integers for a 32-bit processor:
| Type | Size | Minimum Value | Maximum Value |
|---|---|---|---|
short |
16 bit | ||
unsigned short |
16 bit | ||
int |
32 bit | ||
unsigned int |
32 bit | ||
long |
32 bit | ||
unsigned long |
32 bit |
Finally, the following table shows the typical sizes of integers for a 64-bit processor:
| Type | Size | Minimum Value | Maximum Value |
|---|---|---|---|
short |
16 bit | ||
unsigned short |
16 bit | ||
int |
32 bit | ||
unsigned int |
32 bit | ||
long |
64 bit | ||
unsigned long |
64 bit |
Even though we have reported tables that show the typical size of integers depending on the processor architecture, it is good to remember that the sizes reported are not mandatory. This means that not necessarily an int integer on a 32-bit processor will be 32 bits. However, it is very likely that this will happen.
There are various ways to determine the size of an integer type on a specific architecture. We will see them in the next lessons.
Integers in the C99 Standard
The C99 standard provides two new integer types: long long int and unsigned long long int.
These types were added to meet the ever-growing needs to be able to represent and manipulate increasingly larger numeric values on new 64-bit processors.
According to what the C99 standard asserts, a variable of type long long int or of type unsigned long long int must have at least 64 bits. Therefore, based on this, we are sure that such types will have the following characteristics, regardless of the compiler or machine architecture:
| Type | Size | Typical Minimum Value | Typical Maximum Value |
|---|---|---|---|
long long int |
64 bit | ||
unsigned long long int |
64 bit |
Obviously, depending on the compiler implementation, the long long int and unsigned long long int types could have sizes larger than 64 bits.
In general, in the C99 standard, the types short, int, long and long long are called standard signed integer types (standard signed integer types).
Conversely, the types unsigned short, unsigned int, unsigned long and unsigned long long are called standard unsigned integer types (standard unsigned integer types).
These names were chosen to differentiate them from extended integer types (extended integer types), which are integer types that can have sizes larger than the standard ones and that the various compilers can implement at their discretion.
long long Integer Types in C99
In the C99 standard, two new integer types were introduced: long long int and unsigned long long int.
According to the standard, long long variables and unsigned long long variables must use at least 64 bits.
Known-Size Integer Types
To overcome the problem of the variable size of integer types, the C99 standard introduced a series of known-size integer types.
These types are defined in the stdint.h header and are the following:
int8_t: 8-bit signed integer;uint8_t: 8-bit unsigned integer;int16_t: 16-bit signed integer;uint16_t: 16-bit unsigned integer;int32_t: 32-bit signed integer;uint32_t: 32-bit unsigned integer;int64_t: 64-bit signed integer;uint64_t: 64-bit unsigned integer.
These types are guaranteed to have a fixed size and were introduced to ensure the portability of C programs on different architectures.
Therefore, if we declare, for example, a variable of type int32_t, we can be sure that it will always have 32 bits, regardless of the machine architecture:
#include <stdint.h>
int32_t x;
In Summary
In this lesson, we have deepened the catalog of integer numeric types that the C language makes available to the programmer.
In particular, we have studied that:
- Integer types represent integer numbers without fractional part;
- Integer types are divided into two categories: signed integer types and unsigned integer types;
- Signed integer types can represent positive and negative values, while unsigned integer types can represent only positive values;
- The size of an integer type depends on the machine architecture and the compiler used;
- The C99 standard introduced the
long long intandunsigned long long inttypes to represent very large numbers; - The C99 standard introduced known-size integer types to ensure the portability of C programs on different architectures.
In the next lesson, we will study integer constants, also called integer literal values, and we will see how numbers can be written in source code.