Explaination of struct

Can anyone explain what is a struct? Thanks.

In C, it's a collection of variables grouped together for convenience.

In C++ it's the same as a class except that the struct members default to public.

Let's start from the idea of an array. An array is a unit that can hold several items of data, but they must be all of the same datatype - for example int myArray[4] creates an array that holds 4 integers

A struct is a means to create a unit that contains several items of data that can have different datatypes. It also has the advantage that the items in the struct have names rather than the index numbers that you use with an array.

Of course you can have an array of structs in just the same way as you can have an array of arrays

Have a look at this example I posted a short time ago.

...R

Here is a link to a tutorial about Structures; the whole site is useful

wildbill:
In C, it's a collection of variables grouped together for convenience.

In C++ it's the same as a class except that the struct members default to public.

The C version seems to end up being smaller as well.

I have a class I use a lot of: colorObj. It only holds 3 bytes RGB. But it takes up 8 bytes of RAM. I also have a struct RGBpack that also holds 3 bytes RGB and it takes up.. 3 bytes of RAM. (Hence its existence)

-jim lee

jimLee:
I have a class I use a lot of: colorObj. It only holds 3 bytes RGB. But it takes up 8 bytes of RAM. I also have a struct RGBpack that also holds 3 bytes RGB and it takes up.. 3 bytes of RAM. (Hence its existence)

On which device? Sounds very odd that 3 bytes in a struct should require 8 bytes of memory, 6 bytes would have been understandable, but 8..? Have you tried to pack the struct holding the 3 bytes?

struct __attribute__((__packed__)) colorObj {
  uint8_t red, green, blue;
}

jimLee:
The C version seems to end up being smaller as well.

That's not true. Struct padding is compiler-specific, but if you have a toolchain with both a C compiler and a C++ compiler (e.g. GCC), both compilers will use the exact same struct layout.

If this weren't the case, almost all software that relies on C/C++ interoperability would come to a grinding halt.
Structs in the Linux headers, for example: They are widely used in C and C++ code, and the kernel itself is mostly written in C. If a user constructs a struct in his C++ application and passes a pointer to that struct to a Linux function written in C, the layout has to be the same in both languages.

Things change when you add virtual methods to your "struct", but then it's no longer a valid C struct.

Pieter

PieterP:
That's not true.

As is shown by a simple example:

class myClass {
  public:
    uint8_t r;
    uint8_t b;
    uint8_t g;
};

struct myStruct {
  public:
    uint8_t r;
    uint8_t b;
    uint8_t g;
};

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.print("Size of class = ");
  Serial.println(sizeof(myClass));
  Serial.print("Size of struct = ");
  Serial.println(sizeof(myStruct));
}

void loop() {
}

Serial outputs for both an Uno (AVR) and Teensy 3.2 (32-bit ARM) are the same:

Size of class = 3
Size of struct = 3

Taking your code.

Swapping in my stuff.

#include "colorObj.h"



void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.print("Size of class = ");
  Serial.println(sizeof(colorObj));
  Serial.print("Size of struct = ");
  Serial.println(sizeof(RGBpack));
}

void loop() {
}

Gives the result :

Size of class = 8
Size of struct = 3

Here's my colorObj.h file:

#ifndef colorObj_h
#define colorObj_h

#include "mapper.h"
#include "multiMap.h"
// uncomment for debug stuff.
//#define PRINT_COLOR

// Red,Grn,blu
#define LC_BLACK  0,  0,  0
#define LC_CHARCOAL 50, 50, 50
#define LC_DARK_GREY 140,140,140
#define LC_GREY 185,185,185 
#define LC_LIGHT_GREY 250,250,250
#define LC_WHITE   255,255,255

#define LC_RED     255,  0,  0
#define LC_PINK   255,130,208

#define LC_GREEN    0,255,  0
#define LC_DARK_GREEN    0, 30,  0
#define LC_OLIVE   30, 30,  1

#define LC_BLUE      0,  0,255
#define LC_LIGHT_BLUE 164,205,255
#define LC_NAVY    0,  0, 30

#define LC_PURPLE   140,  0,255
#define LC_LAVENDER    218,151,255
#define LC_ORANGE   255,128,  0

#define LC_CYAN      0,255,255
#define LC_MAGENTA 255,  0,255
#define LC_YELLOW   255,255,  0


// If you want a LOT of colorObj saved, like a bitmap?
// The RGBpack is 3 bytes per pixel as opposed to 8 for
// the color object itself. Same color information.
struct RGBpack {
  uint8_t r;
  uint8_t g;
  uint8_t b;
};


class colorObj {

public:
   colorObj(RGBpack* buff); 
   colorObj(byte inRed, byte inGreen, byte inBlue);
   //colorObj(colorObj* inColor); // Wanted this one, but the compiler mixes it up with color16.
   colorObj(word color16);
   colorObj(void);
  virtual ~colorObj(void);
  
  virtual void setColor(RGBpack* buff);
  virtual void setColor(byte inRed, byte inGreen, byte inBlue);
  virtual void setColor(word color16);
  virtual void setColor(colorObj* inColor); // Why doesn't this one get confused? Who knows?
   word getColor16(void);
    
   byte getRed(void);
   byte getGreen(void);
   byte getBlue(void);
 RGBpack packColor(void);
 
