C++ builtin types

C++ has a vast and complicated type system, but strangely, it doesn’t have that many builtin types:

  • void
  • bool
  • char
  • short
  • int
  • long
  • long long
  • float
  • double
  • long double

Integer types

char, short, int, long and long long are integer types. That means that they can hold whole numbers up to some maximum value. The maximum value can theoretically change a lot from implementation to implementation, but nearly all modern architectures uses the same values, which are:

type min max bits
char -128 127 8
short -32768 32767 16
int -2.1 billion 2.1 billion 32
long -9.8 * 10^18 9.8 * 10^18 64
long long -a lot a lot 128

Since these limits could be different, it’s recommended to include <std/types.h> and use int8_t, int16_t, int32_t, int64_t and int128_t. That should always work the same, assuming the compiler even supports the used type.

All the integer types can also be made “unsigned”. Unsigned types cannot have negative values, but in return the maximum value doubles (+1). An “unsigned char” usually have a max value of 255. Again, it’s better to use types from <std/types.h>: uint8_t, uint16_t, uint32_t, etc.

Signed and unsigned types have one more difference: Overflow on signed numbers have undefined effects. What his means is that this code is not guaranteed to work:

  char x = 127;
  x++;
  if (x < 0)  print("X wrapped around\n");

The compiler is allowed to assume that x will never wrap around, and may decide that the if statement can never return true and can be optimized away.

The same is not true for unsigned numbers. For unsigned numbers, wraparound is a defined operation, so the following code will work as expected:

  uint8_t x = 255;
  x++;
  if (x == 0) print("X wrapped around\n");

floating-point types

float, double and long double are floating point “decimal” numbers. They still store numbers as bits, but a few bits are reserved for an exponent. This allows floating point numbers to store very large and very small numbers, but not always precisely. Generally float is enough for most calculations, but double has better precision, and long double is sometimes better still.

Note that Proffieboards have hardware for doing float calculations, but it does not have hardware for doing double calculations. This means that even though a double is only twice as precise as a float, it may be hundreds of times slower.

Examples:

float a = 1000.0;
float b = sqrt(a);  // 31.622..
a = b * b; // approximately 1000.0

When comparing floats, you almost always need to make an approximate comparison rather than an exact one. Here’s an example how how this is done:

// this might not work
if (a == 1000.0) do_something();

// this should work
float epsilon = 0.000001;
if (abs(a - 1000.0) < epsilon) do_something();

bool

Bool is a type that can only have two values: true or false. In some cases a bool can be stored in a single bit, but because you can’t take the address of a bit, that doesn’t really work in C++, so C++ normally uses a byte (uint8_t) to store a bool.

void

Void isn’t a real type. It’s the absence of a type. It’s generally used for functions that don’t return anything.

combinations

The types above are the basics on which everything is built. However, you can use arrays, structs, classes, pointers and function types to combine these into more complicated types / chunks of memory. This includes strings, vectors, and all the other stuff in the std:: library, which is a part of C++, but they are not “builtin” types.

Next up: memory, arrays and pointers.

1 Like