我正在使用Atmel AVR,并试图从存储在闪存(程序)存储器中的结构访问数据。
结构为:
typedef struct {
uint8_t width;
uint8_t height; // row number 0 to 5
uint8_t images; // how many frames does this bitmap have
uint8_t data[]; // the actual pixel data
} bitmap_t;
数据为:
__flash static const bitmap_t bmp_stereo2 = {14,1,1,{126,129,60,66,24,36,60,60,36,24,66,60,129,126}};
我正在尝试使用(显示部分代码)访问数据...
void lcd_bitmap2(const bitmap_t *bit, uint8_t id, uint8_t posx, uint8_t posy) {
uint8_t x;
uint8_t y;
const uint8_t bw = pgm_read_byte(&bit->width); // this works -- I can print out to serial
const uint8_t bh = pgm_read_byte(&bit->height); //this also works -- I can print out to serial
// this doesn't work
const uint8_t *data = pgm_read_word(&bit->data); // I get: - initialization makes pointer from integer without a cast [enabled by default]
const uint8_t *data = (uint8_t *)pgm_read_word(&bit->data); // this also doen't work (no warning, but wrong data read out)
//rest of function...
因此,我可以访问width,height和image变量,但不能访问结构的数据部分。 如果我不将其存储在闪存中,则所有方法都可以正常工作-这是我的检索工作,只是结构的数据数组部分有问题(宽度,高度和图像都可以读取)
答案 0 :(得分:2)
TL; DR :您无法在标准C中安全地做到这一点。
给出此定义...
typedef struct { uint8_t width; uint8_t height; // row number 0 to 5 uint8_t images; // how many frames does this bitmap have uint8_t data[]; // the actual pixel data } bitmap_t;
... bitmap_t.data
是“灵活数组成员”:
作为特殊情况,结构中的最后一个元素包含多个 命名成员的数组类型可能不完整;这叫做 灵活的数组成员。 在大多数情况下,灵活数组成员 被忽略 。特别是,结构的大小就像 柔性数组成员被省略,除了它可能有更多 比省略所暗示的尾随填充。但是,当
.
(或->
)运算符具有一个左操作数,该操作数是一个指向具有灵活数组成员的结构的指针(指向该成员的右操作数), 它的行为就像该成员被最长的数组替换 (具有相同的元素类型)不会使结构变大 比被访问的对象[...]。
[C2011 6.7.2.1/18;强调]
标准中的示例阐明,正如您的代码尝试执行的那样,为灵活的数组成员提供初始化程序是无效的。此外,没有理由期望为具有静态或自动存储期限的对象(例如您的)的灵活数组成员的内容分配任何空间。灵活的数组成员仅在与动态分配的对象结合使用时才有用,除非在实现方面超出了标准要求的实现中除外。
因此,访问静态分配的对象的灵活数组成员会产生未定义的行为。这与存储的实际位置没有直接关系,尽管在闪存中UB的显示方式可能有所不同。 (毕竟,行为是 undefined 。)要以尝试使用的方式使用bitmap_t
类型的对象,则需要修改该类型,以使其{{1} }成员具有完整的类型(即固定尺寸)。
答案 1 :(得分:1)
(独立于约翰·博林格回答)
您在此处错误地将pgm_read_word
用作->data
的{{1}}成员,即使使用bitmap_t
定义了结构,代码也无法正常工作。
让data[14]
是闪存中的字节大小的对象。
您可以使用XXX
阅读它
这样一来,人们可以通过以下方式读取位置pgm_read_byte(& XXX)
(pixel_number
对象)处的像素:
data[pixel_number]
直接结构访问也得到相同的结果
// just address of first byte in data[]
const uint8_t *data = bit->data;
uint8_t pixel = pgm_read_byte(&data[pixel_number]);
最后,现代的avr-gcc编译器(关键字为uint8_t pixel = pgm_read_byte(&bit->data[pixel_number]);
的编译器)可以自动执行此操作。
只需使用__flash
修饰符声明所有指针(__flash
可以省略):
<avr/pgmspace.h>
p.s。根据我的经验,avr-gcc会以您期望的方式处理不完整的数组初始化(也在Flash中),并且无法访问。