如何将结构转换为不同的结构成员

时间:2017-10-31 12:39:43

标签: c casting

如何将变量指向不同结构的成员?这就是我想要做的,但第三行失败了。

volatile uint8_t tx_message_buffer[sizeof(MESSAGE)];
struct MESSAGE *tx_message = (MESSAGE *)tx_message_buffer;
struct PAYLOAD *tx_payload = (PAYLOAD *)tx_message->payload;

以下是结构定义。

#define MSG_MAX_PAYLOAD_LENGTH  64

typedef struct PAYLOAD {
    uint8_t descriptor;
    uint8_t parameters[MSG_MAX_PAYLOAD_LENGTH-1];
};

typedef struct MESSAGE {
    uint8_t address;
    uint8_t length;
    PAYLOAD payload;
    uint8_t checksum;
};

3 个答案:

答案 0 :(得分:3)

此代码存在许多问题。

  • 正如其他答案中所指出的,您无法设置指向PAYLOAD payload;成员的指针,您需要指向其地址&tx_message->payload

  • typedef struct PAYLOAD {}应为typedef struct {} PAYLOAD

  • (MESSAGE *)tx_message_buffer是一个完全狂野的演员,会调用几个定义不明确的行为。首先,你永远不应该抛弃volatile个限定词。但是,一旦您取消引用此结构,您将违反strict aliasing并调用未定义的行为。任何事情都可能发生。

    要解决这些指针错误,您可以执行与此类似的操作:

    typedef struct {
        uint8_t address;
        uint8_t length;
        PAYLOAD payload;
        uint8_t checksum;
    } MESSAGE;
    
    typedef union {
      MESSAGE message;
      uint8_t tx_message_buffer[sizeof(MESSAGE)];
    } message_something;
    

    此代码有效且定义明确。

  • 使用结构来表示数据协议是不好的做法,因为您必须确保结构根本不包含填充。您MESSAGE结构中的内存布局绝不能保证与数据协议的内存布局相对应。该结构可以具有填充字节以适应特定CPU的对齐要求。

    根据您的可移植性要求,禁用使用非标准C(例如#pragma pack(1))的填充可能会也可能不够。要实现完全可移植性,您可能必须编写序列化/反序列化例程。

答案 1 :(得分:2)

您的代码中存在更大的问题:第二行的强制转换无效,因为struct MESSAGE的存储通常可能具有与char[]数组不同的对齐要求。例如,将descriptor的类型更改为uint32_t可能会在某些平台上强制整个结构的偶地址位置。

反之这样做是有效的,因为你可以将任何对象指针转换为char *

volatile struct MESSAGE tx_message;
volatile uint8_t *tx_message_buffer = (char*)tx_message;

第三行失败是因为你没有使用PAYLOAD struct:

的指针
struct PAYLOAD tx_payload = &tx_message.payload;

无需转换结果,因为tx_message.payload已经是正确的类型。

答案 2 :(得分:0)

使用,

PAYLOAD payload;

你得到的变量不是指针。意思是

message->payload;

不是指针。

您需要使用指针。

PAYLOAD * payload;

或者获取结构的地址

&message->payload;