Is uint8_t equivalent to byte, etc.?

I am coming to Arduino from micro-processors that use various flavors of BASIC, and am somewhat confused about number data types.

In some places I see 'uint8_t' and in other places I see 'byte'. After reading various library *.cpp and *.h files, they use uint8_t, etc. On the Arduino Reference page they describe data types: byte, int , word, etc.

From searching this Forum and online, I have not found a definitive explanation. From my research, my understanding so far is:

         byte OR  uint8_t =  8-bit unsigned, 0 to 255

          int OR  int16_t = 16-bit   signed, -32,768 to 32,767
        short OR  int16_t = 16-bit   signed, -32,768 to 32,767 (Same as int) 
 unsigned int OR uint16_t = 16-bit unsigned, 0 to 65,535
         word OR uint16_t = 16-bit unsigned, 0 to 65,535 (Same as unsigned int)

         long OR  int32_t = 32-bit   signed, -2,147,483,648 to 2,147,483,647.
unsigned long OR uint32_t = 32 bit unsigned, 0 to 4,294,967,295

Is one form better than the other? Please let me know if my understanding is correct. A short explanation will be greatly appreciated!! Thanks in advance! I apologize if this is too basic of a question.

From Arduino.h

typedef unsigned int word;
typedef uint8_t boolean;
typedef uint8_t byte;

so its the other way round, “byte” is equivalent to “uint8_t” (for sketches and
for libraries that elect to #include <Arduino.h>)

You’ve missed char and unsigned char, both 8 bit, and double and float,
both 32 bit on Arduino (except for Due which has true 64 bit doubles).

uint8_t, is a standard name that is defined in the stdint.h header file for an unsigned integer that is at least 8 bits in size, while byte is defined in the Arduino headers. Both uint8_t and byte ultimately are defined as the unsigned char data type. If you ever decide to move your code to a different development platform that doesn't use the Arduino libraries, uint8_t is more likely to be defined than byte.

Fundamentally, the are only a few basic integer types:

  • bool -- A type to hold true/false.
  • char -- A type meant to hold native characters. On some systems it can hold values -128 to 127, while on others it can hold 0 to 255. On some historic systems, characters were not just 8 bits, so char could hold a different range of values. Generally these days, you will only see chars that are 8 bits. Also, I'm going to ignore some of the rules for unsigned types that only apply to historic machines that used one's complement or signed magnitude for represeting integers. On AVR chips, char holds -128..127, while on ARM chips, char holds 0..255.
  • unsigned char -- an unsigned type that is the same size as char.
  • signed char -- a signed type that is the same size as char.
  • short -- a signed type that holds at least -32768..32787. Also can be written short int or int short if desired. On both AVR and ARM systems, short is 16-bits.
  • unsigned short -- an unsigned type the same size as short. Also can be written unsigned short int, short unsigned int, or short unsigned.
  • int -- a signed type that holds at least -32768..32767. Also can be written int signed or signed int. On AVR systems int is 16-bits (same size as short), while on ARM systems, int is 32-bits (same size as long). An int must be at least as big as a short.
  • unsigned int -- an unsigned type that is the same size as int. Also can be written as int unsigned.
  • long -- a signed type that holds at least -2147483648..2147483647. Also can be written as long int, signed long int, etc. On both AVR and ARM, long is 32-bits. Note, since AVR's are 8-bits, using long arithmetic will generate more instructions, while on ARM systems, 32-bits is usually a single instruction. A long must be at least as big as an int.
  • unsigned long -- an unsigned type that is the same size as long.
  • long long -- A signed type that holds at least -9223372036854775808..9223372036854775808. Both AVR and ARM systems use 64-bits for long long. On AVR systems, long long can lead to serious code bloat, and particularly multiply/divide take many cycles to execute. A long long must be at least as big as a long.
  • unsigned long long -- An unsigned type the same size as long long.

Thanks Michael for taking time to explain this to me! If I understand you correctly, I should start using 'uint8_t', etc, forms for portability. I have been using byte, int, etc. because is faster to type. I guess this 'old dog' needs to learn some new tricks!

Again, thanks for your explanation!

Chief_Paul: Thanks Michael for taking time to explain this to me! If I understand you correctly, I should start using 'uint8_t', etc, forms for portability. I have been using byte, int, etc. because is faster to type. I guess this 'old dog' needs to learn some new tricks!

Again, thanks for your explanation!

My preference is uint8_t so that you explicitly say what the size is and signedness, or just use unsigned char instead of using the other types. Others have different opinions.

I do think, the whole C typing system, when it comes to int is broken. For various reasons, in 64-bit land, it was chosen to keep int at 32-bits, rather than make it 64-bits (which would have been more natural). It is one of these trade off type situations where either choice you made impacted existing code.

Chief_Paul: If I understand you correctly, I should start using 'uint8_t', etc, forms for portability. I have been using byte, int, etc. because is faster to type.

Not quite... The C standard only defines char, int, short, long... and unsigned variants of course. Both byte and boolean are just conveniences. The later C standards added the intxx_t and uintxx_t types to solve a long-standing problem of not knowing for sure how big an int actually is. However, not all C compilers will have these types defined, so technically they're not good for best portability.

IMO, any C compiler that doesn't support intxx_t and friends can go compile /dev/null though. The enormous advantage of knowing how many bits wide your ints are is worth sacrificing a little backward compatibility. If you're really worried about it, you can always do an #ifndef test and define those types yourself. For Arduino sketches, support for these types is a given, but in regular C code, it's up to you to plan accordingly.

As to which is "better", that's up to you and the application. I use uint8_t a LOT for small numbers because I don't want to allocate >8 bits for something that will never be larger than 255. I use char for single (or arrays of) characters of text, and I often use byte to denote arbitrary bytes of data. They all refer to the same thing -- one byte of memory -- but the definition helps illuminate their intent, along with a well-chosen variable name of course.

MarkT: From Arduino.h

typedef unsigned int word;
typedef uint8_t boolean;
typedef uint8_t byte;

And it is salutary to remember that a "boolean" actually has eight bits, not one. :astonished:

Not quite... The C standard only defines char, int, short, long... and unsigned variants of course. Both byte and boolean are just conveniences

But unless you explicitly use a .c file, your code is compiled as C++, which includes bool. I commonly mix the type alias's like you mentioned, however if compatibility is a concern byte and especially boolean (not bool) should be avoided all together (for more than just compatibility).

Paul__B: And it is salutary to remember that a "boolean" actually has eight bits, not one. :astonished:

A bool is 8 bits also, the difference is boolean conventions do not apply to the boolean type. I'm trying to get this fixed here: (pull request | issue)

What would you expect this to print: Serial.println( true == (boolean)57 ? "true" : "false" );

Well it prints false, whereas using bool, you get the expected value of true. Serial.println( true == (bool)57 ? "true" : "false" );