memcpy float 变量到 uint8_t 数组

memcpy float variable into uint8_t array

我正在尝试从 SPI 传输填充的 uint8_t 类型数组中提取一些浮点变量。 然而到目前为止我无法实现正确的提取,所以我写了一个小程序来理解我做错了什么。

首先,我只是用一些浮点数填充缓冲区,并想检查它是否正确完成。我注意到当使用 memcpy 将浮点变量复制到缓冲区并随后打印缓冲区内容时,它仍然包含所有零。但是,当将 float 变量直接分配给 space 时,每个缓冲区位置指向(当前在 memcpy 函数下方注释掉的行)以我想要的方式初始化缓冲区。

谁能指出我的错误。

#include <stdio.h>
#include <string.h>

#define MAX_MSG_LEN 64

static uint8_t buffer_rx[MAX_MSG_LEN];

int main(){
    for(int i=0; i<MAX_MSG_LEN/sizeof(float); i++){
        float myFloat = (float)i;
        printf("myFloat = %f\n", myFloat);
        printf("buffer_rx+sizeof(float)*%d = %p\n", i, (void*)(&buffer_rx[sizeof(float)*i]));
        memcpy(&buffer_rx[sizeof(float)*i], &myFloat, sizeof(float));
        //buffer_rx[sizeof(float)*i] = myFloat;
        printf("buffer_rx[%d] = %f\n", i, (float)(buffer_rx[sizeof(float)*i]));
    }

}

安全的做法是像复制进去一样复制出来:

float new_float;
memcpy(&new_float, &buffer_rx[sizeof(float)*i], sizeof(float));
printf("buffer_rx[%d] = %f\n", i, new_float);

Demo

另一种选择是通过 union 进行类型双关(这在 C 中是允许的,但在 C++ 中是不允许的)这样可以安全地直接访问数组元素而无需 memcpying 它们出。

示例:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define MAX_MSG_LEN 64

typedef union {
    uint8_t u8[MAX_MSG_LEN];
    float   f[MAX_MSG_LEN/sizeof(float)];
    double  d[MAX_MSG_LEN/sizeof(double)];
} buffer_t;

int main(){
    buffer_t sender;
    buffer_t receiver;

    // prepare a buffer of floats
    for(int i=0; i<MAX_MSG_LEN/sizeof(float); i++) {
        sender.f[i] = i * 3.14159f;
        printf("%d %f\n", i, sender.f[i]);
    }

    // send the buffer somewhere
    memcpy(receiver.u8, sender.u8, MAX_MSG_LEN);

    // look at what was received
    for(int i=0; i<MAX_MSG_LEN/sizeof(float); i++) {
        printf("%d %f\n", i, receiver.f[i]);
    }
}

要将表示 float 的字节移动到字节缓冲区中,只需使用 memcpy:

复制它们
memcpy(&buffer_rx[sizeof(float)*i], &myFloat, sizeof myFloat);

要将缓冲区中的字节复制到表示 float 的字节中,只需复制到其他目录:

float x;
memcpy(&x, &buffer_rx[sizeof(float)*i], &myFloat, sizeof x);

不要尝试将缓冲区中的字节直接重新解释为 float 或字符类型以外的其他对象。这样做违反了 C 中的别名规则 (C 2018 6.5 7),该规则本质上说任何对象的内存都应该只作为其自身的类型(允许一些微小的变化)或作为字符(字节)进行访问。所以 float 可以作为字节访问,因为该规则允许将任何内存作为字节访问,但是字节数组不应作为 float 访问。它还可能违反有关对齐的规则,因为字节数组可能不会按照 float 对象所需的方式在内存中定位。