Use Fixed Width Integers
How many bits does an int have? 32 bits?
Let us check our old friend ISO Standard.
The minimum size of the integer types is defined in <limits.h> (or <climits> in C++). The standard specifies that a signed integer must be able to represent at least the range of numbers [-32767, +32767]. In other words, an int must have 16 bits or more according to the standard.
The "or more" is the source of confusion. For example, most x86-64 compilers, such as the GCC 9.4.0 I am using now, use more bits than the minimum values specified in the ISO Standard. For instance, the GCC will store an int using 32 bits in a x86-64 machine.
So what is the problem? The compiler is giving us more bits than the minimum!
Well, the problem is that many programmers will trust that these wider values are standard. For instance, consider that we are storing phone numbers (e.g., 9999-9999) in an unsigned int. This will only work if the compiler uses 32 bits or more to store such unsigned integers.
There is no problem using conventional integer types (int, short, unsigned int, ...) if you guarantee that the minimum values specified in the ISO standard are enough to store your values without overflows, or if you can ensure that the program will be compiled to a specific architecture using a specific compiler (argh!!!).
If you must declare an integer type and enforce its width (independently of architecture and compiler), you should use the types specified in the <stdint.h> header (<cstdint> in C++), defined in the ISO99 C. In this header, you will find types such as the int32_t, a signed integer guaranteed to have 32 bits.
This is such an essential feature that the Google C++ style guide enforces the use of such types in the developments of Google Software. See in https://google.github.io/styleguide/cppguide.html#Integer_Types
The following Table shows some types defined in the <stdint.h> header. For a complete list, check https://cplusplus.com/reference/cstdint/.
signed type | unsigned type | Description |
int8_t | uint8_t | Signed/unsigned 8-bits integer. |
int16_t | uint16_t | Signed/unsigned 16-bits integer. |
int32_t | uint32_t | Signed/unsigned 32-bits integer. |
int64_t | uint64_t | Signed/unsigned 64-bits integer. This type may not be defined in some architectures. |
Check out an example:
#include <stdio.h>
#include <stdint.h>
int main(){
int v1;
int32_t v2;
//this may not work according to the ISO Standard
v1 = 12345678;
//this is safe
v2 = 12345678;
printf("%d %d\n", v1, v2);
return 0;
}
It is important to mention that the header does not define any new type. For instance, the int8_t will be just a typedef such as: typedef unsigned char int8_t.
And, as always, I strongly recommend reading the references for more details and fun!
References
C ISO Standard. ISO/IEC 9899:2018, 2018.
Google C++ style guide: https://google.github.io/styleguide/cppguide.html#Integer_Types
Seacord, R. C. Effective C: An Introduction to Professional C Programming. No Starch Press. 2020.
Seacord, R. C. Secure Coding in C and C++. Reino Unido: Pearson Education. 2013.