在 AVR 的 C 代码中进行这种转换有什么问题?

电器工程 avr C
2022-02-06 01:25:33

我定义了两个变量:

uint8_t a[2];
uint16_t b;

接下来我想a用作类型的变量uint16_t,例如

b = (uint16_t)a;

但这是错误的!我的程序不适用于此类代码。当我替换buint8_t b[2]并使用元素操作时,一切正常。

为什么?

4个回答

a是指向字节数组的指针。如果将其转换为 uint16_t 并将其分配给bb则将包含 SRAM 中数组基址(存储位置)的地址。如果要将数组的两个字节a视为整数,请按照 user14284 的建议使用联合,但请注意联合将代表体系结构的内存字节顺序中的字节数组(在 AVR 中会很少-endian,这意味着字节 0 是最低有效字节)。在代码中编写它的方法是:

union{
  uint8_t a[2];
  uint16_t b;
} x;

x.b[0] = 0x35;
x.b[1] = 0x4A;

// by virtue of the above two assignments
x.a == 0x4A35 // is true

不使用联合的另一种方法是a强制转换为 uint16_t 指针,然后像这样取消引用它:

uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian

如果您使用缓冲区来存储大端数据(例如网络字节顺序),那么您需要进行字节交换才能使用这些技术中的任何一种。一种没有任何分支或临时变量的方法是:

uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];

a[0] == 0x4A; // true
a[1] == 0x35; // true

顺便说一句,这不是 AVR 甚至不是嵌入式问题。为 PC 编写的应用程序级网络代码通常调用称为htonl, htons(主机到网络,32 位和 16 位变体)和ntohl, ntohs(网络到主机,32 和 16 位变体)的函数,其实现取决于目标体系结构是否是否交换字节(假设“在线”传输的字节是多字节字的一部分时总是大端的)。

如果您的意图是将两个 8 位变量连接成一个 16 位变量,请使用union. 如果要强制a转换 into的单个成员b,请指定要使用的数组的哪个元素。

在您的代码中,您只转换指向数组的指针。

您需要转换a.

b = (uint16_t)*a;

我从未使用过 AVR,但如果您使用的是 16 位架构,则必须确保 a 是字对齐的。不这样做可能会导致异常。

a 的每个成员都是一个8 位数字。它不能容纳更大的东西。将其转换为 16 位不会. 它只是提取a可能能够保存的任何值,并将其转换为 16 位,以便在值存储在那里时与b的格式匹配。

你甚至没有提到 a 的成员您必须使用 a[0] 或 a[1](而不是 a[2]!)。如果你单独使用a,你只会得到它的地址。(很好,布鲁诺)。

将 a声明为两个 8 位数字的数组也不会使其成为 16 位数字。您可以使用 8 位序列以编程方式执行一些操作来存储和检索 16 位值,但不是您想的那样。