【发布时间】:2021-03-12 21:04:47
【问题描述】:
我有一个uint8_t 数组。我有时需要将两个连续元素视为uint16_t,并将它们复制到另一个uint16_t 变量中。元素不一定以uint16_t 字边界开始。
目前我正在使用 memcpy 这样做:
uint8_t bytes[] = { 0x1A, 0x2B, 0x3C, 0x4D };
uint16_t word = 0;
memcpy(&word, &bytes[1], sizeof(word));
gdb 显示这按预期工作:
(gdb) x/2bx &word
0x7fffffffe2e2: 0x2b 0x3c
将对数组元素的引用转换为 uint16_t 会导致仅复制直接引用的数组元素:
word = (uint16_t)bytes[1];
(gdb) x/2bx &word
0x7fffffffe2e2: 0x2b 0x00
使用uint16_t 指针并将其指向转换为uint16_t * 的数组元素地址会导致两个连续元素被引用,但不会被复制:
uint16_t *wordp = (uint16_t *)&bytes[1];
(gdb) x/2bx wordp
0x7fffffffe2e5: 0x2b 0x3c
bytes[1] = 0x5E;
(gdb) x/2bx wordp
0x7fffffffe2e5: 0x5e 0x3c
分配从uint16_t 指针取消引用的值的常规uint16_t 变量会复制两个顺序数组元素:
word = *wordp;
bytes[1] = 0x6F;
(gdb) x/2bx wordp
0x7fffffffe2e5: 0x6f 0x3c
(gdb) x/2bx &word
0x7fffffffe2e2: 0x5e 0x3c
gcc 在使用 -Wall 选项时不会产生警告。
如果没有中间指针,我怎样才能达到同样的效果?
使用如上所述的指针或尝试不使用中间指针是否有任何顾虑?
在某些情况下使用 memcpy 是否更可取?
它的最终用途是处理大端字节流,因此我将酌情使用byteorder(3) 函数。
【问题讨论】:
-
有什么理由不想使用
memcpy版本吗?就避免未定义的行为而言,这是最安全的,我希望任何体面的优化器尽可能优化副本 -
由于严格的别名规则,
memcpy被认为更可取。另一种选择是移位和或创建uint16_t。这也消除了字节顺序问题。 -
没有特别的理由不想使用
memcpy;这是我在 99% 的情况下使用的。我最初使用 shift 和 OR,但将其替换为memcpy。然后我注意到memcpy在链式函数调用中的链式使用,并且产生了疑问。我想我知道这些选项,我只是觉得我没有足够的经验来比较它们并评估我应该使用哪个和为什么。 -
值得注意的是,将常量
2视为memcpy调用的大小的编译器优化器几乎总是会重写以完全不涉及函数调用或循环,而只是使用寄存器操作字节.这样做的成本可以忽略不计,不用担心函数开销或缓存之外不必要的内存访问。
标签: arrays c pointers casting endianness