首页 > 解决方案 > Optimizing structure of arrays in C

问题描述

I'm trying to write up a structure to store coordinates for various graphic elements as well as some set and get functions. The values of the coordinates will change to accommodate the alternative fonts whenever the user changes their language selection. However the number of coordinate sets I need (integers I need to maintain) will be constant and is defined as numElements in the structure.

Please consider the following code. This is a sort of library I've made to support the other graphic drawing functions I use. It works as-is, but I'm in embedded systems so memory is at a premium and I would like to make a change but don't know how to make it work.

#define MAX_ELEM 20

typedef enum{TITLE, HEADER, TEXT, LAST_GRAPHIC_ITEM}GRAPHIC_ITEMS;

typedef struct
{
     uint16_t x;
     uint16_t y;
}COORDINATES;

typedef struct
{
    const uint16_t numElements;
    COORDINATES coord[MAX_ELEM];
}GRAPHIC_COORD;

GRAPHIC_COORD graphicItemCoordinates[LAST_GRAPHIC_ITEM] =
{
    { // TITLE
        1,
    },
    { // HEADER
        2,
    },
    { // TEXT
        14,
    }
};

void SetXCoord(GRAPHIC_ITEMS item, uint16_t new_x, uint16_t elemNum);
void SetYCoord(GRAPHIC_ITEMS item, uint16_t new_y, uint16_t elemNum);
uint16_t GetXCoord(GRAPHIC_ITEMS item, uint16_t elemNum);
uint16_t GetYCoord(GRAPHIC_ITEMS item, uint16_t elemNum);

I'd like to optimize the size of the COORDINATES arrays so they hold just enough information for the required number of elements. So the change to the code I have in mind would be like so:

typedef struct
{
    const uint16_t numElements;
    COORDINATES coord[numElements]; // changed size value here from MAX_ELEM to numElements
}GRAPHIC_COORD;

However when I make this change and try to compile I get errors saying that numElements is undefined. It seems to me that I shouldn't have to define the numElements in the type declaration and that defining it in the instance of the structure I declare should be enough. But obviously that's not the case. I assume I need to do something with dynamic memory allocation, but I haven't breached that subject deeply yet and am not sure what I need to do.

Note: the code I have provided above is much simplified. Part of my goal in doing it this way is to avoid having a large structure I need to maintain. In the actual application there are more like 30 graphic items each with 10-20 elements. If I were to do it as @Clifford or @Bastien suggest, then I will have 1000+ lines of structure/header file.

Note: numElements is only there to size the coord array within the GRAPHIC_COORD structure. If I were to do it as @Clifford or @Bastien suggest, I could omit the numElements member entirely which would simplify the structure. But the structure will still be absurdly long.

I'm working in Keil's uVision5 IDE with an STM32F103 chip on a custom board if that's of any importance.

标签: cmemory-managementembedded

解决方案


Dynamic memory allocation and variable length-arrays are generally ill-advised on memory constrained devices since when an allocation fails, you have to be able to deal with it, and in this case there is probably nothing useful you could do.

If it is the case that for for each element, the coordinate array length is fixed, then dynamic allocation is entirely unnecessary in any case. Consider:

typedef struct
{
    const uint16_t numElements;
    COORDINATES* coord ;  // <<< pointer to coordinates array
}GRAPHIC_COORD;

Then:

COORDINATES title_coord[]  = { /*constant initialiser coords*/ } ;
COORDINATES header_coord[] = { /*constant initialiser coords*/ } ;
COORDINATES text_coord[]   = { /*constant initialiser coords*/ } ;

Then finally:

GRAPHIC_COORD graphicItemCoordinates[] =
{
    { sizeof(title_coord)  / sizeof(GRAPHIC_COORD), title_coord  },
    { sizeof(header_coord) / sizeof(GRAPHIC_COORD), header_coord },
    { sizeof(text_coord)   / sizeof(GRAPHIC_COORD), text_coord   }
};

#define LAST_GRAPHIC_ITEM (sizeof(graphicItemCoordinates) / sizeof(*graphicItemCoordinates))

Each COORDINATES array can then be of a different (albeit individually fixed) length.

You can simplify initialisers using macros perhaps - for example:

#define ITEM_COORDS_INIT( item ) {sizeof(item) / sizeof(GRAPHIC_COORD), (item)}

Then:

GRAPHIC_COORD graphicItemCoordinates[] =
{
    ITEM_COORDS_INIT(title_coord),
    ITEM_COORDS_INIT(header_coord),
    ITEM_COORDS_INIT(text_coord)
};

Note that it is often better to let the size of arrays be determined by their initialiser rather then placing a value in the []. You can create a symbol for the number of elements by creating a macro as I have above for LAST_GRAPHIC_ITEM.

Note also that if you intend that these structures should be placed in ROM - which you have rather more of than RAM typically, then you should declare them static const:

static const COORDINATES title_coord[]  = { /*constant initialiser coords*/ } ;
...
static const GRAPHIC_COORD graphicItemCoordinates[] = ...

推荐阅读