为什么使用指针访问未对齐的 uint16 数组时会抛出“对齐异常”,而使用下标访问数组时不会抛出“对齐异常”?

问题描述 投票:0回答:2

我将使用以下代码来解释我的问题:

typedef struct __attribute__((packed))
{
    uint8_t  var;
    uint16_t array[3];
}struct_t;

uint8_t frame[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD};

volatile struct_t *ptr = NULL;
volatile uint16_t *u16ptr = NULL;
volatile uint16_t a;
volatile uint16_t b;

//******************************************************************************************
int main(int argc, char** argv)
{
    ptr = (struct_t*)frame;
    u16ptr = ptr->array;

    a = ptr->array[0]; // <---- NO Memory Access Exception here 
    b = *u16ptr;       // <---- Memory Access Exception here!
}

在那段代码中,我强制

array
进行内存未对齐以产生内存访问异常。

所以我的问题是,为什么通过指针访问未对齐的 uint16 数组时会抛出“对齐异常”,而使用数组下标访问数组时却不会抛出“对齐异常”?

到目前为止,我找到的资源都没有解释为什么如果

a = ptr->array[0];
b = *u16ptr;
两行都访问完全相同的未对齐内存。

有人可以解释一下或指出正确的方向吗?

arrays pointers exception memory-alignment memory-access
2个回答
2
投票

尝试从未对齐的地址读取 16 位值会陷入某些架构中。尽管标准将

x[y]
的行为定义为与
*((x)+(y))
等效,这会导致数组被分解为指针,然后从该地址获取值(如果地址未对齐则捕获),但 clang 和gcc 将
structOrUnion.memberArray[index]
视为左值,用于标识
structOrUnion
成员的一部分,而不是将
structOrUnion.memberArray
分解为指针,忘记指针来自何处,然后对其进行索引。

如果

[]
运算符直接用于未对齐数组(“打包”结构的成员)或联合体的任何 aarray 类型成员,clang 和 gcc 将执行访问存储所需的操作问题。但是,如果这样的数组被分解为指针,则 clang 和 gcc 都不会可靠地允许使用它来访问其类型的对象。


0
投票

如果结构或联合的成员被打包,因此没有其自然对齐, 那么要访问该成员,必须使用包含该成员的结构或联合。你 不得将此类打包成员的地址用作指针,因为该指针可能 不对齐。即使未对齐的访问被取消,取消引用这样的指针也可能是不安全的。 受目标支持,因为某些指令总是需要字对齐地址。

© www.soinside.com 2019 - 2024. All rights reserved.