你能以与字节无关的方式获得指向低位字节的指针吗?

sap*_*api 5 c embedded endianness

我有一个16位变量data,即:

volatile uint16_t data;
Run Code Online (Sandbox Code Playgroud)

我需要根据外部传感器上两个8位寄存器的内容填充该值.这些通过I2C/TWI访问.

我的TWI例程是async*,并且有签名:

bool twi_read_register(uint8_t sla, uint8_t reg, uint8_t *data, void (*callback)(void));
Run Code Online (Sandbox Code Playgroud)

这读取的价值regsla*data,然后调用callback().

如果我知道它uint16_t被安排在记忆中,比如说MSB LSB,那么我可以这样做:

twi_read_register(SLA, REG_MSB, (uint8_t *)&data, NULL);
twi_read_register(SLA, REG_LSB, (uint8_t *)&data + 1, NULL);
Run Code Online (Sandbox Code Playgroud)

但是,我不喜欢将endian依赖到我的代码中.有没有办法以与endian无关的方式实现这一目标?

(旁注:我目前的实际解决方法是使用结构,即:

typedef struct {
    uint8_t msb;
    uint8_t lsb;
} SensorReading;
Run Code Online (Sandbox Code Playgroud)

但我很好奇,如果我能用一个简单的方法做到这一点uint16_t)

编辑

(*async我的意思是分裂阶段,*data即将在未来的某个时间点设置,此时callback如果请求,将通过函数通知被调用者)

Bry*_*ier 4

下面的方法不行吗?

uint8_t v1, v2;
twi_read_register(SLA, REG_MSB, &v1, NULL);
twi_read_register(SLA, REG_LSB, &v2, NULL);
data = ((uint16_t)v1<<8)|v2;
Run Code Online (Sandbox Code Playgroud)

或者data太不稳定以至于twi_read_register需要写它。在这种情况下,我认为您陷入了依赖于字节序的代码。

正如您在下面指出的那样,它data确实是不稳定的,因为还有另一个设备正在读取它。因此,在字节顺序可能不同的两个设备之间建立内存映射连接。这意味着您陷入了依赖于字节序的代码。

您提到该结构是一种解决方法,但这是处理此问题的标准方法。

#ifdef BIGENDIAN
typedef struct
{       uint8_t  msb, lsb;
} uint16_as_uint8_t;
#else
typedef struct
{       uint8_t  lsb, msb;
} uint16_as_uint8_t;
#endif
Run Code Online (Sandbox Code Playgroud)

最重要的是你可以放一个union

union
{       uint16_as_uint8_t  as8;
        uint16_t           as16;
};
Run Code Online (Sandbox Code Playgroud)

请注意,后者违反了 C89 标准,因为您明确意图写入一个字段union并从另一个字段读取,这会导致未指定的值。从 C99 开始(幸运的是)这是支持的。在 C89 中,可以使用指针转换来(char*)以可移植的方式完成此操作。

请注意,上面的内容似乎以可移植的方式隐藏了字节顺序,结构打包也可能因目标而异,并且在某些目标上仍然可能会中断。对于上面的例子来说,这不太可能,但是周围有一些奇怪的目标。我想说的是,在这个设备级别上进行可移植编程可能是不可能的,最好接受这一点并努力将所有细节隐藏在紧凑的目标接口中,因此更改目标的一个头文件就足以支持它。代码的其余部分看起来可以与目标无关。