Chapter 10 - More integer types
So far, we have encountered two types: the int type, and the char type.
Of course, there are more types than that. For now, though, we will confine ourselves to integer types.
The short int
If there were a prize for the least useful standard C type, short int would undoubtedly win it. Like the int, the short int is guaranteed to be able to represent any value in the range -32767 to +32767. Its value range must not exceed that of int, but its width isn't guaranteed to be less than that of int. In other words, in any situation where you can use a short int, you can just as easily use an int. The only possible advantage is that, on some systems, short int is less wide than int, which might save you a little memory if you need an awful lot of values that fit into a short int.
The printf function supports the short int, using an 'h' between the '%' escape character and the 'd' conversion specifier, as follows:
#include <stdio.h> int main(void) { short int i = 6; printf("%hd\n", i); return 0; }
One more oddity: the word 'int' is optional in a definition of a short int. The above program could have read:
short i = 6;
without changing its meaning.
And that really is all there is to say about the short int.
The long int
The long int is more interesting, because it has a wider guaranteed range: -2147483647 to +2147483647. As a matter of fact, on many modern systems this is the usual range of int, which makes long int slightly less useful than you might have thought.
Again, the word 'int' is optional in a definition of a long int.
The printf function supports long int via a letter 'l' between the '%' escape character and the 'd' conversion specifier. Here, then, is a program that uses a long int:
#include <stdio.h> int main(void) { long million = 1000000L; /* note the L */ printf("%ld\n", million); return 0; }
When we want to specify that an integer constant should be considered to be a long int, we should add a suffix of 'L' (or 'l') to it. 'L' is generally easier to distinguish from '1' than 'l' is, so it is to be preferred. Thus, 567 is an int, but 567L is a long int.
The long int is used for some standard library functions, so it's worth getting to know. Also, it is guaranteed to have the range given above, whereas int is only likely to have that range, so when you need bigger numbers, long int is the way to go.
The long long int
The C language was standardised by ANSI in 1989, and ISO adopted the ANSI C Standard as an ISO Standard (ISO/IEC 9899) in 1990. At that time, the long long int type did not exist.
In 1999, the language definition was revised (ISO/IEC 9899:1999), and long long int was introduced.
Some C programmers are limited by the need to remain compatible with the earlier standard (often referred to as C89 or C90). For them, long long int is not an option. If that is not a concern for you, then you may use the long long int type. The minimum guaranteed range of long long int is rather larger than that of the long int; it is -9223372036854775807 to +9223372036854775807, which is gratifyingly large.
The integer suffix for a long long int is LL (or ll). So 567L is a long int, but 567LL is a long long int.
To print a long long int (or, if you prefer, just long long) using the printf function, insert two 'l' characters between the '%' escape character and the 'd' conversion specifier, as follows:
#include <stdio.h> int main(void) { int billion = 1000000000000LL; printf("An English billion is %lld\n", billion); billion /= 1000; printf("An American billion is %lld\n", billion); return 0; }
Signed and unsigned integer types
With one exception, the int type is a signed integer type. We can, if we wish, be explicit about this:
#include <stdio.h> int main(void) { signed int si = 567; signed short int ssi = 567; signed long int sli = 567L; signed long long int slli = 567LL; printf("%d %hd %ld %lld\n", si, ssi, sli, slli); return 0; }
but most people don't bother.
It is, however, possible and often desirable to use the unsigned equivalent of a signed integer type. An unsigned integer type cannot represent negative integers, but it has (roughly) twice the range of positive integers. It also has the delightful characteristic that it cannot overflow.
Any type can only store a limited range of values, and it therefore has a maximum value and a minimum value. So what happens if you have, say, an int, that already contains the maximum value, and then try to add 1 to it? Or maybe it has the minimum value, and you try to subtract 1 from it. The answer is that the C language does not say what will happen! In theory, anything could happen. (In practice, it is probably the case that the value will silently wrap around to the other extreme, but C does not guarantee this.)
For unsigned integer types, however, C does say what will happen. If you try to assign to an unsigned integer type a value that is outside its range, the value will be adjusted so that it is within the range; the adjustment takes the form of adding or subtracting one more than the maximum value of the type as many times as are necessary until the value is within the range. So, for example, if you had an object of an unsigned integer type with a maximum value of 15 (there is in fact no such type, but it makes the numbers easier if we choose a very small type), and then you tried to assign 83 to it, the actual value assigned would be 83 % 16 = 3. If you tried to assign a negative number (say, -83) to it, the value would be 13, which we get by continually adding 16 to the number we have until it becomes non-negative.
Here are the minimum ranges for the unsigned integer types (leaving char aside for the moment):
Type | Minimum | Maximum |
---|---|---|
unsigned short int | 0 | 65535 |
unsigned int | 0 | 65535 |
unsigned long int | 0 | 4294967295 |
unsigned long long int | 0 | 18446744073709551615 |
The char type is slightly more involved, because signed char is not necessarily an alias for char. In fact, it is best to think of three separate char types:
Type | Range |
---|---|
char | From 0 to +255 or from -127 to +127 |
unsigned char | From 0 to +255 |
signed char | From -127 to +127 |
The unsigned char and signed char types are obvious enough, but whether char itself can represent negative values depends on the implementation. As a rule of thumb, you're probably using an ASCII system, in which case char is probably signed by default, but if you're using an EBCDIC system then char is probably unsigned by default. Some compilers let you make the choice yourself by setting a switch. It is generally best, however, to avoid situations where the choice matters. Here's how to use the three types:
- When you just need to deal with characters from the character set, use char;
- When you need to save memory by using a very small signed integer type, use signed char;
- When you need a type that represents a byte, rather than a character or a number per se, use unsigned char.
All three variants of the char type are guaranteed to be exactly one byte wide. In C terminology, a byte is defined as an addressable unit of data storage large enough to hold any member of the basic character set of the execution environment, and is guaranteed to be at least 8 bits wide, although it might be wider. (A bit is large enough to hold an object that may have one of two values. It's the most fundamental unit in C.)
Summary
In this chapter, you met some new integer types, and were introduced to the idea of unsigned types.
In the next chapter, we will take a closer look at strings, and how they can be used to capture useful input.
Progress
Terminology
- signed integer type
- unsigned integer type
- byte
- bit
- variadic function
- conversion specifier
- precedence
- array
- index
- pointer
- sentinel value
- null character
Syntax
- comments
- types
- operators
- increment and decrement operators
- ++n n++ --n n--
- assignment operators
- = += -= *= /= %=
- additive operators
- the + operator
- the - operator
- multiplicative operators
- the * operator
- the / operator
- the % operator
- equality and relational operators
- == != < > <= >= !
- logical operators
- the && operator
- the || operator
- address and indirection operators
- the unary * operator
- the unary & operator
- the array subscripting operator []
- miscellaneous operators
- the conditional operator ? :
- the comma operator ,
- the sizeof operator
- increment and decrement operators
- control structures
- if/else
- while
- do/while
- for