Math using unsigned char for byte copying.

Being new to all this, I’m not sure if I should use unsigned char when dealing in the view of bytes. I’ve started to write a little function and kept using unsigned char for the math since it seems O.K., but I’m not sure that I should use that or uint8_t (or if it even matters). No matter what uint8_t might be defined as, I’m just worried about the size of bytes right? I’m a little confused because I keep reading things like “unsigned char” is always a byte, but then I read in other places don’t use it in math. However, I’m not sure about any of it when viewing things from a byte only perspective regardless of how many bits are in a byte.

template <typename B, typename T>
inline bool byte_copy(B& buf, const T& var, const unsigned char iters)
{
  if ( iters < 1 ) return false;
  const unsigned char*  varp = (unsigned char*)&var;
  unsigned char*        bufp = (unsigned char*)&buf;
  for (unsigned char i = 0; i < iters; ++i) // <-- is this O.K. ?
    bufp[i] = varp[i];
  return true;
}

uint8_t is the same as unsigned char on most, if not all modern computers, but there is no guarantee that type "char" is 8 bits.

Edit: Just give it to me straight chief, use uint8_t or unsigned char?

Right. Pretending I'm on a system that uses a 7bit byte and supports uint8_t, would uint8_t still be 8bits? I'm not sure such a system exists but, I'm sure I'm making too big of a deal out of this :-). I'm just wondering if it's possible that if I have a 4byte float which is 28bits, that using a 8/16/32bit type for the arithmetic might cause problems somewhere at some point with the +1/2/4bit difference.

I'm just worried about the size of bytes right?

From Arduino.h

typedef uint8_t byte;

Here's more than you want to know about "byte"

Type "char" is guaranteed to be at least 8 bits. From the link posted by cattledog:

The C standard requires that the integral data type unsigned char must hold at least 256 different values, and is represented by at least eight bits (clause 5.2.4.2.1). Various implementations of C and C++ reserve 8, 9, 16, 32, or 36 bits for the storage of a byte. In addition, the C and C++ standards require that there are no gaps between two bytes. This means every bit in memory is part of a byte.

a 4byte float which is 28bits

I'm pretty sure that you will never encounter such a thing.

What's the difference between your byte_copy function and the standard memcpy function?

On pretty much any modern system you can expect a char to be 8 bits. The C++ standard guarantees that char and unsigned char are at least 8 bits wide, with a range of at least −2⁷ to 2⁷ − 1 (inclusive) and 0 to 2⁸ − 1 (inclusive) respectively, see [basic.fundamental] in the standard.

For uint8_t, the C++ standard refers back to the C standard:

7.20.1.1 Exact-width integer types

  1. The typedef name intN_t designates a signed integer type with width N, no padding
    bits, and a two’s complement representation. Thus, int8_t denotes such a signed
    integer type with a width of exactly 8 bits.

  2. The typedef name uintN_t designates an unsigned integer type with width N and no
    padding bits. Thus, uint24_t denotes such an unsigned integer type with a width of
    exactly 24 bits.

  3. These types are optional. However, if an implementation provides integer types with
    widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a
    two’s complement representation, it shall define the corresponding typedef names.

spokes:
I'm a little confused because I keep reading things like "unsigned char" is always a byte, but then I read in other places don't use it in math.

If you need an integer of 8 bits (an integer you want to perform math on), use int8_t/uint8_t, not char/unsigned char. Even though uint8_t == unsigned char on most platforms, it's about showing your intent.

If you need raw memory access, don't use uint8_t, use std::byte in C++20, or char/unsigned char before C++20. See also [basic.lval], 7.2.1.11

basic.lval, 7.2.1.11:
If a program attempts to access (3.1) the stored value of an object through a glvalue whose type is not
similar (7.3.5) to one of the following types the behavior is undefined: 51
(11.1) — the dynamic type of the object,
(11.2) — a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
(11.3) — a char, unsigned char, or std::byte type.

Pieter

Roger that people, thank you!