繁体   English   中英

通过指针偏移量访问结构变量值

[英]Access struct variable value by pointer offset

我有一个看起来像的结构:

#pragma pack(1)
typedef struct WHEATHER_STRUCT {
    uint8_t packetID; // Value 9
    uint16_t packetSize; // Value 7
    float cloudLayerAltitude; // Value 25000
} Wheather_Struct

此结构已正确初始化。 由于算法的设计,我需要通过指针偏移量来读取这三个属性值。 我感谢声明一个数组,这些数组的大小以字节为单位。 就像:

int sizeOfStructAttributes = {1, 2, 4};

最后,要访问这些值,请执行以下操作:

pointer = (*this->wheather_struct->packetID)
for (i=0; i<sizeof(sizeOfStructAttributes); i++)
    cout << &pointer << ' ';
    pointer = pointer + sizeOfStructAttributes[i];

预期结果:

9 7 25000

请问你能帮帮我吗?

您的代码有很多问题,我将尝试全部解决:

1-您的结构的填充值取决于您要定位的体系结构,在第一个成员(packetID)之后,它可能取决于结构和编译器3或7个字节。

2-您以错误的方式初始化了指针,应该是:

pointer = &(this->wheather_struct->packetID);

3- cout应该是:

cout << *((datatype*)pointer) << ' '; 
//datatype should be different in each loop iteration of course.

4-如果您要创建此结构的数组,则不确定是否会遇到填充问题。 在极少数情况下,由于将代码与使用不同编译器指令编译的其他库混合甚至使用#pragma在编译期间修改编译器的行为而使用不同的打包和填充时,会发生这种情况。

最后,我确定根本不需要使用指针枚举结构成员。

我鼓励您阅读有关struct padding和packing的文章,好的起点是SO上的以下问题: Structure padding和packing

可以肯定的是,您将无法手动编写这些偏移量。 这绝对不是一种稳定的操作方式,因为编译器可能会进行优化,例如对齐struct成员

您可以执行以下操作:

Wheather_Struct w;
long offsetsOfStructAttributes[3] = {0, 
                                     (char*)&w.packetSize - (char*)&w.packetID, 
                                     (char*)&w.cloudLayerAltitude - (char*)&w.packetID};

请注意,这是字节大小的差异。

告诉您如何操作后,我必须像人们在评论中说的那样说,请找到另一种方式。 这是不安全的,除非您完全知道自己在做什么。

您的方法存在两个问题:

首先,它要求您正确选择尺寸。 使用sizeof可以做到这一点。 因此,您的数组如下所示:

size_t sizeOfStructAttributes = {sizeof(wheather_struct::packet_id),
                               sizeof(wheather_struct::packet_size),
                               sizeof(wheather_struct::cloudLayerAltitude) };

第二个(更严重的)问题是您不允许在结构中填充。 几乎所有的编译器都将(除非特别指示)在packet_id和packet_size之间插入一个填充字节,以便所有内容都能很好地对齐。 幸运的是,也有一个解决方案-使用offsetof宏(在stddef.h中定义):

size_t offsetOfStructAttributes = {offsetof(wheather_struct, packet_id),
                                 offsetof(wheather_struct, packet_size),
                                 offsetof(wheather_struct, cloudLayerAltitude) };

然后,代码变为:

for (size_t offset: offsetsOfStructAttributes) {
    pointer = &(this->wheather_struct->packetID) + offset
    cout << pointer << ' ';
}

实际上:上面的代码解决了您的代码的第三个问题: sizeof()返回以字节为单位的大小,这不太可能是元素计数。

最后,您的变量有错别字:气象与天气是否好有关。 您已经混淆了这两个词,我很确定您的意思是“天气”。

您的错误是您假设该类在成员之间没有填充。 但是必须有填充,才能满足成员的对齐要求。 因此,偏移量不是您所假定的。

要获取类成员的偏移量, 可以使用标准库提供的offsetof宏。 就是说,在不知道您需要什么的情况下,我仍然怀疑它是否合适。 请注意, offsetof仅在您的类是标准布局类时才有效。 否则,行为将是不确定的。 您的示例WHEATHER_STRUCT是标准布局。

 cout << &pointer << ' '; 

这样的事情可能无法获得您期望的输出。 您使用指针的地址,它可能无法为您提供所需的指向对象的值。

获得指示值的方法是间接运算符。 但是,仅当指针的类型正确时,间接操作符才能正确工作(对于float成员, float* ,对于uint16_t成员, uint16_t* ...),但是由于它必须是指向字节的指针,因此它不能为正确的类型。使用偏移量的指针算法。

除了偏移量,还需要了解变量的类型才能解释该值。 您可以将类型存储在某种结构中。 但是你可以将指针不投在运行时确定的类型,所以你需要的是一些运行时流结构,如switch或跳转表的转换。

您最好不要使用指针黑客:有一天底层的内存布局将被更改,您的程序可能会破坏它。 尝试模拟元数据。

enum WheatherStructFields
{
    wsfPacketID,
    wsfPacketSize,
    wsfCloudLayerAltitude,
    wsfNone
};

typedef struct WHEATHER_STRUCT
{
    uint8_t packetID;
    uint16_t packetSize;
    float cloudLayerAltitude;
    void OutFieldValue(std::ostream& os, WheatherStructFields whatField)
    {
        switch (whatField)
        {
        case wsfPacketID:
            os << (int)packetID;
            break;
        case wsfPacketSize:
            os << packetSize;
            break;
        case wsfCloudLayerAltitude:
            os << cloudLayerAltitude;
            break;
        default:
            os << "Unsupported field: " << whatField;
        }
    }
} Wheather_Struct;


int main()
{
    Wheather_Struct weather = { 9, 7, 25000 };
    for (WheatherStructFields whatField = wsfPacketID; whatField < wsfNone; 
        whatField = (WheatherStructFields)((int)whatField + 1))
    {
        weather.OutFieldValue(std::cout, whatField);
        std::cout << " ";
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM