Arduino Variable Types

Whether you're just starting out or have some experience with microcontrollers, understanding Arduino variable types is essential for effectively programming your Arduino projects.

Data types define how information is stored in variables and manipulated in memory. This tutorial will provide an overview of the main Arduino data types and demonstrate how you can declare them with simple code snippets.

Why do Arduino variable types exist?

You can use Arduino Data types to define the type of data that a variable can hold. Processors work on defined boundaries and for the 8-bit Arduino Uno this is a byte boundary. it is an 8-bit processor. The processor has to know how many bytes that each type of information is using.

If types did not exist then you would be limited to using 8 bits and only able to store values from -128 to 127 ! By using the integer type you can use and manipulate values from -32,768 to 32,767. (the type 'long', allows even larger whole numbers).

Even though the processor is only capable of working with 8 bit entities, it uses a carry flag to allow information to be "carried over" to the next byte for calculation. It means you can have arithmetic operations on large numbers that occupy more than a single byte of memory.

As a programmer you need to be aware of the "type" of a variable so that is capable of holding the values you expect to use.

Types allow the compiler to allocate the right amount of memory for the variable and to perform the correct arithmetic operations on it.

As well as whole number arithmetic, another type is 'float', that while still operating using 8bits, treats those bits in an entirely different way allowing floating-point-number calculation. In addition you can use character types (type 'char') that code the 8 bit data into numbers that represent characters.

Arduino Variables

In the Arduino programming environment, variables allow you to store and work with different pieces of data within your sketches.

Before declaring a variable, you need to specify its data type which tells the microcontroller how much memory space to allocate and what type of data can be stored there.

Common Arduino variable types are:

  • int: Stores integer (whole number) values. Uses 2 bytes of memory.

        Example declaration:     int sensorReading;

  • float: Stores floating point numbers, useful for measurements with decimals. Uses 4 bytes of memory.

    declaration:     float temperature;

  • boolean: Can only hold true or false values. Uses 1 byte of memory. Common for digital outputs.

    declaration:     boolean buttonPressed;

  • char: Stores a single character such as letters, numbers, or symbols. Uses 1 byte of memory.

    declaration:     char initial;

  • String: Special class used to store and manipulate strings (sequences of characters). Takes up dynamic memory as needed.
    [ Warning  - there can be problems using dynamic strings.]
    [ An alternative is to use vanilla C strings. ]
    [ Find out the issues in this link "Arduino String". ]

    declaration:     String message = "Hello";

  • byte: Alias for unsigned 8-bit integer, same as int but uses less memory.

    declaration:     byte ledPin = 13;

  • long: Alias for unsigned 32-bit integer. Uses 4 bytes.

    declaration:     long bigInteger;

Using integer Arduino types

The basic unit of memory in any processor is a byte but in fact in C/C++ programming the byte type is not usually defined.

The 'char' type is the usual way of writing (In non-Arduino code) that you want to use a single byte of data for your variable:

char myByte;

In Arduino code you can write:

byte myByte;

Note: if you want to use the word byte in code other than Arduino, you can define your own type as follows:

typedef char byte;

Now you can write (in any C/C++ programming environment):

byte myByte;

Integer types

Forgetting about 'type' byte for the moment.

There are three fundamental whole number (or integer types):

  1. char
  2. int
  3. long

In the Arduino Environment these are of length:

  1. [ char ] 1 byte.
  2. [ int ]   2 bytes.
  3. [ long ] 4 bytes.

Now the next thing to know about them is that they all come in two flavours!

Signed integers

Using a method called two's complement you can easily do addition and subtraction (by only adding!). [the method is to invert all the bits in the bytes and then add 1 - this will make the  number negative. Try it and see i.e. choose a number say 11 then perform 2's complement on another number (3 say). Add them together and you will get a result of 8].

Signed Integer Range Values

Now, the only reason for saying the above is that it is quite easy to allow integer types to hold negative or positive numbers (the left most bit - if set - indicates a negative number). Here are the range of values for the three integer types:

    char:      -128 to 127

    int         -32,768 to 32,767

    long:     -2,147,483,648 to 2,147,483,647

Unsigned Integer Range Values

The other idea is that you could ignore the negative numbers and focus on only the positive numbers that a type can hold:

    char:     0 to 255

    int         0 to 65,535

    long:     0 to 4,294,967,295

So, you can choose to tell the compiler that you only want positive numbers...

    unsigned char myUnsignedByte;
    unsigned int myUnsignedInt;
    unsigned long myUnsignedLong;

...or both negative and positive numbers (the default where you don't need to write the word signed - it is implied):

    signed char myUnsignedByte;
    signed int myUnsignedInt;
    signed long myUnsignedLong;

Choosing signed or unsigned

It all depends on your application.

For instance if you needed to process numbers above 32768 but below 65535 you would use an unsigned int.

You could ask the question why not use a 'long' type variable?

The answer is you could but it would be less efficient (slower and would use more memory).

A word of warning

Oddly enough the C/C++ language does not specify how many bits are to be used in an integer type. This makes the language more flexible because it can cope with different processor architectures.

For instance if you use a 32 bit processor, it is more work to split the 32 bits into blocks of 16 so an integer will probably be represented by 32 bits; so a bit of memory is wasted but it will be far faster, and the 32bit microcontroller probably has lots of SRAM memory anyway.

Warning: The C/C++ language does not specify how many bits integer types can occupy.

This sounds fine, but when you come to moving a program from one processor to a different one, it may or may not work. This sounds counter intuitive but here's an example.

Example of type size problem

Lets say you have made a counter using a unsigned int and you know that the range of values that the counter can adopt are 0 ~ 65535. Your program relies on this operation i.e. that increasing the value when you get to 65535, resets the counter to zero (the 16 bit int cannot hold a number bigger than this).

Now, moving to a 32 bit processor may allow the range of values from 0 ~ 4,294,967,295 i.e. entirely different operation. Note, I say may because it depends on how the compiler implements type int.

Conclusions on

You have to know about the number ranges represented using each specific Arduino variable type so that your program will work. For instance assigning a number greater than the maximum allowed positive value for a signed int will result in a negative number (because of how the number is interpreted).

When moving to different processor architectures, the bit size of the fundamental types may be different, as the C/C++ standard does not specify them!

Written by John Main who has a degree in Electronic Engineering.

Note: Parts of this page were written using chatgpt as a research assistant.


Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.

Privacy Policy | Contact | About Me

Site Map | Terms of Use