   colorObj mixColors(colorObj* mixinColor,byte mixPercent);  // Create a new color by mixing. (Like the old blend)
   void blend(colorObj* mixinColor,byte mixPercent);      // Just blend with myself. Percent 0% -> 100% of new color.
    
#ifdef PRINT_COLOR    
   void printRGB(void);
#endif

protected :
  byte red;
  byte green;
  byte blue;
};

extern colorObj red;
extern colorObj blue;
extern colorObj white;
extern colorObj black;
extern colorObj green;
extern colorObj cyan;
extern colorObj magenta;
extern colorObj yellow;


// ****** colorMapper ******

class colorMapper {

 public:
 colorMapper(void);
   colorMapper(colorObj* inStart, colorObj* inEnd);
   colorMapper(word startC16,word endC16);
 virtual ~colorMapper(void);
  
   void setColors(colorObj* inStart, colorObj* inEnd);
   colorObj Map(float percent);

#ifdef PRINT_COLOR
   void     printColors(void);
#endif
  
 private :
   mapper* redMapper;
   mapper* greenMapper;
   mapper* blueMapper;
};


// ****** colorMultiMap ******


class colorMultiMap {
    
 public:
 colorMultiMap(void);
 virtual ~colorMultiMap(void);
    
     void addColor(double inX, colorObj* color);  // At some numeric value we resolve to this color.
     void clearMap(void);
     colorObj Map(double inVal);
    
    protected:
     multiMap  redMap;
     multiMap  greenMap;
     multiMap  blueMap;
};


#endif

Teensy 3.2

-jim lee

jimLee:
virtual ~colorObj(void);

PieterP:
Things change when you add virtual methods to your “struct”, but then it’s no longer a valid C struct.

If you use virtual functions, your struct needs some extra data to known which method to call, such as a pointer to the right vtable.

Yeah, it makes sense that the class would need more. It has more to track. I just originally though its RAM footprint would only be the data. That's why I remembered because it was such a surprise to me when the struct version was less than 1/2 the class version with the same color data.

-jim lee

jimLee:
Yeah, it makes sense that the class would need more. It has more to track. I just originally though its RAM footprint would only be the data. That's why I remembered because it was such a surprise to me when the struct version was less than 1/2 the class version with the same color data.

It doesn't matter that it's a class. A class without virtual members would have the exact same layout as the corresponding struct. As mentioned before, "struct" and "class" are almost exactly the same, the only difference is that the members of a struct are public by default, and its default inheritance is public as well, whereas for a class these are both private.

You can replace "class colorObj" in your code with "struct colorObj", and the results would be identical.

class colorObj {
   ...
   RGBpack packColor(void);
   ...

protected :
  byte red;
  byte green;
  byte blue;
}

you have 3bytes + 3bytes + 2bytes (for vtable pointer) = 8bytes

arduino_new:

class colorObj {

...
  RGBpack packColor(void);
  ...

protected :
 byte red;
 byte green;
 byte blue;
}



you have 3bytes + 3bytes + 2bytes (for vtable pointer) = 8bytes

I don't think that's correct. packColor is a member function, not a member variable.

struct S {
  virtual ~S() = default;
  
  byte red;
  byte green;
  byte blue;
};

static_assert(sizeof(S) == 8, "Error");
static_assert(offsetof(S, red) == 4, "Error");
static_assert(offsetof(S, green) == 5, "Error");
static_assert(offsetof(S, blue) == 6, "Error");

The first 4 bytes of S are a vtable pointer (pointers on 32-bit ARM are 32 bits), then bytes 4, 5 and 6 are the RGB values, and byte 7 is a padding byte to ensure that the vtable pointer is word-aligned when creating arrays of S.

Note: this is the layout when using the GCC ARM compiler of the Teensy toolchain, there's no guarantee that this layout will stay the same across compilers.

PieterP:
The first 4 bytes of S are a vtable pointer (pointers on 32-bit ARM are 32 bits), then bytes 4, 5 and 6 are the RGB values, and byte 7 is a padding byte to ensure that the vtable pointer is word-aligned when creating arrays of S.

Note: this is the layout when using the GCC ARM compiler of the Teensy toolchain, there's no guarantee that this layout will stay the same across compilers.

But but.. My RGBpack comes back as 3 bytes. Where's the padding on that?

-jim lee

It only contains bytes, no pointers, so it doesn’t have any alignment requirements.

If you have a virtual method, each object contains a pointer, which has to be aligned on word boundaries (4 bytes).

Ah! That finally answers the question: Why you allocate the same number of bytes for a cstring as strlen()+1?" I always wondered how the padding went in there. Now I know, there was none.

Thanks for that!

-jim lee

Thank you for all your replies especially Perry and Robin :wink:

jimLee:
I have a class I use a lot of: colorObj. It only holds 3 bytes RGB. But it takes up 8 bytes of RAM. I also have a struct RGBpack that also holds 3 bytes RGB and it takes up.. 3 bytes of RAM. (Hence its existence)

If the class is defined inside another class, it will also have a pointer to the outer class unless it is static. If the class has virtual methods, it will have a pointer to its function table. So - yeah: a C++ class has extra stuff that the compiler hides from you.

PaulMurrayCbr:
If the class is defined inside another class, it will also have a pointer to the outer class unless it is static. If the class has virtual methods, it will have a pointer to its function table. So - yeah: a C++ class has extra stuff that the compiler hides from you.

But it doesn't have a class defined inside another class. Its just the one class and outside of that is the struct. The class has methods to export to the struct or create from the struct. Or was it set its color from the struct?.. Either way.

-jim lee