简体   繁体   中英

Pass a pointer to an array of pointers to a C struct

I have a struct:

typedef struct Image {
    byte height;
    byte width;
    byte data[];
} Image;

and I create 2 images:

static const __flash Image GRID = {
    .width = 16,
    .height = 8,
    .data = {
        0x10, 0x10, 0x28, 0x28, 0x44, 0x44, 0x82, 0x82, ...
    }    
};

static const __flash Image HOUSE1 = {
    .width = 24,
    .height = 24,
    .data = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ...
    }
};

Then I create an array of pointers to the images:

static const __flash Image *IMAGES[] = {
    &GRID,
    &HOUSE1,
};

I can draw an image using my draw_image() function:

void main(void)
{
    draw_image(IMAGES[0], 16, 16);
}

I have a map:

typedef struct Map {
    word cols;
    word rows;
    byte tiles[];
} Map;

static const __flash Map level_1 = {
    .cols = 16,
    .rows = 8,
    .tiles = {
        0,0,1,0,...

the .tiles field is a list of indexes into the IMAGES array. I'm doing it this way because my engine doesn't know what images are available without being told:

void draw_map(const Map __memx *map, const Image __memx *tileset[]);
{
    ...
    draw_image(tileset[map->tiles[index]], x, y);
    ...
}

called thusly:

void main(void)
{
    draw_map(&level_1, &IMAGES[0]);
}

The compiler does not like this and gives me incompatible pointer type warnings. The map is not drawn:

note: expected 
‘const __memx Image ** {aka const __memx struct Image **}’
but argument is of type 
‘const __flash Image ** {aka const __flash struct Image **}’

I did try removing the [] from the draw_map() declaration:

void draw_map(const Map __memx *map, const __memx Image *tileset);

but that gave me an error in calling the draw_image() call:

error: incompatible type for argument 1 of ‘draw_image’
             draw_image(tileset[0], c*8+(64 - r*8), r*8);
                        ^
tile-engine.c:63:6: note: expected 
‘const __memx Image * {aka const __memx struct Image *}’ but argument is of type 
‘Image {aka const __memx struct Image}’

Where am I going wrong?

void draw_image(const Image __memx *image, int x, int y)
{
    byte rows = image->height>>3;
    byte cols = image->width>>3;

    for(byte r=0 ; r<rows ; r++)
    {
        for(byte c=0 ; c<cols ; c++)
        {
            draw_tile(&image->data[(r*cols+c)*8], &image->data[(r*cols+c)*8], x+(c*8), y+(r*8));
        }
    }
}

The issue seems to be exactly what the compiler has identified: you are passing a __flash pointer to a function that requires a __memx pointer.

If you change the signature of draw_map to

void draw_map(const Map __memx *map, const Image __flash *tileset[])

then it works fine.

Okay, so why is this necessary, when the compiler is okay with accepting a __flash pointer for the first argument, which is also defined as __memx ?

The reason is that the first pointer is passed by value while the second is passed by reference ( tileset is a pointer to a __memx pointer).

According to the AVR documentation, __flash pointers are 16-bit pointers into (obviously) flash memory, while __memx pointers are 24-bit pointers that can point to locations either in flash or in static RAM.

It looks like the compiler is smart enough to promote a 16-bit __flash pointer to a 24-bit __memx pointer when passing it by value (similar to how it can promote a 16-bit short to a 32-bit int or long), but it can't cause a 16-bit pointer that's stored in memory (in your IMAGES array) to expand to 24 bits.

Since __memx pointers are slower to use than __flash pointers (apparently the compiler has to check if the actual pointer refers to flash or static RAM and use different instructions for each), if you know your image and map data will always be in flash, just pass __flash pointers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM