【问题标题】:Copy larger size variable unit16 to equivalent smaller array size unint8 by casting in c通过在 c 中强制转换,将较大大小的变量单元 16 复制到等效的较小数组大小 uint8
【发布时间】:2022-01-17 12:44:04
【问题描述】:

我有变量uint16 value,我想将它复制到uint8_t buffer[3]。有没有可能做(小端):

*buffer=*(uint8_t *)&value;

代替:

buffer[0] = highByte(value);
buffer[1] = lowByte(value);

由于此替换导致 stm32f7 I2C 无法正常工作。有没有正确的选角?

【问题讨论】:

  • 您可以使用uint16uint8 array 成员定义union
  • @i486 是否可以轻松将value 的内容传递给buffer,如何?工会概念是我所不知道的,我必须研究它,ty。如果您认为有帮助,请随时回答。

标签: c stm32 i2c mcu


【解决方案1】:

STM32 是 little endian,因此您首先获得最低有效字节:

uint8_t* ptr = (uint8_t*)&value;
uint8_t low  = ptr[0];
uint8_t high = ptr[1];

像这样进行强制转换和取消引用只适用于字符类型。上面的代码假设uint8_t是一个字符类型,很可能是这样的(在gcc和其他主流编译器上)。

更多信息见What is CPU endianness?

编辑:

如果您只是想将一个 16 位数字复制到一个字节数组中,正确的解决方案是:

memcpy(buffer, &value, sizeof(uint16_t)).

我们不能执行*(uint16_t*) buffer=value;,因为它会调用未定义的行为。 buffer 可能未对齐,它也是 strict aliasing violation。这就是为什么我在上面强调“这仅适用于字符类型”。

【讨论】:

  • 我们不能改变存储在buffer的指针??然后我不得不在它的指针存储中替换我的值。
  • @mohammadsdtmnd 我添加了一次复制整个 16 位值的解决方案。
【解决方案2】:

假设解决了@Lundin 提到的与高端和低端相关的问题,并且取消引用 uint8_t 仅授予对它的第一个数组元素的访问权限,我只通过一次强制转换就达到了这个解决方案:

*(uint16_t*) buffer=value;

这是简化版:

uint16_t* p;
p= buffer;
*p=value;

【讨论】:

  • 不,这是错误的,这是一个严格的别名冲突,可能会导致访问不对齐。如果您希望进行 16 位复制,您应该使用 memcpy(buffer, &value, sizeof(uint16_t))
  • @Lundin 我知道一些对齐访问但是,如果代码做我想做的事情有什么问题?虽然我读过未对齐的访问速度较慢,并且在每个内存寄存器中都不允许。你能解释一下是怎么回事吗?符合。这段代码会发生什么?我读过 8 16 32 位对齐访问是对齐的,我违反了这个吗?
  • 取决于目标系统。一些系统没有对齐,其他系统会为未对齐的访问产生较慢的代码,而其他系统会产生导致程序崩溃的硬件异常/陷阱指令。但未对齐的访问并不是唯一的问题——正如我在回答中提到的,这也是所谓的“严格别名违规”,这意味着代码具有未定义的行为,因此结果可能会变得意外且不确定。
  • @Lundin 我认为这是安全的。由于buffer 的数组定义分配的内存,8 位到 16 位的转换将起作用,但可能转换为 32 位可能会导致问题。我的意思是你必须注意不要超过buffer的存储限制。
【解决方案3】:

有没有正确的投射?

没有

*buffer = <expression>

将永远且只写信给buffer[0],而永远不会写给buffer[1]

您可以做的一件事 - 但我强烈建议您不要这样做是这样的:

uint16_t *ptr = (uint16_t*)buffer; // Ugh, danger
*ptr = value;

如果你在不同字节序的机器上运行它会出现问题。而且很有可能还有其他理由不这样做。一旦你问自己是否可以通过强制转换解决问题,你真的应该退后一步。以下是我写过的两个答案:

https://stackoverflow.com/a/62563330/6699433

https://stackoverflow.com/a/63773195/6699433

【讨论】:

  • 也许我可以通过洞地址buffer=(uint8_t *)&amp;value;,但仍然存在其他问题:正如@Lundin 所说。低字节将首先保存。
  • @mohammadsdtmnd 您目前的方法有什么问题?需要多一行吗?
  • value 由 DMA 不断地从 ADC 刷新,我想将它传递给使用 8 位缓冲区的 I2C,我不想在 I2C 中进行 CPU 交互以及它相关的 DMA 传输。然后我在一行中开始 I2c 传输并将值传递给它的 uint8_t buffer
  • @mohammadsdtmnd 在这种情况下,请记住 ADC 可能会以自己的特殊方式对齐位。除了字节序之外,这些位还可以是“左对齐”或“右对齐”。 ADC 外设中通常有一个设置,您可以在其中选择您喜欢的格式。
  • @mohammadsdtmnd 是否担心value会在操作中途发生变化?
猜你喜欢
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 2018-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-18
相关资源
最近更新 更多